diff --git a/src/components/chat/bubbles.ts b/src/components/chat/bubbles.ts index c48779d0..6df58ee3 100644 --- a/src/components/chat/bubbles.ts +++ b/src/components/chat/bubbles.ts @@ -395,6 +395,10 @@ export default class ChatBubbles { } this.listenerSetter.add(this.bubblesContainer, 'dblclick', (e) => { + if(this.chat.selection.isSelecting || !this.appMessagesManager.canWriteToPeer(this.peerId)) { + return; + } + const bubble = (e.target as HTMLElement).classList.contains('bubble') ? e.target as HTMLElement : null; if(bubble) { const mid = +bubble.dataset.mid @@ -1633,7 +1637,7 @@ export default class ChatBubbles { } if(this.chat.type === 'chat') { - const dialog = this.appMessagesManager.getDialogByPeerId(peerId)[0]; + const dialog = this.appMessagesManager.getDialogOnly(peerId); if(dialog?.pFlags.unread_mark) { this.appMessagesManager.markDialogUnread(peerId, true); } diff --git a/src/components/chat/chat.ts b/src/components/chat/chat.ts index 71f737ca..32e247ae 100644 --- a/src/components/chat/chat.ts +++ b/src/components/chat/chat.ts @@ -22,7 +22,7 @@ import type { AppDraftsManager } from "../../lib/appManagers/appDraftsManager"; import type { ServerTimeManager } from "../../lib/mtproto/serverTimeManager"; import type sessionStorage from '../../lib/sessionStorage'; import EventListenerBase from "../../helpers/eventListenerBase"; -import { logger, LogLevels } from "../../lib/logger"; +import { logger, LogTypes } from "../../lib/logger"; import rootScope from "../../lib/rootScope"; import appSidebarRight from "../sidebarRight"; import ChatBubbles from "./bubbles"; @@ -75,7 +75,7 @@ export default class Chat extends EventListenerBase<{ // * constructor end - this.log = logger('CHAT', LogLevels.log | LogLevels.warn | LogLevels.debug | LogLevels.error); + this.log = logger('CHAT', LogTypes.Log | LogTypes.Warn | LogTypes.Debug | LogTypes.Error); //this.log.error('Chat construction'); this.container.append(this.backgroundEl); diff --git a/src/components/chat/contextMenu.ts b/src/components/chat/contextMenu.ts index 4e524002..04c08961 100644 --- a/src/components/chat/contextMenu.ts +++ b/src/components/chat/contextMenu.ts @@ -186,7 +186,7 @@ export default class ChatContextMenu { icon: 'reply', text: 'Reply', onClick: this.onReplyClick, - verify: () => (this.peerId > 0 || this.appChatsManager.hasRights(-this.peerId, 'send_messages')) && + verify: () => this.appMessagesManager.canWriteToPeer(this.peerId) && !this.message.pFlags.is_outgoing && !!this.chat.input.messageInput && this.chat.type !== 'scheduled'/* , diff --git a/src/components/chat/input.ts b/src/components/chat/input.ts index 03ff0ae0..1ccb3577 100644 --- a/src/components/chat/input.ts +++ b/src/components/chat/input.ts @@ -578,7 +578,7 @@ export default class ChatInput { }; public setUnreadCount() { - const dialog = this.appMessagesManager.getDialogByPeerId(this.chat.peerId)[0]; + const dialog = this.appMessagesManager.getDialogOnly(this.chat.peerId); const count = dialog?.unread_count; this.goDownUnreadBadge.innerText = '' + (count || ''); this.goDownUnreadBadge.classList.toggle('badge-gray', this.appNotificationsManager.isPeerLocalMuted(this.chat.peerId, true)); diff --git a/src/components/chat/topbar.ts b/src/components/chat/topbar.ts index 9de7352b..7c4cbe11 100644 --- a/src/components/chat/topbar.ts +++ b/src/components/chat/topbar.ts @@ -229,7 +229,7 @@ export default class ChatTopbar { onClick: () => { new PopupDeleteDialog(this.peerId); }, - verify: () => this.chat.type === 'chat' && !!this.appMessagesManager.getDialogByPeerId(this.peerId)[0] + verify: () => this.chat.type === 'chat' && !!this.appMessagesManager.getDialogOnly(this.peerId) }]; this.btnSearch = ButtonIcon('search'); diff --git a/src/components/dialogsContextMenu.ts b/src/components/dialogsContextMenu.ts index 7827d27a..6400b0d1 100644 --- a/src/components/dialogsContextMenu.ts +++ b/src/components/dialogsContextMenu.ts @@ -94,7 +94,7 @@ export default class DialogsContextMenu { } private onArchiveClick = () => { - let dialog = appMessagesManager.getDialogByPeerId(this.selectedId)[0]; + let dialog = appMessagesManager.getDialogOnly(this.selectedId); if(dialog) { appMessagesManager.editPeerFolders([dialog.peerId], +!dialog.folder_id); } @@ -113,7 +113,7 @@ export default class DialogsContextMenu { }; private onUnreadClick = () => { - const dialog = appMessagesManager.getDialogByPeerId(this.selectedId)[0]; + const dialog = appMessagesManager.getDialogOnly(this.selectedId); if(!dialog) return; if(dialog.unread_count) { @@ -151,7 +151,7 @@ export default class DialogsContextMenu { this.filterId = appDialogsManager.filterId; this.selectedId = +li.dataset.peerId; - this.dialog = appMessagesManager.getDialogByPeerId(this.selectedId)[0]; + this.dialog = appMessagesManager.getDialogOnly(this.selectedId); this.buttons.forEach(button => { const good = button.verify(); diff --git a/src/components/lazyLoadQueue.ts b/src/components/lazyLoadQueue.ts index 8201b53a..4c4356ed 100644 --- a/src/components/lazyLoadQueue.ts +++ b/src/components/lazyLoadQueue.ts @@ -5,7 +5,7 @@ */ import { throttle } from "../helpers/schedulers"; -import { logger, LogLevels } from "../lib/logger"; +import { logger, LogTypes } from "../lib/logger"; import VisibilityIntersector, { OnVisibilityChange } from "./visibilityIntersector"; import { findAndSpliceAll } from "../helpers/array"; @@ -29,7 +29,7 @@ export class LazyLoadQueueBase { protected lockPromise: Promise = null; protected unlockResolve: () => void = null; - protected log = logger('LL', LogLevels.error); + protected log = logger('LL', LogTypes.Error); protected processQueue: () => void; constructor(protected parallelLimit = PARALLEL_LIMIT) { diff --git a/src/components/scrollable.ts b/src/components/scrollable.ts index 4b3a3bcf..69a2ed3d 100644 --- a/src/components/scrollable.ts +++ b/src/components/scrollable.ts @@ -5,7 +5,7 @@ */ import { isTouchSupported } from "../helpers/touchSupport"; -import { logger, LogLevels } from "../lib/logger"; +import { logger, LogTypes } from "../lib/logger"; import fastSmoothScroll, { FocusDirection } from "../helpers/fastSmoothScroll"; import useHeavyAnimationCheck from "../hooks/useHeavyAnimationCheck"; import { cancelEvent } from "../helpers/dom"; @@ -64,7 +64,7 @@ export class ScrollableBase { constructor(public el: HTMLElement, logPrefix = '', public container: HTMLElement = document.createElement('div')) { this.container.classList.add('scrollable'); - this.log = logger('SCROLL' + (logPrefix ? '-' + logPrefix : ''), LogLevels.error); + this.log = logger('SCROLL' + (logPrefix ? '-' + logPrefix : ''), LogTypes.Error); if(el) { Array.from(el.children).forEach(c => this.container.append(c)); diff --git a/src/components/sidebarLeft/index.ts b/src/components/sidebarLeft/index.ts index 4c34251f..8876a753 100644 --- a/src/components/sidebarLeft/index.ts +++ b/src/components/sidebarLeft/index.ts @@ -466,7 +466,7 @@ export class AppSidebarLeft extends SidebarSlider { appStateManager.pushToState('recentSearch', recentSearch); for(const peerId of recentSearch) { - appStateManager.setPeer(peerId, appPeersManager.getPeer(peerId)); + appStateManager.requestPeer(peerId, 'recentSearch'); } } }); diff --git a/src/layer.d.ts b/src/layer.d.ts index a7f7ac77..2f13cb48 100644 --- a/src/layer.d.ts +++ b/src/layer.d.ts @@ -508,12 +508,7 @@ export namespace User { bot_inline_placeholder?: string, lang_code?: string, initials?: string, - rFirstName?: string, - rFullName?: string, - rPhone?: string, - sortName?: string, - sortStatus?: number, - num?: number + sortName?: string }; } @@ -1225,7 +1220,8 @@ export namespace Dialog { draft?: DraftMessage, folder_id?: number, index?: number, - peerId?: number + peerId?: number, + topMessage?: any }; export type dialogFolder = { diff --git a/src/lib/appManagers/apiUpdatesManager.ts b/src/lib/appManagers/apiUpdatesManager.ts index c91af595..6f98d217 100644 --- a/src/lib/appManagers/apiUpdatesManager.ts +++ b/src/lib/appManagers/apiUpdatesManager.ts @@ -13,7 +13,7 @@ import DEBUG, { MOUNT_CLASS_TO } from '../../config/debug'; import { copy } from '../../helpers/object'; import { Update } from '../../layer'; -import { logger, LogLevels } from '../logger'; +import { logger, LogTypes } from '../logger'; import apiManager from '../mtproto/mtprotoworker'; import rootScope from '../rootScope'; //import networkerFactory from '../mtproto/networkerFactory'; @@ -51,7 +51,7 @@ export class ApiUpdatesManager { public channelStates: {[channelId: number]: UpdatesState} = {}; private attached = false; - private log = logger('UPDATES', LogLevels.error | LogLevels.log | LogLevels.warn | LogLevels.debug); + private log = logger('UPDATES', LogTypes.Error | LogTypes.Warn/* | LogTypes.Log | LogTypes.Debug */); private debug = DEBUG; private popPendingSeqUpdate() { diff --git a/src/lib/appManagers/appChatsManager.ts b/src/lib/appManagers/appChatsManager.ts index aea3d627..70594448 100644 --- a/src/lib/appManagers/appChatsManager.ts +++ b/src/lib/appManagers/appChatsManager.ts @@ -18,7 +18,7 @@ import apiManagerProxy from "../mtproto/mtprotoworker"; import apiManager from '../mtproto/mtprotoworker'; import { RichTextProcessor } from "../richtextprocessor"; import rootScope from "../rootScope"; -//import AppStorage from "../storage"; +import AppStorage from "../storage"; import apiUpdatesManager from "./apiUpdatesManager"; import appMessagesManager from "./appMessagesManager"; import appPeersManager from "./appPeersManager"; @@ -33,9 +33,9 @@ export type ChatRights = keyof ChatBannedRights['pFlags'] | keyof ChatAdminRight export type UserTyping = Partial<{userId: number, action: SendMessageAction, timeout: number}>; export class AppChatsManager { - /* private storage = new AppStorage>({ + private storage = new AppStorage>({ storeName: 'chats' - }); */ + }); private chats: {[id: number]: Chat.channel | Chat.chat | any} = {}; //private usernames: any = {}; @@ -76,6 +76,24 @@ export class AppChatsManager { appStateManager.getState().then((state) => { this.chats = state.chats; + + appStateManager.addEventListener('peerNeeded', (peerId: number) => { + if(peerId > 0 || this.storage.getFromCache(-peerId)) { + return; + } + + this.storage.set({ + [-peerId]: this.getChat(-peerId) + }); + }); + + appStateManager.addEventListener('peerUnneeded', (peerId: number) => { + if(peerId > 0 || !this.storage.getFromCache(-peerId)) { + return; + } + + this.storage.delete(-peerId); + }); }); } @@ -179,8 +197,6 @@ export class AppChatsManager { chat.initials = RichTextProcessor.getAbbreviation(chat.title); - //console.log('im the weatherman', chat.id); - if(chat._ === 'channel' && chat.participants_count === undefined && oldChat !== undefined && @@ -219,9 +235,11 @@ export class AppChatsManager { rootScope.broadcast('peer_title_edit', -chat.id); } - /* this.storage.set({ - [chat.id]: chat - }); */ + if(appStateManager.isPeerNeeded(-chat.id)) { + this.storage.set({ + [chat.id]: chat + }); + } } public getChat(id: number) { diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index f1f54221..4969f51c 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -13,7 +13,7 @@ import { ripple } from "../../components/ripple"; import Scrollable, { ScrollableX, SliceSides } from "../../components/scrollable"; import { formatDateAccordingToTodayNew } from "../../helpers/date"; import { isSafari } from "../../helpers/userAgent"; -import { logger, LogLevels } from "../logger"; +import { logger, LogTypes } from "../logger"; import { RichTextProcessor } from "../richtextprocessor"; import rootScope from "../rootScope"; import { positionElementByIndex, replaceContent } from "../../helpers/dom"; @@ -194,7 +194,7 @@ export class AppDialogsManager { public scroll: Scrollable = null; public _scroll: Scrollable = null; - private log = logger('DIALOGS', LogLevels.log | LogLevels.error | LogLevels.warn | LogLevels.debug); + private log = logger('DIALOGS', LogTypes.Log | LogTypes.Error | LogTypes.Warn | LogTypes.Debug); public contextMenu = new DialogsContextMenu(); @@ -321,7 +321,7 @@ export class AppDialogsManager { rootScope.on('dialog_flush', (e) => { const peerId: number = e.peerId; - const dialog = appMessagesManager.getDialogByPeerId(peerId)[0]; + const dialog = appMessagesManager.getDialogOnly(peerId); if(dialog) { this.setLastMessage(dialog); this.validateForFilter(); @@ -356,7 +356,7 @@ export class AppDialogsManager { rootScope.on('dialog_unread', (e) => { const info = e; - const dialog = appMessagesManager.getDialogByPeerId(info.peerId)[0]; + const dialog = appMessagesManager.getDialogOnly(info.peerId); if(dialog) { this.setUnreadMessages(dialog); this.validateForFilter(); @@ -369,7 +369,7 @@ export class AppDialogsManager { }); rootScope.on('dialog_draft', (e) => { - const dialog = appMessagesManager.getDialogByPeerId(e.peerId)[0]; + const dialog = appMessagesManager.getDialogOnly(e.peerId); if(dialog) { this.updateDialog(dialog); } @@ -455,7 +455,7 @@ export class AppDialogsManager { rootScope.on('peer_typings', (e) => { const {peerId, typings} = e; - const dialog = appMessagesManager.getDialogByPeerId(peerId)[0]; + const dialog = appMessagesManager.getDialogOnly(peerId); if(!dialog) return; if(typings.length) { @@ -1209,7 +1209,7 @@ export class AppDialogsManager { let dialog: Dialog; if(typeof(_dialog) === 'number') { - let originalDialog = appMessagesManager.getDialogByPeerId(_dialog)[0]; + let originalDialog = appMessagesManager.getDialogOnly(_dialog); if(!originalDialog) { originalDialog = { peerId: _dialog, diff --git a/src/lib/appManagers/appDraftsManager.ts b/src/lib/appManagers/appDraftsManager.ts index 92152d25..0a3bde12 100644 --- a/src/lib/appManagers/appDraftsManager.ts +++ b/src/lib/appManagers/appDraftsManager.ts @@ -62,7 +62,7 @@ export class AppDraftsManager { } const peerId = +key; - const dialog = appMessagesManager.getDialogByPeerId(peerId)[0]; + const dialog = appMessagesManager.getDialogOnly(peerId); if(!dialog) { appMessagesManager.reloadConversation(peerId); /* const dialog = appMessagesManager.generateDialog(peerId); diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index 0d2f7690..c9f89b6e 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -9,7 +9,7 @@ import animationIntersector from '../../components/animationIntersector'; import appSidebarLeft, { LEFT_COLUMN_ACTIVE_CLASSNAME } from "../../components/sidebarLeft"; import appSidebarRight, { RIGHT_COLUMN_ACTIVE_CLASSNAME } from '../../components/sidebarRight'; import mediaSizes, { ScreenSize } from '../../helpers/mediaSizes'; -import { logger, LogLevels } from "../logger"; +import { logger, LogTypes } from "../logger"; import apiManager from '../mtproto/mtprotoworker'; import rootScope from '../rootScope'; import apiUpdatesManager from './apiUpdatesManager'; @@ -95,7 +95,7 @@ export class AppImManager { constructor() { apiUpdatesManager.attach(); - this.log = logger('IM', LogLevels.log | LogLevels.warn | LogLevels.debug | LogLevels.error); + this.log = logger('IM', LogTypes.Log | LogTypes.Warn | LogTypes.Debug | LogTypes.Error); this.selectTab(0); diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index f6babe46..d0fd7817 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -20,7 +20,7 @@ import { splitStringByLength, limitSymbols, escapeRegExp } from "../../helpers/s import { Chat, ChatFull, Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMedia, InputMessage, InputPeerNotifySettings, InputSingleMedia, Message, MessageAction, MessageEntity, MessageFwdHeader, MessageMedia, MessageReplies, MessageReplyHeader, MessagesDialogs, MessagesFilter, MessagesMessages, MessagesPeerDialogs, MethodDeclMap, NotifyPeer, PeerNotifySettings, PhotoSize, SendMessageAction, Update, Photo } from "../../layer"; import { InvokeApiOptions } from "../../types"; import I18n, { i18n, join, langPack, LangPackKey, _i18n } from "../langPack"; -import { logger, LogLevels } from "../logger"; +import { logger, LogTypes } from "../logger"; import type { ApiFileManager } from '../mtproto/apiFileManager'; //import apiManager from '../mtproto/apiManager'; import apiManager from '../mtproto/mtprotoworker'; @@ -28,7 +28,6 @@ import referenceDatabase, { ReferenceContext } from "../mtproto/referenceDatabas import serverTimeManager from "../mtproto/serverTimeManager"; import { RichTextProcessor } from "../richtextprocessor"; import rootScope from "../rootScope"; -import searchIndexManager from '../searchIndexManager'; import DialogsStorage from "../storages/dialogs"; import FiltersStorage from "../storages/filters"; //import { telegramMeWebService } from "../mtproto/mtproto"; @@ -172,8 +171,8 @@ export class AppMessagesManager { public newMessagesHandlePromise = 0; public newMessagesToHandle: {[peerId: string]: Set} = {}; - public newDialogsHandlePromise = 0; - public newDialogsToHandle: {[peerId: string]: {reload: true} | Dialog} = {}; + public newDialogsHandlePromise: Promise; + public newDialogsToHandle: {[peerId: string]: Dialog} = {}; public newUpdatesAfterReloadToHandle: {[peerId: string]: Set} = {}; private notificationsHandlePromise = 0; @@ -186,19 +185,7 @@ export class AppMessagesManager { private reloadConversationsPromise: Promise; private reloadConversationsPeers: number[] = []; - private cachedResults: { - query: string, - count: number, - dialogs: Dialog[], - folderId: number - } = { - query: '', - count: 0, - dialogs: [], - folderId: 0 - }; - - private log = logger('MESSAGES', LogLevels.error | LogLevels.debug | LogLevels.log | LogLevels.warn); + public log = logger('MESSAGES', LogTypes.Error | LogTypes.Debug | LogTypes.Log | LogTypes.Warn); public dialogsStorage: DialogsStorage; public filtersStorage: FiltersStorage; @@ -206,7 +193,7 @@ export class AppMessagesManager { private groupedTempId = 0; constructor() { - this.dialogsStorage = new DialogsStorage(this, appChatsManager, appPeersManager, serverTimeManager); + this.dialogsStorage = new DialogsStorage(this, appChatsManager, appPeersManager, appUsersManager, appDraftsManager, appNotificationsManager, appStateManager, apiUpdatesManager, serverTimeManager); this.filtersStorage = new FiltersStorage(this, appPeersManager, appUsersManager, appNotificationsManager, apiUpdatesManager, /* apiManager, */ rootScope); rootScope.addMultipleEventsListeners({ @@ -218,12 +205,6 @@ export class AppMessagesManager { updateDialogUnreadMark: this.onUpdateDialogUnreadMark, - updateFolderPeers: this.onUpdateFolderPeers, - - updateDialogPinned: this.onUpdateDialogPinned, - - updatePinnedDialogs: this.onUpdatePinnedDialogs, - updateEditMessage: this.onUpdateEditMessage, updateEditChannelMessage: this.onUpdateEditMessage, @@ -277,15 +258,6 @@ export class AppMessagesManager { }); }); - rootScope.on('language_change', (e) => { - const peerId = appUsersManager.getSelf().id; - const dialog = this.getDialogByPeerId(peerId)[0]; - if(dialog) { - const peerText = appPeersManager.getPeerSearchText(peerId); - searchIndexManager.indexObject(peerId, peerText, this.dialogsStorage.dialogsIndex); - } - }); - rootScope.on('webpage_updated', (e) => { const eventData = e; eventData.msgs.forEach((mid) => { @@ -311,7 +283,7 @@ export class AppMessagesManager { if(threadId) return; - const dialog = this.getDialogByPeerId(peerId)[0]; + const dialog = this.getDialogOnly(peerId); if(dialog && !threadId) { dialog.draft = draft; this.dialogsStorage.generateIndexForDialog(dialog); @@ -368,7 +340,7 @@ export class AppMessagesManager { forEachReverse(state.dialogs, dialog => { dialog.top_message = this.getServerMessageId(dialog.top_message); // * fix outgoing message to avoid copying dialog - this.saveConversation(dialog); + this.dialogsStorage.saveDialog(dialog); // ! WARNING, убрать это когда нужно будет делать чтобы pending сообщения сохранялись const message = this.getMessageByPeer(dialog.peerId, dialog.top_message); @@ -392,37 +364,27 @@ export class AppMessagesManager { const processDialog = (dialog: MTDialog.dialog) => { const historyStorage = this.getHistoryStorage(dialog.peerId); const history = [].concat(historyStorage.history.slice); - //dialog = copy(dialog); - let removeUnread = 0; for(const mid of history) { const message = this.getMessageByPeer(dialog.peerId, mid); - if(/* message._ !== 'messageEmpty' && */!message.pFlags.is_outgoing) { + if(!message.pFlags.is_outgoing) { messages.push(message); if(message.fromId !== dialog.peerId) { - appStateManager.setPeer(message.fromId, appPeersManager.getPeer(message.fromId)); + appStateManager.requestPeer(message.fromId, 'topMessage_' + dialog.peerId, 1); } - - /* dialog.top_message = message.mid; - this.dialogsStorage.generateIndexForDialog(dialog, false, message); */ - + break; - } else if(message.pFlags && message.pFlags.unread) { - ++removeUnread; } } - if(removeUnread && dialog.unread_count) dialog.unread_count -= removeUnread; - if(dialog.peerId < 0 && dialog.pts) { const newPts = apiUpdatesManager.channelStates[-dialog.peerId].pts; dialog.pts = newPts; } - dialog.unread_count = Math.max(0, dialog.unread_count); dialogs.push(dialog); - appStateManager.setPeer(dialog.peerId, appPeersManager.getPeer(dialog.peerId)); + appStateManager.requestPeer(dialog.peerId, 'dialog'); }; for(const folderId in this.dialogsStorage.byFolders) { @@ -1627,7 +1589,7 @@ export class AppMessagesManager { return message; } - public setDialogTopMessage(message: MyMessage, dialog: MTDialog.dialog = this.getDialogByPeerId(message.peerId)[0]) { + public setDialogTopMessage(message: MyMessage, dialog: MTDialog.dialog = this.getDialogOnly(message.peerId)) { if(dialog) { dialog.top_message = message.mid; @@ -1636,8 +1598,7 @@ export class AppMessagesManager { this.dialogsStorage.generateIndexForDialog(dialog, false, message); - this.newDialogsToHandle[message.peerId] = dialog; - this.scheduleHandleNewDialogs(); + this.scheduleHandleNewDialogs(message.peerId, dialog); } } @@ -1729,72 +1690,7 @@ export class AppMessagesManager { } public getConversations(query = '', offsetIndex?: number, limit = 20, folderId = 0) { - const realFolderId = folderId > 1 ? 0 : folderId; - let curDialogStorage = this.dialogsStorage.getFolder(folderId); - - if(query) { - if(!limit || this.cachedResults.query !== query || this.cachedResults.folderId !== folderId) { - this.cachedResults.query = query; - this.cachedResults.folderId = folderId; - - const results = searchIndexManager.search(query, this.dialogsStorage.dialogsIndex); - - this.cachedResults.dialogs = []; - - for(const peerId in this.dialogsStorage.dialogs) { - const dialog = this.dialogsStorage.dialogs[peerId]; - if(results[dialog.peerId] && dialog.folder_id === folderId) { - this.cachedResults.dialogs.push(dialog); - } - } - - this.cachedResults.dialogs.sort((d1, d2) => d2.index - d1.index); - - this.cachedResults.count = this.cachedResults.dialogs.length; - } - - curDialogStorage = this.cachedResults.dialogs; - } else { - this.cachedResults.query = ''; - } - - let offset = 0; - if(offsetIndex > 0) { - for(; offset < curDialogStorage.length; offset++) { - if(offsetIndex > curDialogStorage[offset].index) { - break; - } - } - } - - if(query || this.dialogsStorage.allDialogsLoaded[realFolderId] || curDialogStorage.length >= offset + limit) { - return Promise.resolve({ - dialogs: curDialogStorage.slice(offset, offset + limit), - count: this.dialogsStorage.allDialogsLoaded[realFolderId] ? curDialogStorage.length : null, - isEnd: this.dialogsStorage.allDialogsLoaded[realFolderId] && (offset + limit) >= curDialogStorage.length - }); - } - - return this.getTopMessages(limit, realFolderId).then(messagesDialogs => { - //const curDialogStorage = this.dialogsStorage[folderId]; - - offset = 0; - if(offsetIndex > 0) { - for(; offset < curDialogStorage.length; offset++) { - if(offsetIndex > curDialogStorage[offset].index) { - break; - } - } - } - - //this.log.warn(offset, offset + limit, curDialogStorage.dialogs.length, this.dialogsStorage.dialogs.length); - - return { - dialogs: curDialogStorage.slice(offset, offset + limit), - count: messagesDialogs._ === 'messages.dialogs' ? messagesDialogs.dialogs.length : messagesDialogs.count, - isEnd: this.dialogsStorage.allDialogsLoaded[realFolderId] && (offset + limit) >= curDialogStorage.length - }; - }); + return this.dialogsStorage.getDialogs(query, offsetIndex, limit, folderId); } public getReadMaxIdIfUnread(peerId: number, threadId?: number) { @@ -1860,7 +1756,7 @@ export class AppMessagesManager { forEachReverse((dialogsResult.dialogs as Dialog[]), dialog => { //const d = Object.assign({}, dialog); // ! нужно передавать folderId, так как по папке !== 0 нет свойства folder_id - this.saveConversation(dialog, dialog.folder_id ?? folderId); + this.dialogsStorage.saveDialog(dialog, dialog.folder_id ?? folderId); if(dialog.peerId === undefined) { return; @@ -1871,7 +1767,7 @@ export class AppMessagesManager { } */ if(offsetIndex && dialog.index > offsetIndex) { - this.newDialogsToHandle[dialog.peerId] = dialog; + this.scheduleHandleNewDialogs(dialog.peerId, dialog); hasPrepend = true; } @@ -2074,6 +1970,10 @@ export class AppMessagesManager { return this.dialogsStorage.getDialog(peerId); } + public getDialogOnly(peerId: number) { + return this.dialogsStorage.getDialogOnly(peerId); + } + public reloadConversation(peerId: number | number[]) { [].concat(peerId).forEach(peerId => { if(!this.reloadConversationsPeers.includes(peerId)) { @@ -2089,7 +1989,7 @@ export class AppMessagesManager { this.reloadConversationsPeers.length = 0; apiManager.invokeApi('messages.getPeerDialogs', {peers}).then((result) => { - this.applyConversations(result); + this.dialogsStorage.applyDialogs(result); resolve(); }, reject).finally(() => { this.reloadConversationsPromise = null; @@ -2288,7 +2188,7 @@ export class AppMessagesManager { } public generateTempMessageId(peerId: number) { - const dialog = this.getDialogByPeerId(peerId)[0]; + const dialog = this.getDialogOnly(peerId); return this.generateMessageId(dialog?.top_message || 0, true); } @@ -2369,7 +2269,7 @@ export class AppMessagesManager { storage[mid] = message; } - const dialog = this.getDialogByPeerId(peerId)[0]; + const dialog = this.getDialogOnly(peerId); if(dialog && mid) { if(mid > dialog[message.pFlags.out ? 'read_outbox_max_id' @@ -2941,11 +2841,10 @@ export class AppMessagesManager { public toggleDialogPin(peerId: number, filterId?: number) { if(filterId > 1) { - this.filtersStorage.toggleDialogPin(peerId, filterId); - return; + return this.filtersStorage.toggleDialogPin(peerId, filterId); } - const dialog = this.getDialogByPeerId(peerId)[0]; + const dialog = this.getDialogOnly(peerId); if(!dialog) return Promise.reject(); const pinned = dialog.pFlags?.pinned ? undefined : true; @@ -2955,7 +2854,7 @@ export class AppMessagesManager { }).then(bool => { if(bool) { const pFlags: Update.updateDialogPinned['pFlags'] = pinned ? {pinned} : {}; - this.onUpdateDialogPinned({ + apiUpdatesManager.saveUpdate({ _: 'updateDialogPinned', peer: appPeersManager.getDialogPeer(peerId), folder_id: filterId, @@ -2966,7 +2865,7 @@ export class AppMessagesManager { } public markDialogUnread(peerId: number, read?: true) { - const dialog = this.getDialogByPeerId(peerId)[0]; + const dialog = this.getDialogOnly(peerId); if(!dialog) return Promise.reject(); const unread = read || dialog.pFlags?.unread_mark ? undefined : true; @@ -3067,191 +2966,6 @@ export class AppMessagesManager { ) && !message.pFlags.is_outgoing; } - public applyConversations(dialogsResult: MessagesPeerDialogs.messagesPeerDialogs) { - // * В эту функцию попадут только те диалоги, в которых есть read_inbox_max_id и read_outbox_max_id, в отличие от тех, что будут в getTopMessages - - // ! fix 'dialogFolder', maybe there is better way to do it, this only can happen by 'messages.getPinnedDialogs' by folder_id: 0 - forEachReverse(dialogsResult.dialogs, (dialog, idx) => { - if(dialog._ === 'dialogFolder') { - dialogsResult.dialogs.splice(idx, 1); - } - }); - - appUsersManager.saveApiUsers(dialogsResult.users); - appChatsManager.saveApiChats(dialogsResult.chats); - this.saveMessages(dialogsResult.messages); - - this.log('applyConversation', dialogsResult); - - const updatedDialogs: {[peerId: number]: Dialog} = {}; - (dialogsResult.dialogs as Dialog[]).forEach((dialog) => { - const peerId = appPeersManager.getPeerId(dialog.peer); - let topMessage = dialog.top_message; - - const topPendingMessage = this.pendingTopMsgs[peerId]; - if(topPendingMessage) { - if(!topMessage - || (this.getMessageByPeer(peerId, topPendingMessage) as MyMessage).date > (this.getMessageByPeer(peerId, topMessage) as MyMessage).date) { - dialog.top_message = topMessage = topPendingMessage; - this.getHistoryStorage(peerId).maxId = topPendingMessage; - } - } - - /* const d = Object.assign({}, dialog); - if(peerId === 239602833) { - this.log.error('applyConversation lun', dialog, d); - } */ - - if(topMessage || (dialog.draft && dialog.draft._ === 'draftMessage')) { - this.saveConversation(dialog); - updatedDialogs[peerId] = dialog; - } else { - const dropped = this.dialogsStorage.dropDialog(peerId); - if(dropped.length) { - rootScope.broadcast('dialog_drop', {peerId, dialog: dropped[0]}); - } - } - - if(this.newUpdatesAfterReloadToHandle[peerId] !== undefined) { - for(const update of this.newUpdatesAfterReloadToHandle[peerId]) { - apiUpdatesManager.saveUpdate(update); - } - - delete this.newUpdatesAfterReloadToHandle[peerId]; - } - }); - - if(Object.keys(updatedDialogs).length) { - rootScope.broadcast('dialogs_multiupdate', updatedDialogs); - } - } - - public generateDialog(peerId: number) { - const dialog: Dialog = { - _: 'dialog', - pFlags: {}, - peer: appPeersManager.getOutputPeer(peerId), - top_message: 0, - read_inbox_max_id: 0, - read_outbox_max_id: 0, - unread_count: 0, - unread_mentions_count: 0, - notify_settings: { - _: 'peerNotifySettings', - }, - }; - - return dialog; - } - - /** - * Won't save migrated from peer, forbidden peers, left and kicked - */ - public saveConversation(dialog: Dialog, folderId = 0) { - const peerId = appPeersManager.getPeerId(dialog.peer); - if(!peerId) { - console.error('saveConversation no peerId???', dialog, folderId); - return false; - } - - if(dialog._ !== 'dialog'/* || peerId === 239602833 */) { - console.error('saveConversation not regular dialog', dialog, Object.assign({}, dialog)); - } - - const channelId = appPeersManager.isChannel(peerId) ? -peerId : 0; - - if(peerId < 0) { - const chat: Chat = appChatsManager.getChat(-peerId); - if(chat._ === 'channelForbidden' || chat._ === 'chatForbidden' || (chat as Chat.chat).pFlags.left || (chat as Chat.chat).pFlags.kicked) { - return false; - } - } - - const peerText = appPeersManager.getPeerSearchText(peerId); - searchIndexManager.indexObject(peerId, peerText, this.dialogsStorage.dialogsIndex); - - let mid: number, message; - if(dialog.top_message) { - mid = this.generateMessageId(dialog.top_message);//dialog.top_message; - message = this.getMessageByPeer(peerId, mid); - } else { - mid = this.generateTempMessageId(peerId); - message = { - _: 'message', - id: mid, - mid, - from_id: appPeersManager.getOutputPeer(appUsersManager.getSelf().id), - peer_id: appPeersManager.getOutputPeer(peerId), - deleted: true, - pFlags: {out: true}, - date: 0, - message: '' - }; - this.saveMessages([message], {isOutgoing: true}); - } - - if(!message?.pFlags) { - this.log.error('saveConversation no message:', dialog, message); - } - - if(!channelId && peerId < 0) { - const chat = appChatsManager.getChat(-peerId); - if(chat && chat.migrated_to && chat.pFlags.deactivated) { - const migratedToPeer = appPeersManager.getPeerId(chat.migrated_to); - this.migratedFromTo[peerId] = migratedToPeer; - this.migratedToFrom[migratedToPeer] = peerId; - return; - } - } - - const wasDialogBefore = this.getDialogByPeerId(peerId)[0]; - - dialog.top_message = mid; - dialog.read_inbox_max_id = this.generateMessageId(wasDialogBefore && !dialog.read_inbox_max_id ? wasDialogBefore.read_inbox_max_id : dialog.read_inbox_max_id); - dialog.read_outbox_max_id = this.generateMessageId(wasDialogBefore && !dialog.read_outbox_max_id ? wasDialogBefore.read_outbox_max_id : dialog.read_outbox_max_id); - - if(!dialog.hasOwnProperty('folder_id')) { - if(dialog._ === 'dialog') { - // ! СЛОЖНО ! СМОТРИ В getTopMessages - dialog.folder_id = wasDialogBefore ? wasDialogBefore.folder_id : folderId; - }/* else if(dialog._ === 'dialogFolder') { - dialog.folder_id = dialog.folder.id; - } */ - } - - dialog.draft = appDraftsManager.saveDraft(peerId, 0, dialog.draft); - dialog.peerId = peerId; - - // Because we saved message without dialog present - if(message.pFlags.is_outgoing) { - if(mid > dialog[message.pFlags.out ? 'read_outbox_max_id' : 'read_inbox_max_id']) message.pFlags.unread = true; - else delete message.pFlags.unread; - } - - let historyStorage = this.getHistoryStorage(peerId); - /* if(historyStorage === undefined) { // warning - historyStorage.history.push(mid); - if(this.mergeReplyKeyboard(historyStorage, message)) { - rootScope.broadcast('history_reply_markup', {peerId}); - } - } else */if(!historyStorage.history.slice.length) { - historyStorage.history.unshift(mid); - } - - historyStorage.maxId = mid; - historyStorage.readMaxId = dialog.read_inbox_max_id; - historyStorage.readOutboxMaxId = dialog.read_outbox_max_id; - - appNotificationsManager.savePeerSettings(peerId, dialog.notify_settings) - - if(channelId && dialog.pts) { - apiUpdatesManager.addChannelState(channelId, dialog.pts); - } - - this.dialogsStorage.generateIndexForDialog(dialog); - this.dialogsStorage.pushDialog(dialog, message.date); - } - public mergeReplyKeyboard(historyStorage: HistoryStorage, message: any) { // this.log('merge', message.mid, message.reply_markup, historyStorage.reply_markup) if(!message.reply_markup && @@ -3689,15 +3403,13 @@ export class AppMessagesManager { }; handleNewDialogs = () => { - clearTimeout(this.newDialogsHandlePromise); - this.newDialogsHandlePromise = 0; - let newMaxSeenId = 0; - for(const peerId in this.newDialogsToHandle) { - const dialog = this.newDialogsToHandle[peerId]; - if('reload' in dialog) { + const obj = this.newDialogsToHandle; + for(const peerId in obj) { + const dialog = obj[peerId]; + if(!dialog) { this.reloadConversation(+peerId); - delete this.newDialogsToHandle[peerId]; + delete obj[peerId]; } else { this.dialogsStorage.pushDialog(dialog); if(!appPeersManager.isChannel(+peerId)) { @@ -3712,14 +3424,22 @@ export class AppMessagesManager { this.incrementMaxSeenId(newMaxSeenId); } - rootScope.broadcast('dialogs_multiupdate', this.newDialogsToHandle as any); + rootScope.broadcast('dialogs_multiupdate', obj); this.newDialogsToHandle = {}; }; - public scheduleHandleNewDialogs() { - if(!this.newDialogsHandlePromise) { - this.newDialogsHandlePromise = window.setTimeout(this.handleNewDialogs, 0); + public scheduleHandleNewDialogs(peerId?: number, dialog?: Dialog) { + if(peerId !== undefined) { + this.newDialogsToHandle[peerId] = dialog; } + + if(this.newDialogsHandlePromise) return this.newDialogsHandlePromise; + return this.newDialogsHandlePromise = new Promise((resolve) => { + setTimeout(() => { + this.newDialogsHandlePromise = undefined; + this.handleNewDialogs(); + }, 0); + }); } public deleteMessages(peerId: number, mids: number[], revoke?: true) { @@ -4009,7 +3729,7 @@ export class AppMessagesManager { const message = update.message as MyMessage; const peerId = this.getMessagePeer(message); const storage = this.getMessagesStorage(peerId); - const foundDialog = this.getDialogByPeerId(peerId); + const dialog = this.getDialogOnly(peerId); // * local update const isLocalThreadUpdate = update._ === 'updateNewDiscussionMessage'; @@ -4028,7 +3748,7 @@ export class AppMessagesManager { this.onUpdateNewMessage(update); } - if(!foundDialog.length && !isLocalThreadUpdate) { + if(!dialog && !isLocalThreadUpdate) { let good = true; if(peerId < 0) { const chat = appChatsManager.getChat(-peerId); @@ -4048,9 +3768,8 @@ export class AppMessagesManager { return; } - this.newDialogsToHandle[peerId] = {reload: true}; - this.scheduleHandleNewDialogs(); - this.newUpdatesAfterReloadToHandle[peerId].add(update); + this.scheduleHandleNewDialogs(peerId); + set.add(update); } return; @@ -4118,7 +3837,6 @@ export class AppMessagesManager { return; } - const dialog = foundDialog[0]; const inboxUnread = !message.pFlags.out && message.pFlags.unread; if(dialog) { this.setDialogTopMessage(message, dialog); @@ -4157,14 +3875,11 @@ export class AppMessagesManager { private onUpdateDialogUnreadMark = (update: Update.updateDialogUnreadMark) => { //this.log('updateDialogUnreadMark', update); const peerId = appPeersManager.getPeerId((update.peer as DialogPeer.dialogPeer).peer); - const foundDialog = this.getDialogByPeerId(peerId); + const dialog = this.getDialogOnly(peerId); - if(!foundDialog.length) { - this.newDialogsToHandle[peerId] = {reload: true}; - this.scheduleHandleNewDialogs(); + if(!dialog) { + this.scheduleHandleNewDialogs(peerId); } else { - const dialog = foundDialog[0]; - if(!update.pFlags.unread) { delete dialog.pFlags.unread_mark; } else { @@ -4175,142 +3890,6 @@ export class AppMessagesManager { } }; - // only 0 and 1 folders - private onUpdateFolderPeers = (update: Update.updateFolderPeers) => { - //this.log('updateFolderPeers', update); - const peers = update.folder_peers; - - this.scheduleHandleNewDialogs(); - peers.forEach((folderPeer) => { - const {folder_id, peer} = folderPeer; - - const peerId = appPeersManager.getPeerId(peer); - const dropped = this.dialogsStorage.dropDialog(peerId); - if(!dropped.length) { - this.newDialogsToHandle[peerId] = {reload: true}; - } else { - const dialog = dropped[0]; - this.newDialogsToHandle[peerId] = dialog; - - if(dialog.pFlags?.pinned) { - delete dialog.pFlags.pinned; - this.dialogsStorage.pinnedOrders[folder_id].findAndSplice(p => p === dialog.peerId); - } - - dialog.folder_id = folder_id; - - this.dialogsStorage.generateIndexForDialog(dialog); - this.dialogsStorage.pushDialog(dialog); // need for simultaneously updatePinnedDialogs - } - }); - }; - - private onUpdateDialogPinned = (update: Update.updateDialogPinned) => { - const folderId = update.folder_id ?? 0; - //this.log('updateDialogPinned', update); - const peerId = appPeersManager.getPeerId((update.peer as DialogPeer.dialogPeer).peer); - const foundDialog = this.getDialogByPeerId(peerId); - - // этот код внизу никогда не сработает, в папках за пиннед отвечает updateDialogFilter - /* if(update.folder_id > 1) { - const filter = this.filtersStorage.filters[update.folder_id]; - if(update.pFlags.pinned) { - filter.pinned_peers.unshift(peerId); - } else { - filter.pinned_peers.findAndSplice(p => p === peerId); - } - } */ - - this.scheduleHandleNewDialogs(); - if(!foundDialog.length) { - this.newDialogsToHandle[peerId] = {reload: true}; - } else { - const dialog = foundDialog[0]; - this.newDialogsToHandle[peerId] = dialog; - - if(!update.pFlags.pinned) { - delete dialog.pFlags.pinned; - this.dialogsStorage.pinnedOrders[folderId].findAndSplice(p => p === dialog.peerId); - } else { // means set - dialog.pFlags.pinned = true; - } - - this.dialogsStorage.generateIndexForDialog(dialog); - } - }; - - private onUpdatePinnedDialogs = (update: Update.updatePinnedDialogs) => { - const folderId = update.folder_id ?? 0; - - const handleOrder = (order: number[]) => { - this.dialogsStorage.pinnedOrders[folderId].length = 0; - let willHandle = false; - order.reverse(); // index must be higher - order.forEach((peerId) => { - newPinned[peerId] = true; - - const foundDialog = this.getDialogByPeerId(peerId); - if(!foundDialog.length) { - this.newDialogsToHandle[peerId] = {reload: true}; - willHandle = true; - return; - } - - const dialog = foundDialog[0]; - dialog.pFlags.pinned = true; - this.dialogsStorage.generateIndexForDialog(dialog); - - this.newDialogsToHandle[peerId] = dialog; - willHandle = true; - }); - - this.dialogsStorage.getFolder(folderId).forEach(dialog => { - const peerId = dialog.peerId; - if(dialog.pFlags.pinned && !newPinned[peerId]) { - this.newDialogsToHandle[peerId] = {reload: true}; - willHandle = true; - } - }); - - if(willHandle) { - this.scheduleHandleNewDialogs(); - } - }; - - //this.log('updatePinnedDialogs', update); - const newPinned: {[peerId: number]: true} = {}; - if(!update.order) { - apiManager.invokeApi('messages.getPinnedDialogs', { - folder_id: folderId - }).then((dialogsResult) => { - // * for test reordering and rendering - // dialogsResult.dialogs.reverse(); - - this.applyConversations(dialogsResult); - - handleOrder(dialogsResult.dialogs.map(d => d.peerId)); - - /* dialogsResult.dialogs.forEach((dialog) => { - newPinned[dialog.peerId] = true; - }); - - this.dialogsStorage.getFolder(folderId).forEach((dialog) => { - const peerId = dialog.peerId; - if(dialog.pFlags.pinned && !newPinned[peerId]) { - this.newDialogsToHandle[peerId] = {reload: true}; - this.scheduleHandleNewDialogs(); - } - }); */ - }); - - return; - } - - //this.log('before order:', this.dialogsStorage[0].map(d => d.peerId)); - - handleOrder(update.order.map(peer => appPeersManager.getPeerId((peer as DialogPeer.dialogPeer).peer))); - }; - private onUpdateEditMessage = (update: Update.updateEditMessage | Update.updateEditChannelMessage) => { const message = update.message as MyMessage; const peerId = this.getMessagePeer(message); @@ -4328,7 +3907,7 @@ export class AppMessagesManager { this.handleEditedMessage(oldMessage, newMessage); - const dialog = this.getDialogByPeerId(peerId)[0]; + const dialog = this.getDialogOnly(peerId); const isTopMessage = dialog && dialog.top_message === mid; // @ts-ignore if(message.clear_history) { // that's will never happen @@ -4362,7 +3941,7 @@ export class AppMessagesManager { const storage = this.getMessagesStorage(peerId); const history = getObjectKeysAndSort(storage, 'desc'); - const foundDialog = this.getDialogByPeerId(peerId)[0]; + const foundDialog = this.getDialogOnly(peerId); const stillUnreadCount = (update as Update.updateReadChannelInbox).still_unread_count; let newUnreadCount = 0; let foundAffected = false; @@ -4531,7 +4110,7 @@ export class AppMessagesManager { rootScope.broadcast('history_delete', {peerId, msgs: historyUpdated.msgs}); - const foundDialog = this.getDialogByPeerId(peerId)[0]; + const foundDialog = this.getDialogOnly(peerId); if(foundDialog) { if(historyUpdated.unread) { foundDialog.unread_count -= historyUpdated.unread; @@ -4551,8 +4130,7 @@ export class AppMessagesManager { const channel = appChatsManager.getChat(channelId); const needDialog = channel._ === 'channel' && (!channel.pFlags.left && !channel.pFlags.kicked); - const foundDialog = this.getDialogByPeerId(peerId); - const hasDialog = foundDialog.length > 0; + const dialog = this.getDialogOnly(peerId); const canViewHistory = channel._ === 'channel' && (channel.username || !channel.pFlags.left && !channel.pFlags.kicked); const hasHistory = this.historiesStorage[peerId] !== undefined; @@ -4562,13 +4140,13 @@ export class AppMessagesManager { rootScope.broadcast('history_forbidden', peerId); } - if(hasDialog !== needDialog) { + if(!!dialog !== needDialog) { if(needDialog) { this.reloadConversation(-channelId); } else { - if(foundDialog[0]) { + if(dialog) { this.dialogsStorage.dropDialog(peerId); - rootScope.broadcast('dialog_drop', {peerId: peerId, dialog: foundDialog[0]}); + rootScope.broadcast('dialog_drop', {peerId, dialog}); } } } @@ -4695,7 +4273,7 @@ export class AppMessagesManager { if(peer._ === 'notifyPeer') { const peerId = appPeersManager.getPeerId((peer as NotifyPeer.notifyPeer).peer); - const dialog = this.getDialogByPeerId(peerId)[0]; + const dialog = this.getDialogOnly(peerId); if(dialog) { dialog.notify_settings = notify_settings; rootScope.broadcast('dialog_notify_settings', dialog); @@ -4800,14 +4378,10 @@ export class AppMessagesManager { }; if(mute === undefined) { - mute = false; - const dialog = appMessagesManager.getDialogByPeerId(peerId)[0]; - if(dialog && dialog.notify_settings) { - mute = (dialog.notify_settings.mute_until || 0) <= (Date.now() / 1000 | 0); - } + mute = !appNotificationsManager.isPeerLocalMuted(peerId, false); } - settings.mute_until = mute ? 0xFFFFFFFF : 0; + settings.mute_until = mute ? 0x7FFFFFFF : 0; return appNotificationsManager.updateNotifySettings({ _: 'inputNotifyPeer', diff --git a/src/lib/appManagers/appNotificationsManager.ts b/src/lib/appManagers/appNotificationsManager.ts index 3578e2e2..63e1bc78 100644 --- a/src/lib/appManagers/appNotificationsManager.ts +++ b/src/lib/appManagers/appNotificationsManager.ts @@ -409,7 +409,7 @@ export class AppNotificationsManager { public isMuted(peerNotifySettings: PeerNotifySettings) { return peerNotifySettings._ === 'peerNotifySettings' && - (peerNotifySettings.mute_until * 1000) > tsNow(); + ((peerNotifySettings.mute_until * 1000) > tsNow() || peerNotifySettings.silent); } public getPeerMuted(peerId: number) { @@ -488,7 +488,8 @@ export class AppNotificationsManager { }; public notify(data: NotifyOptions) { - console.log('notify', data, rootScope.idle.isIDLE, this.notificationsUiSupport, this.stopped); + //console.log('notify', data, rootScope.idle.isIDLE, this.notificationsUiSupport, this.stopped); + if(this.stopped) { return; } @@ -566,7 +567,8 @@ export class AppNotificationsManager { tag: data.tag || '', silent: data.silent || false }); - console.log('notify constructed notification'); + + //console.log('notify constructed notification'); } catch(e) { this.notificationsUiSupport = false; //WebPushApiManager.setLocalNotificationsDisabled(); diff --git a/src/lib/appManagers/appPollsManager.ts b/src/lib/appManagers/appPollsManager.ts index 5881ccaf..6a60a644 100644 --- a/src/lib/appManagers/appPollsManager.ts +++ b/src/lib/appManagers/appPollsManager.ts @@ -7,7 +7,7 @@ import { MOUNT_CLASS_TO } from "../../config/debug"; import { copy } from "../../helpers/object"; import { InputMedia, MessageEntity } from "../../layer"; -import { logger, LogLevels } from "../logger"; +import { logger, LogTypes } from "../logger"; import apiManager from "../mtproto/mtprotoworker"; import { RichTextProcessor } from "../richtextprocessor"; import rootScope from "../rootScope"; @@ -80,7 +80,7 @@ export class AppPollsManager { public polls: {[id: string]: Poll} = {}; public results: {[id: string]: PollResults} = {}; - private log = logger('POLLS', LogLevels.error); + private log = logger('POLLS', LogTypes.Error); constructor() { rootScope.addMultipleEventsListeners({ diff --git a/src/lib/appManagers/appStateManager.ts b/src/lib/appManagers/appStateManager.ts index ff147361..4291cdf3 100644 --- a/src/lib/appManagers/appStateManager.ts +++ b/src/lib/appManagers/appStateManager.ts @@ -157,7 +157,9 @@ const REFRESH_KEYS = ['dialogs', 'allDialogsLoaded', 'messages', 'contactsList', 'updates', 'maxSeenMsgId', 'filters', 'topPeers'] as any as Array; export class AppStateManager extends EventListenerBase<{ - save: (state: State) => Promise + save: (state: State) => Promise, + peerNeeded: (peerId: number) => void, + peerUnneeded: (peerId: number) => void, }> { public static STATE_INIT = STATE_INIT; public loaded: Promise; @@ -167,6 +169,9 @@ export class AppStateManager extends EventListenerBase<{ private savePromise: Promise; private tempId = 0; + private neededPeers: Map> = new Map(); + private singlePeerMap: Map = new Map(); + constructor() { super(); this.loadSavedState(); @@ -266,7 +271,7 @@ export class AppStateManager extends EventListenerBase<{ public saveState() { if(this.state === undefined || this.savePromise) return; - //return; + return; const tempId = this.tempId; this.savePromise = getHeavyAnimationPromise().then(() => { @@ -295,10 +300,18 @@ export class AppStateManager extends EventListenerBase<{ public setByKey(key: string, value: any) { setDeepProperty(this.state, key, value); rootScope.broadcast('settings_updated', {key, value}); + + const first = key.split('.')[0]; + // @ts-ignore + this.pushToState(first, this.state[first]); } public pushToState(key: T, value: State[T]) { this.state[key] = value; + + sessionStorage.set({ + [key]: value + }); } public setPeer(peerId: number, peer: any) { @@ -307,7 +320,45 @@ export class AppStateManager extends EventListenerBase<{ container[peerId] = peer; } - public resetState() { + public requestPeer(peerId: number, type: string, limit?: number) { + let set = this.neededPeers.get(peerId); + if(set && set.has(type)) { + return; + } + + if(!set) { + set = new Set(); + this.neededPeers.set(peerId, set); + } + + set.add(type); + this.dispatchEvent('peerNeeded', peerId); + + if(limit !== undefined) { + this.keepPeerSingle(peerId, type); + } + } + + public isPeerNeeded(peerId: number) { + return this.neededPeers.has(peerId); + } + + public keepPeerSingle(peerId: number, type: string) { + const existsPeerId = this.singlePeerMap.get(type); + if(existsPeerId && existsPeerId !== peerId) { + const set = this.neededPeers.get(existsPeerId); + set.delete(type); + + if(!set.size) { + this.neededPeers.delete(existsPeerId); + this.dispatchEvent('peerUnneeded', existsPeerId); + } + } + + this.singlePeerMap.set(type, peerId); + } + + /* public resetState() { for(let i in this.state) { // @ts-ignore this.state[i] = false; @@ -315,7 +366,7 @@ export class AppStateManager extends EventListenerBase<{ sessionStorage.set(this.state).then(() => { location.reload(); }); - } + } */ } //console.trace('appStateManager include'); diff --git a/src/lib/appManagers/appStickersManager.ts b/src/lib/appManagers/appStickersManager.ts index b1e7d2d5..e67f8146 100644 --- a/src/lib/appManagers/appStickersManager.ts +++ b/src/lib/appManagers/appStickersManager.ts @@ -187,7 +187,7 @@ export class AppStickersManager { if(res) { delete set.installed_date; rootScope.broadcast('stickers_deleted', set); - this.storage.remove(set.id, true); + this.storage.delete(set.id, true); return true; } } else { diff --git a/src/lib/appManagers/appUsersManager.ts b/src/lib/appManagers/appUsersManager.ts index 9c13d95e..07caaac2 100644 --- a/src/lib/appManagers/appUsersManager.ts +++ b/src/lib/appManagers/appUsersManager.ts @@ -22,7 +22,7 @@ import serverTimeManager from "../mtproto/serverTimeManager"; import { RichTextProcessor } from "../richtextprocessor"; import rootScope from "../rootScope"; import searchIndexManager from "../searchIndexManager"; -//import AppStorage from "../storage"; +import AppStorage from "../storage"; import apiUpdatesManager from "./apiUpdatesManager"; import appChatsManager from "./appChatsManager"; import appPeersManager from "./appPeersManager"; @@ -33,9 +33,9 @@ import appStateManager from "./appStateManager"; export type User = MTUser.user; export class AppUsersManager { - /* private storage = new AppStorage>({ + private storage = new AppStorage>({ storeName: 'users' - }); */ + }); private users: {[userId: number]: User} = {}; private usernames: {[username: string]: number} = {}; @@ -47,8 +47,6 @@ export class AppUsersManager { private getTopPeersPromise: Promise; constructor() { - //this.users = this.storage.getCache(); - setInterval(this.updateUsersStatuses, 60000); rootScope.on('state_synchronized', this.updateUsersStatuses); @@ -132,11 +130,29 @@ export class AppUsersManager { appStateManager.addEventListener('save', async() => { const contactsList = [...this.contactsList]; for(const userId of contactsList) { - appStateManager.setPeer(userId, this.getUser(userId)); + appStateManager.requestPeer(userId, 'contacts'); } appStateManager.pushToState('contactsList', contactsList); }); + + appStateManager.addEventListener('peerNeeded', (peerId: number) => { + if(peerId < 0 || this.storage.getFromCache(peerId)) { + return; + } + + this.storage.set({ + [peerId]: this.getUser(peerId) + }); + }); + + appStateManager.addEventListener('peerUnneeded', (peerId: number) => { + if(peerId < 0 || !this.storage.getFromCache(peerId)) { + return; + } + + this.storage.delete(peerId); + }); }); } @@ -330,15 +346,15 @@ export class AppUsersManager { rootScope.broadcast('user_update', userId); } - //console.log('we never give this up'); - - /* this.storage.set({ - [userId]: user - }); */ - if(changedTitle) { rootScope.broadcast('peer_title_edit', user.id); } + + if(appStateManager.isPeerNeeded(userId)) { + this.storage.set({ + [userId]: user + }); + } } public formatUserPhone(phone: string) { @@ -670,7 +686,7 @@ export class AppUsersManager { if(result.categories.length) { peerIds = result.categories[0].peers.map((topPeer) => { const peerId = appPeersManager.getPeerId(topPeer.peer); - appStateManager.setPeer(peerId, this.getUser(peerId)); + appStateManager.requestPeer(peerId, 'topPeer'); return peerId; }); } diff --git a/src/lib/crypto/srp.ts b/src/lib/crypto/srp.ts index 27ea3dba..e0755def 100644 --- a/src/lib/crypto/srp.ts +++ b/src/lib/crypto/srp.ts @@ -2,12 +2,12 @@ import CryptoWorker from "../crypto/cryptoworker"; import {str2bigInt, isZero, bigInt2str, powMod, int2bigInt, mult, mod, sub, bitSize, negative, add, greater} from '../../vendor/leemon'; -import {logger, LogLevels} from '../logger'; +import {logger, LogTypes} from '../logger'; import { AccountPassword, PasswordKdfAlgo } from "../../layer"; import { bufferConcats, bytesToHex, bytesFromHex, bufferConcat, bytesXor } from "../../helpers/bytes"; //import { MOUNT_CLASS_TO } from "../../config/debug"; -const log = logger('SRP', LogLevels.error); +const log = logger('SRP', LogTypes.Error); //MOUNT_CLASS_TO && Object.assign(MOUNT_CLASS_TO, {str2bigInt, bigInt2str, int2bigInt}); diff --git a/src/lib/idb.ts b/src/lib/idb.ts index 3eb2c698..1a2197f7 100644 --- a/src/lib/idb.ts +++ b/src/lib/idb.ts @@ -36,21 +36,23 @@ export type IDBOptions = { }; export default class IDBStorage { - //public static STORAGES: IDBStorage[] = []; - public openDbPromise: Promise; - public storageIsAvailable = true; + //private static STORAGES: IDBStorage[] = []; + private openDbPromise: Promise; + private storageIsAvailable = true; - private log: ReturnType = logger('IDB'); + private log: ReturnType; - public name: string = Database.name; - public version: number = Database.version; - public stores: IDBStore[] = Database.stores; + private name: string = Database.name; + private version: number = Database.version; + private stores: IDBStore[] = Database.stores; - public storeName: string; + private storeName: string; constructor(options: IDBOptions) { safeAssign(this, options); + this.log = logger('IDB-' + this.storeName); + this.openDatabase(true); //IDBStorage.STORAGES.push(this); @@ -161,35 +163,44 @@ export default class IDBStorage { }); } - public delete(entryName: string): Promise { + public delete(entryName: string | string[]): Promise { //return Promise.resolve(); return this.openDatabase().then((db) => { - try { - //this.log('delete: `' + entryName + '`'); - var objectStore = db.transaction([this.storeName], 'readwrite') - .objectStore(this.storeName); + return new Promise((resolve, reject) => { + try { + //this.log('delete: `' + entryName + '`'); + const transaction = db.transaction([this.storeName], 'readwrite'); + const objectStore = transaction.objectStore(this.storeName); - var request = objectStore.delete(entryName); - } catch(error) { - return Promise.reject(error); - } + transaction.onerror = (e) => { + reject(transaction.error); + clearTimeout(timeout); + }; - return new Promise((resolve, reject) => { - const timeout = setTimeout(() => { - this.log.error('delete: request not finished!', entryName, request); - resolve(); - }, 3000); + transaction.oncomplete = (e) => { + this.log('delete: transaction complete', entryName); + resolve(); + clearTimeout(timeout); + }; - request.onsuccess = (event) => { - //this.log('delete: deleted file', event); - resolve(); - clearTimeout(timeout); - }; - - request.onerror = (error) => { + const timeout = setTimeout(() => { + this.log.error('delete: transaction not finished', entryName, transaction); + }, 10000); + + if(!Array.isArray(entryName)) { + entryName = [].concat(entryName); + } + + for(let i = 0, length = entryName.length; i < length; ++i) { + const request = objectStore.delete(entryName[i]); + request.onerror = (error) => { + reject(transaction.error); + clearTimeout(timeout); + }; + } + } catch(error) { reject(error); - clearTimeout(timeout); - }; + } }); }); } @@ -251,7 +262,7 @@ export default class IDBStorage { }; transaction.oncomplete = (e) => { - //this.log('save: transaction complete:', entryName); + this.log('save: transaction complete:', entryName); resolve(); clearTimeout(timeout); }; diff --git a/src/lib/logger.ts b/src/lib/logger.ts index 2dbac481..98b13fe8 100644 --- a/src/lib/logger.ts +++ b/src/lib/logger.ts @@ -6,43 +6,46 @@ import DEBUG from "../config/debug"; -export enum LogLevels { - log = 1, - warn = 2, - error = 4, - debug = 8 +export enum LogTypes { + None = 0, + Error = 1, + Warn = 2, + Log = 4, + Debug = 8 }; +export const LOG_LEVELS = [LogTypes.None, LogTypes.Error, LogTypes.Warn, LogTypes.Log, LogTypes.Debug]; + const _logTimer = Date.now(); function dT() { return '[' + ((Date.now() - _logTimer) / 1000).toFixed(3) + ']'; } -export function logger(prefix: string, level = LogLevels.log | LogLevels.warn | LogLevels.error) { +export function logger(prefix: string, type: LogTypes = LogTypes.Log | LogTypes.Warn | LogTypes.Error) { if(!DEBUG/* || true */) { - level = LogLevels.error; + type = LogTypes.Error; } //level = LogLevels.log | LogLevels.warn | LogLevels.error | LogLevels.debug function Log(...args: any[]) { - return level & LogLevels.log && console.log(dT(), prefix, ...args); + return type & LogTypes.Log && console.log(dT(), prefix, ...args); } Log.warn = function(...args: any[]) { - return level & LogLevels.warn && console.warn(dT(), prefix, ...args); + return type & LogTypes.Warn && console.warn(dT(), prefix, ...args); }; Log.info = function(...args: any[]) { - return level & LogLevels.log && console.info(dT(), prefix, ...args); + return type & LogTypes.Log && console.info(dT(), prefix, ...args); }; Log.error = function(...args: any[]) { - return level & LogLevels.error && console.error(dT(), prefix, ...args); + return type & LogTypes.Error && console.error(dT(), prefix, ...args); }; Log.trace = function(...args: any[]) { - return level & LogLevels.log && console.trace(dT(), prefix, ...args); + return type & LogTypes.Log && console.trace(dT(), prefix, ...args); }; /* Log.debug = function(...args: any[]) { @@ -50,7 +53,7 @@ export function logger(prefix: string, level = LogLevels.log | LogLevels.warn | }; */ Log.debug = function(...args: any[]) { - return level & LogLevels.debug && console.debug(dT(), prefix, ...args); + return type & LogTypes.Debug && console.debug(dT(), prefix, ...args); }; Log.setPrefix = function(_prefix: string) { @@ -58,6 +61,10 @@ export function logger(prefix: string, level = LogLevels.log | LogLevels.warn | }; Log.setPrefix(prefix); + + Log.setLevel = function(level: 0 | 1 | 2 | 3 | 4) { + type = LOG_LEVELS.slice(0, level + 1).reduce((acc, v) => acc | v, 0) as any; + }; return Log; -}; \ No newline at end of file +}; diff --git a/src/lib/lottieLoader.ts b/src/lib/lottieLoader.ts index f2cff7d2..6290b8e3 100644 --- a/src/lib/lottieLoader.ts +++ b/src/lib/lottieLoader.ts @@ -12,7 +12,7 @@ import mediaSizes from "../helpers/mediaSizes"; import { clamp } from '../helpers/number'; import { pause } from '../helpers/schedulers'; import { isAndroid, isApple, isAppleMobile, isSafari } from "../helpers/userAgent"; -import { logger, LogLevels } from "./logger"; +import { logger, LogTypes } from "./logger"; import apiManager from "./mtproto/mtprotoworker"; let convert = (value: number) => { @@ -563,7 +563,7 @@ class LottieLoader { private workers: QueryableWorker[] = []; private curWorkerNum = 0; - private log = logger('LOTTIE', LogLevels.error); + private log = logger('LOTTIE', LogTypes.Error); public getAnimation(element: HTMLElement) { for(const i in this.players) { diff --git a/src/lib/mtproto/apiFileManager.ts b/src/lib/mtproto/apiFileManager.ts index 787937d6..a07e2ab9 100644 --- a/src/lib/mtproto/apiFileManager.ts +++ b/src/lib/mtproto/apiFileManager.ts @@ -19,7 +19,7 @@ import { FileLocation, InputFile, InputFileLocation, UploadFile } from "../../la import CacheStorageController from "../cacheStorage"; import cryptoWorker from "../crypto/cryptoworker"; import FileManager from "../filemanager"; -import { logger, LogLevels } from "../logger"; +import { logger, LogTypes } from "../logger"; import apiManager from "./apiManager"; import { isWebpSupported } from "./mtproto.worker"; @@ -71,7 +71,7 @@ export class ApiFileManager { public webpConvertPromises: {[fileName: string]: CancellablePromise} = {}; - private log: ReturnType = logger('AFM', LogLevels.error | LogLevels.log); + private log: ReturnType = logger('AFM', LogTypes.Error | LogTypes.Log); private tempId = 0; private queueId = 0; private debug = Modes.debug; diff --git a/src/lib/mtproto/apiManager.ts b/src/lib/mtproto/apiManager.ts index 0f8de549..db617149 100644 --- a/src/lib/mtproto/apiManager.ts +++ b/src/lib/mtproto/apiManager.ts @@ -352,8 +352,8 @@ export class ApiManager { if(error.code === 401 && this.baseDcId === dcId) { if(error.type !== 'SESSION_PASSWORD_NEEDED') { - sessionStorage.remove('dc') - sessionStorage.remove('user_auth'); // ! возможно тут вообще не нужно это делать, но нужно проверить случай с USER_DEACTIVATED (https://core.telegram.org/api/errors) + sessionStorage.delete('dc') + sessionStorage.delete('user_auth'); // ! возможно тут вообще не нужно это делать, но нужно проверить случай с USER_DEACTIVATED (https://core.telegram.org/api/errors) //this.telegramMeNotify(false); } diff --git a/src/lib/mtproto/authorizer.ts b/src/lib/mtproto/authorizer.ts index 0759e497..44ab6598 100644 --- a/src/lib/mtproto/authorizer.ts +++ b/src/lib/mtproto/authorizer.ts @@ -16,7 +16,7 @@ import timeManager from "./timeManager"; import CryptoWorker from "../crypto/cryptoworker"; -import { logger, LogLevels } from "../logger"; +import { logger, LogTypes } from "../logger"; import { bytesCmp, bytesToHex, bytesFromHex, bytesXor } from "../../helpers/bytes"; import DEBUG from "../../config/debug"; import { cmp, int2bigInt, one, pow, str2bigInt, sub } from "../../vendor/leemon"; @@ -74,7 +74,7 @@ export class Authorizer { private log: ReturnType; constructor() { - this.log = logger(`AUTHORIZER`, LogLevels.error | LogLevels.log); + this.log = logger(`AUTHORIZER`, LogTypes.Error | LogTypes.Log); } public mtpSendPlainRequest(dcId: number, requestArray: Uint8Array) { diff --git a/src/lib/mtproto/mtproto.service.ts b/src/lib/mtproto/mtproto.service.ts index 3e9d0b8a..e4ec5403 100644 --- a/src/lib/mtproto/mtproto.service.ts +++ b/src/lib/mtproto/mtproto.service.ts @@ -8,14 +8,14 @@ import './mtproto.worker'; /// #endif import { isSafari } from '../../helpers/userAgent'; -import { logger, LogLevels } from '../logger'; +import { logger, LogTypes } from '../logger'; import type { DownloadOptions } from './apiFileManager'; import type { WorkerTaskTemplate } from '../../types'; import { notifySomeone } from '../../helpers/context'; import type { InputFileLocation, FileLocation, UploadFile } from '../../layer'; import { CancellablePromise, deferredPromise } from '../../helpers/cancellablePromise'; -const log = logger('SW', LogLevels.error | LogLevels.debug | LogLevels.log | LogLevels.warn); +const log = logger('SW', LogTypes.Error | LogTypes.Debug | LogTypes.Log | LogTypes.Warn); const ctx = self as any as ServiceWorkerGlobalScope; const deferredPromises: {[taskId: number]: CancellablePromise} = {}; diff --git a/src/lib/mtproto/networker.ts b/src/lib/mtproto/networker.ts index 8ccb4b28..34cac3f6 100644 --- a/src/lib/mtproto/networker.ts +++ b/src/lib/mtproto/networker.ts @@ -16,7 +16,7 @@ import sessionStorage from '../sessionStorage'; import Schema from './schema'; import timeManager from './timeManager'; import NetworkerFactory from './networkerFactory'; -import { logger, LogLevels } from '../logger'; +import { logger, LogTypes } from '../logger'; import { InvokeApiOptions } from '../../types'; import { longToBytes } from '../crypto/crypto_utils'; import MTTransport from './transports/transport'; @@ -141,7 +141,7 @@ export default class MTPNetworker { const suffix = this.isFileUpload ? '-U' : this.isFileDownload ? '-D' : ''; this.name = 'NET-' + dcId + suffix; //this.log = logger(this.name, this.upload && this.dcId === 2 ? LogLevels.debug | LogLevels.warn | LogLevels.log | LogLevels.error : LogLevels.error); - this.log = logger(this.name, LogLevels.log | LogLevels.error | LogLevels.debug | LogLevels.warn); + this.log = logger(this.name, /* LogTypes.Log | LogTypes.Debug | */LogTypes.Error | LogTypes.Warn); this.log('constructor'/* , this.authKey, this.authKeyID, this.serverSalt */); // Test resend after bad_server_salt diff --git a/src/lib/mtproto/transports/tcpObfuscated.ts b/src/lib/mtproto/transports/tcpObfuscated.ts index 46ca0f36..8a83d96b 100644 --- a/src/lib/mtproto/transports/tcpObfuscated.ts +++ b/src/lib/mtproto/transports/tcpObfuscated.ts @@ -5,7 +5,7 @@ */ import Modes from "../../../config/modes"; -import { logger, LogLevels } from "../../logger"; +import { logger, LogTypes } from "../../logger"; import MTPNetworker from "../networker"; import Obfuscation from "./obfuscation"; import MTTransport, { MTConnection, MTConnectionConstructable } from "./transport"; @@ -33,9 +33,9 @@ export default class TcpObfuscated implements MTTransport { //private debugPayloads: MTPNetworker['debugRequests'] = []; constructor(private Connection: MTConnectionConstructable, private dcId: number, private url: string, private logSuffix: string, public retryTimeout: number) { - let logLevel = LogLevels.error | LogLevels.log; - if(this.debug) logLevel |= LogLevels.debug; - this.log = logger(`TCP-${dcId}` + logSuffix, logLevel); + let logTypes = LogTypes.Error | LogTypes.Log; + if(this.debug) logTypes |= LogTypes.Debug; + this.log = logger(`TCP-${dcId}` + logSuffix, logTypes); this.log('constructor'); this.connect(); diff --git a/src/lib/mtproto/transports/websocket.ts b/src/lib/mtproto/transports/websocket.ts index 0598f72c..aa7e2490 100644 --- a/src/lib/mtproto/transports/websocket.ts +++ b/src/lib/mtproto/transports/websocket.ts @@ -4,7 +4,7 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import { logger, LogLevels } from '../../logger'; +import { logger, LogTypes } from '../../logger'; import Modes from '../../../config/modes'; import EventListenerBase from '../../../helpers/eventListenerBase'; import { MTConnection } from './transport'; @@ -21,9 +21,9 @@ export default class Socket extends EventListenerBase<{ constructor(protected dcId: number, protected url: string, logSuffix: string) { super(); - let logLevel = LogLevels.error | LogLevels.log; - if(this.debug) logLevel |= LogLevels.debug; - this.log = logger(`WS-${dcId}` + logSuffix, logLevel); + let logTypes = LogTypes.Error | LogTypes.Log; + if(this.debug) logTypes |= LogTypes.Debug; + this.log = logger(`WS-${dcId}` + logSuffix, logTypes); this.log('constructor'); this.connect(); diff --git a/src/lib/opusDecodeController.ts b/src/lib/opusDecodeController.ts index ec3e8dc3..41b72444 100644 --- a/src/lib/opusDecodeController.ts +++ b/src/lib/opusDecodeController.ts @@ -6,7 +6,7 @@ import { MOUNT_CLASS_TO } from "../config/debug"; import { isSafari } from "../helpers/userAgent"; -import { logger, LogLevels } from "./logger"; +import { logger, LogTypes } from "./logger"; type Result = { bytes: Uint8Array, @@ -28,7 +28,7 @@ export class OpusDecodeController { private tasks: Array = []; private keepAlive = false; private isPlaySupportedResult: boolean; - private log = logger('OPUS', LogLevels.error); + private log = logger('OPUS', LogTypes.Error); public isPlaySupported() { if(this.isPlaySupportedResult !== undefined) return this.isPlaySupportedResult; diff --git a/src/lib/storage.ts b/src/lib/storage.ts index 7606338a..10aef3c2 100644 --- a/src/lib/storage.ts +++ b/src/lib/storage.ts @@ -20,7 +20,7 @@ export default class AppStorage/* Storage ex //private cache: Partial<{[key: string]: Storage[typeof key]}> = {}; private cache: Partial = {}; private useStorage = true; - private updateKeys: Set = new Set(); + private keysToSet: Set = new Set(); private saveThrottled: () => void; constructor(storageOptions: Omit & {stores?: DatabaseStore[], storeName: DatabaseStoreName}) { @@ -29,8 +29,12 @@ export default class AppStorage/* Storage ex AppStorage.STORAGES.push(this); this.saveThrottled = throttle(async() => { - const keys = Array.from(this.updateKeys.values()) as string[]; - this.updateKeys.clear(); + if(!this.keysToSet.size) { + return; + } + + const keys = Array.from(this.keysToSet.values()) as string[]; + this.keysToSet.clear(); try { //console.log('setItem: will set', key/* , value */); @@ -102,23 +106,28 @@ export default class AppStorage/* Storage ex console.log('LocalStorage set: stringify time by own stringify:', performance.now() - perf); */ if(this.useStorage && !onlyLocal) { - this.updateKeys.add(key); + this.keysToSet.add(key); this.saveThrottled(); } } } } - public async remove(key: keyof Storage, saveLocal = false) { + public async delete(key: keyof Storage, saveLocal = false) { /* if(!this.cache.hasOwnProperty(key)) { return; } */ + // ! it is needed here + key = '' + key; + if(!saveLocal) { delete this.cache[key]; } if(this.useStorage) { + this.keysToSet.delete(key); + try { await this.storage.delete(key as string); } catch(e) { @@ -137,7 +146,7 @@ export default class AppStorage/* Storage ex storage.useStorage = enabled; if(!enabled) { - storage.updateKeys.clear(); + storage.keysToSet.clear(); return storage.clear(); } else { return storage.set(storage.cache); diff --git a/src/lib/storages/dialogs.ts b/src/lib/storages/dialogs.ts index 5aa5ecb0..9719c459 100644 --- a/src/lib/storages/dialogs.ts +++ b/src/lib/storages/dialogs.ts @@ -10,27 +10,80 @@ */ import { tsNow } from "../../helpers/date"; -import type { Message } from "../../layer"; +import type { Chat, DialogPeer, Message, MessagesPeerDialogs, Update } from "../../layer"; import type { AppChatsManager } from "../appManagers/appChatsManager"; import type { AppMessagesManager, Dialog, MyMessage } from "../appManagers/appMessagesManager"; import type { AppPeersManager } from "../appManagers/appPeersManager"; +import type { AppUsersManager } from "../appManagers/appUsersManager"; +import type { AppDraftsManager } from "../appManagers/appDraftsManager"; +import type { AppNotificationsManager } from "../appManagers/appNotificationsManager"; +import type { ApiUpdatesManager } from "../appManagers/apiUpdatesManager"; import type { ServerTimeManager } from "../mtproto/serverTimeManager"; +import apiManager from "../mtproto/mtprotoworker"; import searchIndexManager from "../searchIndexManager"; -import { insertInDescendSortedArray } from "../../helpers/array"; +import { forEachReverse, insertInDescendSortedArray } from "../../helpers/array"; +import rootScope from "../rootScope"; +import AppStorage from "../storage"; +import { safeReplaceObject } from "../../helpers/object"; +import { AppStateManager } from "../appManagers/appStateManager"; export default class DialogsStorage { - public dialogs: {[peerId: string]: Dialog} = {}; + private storage = new AppStorage>({ + storeName: 'dialogs' + }); + + private dialogs: {[peerId: string]: Dialog} = {}; public byFolders: {[folderId: number]: Dialog[]} = {}; public allDialogsLoaded: {[folder_id: number]: boolean}; private dialogsOffsetDate: {[folder_id: number]: number}; - public pinnedOrders: {[folder_id: number]: number[]}; + private pinnedOrders: {[folder_id: number]: number[]}; private dialogsNum: number; - public dialogsIndex = searchIndexManager.createIndex(); + private dialogsIndex = searchIndexManager.createIndex(); + + private cachedResults: { + query: string, + count: number, + dialogs: Dialog[], + folderId: number + } = { + query: '', + count: 0, + dialogs: [], + folderId: 0 + }; + + constructor(private appMessagesManager: AppMessagesManager, + private appChatsManager: AppChatsManager, + private appPeersManager: AppPeersManager, + private appUsersManager: AppUsersManager, + private appDraftsManager: AppDraftsManager, + private appNotificationsManager: AppNotificationsManager, + private appStateManager: AppStateManager, + private apiUpdatesManager: ApiUpdatesManager, + private serverTimeManager: ServerTimeManager + ) { + this.dialogs = this.storage.getCache(); - constructor(private appMessagesManager: AppMessagesManager, private appChatsManager: AppChatsManager, private appPeersManager: AppPeersManager, private serverTimeManager: ServerTimeManager) { this.reset(); + + rootScope.on('language_change', (e) => { + const peerId = appUsersManager.getSelf().id; + const dialog = this.getDialogOnly(peerId); + if(dialog) { + const peerText = appPeersManager.getPeerSearchText(peerId); + searchIndexManager.indexObject(peerId, peerText, this.dialogsIndex); + } + }); + + rootScope.addMultipleEventsListeners({ + updateFolderPeers: this.onUpdateFolderPeers, + + updateDialogPinned: this.onUpdateDialogPinned, + + updatePinnedDialogs: this.onUpdatePinnedDialogs, + }); } public reset() { @@ -99,6 +152,10 @@ export default class DialogsStorage { return []; } + public getDialogOnly(peerId: number) { + return this.dialogs[peerId]; + } + /* var date = Date.now() / 1000 | 0; var m = date * 0x10000; @@ -134,7 +191,7 @@ export default class DialogsStorage { } } - if(dialog.draft && dialog.draft._ === 'draftMessage' && dialog.draft.date > topDate) { + if(dialog.draft?._ === 'draftMessage' && dialog.draft.date > topDate) { topDate = dialog.draft.date; } } @@ -161,6 +218,24 @@ export default class DialogsStorage { return this.generateDialogPinnedDateByIndex(pinnedIndex); } + public generateDialog(peerId: number) { + const dialog: Dialog = { + _: 'dialog', + pFlags: {}, + peer: this.appPeersManager.getOutputPeer(peerId), + top_message: 0, + read_inbox_max_id: 0, + read_outbox_max_id: 0, + unread_count: 0, + unread_mentions_count: 0, + notify_settings: { + _: 'peerNotifySettings', + }, + }; + + return dialog; + } + public pushDialog(dialog: Dialog, offsetDate?: number) { const dialogs = this.getFolder(dialog.folder_id); const pos = dialogs.findIndex(d => d.peerId === dialog.peerId); @@ -170,6 +245,35 @@ export default class DialogsStorage { //if(!this.dialogs[dialog.peerId]) { this.dialogs[dialog.peerId] = dialog; + + const historyStorage = this.appMessagesManager.getHistoryStorage(dialog.peerId); + const history = [].concat(historyStorage.history.slice); + let incomingMessage: any; + for(const mid of history) { + const message = this.appMessagesManager.getMessageByPeer(dialog.peerId, mid); + if(!message.pFlags.is_outgoing) { + incomingMessage = message; + + if(message.fromId !== dialog.peerId) { + this.appStateManager.requestPeer(message.fromId, 'topMessage_' + dialog.peerId, 1); + } + + break; + } + } + + dialog.topMessage = incomingMessage; + + if(dialog.peerId < 0 && dialog.pts) { + const newPts = this.apiUpdatesManager.channelStates[-dialog.peerId].pts; + dialog.pts = newPts; + } + + this.storage.set({ + [dialog.peerId]: dialog + }); + + this.appStateManager.requestPeer(dialog.peerId, 'dialog'); //} if(offsetDate && @@ -195,4 +299,362 @@ export default class DialogsStorage { return foundDialog; } + + public applyDialogs(dialogsResult: MessagesPeerDialogs.messagesPeerDialogs) { + // * В эту функцию попадут только те диалоги, в которых есть read_inbox_max_id и read_outbox_max_id, в отличие от тех, что будут в getTopMessages + + // ! fix 'dialogFolder', maybe there is better way to do it, this only can happen by 'messages.getPinnedDialogs' by folder_id: 0 + forEachReverse(dialogsResult.dialogs, (dialog, idx) => { + if(dialog._ === 'dialogFolder') { + dialogsResult.dialogs.splice(idx, 1); + } + }); + + this.appUsersManager.saveApiUsers(dialogsResult.users); + this.appChatsManager.saveApiChats(dialogsResult.chats); + this.appMessagesManager.saveMessages(dialogsResult.messages); + + this.appMessagesManager.log('applyConversation', dialogsResult); + + const updatedDialogs: {[peerId: number]: Dialog} = {}; + (dialogsResult.dialogs as Dialog[]).forEach((dialog) => { + const peerId = this.appPeersManager.getPeerId(dialog.peer); + let topMessage = dialog.top_message; + + const topPendingMessage = this.appMessagesManager.pendingTopMsgs[peerId]; + if(topPendingMessage) { + if(!topMessage + || (this.appMessagesManager.getMessageByPeer(peerId, topPendingMessage) as MyMessage).date > (this.appMessagesManager.getMessageByPeer(peerId, topMessage) as MyMessage).date) { + dialog.top_message = topMessage = topPendingMessage; + this.appMessagesManager.getHistoryStorage(peerId).maxId = topPendingMessage; + } + } + + /* const d = Object.assign({}, dialog); + if(peerId === 239602833) { + this.log.error('applyConversation lun', dialog, d); + } */ + + if(topMessage || (dialog.draft && dialog.draft._ === 'draftMessage')) { + this.saveDialog(dialog); + updatedDialogs[peerId] = dialog; + } else { + const dropped = this.dropDialog(peerId); + if(dropped.length) { + rootScope.broadcast('dialog_drop', {peerId, dialog: dropped[0]}); + } + } + + const updates = this.appMessagesManager.newUpdatesAfterReloadToHandle[peerId]; + if(updates !== undefined) { + for(const update of updates) { + this.apiUpdatesManager.saveUpdate(update); + } + + delete this.appMessagesManager.newUpdatesAfterReloadToHandle[peerId]; + } + }); + + if(Object.keys(updatedDialogs).length) { + rootScope.broadcast('dialogs_multiupdate', updatedDialogs); + } + } + + /** + * Won't save migrated from peer, forbidden peers, left and kicked + */ + public saveDialog(dialog: Dialog, folderId = 0) { + const peerId = this.appPeersManager.getPeerId(dialog.peer); + if(!peerId) { + console.error('saveConversation no peerId???', dialog, folderId); + return false; + } + + if(dialog._ !== 'dialog'/* || peerId === 239602833 */) { + console.error('saveConversation not regular dialog', dialog, Object.assign({}, dialog)); + } + + const channelId = this.appPeersManager.isChannel(peerId) ? -peerId : 0; + + if(peerId < 0) { + const chat: Chat = this.appChatsManager.getChat(-peerId); + if(chat._ === 'channelForbidden' || chat._ === 'chatForbidden' || (chat as Chat.chat).pFlags.left || (chat as Chat.chat).pFlags.kicked) { + return false; + } + } + + const peerText = this.appPeersManager.getPeerSearchText(peerId); + searchIndexManager.indexObject(peerId, peerText, this.dialogsIndex); + + let mid: number, message; + if(dialog.top_message) { + mid = this.appMessagesManager.generateMessageId(dialog.top_message);//dialog.top_message; + message = this.appMessagesManager.getMessageByPeer(peerId, mid); + } else { + mid = this.appMessagesManager.generateTempMessageId(peerId); + message = { + _: 'message', + id: mid, + mid, + from_id: this.appPeersManager.getOutputPeer(this.appUsersManager.getSelf().id), + peer_id: this.appPeersManager.getOutputPeer(peerId), + deleted: true, + pFlags: {out: true}, + date: 0, + message: '' + }; + this.appMessagesManager.saveMessages([message], {isOutgoing: true}); + } + + if(!message?.pFlags) { + this.appMessagesManager.log.error('saveConversation no message:', dialog, message); + } + + if(!channelId && peerId < 0) { + const chat = this.appChatsManager.getChat(-peerId); + if(chat && chat.migrated_to && chat.pFlags.deactivated) { + const migratedToPeer = this.appPeersManager.getPeerId(chat.migrated_to); + this.appMessagesManager.migratedFromTo[peerId] = migratedToPeer; + this.appMessagesManager.migratedToFrom[migratedToPeer] = peerId; + return; + } + } + + const wasDialogBefore = this.getDialogOnly(peerId); + + dialog.top_message = mid; + dialog.read_inbox_max_id = this.appMessagesManager.generateMessageId(wasDialogBefore && !dialog.read_inbox_max_id ? wasDialogBefore.read_inbox_max_id : dialog.read_inbox_max_id); + dialog.read_outbox_max_id = this.appMessagesManager.generateMessageId(wasDialogBefore && !dialog.read_outbox_max_id ? wasDialogBefore.read_outbox_max_id : dialog.read_outbox_max_id); + + if(!dialog.hasOwnProperty('folder_id')) { + if(dialog._ === 'dialog') { + // ! СЛОЖНО ! СМОТРИ В getTopMessages + dialog.folder_id = wasDialogBefore ? wasDialogBefore.folder_id : folderId; + }/* else if(dialog._ === 'dialogFolder') { + dialog.folder_id = dialog.folder.id; + } */ + } + + dialog.draft = this.appDraftsManager.saveDraft(peerId, 0, dialog.draft); + dialog.peerId = peerId; + + // Because we saved message without dialog present + if(message.pFlags.is_outgoing) { + if(mid > dialog[message.pFlags.out ? 'read_outbox_max_id' : 'read_inbox_max_id']) message.pFlags.unread = true; + else delete message.pFlags.unread; + } + + const historyStorage = this.appMessagesManager.getHistoryStorage(peerId); + /* if(historyStorage === undefined) { // warning + historyStorage.history.push(mid); + if(this.mergeReplyKeyboard(historyStorage, message)) { + rootScope.broadcast('history_reply_markup', {peerId}); + } + } else */if(!historyStorage.history.slice.length) { + historyStorage.history.unshift(mid); + } + + historyStorage.maxId = mid; + historyStorage.readMaxId = dialog.read_inbox_max_id; + historyStorage.readOutboxMaxId = dialog.read_outbox_max_id; + + this.appNotificationsManager.savePeerSettings(peerId, dialog.notify_settings); + + if(channelId && dialog.pts) { + this.apiUpdatesManager.addChannelState(channelId, dialog.pts); + } + + this.generateIndexForDialog(dialog); + + if(wasDialogBefore) { + safeReplaceObject(wasDialogBefore, dialog); + } + + this.pushDialog(dialog, message.date); + } + + public getDialogs(query = '', offsetIndex?: number, limit = 20, folderId = 0) { + const realFolderId = folderId > 1 ? 0 : folderId; + let curDialogStorage = this.getFolder(folderId); + + if(query) { + if(!limit || this.cachedResults.query !== query || this.cachedResults.folderId !== folderId) { + this.cachedResults.query = query; + this.cachedResults.folderId = folderId; + + const results = searchIndexManager.search(query, this.dialogsIndex); + + this.cachedResults.dialogs = []; + + for(const peerId in this.dialogs) { + const dialog = this.dialogs[peerId]; + if(results[dialog.peerId] && dialog.folder_id === folderId) { + this.cachedResults.dialogs.push(dialog); + } + } + + this.cachedResults.dialogs.sort((d1, d2) => d2.index - d1.index); + + this.cachedResults.count = this.cachedResults.dialogs.length; + } + + curDialogStorage = this.cachedResults.dialogs; + } else { + this.cachedResults.query = ''; + } + + let offset = 0; + if(offsetIndex > 0) { + for(; offset < curDialogStorage.length; offset++) { + if(offsetIndex > curDialogStorage[offset].index) { + break; + } + } + } + + if(query || this.allDialogsLoaded[realFolderId] || curDialogStorage.length >= offset + limit) { + return Promise.resolve({ + dialogs: curDialogStorage.slice(offset, offset + limit), + count: this.allDialogsLoaded[realFolderId] ? curDialogStorage.length : null, + isEnd: this.allDialogsLoaded[realFolderId] && (offset + limit) >= curDialogStorage.length + }); + } + + return this.appMessagesManager.getTopMessages(limit, realFolderId).then(messagesDialogs => { + //const curDialogStorage = this[folderId]; + + offset = 0; + if(offsetIndex > 0) { + for(; offset < curDialogStorage.length; offset++) { + if(offsetIndex > curDialogStorage[offset].index) { + break; + } + } + } + + //this.log.warn(offset, offset + limit, curDialogStorage.dialogs.length, this.dialogs.length); + + return { + dialogs: curDialogStorage.slice(offset, offset + limit), + count: messagesDialogs._ === 'messages.dialogs' ? messagesDialogs.dialogs.length : messagesDialogs.count, + isEnd: this.allDialogsLoaded[realFolderId] && (offset + limit) >= curDialogStorage.length + }; + }); + } + + // only 0 and 1 folders + private onUpdateFolderPeers = (update: Update.updateFolderPeers) => { + //this.log('updateFolderPeers', update); + const peers = update.folder_peers; + + peers.forEach((folderPeer) => { + const {folder_id, peer} = folderPeer; + + const peerId = this.appPeersManager.getPeerId(peer); + const dialog = this.dropDialog(peerId)[0]; + if(dialog) { + if(dialog.pFlags?.pinned) { + delete dialog.pFlags.pinned; + this.pinnedOrders[folder_id].findAndSplice(p => p === dialog.peerId); + } + + dialog.folder_id = folder_id; + this.generateIndexForDialog(dialog); + this.pushDialog(dialog); // need for simultaneously updatePinnedDialogs + } + + this.appMessagesManager.scheduleHandleNewDialogs(peerId, dialog); + }); + }; + + private onUpdateDialogPinned = (update: Update.updateDialogPinned) => { + const folderId = update.folder_id ?? 0; + //this.log('updateDialogPinned', update); + const peerId = this.appPeersManager.getPeerId((update.peer as DialogPeer.dialogPeer).peer); + const dialog = this.getDialogOnly(peerId); + + // этот код внизу никогда не сработает, в папках за пиннед отвечает updateDialogFilter + /* if(update.folder_id > 1) { + const filter = this.filtersStorage.filters[update.folder_id]; + if(update.pFlags.pinned) { + filter.pinned_peers.unshift(peerId); + } else { + filter.pinned_peers.findAndSplice(p => p === peerId); + } + } */ + + if(dialog) { + if(!update.pFlags.pinned) { + delete dialog.pFlags.pinned; + this.pinnedOrders[folderId].findAndSplice(p => p === dialog.peerId); + } else { // means set + dialog.pFlags.pinned = true; + } + + this.generateIndexForDialog(dialog); + } + + this.appMessagesManager.scheduleHandleNewDialogs(peerId, dialog); + }; + + private onUpdatePinnedDialogs = (update: Update.updatePinnedDialogs) => { + const folderId = update.folder_id ?? 0; + + const handleOrder = (order: number[]) => { + this.pinnedOrders[folderId].length = 0; + order.reverse(); // index must be higher + order.forEach((peerId) => { + newPinned[peerId] = true; + + const dialog = this.getDialogOnly(peerId); + this.appMessagesManager.scheduleHandleNewDialogs(peerId, dialog); + if(!dialog) { + return; + } + + dialog.pFlags.pinned = true; + this.generateIndexForDialog(dialog); + }); + + this.getFolder(folderId).forEach(dialog => { + const peerId = dialog.peerId; + if(dialog.pFlags.pinned && !newPinned[peerId]) { + this.appMessagesManager.scheduleHandleNewDialogs(peerId); + } + }); + }; + + //this.log('updatePinnedDialogs', update); + const newPinned: {[peerId: number]: true} = {}; + if(!update.order) { + apiManager.invokeApi('messages.getPinnedDialogs', { + folder_id: folderId + }).then((dialogsResult) => { + // * for test reordering and rendering + // dialogsResult.dialogs.reverse(); + + this.applyDialogs(dialogsResult); + + handleOrder(dialogsResult.dialogs.map(d => d.peerId)); + + /* dialogsResult.dialogs.forEach((dialog) => { + newPinned[dialog.peerId] = true; + }); + + this.dialogsStorage.getFolder(folderId).forEach((dialog) => { + const peerId = dialog.peerId; + if(dialog.pFlags.pinned && !newPinned[peerId]) { + this.newDialogsToHandle[peerId] = {reload: true}; + this.scheduleHandleNewDialogs(); + } + }); */ + }); + + return; + } + + //this.log('before order:', this.dialogsStorage[0].map(d => d.peerId)); + + handleOrder(update.order.map(peer => this.appPeersManager.getPeerId((peer as DialogPeer.dialogPeer).peer))); + }; } diff --git a/src/scripts/in/schema_additional_params.json b/src/scripts/in/schema_additional_params.json index 35e18f03..38649b9b 100644 --- a/src/scripts/in/schema_additional_params.json +++ b/src/scripts/in/schema_additional_params.json @@ -52,7 +52,8 @@ "predicate": "dialog", "params": [ {"name": "index", "type": "number"}, - {"name": "peerId", "type": "number"} + {"name": "peerId", "type": "number"}, + {"name": "topMessage", "type": "any"} ] }, { "predicate": "dialogFolder", @@ -121,12 +122,7 @@ "predicate": "user", "params": [ {"name": "initials", "type": "string"}, - {"name": "rFirstName", "type": "string"}, - {"name": "rFullName", "type": "string"}, - {"name": "rPhone", "type": "string"}, - {"name": "sortName", "type": "string"}, - {"name": "sortStatus", "type": "number"}, - {"name": "num", "type": "number"} + {"name": "sortName", "type": "string"} ] }, { "predicate": "auth.sentCode",