Comments: Fix focus on top message (#3356)
This commit is contained in:
parent
86f8ad9e2f
commit
915dbe5736
@ -36,7 +36,6 @@ import {
|
|||||||
selectIsChatBotNotStarted,
|
selectIsChatBotNotStarted,
|
||||||
selectScrollOffset,
|
selectScrollOffset,
|
||||||
selectThreadTopMessageId,
|
selectThreadTopMessageId,
|
||||||
selectFirstMessageId,
|
|
||||||
selectChatScheduledMessages,
|
selectChatScheduledMessages,
|
||||||
selectCurrentMessageIds,
|
selectCurrentMessageIds,
|
||||||
selectIsCurrentUserPremium,
|
selectIsCurrentUserPremium,
|
||||||
@ -127,7 +126,6 @@ type StateProps = {
|
|||||||
isLoadingBotInfo?: boolean;
|
isLoadingBotInfo?: boolean;
|
||||||
botInfo?: ApiBotInfo;
|
botInfo?: ApiBotInfo;
|
||||||
threadTopMessageId?: number;
|
threadTopMessageId?: number;
|
||||||
threadFirstMessageId?: number;
|
|
||||||
hasLinkedChat?: boolean;
|
hasLinkedChat?: boolean;
|
||||||
lastSyncTime?: number;
|
lastSyncTime?: number;
|
||||||
topic?: ApiTopic;
|
topic?: ApiTopic;
|
||||||
@ -170,7 +168,6 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
|||||||
firstUnreadId,
|
firstUnreadId,
|
||||||
isComments,
|
isComments,
|
||||||
isViewportNewest,
|
isViewportNewest,
|
||||||
threadFirstMessageId,
|
|
||||||
isRestricted,
|
isRestricted,
|
||||||
restrictionReason,
|
restrictionReason,
|
||||||
focusingId,
|
focusingId,
|
||||||
@ -259,23 +256,11 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
|||||||
useNativeCopySelectedMessages(copyMessagesByIds);
|
useNativeCopySelectedMessages(copyMessagesByIds);
|
||||||
|
|
||||||
const messageGroups = useMemo(() => {
|
const messageGroups = useMemo(() => {
|
||||||
if (!messageIds || !messagesById) {
|
if (!messageIds?.length || !messagesById) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewportIds = (
|
const listedMessages = messageIds.map((id) => messagesById[id]).filter(Boolean);
|
||||||
threadTopMessageId
|
|
||||||
&& threadFirstMessageId !== threadTopMessageId
|
|
||||||
&& (!messageIds[0] || threadFirstMessageId === messageIds[0])
|
|
||||||
)
|
|
||||||
? [threadTopMessageId, ...messageIds]
|
|
||||||
: messageIds;
|
|
||||||
|
|
||||||
if (!viewportIds.length) {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
const listedMessages = viewportIds.map((id) => messagesById[id]).filter(Boolean);
|
|
||||||
|
|
||||||
// Service notifications have local IDs which may be not in sync with real message history
|
// Service notifications have local IDs which may be not in sync with real message history
|
||||||
const orderRule: (keyof ApiMessage)[] = type === 'scheduled' || isServiceNotificationsChat
|
const orderRule: (keyof ApiMessage)[] = type === 'scheduled' || isServiceNotificationsChat
|
||||||
@ -285,7 +270,7 @@ const MessageList: FC<OwnProps & StateProps> = ({
|
|||||||
return listedMessages.length
|
return listedMessages.length
|
||||||
? groupMessages(orderBy(listedMessages, orderRule), memoUnreadDividerBeforeIdRef.current)
|
? groupMessages(orderBy(listedMessages, orderRule), memoUnreadDividerBeforeIdRef.current)
|
||||||
: undefined;
|
: undefined;
|
||||||
}, [messageIds, messagesById, threadFirstMessageId, threadTopMessageId, type, isServiceNotificationsChat]);
|
}, [messageIds, messagesById, type, isServiceNotificationsChat]);
|
||||||
|
|
||||||
useInterval(() => {
|
useInterval(() => {
|
||||||
if (!messageIds || !messagesById || type === 'scheduled') {
|
if (!messageIds || !messagesById || type === 'scheduled') {
|
||||||
@ -746,7 +731,6 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
isComments: Boolean(threadInfo?.originChannelId),
|
isComments: Boolean(threadInfo?.originChannelId),
|
||||||
firstUnreadId: selectFirstUnreadId(global, chatId, threadId),
|
firstUnreadId: selectFirstUnreadId(global, chatId, threadId),
|
||||||
isViewportNewest: type !== 'thread' || selectIsViewportNewest(global, chatId, threadId),
|
isViewportNewest: type !== 'thread' || selectIsViewportNewest(global, chatId, threadId),
|
||||||
threadFirstMessageId: selectFirstMessageId(global, chatId, threadId),
|
|
||||||
focusingId,
|
focusingId,
|
||||||
isSelectModeActive: selectIsInSelectMode(global),
|
isSelectModeActive: selectIsInSelectMode(global),
|
||||||
isLoadingBotInfo,
|
isLoadingBotInfo,
|
||||||
|
|||||||
@ -45,7 +45,7 @@ import {
|
|||||||
selectReplyingToId,
|
selectReplyingToId,
|
||||||
selectTabState,
|
selectTabState,
|
||||||
selectTheme,
|
selectTheme,
|
||||||
selectThreadInfo,
|
selectThreadInfo, selectThreadTopMessageId,
|
||||||
} from '../../global/selectors';
|
} from '../../global/selectors';
|
||||||
import {
|
import {
|
||||||
getCanPostInChat,
|
getCanPostInChat,
|
||||||
@ -140,6 +140,7 @@ type StateProps = {
|
|||||||
shouldSendJoinRequest?: boolean;
|
shouldSendJoinRequest?: boolean;
|
||||||
lastSyncTime?: number;
|
lastSyncTime?: number;
|
||||||
pinnedIds?: number[];
|
pinnedIds?: number[];
|
||||||
|
topMessageId?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
function isImage(item: DataTransferItem) {
|
function isImage(item: DataTransferItem) {
|
||||||
@ -193,6 +194,7 @@ function MiddleColumn({
|
|||||||
shouldLoadFullChat,
|
shouldLoadFullChat,
|
||||||
lastSyncTime,
|
lastSyncTime,
|
||||||
pinnedIds,
|
pinnedIds,
|
||||||
|
topMessageId,
|
||||||
}: OwnProps & StateProps) {
|
}: OwnProps & StateProps) {
|
||||||
const {
|
const {
|
||||||
openChat,
|
openChat,
|
||||||
@ -226,7 +228,7 @@ function MiddleColumn({
|
|||||||
getCurrentPinnedIndexes,
|
getCurrentPinnedIndexes,
|
||||||
getLoadingPinnedId,
|
getLoadingPinnedId,
|
||||||
getForceNextPinnedInHeader,
|
getForceNextPinnedInHeader,
|
||||||
} = usePinnedMessage(chatId, threadId, pinnedIds);
|
} = usePinnedMessage(chatId, threadId, pinnedIds, topMessageId);
|
||||||
|
|
||||||
const isMobileSearchActive = isMobile && hasCurrentTextSearch;
|
const isMobileSearchActive = isMobile && hasCurrentTextSearch;
|
||||||
const closeAnimationDuration = isMobile ? LAYER_ANIMATION_DURATION_MS : undefined;
|
const closeAnimationDuration = isMobile ? LAYER_ANIMATION_DURATION_MS : undefined;
|
||||||
@ -720,6 +722,9 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
? selectChatMessage(global, audioChatId, audioMessageId)
|
? selectChatMessage(global, audioChatId, audioMessageId)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
const isCommentThread = threadId !== MAIN_THREAD_ID && !chat?.isForum;
|
||||||
|
const topMessageId = isCommentThread ? selectThreadTopMessageId(global, chatId, threadId) : undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
chatId,
|
chatId,
|
||||||
@ -737,10 +742,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
isPinnedMessageList,
|
isPinnedMessageList,
|
||||||
currentUserBannedRights: chat?.currentUserBannedRights,
|
currentUserBannedRights: chat?.currentUserBannedRights,
|
||||||
defaultBannedRights: chat?.defaultBannedRights,
|
defaultBannedRights: chat?.defaultBannedRights,
|
||||||
hasPinned: (
|
hasPinned: isCommentThread || Boolean(!isPinnedMessageList && pinnedIds?.length),
|
||||||
(threadId !== MAIN_THREAD_ID && !chat?.isForum)
|
|
||||||
|| Boolean(!isPinnedMessageList && pinnedIds?.length)
|
|
||||||
),
|
|
||||||
hasAudioPlayer: Boolean(audioMessage),
|
hasAudioPlayer: Boolean(audioMessage),
|
||||||
hasButtonInHeader: canStartBot || canRestartBot || canSubscribe || shouldSendJoinRequest,
|
hasButtonInHeader: canStartBot || canRestartBot || canSubscribe || shouldSendJoinRequest,
|
||||||
pinnedMessagesCount: pinnedIds ? pinnedIds.length : 0,
|
pinnedMessagesCount: pinnedIds ? pinnedIds.length : 0,
|
||||||
@ -753,6 +755,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
shouldSendJoinRequest,
|
shouldSendJoinRequest,
|
||||||
shouldLoadFullChat,
|
shouldLoadFullChat,
|
||||||
pinnedIds,
|
pinnedIds,
|
||||||
|
topMessageId,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(MiddleColumn));
|
)(MiddleColumn));
|
||||||
|
|||||||
@ -23,7 +23,9 @@ type PinnedIntersectionChangedParams = {
|
|||||||
|
|
||||||
export type PinnedIntersectionChangedCallback = (params: PinnedIntersectionChangedParams) => void;
|
export type PinnedIntersectionChangedCallback = (params: PinnedIntersectionChangedParams) => void;
|
||||||
|
|
||||||
export default function usePinnedMessage(chatId?: string, threadId?: number, pinnedIds?: number[]) {
|
export default function usePinnedMessage(
|
||||||
|
chatId?: string, threadId?: number, pinnedIds?: number[], topMessageId?: number,
|
||||||
|
) {
|
||||||
const [getCurrentPinnedIndexes, setCurrentPinnedIndexes] = useSignal<Record<string, number>>({});
|
const [getCurrentPinnedIndexes, setCurrentPinnedIndexes] = useSignal<Record<string, number>>({});
|
||||||
const [getForceNextPinnedInHeader, setForceNextPinnedInHeader] = useSignal<boolean | undefined>();
|
const [getForceNextPinnedInHeader, setForceNextPinnedInHeader] = useSignal<boolean | undefined>();
|
||||||
const viewportPinnedIdsRef = useRef<number[] | undefined>();
|
const viewportPinnedIdsRef = useRef<number[] | undefined>();
|
||||||
@ -133,7 +135,10 @@ export default function usePinnedMessage(chatId?: string, threadId?: number, pin
|
|||||||
if (!chatId || !threadId || !key || getLoadingPinnedId()) return false;
|
if (!chatId || !threadId || !key || getLoadingPinnedId()) return false;
|
||||||
|
|
||||||
const global = getGlobal();
|
const global = getGlobal();
|
||||||
if (!pinnedIds?.length) return false;
|
if (!pinnedIds?.length) {
|
||||||
|
// Focusing on a post in comments
|
||||||
|
return topMessageId === messageId;
|
||||||
|
}
|
||||||
|
|
||||||
const index = pinnedIds.indexOf(messageId);
|
const index = pinnedIds.indexOf(messageId);
|
||||||
const newPinnedIndex = cycleRestrict(pinnedIds.length, index + 1);
|
const newPinnedIndex = cycleRestrict(pinnedIds.length, index + 1);
|
||||||
|
|||||||
@ -68,7 +68,7 @@ import {
|
|||||||
selectDraft,
|
selectDraft,
|
||||||
selectEditingId,
|
selectEditingId,
|
||||||
selectEditingMessage,
|
selectEditingMessage,
|
||||||
selectEditingScheduledId,
|
selectEditingScheduledId, selectFirstMessageId,
|
||||||
selectFirstUnreadId,
|
selectFirstUnreadId,
|
||||||
selectFocusedMessageId,
|
selectFocusedMessageId,
|
||||||
selectForwardsCanBeSentToChat,
|
selectForwardsCanBeSentToChat,
|
||||||
@ -1020,6 +1020,12 @@ async function loadViewportMessages<T extends GlobalState>(
|
|||||||
const allMessages = ([] as ApiMessage[]).concat(messages, localMessages);
|
const allMessages = ([] as ApiMessage[]).concat(messages, localMessages);
|
||||||
const byId = buildCollectionByKey(allMessages, 'id');
|
const byId = buildCollectionByKey(allMessages, 'id');
|
||||||
const ids = Object.keys(byId).map(Number);
|
const ids = Object.keys(byId).map(Number);
|
||||||
|
const threadFirstMessageId = selectFirstMessageId(global, chatId, threadId) || {};
|
||||||
|
if (threadId
|
||||||
|
&& threadFirstMessageId !== threadId
|
||||||
|
&& (!ids[0] || threadFirstMessageId === ids[0])) {
|
||||||
|
ids.unshift(threadId);
|
||||||
|
}
|
||||||
|
|
||||||
global = addChatMessagesById(global, chatId, byId);
|
global = addChatMessagesById(global, chatId, byId);
|
||||||
global = isOutlying
|
global = isOutlying
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user