From 76ee31229e4bb700ff8ce81b89244bb2ec68024d Mon Sep 17 00:00:00 2001 From: zubiden <19638254+zubiden@users.noreply.github.com> Date: Wed, 23 Apr 2025 18:59:31 +0200 Subject: [PATCH] Multi-Account: Fix cache load (#5854) --- src/components/App.tsx | 31 +++++++++++++------ .../middle/message/ActionMessage.tsx | 3 +- src/global/actions/ui/passcode.ts | 2 ++ src/global/cache.ts | 8 +++-- src/global/initialState.ts | 20 ++++++------ src/global/reducers/settings.ts | 22 +++++++------ src/global/selectors/settings.ts | 5 ++- src/global/selectors/ui.ts | 2 +- src/global/types/globalState.ts | 3 ++ src/global/types/sharedState.ts | 2 -- src/serviceWorker/progressive.ts | 18 +++++++---- src/util/cacheApi.ts | 1 + src/util/multiaccount.ts | 5 +-- src/util/passcode.ts | 4 +++ src/util/sessions.ts | 2 +- 15 files changed, 83 insertions(+), 45 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 0d9b83bce..e194993ba 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -14,8 +14,9 @@ import { IS_INSTALL_PROMPT_SUPPORTED, PLATFORM_ENV } from '../util/browser/windo import buildClassName from '../util/buildClassName'; import { setupBeforeInstallPrompt } from '../util/installPrompt'; import { ACCOUNT_SLOT, getAccountsInfo, getAccountSlotUrl } from '../util/multiaccount'; +import { hasEncryptedSession } from '../util/passcode'; import { getInitialLocationHash, parseInitialLocationHash } from '../util/routing'; -import { hasStoredSession } from '../util/sessions'; +import { checkSessionLocked, hasStoredSession } from '../util/sessions'; import { updateSizes } from '../util/windowSize'; import useAppLayout from '../hooks/useAppLayout'; @@ -76,15 +77,27 @@ const App: FC = ({ // If there is no stored session on first slot, navigate to any other slot with stored session if (!hasStoredSession() && !ACCOUNT_SLOT && !hash) { const accounts = getAccountsInfo(); - Object.keys(accounts).forEach((key) => { - const slot = Number(key); - const account = accounts[slot]; - if (account) { - const url = getAccountSlotUrl(slot); - window.location.href = `${url}#${hash || 'login'}`; - } - }); + Object.keys(accounts) + .map(Number) + .sort((a, b) => b - a) + .forEach((key) => { + const slot = Number(key); + const account = accounts[slot]; + if (account) { + const url = getAccountSlotUrl(slot); + window.location.href = `${url}#${hash || 'login'}`; + } + }); } + + // TODO[Passcode]: Remove when multiacc passcode is implemented + const checkMultiaccPasscode = async () => { + if (checkSessionLocked() && ACCOUNT_SLOT && await hasEncryptedSession()) { + const url = getAccountSlotUrl(1); + window.location.href = url; + } + }; + checkMultiaccPasscode(); }, []); // Prevent drop on elements that do not accept it diff --git a/src/components/middle/message/ActionMessage.tsx b/src/components/middle/message/ActionMessage.tsx index 616cc8e1a..c828f6125 100644 --- a/src/components/middle/message/ActionMessage.tsx +++ b/src/components/middle/message/ActionMessage.tsx @@ -27,7 +27,6 @@ import { selectTabState, selectTheme, } from '../../../global/selectors'; -import { selectSharedSettings } from '../../../global/selectors/sharedState'; import { IS_ANDROID, IS_ELECTRON, IS_FLUID_BACKGROUND_SUPPORTED } from '../../../util/browser/windowEnvironment'; import buildClassName from '../../../util/buildClassName'; import { isLocalMessageId } from '../../../util/keys/messageKey'; @@ -461,7 +460,7 @@ const ActionMessage = ({ export default memo(withGlobal( (global, { message, threadId }): StateProps => { const tabState = selectTabState(global); - const { themes } = selectSharedSettings(global); + const { themes } = global.settings; const chat = selectChat(global, message.chatId); diff --git a/src/global/actions/ui/passcode.ts b/src/global/actions/ui/passcode.ts index 6d71189c7..ac5906779 100644 --- a/src/global/actions/ui/passcode.ts +++ b/src/global/actions/ui/passcode.ts @@ -1,6 +1,7 @@ import type { ActionReturnType } from '../../types'; import { SettingsScreens } from '../../../types'; +import { IS_SCREEN_LOCKED_CACHE_KEY } from '../../../config'; import { getCurrentTabId, signalPasscodeHash } from '../../../util/establishMultitabRole'; import { cloneDeep } from '../../../util/iteratees'; import { @@ -72,6 +73,7 @@ addActionHandler('setPasscode', async (global, actions, payload): Promise addActionHandler('clearPasscode', (global): ActionReturnType => { void clearEncryptedSession(); + localStorage.removeItem(IS_SCREEN_LOCKED_CACHE_KEY); return clearPasscodeSettings(global); }); diff --git a/src/global/cache.ts b/src/global/cache.ts index 811b1dec3..b296e2893 100644 --- a/src/global/cache.ts +++ b/src/global/cache.ts @@ -321,7 +321,6 @@ function unsafeMigrateCache(cached: GlobalState, initialState: GlobalState) { messageTextSize: untypedCached.settings.byKey.messageTextSize, performance: untypedCached.settings.performance, theme: untypedCached.settings.byKey.theme, - themes: untypedCached.settings.themes, timeFormat: untypedCached.settings.byKey.timeFormat, wasTimeFormatSetManually: untypedCached.settings.byKey.wasTimeFormatSetManually, shouldUseSystemTheme: untypedCached.settings.byKey.shouldUseSystemTheme, @@ -338,6 +337,10 @@ function unsafeMigrateCache(cached: GlobalState, initialState: GlobalState) { shouldWarnAboutSvg: untypedCached.settings.byKey.shouldWarnAboutSvg, }; } + + if (!cached.settings.themes) { + cached.settings.themes = initialState.settings.themes; + } } function updateCache(force?: boolean) { @@ -706,7 +709,7 @@ function omitLocalMedia(message: ApiMessage): ApiMessage { function reduceSettings(global: T): GlobalState['settings'] { const { - byKey, botVerificationShownPeerIds, notifyDefaults, lastPremiumBandwithNotificationDate, + byKey, botVerificationShownPeerIds, notifyDefaults, lastPremiumBandwithNotificationDate, themes, } = global.settings; return { @@ -715,6 +718,7 @@ function reduceSettings(global: T): GlobalState['settings botVerificationShownPeerIds, lastPremiumBandwithNotificationDate, notifyDefaults, + themes, }; } diff --git a/src/global/initialState.ts b/src/global/initialState.ts index 7cb383c28..87eb9b8be 100644 --- a/src/global/initialState.ts +++ b/src/global/initialState.ts @@ -78,16 +78,6 @@ export const INITIAL_SHARED_STATE: SharedState = { : (IS_MAC_OS ? MACOS_DEFAULT_MESSAGE_TEXT_SIZE_PX : DEFAULT_MESSAGE_TEXT_SIZE_PX), animationLevel: ANIMATION_LEVEL_DEFAULT, messageSendKeyCombo: 'enter', - themes: { - light: { - isBlurred: true, - patternColor: DEFAULT_PATTERN_COLOR, - }, - dark: { - isBlurred: true, - patternColor: DARK_THEME_PATTERN_COLOR, - }, - }, performance: INITIAL_PERFORMANCE_STATE_MAX, shouldSkipWebAppCloseConfirmation: false, language: 'en', @@ -308,6 +298,16 @@ export const INITIAL_GLOBAL_STATE: GlobalState = { }, privacy: {}, botVerificationShownPeerIds: [], + themes: { + light: { + isBlurred: true, + patternColor: DEFAULT_PATTERN_COLOR, + }, + dark: { + isBlurred: true, + patternColor: DARK_THEME_PATTERN_COLOR, + }, + }, }, serviceNotifications: [], diff --git a/src/global/reducers/settings.ts b/src/global/reducers/settings.ts index 74cdb5db5..b1ef6cc5b 100644 --- a/src/global/reducers/settings.ts +++ b/src/global/reducers/settings.ts @@ -5,7 +5,7 @@ import type { } from '../../types'; import type { GlobalState, SharedState } from '../types'; -import { selectSharedSettings, selectSharedState } from '../selectors/sharedState'; +import { selectSharedSettings } from '../selectors/sharedState'; import { updateSharedState } from './sharedState'; import { updateUserBlockedState } from './users'; @@ -37,18 +37,22 @@ export function updateSharedSettings( export function updateThemeSettings( global: T, theme: ThemeKey, newSettings?: Partial, ): T { - const settings = selectSharedState(global).settings; + const settings = global.settings; const current = settings.themes[theme]; - return updateSharedSettings(global, { - themes: { - ...settings.themes, - [theme]: { - ...current, - ...newSettings, + return { + ...global, + settings: { + ...global.settings, + themes: { + ...settings.themes, + [theme]: { + ...current, + ...newSettings, + }, }, }, - }); + }; } export function addNotifyExceptions( diff --git a/src/global/selectors/settings.ts b/src/global/selectors/settings.ts index 18fa9ffe3..e3d45228b 100644 --- a/src/global/selectors/settings.ts +++ b/src/global/selectors/settings.ts @@ -1,5 +1,6 @@ import type { GlobalState } from '../types'; +import { ACCOUNT_SLOT, getAccountsInfo } from '../../util/multiaccount'; import { selectSharedSettings } from './sharedState'; export function selectNotifySettings(global: T) { @@ -19,7 +20,9 @@ export function selectLanguageCode(global: T) { } export function selectCanSetPasscode(global: T) { - return global.authRememberMe; + // TODO[passcode]: remove this when multiacc passcode is implemented + const accounts = getAccountsInfo(); + return global.authRememberMe && !ACCOUNT_SLOT && Object.keys(accounts).length === 1; } export function selectTranslationLanguage(global: T) { diff --git a/src/global/selectors/ui.ts b/src/global/selectors/ui.ts index adfcf73cb..90ad12cba 100644 --- a/src/global/selectors/ui.ts +++ b/src/global/selectors/ui.ts @@ -76,7 +76,7 @@ export function selectTheme(global: T) { } export function selectThemeValues(global: T, themeKey: ThemeKey) { - return selectSharedSettings(global).themes[themeKey]; + return global.settings.themes[themeKey]; } export function selectIsForumPanelOpen( diff --git a/src/global/types/globalState.ts b/src/global/types/globalState.ts index 027f59621..6facd245c 100644 --- a/src/global/types/globalState.ts +++ b/src/global/types/globalState.ts @@ -54,11 +54,13 @@ import type { ChatListType, ChatTranslatedMessages, EmojiKeywords, + IThemeSettings, ServiceNotification, SimilarBotsInfo, StarGiftCategory, StarsSubscriptions, StarsTransactionHistory, + ThemeKey, Thread, ThreadId, TopicsInfo, @@ -412,6 +414,7 @@ export type GlobalState = { lastPremiumBandwithNotificationDate?: number; paidReactionPrivacy?: ApiPaidReactionPrivacyType; botVerificationShownPeerIds: string[]; + themes: Partial>; }; push?: { diff --git a/src/global/types/sharedState.ts b/src/global/types/sharedState.ts index 93f926294..b244e5227 100644 --- a/src/global/types/sharedState.ts +++ b/src/global/types/sharedState.ts @@ -1,7 +1,6 @@ import type { ApiLanguage } from '../../api/types'; import type { AnimationLevel, - IThemeSettings, PerformanceType, Point, Size, @@ -13,7 +12,6 @@ export type SharedState = { settings: { shouldUseSystemTheme: boolean; theme: ThemeKey; - themes: Partial>; language: string; languages?: ApiLanguage[]; performance: PerformanceType; diff --git a/src/serviceWorker/progressive.ts b/src/serviceWorker/progressive.ts index ca5b58aab..7b24c0ffb 100644 --- a/src/serviceWorker/progressive.ts +++ b/src/serviceWorker/progressive.ts @@ -31,6 +31,7 @@ const requestStates = new Map(); export async function respondForProgressive(e: FetchEvent) { const { url } = e.request; + const accountSlot = getAccountSlot(url); const range = e.request.headers.get('range'); const bytes = /^bytes=(\d+)-(\d+)?$/g.exec(range || '')!; const start = Number(bytes[1]); @@ -66,7 +67,8 @@ export async function respondForProgressive(e: FetchEvent) { parsedUrl.searchParams.set('start', String(start)); parsedUrl.searchParams.set('end', String(end)); const cacheKey = parsedUrl.href; - const [cachedArrayBuffer, cachedHeaders] = !MEDIA_PROGRESSIVE_CACHE_DISABLED ? await fetchFromCache(cacheKey) : []; + const [cachedArrayBuffer, cachedHeaders] = !MEDIA_PROGRESSIVE_CACHE_DISABLED + ? await fetchFromCache(accountSlot, cacheKey) : []; if (DEBUG) { // eslint-disable-next-line no-console @@ -113,7 +115,7 @@ export async function respondForProgressive(e: FetchEvent) { ]; if (!MEDIA_PROGRESSIVE_CACHE_DISABLED && partSize <= MEDIA_CACHE_MAX_BYTES && end < MAX_END_TO_CACHE) { - saveToCache(cacheKey, arrayBufferPart, headers); + saveToCache(accountSlot, cacheKey, arrayBufferPart, headers); } return new Response(arrayBufferPart, { @@ -124,8 +126,9 @@ export async function respondForProgressive(e: FetchEvent) { } // We can not cache 206 responses: https://github.com/GoogleChrome/workbox/issues/1644#issuecomment-638741359 -async function fetchFromCache(cacheKey: string) { - const cache = await self.caches.open(MEDIA_PROGRESSIVE_CACHE_NAME); +async function fetchFromCache(accountSlot: number | undefined, cacheKey: string) { + const cacheName = !accountSlot ? MEDIA_PROGRESSIVE_CACHE_NAME : `${MEDIA_PROGRESSIVE_CACHE_NAME}_${accountSlot}`; + const cache = await self.caches.open(cacheName); return Promise.all([ cache.match(`${cacheKey}&type=arrayBuffer`).then((r) => (r ? r.arrayBuffer() : undefined)), @@ -133,8 +136,11 @@ async function fetchFromCache(cacheKey: string) { ]); } -async function saveToCache(cacheKey: string, arrayBuffer: ArrayBuffer, headers: HeadersInit) { - const cache = await self.caches.open(MEDIA_PROGRESSIVE_CACHE_NAME); +async function saveToCache( + accountSlot: number | undefined, cacheKey: string, arrayBuffer: ArrayBuffer, headers: HeadersInit, +) { + const cacheName = !accountSlot ? MEDIA_PROGRESSIVE_CACHE_NAME : `${MEDIA_PROGRESSIVE_CACHE_NAME}_${accountSlot}`; + const cache = await self.caches.open(cacheName); return Promise.all([ cache.put(new Request(`${cacheKey}&type=arrayBuffer`), new Response(arrayBuffer)), diff --git a/src/util/cacheApi.ts b/src/util/cacheApi.ts index 5a33a60a6..b48d4a126 100644 --- a/src/util/cacheApi.ts +++ b/src/util/cacheApi.ts @@ -97,6 +97,7 @@ export async function save(cacheName: string, key: string, data: AnyLiteral | Bl const response = new Response(cacheData); const cache = await cacheApi.open(`${cacheName}${SUFFIX}`); await cache.put(request, response); + return true; } catch (err) { // eslint-disable-next-line no-console diff --git a/src/util/multiaccount.ts b/src/util/multiaccount.ts index ac63a6fa7..5add6bf77 100644 --- a/src/util/multiaccount.ts +++ b/src/util/multiaccount.ts @@ -15,8 +15,9 @@ const WORKER_NAME = typeof WorkerGlobalScope !== 'undefined' && globalThis.self ? globalThis.self.name : undefined; const WORKER_ACCOUNT_SLOT = WORKER_NAME ? Number(new URLSearchParams(WORKER_NAME).get(ACCOUNT_QUERY)) : undefined; -export const ACCOUNT_SLOT = IS_MULTIACCOUNT_SUPPORTED - ? (WORKER_ACCOUNT_SLOT || getAccountSlot(globalThis.location.href)) : undefined; +export const ACCOUNT_SLOT = WORKER_ACCOUNT_SLOT || ( + IS_MULTIACCOUNT_SUPPORTED ? getAccountSlot(globalThis.location.href) : undefined +); export const DATA_BROADCAST_CHANNEL_NAME = `${DATA_BROADCAST_CHANNEL_PREFIX}_${ACCOUNT_SLOT || 1}`; export const ESTABLISH_BROADCAST_CHANNEL_NAME = `${ESTABLISH_BROADCAST_CHANNEL_PREFIX}_${ACCOUNT_SLOT || 1}`; diff --git a/src/util/passcode.ts b/src/util/passcode.ts index 9289dfedf..7e9879565 100644 --- a/src/util/passcode.ts +++ b/src/util/passcode.ts @@ -125,6 +125,10 @@ function store(key: string, value: ArrayBuffer) { PASSCODE_IDB_STORE.set(key, asArray); } +export function hasEncryptedSession() { + return PASSCODE_IDB_STORE.get('sessionEncrypted'); +} + async function load(key: string) { const cached = await PASSCODE_IDB_STORE.get(key); if (cached) { diff --git a/src/util/sessions.ts b/src/util/sessions.ts index 1467d4689..f8f9ad4ea 100644 --- a/src/util/sessions.ts +++ b/src/util/sessions.ts @@ -183,6 +183,6 @@ export function importTestSession() { } } -function checkSessionLocked() { +export function checkSessionLocked() { return localStorage.getItem(IS_SCREEN_LOCKED_CACHE_KEY) === 'true'; }