diff --git a/src/components/common/UiLoader.tsx b/src/components/common/UiLoader.tsx index 19c51ddff..8f19421a0 100644 --- a/src/components/common/UiLoader.tsx +++ b/src/components/common/UiLoader.tsx @@ -186,7 +186,7 @@ export default withGlobal( shouldSkipHistoryAnimations: tabState.shouldSkipHistoryAnimations, uiReadyState: tabState.uiReadyState, isRightColumnShown: selectIsRightColumnShown(global, isMobile), - leftColumnWidth: tabState.leftColumnWidth, + leftColumnWidth: global.leftColumnWidth, theme, }; }, diff --git a/src/components/left/LeftColumn.tsx b/src/components/left/LeftColumn.tsx index 3a0c8af08..25e98fe1a 100644 --- a/src/components/left/LeftColumn.tsx +++ b/src/components/left/LeftColumn.tsx @@ -488,11 +488,11 @@ export default memo(withGlobal( date, }, shouldSkipHistoryAnimations, - leftColumnWidth, activeChatFolder, nextSettingsScreen, } = tabState; const { + leftColumnWidth, currentUserId, passcode: { hasPasscode, diff --git a/src/components/left/main/ChatList.tsx b/src/components/left/main/ChatList.tsx index cf5e0164b..a6ca83222 100644 --- a/src/components/left/main/ChatList.tsx +++ b/src/components/left/main/ChatList.tsx @@ -152,7 +152,7 @@ const ChatList: FC = ({ const expendedOffsetTop = currentChatListHeight; const collapsedOffsetTop = (viewportOffset + i) * CHAT_HEIGHT_PX; - currentChatListHeight += (selectChat(global, id)!.isForum ? CHAT_HEIGHT_FORUM_PX : CHAT_HEIGHT_PX); + currentChatListHeight += (selectChat(global, id)?.isForum ? CHAT_HEIGHT_FORUM_PX : CHAT_HEIGHT_PX); return ( (global: T, actions: global = getGlobal(); + let wasReset = false; + for (const { id: tabId } of Object.values(global.byTabId)) { global = getGlobal(); const { chatId: currentChatId, threadId: currentThreadId } = selectCurrentMessageList(global, tabId) || {}; @@ -137,9 +140,32 @@ async function loadAndReplaceMessages(global: T, actions: const byId = buildCollectionByKey(allMessages, 'id'); const listedIds = Object.keys(byId).map(Number); + if (!wasReset) { + global = { + ...global, + messages: { + ...global.messages, + byChatId: {}, + }, + }; + // eslint-disable-next-line @typescript-eslint/no-loop-func + Object.values(global.byTabId).forEach(({ id: otherTabId }) => { + global = updateTabState(global, { + tabThreads: {}, + }, otherTabId); + }); + wasReset = true; + } + global = addChatMessagesById(global, activeCurrentChatId, byId); global = updateListedIds(global, activeCurrentChatId, activeThreadId, listedIds); - global = safeReplaceViewportIds(global, activeCurrentChatId, activeThreadId, listedIds, tabId); + // eslint-disable-next-line @typescript-eslint/no-loop-func + Object.values(global.byTabId).forEach(({ id: otherTabId }) => { + const { chatId: otherChatId, threadId: otherThreadId } = selectCurrentMessageList(global, otherTabId) || {}; + if (otherChatId === activeCurrentChatId && otherThreadId === activeThreadId) { + global = safeReplaceViewportIds(global, activeCurrentChatId, activeThreadId, listedIds, otherTabId); + } + }); global = updateChats(global, buildCollectionByKey(result.chats, 'id')); global = updateUsers(global, buildCollectionByKey(result.users, 'id')); global = updateThreadInfos(global, activeCurrentChatId, result.repliesThreadInfos); @@ -176,6 +202,14 @@ async function loadAndReplaceMessages(global: T, actions: byChatId: {}, }, }; + // eslint-disable-next-line @typescript-eslint/no-loop-func + Object.values(global.byTabId).forEach(({ id: otherTabId }) => { + global = updateTabState(global, { + tabThreads: {}, + }, otherTabId); + }); + + setGlobal(global); } Object.values(global.byTabId).forEach(({ id: tabId }) => { diff --git a/src/global/actions/ui/messages.ts b/src/global/actions/ui/messages.ts index 624b54223..0c259c05e 100644 --- a/src/global/actions/ui/messages.ts +++ b/src/global/actions/ui/messages.ts @@ -749,7 +749,7 @@ addActionHandler('openReactorListModal', (global, actions, payload): ActionRetur }); addActionHandler('closeReactorListModal', (global, actions, payload): ActionReturnType => { - const { tabId = getCurrentTabId() } = payload; + const { tabId = getCurrentTabId() } = payload || {}; return updateTabState(global, { reactorModal: undefined, @@ -765,7 +765,7 @@ addActionHandler('openSeenByModal', (global, actions, payload): ActionReturnType }); addActionHandler('closeSeenByModal', (global, actions, payload): ActionReturnType => { - const { tabId = getCurrentTabId() } = payload; + const { tabId = getCurrentTabId() } = payload || {}; return updateTabState(global, { seenByModal: undefined, diff --git a/src/global/actions/ui/misc.ts b/src/global/actions/ui/misc.ts index f0252eff1..92c6ee4ec 100644 --- a/src/global/actions/ui/misc.ts +++ b/src/global/actions/ui/misc.ts @@ -32,18 +32,19 @@ addActionHandler('toggleChatInfo', (global, actions, payload): ActionReturnType }); addActionHandler('setLeftColumnWidth', (global, actions, payload): ActionReturnType => { - const { leftColumnWidth, tabId = getCurrentTabId() } = payload; + const { leftColumnWidth } = payload; - return updateTabState(global, { + return { + ...global, leftColumnWidth, - }, tabId); + }; }); -addActionHandler('resetLeftColumnWidth', (global, actions, payload): ActionReturnType => { - const { tabId = getCurrentTabId() } = payload || {}; - return updateTabState(global, { +addActionHandler('resetLeftColumnWidth', (global): ActionReturnType => { + return { + ...global, leftColumnWidth: undefined, - }, tabId); + }; }); addActionHandler('toggleManagement', (global, actions, payload): ActionReturnType => { diff --git a/src/global/cache.ts b/src/global/cache.ts index 6b11143fa..236c0cfb4 100644 --- a/src/global/cache.ts +++ b/src/global/cache.ts @@ -383,6 +383,7 @@ export function serializeGlobal(global: T) { 'push', 'serviceNotifications', 'attachmentSettings', + 'leftColumnWidth', ]), customEmojis: reduceCustomEmojis(global), users: reduceUsers(global), @@ -510,12 +511,21 @@ function reduceMessages(global: T): GlobalState['messages } const viewportIdsToSave = unique(Object.values(threadsToSave).flatMap((thread) => thread.lastViewportIds || [])); - const lastMessagesToSave = chat?.topics + const lastMessageIdsToSave = chat?.topics ? Object.values(chat.topics).map(({ lastMessageId }) => lastMessageId) : []; + const byId = pick(current.byId, viewportIdsToSave.concat(lastMessageIdsToSave)); + const threadsById = Object.keys(threadsToSave).reduce((acc, key) => { + const t = threadsToSave[Number(key)]; + acc[Number(key)] = { + ...t, + listedIds: t.lastViewportIds, + }; + return acc; + }, {} as GlobalState['messages']['byChatId'][string]['threadsById']); byChatId[chatId] = { - byId: pick(current.byId, viewportIdsToSave.concat(lastMessagesToSave)), - threadsById: threadsToSave, + byId, + threadsById, }; }); diff --git a/src/global/init.ts b/src/global/init.ts index a21967f83..1bbac9d10 100644 --- a/src/global/init.ts +++ b/src/global/init.ts @@ -6,7 +6,7 @@ import { INITIAL_GLOBAL_STATE, INITIAL_TAB_STATE } from './initialState'; import { IS_MOCKED_CLIENT } from '../config'; import { initCache, loadCache } from './cache'; import { cloneDeep } from '../util/iteratees'; -import { replaceTabThreadParam, updatePasscodeSettings } from './reducers'; +import { replaceTabThreadParam, replaceThreadParam, updatePasscodeSettings } from './reducers'; import { clearStoredSession } from '../util/sessions'; import { parseLocationHash } from '../util/routing'; import { MAIN_THREAD_ID } from '../api/types'; @@ -14,7 +14,7 @@ import { selectTabState, selectThreadParam } from './selectors'; import { Bundles, loadBundle } from '../util/moduleLoader'; import { getCurrentTabId, reestablishMasterToSelf } from '../util/establishMultitabRole'; import { updateTabState } from './reducers/tabs'; -import type { ActionReturnType } from './types'; +import type { ActionReturnType, GlobalState } from './types'; import { getIsMobile } from '../hooks/useAppLayout'; initCache(); @@ -61,15 +61,49 @@ addActionHandler('init', (global, actions, payload): ActionReturnType => { } Object.keys(global.messages.byChatId).forEach((chatId) => { + const lastViewportIds = selectThreadParam(global, chatId, MAIN_THREAD_ID, 'lastViewportIds'); + // Check if migration from previous version is faulty + if (!lastViewportIds?.every((id) => global.messages.byChatId[chatId]?.byId[id])) { + global = replaceThreadParam(global, chatId, MAIN_THREAD_ID, 'lastViewportIds', undefined); + return; + } global = replaceTabThreadParam( global, chatId, MAIN_THREAD_ID, 'viewportIds', - selectThreadParam(global, chatId, MAIN_THREAD_ID, 'lastViewportIds'), + lastViewportIds, tabId, ); }); + + // Temporary state fix + Object.keys(global.messages.byChatId).forEach((chatId) => { + const threadsById = global.messages.byChatId[chatId].threadsById; + const fixedThreadsById = Object.keys(threadsById).reduce((acc, key) => { + const t = threadsById[Number(key)]; + acc[Number(key)] = { + ...t, + listedIds: t.lastViewportIds, + }; + return acc; + }, {} as GlobalState['messages']['byChatId'][string]['threadsById']); + + global = { + ...global, + messages: { + ...global.messages, + byChatId: { + ...global.messages.byChatId, + [chatId]: { + ...global.messages.byChatId[chatId], + threadsById: fixedThreadsById, + }, + }, + }, + }; + }); + const parsedMessageList = !getIsMobile() ? parseLocationHash() : undefined; if (global.authState !== 'authorizationStateReady' @@ -101,8 +135,8 @@ addActionHandler('requestMasterAndCallAction', async ( if (global.phoneCall || global.groupCalls.activeGroupCallId) { await loadBundle(Bundles.Calls); - actions.hangUp({ tabId }); - actions.leaveGroupCall({ tabId }); + if ('hangUp' in actions) actions.hangUp({ tabId }); + if ('leaveGroupCall' in actions) actions.leaveGroupCall({ tabId }); } else { reestablishMasterToSelf(); } diff --git a/src/global/reducers/reactions.ts b/src/global/reducers/reactions.ts index 0aa7663d6..6f2d5aa54 100644 --- a/src/global/reducers/reactions.ts +++ b/src/global/reducers/reactions.ts @@ -34,7 +34,7 @@ function getLeftColumnWidth(windowWidth: number) { export function subtractXForEmojiInteraction(global: GlobalState, x: number) { const tabState = selectTabState(global); return x - ((tabState.isLeftColumnShown && !getIsMobile()) - ? tabState.leftColumnWidth || getLeftColumnWidth(windowSize.get().width) + ? global.leftColumnWidth || getLeftColumnWidth(windowSize.get().width) : 0); } diff --git a/src/global/types.ts b/src/global/types.ts index 0ff7c7e4b..45d9a4ae4 100644 --- a/src/global/types.ts +++ b/src/global/types.ts @@ -173,7 +173,6 @@ export type TabState = { uiReadyState: 0 | 1 | 2; shouldInit: boolean; shouldSkipHistoryAnimations?: boolean; - leftColumnWidth?: number; gifSearch: { query?: string; @@ -534,6 +533,7 @@ export type GlobalState = { lastSyncTime?: number; serverTimeOffset: number; blurredTabTokens: number[]; + leftColumnWidth?: number; initialUnreadNotifications?: number; notificationIndex?: number; allNotificationsCount?: number; @@ -1315,8 +1315,8 @@ export interface ActionPayloads { disableHistoryAnimations: WithTabId | undefined; setLeftColumnWidth: { leftColumnWidth: number; - } & WithTabId; - resetLeftColumnWidth: WithTabId | undefined; + }; + resetLeftColumnWidth: undefined; copySelectedMessages: WithTabId; copyMessagesByIds: { @@ -1326,8 +1326,8 @@ export interface ActionPayloads { chatId: string; messageId: number; } & WithTabId; - closeSeenByModal: WithTabId; - closeReactorListModal: WithTabId; + closeSeenByModal: WithTabId | undefined; + closeReactorListModal: WithTabId | undefined; openReactorListModal: { chatId: string; messageId: number;