Message List: Reply focusing stack (#1260)

This commit is contained in:
Alexander Zinchuk 2021-07-13 17:31:39 +03:00
parent fa8e750433
commit e744febfe8
6 changed files with 53 additions and 11 deletions

View File

@ -27,7 +27,7 @@ type StateProps = {
unreadCount?: number; unreadCount?: number;
}; };
type DispatchProps = Pick<GlobalActions, 'focusLastMessage'>; type DispatchProps = Pick<GlobalActions, 'focusNextReply'>;
const FOCUS_MARGIN = 20; const FOCUS_MARGIN = 20;
@ -36,7 +36,7 @@ const ScrollDownButton: FC<OwnProps & StateProps & DispatchProps> = ({
canPost, canPost,
messageListType, messageListType,
unreadCount, unreadCount,
focusLastMessage, focusNextReply,
}) => { }) => {
const lang = useLang(); const lang = useLang();
// eslint-disable-next-line no-null/no-null // eslint-disable-next-line no-null/no-null
@ -48,7 +48,7 @@ const ScrollDownButton: FC<OwnProps & StateProps & DispatchProps> = ({
} }
if (messageListType === 'thread') { if (messageListType === 'thread') {
focusLastMessage(); focusNextReply();
} else { } else {
const messagesContainer = elementRef.current!.parentElement!.querySelector<HTMLDivElement>('.MessageList')!; const messagesContainer = elementRef.current!.parentElement!.querySelector<HTMLDivElement>('.MessageList')!;
const messageElements = messagesContainer.querySelectorAll<HTMLDivElement>('.message-list-item'); const messageElements = messagesContainer.querySelectorAll<HTMLDivElement>('.message-list-item');
@ -59,7 +59,7 @@ const ScrollDownButton: FC<OwnProps & StateProps & DispatchProps> = ({
fastSmoothScroll(messagesContainer, lastMessageElement, 'end', FOCUS_MARGIN); fastSmoothScroll(messagesContainer, lastMessageElement, 'end', FOCUS_MARGIN);
} }
}, [isShown, messageListType, focusLastMessage]); }, [isShown, messageListType, focusNextReply]);
const fabClassName = buildClassName( const fabClassName = buildClassName(
'ScrollDownButton', 'ScrollDownButton',
@ -101,5 +101,5 @@ export default memo(withGlobal<OwnProps>(
unreadCount: chat && threadId === MAIN_THREAD_ID && messageListType === 'thread' ? chat.unreadCount : undefined, unreadCount: chat && threadId === MAIN_THREAD_ID && messageListType === 'thread' ? chat.unreadCount : undefined,
}; };
}, },
(setGlobal, actions): DispatchProps => pick(actions, ['focusLastMessage']), (setGlobal, actions): DispatchProps => pick(actions, ['focusNextReply']),
)(ScrollDownButton)); )(ScrollDownButton));

View File

