Global: Migrate cache from localStorage to IndexedDB (#4770)
This commit is contained in:
parent
66d54b2f63
commit
f590963b12
@ -42,6 +42,8 @@ export const GLOBAL_STATE_CACHE_CHAT_LIST_LIMIT = 200;
|
||||
export const GLOBAL_STATE_CACHE_ARCHIVED_CHAT_LIST_LIMIT = 10;
|
||||
export const GLOBAL_STATE_CACHE_CUSTOM_EMOJI_LIMIT = 150;
|
||||
|
||||
export const IS_SCREEN_LOCKED_CACHE_KEY = 'tt-is-screen-locked';
|
||||
|
||||
export const MEDIA_CACHE_DISABLED = false;
|
||||
export const MEDIA_CACHE_NAME = 'tt-media';
|
||||
export const MEDIA_CACHE_NAME_AVATARS = 'tt-media-avatars';
|
||||
|
||||
@ -19,8 +19,10 @@ import {
|
||||
GLOBAL_STATE_CACHE_DISABLED,
|
||||
GLOBAL_STATE_CACHE_KEY,
|
||||
GLOBAL_STATE_CACHE_USER_LIST_LIMIT,
|
||||
IS_SCREEN_LOCKED_CACHE_KEY,
|
||||
SAVED_FOLDER_ID,
|
||||
} from '../config';
|
||||
import { MAIN_IDB_STORE } from '../util/browser/idb';
|
||||
import { getOrderedIds } from '../util/folderManager';
|
||||
import {
|
||||
compact, pick, pickTruthy, unique,
|
||||
@ -50,21 +52,41 @@ const updateCacheThrottled = throttle(() => onIdle(() => updateCache()), UPDATE_
|
||||
const updateCacheForced = () => updateCache(true);
|
||||
|
||||
let isCaching = false;
|
||||
let isRemovingCache = false;
|
||||
let unsubscribeFromBeforeUnload: NoneToVoidFunction | undefined;
|
||||
|
||||
export function cacheGlobal(global: GlobalState) {
|
||||
return MAIN_IDB_STORE.set(GLOBAL_STATE_CACHE_KEY, global);
|
||||
}
|
||||
|
||||
export function loadCachedGlobal() {
|
||||
return MAIN_IDB_STORE.get<GlobalState>(GLOBAL_STATE_CACHE_KEY);
|
||||
}
|
||||
|
||||
export function removeGlobalFromCache() {
|
||||
return MAIN_IDB_STORE.del(GLOBAL_STATE_CACHE_KEY);
|
||||
}
|
||||
|
||||
function cacheIsScreenLocked(global: GlobalState) {
|
||||
if (global?.passcode?.isScreenLocked) localStorage.setItem(IS_SCREEN_LOCKED_CACHE_KEY, 'true');
|
||||
}
|
||||
|
||||
export function initCache() {
|
||||
if (GLOBAL_STATE_CACHE_DISABLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
const resetCache = () => {
|
||||
localStorage.removeItem(GLOBAL_STATE_CACHE_KEY);
|
||||
isRemovingCache = true;
|
||||
removeGlobalFromCache().finally(() => {
|
||||
localStorage.removeItem(IS_SCREEN_LOCKED_CACHE_KEY);
|
||||
isRemovingCache = false;
|
||||
if (!isCaching) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isCaching) {
|
||||
return;
|
||||
}
|
||||
|
||||
clearCaching();
|
||||
clearCaching();
|
||||
});
|
||||
};
|
||||
|
||||
addActionHandler('saveSession', (): ActionReturnType => {
|
||||
@ -79,12 +101,12 @@ export function initCache() {
|
||||
addActionHandler('reset', resetCache);
|
||||
}
|
||||
|
||||
export function loadCache(initialState: GlobalState): GlobalState | undefined {
|
||||
export async function loadCache(initialState: GlobalState): Promise<GlobalState | undefined> {
|
||||
if (GLOBAL_STATE_CACHE_DISABLED) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const cache = readCache(initialState);
|
||||
const cache = await readCache(initialState);
|
||||
|
||||
if (cache.passcode.hasPasscode || hasStoredSession()) {
|
||||
setupCaching();
|
||||
@ -113,14 +135,17 @@ export function clearCaching() {
|
||||
}
|
||||
}
|
||||
|
||||
function readCache(initialState: GlobalState): GlobalState {
|
||||
async function readCache(initialState: GlobalState): Promise<GlobalState> {
|
||||
if (DEBUG) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.time('global-state-cache-read');
|
||||
}
|
||||
|
||||
const json = localStorage.getItem(GLOBAL_STATE_CACHE_KEY);
|
||||
const cached = json ? JSON.parse(json) as GlobalState : undefined;
|
||||
const cachedFromLocalStorage = json ? JSON.parse(json) as GlobalState : undefined;
|
||||
if (cachedFromLocalStorage) localStorage.removeItem(GLOBAL_STATE_CACHE_KEY);
|
||||
|
||||
const cached = cachedFromLocalStorage || await loadCachedGlobal();
|
||||
|
||||
if (DEBUG) {
|
||||
// eslint-disable-next-line no-console
|
||||
@ -231,7 +256,7 @@ function unsafeMigrateCache(cached: GlobalState, initialState: GlobalState) {
|
||||
|
||||
function updateCache(force?: boolean) {
|
||||
const global = getGlobal();
|
||||
if (!isCaching || global.isLoggingOut || (!force && isHeavyAnimating())) {
|
||||
if (isRemovingCache || !isCaching || global.isLoggingOut || (!force && isHeavyAnimating())) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -248,16 +273,16 @@ export function forceUpdateCache(noEncrypt = false) {
|
||||
void encryptSession(undefined, serializedGlobal);
|
||||
}
|
||||
|
||||
const serializedGlobalClean = JSON.stringify(clearGlobalForLockScreen(global, false));
|
||||
localStorage.setItem(GLOBAL_STATE_CACHE_KEY, serializedGlobalClean);
|
||||
|
||||
cacheIsScreenLocked(global);
|
||||
cacheGlobal(clearGlobalForLockScreen(global, false));
|
||||
return;
|
||||
}
|
||||
|
||||
localStorage.setItem(GLOBAL_STATE_CACHE_KEY, serializedGlobal);
|
||||
cacheIsScreenLocked(global);
|
||||
cacheGlobal(reduceGlobal(global));
|
||||
}
|
||||
|
||||
export function serializeGlobal<T extends GlobalState>(global: T) {
|
||||
function reduceGlobal<T extends GlobalState>(global: T) {
|
||||
const reducedGlobal: GlobalState = {
|
||||
...INITIAL_GLOBAL_STATE,
|
||||
...pick(global, [
|
||||
@ -314,7 +339,11 @@ export function serializeGlobal<T extends GlobalState>(global: T) {
|
||||
]),
|
||||
};
|
||||
|
||||
return JSON.stringify(reducedGlobal);
|
||||
return reducedGlobal;
|
||||
}
|
||||
|
||||
export function serializeGlobal<T extends GlobalState>(global: T) {
|
||||
return JSON.stringify(reduceGlobal(global));
|
||||
}
|
||||
|
||||
function reduceCustomEmojis<T extends GlobalState>(global: T): GlobalState['customEmojis'] {
|
||||
|
||||
@ -2,50 +2,30 @@ import './intervals';
|
||||
|
||||
import type { ActionReturnType, GlobalState } from './types';
|
||||
|
||||
import { IS_MOCKED_CLIENT } from '../config';
|
||||
import { isCacheApiSupported } from '../util/cacheApi';
|
||||
import { getCurrentTabId, reestablishMasterToSelf } from '../util/establishMultitabRole';
|
||||
import { initGlobal } from '../util/init';
|
||||
import { cloneDeep } from '../util/iteratees';
|
||||
import { isLocalMessageId } from '../util/messageKey';
|
||||
import { Bundles, loadBundle } from '../util/moduleLoader';
|
||||
import { parseLocationHash } from '../util/routing';
|
||||
import { clearStoredSession } from '../util/sessions';
|
||||
import { updatePeerColors } from '../util/theme';
|
||||
import { IS_MULTITAB_SUPPORTED } from '../util/windowEnvironment';
|
||||
import { initializeChatMediaSearchResults } from './reducers/localSearch';
|
||||
import { updateTabState } from './reducers/tabs';
|
||||
import { initCache, loadCache } from './cache';
|
||||
import { initCache } from './cache';
|
||||
import {
|
||||
addActionHandler, getGlobal, setGlobal,
|
||||
} from './index';
|
||||
import { INITIAL_GLOBAL_STATE, INITIAL_TAB_STATE } from './initialState';
|
||||
import { replaceTabThreadParam, replaceThreadParam, updatePasscodeSettings } from './reducers';
|
||||
import { INITIAL_TAB_STATE } from './initialState';
|
||||
import { replaceTabThreadParam, replaceThreadParam } from './reducers';
|
||||
import { selectTabState, selectThreadParam } from './selectors';
|
||||
|
||||
initCache();
|
||||
|
||||
addActionHandler('initShared', (prevGlobal, actions, payload): ActionReturnType => {
|
||||
addActionHandler('initShared', async (prevGlobal, actions, payload): Promise<void> => {
|
||||
const { force } = payload || {};
|
||||
if (!force && 'byTabId' in prevGlobal) return prevGlobal;
|
||||
|
||||
const initial = cloneDeep(INITIAL_GLOBAL_STATE);
|
||||
let global = loadCache(initial) || initial;
|
||||
if (IS_MOCKED_CLIENT) global.authState = 'authorizationStateReady';
|
||||
|
||||
const { hasPasscode, isScreenLocked } = global.passcode;
|
||||
if (hasPasscode && !isScreenLocked) {
|
||||
global = updatePasscodeSettings(global, {
|
||||
isScreenLocked: true,
|
||||
});
|
||||
|
||||
clearStoredSession();
|
||||
}
|
||||
|
||||
if (force) {
|
||||
global.byTabId = prevGlobal.byTabId;
|
||||
}
|
||||
|
||||
return global;
|
||||
await initGlobal(force, prevGlobal);
|
||||
});
|
||||
|
||||
addActionHandler('init', (global, actions, payload): ActionReturnType => {
|
||||
|
||||
@ -15,6 +15,7 @@ import { enableStrict, requestMutation } from './lib/fasterdom/fasterdom';
|
||||
import { selectTabState } from './global/selectors';
|
||||
import { betterView } from './util/betterView';
|
||||
import { establishMultitabRole, subscribeToMasterChange } from './util/establishMultitabRole';
|
||||
import { initGlobal } from './util/init';
|
||||
import { initLocalization } from './util/localization';
|
||||
import { requestGlobal, subscribeToMultitabBroadcastChannel } from './util/multitab';
|
||||
import { checkAndAssignPermanentWebVersion } from './util/permanentWebVersion';
|
||||
@ -57,7 +58,7 @@ async function init() {
|
||||
});
|
||||
}
|
||||
|
||||
getActions().initShared();
|
||||
await initGlobal();
|
||||
getActions().init();
|
||||
|
||||
getActions().updateShouldEnableDebugLog();
|
||||
|
||||
37
src/util/init.ts
Normal file
37
src/util/init.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import type { GlobalState } from '../global/types';
|
||||
|
||||
import { IS_MOCKED_CLIENT } from '../config';
|
||||
import { loadCache } from '../global/cache';
|
||||
import {
|
||||
getGlobal, setGlobal,
|
||||
} from '../global/index';
|
||||
import { INITIAL_GLOBAL_STATE } from '../global/initialState';
|
||||
import { updatePasscodeSettings } from '../global/reducers';
|
||||
import { cloneDeep } from './iteratees';
|
||||
import { clearStoredSession } from './sessions';
|
||||
|
||||
export async function initGlobal(force: boolean = false, prevGlobal?: GlobalState) {
|
||||
prevGlobal = prevGlobal || getGlobal();
|
||||
if (!force && 'byTabId' in prevGlobal) {
|
||||
return;
|
||||
}
|
||||
|
||||
const initial = cloneDeep(INITIAL_GLOBAL_STATE);
|
||||
let global = await loadCache(initial) || initial;
|
||||
if (IS_MOCKED_CLIENT) global.authState = 'authorizationStateReady';
|
||||
|
||||
const { hasPasscode, isScreenLocked } = global.passcode;
|
||||
if (hasPasscode && !isScreenLocked) {
|
||||
global = updatePasscodeSettings(global, {
|
||||
isScreenLocked: true,
|
||||
});
|
||||
|
||||
clearStoredSession();
|
||||
}
|
||||
|
||||
if (force) {
|
||||
global.byTabId = prevGlobal.byTabId;
|
||||
}
|
||||
|
||||
setGlobal(global);
|
||||
}
|
||||
@ -1,7 +1,8 @@
|
||||
import type { ApiSessionData } from '../api/types';
|
||||
|
||||
import {
|
||||
DEBUG, GLOBAL_STATE_CACHE_KEY, SESSION_USER_KEY,
|
||||
DEBUG, IS_SCREEN_LOCKED_CACHE_KEY,
|
||||
SESSION_USER_KEY,
|
||||
} from '../config';
|
||||
|
||||
const DC_IDS = [1, 2, 3, 4, 5];
|
||||
@ -109,7 +110,5 @@ export function importTestSession() {
|
||||
}
|
||||
|
||||
function checkSessionLocked() {
|
||||
const stateFromCache = JSON.parse(localStorage.getItem(GLOBAL_STATE_CACHE_KEY) || '{}');
|
||||
|
||||
return Boolean(stateFromCache?.passcode?.isScreenLocked);
|
||||
return localStorage.getItem(IS_SCREEN_LOCKED_CACHE_KEY) === 'true';
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user