From ea0da789ce1c41b343e2eb41f0a1cd2a0440aeb4 Mon Sep 17 00:00:00 2001 From: Alexander Zinchuk Date: Tue, 31 May 2022 22:30:52 +0400 Subject: [PATCH] Message List: Various fixes for local message IDs --- src/api/gramjs/apiBuilders/messages.ts | 11 ++++++----- src/components/middle/MessageList.tsx | 4 ++-- src/components/middle/MessageListContent.tsx | 8 +++----- src/components/middle/hooks/useScrollHooks.ts | 4 ++-- src/components/middle/message/Album.tsx | 4 ++-- src/config.ts | 2 +- src/global/actions/api/messages.ts | 4 ++-- src/global/actions/apiUpdaters/messages.ts | 4 ++-- src/global/helpers/messages.ts | 4 ++-- src/global/selectors/messages.ts | 9 +++++---- 10 files changed, 27 insertions(+), 27 deletions(-) diff --git a/src/api/gramjs/apiBuilders/messages.ts b/src/api/gramjs/apiBuilders/messages.ts index e4338b16f..15eff055b 100644 --- a/src/api/gramjs/apiBuilders/messages.ts +++ b/src/api/gramjs/apiBuilders/messages.ts @@ -35,7 +35,6 @@ import type { import { DELETED_COMMENTS_CHANNEL_ID, - LOCAL_MESSAGE_ID_BASE, SERVICE_NOTIFICATIONS_USER_ID, SPONSORED_MESSAGE_CACHE_MS, SUPPORTED_AUDIO_CONTENT_TYPES, @@ -56,7 +55,9 @@ import { buildApiCallDiscardReason } from './calls'; const LOCAL_MEDIA_UPLOADING_TEMP_ID = 'temp'; const INPUT_WAVEFORM_LENGTH = 63; -let localMessageCounter = LOCAL_MESSAGE_ID_BASE; +let localMessageCounter = 0; +const getNextLocalMessageId = () => parseFloat(`${Date.now()}.${localMessageCounter++}`); + let currentUserId!: string; export function setMessageBuilderCurrentUserId(_currentUserId: string) { @@ -120,7 +121,7 @@ export function buildApiMessageFromNotification( notification: GramJs.UpdateServiceNotification, currentDate: number, ): ApiMessage { - const localId = localMessageCounter++; + const localId = getNextLocalMessageId(); const content = buildMessageContent(notification); return { @@ -1148,7 +1149,7 @@ export function buildLocalMessage( sendAs?: ApiChat | ApiUser, serverTimeOffset = 0, ): ApiMessage { - const localId = localMessageCounter++; + const localId = getNextLocalMessageId(); const media = attachment && buildUploadingMedia(attachment); const isChannel = chat.type === 'chatTypeChannel'; @@ -1186,7 +1187,7 @@ export function buildLocalForwardedMessage( serverTimeOffset: number, scheduledAt?: number, ): ApiMessage { - const localId = localMessageCounter++; + const localId = getNextLocalMessageId(); const { content, chatId: fromChatId, diff --git a/src/components/middle/MessageList.tsx b/src/components/middle/MessageList.tsx index 6d8050c28..8eb5bf380 100644 --- a/src/components/middle/MessageList.tsx +++ b/src/components/middle/MessageList.tsx @@ -9,7 +9,7 @@ import { MAIN_THREAD_ID } from '../../api/types'; import type { MessageListType } from '../../global/types'; import { LoadMoreDirection } from '../../types'; -import { ANIMATION_END_DELAY, LOCAL_MESSAGE_ID_BASE, MESSAGE_LIST_SLICE } from '../../config'; +import { ANIMATION_END_DELAY, LOCAL_MESSAGE_MIN_ID, MESSAGE_LIST_SLICE } from '../../config'; import { selectChatMessages, selectIsViewportNewest, @@ -303,7 +303,7 @@ const MessageList: FC = ({ } // Loading history while sending a message can return the same message and cause ambiguity - const isLastMessageLocal = messageIds && messageIds[messageIds.length - 1] >= LOCAL_MESSAGE_ID_BASE; + const isLastMessageLocal = messageIds && messageIds[messageIds.length - 1] > LOCAL_MESSAGE_MIN_ID; if (isLastMessageLocal) { return; } diff --git a/src/components/middle/MessageListContent.tsx b/src/components/middle/MessageListContent.tsx index fb8654448..90807836e 100644 --- a/src/components/middle/MessageListContent.tsx +++ b/src/components/middle/MessageListContent.tsx @@ -9,7 +9,7 @@ import buildClassName from '../../util/buildClassName'; import { compact } from '../../util/iteratees'; import { formatHumanDate } from '../../util/dateFormat'; import { - getMessageHtmlId, getMessageOriginalId, isActionMessage, isOwnMessage, + getMessageHtmlId, getMessageOriginalId, isActionMessage, isOwnMessage, isServiceNotificationMessage, } from '../../global/helpers'; import useLang from '../../hooks/useLang'; import type { MessageDateGroup } from './helpers/groupMessages'; @@ -182,10 +182,8 @@ const MessageListContent: FC = ({ currentDocumentGroupId = documentGroupId; const originalId = getMessageOriginalId(message); - // Scheduled messages can have local IDs in the middle of the list, - // and keys should be ordered, so we prefix it with a date. - // However, this may lead to issues if server date is not synchronized with the local one. - const key = type !== 'scheduled' ? originalId : `${message.date}_${originalId}`; + // Service notifications saved in cache in previous versions may share the same `previousLocalId` + const key = isServiceNotificationMessage(message) ? `${message.date}_${originalId}` : originalId; return compact([ message.id === memoUnreadDividerBeforeIdRef.current && unreadDivider, diff --git a/src/components/middle/hooks/useScrollHooks.ts b/src/components/middle/hooks/useScrollHooks.ts index cb87d1a91..520701e20 100644 --- a/src/components/middle/hooks/useScrollHooks.ts +++ b/src/components/middle/hooks/useScrollHooks.ts @@ -5,7 +5,7 @@ import { useMemo, useRef } from '../../../lib/teact/teact'; import { LoadMoreDirection } from '../../../types'; import type { MessageListType } from '../../../global/types'; -import { LOCAL_MESSAGE_ID_BASE, MESSAGE_LIST_SLICE } from '../../../config'; +import { LOCAL_MESSAGE_MIN_ID, MESSAGE_LIST_SLICE } from '../../../config'; import { IS_SCROLL_PATCH_NEEDED, MESSAGE_LIST_SENSITIVE_AREA } from '../../../util/environment'; import { debounce } from '../../../util/schedulers'; import { useIntersectionObserver, useOnIntersect } from '../../../hooks/useIntersectionObserver'; @@ -84,7 +84,7 @@ export default function useScrollHooks( } // Loading history while sending a message can return the same message and cause ambiguity - const isFirstMessageLocal = messageIds[0] >= LOCAL_MESSAGE_ID_BASE; + const isFirstMessageLocal = messageIds[0] > LOCAL_MESSAGE_MIN_ID; if (isFirstMessageLocal) { return; } diff --git a/src/components/middle/message/Album.tsx b/src/components/middle/message/Album.tsx index c71a2f45e..569f9bb12 100644 --- a/src/components/middle/message/Album.tsx +++ b/src/components/middle/message/Album.tsx @@ -7,7 +7,7 @@ import type { IAlbum, ISettings } from '../../../types'; import type { IAlbumLayout } from './helpers/calculateAlbumLayout'; import { AlbumRectPart } from './helpers/calculateAlbumLayout'; -import { getMessageContent, getMessageHtmlId } from '../../../global/helpers'; +import { getMessageContent, getMessageHtmlId, getMessageOriginalId } from '../../../global/helpers'; import { getActions, getGlobal, withGlobal } from '../../../global'; import withSelectControl from './hocs/withSelectControl'; import type { ObserveFn } from '../../../hooks/useIntersectionObserver'; @@ -66,7 +66,7 @@ const Album: FC = ({ function renderAlbumMessage(message: ApiMessage, index: number) { const { photo, video } = getMessageContent(message); - const fileUpload = uploadsById[message.previousLocalId || message.id]; + const fileUpload = uploadsById[getMessageOriginalId(message)]; const uploadProgress = fileUpload?.progress; const { dimensions, sides } = albumLayout.layout[index]; diff --git a/src/config.ts b/src/config.ts index 3cff920fb..302676c3d 100644 --- a/src/config.ts +++ b/src/config.ts @@ -107,7 +107,7 @@ export const MOBILE_SCREEN_MAX_WIDTH = 600; // px export const MOBILE_SCREEN_LANDSCAPE_MAX_WIDTH = 950; // px export const MOBILE_SCREEN_LANDSCAPE_MAX_HEIGHT = 450; // px -export const LOCAL_MESSAGE_ID_BASE = 1e9; +export const LOCAL_MESSAGE_MIN_ID = 1e11; // `Date.now()` is always used as base export const TMP_CHAT_ID = '0'; export const ANIMATION_END_DELAY = 100; diff --git a/src/global/actions/api/messages.ts b/src/global/actions/api/messages.ts index 11f897a38..ef72aa970 100644 --- a/src/global/actions/api/messages.ts +++ b/src/global/actions/api/messages.ts @@ -68,7 +68,7 @@ import { selectSponsoredMessage, } from '../../selectors'; import { debounce, onTickEnd, rafPromise } from '../../../util/schedulers'; -import { isServiceNotificationMessage } from '../../helpers'; +import { getMessageOriginalId, isServiceNotificationMessage } from '../../helpers'; import { getTranslation } from '../../../util/langProvider'; const uploadProgressCallbacks = new Map(); @@ -302,7 +302,7 @@ addActionHandler('editMessage', (global, actions, payload) => { addActionHandler('cancelSendingMessage', (global, actions, payload) => { const { chatId, messageId } = payload!; const message = selectChatMessage(global, chatId, messageId); - const progressCallback = message && uploadProgressCallbacks.get(message.previousLocalId || message.id); + const progressCallback = message && uploadProgressCallbacks.get(getMessageOriginalId(message)); if (progressCallback) { cancelApiProgress(progressCallback); } diff --git a/src/global/actions/apiUpdaters/messages.ts b/src/global/actions/apiUpdaters/messages.ts index e229e4c85..5eb1386fc 100644 --- a/src/global/actions/apiUpdaters/messages.ts +++ b/src/global/actions/apiUpdaters/messages.ts @@ -47,7 +47,7 @@ import { selectLocalAnimatedEmoji, } from '../../selectors'; import { - getMessageContent, isUserId, isMessageLocal, getMessageText, checkIfHasUnreadReactions, + getMessageContent, isUserId, isMessageLocal, getMessageText, checkIfHasUnreadReactions, getMessageOriginalId, } from '../../helpers'; import { onTickEnd } from '../../../util/schedulers'; import { updateUnreadReactions } from '../../reducers/reactions'; @@ -665,7 +665,7 @@ function updateChatLastMessage( if (currentLastMessage && !force) { const isSameOrNewer = ( - currentLastMessage.id === message.id || currentLastMessage.id === message.previousLocalId + currentLastMessage.id === getMessageOriginalId(message) ) || message.id > currentLastMessage.id; if (!isSameOrNewer) { diff --git a/src/global/helpers/messages.ts b/src/global/helpers/messages.ts index 1a36d9169..0f3af05f2 100644 --- a/src/global/helpers/messages.ts +++ b/src/global/helpers/messages.ts @@ -6,7 +6,7 @@ import type { LangFn } from '../../hooks/useLang'; import { CONTENT_NOT_SUPPORTED, - LOCAL_MESSAGE_ID_BASE, + LOCAL_MESSAGE_MIN_ID, RE_LINK_TEMPLATE, SERVICE_NOTIFICATIONS_USER_ID, } from '../../config'; @@ -175,7 +175,7 @@ export function getSendingState(message: ApiMessage) { } export function isMessageLocal(message: ApiMessage) { - return message.id >= LOCAL_MESSAGE_ID_BASE; + return message.id > LOCAL_MESSAGE_MIN_ID; } export function isHistoryClearMessage(message: ApiMessage) { diff --git a/src/global/selectors/messages.ts b/src/global/selectors/messages.ts index f5fb7814c..0262fdab9 100644 --- a/src/global/selectors/messages.ts +++ b/src/global/selectors/messages.ts @@ -9,7 +9,7 @@ import { MAIN_THREAD_ID, } from '../../api/types'; -import { LOCAL_MESSAGE_ID_BASE, REPLIES_USER_ID, SERVICE_NOTIFICATIONS_USER_ID } from '../../config'; +import { LOCAL_MESSAGE_MIN_ID, REPLIES_USER_ID, SERVICE_NOTIFICATIONS_USER_ID } from '../../config'; import { selectChat, selectChatBot, selectIsChatWithBot, selectIsChatWithSelf, } from './chats'; @@ -37,6 +37,7 @@ import { getMessageVoice, getMessageDocument, getMessageWebPagePhoto, + getMessageOriginalId, } from '../helpers'; import { findLast } from '../../util/iteratees'; import { selectIsStickerFavorite } from './symbols'; @@ -253,7 +254,7 @@ export function selectIsViewportNewest(global: GlobalState, chatId: string, thre } // Edge case: outgoing `lastMessage` is updated with a delay to optimize animation - if (lastMessageId >= LOCAL_MESSAGE_ID_BASE && !selectChatMessage(global, chatId, lastMessageId)) { + if (lastMessageId > LOCAL_MESSAGE_MIN_ID && !selectChatMessage(global, chatId, lastMessageId)) { return true; } @@ -310,7 +311,7 @@ export function selectFocusedMessageId(global: GlobalState, chatId: string) { export function selectIsMessageFocused(global: GlobalState, message: ApiMessage) { const focusedId = selectFocusedMessageId(global, message.chatId); - return focusedId ? focusedId === message.id || focusedId === message.previousLocalId : false; + return focusedId ? focusedId === getMessageOriginalId(message) : false; } export function selectIsMessageUnread(global: GlobalState, message: ApiMessage) { @@ -554,7 +555,7 @@ export function selectActiveDownloadIds(global: GlobalState, chatId: string) { } export function selectUploadProgress(global: GlobalState, message: ApiMessage) { - return global.fileUploads.byMessageLocalId[message.previousLocalId || message.id]?.progress; + return global.fileUploads.byMessageLocalId[getMessageOriginalId(message)]?.progress; } export function selectRealLastReadId(global: GlobalState, chatId: string, threadId: number) {