@ -416,8 +416,10 @@ const Message: FC<OwnProps & StateProps & DispatchProps> = ({
}, [botSender, openUserInfo]); }, [botSender, openUserInfo]);
const handleReplyClick = useCallback((): void => { const handleReplyClick = useCallback((): void => {
focusMessage({ chatId, threadId, messageId: message.replyToMessageId }); focusMessage({
}, [focusMessage, chatId, threadId, message.replyToMessageId]); chatId, threadId, messageId: message.replyToMessageId, replyMessageId: messageId,
});
}, [focusMessage, chatId, threadId, message.replyToMessageId, messageId]);
const handleMediaClick = useCallback((): void => { const handleMediaClick = useCallback((): void => {
openMediaViewer({ openMediaViewer({

View File

@ -58,6 +58,7 @@ export interface Thread {
noWebPage?: boolean; noWebPage?: boolean;
threadInfo?: ApiThreadInfo; threadInfo?: ApiThreadInfo;
firstMessageId?: number; firstMessageId?: number;
replyStack?: number[];
} }
export type GlobalState = { export type GlobalState = {
@ -424,7 +425,7 @@ export type ActionTypes = (
'openTelegramLink' | 'openChatByUsername' | 'requestThreadInfoUpdate' | 'setScrollOffset' | 'unpinAllMessages' | 'openTelegramLink' | 'openChatByUsername' | 'requestThreadInfoUpdate' | 'setScrollOffset' | 'unpinAllMessages' |
'setReplyingToId' | 'setEditingId' | 'editLastMessage' | 'saveDraft' | 'clearDraft' | 'loadPinnedMessages' | 'setReplyingToId' | 'setEditingId' | 'editLastMessage' | 'saveDraft' | 'clearDraft' | 'loadPinnedMessages' |
'loadMessageLink' | 'toggleMessageWebPage' | 'replyToNextMessage' | 'deleteChatUser' | 'deleteChat' | 'loadMessageLink' | 'toggleMessageWebPage' | 'replyToNextMessage' | 'deleteChatUser' | 'deleteChat' |
'reportMessages' | 'reportMessages' | 'focusNextReply' |
// scheduled messages // scheduled messages
'loadScheduledHistory' | 'sendScheduledMessages' | 'rescheduleMessage' | 'deleteScheduledMessages' | 'loadScheduledHistory' | 'sendScheduledMessages' | 'rescheduleMessage' | 'deleteScheduledMessages' |
// poll result // poll result

View File

@ -1,7 +1,6 @@
import { addReducer, setGlobal } from '../../../lib/teact/teactn'; import { addReducer, setGlobal } from '../../../lib/teact/teactn';
import { import {
exitMessageSelectMode, exitMessageSelectMode, replaceThreadParam, updateCurrentMessageList,
updateCurrentMessageList,
} from '../../reducers'; } from '../../reducers';
import { selectCurrentMessageList } from '../../selectors'; import { selectCurrentMessageList } from '../../selectors';
import { closeLocalTextSearch } from './localSearch'; import { closeLocalTextSearch } from './localSearch';
@ -19,6 +18,7 @@ addReducer('openChat', (global, actions, payload) => {
|| currentMessageList.threadId !== threadId || currentMessageList.threadId !== threadId
|| currentMessageList.type !== type || currentMessageList.type !== type
)) { )) {
global = replaceThreadParam(global, id, threadId, 'replyStack', []);
global = exitMessageSelectMode(global); global = exitMessageSelectMode(global);
global = closeLocalTextSearch(global); global = closeLocalTextSearch(global);

View File

@ -21,7 +21,7 @@ import {
selectChatMessages, selectChatMessages,
selectAllowedMessageActions, selectAllowedMessageActions,
selectMessageIdsByGroupId, selectMessageIdsByGroupId,
selectForwardedMessageIdsByGroupId, selectIsViewportNewest, selectReplyingToId, selectForwardedMessageIdsByGroupId, selectIsViewportNewest, selectReplyingToId, selectReplyStack,
} from '../../selectors'; } from '../../selectors';
import { findLast } from '../../../util/iteratees'; import { findLast } from '../../../util/iteratees';
import { IS_TOUCH_ENV } from '../../../util/environment'; import { IS_TOUCH_ENV } from '../../../util/environment';
@ -240,9 +240,39 @@ addReducer('focusLastMessage', (global, actions) => {
}); });
}); });
addReducer('focusNextReply', (global, actions) => {
const currentMessageList = selectCurrentMessageList(global);
if (!currentMessageList) {
return undefined;
}
const { chatId, threadId } = currentMessageList;
const replyStack = selectReplyStack(global, chatId, threadId);
if (!replyStack || replyStack.length === 0) {
actions.focusLastMessage();
} else {
const messageId = replyStack.pop();
global = replaceThreadParam(global, chatId, threadId, 'replyStack', [...replyStack]);
setGlobal(global);
actions.focusMessage({
chatId,
threadId,
messageId,
});
}
return undefined;
});
addReducer('focusMessage', (global, actions, payload) => { addReducer('focusMessage', (global, actions, payload) => {
const { const {
chatId, threadId = MAIN_THREAD_ID, messageListType = 'thread', noHighlight, groupedId, groupedChatId, chatId, threadId = MAIN_THREAD_ID, messageListType = 'thread', noHighlight, groupedId, groupedChatId,
replyMessageId,
} = payload!; } = payload!;
let { messageId } = payload!; let { messageId } = payload!;
@ -275,6 +305,11 @@ addReducer('focusMessage', (global, actions, payload) => {
global = updateFocusedMessage(global, chatId, messageId, noHighlight); global = updateFocusedMessage(global, chatId, messageId, noHighlight);
global = updateFocusDirection(global, undefined); global = updateFocusDirection(global, undefined);
if (replyMessageId) {
const replyStack = selectReplyStack(global, chatId, threadId) || [];
global = replaceThreadParam(global, chatId, threadId, 'replyStack', [...replyStack, replyMessageId]);
}
if (shouldSwitchChat) { if (shouldSwitchChat) {
global = updateFocusDirection(global, FocusDirection.Static); global = updateFocusDirection(global, FocusDirection.Static);
} }

View File

@ -150,6 +150,10 @@ export function selectFirstMessageId(global: GlobalState, chatId: number, thread
return selectThreadParam(global, chatId, threadId, 'firstMessageId'); return selectThreadParam(global, chatId, threadId, 'firstMessageId');
} }
export function selectReplyStack(global: GlobalState, chatId: number, threadId: number) {
return selectThreadParam(global, chatId, threadId, 'replyStack');
}
export function selectThreadOriginChat(global: GlobalState, chatId: number, threadId: number) { export function selectThreadOriginChat(global: GlobalState, chatId: number, threadId: number) {
if (threadId === MAIN_THREAD_ID) { if (threadId === MAIN_THREAD_ID) {
return selectChat(global, chatId); return selectChat(global, chatId);