FAB: Scroll to unread reaction on click (#4709)

This commit is contained in:
Alexander Zinchuk 2024-07-15 15:50:30 +02:00
parent e2c6199431
commit a4b72cfd3e
8 changed files with 37 additions and 10 deletions

View File

@ -26,7 +26,8 @@ import type {
} from '../../../global/types';
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
import type {
FocusDirection, IAlbum, ISettings, ThreadId,
FocusDirection, IAlbum, ISettings, ScrollTargetPosition,
ThreadId,
} from '../../../types';
import type { Signal } from '../../../util/signals';
import type { PinnedIntersectionChangedCallback } from '../hooks/usePinnedMessage';
@ -234,6 +235,7 @@ type StateProps = {
focusDirection?: FocusDirection;
focusedQuote?: string;
noFocusHighlight?: boolean;
scrollTargetPosition?: ScrollTargetPosition;
isResizingContainer?: boolean;
isForwarding?: boolean;
isChatWithSelf?: boolean;
@ -354,6 +356,7 @@ const Message: FC<OwnProps & StateProps> = ({
focusDirection,
focusedQuote,
noFocusHighlight,
scrollTargetPosition,
isResizingContainer,
isForwarding,
isChatWithSelf,
@ -770,7 +773,15 @@ const Message: FC<OwnProps & StateProps> = ({
);
useFocusMessage(
ref, chatId, isFocused, focusDirection, noFocusHighlight, isResizingContainer, isJustAdded, Boolean(focusedQuote),
ref,
chatId,
isFocused,
focusDirection,
noFocusHighlight,
isResizingContainer,
isJustAdded,
Boolean(focusedQuote),
scrollTargetPosition,
);
const viaBusinessBotTitle = viaBusinessBot ? getSenderTitle(lang, viaBusinessBot) : undefined;
@ -1659,7 +1670,8 @@ export default memo(withGlobal<OwnProps>(
);
const {
direction: focusDirection, noHighlight: noFocusHighlight, isResizingContainer, quote: focusedQuote,
direction: focusDirection, noHighlight: noFocusHighlight, isResizingContainer,
quote: focusedQuote, scrollTargetPosition,
} = (isFocused && focusedMessage) || {};
const { query: highlight } = selectCurrentTextSearch(global) || {};
@ -1795,6 +1807,7 @@ export default memo(withGlobal<OwnProps>(
noFocusHighlight,
isResizingContainer,
focusedQuote,
scrollTargetPosition,
}),
senderBoosts,
tags: global.savedReactionTags?.byKey,

View File

@ -1,7 +1,7 @@
import { useLayoutEffect, useRef } from '../../../../lib/teact/teact';
import { addExtraClass } from '../../../../lib/teact/teact-dom';
import type { FocusDirection } from '../../../../types';
import type { FocusDirection, ScrollTargetPosition } from '../../../../types';
import {
requestForcedReflow, requestMeasure, requestMutation,
@ -22,6 +22,7 @@ export default function useFocusMessage(
isResizingContainer?: boolean,
isJustAdded?: boolean,
isQuote?: boolean,
scrollTargetPosition?: ScrollTargetPosition,
) {
const isRelocatedRef = useRef(!isJustAdded);
@ -33,12 +34,13 @@ export default function useFocusMessage(
const messagesContainer = elementRef.current.closest<HTMLDivElement>('.MessageList')!;
// `noFocusHighlight` is always called with “scroll-to-bottom” buttons
const isToBottom = noFocusHighlight;
const scrollPosition = scrollTargetPosition || isToBottom ? 'end' : 'centerOrTop';
const exec = () => {
const result = animateScroll(
messagesContainer,
elementRef.current!,
isToBottom ? 'end' : 'centerOrTop',
scrollPosition,
FOCUS_MARGIN,
focusDirection !== undefined ? (isToBottom ? BOTTOM_FOCUS_OFFSET : RELOCATED_FOCUS_OFFSET) : undefined,
focusDirection,
@ -69,6 +71,6 @@ export default function useFocusMessage(
}
}
}, [
elementRef, chatId, isFocused, focusDirection, noFocusHighlight, isResizingContainer, isQuote,
elementRef, chatId, isFocused, focusDirection, noFocusHighlight, isResizingContainer, isQuote, scrollTargetPosition,
]);
}

View File

@ -440,7 +440,9 @@ addActionHandler('focusNextReaction', (global, actions, payload): ActionReturnTy
return undefined;
}
actions.focusMessage({ chatId: chat.id, messageId: chat.unreadReactions[0], tabId });
actions.focusMessage({
chatId: chat.id, messageId: chat.unreadReactions[0], tabId, scrollTargetPosition: 'end',
});
actions.markMessagesRead({ messageIds: [chat.unreadReactions[0]], tabId });
return undefined;
});

View File

@ -400,7 +400,7 @@ addActionHandler('focusNextReply', (global, actions, payload): ActionReturnType
addActionHandler('focusMessage', (global, actions, payload): ActionReturnType => {
const {
chatId, threadId = MAIN_THREAD_ID, messageListType = 'thread', noHighlight, groupedId, groupedChatId,
replyMessageId, isResizingContainer, shouldReplaceHistory, noForumTopicPanel, quote,
replyMessageId, isResizingContainer, shouldReplaceHistory, noForumTopicPanel, quote, scrollTargetPosition,
tabId = getCurrentTabId(),
} = payload;
@ -445,6 +445,7 @@ addActionHandler('focusMessage', (global, actions, payload): ActionReturnType =>
noHighlight,
isResizingContainer,
quote,
scrollTargetPosition,
}, tabId);
global = updateFocusDirection(global, undefined, tabId);

View File

@ -1,7 +1,7 @@
import type {
ApiMessage, ApiQuickReply, ApiSponsoredMessage, ApiThreadInfo,
} from '../../api/types';
import type { FocusDirection, ThreadId } from '../../types';
import type { FocusDirection, ScrollTargetPosition, ThreadId } from '../../types';
import type {
GlobalState, MessageList, MessageListType, TabArgs, TabThread, Thread,
} from '../types';
@ -643,6 +643,7 @@ export function updateFocusedMessage<T extends GlobalState>({
noHighlight = false,
isResizingContainer = false,
quote,
scrollTargetPosition,
}: {
global: T;
chatId?: string;
@ -651,6 +652,7 @@ export function updateFocusedMessage<T extends GlobalState>({
noHighlight?: boolean;
isResizingContainer?: boolean;
quote?: string;
scrollTargetPosition?: ScrollTargetPosition;
},
...[tabId = getCurrentTabId()]: TabArgs<T>): T {
return updateTabState(global, {
@ -662,6 +664,7 @@ export function updateFocusedMessage<T extends GlobalState>({
noHighlight,
isResizingContainer,
quote,
scrollTargetPosition,
},
}, tabId);
}

View File

@ -115,6 +115,7 @@ import type {
PrivacyVisibility,
ProfileEditProgress,
ProfileTabType,
ScrollTargetPosition,
SettingsScreens,
SharedMediaType,
ShippingOption,
@ -311,6 +312,7 @@ export type TabState = {
noHighlight?: boolean;
isResizingContainer?: boolean;
quote?: string;
scrollTargetPosition?: ScrollTargetPosition;
};
selectedMessages?: {
@ -1908,6 +1910,7 @@ export interface ActionPayloads {
shouldReplaceHistory?: boolean;
noForumTopicPanel?: boolean;
quote?: string;
scrollTargetPosition?: ScrollTargetPosition;
} & WithTabId;
focusLastMessage: WithTabId | undefined;

View File

@ -30,6 +30,8 @@ export enum FocusDirection {
Static,
}
export type ScrollTargetPosition = ScrollLogicalPosition | 'centerOrTop';
export interface IAlbum {
albumId: string;
messages: ApiMessage[];

View File

@ -1,5 +1,6 @@
import { getGlobal } from '../global';
import type { ScrollTargetPosition } from '../types';
import { FocusDirection } from '../types';
import {
@ -49,7 +50,7 @@ export function restartCurrentScrollAnimation() {
function createMutateFunction(
container: HTMLElement,
element: HTMLElement,
position: ScrollLogicalPosition | 'centerOrTop',
position: ScrollTargetPosition,
margin = 0,
maxDistance = FAST_SMOOTH_MAX_DISTANCE,
forceDirection?: FocusDirection,