import type { Dialog, DialogsStorage, FiltersStorage } from './appMessagesManager'; import type { AppStickersManager } from './appStickersManager'; import type { AppPeersManager } from './appPeersManager'; import { App, MOUNT_CLASS_TO, UserAuth } from '../mtproto/mtproto_config'; import EventListenerBase from '../../helpers/eventListenerBase'; import $rootScope from '../rootScope'; import AppStorage from '../storage'; import { logger } from '../logger'; import type { AppUsersManager } from './appUsersManager'; import type { AppChatsManager } from './appChatsManager'; const REFRESH_EVERY = 24 * 60 * 60 * 1000; // 1 day const STATE_VERSION = App.version; type State = Partial<{ dialogs: Dialog[], allDialogsLoaded: DialogsStorage['allDialogsLoaded'], //peers: {[peerID: string]: ReturnType}, chats: {[peerID: string]: ReturnType}, users: {[peerID: string]: ReturnType}, messages: any[], contactsList: number[], updates: any, filters: FiltersStorage['filters'], maxSeenMsgID: number, stateCreatedTime: number, recentEmoji: string[], topPeers: number[], recentSearch: number[], stickerSets: AppStickersManager['stickerSets'], version: typeof STATE_VERSION }>; const REFRESH_KEYS = ['dialogs', 'allDialogsLoaded', 'messages', 'contactsList', 'stateCreatedTime', 'updates', 'maxSeenMsgID', 'filters', 'topPeers'] as any as Array; export class AppStateManager extends EventListenerBase<{ save: (state: State) => void }> { public loaded: Promise; private log = logger('STATE'/* , LogLevels.error */); private state: State; constructor() { super(); this.loadSavedState(); } public loadSavedState() { if(this.loaded) return this.loaded; //console.time('load state'); return this.loaded = new Promise((resolve) => { AppStorage.get<[State, UserAuth]>('state', 'user_auth').then(([state, auth]) => { const time = Date.now(); if(state) { if(state.version != STATE_VERSION) { state = {}; } else if((state.stateCreatedTime || 0) + REFRESH_EVERY < time) { this.log('will refresh state', state.stateCreatedTime, time); REFRESH_KEYS.forEach(key => { delete state[key]; }); //state = {}; } } this.state = state || {}; this.state.chats = state.chats || {}; this.state.users = state.users || {}; this.state.version = STATE_VERSION; // ??= doesn't compiles if(!this.state.hasOwnProperty('stateCreatedTime')) { this.state.stateCreatedTime = Date.now(); } this.log('state res', state); //return resolve(); if(auth?.id) { $rootScope.$broadcast('user_auth', {id: auth.id}); } //console.timeEnd('load state'); resolve(state); }).catch(resolve).finally(() => { setInterval(() => this.saveState(), 10000); }); }); } public getState() { return this.state === undefined ? this.loadSavedState() : Promise.resolve(this.state); } public saveState() { if(this.state === undefined) return; //let perf = performance.now(); this.setListenerResult('save', this.state); //this.log('saveState: event time:', performance.now() - perf); //const pinnedOrders = appMessagesManager.dialogsStorage.pinnedOrders; //perf = performance.now(); AppStorage.set({ state: this.state }); //this.log('saveState: storage set time:', performance.now() - perf); } public pushToState(key: T, value: State[T]) { this.state[key] = value; } public setPeer(peerID: number, peer: any) { const container = peerID > 0 ? this.state.users : this.state.chats; if(container.hasOwnProperty(peerID)) return; container[peerID] = peer; } } //console.trace('appStateManager include'); const appStateManager = new AppStateManager(); MOUNT_CLASS_TO && (MOUNT_CLASS_TO.appStateManager = appStateManager); export default appStateManager;