Telegram Web K with changes to work inside I2P
https://web.telegram.i2p/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
223 lines
6.5 KiB
223 lines
6.5 KiB
import AppStorage from '../storage'; |
|
import appMessagesManager, { Dialog, DialogsStorage, FiltersStorage } from './appMessagesManager'; |
|
import appMessagesIDsManager from './appMessagesIDsManager'; |
|
import appPeersManager from './appPeersManager'; |
|
import appChatsManager from './appChatsManager'; |
|
import appUsersManager from './appUsersManager'; |
|
import apiUpdatesManager from './apiUpdatesManager'; |
|
import { copy } from '../utils'; |
|
import { logger } from '../logger'; |
|
|
|
const REFRESH_EVERY = 24 * 60 * 60 * 1000; // 1 day |
|
|
|
type State = Partial<{ |
|
dialogs: Dialog[], |
|
allDialogsLoaded: DialogsStorage['allDialogsLoaded'], |
|
peers: {[peerID: string]: any}, |
|
messages: any[], |
|
contactsList: number[], |
|
updates: any, |
|
filters: FiltersStorage['filters'], |
|
maxSeenMsgID: number, |
|
stateCreatedTime: number, |
|
recentEmoji: string[], |
|
topPeers: number[], |
|
recentSearch: number[] |
|
}>; |
|
|
|
export class AppStateManager { |
|
public loaded: Promise<State>; |
|
private log = logger('STATE'/* , LogLevels.error */); |
|
|
|
private state: State = {}; |
|
|
|
constructor() { |
|
this.loadSavedState(); |
|
} |
|
|
|
public loadSavedState() { |
|
if(this.loaded) return this.loaded; |
|
return this.loaded = new Promise((resolve, reject) => { |
|
AppStorage.get<State>('state').then((state) => { |
|
const time = Date.now(); |
|
if((state?.stateCreatedTime ?? 0) + REFRESH_EVERY < time) { |
|
this.log('will refresh state', state.stateCreatedTime, time); |
|
(['dialogs', 'allDialogsLoaded', 'messages', 'contactsList', 'stateCreatedTime', |
|
'updates', 'maxSeenMsgID', 'filters', 'topPeers'] as any as Array<keyof State>).forEach(key => { |
|
delete state[key]; |
|
}); |
|
//state = {}; |
|
} |
|
|
|
const {dialogs, allDialogsLoaded, peers, messages, contactsList, maxSeenMsgID, updates, filters} = state; |
|
|
|
this.state = state || {}; |
|
this.state.peers = peers || {}; |
|
|
|
if(!this.state.hasOwnProperty('stateCreatedTime')) { |
|
this.state.stateCreatedTime = Date.now(); |
|
} |
|
|
|
this.log('state res', dialogs, messages); |
|
|
|
if(maxSeenMsgID && !appMessagesIDsManager.getMessageIDInfo(maxSeenMsgID)[1]) { |
|
appMessagesManager.maxSeenID = maxSeenMsgID; |
|
} |
|
|
|
//return resolve(); |
|
|
|
if(peers) { |
|
for(let peerID in peers) { |
|
let peer = peers[peerID]; |
|
if(+peerID < 0) appChatsManager.saveApiChat(peer); |
|
else appUsersManager.saveApiUser(peer); |
|
} |
|
} |
|
|
|
if(contactsList && Array.isArray(contactsList) && contactsList.length) { |
|
contactsList.forEach(userID => { |
|
appUsersManager.pushContact(userID); |
|
}); |
|
appUsersManager.contactsFillPromise = Promise.resolve(appUsersManager.contactsList); |
|
} |
|
|
|
if(messages) { |
|
/* let tempID = this.tempID; |
|
|
|
for(let message of messages) { |
|
if(message.id < tempID) { |
|
tempID = message.id; |
|
} |
|
} |
|
|
|
if(tempID != this.tempID) { |
|
this.log('Set tempID to:', tempID); |
|
this.tempID = tempID; |
|
} */ |
|
|
|
appMessagesManager.saveMessages(messages); |
|
|
|
// FIX FILE_REFERENCE_EXPIRED KOSTIL'1999 |
|
for(let message of messages) { |
|
if(message.media) { |
|
appMessagesManager.wrapSingleMessage(message.mid, true); |
|
} |
|
} |
|
} |
|
|
|
if(allDialogsLoaded) { |
|
appMessagesManager.dialogsStorage.allDialogsLoaded = allDialogsLoaded; |
|
} |
|
|
|
if(filters) { |
|
for(const filterID in filters) { |
|
appMessagesManager.filtersStorage.saveDialogFilter(filters[filterID], false); |
|
} |
|
} |
|
|
|
if(dialogs) { |
|
dialogs.forEachReverse(dialog => { |
|
appMessagesManager.saveConversation(dialog); |
|
}); |
|
} |
|
|
|
apiUpdatesManager.attach(updates ?? null); |
|
|
|
resolve(state); |
|
}).catch(resolve).finally(() => { |
|
setInterval(() => this.saveState(), 10000); |
|
}); |
|
}); |
|
} |
|
|
|
public getState() { |
|
return this.loadSavedState(); |
|
} |
|
|
|
public saveState() { |
|
const messages: any[] = []; |
|
const dialogs: Dialog[] = []; |
|
const peers = this.state.peers; |
|
|
|
for(const folderID in appMessagesManager.dialogsStorage.byFolders) { |
|
const folder = appMessagesManager.dialogsStorage.getFolder(+folderID); |
|
|
|
for(let dialog of folder) { |
|
const historyStorage = appMessagesManager.historiesStorage[dialog.peerID]; |
|
const history = [].concat(historyStorage?.pending ?? [], historyStorage?.history ?? []); |
|
|
|
dialog = copy(dialog); |
|
let removeUnread = 0; |
|
for(const mid of history) { |
|
const message = appMessagesManager.getMessage(mid); |
|
if(/* message._ != 'messageEmpty' && */message.id > 0) { |
|
messages.push(message); |
|
|
|
if(message.fromID != dialog.peerID) { |
|
peers[message.fromID] = appPeersManager.getPeer(message.fromID); |
|
} |
|
|
|
dialog.top_message = message.mid; |
|
|
|
break; |
|
} else if(message.pFlags && message.pFlags.unread) { |
|
++removeUnread; |
|
} |
|
} |
|
|
|
if(removeUnread && dialog.unread_count) dialog.unread_count -= removeUnread; |
|
|
|
dialogs.push(dialog); |
|
|
|
peers[dialog.peerID] = appPeersManager.getPeer(dialog.peerID); |
|
} |
|
} |
|
|
|
|
|
const us = apiUpdatesManager.updatesState; |
|
const updates = { |
|
seq: us.seq, |
|
pts: us.pts, |
|
date: us.date |
|
}; |
|
|
|
const contactsList = [...appUsersManager.contactsList]; |
|
for(const userID of contactsList) { |
|
if(!peers[userID]) { |
|
peers[userID] = appUsersManager.getUser(userID); |
|
} |
|
} |
|
|
|
const filters = appMessagesManager.filtersStorage.filters; |
|
//const pinnedOrders = appMessagesManager.dialogsStorage.pinnedOrders; |
|
|
|
AppStorage.set({ |
|
state: Object.assign({}, this.state, { |
|
dialogs, |
|
messages, |
|
allDialogsLoaded: appMessagesManager.dialogsStorage.allDialogsLoaded, |
|
peers, |
|
contactsList, |
|
filters, |
|
//pinnedOrders, |
|
updates, |
|
maxSeenMsgID: appMessagesManager.maxSeenID |
|
}) |
|
}); |
|
} |
|
|
|
public pushToState<T extends keyof State>(key: T, value: State[T]) { |
|
this.state[key] = value; |
|
} |
|
|
|
public pushPeer(peerID: number) { |
|
this.state.peers[peerID] = appPeersManager.getPeer(peerID); |
|
} |
|
} |
|
|
|
const appStateManager = new AppStateManager(); |
|
// @ts-ignore |
|
if(process.env.NODE_ENV != 'production') { |
|
(window as any).appStateManager = appStateManager; |
|
} |
|
export default appStateManager; |