Message List: Fix double focusing

This commit is contained in:
Alexander Zinchuk 2023-04-09 04:02:56 +02:00
parent 55dbc323aa
commit fc919a0c4c
4 changed files with 42 additions and 13 deletions

View File

@ -9,6 +9,7 @@ import type {
} from '../../api/types'; } from '../../api/types';
import type { FocusDirection } from '../../types'; import type { FocusDirection } from '../../types';
import type { PinnedIntersectionChangedCallback } from './hooks/usePinnedMessage'; import type { PinnedIntersectionChangedCallback } from './hooks/usePinnedMessage';
import type { MessageListType } from '../../global/types';
import { import {
selectUser, selectUser,
@ -17,6 +18,7 @@ import {
selectChat, selectChat,
selectTopicFromMessage, selectTopicFromMessage,
selectTabState, selectTabState,
selectCurrentMessageIds,
} from '../../global/selectors'; } from '../../global/selectors';
import { getMessageHtmlId, isChatChannel } from '../../global/helpers'; import { getMessageHtmlId, isChatChannel } from '../../global/helpers';
import buildClassName from '../../util/buildClassName'; import buildClassName from '../../util/buildClassName';
@ -38,6 +40,7 @@ import ActionMessageSuggestedAvatar from './ActionMessageSuggestedAvatar';
type OwnProps = { type OwnProps = {
message: ApiMessage; message: ApiMessage;
threadId?: number; threadId?: number;
messageListType?: MessageListType;
observeIntersectionForReading?: ObserveFn; observeIntersectionForReading?: ObserveFn;
observeIntersectionForLoading?: ObserveFn; observeIntersectionForLoading?: ObserveFn;
observeIntersectionForPlaying?: ObserveFn; observeIntersectionForPlaying?: ObserveFn;
@ -60,6 +63,7 @@ type StateProps = {
topic?: ApiTopic; topic?: ApiTopic;
focusDirection?: FocusDirection; focusDirection?: FocusDirection;
noFocusHighlight?: boolean; noFocusHighlight?: boolean;
viewportIds?: number[];
premiumGiftSticker?: ApiSticker; premiumGiftSticker?: ApiSticker;
}; };
@ -79,6 +83,7 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
isFocused, isFocused,
focusDirection, focusDirection,
noFocusHighlight, noFocusHighlight,
viewportIds,
premiumGiftSticker, premiumGiftSticker,
isInsideTopic, isInsideTopic,
topic, topic,
@ -97,7 +102,7 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
useOnIntersect(ref, observeIntersectionForReading); useOnIntersect(ref, observeIntersectionForReading);
useEnsureMessage(message.chatId, message.replyToMessageId, targetMessage); useEnsureMessage(message.chatId, message.replyToMessageId, targetMessage);
useFocusMessage(ref, message.chatId, isFocused, focusDirection, noFocusHighlight); useFocusMessage(ref, message.id, message.chatId, isFocused, focusDirection, noFocusHighlight, viewportIds);
useEffect(() => { useEffect(() => {
if (!message.isPinned) return undefined; if (!message.isPinned) return undefined;
@ -247,13 +252,17 @@ const ActionMessage: FC<OwnProps & StateProps> = ({
}; };
export default memo(withGlobal<OwnProps>( export default memo(withGlobal<OwnProps>(
(global, { message, threadId }): StateProps => { (global, { message, threadId, messageListType }): StateProps => {
const {
chatId, senderId, replyToMessageId, content,
} = message;
const { byId: usersById } = global.users; const { byId: usersById } = global.users;
const userId = message.senderId; const userId = senderId;
const { targetUserIds, targetChatId } = message.content.action || {}; const { targetUserIds, targetChatId } = content.action || {};
const targetMessageId = message.replyToMessageId; const targetMessageId = replyToMessageId;
const targetMessage = targetMessageId const targetMessage = targetMessageId
? selectChatMessage(global, message.chatId, targetMessageId) ? selectChatMessage(global, chatId, targetMessageId)
: undefined; : undefined;
const isFocused = threadId ? selectIsMessageFocused(global, message, threadId) : false; const isFocused = threadId ? selectIsMessageFocused(global, message, threadId) : false;
@ -262,8 +271,8 @@ export default memo(withGlobal<OwnProps>(
noHighlight: noFocusHighlight, noHighlight: noFocusHighlight,
} = (isFocused && selectTabState(global).focusedMessage) || {}; } = (isFocused && selectTabState(global).focusedMessage) || {};
const chat = selectChat(global, message.chatId); const chat = selectChat(global, chatId);
const isChat = chat && (isChatChannel(chat) || userId === message.chatId); const isChat = chat && (isChatChannel(chat) || userId === chatId);
const senderUser = !isChat && userId ? selectUser(global, userId) : undefined; const senderUser = !isChat && userId ? selectUser(global, userId) : undefined;
const senderChat = isChat ? chat : undefined; const senderChat = isChat ? chat : undefined;
const premiumGiftSticker = global.premiumGifts?.stickers?.[0]; const premiumGiftSticker = global.premiumGifts?.stickers?.[0];
@ -279,7 +288,13 @@ export default memo(withGlobal<OwnProps>(
isFocused, isFocused,
premiumGiftSticker, premiumGiftSticker,
topic, topic,
...(isFocused && { focusDirection, noFocusHighlight }), ...(isFocused && {
focusDirection,
noFocusHighlight,
viewportIds: threadId && messageListType
? selectCurrentMessageIds(global, chatId, threadId, messageListType)
: undefined,
}),
}; };
}, },
)(ActionMessage)); )(ActionMessage));

View File

@ -150,6 +150,7 @@ const MessageListContent: FC<OwnProps> = ({
key={message.id} key={message.id}
message={message} message={message}
threadId={threadId} threadId={threadId}
messageListType={type}
isInsideTopic={Boolean(threadId && threadId !== MAIN_THREAD_ID)} isInsideTopic={Boolean(threadId && threadId !== MAIN_THREAD_ID)}
observeIntersectionForReading={observeIntersectionForReading} observeIntersectionForReading={observeIntersectionForReading}
observeIntersectionForLoading={observeIntersectionForLoading} observeIntersectionForLoading={observeIntersectionForLoading}

View File

@ -647,7 +647,10 @@ const Message: FC<OwnProps & StateProps> = ({
replyMessage, replyMessage,
message.id, message.id,
); );
useFocusMessage(ref, chatId, isFocused, focusDirection, noFocusHighlight, isResizingContainer, viewportIds);
useFocusMessage(
ref, messageId, chatId, isFocused, focusDirection, noFocusHighlight, viewportIds, isResizingContainer,
);
const shouldFocusOnResize = isLastInGroup; const shouldFocusOnResize = isLastInGroup;

View File

@ -1,6 +1,6 @@
import type { FocusDirection } from '../../../../types'; import type { FocusDirection } from '../../../../types';
import { useLayoutEffect } from '../../../../lib/teact/teact'; import { useLayoutEffect, useMemo } from '../../../../lib/teact/teact';
import fastSmoothScroll from '../../../../util/fastSmoothScroll'; import fastSmoothScroll from '../../../../util/fastSmoothScroll';
// This is used when the viewport was replaced. // This is used when the viewport was replaced.
@ -9,13 +9,23 @@ const FOCUS_MARGIN = 20;
export default function useFocusMessage( export default function useFocusMessage(
elementRef: { current: HTMLDivElement | null }, elementRef: { current: HTMLDivElement | null },
messageId: number,
chatId: string, chatId: string,
isFocused?: boolean, isFocused?: boolean,
focusDirection?: FocusDirection, focusDirection?: FocusDirection,
noFocusHighlight?: boolean, noFocusHighlight?: boolean,
isResizingContainer?: boolean,
viewportIds?: number[], viewportIds?: number[],
isResizingContainer?: boolean,
) { ) {
const viewportIndex = useMemo(() => {
if (!viewportIds) {
return 0;
}
const index = viewportIds.indexOf(messageId);
return Math.min(index, viewportIds.length - index - 1);
}, [messageId, viewportIds]);
useLayoutEffect(() => { useLayoutEffect(() => {
if (isFocused && elementRef.current) { if (isFocused && elementRef.current) {
const messagesContainer = elementRef.current.closest<HTMLDivElement>('.MessageList')!; const messagesContainer = elementRef.current.closest<HTMLDivElement>('.MessageList')!;
@ -33,6 +43,6 @@ export default function useFocusMessage(
); );
} }
}, [ }, [
elementRef, chatId, isFocused, focusDirection, noFocusHighlight, isResizingContainer, viewportIds, elementRef, chatId, isFocused, focusDirection, noFocusHighlight, isResizingContainer, viewportIndex,
]); ]);
} }