2021-04-29 18:24:13 +03:00

214 lines
5.5 KiB
TypeScript

import {
addCallback, addReducer, getGlobal, removeCallback,
} from '../lib/teact/teactn';
import { GlobalState } from './types';
import { MAIN_THREAD_ID } from '../api/types';
import { onIdle, throttle } from '../util/schedulers';
import {
DEBUG,
GLOBAL_STATE_CACHE_DISABLED,
GLOBAL_STATE_CACHE_KEY,
GLOBAL_STATE_CACHE_CHAT_LIST_LIMIT,
GRAMJS_SESSION_ID_KEY,
MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN, GLOBAL_STATE_CACHE_USER_LIST_LIMIT,
} from '../config';
import { IS_MOBILE_SCREEN } from '../util/environment';
import { pick } from '../util/iteratees';
import { INITIAL_STATE } from './initial';
import { selectCurrentMessageList } from '../modules/selectors';
const UPDATE_THROTTLE = 1000;
const updateCacheThrottled = throttle(updateCache, UPDATE_THROTTLE, false);
let isAllowed = false;
export function initCache() {
if (GLOBAL_STATE_CACHE_DISABLED) {
return;
}
addReducer('saveSession', () => {
isAllowed = true;
addCallback(updateCacheThrottled);
});
addReducer('reset', () => {
isAllowed = false;
removeCallback(updateCacheThrottled);
localStorage.removeItem(GLOBAL_STATE_CACHE_KEY);
});
}
export function loadCache(initialState: GlobalState) {
if (!GLOBAL_STATE_CACHE_DISABLED) {
const hasActiveSession = localStorage.getItem(GRAMJS_SESSION_ID_KEY);
if (hasActiveSession) {
isAllowed = true;
addCallback(updateCacheThrottled);
return readCache(initialState);
} else {
isAllowed = false;
}
}
return undefined;
}
function readCache(initialState: 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;
if (DEBUG) {
// eslint-disable-next-line no-console
console.timeEnd('global-state-cache-read');
}
if (cached) {
// Pre-fill defaults for new settings which may be missing in older cache
cached.settings.byKey = {
...initialState.settings.byKey,
...cached.settings.byKey,
};
}
return {
...initialState,
...cached,
};
}
function updateCache() {
onIdle(() => {
if (!isAllowed) {
return;
}
const global = getGlobal();
if (global.isLoggingOut) {
return;
}
const reducedGlobal: GlobalState = {
...INITIAL_STATE,
...pick(global, [
'authState',
'authPhoneNumber',
'authRememberMe',
'authIsSessionRemembered',
'authNearestCountry',
'currentUserId',
'contactList',
'chatFolders',
'topPeers',
'recentEmojis',
]),
isChatInfoShown: reduceShowChatInfo(global),
users: reduceUsers(global),
chats: reduceChats(global),
messages: reduceMessages(global),
globalSearch: {
recentlyFoundChatIds: global.globalSearch.recentlyFoundChatIds,
},
settings: reduceSettings(global),
};
const json = JSON.stringify(reducedGlobal);
localStorage.setItem(GLOBAL_STATE_CACHE_KEY, json);
});
}
function reduceShowChatInfo(global: GlobalState): boolean {
return window.innerWidth > MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN
? global.isChatInfoShown
: false;
}
function reduceUsers(global: GlobalState): GlobalState['users'] {
const { users: { byId, selectedId } } = global;
const idsToSave = [
...(global.chats.listIds.active || []).slice(0, GLOBAL_STATE_CACHE_CHAT_LIST_LIMIT).filter((cid) => cid > 0),
...Object.keys(byId),
].slice(0, GLOBAL_STATE_CACHE_USER_LIST_LIMIT);
return {
byId: pick(byId, idsToSave as number[]),
selectedId: window.innerWidth > MIN_SCREEN_WIDTH_FOR_STATIC_RIGHT_COLUMN ? selectedId : undefined,
};
}
function reduceChats(global: GlobalState): GlobalState['chats'] {
const chatIdsToSave = [
...(global.chats.listIds.active || []).slice(0, GLOBAL_STATE_CACHE_CHAT_LIST_LIMIT),
];
const { chatId: currentChatId } = selectCurrentMessageList(global) || {};
return {
...global.chats,
byId: pick(global.chats.byId, currentChatId ? [...chatIdsToSave, currentChatId] : chatIdsToSave),
listIds: {
active: chatIdsToSave,
},
isFullyLoaded: {},
orderedPinnedIds: {
active: global.chats.orderedPinnedIds.active,
},
};
}
function reduceMessages(global: GlobalState): GlobalState['messages'] {
const byChatId: GlobalState['messages']['byChatId'] = {};
const { chatId: currentChatId } = selectCurrentMessageList(global) || {};
const chatIdsToSave = [
...(global.chats.listIds.active || []).slice(0, GLOBAL_STATE_CACHE_CHAT_LIST_LIMIT),
...(currentChatId ? [currentChatId] : []),
];
chatIdsToSave.forEach((chatId) => {
const current = global.messages.byChatId[chatId];
if (!current) {
return;
}
const mainThread = current.threadsById[MAIN_THREAD_ID];
if (!mainThread || !mainThread.viewportIds) {
return;
}
byChatId[chatId] = {
byId: pick(current.byId, mainThread.viewportIds),
threadsById: {
[MAIN_THREAD_ID]: mainThread,
},
};
});
const currentMessageList = selectCurrentMessageList(global);
return {
byChatId,
messageLists: !currentMessageList || IS_MOBILE_SCREEN ? undefined : [{
...currentMessageList,
threadId: MAIN_THREAD_ID,
type: 'thread',
}],
};
}
function reduceSettings(global: GlobalState): GlobalState['settings'] {
const { byKey } = global.settings;
return {
byKey,
privacy: {},
};
}