Multi-Account: Fix cache load (#5854)

This commit is contained in:
zubiden 2025-04-23 18:59:31 +02:00 committed by Alexander Zinchuk
parent f1297f877b
commit 76ee31229e
15 changed files with 83 additions and 45 deletions

View File

@ -14,8 +14,9 @@ import { IS_INSTALL_PROMPT_SUPPORTED, PLATFORM_ENV } from '../util/browser/windo
import buildClassName from '../util/buildClassName'; import buildClassName from '../util/buildClassName';
import { setupBeforeInstallPrompt } from '../util/installPrompt'; import { setupBeforeInstallPrompt } from '../util/installPrompt';
import { ACCOUNT_SLOT, getAccountsInfo, getAccountSlotUrl } from '../util/multiaccount'; import { ACCOUNT_SLOT, getAccountsInfo, getAccountSlotUrl } from '../util/multiaccount';
import { hasEncryptedSession } from '../util/passcode';
import { getInitialLocationHash, parseInitialLocationHash } from '../util/routing'; import { getInitialLocationHash, parseInitialLocationHash } from '../util/routing';
import { hasStoredSession } from '../util/sessions'; import { checkSessionLocked, hasStoredSession } from '../util/sessions';
import { updateSizes } from '../util/windowSize'; import { updateSizes } from '../util/windowSize';
import useAppLayout from '../hooks/useAppLayout'; import useAppLayout from '../hooks/useAppLayout';
@ -76,15 +77,27 @@ const App: FC<StateProps> = ({
// If there is no stored session on first slot, navigate to any other slot with stored session // If there is no stored session on first slot, navigate to any other slot with stored session
if (!hasStoredSession() && !ACCOUNT_SLOT && !hash) { if (!hasStoredSession() && !ACCOUNT_SLOT && !hash) {
const accounts = getAccountsInfo(); const accounts = getAccountsInfo();
Object.keys(accounts).forEach((key) => { Object.keys(accounts)
const slot = Number(key); .map(Number)
const account = accounts[slot]; .sort((a, b) => b - a)
if (account) { .forEach((key) => {
const url = getAccountSlotUrl(slot); const slot = Number(key);
window.location.href = `${url}#${hash || 'login'}`; 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 // Prevent drop on elements that do not accept it

View File

@ -27,7 +27,6 @@ import {
selectTabState, selectTabState,
selectTheme, selectTheme,
} from '../../../global/selectors'; } from '../../../global/selectors';
import { selectSharedSettings } from '../../../global/selectors/sharedState';
import { IS_ANDROID, IS_ELECTRON, IS_FLUID_BACKGROUND_SUPPORTED } from '../../../util/browser/windowEnvironment'; import { IS_ANDROID, IS_ELECTRON, IS_FLUID_BACKGROUND_SUPPORTED } from '../../../util/browser/windowEnvironment';
import buildClassName from '../../../util/buildClassName'; import buildClassName from '../../../util/buildClassName';
import { isLocalMessageId } from '../../../util/keys/messageKey'; import { isLocalMessageId } from '../../../util/keys/messageKey';
@ -461,7 +460,7 @@ const ActionMessage = ({
export default memo(withGlobal<OwnProps>( export default memo(withGlobal<OwnProps>(
(global, { message, threadId }): StateProps => { (global, { message, threadId }): StateProps => {
const tabState = selectTabState(global); const tabState = selectTabState(global);
const { themes } = selectSharedSettings(global); const { themes } = global.settings;
const chat = selectChat(global, message.chatId); const chat = selectChat(global, message.chatId);

View File

@ -1,6 +1,7 @@
import type { ActionReturnType } from '../../types'; import type { ActionReturnType } from '../../types';
import { SettingsScreens } from '../../../types'; import { SettingsScreens } from '../../../types';
import { IS_SCREEN_LOCKED_CACHE_KEY } from '../../../config';
import { getCurrentTabId, signalPasscodeHash } from '../../../util/establishMultitabRole'; import { getCurrentTabId, signalPasscodeHash } from '../../../util/establishMultitabRole';
import { cloneDeep } from '../../../util/iteratees'; import { cloneDeep } from '../../../util/iteratees';
import { import {
@ -72,6 +73,7 @@ addActionHandler('setPasscode', async (global, actions, payload): Promise<void>
addActionHandler('clearPasscode', (global): ActionReturnType => { addActionHandler('clearPasscode', (global): ActionReturnType => {
void clearEncryptedSession(); void clearEncryptedSession();
localStorage.removeItem(IS_SCREEN_LOCKED_CACHE_KEY);
return clearPasscodeSettings(global); return clearPasscodeSettings(global);
}); });

View File

@ -321,7 +321,6 @@ function unsafeMigrateCache(cached: GlobalState, initialState: GlobalState) {
messageTextSize: untypedCached.settings.byKey.messageTextSize, messageTextSize: untypedCached.settings.byKey.messageTextSize,
performance: untypedCached.settings.performance, performance: untypedCached.settings.performance,
theme: untypedCached.settings.byKey.theme, theme: untypedCached.settings.byKey.theme,
themes: untypedCached.settings.themes,
timeFormat: untypedCached.settings.byKey.timeFormat, timeFormat: untypedCached.settings.byKey.timeFormat,
wasTimeFormatSetManually: untypedCached.settings.byKey.wasTimeFormatSetManually, wasTimeFormatSetManually: untypedCached.settings.byKey.wasTimeFormatSetManually,
shouldUseSystemTheme: untypedCached.settings.byKey.shouldUseSystemTheme, shouldUseSystemTheme: untypedCached.settings.byKey.shouldUseSystemTheme,
@ -338,6 +337,10 @@ function unsafeMigrateCache(cached: GlobalState, initialState: GlobalState) {
shouldWarnAboutSvg: untypedCached.settings.byKey.shouldWarnAboutSvg, shouldWarnAboutSvg: untypedCached.settings.byKey.shouldWarnAboutSvg,
}; };
} }
if (!cached.settings.themes) {
cached.settings.themes = initialState.settings.themes;
}
} }
function updateCache(force?: boolean) { function updateCache(force?: boolean) {
@ -706,7 +709,7 @@ function omitLocalMedia(message: ApiMessage): ApiMessage {
function reduceSettings<T extends GlobalState>(global: T): GlobalState['settings'] { function reduceSettings<T extends GlobalState>(global: T): GlobalState['settings'] {
const { const {
byKey, botVerificationShownPeerIds, notifyDefaults, lastPremiumBandwithNotificationDate, byKey, botVerificationShownPeerIds, notifyDefaults, lastPremiumBandwithNotificationDate, themes,
} = global.settings; } = global.settings;
return { return {
@ -715,6 +718,7 @@ function reduceSettings<T extends GlobalState>(global: T): GlobalState['settings
botVerificationShownPeerIds, botVerificationShownPeerIds,
lastPremiumBandwithNotificationDate, lastPremiumBandwithNotificationDate,
notifyDefaults, notifyDefaults,
themes,
}; };
} }

View File

@ -78,16 +78,6 @@ export const INITIAL_SHARED_STATE: SharedState = {
: (IS_MAC_OS ? MACOS_DEFAULT_MESSAGE_TEXT_SIZE_PX : DEFAULT_MESSAGE_TEXT_SIZE_PX), : (IS_MAC_OS ? MACOS_DEFAULT_MESSAGE_TEXT_SIZE_PX : DEFAULT_MESSAGE_TEXT_SIZE_PX),
animationLevel: ANIMATION_LEVEL_DEFAULT, animationLevel: ANIMATION_LEVEL_DEFAULT,
messageSendKeyCombo: 'enter', messageSendKeyCombo: 'enter',
themes: {
light: {
isBlurred: true,
patternColor: DEFAULT_PATTERN_COLOR,
},
dark: {
isBlurred: true,
patternColor: DARK_THEME_PATTERN_COLOR,
},
},
performance: INITIAL_PERFORMANCE_STATE_MAX, performance: INITIAL_PERFORMANCE_STATE_MAX,
shouldSkipWebAppCloseConfirmation: false, shouldSkipWebAppCloseConfirmation: false,
language: 'en', language: 'en',
@ -308,6 +298,16 @@ export const INITIAL_GLOBAL_STATE: GlobalState = {
}, },
privacy: {}, privacy: {},
botVerificationShownPeerIds: [], botVerificationShownPeerIds: [],
themes: {
light: {
isBlurred: true,
patternColor: DEFAULT_PATTERN_COLOR,
},
dark: {
isBlurred: true,
patternColor: DARK_THEME_PATTERN_COLOR,
},
},
}, },
serviceNotifications: [], serviceNotifications: [],

View File

@ -5,7 +5,7 @@ import type {
} from '../../types'; } from '../../types';
import type { GlobalState, SharedState } from '../types'; import type { GlobalState, SharedState } from '../types';
import { selectSharedSettings, selectSharedState } from '../selectors/sharedState'; import { selectSharedSettings } from '../selectors/sharedState';
import { updateSharedState } from './sharedState'; import { updateSharedState } from './sharedState';
import { updateUserBlockedState } from './users'; import { updateUserBlockedState } from './users';
@ -37,18 +37,22 @@ export function updateSharedSettings<T extends GlobalState>(
export function updateThemeSettings<T extends GlobalState>( export function updateThemeSettings<T extends GlobalState>(
global: T, theme: ThemeKey, newSettings?: Partial<IThemeSettings>, global: T, theme: ThemeKey, newSettings?: Partial<IThemeSettings>,
): T { ): T {
const settings = selectSharedState(global).settings; const settings = global.settings;
const current = settings.themes[theme]; const current = settings.themes[theme];
return updateSharedSettings(global, { return {
themes: { ...global,
...settings.themes, settings: {
[theme]: { ...global.settings,
...current, themes: {
...newSettings, ...settings.themes,
[theme]: {
...current,
...newSettings,
},
}, },
}, },
}); };
} }
export function addNotifyExceptions<T extends GlobalState>( export function addNotifyExceptions<T extends GlobalState>(

View File

@ -1,5 +1,6 @@
import type { GlobalState } from '../types'; import type { GlobalState } from '../types';
import { ACCOUNT_SLOT, getAccountsInfo } from '../../util/multiaccount';
import { selectSharedSettings } from './sharedState'; import { selectSharedSettings } from './sharedState';
export function selectNotifySettings<T extends GlobalState>(global: T) { export function selectNotifySettings<T extends GlobalState>(global: T) {
@ -19,7 +20,9 @@ export function selectLanguageCode<T extends GlobalState>(global: T) {
} }
export function selectCanSetPasscode<T extends GlobalState>(global: T) { export function selectCanSetPasscode<T extends GlobalState>(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<T extends GlobalState>(global: T) { export function selectTranslationLanguage<T extends GlobalState>(global: T) {

View File

@ -76,7 +76,7 @@ export function selectTheme<T extends GlobalState>(global: T) {
} }
export function selectThemeValues<T extends GlobalState>(global: T, themeKey: ThemeKey) { export function selectThemeValues<T extends GlobalState>(global: T, themeKey: ThemeKey) {
return selectSharedSettings(global).themes[themeKey]; return global.settings.themes[themeKey];
} }
export function selectIsForumPanelOpen<T extends GlobalState>( export function selectIsForumPanelOpen<T extends GlobalState>(

View File

@ -54,11 +54,13 @@ import type {
ChatListType, ChatListType,
ChatTranslatedMessages, ChatTranslatedMessages,
EmojiKeywords, EmojiKeywords,
IThemeSettings,
ServiceNotification, ServiceNotification,
SimilarBotsInfo, SimilarBotsInfo,
StarGiftCategory, StarGiftCategory,
StarsSubscriptions, StarsSubscriptions,
StarsTransactionHistory, StarsTransactionHistory,
ThemeKey,
Thread, Thread,
ThreadId, ThreadId,
TopicsInfo, TopicsInfo,
@ -412,6 +414,7 @@ export type GlobalState = {
lastPremiumBandwithNotificationDate?: number; lastPremiumBandwithNotificationDate?: number;
paidReactionPrivacy?: ApiPaidReactionPrivacyType; paidReactionPrivacy?: ApiPaidReactionPrivacyType;
botVerificationShownPeerIds: string[]; botVerificationShownPeerIds: string[];
themes: Partial<Record<ThemeKey, IThemeSettings>>;
}; };
push?: { push?: {

View File

@ -1,7 +1,6 @@
import type { ApiLanguage } from '../../api/types'; import type { ApiLanguage } from '../../api/types';
import type { import type {
AnimationLevel, AnimationLevel,
IThemeSettings,
PerformanceType, PerformanceType,
Point, Point,
Size, Size,
@ -13,7 +12,6 @@ export type SharedState = {
settings: { settings: {
shouldUseSystemTheme: boolean; shouldUseSystemTheme: boolean;
theme: ThemeKey; theme: ThemeKey;
themes: Partial<Record<ThemeKey, IThemeSettings>>;
language: string; language: string;
languages?: ApiLanguage[]; languages?: ApiLanguage[];
performance: PerformanceType; performance: PerformanceType;

View File

@ -31,6 +31,7 @@ const requestStates = new Map<string, RequestStates>();
export async function respondForProgressive(e: FetchEvent) { export async function respondForProgressive(e: FetchEvent) {
const { url } = e.request; const { url } = e.request;
const accountSlot = getAccountSlot(url);
const range = e.request.headers.get('range'); const range = e.request.headers.get('range');
const bytes = /^bytes=(\d+)-(\d+)?$/g.exec(range || '')!; const bytes = /^bytes=(\d+)-(\d+)?$/g.exec(range || '')!;
const start = Number(bytes[1]); const start = Number(bytes[1]);
@ -66,7 +67,8 @@ export async function respondForProgressive(e: FetchEvent) {
parsedUrl.searchParams.set('start', String(start)); parsedUrl.searchParams.set('start', String(start));
parsedUrl.searchParams.set('end', String(end)); parsedUrl.searchParams.set('end', String(end));
const cacheKey = parsedUrl.href; 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) { if (DEBUG) {
// eslint-disable-next-line no-console // 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) { 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, { 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 // We can not cache 206 responses: https://github.com/GoogleChrome/workbox/issues/1644#issuecomment-638741359
async function fetchFromCache(cacheKey: string) { async function fetchFromCache(accountSlot: number | undefined, cacheKey: string) {
const cache = await self.caches.open(MEDIA_PROGRESSIVE_CACHE_NAME); const cacheName = !accountSlot ? MEDIA_PROGRESSIVE_CACHE_NAME : `${MEDIA_PROGRESSIVE_CACHE_NAME}_${accountSlot}`;
const cache = await self.caches.open(cacheName);
return Promise.all([ return Promise.all([
cache.match(`${cacheKey}&type=arrayBuffer`).then((r) => (r ? r.arrayBuffer() : undefined)), 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) { async function saveToCache(
const cache = await self.caches.open(MEDIA_PROGRESSIVE_CACHE_NAME); 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([ return Promise.all([
cache.put(new Request(`${cacheKey}&type=arrayBuffer`), new Response(arrayBuffer)), cache.put(new Request(`${cacheKey}&type=arrayBuffer`), new Response(arrayBuffer)),

View File

@ -97,6 +97,7 @@ export async function save(cacheName: string, key: string, data: AnyLiteral | Bl
const response = new Response(cacheData); const response = new Response(cacheData);
const cache = await cacheApi.open(`${cacheName}${SUFFIX}`); const cache = await cacheApi.open(`${cacheName}${SUFFIX}`);
await cache.put(request, response); await cache.put(request, response);
return true; return true;
} catch (err) { } catch (err) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console

View File

@ -15,8 +15,9 @@ const WORKER_NAME = typeof WorkerGlobalScope !== 'undefined' && globalThis.self
? globalThis.self.name : undefined; ? globalThis.self.name : undefined;
const WORKER_ACCOUNT_SLOT = WORKER_NAME ? Number(new URLSearchParams(WORKER_NAME).get(ACCOUNT_QUERY)) : undefined; const WORKER_ACCOUNT_SLOT = WORKER_NAME ? Number(new URLSearchParams(WORKER_NAME).get(ACCOUNT_QUERY)) : undefined;
export const ACCOUNT_SLOT = IS_MULTIACCOUNT_SUPPORTED export const ACCOUNT_SLOT = WORKER_ACCOUNT_SLOT || (
? (WORKER_ACCOUNT_SLOT || getAccountSlot(globalThis.location.href)) : undefined; IS_MULTIACCOUNT_SUPPORTED ? getAccountSlot(globalThis.location.href) : undefined
);
export const DATA_BROADCAST_CHANNEL_NAME = `${DATA_BROADCAST_CHANNEL_PREFIX}_${ACCOUNT_SLOT || 1}`; 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}`; export const ESTABLISH_BROADCAST_CHANNEL_NAME = `${ESTABLISH_BROADCAST_CHANNEL_PREFIX}_${ACCOUNT_SLOT || 1}`;

View File

@ -125,6 +125,10 @@ function store(key: string, value: ArrayBuffer) {
PASSCODE_IDB_STORE.set(key, asArray); PASSCODE_IDB_STORE.set(key, asArray);
} }
export function hasEncryptedSession() {
return PASSCODE_IDB_STORE.get('sessionEncrypted');
}
async function load(key: string) { async function load(key: string) {
const cached = await PASSCODE_IDB_STORE.get<number[]>(key); const cached = await PASSCODE_IDB_STORE.get<number[]>(key);
if (cached) { if (cached) {

View File

@ -183,6 +183,6 @@ export function importTestSession() {
} }
} }
function checkSessionLocked() { export function checkSessionLocked() {
return localStorage.getItem(IS_SCREEN_LOCKED_CACHE_KEY) === 'true'; return localStorage.getItem(IS_SCREEN_LOCKED_CACHE_KEY) === 'true';
} }