FAB: Scroll to unread reaction on click (#4709)
This commit is contained in:
parent
e2c6199431
commit
a4b72cfd3e
@ -26,7 +26,8 @@ import type {
|
|||||||
} from '../../../global/types';
|
} from '../../../global/types';
|
||||||
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
||||||
import type {
|
import type {
|
||||||
FocusDirection, IAlbum, ISettings, ThreadId,
|
FocusDirection, IAlbum, ISettings, ScrollTargetPosition,
|
||||||
|
ThreadId,
|
||||||
} from '../../../types';
|
} from '../../../types';
|
||||||
import type { Signal } from '../../../util/signals';
|
import type { Signal } from '../../../util/signals';
|
||||||
import type { PinnedIntersectionChangedCallback } from '../hooks/usePinnedMessage';
|
import type { PinnedIntersectionChangedCallback } from '../hooks/usePinnedMessage';
|
||||||
@ -234,6 +235,7 @@ type StateProps = {
|
|||||||
focusDirection?: FocusDirection;
|
focusDirection?: FocusDirection;
|
||||||
focusedQuote?: string;
|
focusedQuote?: string;
|
||||||
noFocusHighlight?: boolean;
|
noFocusHighlight?: boolean;
|
||||||
|
scrollTargetPosition?: ScrollTargetPosition;
|
||||||
isResizingContainer?: boolean;
|
isResizingContainer?: boolean;
|
||||||
isForwarding?: boolean;
|
isForwarding?: boolean;
|
||||||
isChatWithSelf?: boolean;
|
isChatWithSelf?: boolean;
|
||||||
@ -354,6 +356,7 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
focusDirection,
|
focusDirection,
|
||||||
focusedQuote,
|
focusedQuote,
|
||||||
noFocusHighlight,
|
noFocusHighlight,
|
||||||
|
scrollTargetPosition,
|
||||||
isResizingContainer,
|
isResizingContainer,
|
||||||
isForwarding,
|
isForwarding,
|
||||||
isChatWithSelf,
|
isChatWithSelf,
|
||||||
@ -770,7 +773,15 @@ const Message: FC<OwnProps & StateProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
useFocusMessage(
|
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;
|
const viaBusinessBotTitle = viaBusinessBot ? getSenderTitle(lang, viaBusinessBot) : undefined;
|
||||||
@ -1659,7 +1670,8 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
direction: focusDirection, noHighlight: noFocusHighlight, isResizingContainer, quote: focusedQuote,
|
direction: focusDirection, noHighlight: noFocusHighlight, isResizingContainer,
|
||||||
|
quote: focusedQuote, scrollTargetPosition,
|
||||||
} = (isFocused && focusedMessage) || {};
|
} = (isFocused && focusedMessage) || {};
|
||||||
|
|
||||||
const { query: highlight } = selectCurrentTextSearch(global) || {};
|
const { query: highlight } = selectCurrentTextSearch(global) || {};
|
||||||
@ -1795,6 +1807,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
noFocusHighlight,
|
noFocusHighlight,
|
||||||
isResizingContainer,
|
isResizingContainer,
|
||||||
focusedQuote,
|
focusedQuote,
|
||||||
|
scrollTargetPosition,
|
||||||
}),
|
}),
|
||||||
senderBoosts,
|
senderBoosts,
|
||||||
tags: global.savedReactionTags?.byKey,
|
tags: global.savedReactionTags?.byKey,
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { useLayoutEffect, useRef } from '../../../../lib/teact/teact';
|
import { useLayoutEffect, useRef } from '../../../../lib/teact/teact';
|
||||||
import { addExtraClass } from '../../../../lib/teact/teact-dom';
|
import { addExtraClass } from '../../../../lib/teact/teact-dom';
|
||||||
|
|
||||||
import type { FocusDirection } from '../../../../types';
|
import type { FocusDirection, ScrollTargetPosition } from '../../../../types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
requestForcedReflow, requestMeasure, requestMutation,
|
requestForcedReflow, requestMeasure, requestMutation,
|
||||||
@ -22,6 +22,7 @@ export default function useFocusMessage(
|
|||||||
isResizingContainer?: boolean,
|
isResizingContainer?: boolean,
|
||||||
isJustAdded?: boolean,
|
isJustAdded?: boolean,
|
||||||
isQuote?: boolean,
|
isQuote?: boolean,
|
||||||
|
scrollTargetPosition?: ScrollTargetPosition,
|
||||||
) {
|
) {
|
||||||
const isRelocatedRef = useRef(!isJustAdded);
|
const isRelocatedRef = useRef(!isJustAdded);
|
||||||
|
|
||||||
@ -33,12 +34,13 @@ export default function useFocusMessage(
|
|||||||
const messagesContainer = elementRef.current.closest<HTMLDivElement>('.MessageList')!;
|
const messagesContainer = elementRef.current.closest<HTMLDivElement>('.MessageList')!;
|
||||||
// `noFocusHighlight` is always called with “scroll-to-bottom” buttons
|
// `noFocusHighlight` is always called with “scroll-to-bottom” buttons
|
||||||
const isToBottom = noFocusHighlight;
|
const isToBottom = noFocusHighlight;
|
||||||
|
const scrollPosition = scrollTargetPosition || isToBottom ? 'end' : 'centerOrTop';
|
||||||
|
|
||||||
const exec = () => {
|
const exec = () => {
|
||||||
const result = animateScroll(
|
const result = animateScroll(
|
||||||
messagesContainer,
|
messagesContainer,
|
||||||
elementRef.current!,
|
elementRef.current!,
|
||||||
isToBottom ? 'end' : 'centerOrTop',
|
scrollPosition,
|
||||||
FOCUS_MARGIN,
|
FOCUS_MARGIN,
|
||||||
focusDirection !== undefined ? (isToBottom ? BOTTOM_FOCUS_OFFSET : RELOCATED_FOCUS_OFFSET) : undefined,
|
focusDirection !== undefined ? (isToBottom ? BOTTOM_FOCUS_OFFSET : RELOCATED_FOCUS_OFFSET) : undefined,
|
||||||
focusDirection,
|
focusDirection,
|
||||||
@ -69,6 +71,6 @@ export default function useFocusMessage(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [
|
}, [
|
||||||
elementRef, chatId, isFocused, focusDirection, noFocusHighlight, isResizingContainer, isQuote,
|
elementRef, chatId, isFocused, focusDirection, noFocusHighlight, isResizingContainer, isQuote, scrollTargetPosition,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -440,7 +440,9 @@ addActionHandler('focusNextReaction', (global, actions, payload): ActionReturnTy
|
|||||||
return undefined;
|
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 });
|
actions.markMessagesRead({ messageIds: [chat.unreadReactions[0]], tabId });
|
||||||
return undefined;
|
return undefined;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -400,7 +400,7 @@ addActionHandler('focusNextReply', (global, actions, payload): ActionReturnType
|
|||||||
addActionHandler('focusMessage', (global, actions, payload): ActionReturnType => {
|
addActionHandler('focusMessage', (global, actions, payload): ActionReturnType => {
|
||||||
const {
|
const {
|
||||||
chatId, threadId = MAIN_THREAD_ID, messageListType = 'thread', noHighlight, groupedId, groupedChatId,
|
chatId, threadId = MAIN_THREAD_ID, messageListType = 'thread', noHighlight, groupedId, groupedChatId,
|
||||||
replyMessageId, isResizingContainer, shouldReplaceHistory, noForumTopicPanel, quote,
|
replyMessageId, isResizingContainer, shouldReplaceHistory, noForumTopicPanel, quote, scrollTargetPosition,
|
||||||
tabId = getCurrentTabId(),
|
tabId = getCurrentTabId(),
|
||||||
} = payload;
|
} = payload;
|
||||||
|
|
||||||
@ -445,6 +445,7 @@ addActionHandler('focusMessage', (global, actions, payload): ActionReturnType =>
|
|||||||
noHighlight,
|
noHighlight,
|
||||||
isResizingContainer,
|
isResizingContainer,
|
||||||
quote,
|
quote,
|
||||||
|
scrollTargetPosition,
|
||||||
}, tabId);
|
}, tabId);
|
||||||
global = updateFocusDirection(global, undefined, tabId);
|
global = updateFocusDirection(global, undefined, tabId);
|
||||||
|
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import type {
|
import type {
|
||||||
ApiMessage, ApiQuickReply, ApiSponsoredMessage, ApiThreadInfo,
|
ApiMessage, ApiQuickReply, ApiSponsoredMessage, ApiThreadInfo,
|
||||||
} from '../../api/types';
|
} from '../../api/types';
|
||||||
import type { FocusDirection, ThreadId } from '../../types';
|
import type { FocusDirection, ScrollTargetPosition, ThreadId } from '../../types';
|
||||||
import type {
|
import type {
|
||||||
GlobalState, MessageList, MessageListType, TabArgs, TabThread, Thread,
|
GlobalState, MessageList, MessageListType, TabArgs, TabThread, Thread,
|
||||||
} from '../types';
|
} from '../types';
|
||||||
@ -643,6 +643,7 @@ export function updateFocusedMessage<T extends GlobalState>({
|
|||||||
noHighlight = false,
|
noHighlight = false,
|
||||||
isResizingContainer = false,
|
isResizingContainer = false,
|
||||||
quote,
|
quote,
|
||||||
|
scrollTargetPosition,
|
||||||
}: {
|
}: {
|
||||||
global: T;
|
global: T;
|
||||||
chatId?: string;
|
chatId?: string;
|
||||||
@ -651,6 +652,7 @@ export function updateFocusedMessage<T extends GlobalState>({
|
|||||||
noHighlight?: boolean;
|
noHighlight?: boolean;
|
||||||
isResizingContainer?: boolean;
|
isResizingContainer?: boolean;
|
||||||
quote?: string;
|
quote?: string;
|
||||||
|
scrollTargetPosition?: ScrollTargetPosition;
|
||||||
},
|
},
|
||||||
...[tabId = getCurrentTabId()]: TabArgs<T>): T {
|
...[tabId = getCurrentTabId()]: TabArgs<T>): T {
|
||||||
return updateTabState(global, {
|
return updateTabState(global, {
|
||||||
@ -662,6 +664,7 @@ export function updateFocusedMessage<T extends GlobalState>({
|
|||||||
noHighlight,
|
noHighlight,
|
||||||
isResizingContainer,
|
isResizingContainer,
|
||||||
quote,
|
quote,
|
||||||
|
scrollTargetPosition,
|
||||||
},
|
},
|
||||||
}, tabId);
|
}, tabId);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -115,6 +115,7 @@ import type {
|
|||||||
PrivacyVisibility,
|
PrivacyVisibility,
|
||||||
ProfileEditProgress,
|
ProfileEditProgress,
|
||||||
ProfileTabType,
|
ProfileTabType,
|
||||||
|
ScrollTargetPosition,
|
||||||
SettingsScreens,
|
SettingsScreens,
|
||||||
SharedMediaType,
|
SharedMediaType,
|
||||||
ShippingOption,
|
ShippingOption,
|
||||||
@ -311,6 +312,7 @@ export type TabState = {
|
|||||||
noHighlight?: boolean;
|
noHighlight?: boolean;
|
||||||
isResizingContainer?: boolean;
|
isResizingContainer?: boolean;
|
||||||
quote?: string;
|
quote?: string;
|
||||||
|
scrollTargetPosition?: ScrollTargetPosition;
|
||||||
};
|
};
|
||||||
|
|
||||||
selectedMessages?: {
|
selectedMessages?: {
|
||||||
@ -1908,6 +1910,7 @@ export interface ActionPayloads {
|
|||||||
shouldReplaceHistory?: boolean;
|
shouldReplaceHistory?: boolean;
|
||||||
noForumTopicPanel?: boolean;
|
noForumTopicPanel?: boolean;
|
||||||
quote?: string;
|
quote?: string;
|
||||||
|
scrollTargetPosition?: ScrollTargetPosition;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
|
|
||||||
focusLastMessage: WithTabId | undefined;
|
focusLastMessage: WithTabId | undefined;
|
||||||
|
|||||||
@ -30,6 +30,8 @@ export enum FocusDirection {
|
|||||||
Static,
|
Static,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ScrollTargetPosition = ScrollLogicalPosition | 'centerOrTop';
|
||||||
|
|
||||||
export interface IAlbum {
|
export interface IAlbum {
|
||||||
albumId: string;
|
albumId: string;
|
||||||
messages: ApiMessage[];
|
messages: ApiMessage[];
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import { getGlobal } from '../global';
|
import { getGlobal } from '../global';
|
||||||
|
|
||||||
|
import type { ScrollTargetPosition } from '../types';
|
||||||
import { FocusDirection } from '../types';
|
import { FocusDirection } from '../types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -49,7 +50,7 @@ export function restartCurrentScrollAnimation() {
|
|||||||
function createMutateFunction(
|
function createMutateFunction(
|
||||||
container: HTMLElement,
|
container: HTMLElement,
|
||||||
element: HTMLElement,
|
element: HTMLElement,
|
||||||
position: ScrollLogicalPosition | 'centerOrTop',
|
position: ScrollTargetPosition,
|
||||||
margin = 0,
|
margin = 0,
|
||||||
maxDistance = FAST_SMOOTH_MAX_DISTANCE,
|
maxDistance = FAST_SMOOTH_MAX_DISTANCE,
|
||||||
forceDirection?: FocusDirection,
|
forceDirection?: FocusDirection,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user