Multitab: Follow-up

This commit is contained in:
Alexander Zinchuk 2023-01-30 15:55:35 +01:00
parent 847139e7ce
commit cdad1bc867
10 changed files with 106 additions and 27 deletions

View File

@ -186,7 +186,7 @@ export default withGlobal<OwnProps>(
shouldSkipHistoryAnimations: tabState.shouldSkipHistoryAnimations, shouldSkipHistoryAnimations: tabState.shouldSkipHistoryAnimations,
uiReadyState: tabState.uiReadyState, uiReadyState: tabState.uiReadyState,
isRightColumnShown: selectIsRightColumnShown(global, isMobile), isRightColumnShown: selectIsRightColumnShown(global, isMobile),
leftColumnWidth: tabState.leftColumnWidth, leftColumnWidth: global.leftColumnWidth,
theme, theme,
}; };
}, },

View File

@ -488,11 +488,11 @@ export default memo(withGlobal(
date, date,
}, },
shouldSkipHistoryAnimations, shouldSkipHistoryAnimations,
leftColumnWidth,
activeChatFolder, activeChatFolder,
nextSettingsScreen, nextSettingsScreen,
} = tabState; } = tabState;
const { const {
leftColumnWidth,
currentUserId, currentUserId,
passcode: { passcode: {
hasPasscode, hasPasscode,

View File

@ -152,7 +152,7 @@ const ChatList: FC<OwnProps> = ({
const expendedOffsetTop = currentChatListHeight; const expendedOffsetTop = currentChatListHeight;
const collapsedOffsetTop = (viewportOffset + i) * CHAT_HEIGHT_PX; 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 ( return (
<Chat <Chat

View File

@ -35,6 +35,7 @@ import {
selectTabState, selectTabState,
} from '../../selectors'; } from '../../selectors';
import { init as initFolderManager } from '../../../util/folderManager'; import { init as initFolderManager } from '../../../util/folderManager';
import { updateTabState } from '../../reducers/tabs';
const RELEASE_STATUS_TIMEOUT = 15000; // 15 sec; const RELEASE_STATUS_TIMEOUT = 15000; // 15 sec;
@ -96,6 +97,8 @@ async function loadAndReplaceMessages<T extends GlobalState>(global: T, actions:
global = getGlobal(); global = getGlobal();
let wasReset = false;
for (const { id: tabId } of Object.values(global.byTabId)) { for (const { id: tabId } of Object.values(global.byTabId)) {
global = getGlobal(); global = getGlobal();
const { chatId: currentChatId, threadId: currentThreadId } = selectCurrentMessageList(global, tabId) || {}; const { chatId: currentChatId, threadId: currentThreadId } = selectCurrentMessageList(global, tabId) || {};
@ -137,9 +140,32 @@ async function loadAndReplaceMessages<T extends GlobalState>(global: T, actions:
const byId = buildCollectionByKey(allMessages, 'id'); const byId = buildCollectionByKey(allMessages, 'id');
const listedIds = Object.keys(byId).map(Number); 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 = addChatMessagesById(global, activeCurrentChatId, byId);
global = updateListedIds(global, activeCurrentChatId, activeThreadId, listedIds); 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 = updateChats(global, buildCollectionByKey(result.chats, 'id'));
global = updateUsers(global, buildCollectionByKey(result.users, 'id')); global = updateUsers(global, buildCollectionByKey(result.users, 'id'));
global = updateThreadInfos(global, activeCurrentChatId, result.repliesThreadInfos); global = updateThreadInfos(global, activeCurrentChatId, result.repliesThreadInfos);
@ -176,6 +202,14 @@ async function loadAndReplaceMessages<T extends GlobalState>(global: T, actions:
byChatId: {}, 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 }) => { Object.values(global.byTabId).forEach(({ id: tabId }) => {

View File

@ -749,7 +749,7 @@ addActionHandler('openReactorListModal', (global, actions, payload): ActionRetur
}); });
addActionHandler('closeReactorListModal', (global, actions, payload): ActionReturnType => { addActionHandler('closeReactorListModal', (global, actions, payload): ActionReturnType => {
const { tabId = getCurrentTabId() } = payload; const { tabId = getCurrentTabId() } = payload || {};
return updateTabState(global, { return updateTabState(global, {
reactorModal: undefined, reactorModal: undefined,
@ -765,7 +765,7 @@ addActionHandler('openSeenByModal', (global, actions, payload): ActionReturnType
}); });
addActionHandler('closeSeenByModal', (global, actions, payload): ActionReturnType => { addActionHandler('closeSeenByModal', (global, actions, payload): ActionReturnType => {
const { tabId = getCurrentTabId() } = payload; const { tabId = getCurrentTabId() } = payload || {};
return updateTabState(global, { return updateTabState(global, {
seenByModal: undefined, seenByModal: undefined,

View File

@ -32,18 +32,19 @@ addActionHandler('toggleChatInfo', (global, actions, payload): ActionReturnType
}); });
addActionHandler('setLeftColumnWidth', (global, actions, payload): ActionReturnType => { addActionHandler('setLeftColumnWidth', (global, actions, payload): ActionReturnType => {
const { leftColumnWidth, tabId = getCurrentTabId() } = payload; const { leftColumnWidth } = payload;
return updateTabState(global, { return {
...global,
leftColumnWidth, leftColumnWidth,
}, tabId); };
}); });
addActionHandler('resetLeftColumnWidth', (global, actions, payload): ActionReturnType => { addActionHandler('resetLeftColumnWidth', (global): ActionReturnType => {
const { tabId = getCurrentTabId() } = payload || {}; return {
return updateTabState(global, { ...global,
leftColumnWidth: undefined, leftColumnWidth: undefined,
}, tabId); };
}); });
addActionHandler('toggleManagement', (global, actions, payload): ActionReturnType => { addActionHandler('toggleManagement', (global, actions, payload): ActionReturnType => {

View File

@ -383,6 +383,7 @@ export function serializeGlobal<T extends GlobalState>(global: T) {
'push', 'push',
'serviceNotifications', 'serviceNotifications',
'attachmentSettings', 'attachmentSettings',
'leftColumnWidth',
]), ]),
customEmojis: reduceCustomEmojis(global), customEmojis: reduceCustomEmojis(global),
users: reduceUsers(global), users: reduceUsers(global),
@ -510,12 +511,21 @@ function reduceMessages<T extends GlobalState>(global: T): GlobalState['messages
} }
const viewportIdsToSave = unique(Object.values(threadsToSave).flatMap((thread) => thread.lastViewportIds || [])); 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) : []; ? 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] = { byChatId[chatId] = {
byId: pick(current.byId, viewportIdsToSave.concat(lastMessagesToSave)), byId,
threadsById: threadsToSave, threadsById,
}; };
}); });

View File

@ -6,7 +6,7 @@ import { INITIAL_GLOBAL_STATE, INITIAL_TAB_STATE } from './initialState';
import { IS_MOCKED_CLIENT } from '../config'; import { IS_MOCKED_CLIENT } from '../config';
import { initCache, loadCache } from './cache'; import { initCache, loadCache } from './cache';
import { cloneDeep } from '../util/iteratees'; import { cloneDeep } from '../util/iteratees';
import { replaceTabThreadParam, updatePasscodeSettings } from './reducers'; import { replaceTabThreadParam, replaceThreadParam, updatePasscodeSettings } from './reducers';
import { clearStoredSession } from '../util/sessions'; import { clearStoredSession } from '../util/sessions';
import { parseLocationHash } from '../util/routing'; import { parseLocationHash } from '../util/routing';
import { MAIN_THREAD_ID } from '../api/types'; import { MAIN_THREAD_ID } from '../api/types';
@ -14,7 +14,7 @@ import { selectTabState, selectThreadParam } from './selectors';
import { Bundles, loadBundle } from '../util/moduleLoader'; import { Bundles, loadBundle } from '../util/moduleLoader';
import { getCurrentTabId, reestablishMasterToSelf } from '../util/establishMultitabRole'; import { getCurrentTabId, reestablishMasterToSelf } from '../util/establishMultitabRole';
import { updateTabState } from './reducers/tabs'; import { updateTabState } from './reducers/tabs';
import type { ActionReturnType } from './types'; import type { ActionReturnType, GlobalState } from './types';
import { getIsMobile } from '../hooks/useAppLayout'; import { getIsMobile } from '../hooks/useAppLayout';
initCache(); initCache();
@ -61,15 +61,49 @@ addActionHandler('init', (global, actions, payload): ActionReturnType => {
} }
Object.keys(global.messages.byChatId).forEach((chatId) => { 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 = replaceTabThreadParam(
global, global,
chatId, chatId,
MAIN_THREAD_ID, MAIN_THREAD_ID,
'viewportIds', 'viewportIds',
selectThreadParam(global, chatId, MAIN_THREAD_ID, 'lastViewportIds'), lastViewportIds,
tabId, 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; const parsedMessageList = !getIsMobile() ? parseLocationHash() : undefined;
if (global.authState !== 'authorizationStateReady' if (global.authState !== 'authorizationStateReady'
@ -101,8 +135,8 @@ addActionHandler('requestMasterAndCallAction', async (
if (global.phoneCall || global.groupCalls.activeGroupCallId) { if (global.phoneCall || global.groupCalls.activeGroupCallId) {
await loadBundle(Bundles.Calls); await loadBundle(Bundles.Calls);
actions.hangUp({ tabId }); if ('hangUp' in actions) actions.hangUp({ tabId });
actions.leaveGroupCall({ tabId }); if ('leaveGroupCall' in actions) actions.leaveGroupCall({ tabId });
} else { } else {
reestablishMasterToSelf(); reestablishMasterToSelf();
} }

View File

@ -34,7 +34,7 @@ function getLeftColumnWidth(windowWidth: number) {
export function subtractXForEmojiInteraction(global: GlobalState, x: number) { export function subtractXForEmojiInteraction(global: GlobalState, x: number) {
const tabState = selectTabState(global); const tabState = selectTabState(global);
return x - ((tabState.isLeftColumnShown && !getIsMobile()) return x - ((tabState.isLeftColumnShown && !getIsMobile())
? tabState.leftColumnWidth || getLeftColumnWidth(windowSize.get().width) ? global.leftColumnWidth || getLeftColumnWidth(windowSize.get().width)
: 0); : 0);
} }

View File

@ -173,7 +173,6 @@ export type TabState = {
uiReadyState: 0 | 1 | 2; uiReadyState: 0 | 1 | 2;
shouldInit: boolean; shouldInit: boolean;
shouldSkipHistoryAnimations?: boolean; shouldSkipHistoryAnimations?: boolean;
leftColumnWidth?: number;
gifSearch: { gifSearch: {
query?: string; query?: string;
@ -534,6 +533,7 @@ export type GlobalState = {
lastSyncTime?: number; lastSyncTime?: number;
serverTimeOffset: number; serverTimeOffset: number;
blurredTabTokens: number[]; blurredTabTokens: number[];
leftColumnWidth?: number;
initialUnreadNotifications?: number; initialUnreadNotifications?: number;
notificationIndex?: number; notificationIndex?: number;
allNotificationsCount?: number; allNotificationsCount?: number;
@ -1315,8 +1315,8 @@ export interface ActionPayloads {
disableHistoryAnimations: WithTabId | undefined; disableHistoryAnimations: WithTabId | undefined;
setLeftColumnWidth: { setLeftColumnWidth: {
leftColumnWidth: number; leftColumnWidth: number;
} & WithTabId; };
resetLeftColumnWidth: WithTabId | undefined; resetLeftColumnWidth: undefined;
copySelectedMessages: WithTabId; copySelectedMessages: WithTabId;
copyMessagesByIds: { copyMessagesByIds: {
@ -1326,8 +1326,8 @@ export interface ActionPayloads {
chatId: string; chatId: string;
messageId: number; messageId: number;
} & WithTabId; } & WithTabId;
closeSeenByModal: WithTabId; closeSeenByModal: WithTabId | undefined;
closeReactorListModal: WithTabId; closeReactorListModal: WithTabId | undefined;
openReactorListModal: { openReactorListModal: {
chatId: string; chatId: string;
messageId: number; messageId: number;