diff --git a/src/components/appMediaPlaybackController.ts b/src/components/appMediaPlaybackController.ts index f757fe53..644d787f 100644 --- a/src/components/appMediaPlaybackController.ts +++ b/src/components/appMediaPlaybackController.ts @@ -34,7 +34,7 @@ class AppMediaPlaybackController { document.body.append(this.container); } - public addMedia(doc: MyDocument, mid: number, autoload = true): HTMLMediaElement { + public addMedia(peerId: number, doc: MyDocument, mid: number, autoload = true): HTMLMediaElement { if(this.media[mid]) return this.media[mid]; const media = document.createElement(doc.type == 'round' ? 'video' : 'audio'); @@ -61,12 +61,12 @@ class AppMediaPlaybackController { } this.playingMedia = media; - this.loadSiblingsMedia(doc.type as MediaType, mid); + this.loadSiblingsMedia(peerId, doc.type as MediaType, mid); } // audio_pause не успеет сработать без таймаута setTimeout(() => { - rootScope.broadcast('audio_play', {doc, mid}); + rootScope.broadcast('audio_play', {peerId, doc, mid}); }, 0); }); @@ -75,7 +75,7 @@ class AppMediaPlaybackController { const onError = (e: Event) => { if(this.nextMid == mid) { - this.loadSiblingsMedia(doc.type as MediaType, mid).then(() => { + this.loadSiblingsMedia(peerId, doc.type as MediaType, mid).then(() => { if(this.nextMid && this.media[this.nextMid]) { this.media[this.nextMid].play(); } @@ -181,12 +181,11 @@ class AppMediaPlaybackController { } }; - private loadSiblingsMedia(type: MediaType, mid: number) { + private loadSiblingsMedia(peerId: number, type: MediaType, mid: number) { const media = this.playingMedia; - const message = appMessagesManager.getMessage(mid); this.prevMid = this.nextMid = 0; - return appMessagesManager.getSearch(message.peerId, '', { + return appMessagesManager.getSearch(peerId, '', { //_: type == 'audio' ? 'inputMessagesFilterMusic' : (type == 'round' ? 'inputMessagesFilterRoundVideo' : 'inputMessagesFilterVoice') _: type == 'audio' ? 'inputMessagesFilterMusic' : 'inputMessagesFilterRoundVoice' }, mid, 3, 0, 2).then(value => { @@ -204,8 +203,8 @@ class AppMediaPlaybackController { } [this.prevMid, this.nextMid].filter(Boolean).forEach(mid => { - const message = appMessagesManager.getMessage(mid); - this.addMedia(message.media.document, mid, false); + const message = appMessagesManager.getMessageByPeer(peerId, mid); + this.addMedia(peerId, message.media.document, mid, false); }); //console.log('loadSiblingsAudio', audio, type, mid, value, this.prevMid, this.nextMid); diff --git a/src/components/appMediaViewer.ts b/src/components/appMediaViewer.ts index f5515925..1a7f1e48 100644 --- a/src/components/appMediaViewer.ts +++ b/src/components/appMediaViewer.ts @@ -1165,18 +1165,18 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet onPrevClick = (target: AppMediaViewerTargetType) => { this.nextTargets.unshift({element: this.lastTarget, mid: this.currentMessageId}); - this.openMedia(appMessagesManager.getMessage(target.mid), target.element); + this.openMedia(appMessagesManager.getMessageByPeer(this.peerId, target.mid), target.element); }; onNextClick = (target: AppMediaViewerTargetType) => { this.prevTargets.push({element: this.lastTarget, mid: this.currentMessageId}); - this.openMedia(appMessagesManager.getMessage(target.mid), target.element); + this.openMedia(appMessagesManager.getMessageByPeer(this.peerId, target.mid), target.element); }; onForwardClick = () => { if(this.currentMessageId) { //appSidebarRight.forwardTab.open([this.currentMessageId]); - new PopupForward([this.currentMessageId], () => { + new PopupForward(this.peerId, [this.currentMessageId], () => { return this.close(); }); } @@ -1192,14 +1192,14 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet appSidebarRight.sharedMediaTab.closeBtn.click(); } - const message = appMessagesManager.getMessage(mid); + const message = appMessagesManager.getMessageByPeer(this.peerId, mid); appImManager.setPeer(message.peerId, mid); }); } }; onDownloadClick = () => { - const message = appMessagesManager.getMessage(this.currentMessageId); + const message = appMessagesManager.getMessageByPeer(this.peerId, this.currentMessageId); if(message.media.photo) { appPhotosManager.savePhotoFile(message.media.photo, appImManager.chat.bubbles.lazyLoadQueue.queueId); } else { @@ -1262,7 +1262,7 @@ export default class AppMediaViewer extends AppMediaViewerBase<'caption', 'delet const method = older ? value.history.forEach : value.history.forEachReverse; method.call(value.history, mid => { - const message = appMessagesManager.getMessage(mid); + const message = appMessagesManager.getMessageByPeer(this.peerId, mid); const media = this.getMediaFromMessage(message); if(!media) return; diff --git a/src/components/appSearch.ts b/src/components/appSearch.ts index 90f6c00f..04823988 100644 --- a/src/components/appSearch.ts +++ b/src/components/appSearch.ts @@ -1,6 +1,5 @@ import appDialogsManager from "../lib/appManagers/appDialogsManager"; import Scrollable from "./scrollable"; -import appMessagesIdsManager from "../lib/appManagers/appMessagesIdsManager"; import appUsersManager from "../lib/appManagers/appUsersManager"; import appPeersManager from '../lib/appManagers/appPeersManager'; import appMessagesManager from "../lib/appManagers/appMessagesManager"; @@ -157,7 +156,7 @@ export default class AppSearch { return Promise.resolve(); } - const maxId = appMessagesIdsManager.getMessageIdInfo(this.minMsgId)[0] || 0; + const maxId = this.minMsgId || 0; if(!this.peerId && !maxId && !this.loadedContacts) { const renderedPeerIds: Set = new Set(); @@ -259,7 +258,7 @@ export default class AppSearch { const searchGroup = this.searchGroups.messages; history.forEach((msgId: number) => { - const message = appMessagesManager.getMessage(msgId); + const message = appMessagesManager.getMessageByPeer(this.peerId, msgId); const {dialog, dom} = appDialogsManager.addDialogNew({ dialog: message.peerId, diff --git a/src/components/audio.ts b/src/components/audio.ts index 14ace4ff..64be2340 100644 --- a/src/components/audio.ts +++ b/src/components/audio.ts @@ -14,10 +14,10 @@ import './middleEllipsis'; import { attachClickEvent, cancelEvent, detachClickEvent } from "../helpers/dom"; rootScope.on('messages_media_read', e => { - const mids = e.detail; + const {mids, peerId} = e.detail; mids.forEach(mid => { - (Array.from(document.querySelectorAll('audio-element[message-id="' + mid + '"]')) as AudioElement[]).forEach(elem => { + (Array.from(document.querySelectorAll('audio-element[message-id="' + mid + '"][peer-id="' + peerId + '"]')) as AudioElement[]).forEach(elem => { //console.log('updating avatar:', elem); elem.classList.remove('is-unread'); }); @@ -61,10 +61,10 @@ export function decodeWaveform(waveform: Uint8Array | number[]) { return result; } -function wrapVoiceMessage(doc: MyDocument, audioEl: AudioElement, mid: number) { +function wrapVoiceMessage(peerId: number, doc: MyDocument, audioEl: AudioElement, mid: number) { audioEl.classList.add('is-voice'); - const message = appMessagesManager.getMessage(mid); + const message = appMessagesManager.getMessageByPeer(peerId, mid); const isOut = message.fromId == rootScope.myId && message.peerId != rootScope.myId; let isUnread = message && message.pFlags.media_unread; if(isUnread) { @@ -171,7 +171,7 @@ function wrapVoiceMessage(doc: MyDocument, audioEl: AudioElement, mid: number) { audioEl.addAudioListener('playing', () => { if(isUnread && !isOut && audioEl.classList.contains('is-unread')) { audioEl.classList.remove('is-unread'); - appMessagesManager.readMessages([mid]); + appMessagesManager.readMessages(peerId, [mid]); isUnread = false; } @@ -329,6 +329,7 @@ export default class AudioElement extends HTMLElement { this.classList.add('audio'); + const peerId = +this.getAttribute('peer-id'); const mid = +this.getAttribute('message-id'); const docId = this.getAttribute('doc-id'); const doc = appDocsManager.getDoc(docId); @@ -351,13 +352,13 @@ export default class AudioElement extends HTMLElement { this.append(downloadDiv); } - const onTypeLoad = doc.type == 'voice' ? wrapVoiceMessage(doc, this, mid) : wrapAudio(doc, this); + const onTypeLoad = doc.type == 'voice' ? wrapVoiceMessage(peerId, doc, this, mid) : wrapAudio(doc, this); const audioTimeDiv = this.querySelector('.audio-time') as HTMLDivElement; audioTimeDiv.innerHTML = durationStr; const onLoad = (autoload = true) => { - const audio = this.audio = appMediaPlaybackController.addMedia(doc, mid, autoload); + const audio = this.audio = appMediaPlaybackController.addMedia(peerId, doc, mid, autoload); this.onTypeDisconnect = onTypeLoad(); diff --git a/src/components/avatar.ts b/src/components/avatar.ts index 9200006f..30f6d658 100644 --- a/src/components/avatar.ts +++ b/src/components/avatar.ts @@ -57,7 +57,7 @@ export default class AvatarElement extends HTMLElement { if(mid) { // ! гений в деле, костылируем (но это гениально) - let message = appMessagesManager.getMessage(mid); + let message = appMessagesManager.getMessageByPeer(peerId, mid); const messagePhoto = message.action.photo; if(messagePhoto.id != photo.id) { message = { @@ -72,7 +72,7 @@ export default class AvatarElement extends HTMLElement { fromId: peerId }; - appMessagesManager.messagesStorage[maxId] = message; + appMessagesManager.getMessagesStorage(peerId)[maxId] = message; } const good = Array.from(this.querySelectorAll('img')).find(img => !img.classList.contains('emoji')); diff --git a/src/components/chat/audio.ts b/src/components/chat/audio.ts index 170eb2a7..e3070142 100644 --- a/src/components/chat/audio.ts +++ b/src/components/chat/audio.ts @@ -35,10 +35,10 @@ export default class ChatAudio extends PinnedContainer { this.wrapper.prepend(this.toggleEl); this.topbar.listenerSetter.add(rootScope, 'audio_play', (e) => { - const {doc, mid} = e.detail; + const {doc, mid, peerId} = e.detail; let title: string, subtitle: string; - const message = appMessagesManager.getMessage(mid); + const message = appMessagesManager.getMessageByPeer(peerId, mid); if(doc.type == 'voice' || doc.type == 'round') { title = appPeersManager.getPeerTitle(message.fromId, false, true); //subtitle = 'Voice message'; diff --git a/src/components/chat/bubbles.ts b/src/components/chat/bubbles.ts index 9d417d78..2dd27970 100644 --- a/src/components/chat/bubbles.ts +++ b/src/components/chat/bubbles.ts @@ -35,7 +35,6 @@ import LazyLoadQueue from "../lazyLoadQueue"; import { AppChatsManager } from "../../lib/appManagers/appChatsManager"; import Chat from "./chat"; import ListenerSetter from "../../helpers/listenerSetter"; -import { pause } from "../../helpers/schedulers"; const IGNORE_ACTIONS = ['messageActionHistoryClear']; @@ -148,7 +147,7 @@ export default class ChatBubbles { let bubble = this.bubbles[mid]; if(!bubble) return; - let message = appMessagesManager.getMessage(mid); + let message = this.chat.getMessage(mid); //this.log('history_update', this.bubbles[mid], mid, message); let dateMessage = this.getDateContainerByMessage(message, false); @@ -196,7 +195,7 @@ export default class ChatBubbles { this.log('message_sent', e.detail); - const message = this.appMessagesManager.getMessage(mid); + const message = this.chat.getMessage(mid); appSidebarRight.sharedMediaTab.renderNewMessages(message.peerId, [mid]); @@ -282,7 +281,7 @@ export default class ChatBubbles { if(!this.bubbles[maxId]) return; const renderMaxId = getObjectKeysAndSort(appMessagesManager.groupedMessagesStorage[groupId], 'asc').pop(); - this.renderMessage(appMessagesManager.getMessage(renderMaxId), true, false, this.bubbles[maxId], false); + this.renderMessage(this.chat.getMessage(renderMaxId), true, false, this.bubbles[maxId], false); }); this.listenerSetter.add(rootScope, 'messages_downloaded', (e) => { @@ -301,9 +300,9 @@ export default class ChatBubbles { const bubble = this.bubbles[mid]; if(!bubble) return; - const message = appMessagesManager.getMessage(mid); + const message = this.chat.getMessage(mid); - const repliedMessage = appMessagesManager.getMessage(replyMid); + const repliedMessage = this.chat.getMessage(replyMid); if(repliedMessage.deleted) { // чтобы не пыталось бесконечно загрузить удалённое сообщение delete message.reply_to_mid; // WARNING! } @@ -321,10 +320,14 @@ export default class ChatBubbles { } }); + this.listenerSetter.add(this.bubblesContainer, 'click', this.onBubblesClick/* , {capture: true, passive: false} */); + } + + public constructPeerHelpers() { this.listenerSetter.add(rootScope, 'dialog_unread', (e) => { const info = e.detail; - const dialog = appMessagesManager.getDialogByPeerId(info.peerId)[0]; + const dialog = this.appMessagesManager.getDialogByPeerId(info.peerId)[0]; if(dialog?.peerId == this.peerId) { this.chat.input.setUnreadCount(); this.updateUnreadByDialog(dialog); @@ -347,8 +350,6 @@ export default class ChatBubbles { } }); - this.listenerSetter.add(this.bubblesContainer, 'click', this.onBubblesClick/* , {capture: true, passive: false} */); - this.stickyIntersector = new StickyIntersector(this.scrollable.container, (stuck, target) => { for(const timestamp in this.dateMessages) { const dateMessage = this.dateMessages[timestamp]; @@ -395,9 +396,9 @@ export default class ChatBubbles { } */ //appMessagesManager.readMessages(readed); - /* false && */ appMessagesManager.readHistory(this.peerId, max).catch((err: any) => { + /* false && */ this.appMessagesManager.readHistory(this.peerId, max).catch((err: any) => { this.log.error('readHistory err:', err); - appMessagesManager.readHistory(this.peerId, max); + this.appMessagesManager.readHistory(this.peerId, max); }); } }); @@ -406,7 +407,6 @@ export default class ChatBubbles { public constructPinnedHelpers() { this.listenerSetter.add(rootScope, 'peer_pinned_messages', (e) => { const {peerId, mids, pinned} = e.detail; - if(peerId !== this.peerId) return; if(mids) { @@ -417,6 +417,15 @@ export default class ChatBubbles { }); } + public constructScheduledHelpers() { + this.listenerSetter.add(rootScope, 'scheduled_delete', (e) => { + const {peerId, mids} = e.detail; + if(peerId !== this.peerId) return; + + this.deleteMessagesByIds(mids); + }); + } + public onBubblesClick = (e: Event) => { let target = e.target as HTMLElement; let bubble: HTMLElement = null; @@ -479,7 +488,7 @@ export default class ChatBubbles { if(bubble.classList.contains('sticker') && target.parentElement.classList.contains('attachment')) { const messageId = +bubble.dataset.mid; - const message = this.appMessagesManager.getMessage(messageId); + const message = this.chat.getMessage(messageId); const doc = message.media?.document; @@ -495,7 +504,7 @@ export default class ChatBubbles { || isVideoComponentElement || (target.tagName == 'VIDEO' && !bubble.classList.contains('round'))) { let messageId = +findUpClassName(target, 'album-item')?.dataset.mid || +bubble.dataset.mid; - let message = this.appMessagesManager.getMessage(messageId); + let message = this.chat.getMessage(messageId); if(!message) { this.log.warn('no message by messageId:', messageId); return; @@ -505,7 +514,7 @@ export default class ChatBubbles { let ids = Object.keys(this.bubbles).map(k => +k).filter(id => { //if(!this.scrollable.visibleElements.find(e => e.element == this.bubbles[id])) return false; - let message = this.appMessagesManager.getMessage(id); + let message = this.chat.getMessage(id); return message.media && (message.media.photo || (message.media.document && (message.media.document.type == 'video' || message.media.document.type == 'gif')) || (message.media.webpage && (message.media.webpage.document || message.media.webpage.photo))); }).sort((a, b) => a - b); @@ -548,9 +557,9 @@ export default class ChatBubbles { return; } - if(['IMG', 'DIV', "AVATAR-ELEMENT", 'A'].indexOf(target.tagName) === -1) target = findUpTag(target, 'DIV'); + if(['IMG', 'DIV', "AVATAR-ELEMENT"/* , 'A' */].indexOf(target.tagName) === -1) target = findUpTag(target, 'DIV'); - if(target.tagName == 'DIV' || target.tagName == "AVATAR-ELEMENT" || target.tagName == 'A') { + if(target.tagName == 'DIV' || target.tagName == "AVATAR-ELEMENT"/* || target.tagName == 'A' */) { if(target.classList.contains('goto-original')) { const savedFrom = bubble.dataset.savedFrom; const splitted = savedFrom.split('_'); @@ -561,25 +570,10 @@ export default class ChatBubbles { return; } else if(target.classList.contains('forward')) { const mid = +bubble.dataset.mid; - new PopupForward([mid]); + new PopupForward(this.peerId, [mid]); //appSidebarRight.forwardTab.open([mid]); return; - }/* else if(target.classList.contains('follow')) { - cancelEvent(e); - const savedFrom = target.dataset.follow; - const splitted = savedFrom.split('_'); - this.chat.appImManager.setInnerPeer(+splitted[0], splitted.length > 1 ? +splitted[1] : undefined); - - return; - } else if(target.classList.contains('mention')) { - cancelEvent(e); - const username = target.innerText; - this.appUsersManager.resolveUsername(username.slice(1)).then(peer => { - this.chat.appImManager.setInnerPeer(peer._ == 'user' ? peer.id : -peer.id); - }); - - return; - } */ else if(target.classList.contains('name')) { + } else if(target.classList.contains('name')) { const peerId = +target.dataset.peerId; if(peerId) { @@ -628,7 +622,7 @@ export default class ChatBubbles { const rect = bubble.getBoundingClientRect(); bad = (this.appPhotosManager.windowH / 2) > rect.top; } else { - const message = this.appMessagesManager.getMessage(mid); + const message = this.chat.getMessage(mid); if(!message.deleted) { bad = false; } @@ -661,7 +655,7 @@ export default class ChatBubbles { if(this.bubbles[mid]) { return { bubble: this.bubbles[mid], - message: this.appMessagesManager.getMessage(+mid) + message: this.chat.getMessage(+mid) }; } } @@ -674,7 +668,7 @@ export default class ChatBubbles { } public getMountedBubble(mid: number) { - const message = this.appMessagesManager.getMessage(mid); + const message = this.chat.getMessage(mid); if(message.grouped_id && this.appMessagesManager.getMidsByAlbum(message.grouped_id).length > 1) { const a = this.getGroupedBubble(message.grouped_id); @@ -837,7 +831,9 @@ export default class ChatBubbles { } this.bubbleGroups.removeBubble(bubble, mid); - this.unreadedObserver.unobserve(bubble); + if(this.unreadedObserver) { + this.unreadedObserver.unobserve(bubble); + } //this.unreaded.findAndSplice(mid => mid == id); bubble.remove(); //bubble.remove(); @@ -854,7 +850,7 @@ export default class ChatBubbles { } msgIds.forEach((msgId: number) => { - let message = this.appMessagesManager.getMessage(msgId); + let message = this.chat.getMessage(msgId); /////////this.log('got new message to append:', message); @@ -937,7 +933,9 @@ export default class ChatBubbles { this.chatInner.append(container); } - this.stickyIntersector.observeStickyHeaderChanges(container); + if(this.stickyIntersector) { + this.stickyIntersector.observeStickyHeaderChanges(container); + } } return this.dateMessages[dateTimestamp]; @@ -951,12 +949,12 @@ export default class ChatBubbles { this.listenerSetter.removeAll(); this.lazyLoadQueue.clear(); - this.unreadedObserver.disconnect(); - this.stickyIntersector.disconnect(); + this.unreadedObserver && this.unreadedObserver.disconnect(); + this.stickyIntersector && this.stickyIntersector.disconnect(); delete this.lazyLoadQueue; - delete this.unreadedObserver; - delete this.stickyIntersector; + this.unreadedObserver && delete this.unreadedObserver; + this.stickyIntersector && delete this.stickyIntersector; } public cleanup(bubblesToo = false) { @@ -990,10 +988,14 @@ export default class ChatBubbles { this.getHistoryTopPromise = this.getHistoryBottomPromise = undefined; - this.stickyIntersector.disconnect(); + if(this.stickyIntersector) { + this.stickyIntersector.disconnect(); + } - this.unreadedObserver.disconnect(); - this.unreaded.length = 0; + if(this.unreadedObserver) { + this.unreadedObserver.disconnect(); + this.unreaded.length = 0; + } this.loadedTopTimes = this.loadedBottomTimes = 0; @@ -1350,7 +1352,7 @@ export default class ChatBubbles { if(!our && !message.pFlags.out) { //this.log('not our message', message, message.pFlags.unread); - if(message.pFlags.unread) { + if(message.pFlags.unread && this.unreadedObserver) { this.unreadedObserver.observe(bubble); if(!this.unreaded.indexOf(message.mid)) { this.unreaded.push(message.mid); @@ -1539,7 +1541,7 @@ export default class ChatBubbles { } const button = row.buttons[column]; - this.appInlineBotsManager.callbackButtonClick(message.mid, button); + this.appInlineBotsManager.callbackButtonClick(this.peerId, message.mid, button); }); const offset = rows.length * 45 + 'px'; @@ -1585,7 +1587,8 @@ export default class ChatBubbles { groupId: '' + message.id, attachmentDiv, uploading: true, - isOut: true + isOut: true, + chat: this.chat }); break; @@ -1645,7 +1648,8 @@ export default class ChatBubbles { albumMustBeRenderedFull, message, bubble, - messageDiv + messageDiv, + chat: this.chat }); if(newNameContainer) { @@ -1681,7 +1685,8 @@ export default class ChatBubbles { attachmentDiv, middleware: this.getMiddleware(), isOut: our, - lazyLoadQueue: this.lazyLoadQueue + lazyLoadQueue: this.lazyLoadQueue, + chat: this.chat }); break; @@ -1748,7 +1753,7 @@ export default class ChatBubbles { }); //} } else { - const docDiv = wrapDocument(doc, false, false, message.mid); + const docDiv = wrapDocument(this.peerId, doc, false, false, message.mid); preview.append(docDiv); preview.classList.add('preview-with-document'); //messageDiv.classList.add((webpage.type || 'document') + '-message'); @@ -1865,7 +1870,8 @@ export default class ChatBubbles { attachmentDiv, middleware: this.getMiddleware(), isOut: our, - lazyLoadQueue: this.lazyLoadQueue + lazyLoadQueue: this.lazyLoadQueue, + chat: this.chat }); } else { const withTail = !isAndroid && !isApple && doc.type != 'round' && !message.message; @@ -1890,7 +1896,8 @@ export default class ChatBubbles { albumMustBeRenderedFull, message, bubble, - messageDiv + messageDiv, + chat: this.chat }); if(newNameContainer) { @@ -1943,7 +1950,7 @@ export default class ChatBubbles { case 'messageMediaPoll': { bubble.classList.remove('is-message-empty'); - const pollElement = wrapPoll(message.media.poll.id, message.mid); + const pollElement = wrapPoll(this.peerId, message.media.poll.id, message.mid); messageDiv.prepend(pollElement); messageDiv.classList.add('poll-message'); @@ -2015,7 +2022,7 @@ export default class ChatBubbles { } } else { if(message.reply_to_mid) { - let originalMessage = this.appMessagesManager.getMessage(message.reply_to_mid); + let originalMessage = this.chat.getMessage(message.reply_to_mid); let originalPeerTitle = this.appPeersManager.getPeerTitle(originalMessage.fromId || originalMessage.fwdFromId, true) || ''; /////////this.log('message to render reply', originalMessage, originalPeerTitle, bubble, message); @@ -2023,7 +2030,7 @@ export default class ChatBubbles { // need to download separately if(originalMessage._ == 'messageEmpty') { //////////this.log('message to render reply empty, need download', message, message.reply_to_mid); - this.appMessagesManager.wrapSingleMessage(message.reply_to_mid); + this.appMessagesManager.wrapSingleMessage(this.peerId, message.reply_to_mid); this.needUpdate.push({replyMid: message.reply_to_mid, mid: message.mid}); originalPeerTitle = 'Loading...'; @@ -2071,7 +2078,7 @@ export default class ChatBubbles { bubble.classList.add('hide-name'); } - if(this.chat.type == 'pinned') { + if(this.chat.type === 'pinned') { savedFrom = `${this.chat.peerId}_${message.mid}`; } @@ -2163,7 +2170,7 @@ export default class ChatBubbles { } while(history.length) { - let message = this.appMessagesManager.getMessage(method()); + let message = this.chat.getMessage(method()); this.renderMessage(message, reverse, true); } @@ -2223,9 +2230,9 @@ export default class ChatBubbles { public requestHistory(maxId: number, loadCount: number, backLimit: number) { //const middleware = this.getMiddleware(); - if(this.chat.type == 'chat') { + if(this.chat.type === 'chat') { return this.appMessagesManager.getHistory(this.peerId, maxId, loadCount, backLimit); - } else if(this.chat.type == 'pinned') { + } else if(this.chat.type === 'pinned') { const promise = this.appMessagesManager.getSearch(this.peerId, '', {_: 'inputMessagesFilterPinned'}, maxId, loadCount, 0, backLimit); /* if(maxId) { @@ -2238,6 +2245,8 @@ export default class ChatBubbles { } */ return promise; + } else if(this.chat.type === 'scheduled') { + return this.appMessagesManager.getScheduledMessages(this.peerId).then(mids => ({history: mids})); } } @@ -2293,7 +2302,7 @@ export default class ChatBubbles { // * filter last album, because we don't know is this the last item for(let i = additionMsgIds.length - 1; i >= 0; --i) { - const message = this.appMessagesManager.getMessage(additionMsgIds[i]); + const message = this.chat.getMessage(additionMsgIds[i]); if(message.grouped_id) additionMsgIds.splice(i, 1); else break; } @@ -2438,7 +2447,7 @@ export default class ChatBubbles { // preload more //if(!isFirstMessageRender) { - if(this.chat.type == 'chat') { + if(this.chat.type === 'chat') { const storage = this.appMessagesManager.historiesStorage[peerId]; const isMaxIdInHistory = storage.history.indexOf(maxId) !== -1; if(isMaxIdInHistory) { // * otherwise it is a search or jump @@ -2490,7 +2499,9 @@ export default class ChatBubbles { if(dateMessage.container.childElementCount == 2) { // only date div + sentinel div dateMessage.container.remove(); - this.stickyIntersector.unobserve(dateMessage.container, dateMessage.div); + if(this.stickyIntersector) { + this.stickyIntersector.unobserve(dateMessage.container, dateMessage.div); + } delete this.dateMessages[i]; } } diff --git a/src/components/chat/chat.ts b/src/components/chat/chat.ts index df0fa01b..101f5d1e 100644 --- a/src/components/chat/chat.ts +++ b/src/components/chat/chat.ts @@ -21,7 +21,7 @@ import ChatInput from "./input"; import ChatSelection from "./selection"; import ChatTopbar from "./topbar"; -export type ChatType = 'chat' | 'pinned' | 'replies' | 'discussion'; +export type ChatType = 'chat' | 'pinned' | 'replies' | 'discussion' | 'scheduled'; export default class Chat extends EventListenerBase<{ setPeer: (mid: number, isTopMessage: boolean) => void @@ -61,6 +61,14 @@ export default class Chat extends EventListenerBase<{ this.appImManager.chatsContainer.append(this.container); } + public setType(type: ChatType) { + this.type = type; + + if(this.type === 'scheduled') { + this.getMessage = (mid) => this.appMessagesManager.getMessageFromStorage(this.appMessagesManager.getScheduledMessagesStorage(this.peerId), mid); + } + } + private init() { this.topbar = new ChatTopbar(this, appSidebarRight, this.appMessagesManager, this.appPeersManager, this.appChatsManager); this.bubbles = new ChatBubbles(this, this.appMessagesManager, this.appSidebarRight, this.appStickersManager, this.appUsersManager, this.appInlineBotsManager, this.appPhotosManager, this.appDocsManager, this.appPeersManager, this.appChatsManager); @@ -68,20 +76,23 @@ export default class Chat extends EventListenerBase<{ this.selection = new ChatSelection(this.bubbles, this.input, this.appMessagesManager); this.contextMenu = new ChatContextMenu(this.bubbles.bubblesContainer, this, this.appMessagesManager, this.appChatsManager, this.appPeersManager, this.appPollsManager); - if(this.type == 'chat') { + if(this.type === 'chat') { this.topbar.constructPeerHelpers(); - } else if(this.type == 'pinned') { + } else if(this.type === 'pinned') { this.topbar.constructPinnedHelpers(); } this.topbar.construct(); this.input.construct(); - if(this.type == 'chat') { // * гений в деле, разный порядок из-за разной последовательности действий + if(this.type === 'chat') { // * гений в деле, разный порядок из-за разной последовательности действий + this.bubbles.constructPeerHelpers(); this.input.constructPeerHelpers(); - } else if(this.type == 'pinned') { - this.input.constructPinnedHelpers(); + } else if(this.type === 'pinned') { this.bubbles.constructPinnedHelpers(); + this.input.constructPinnedHelpers(); + } else if(this.type === 'scheduled') { + this.bubbles.constructScheduledHelpers(); } this.container.classList.add('type-' + this.type); @@ -187,4 +198,12 @@ export default class Chat extends EventListenerBase<{ rootScope.broadcast('peer_changed', peerId); } + + public getMessage(mid: number) { + return this.appMessagesManager.getMessageByPeer(this.peerId, mid); + } + + public getMidsByMid(mid: number) { + return this.appMessagesManager.getMidsByMid(this.peerId, mid); + } } \ No newline at end of file diff --git a/src/components/chat/contextMenu.ts b/src/components/chat/contextMenu.ts index d5c1fbd2..91d16fd7 100644 --- a/src/components/chat/contextMenu.ts +++ b/src/components/chat/contextMenu.ts @@ -50,7 +50,7 @@ export default class ChatContextMenu { // * если открыть контекстное меню для альбома не по бабблу, и последний элемент не выбран, чтобы показать остальные пункты if(chat.selection.isSelecting && !bubbleContainer) { - const mids = appMessagesManager.getMidsByMid(mid); + const mids = this.chat.getMidsByMid(mid); if(mids.length > 1) { const selectedMid = chat.selection.selectedMids.has(mid) ? mid : mids.find(mid => chat.selection.selectedMids.has(mid)); if(selectedMid) { @@ -147,17 +147,17 @@ export default class ChatContextMenu { icon: 'edit', text: 'Edit', onClick: this.onEditClick, - verify: () => this.appMessagesManager.canEditMessage(this.mid, 'text') && !!this.chat.input.messageInput + verify: () => this.appMessagesManager.canEditMessage(this.peerId, this.mid, 'text') && !!this.chat.input.messageInput }, { icon: 'copy', text: 'Copy', onClick: this.onCopyClick, - verify: () => !!this.appMessagesManager.getMessage(this.mid).message + verify: () => !!this.chat.getMessage(this.mid).message }, { icon: 'copy', text: 'Copy selected', onClick: this.onCopyClick, - verify: () => this.chat.selection.selectedMids.has(this.mid) && !![...this.chat.selection.selectedMids].find(mid => !!this.appMessagesManager.getMessage(mid).message), + verify: () => this.chat.selection.selectedMids.has(this.mid) && !![...this.chat.selection.selectedMids].find(mid => !!this.chat.getMessage(mid).message), notDirect: () => true, withSelection: true }, { @@ -165,7 +165,7 @@ export default class ChatContextMenu { text: 'Pin', onClick: this.onPinClick, verify: () => { - const message = this.appMessagesManager.getMessage(this.mid); + const message = this.chat.getMessage(this.mid); return this.mid > 0 && message._ != 'messageService' && !message.pFlags.pinned && this.appPeersManager.canPinMessage(this.peerId); } }, { @@ -173,7 +173,7 @@ export default class ChatContextMenu { text: 'Unpin', onClick: this.onUnpinClick, verify: () => { - const message = this.appMessagesManager.getMessage(this.mid); + const message = this.chat.getMessage(this.mid); return message.pFlags.pinned && this.appPeersManager.canPinMessage(this.peerId); } }, { @@ -181,7 +181,7 @@ export default class ChatContextMenu { text: 'Revote', onClick: this.onRetractVote, verify: () => { - const message = this.appMessagesManager.getMessage(this.mid); + const message = this.chat.getMessage(this.mid); const poll = message.media?.poll as Poll; return poll && poll.chosenIndexes.length && !poll.pFlags.closed && !poll.pFlags.quiz; }/* , @@ -191,9 +191,9 @@ export default class ChatContextMenu { text: 'Stop poll', onClick: this.onStopPoll, verify: () => { - const message = this.appMessagesManager.getMessage(this.mid); + const message = this.chat.getMessage(this.mid); const poll = message.media?.poll; - return this.appMessagesManager.canEditMessage(this.mid, 'poll') && poll && !poll.pFlags.closed && this.mid > 0; + return this.appMessagesManager.canEditMessage(this.peerId, this.mid, 'poll') && poll && !poll.pFlags.closed && this.mid > 0; }/* , cancelEvent: true */ }, { @@ -213,7 +213,7 @@ export default class ChatContextMenu { text: 'Select', onClick: this.onSelectClick, verify: () => { - const message = this.appMessagesManager.getMessage(this.mid); + const message = this.chat.getMessage(this.mid); return !message.action && !this.chat.selection.selectedMids.has(this.mid); }, notDirect: () => true, @@ -229,7 +229,7 @@ export default class ChatContextMenu { icon: 'delete danger', text: 'Delete', onClick: this.onDeleteClick, - verify: () => this.appMessagesManager.canDeleteMessage(this.mid) + verify: () => this.appMessagesManager.canDeleteMessage(this.peerId, this.mid) }, { icon: 'delete danger', text: 'Delete selected', @@ -245,7 +245,7 @@ export default class ChatContextMenu { }; private onReplyClick = () => { - const message = this.appMessagesManager.getMessage(this.mid); + const message = this.chat.getMessage(this.mid); const chatInputC = this.chat.input; const f = () => { chatInputC.setTopInfo('reply', f, this.appPeersManager.getPeerTitle(message.fromId, true), message.message, undefined, message); @@ -261,7 +261,7 @@ export default class ChatContextMenu { private onCopyClick = () => { const mids = this.chat.selection.isSelecting ? [...this.chat.selection.selectedMids] : [this.mid]; const str = mids.reduce((acc, mid) => { - const message = this.appMessagesManager.getMessage(mid); + const message = this.chat.getMessage(mid); return acc + (message?.message ? message.message + '\n' : ''); }, '').trim(); @@ -277,18 +277,18 @@ export default class ChatContextMenu { }; private onRetractVote = () => { - this.appPollsManager.sendVote(this.mid, []); + this.appPollsManager.sendVote(this.peerId, this.mid, []); }; private onStopPoll = () => { - this.appPollsManager.stopPoll(this.mid); + this.appPollsManager.stopPoll(this.peerId, this.mid); }; private onForwardClick = () => { if(this.chat.selection.isSelecting) { this.chat.selection.selectionForwardBtn.click(); } else { - new PopupForward(this.isTargetAGroupedItem ? [this.mid] : this.appMessagesManager.getMidsByMid(this.mid)); + new PopupForward(this.peerId, this.isTargetAGroupedItem ? [this.mid] : this.chat.getMidsByMid(this.mid)); } }; @@ -304,7 +304,7 @@ export default class ChatContextMenu { if(this.chat.selection.isSelecting) { this.chat.selection.selectionDeleteBtn.click(); } else { - new PopupDeleteMessages(this.isTargetAGroupedItem ? [this.mid] : this.appMessagesManager.getMidsByMid(this.mid)); + new PopupDeleteMessages(this.peerId, this.isTargetAGroupedItem ? [this.mid] : this.chat.getMidsByMid(this.mid)); } }; } \ No newline at end of file diff --git a/src/components/chat/input.ts b/src/components/chat/input.ts index 0a7f2f09..ad664ec5 100644 --- a/src/components/chat/input.ts +++ b/src/components/chat/input.ts @@ -68,6 +68,7 @@ export default class ChatInput { public willSendWebPage: any = null; public forwardingMids: number[] = []; + public forwardingFromPeerId: number = 0; public replyToMsgId = 0; public editMsgId = 0; public noWebPage: true; @@ -102,6 +103,7 @@ export default class ChatInput { public goDownBtn: HTMLButtonElement; public goDownUnreadBadge: HTMLElement; + btnScheduled: HTMLButtonElement; constructor(private chat: Chat, private appMessagesManager: AppMessagesManager, private appDocsManager: AppDocsManager, private appChatsManager: AppChatsManager, private appPeersManager: AppPeersManager, private appWebPagesManager: AppWebPagesManager, private appImManager: AppImManager) { this.listenerSetter = new ListenerSetter(); @@ -158,6 +160,9 @@ export default class ChatInput { this.inputScroll = new Scrollable(this.inputMessageContainer); + this.btnScheduled = ButtonIcon('schedule', {noRipple: true}); + this.btnScheduled.classList.add('btn-scheduled', 'hide'); + this.attachMenuButtons = [{ icon: 'photo', text: 'Photo or Video', @@ -219,7 +224,7 @@ export default class ChatInput { this.fileInput.multiple = true; this.fileInput.style.display = 'none'; - this.newMessageWrapper.append(this.btnToggleEmoticons, this.inputMessageContainer, this.attachMenu, this.recordTimeEl, this.fileInput); + this.newMessageWrapper.append(this.btnToggleEmoticons, this.inputMessageContainer, this.btnScheduled, this.attachMenu, this.recordTimeEl, this.fileInput); this.rowsWrapper.append(this.replyElements.container, this.newMessageWrapper); @@ -311,6 +316,10 @@ export default class ChatInput { }); */ attachClickEvent(this.btnSend, this.onBtnSendClick, {listenerSetter: this.listenerSetter}); + attachClickEvent(this.btnScheduled, (e) => { + this.appImManager.setInnerPeer(this.chat.peerId, 0, 'scheduled'); + }, {listenerSetter: this.listenerSetter}); + if(this.recorder) { const onCancelRecordClick = (e: Event) => { cancelEvent(e); @@ -450,6 +459,13 @@ export default class ChatInput { if(this.chat.type == 'pinned') { this.chatInput.classList.toggle('can-pin', this.appPeersManager.canPinMessage(peerId)); + } else if(this.chat.type == 'chat') { + this.btnScheduled.classList.add('hide'); + const middleware = this.chat.bubbles.getMiddleware(); + this.appMessagesManager.getScheduledMessages(peerId).then(mids => { + if(!middleware()) return; + this.btnScheduled.classList.toggle('hide', !mids.length); + }); } if(this.messageInput) { @@ -953,11 +969,12 @@ export default class ChatInput { if(this.helperWaitingForward) return; this.helperWaitingForward = true; + const fromId = this.forwardingFromPeerId; const mids = this.forwardingMids.slice(); const helperFunc = this.helperFunc; this.clearHelper(); let selected = false; - new PopupForward(mids, () => { + new PopupForward(fromId, mids, () => { selected = true; }, () => { this.helperWaitingForward = false; @@ -1032,7 +1049,7 @@ export default class ChatInput { //return; if(this.editMsgId) { - this.appMessagesManager.editMessage(this.editMsgId, str, { + this.appMessagesManager.editMessage(this.chat.peerId, this.editMsgId, str, { noWebPage: this.noWebPage }); } else { @@ -1046,8 +1063,10 @@ export default class ChatInput { // * wait for sendText set messageId for invokeAfterMsg if(this.forwardingMids.length) { const mids = this.forwardingMids.slice(); + const fromPeerId = this.forwardingFromPeerId; + const peerId = this.chat.peerId; setTimeout(() => { - this.appMessagesManager.forwardMessages(this.chat.peerId, mids); + this.appMessagesManager.forwardMessages(peerId, fromPeerId, mids); }, 0); } @@ -1071,7 +1090,7 @@ export default class ChatInput { } public initMessageEditing(mid: number) { - const message = this.appMessagesManager.getMessage(mid); + const message = this.chat.getMessage(mid); let input = RichTextProcessor.wrapDraftText(message.message, {entities: message.totalEntities}); const f = () => { @@ -1082,11 +1101,11 @@ export default class ChatInput { f(); } - public initMessagesForward(mids: number[]) { + public initMessagesForward(fromPeerId: number, mids: number[]) { const f = () => { //const peerTitles: string[] const smth: Set = new Set(mids.map(mid => { - const message = this.appMessagesManager.getMessage(mid); + const message = this.appMessagesManager.getMessageByPeer(fromPeerId, mid); if(message.fwd_from && message.fwd_from.from_name && !message.fromId && !message.fwdFromId) { return message.fwd_from.from_name; } else { @@ -1103,13 +1122,14 @@ export default class ChatInput { const title = peerTitles.length < 3 ? peerTitles.join(' and ') : peerTitles[0] + ' and ' + (peerTitles.length - 1) + ' others'; if(mids.length == 1) { - const message = this.appMessagesManager.getMessage(mids[0]); + const message = this.appMessagesManager.getMessageByPeer(fromPeerId, mids[0]); this.setTopInfo('forward', f, title, message.message, undefined, message); } else { this.setTopInfo('forward', f, title, mids.length + ' forwarded messages'); } this.forwardingMids = mids.slice(); + this.forwardingFromPeerId = fromPeerId; }; f(); @@ -1128,6 +1148,7 @@ export default class ChatInput { this.replyToMsgId = 0; this.forwardingMids.length = 0; + this.forwardingFromPeerId = 0; this.editMsgId = 0; this.helperType = this.helperFunc = undefined; this.chat.container.classList.remove('is-helper-active'); diff --git a/src/components/chat/pinnedMessage.ts b/src/components/chat/pinnedMessage.ts index 59d42166..fc04c0eb 100644 --- a/src/components/chat/pinnedMessage.ts +++ b/src/components/chat/pinnedMessage.ts @@ -524,7 +524,7 @@ export default class ChatPinnedMessage { } public async followPinnedMessage(mid: number) { - const message = this.appMessagesManager.getMessage(mid); + const message = this.chat.getMessage(mid); if(message && !message.deleted) { this.chat.setPeer(this.topbar.peerId, mid); (this.chat.setPeerPromise || Promise.resolve()).then(() => { // * debounce fast clicker @@ -550,7 +550,7 @@ export default class ChatPinnedMessage { const count = this.count; if(count) { const pinnedIndex = this.pinnedIndex; - const message = this.appMessagesManager.getMessage(this.pinnedMid); + const message = this.chat.getMessage(this.pinnedMid); //this.animatedCounter.prepareNumber(count); diff --git a/src/components/chat/selection.ts b/src/components/chat/selection.ts index 138c52a4..d5fb908e 100644 --- a/src/components/chat/selection.ts +++ b/src/components/chat/selection.ts @@ -207,7 +207,7 @@ export default class ChatSelection { let cantForward = !this.selectedMids.size, cantDelete = !this.selectedMids.size; for(const mid of this.selectedMids.values()) { - const message = this.appMessagesManager.getMessage(mid); + const message = this.appMessagesManager.getMessageByPeer(this.bubbles.peerId, mid); if(!cantForward) { if(message.action) { cantForward = true; @@ -216,7 +216,7 @@ export default class ChatSelection { if(!cantDelete) { - const canDelete = this.appMessagesManager.canDeleteMessage(mid); + const canDelete = this.appMessagesManager.canDeleteMessage(this.bubbles.peerId, mid); if(!canDelete) { cantDelete = true; } @@ -295,7 +295,7 @@ export default class ChatSelection { this.selectionForwardBtn = Button('btn-primary btn-transparent selection-container-forward', {icon: 'forward'}); this.selectionForwardBtn.append('Forward'); this.listenerSetter.add(this.selectionForwardBtn, 'click', () => { - new PopupForward([...this.selectedMids], () => { + new PopupForward(this.bubbles.peerId, [...this.selectedMids], () => { this.cancelSelection(); }); }); @@ -303,7 +303,7 @@ export default class ChatSelection { this.selectionDeleteBtn = Button('btn-primary btn-transparent danger selection-container-delete', {icon: 'delete'}); this.selectionDeleteBtn.append('Delete'); this.listenerSetter.add(this.selectionDeleteBtn, 'click', () => { - new PopupDeleteMessages([...this.selectedMids], () => { + new PopupDeleteMessages(this.bubbles.peerId, [...this.selectedMids], () => { this.cancelSelection(); }); }); @@ -365,7 +365,7 @@ export default class ChatSelection { } public isGroupedMidsSelected(mid: number) { - const mids = this.appMessagesManager.getMidsByMid(mid); + const mids = this.appMessagesManager.getMidsByMid(this.bubbles.peerId, mid); const selectedMids = mids.filter(mid => this.selectedMids.has(mid)); return mids.length == selectedMids.length; } @@ -376,7 +376,7 @@ export default class ChatSelection { const isGrouped = bubble.classList.contains('is-grouped'); if(isGrouped) { if(!this.isGroupedBubbleSelected(bubble)) { - const mids = this.appMessagesManager.getMidsByMid(mid); + const mids = this.appMessagesManager.getMidsByMid(this.bubbles.peerId, mid); mids.forEach(mid => this.selectedMids.delete(mid)); } diff --git a/src/components/chat/topbar.ts b/src/components/chat/topbar.ts index 6f96d392..dc1b6d53 100644 --- a/src/components/chat/topbar.ts +++ b/src/components/chat/topbar.ts @@ -125,7 +125,7 @@ export default class ChatTopbar { this.pinnedMessage.followPinnedMessage(mid); //} } else { - const message = this.appMessagesManager.getMessage(mid); + const message = this.appMessagesManager.getMessageByPeer(this.peerId, mid); this.chat.appImManager.setInnerPeer(message.peerId, mid); } @@ -373,7 +373,7 @@ export default class ChatTopbar { public setTitle(count?: number) { let title = ''; - if(this.chat.type == 'pinned') { + if(this.chat.type === 'pinned') { title = count === -1 ? 'Pinned Messages' : (count === 1 ? 'Pinned Message' : (count + ' Pinned Messages')); if(count === undefined) { @@ -393,7 +393,15 @@ export default class ChatTopbar { } }); } - } else { + } else if(this.chat.type === 'scheduled') { + title = count === -1 ? 'Scheduled Messages' : (count === 1 ? 'Scheduled Message' : (count + ' Scheduled Messages')); + + if(count === undefined) { + this.appMessagesManager.getScheduledMessages(this.peerId).then(mids => { + this.setTitle(mids.length); + }); + } + } else if(this.chat.type === 'chat') { if(this.peerId == rootScope.myId) title = 'Saved Messages'; else title = this.appPeersManager.getPeerTitle(this.peerId); } diff --git a/src/components/poll.ts b/src/components/poll.ts index 716ac48a..456da9f9 100644 --- a/src/components/poll.ts +++ b/src/components/poll.ts @@ -152,6 +152,7 @@ export default class PollElement extends HTMLElement { private chosenIndexes: number[] = []; private percents: number[]; + private peerId: number; private pollId: string; private mid: number; @@ -178,6 +179,7 @@ export default class PollElement extends HTMLElement { //console.log('line total length:', lineTotalLength); } + this.peerId = +this.getAttribute('peer-id'); this.pollId = this.getAttribute('poll-id'); this.mid = +this.getAttribute('message-id'); const {poll, results} = appPollsManager.getPoll(this.pollId); @@ -307,7 +309,7 @@ export default class PollElement extends HTMLElement { setTimeout(() => { // нужно запросить апдейт чтобы опрос обновился - appPollsManager.getResults(this.mid); + appPollsManager.getResults(this.peerId, this.mid); }, 3e3); } }, 1e3); @@ -324,7 +326,7 @@ export default class PollElement extends HTMLElement { this.viewResults.addEventListener('click', (e) => { cancelEvent(e); - appSidebarRight.pollResultsTab.init(this.pollId, this.mid); + appSidebarRight.pollResultsTab.init(this.peerId, this.pollId, this.mid); }); ripple(this.viewResults); @@ -464,7 +466,7 @@ export default class PollElement extends HTMLElement { this.classList.add('disable-hover'); this.sentVote = true; - return this.sendVotePromise = appPollsManager.sendVote(this.mid, indexes).then(() => { + return this.sendVotePromise = appPollsManager.sendVote(this.peerId, this.mid, indexes).then(() => { targets.forEach(target => { target.classList.remove('is-voting'); }); diff --git a/src/components/popupDeleteMessages.ts b/src/components/popupDeleteMessages.ts index c7274a12..97324501 100644 --- a/src/components/popupDeleteMessages.ts +++ b/src/components/popupDeleteMessages.ts @@ -6,14 +6,13 @@ import { PopupButton } from "./popup"; import PopupPeer from "./popupPeer"; export default class PopupDeleteMessages { - constructor(mids: number[], onConfirm?: () => void) { - const peerId = appMessagesManager.getMessage(mids[0]).peerId; + constructor(peerId: number, mids: number[], onConfirm?: () => void) { const firstName = appPeersManager.getPeerTitle(peerId, false, true); mids = mids.slice(); const callback = (revoke: boolean) => { onConfirm && onConfirm(); - appMessagesManager.deleteMessages(mids, revoke); + appMessagesManager.deleteMessages(peerId, mids, revoke); }; let title: string, description: string, buttons: PopupButton[]; @@ -45,7 +44,7 @@ export default class PopupDeleteMessages { const hasRights = appChatsManager.hasRights(-peerId, 'deleteRevoke'); if(chat._ == 'chat') { const canRevoke = hasRights ? mids.slice() : mids.filter(mid => { - const message = appMessagesManager.getMessage(mid); + const message = appMessagesManager.getMessageByPeer(peerId, mid); return message.fromId == rootScope.myId; }); diff --git a/src/components/popupForward.ts b/src/components/popupForward.ts index 5c8f3715..ba1fcd96 100644 --- a/src/components/popupForward.ts +++ b/src/components/popupForward.ts @@ -7,7 +7,7 @@ export default class PopupForward extends PopupElement { private selector: AppSelectPeers; //private scrollable: Scrollable; - constructor(mids: number[], onSelect?: () => Promise | void, onClose?: () => void) { + constructor(fromPeerId: number, mids: number[], onSelect?: () => Promise | void, onClose?: () => void) { super('popup-forward', null, {closable: true, overlayClosable: true, body: true}); if(onClose) this.onClose = onClose; @@ -21,7 +21,7 @@ export default class PopupForward extends PopupElement { await (onSelect ? onSelect() || Promise.resolve() : Promise.resolve()); appImManager.setInnerPeer(peerId); - appImManager.chat.input.initMessagesForward(mids.slice()); + appImManager.chat.input.initMessagesForward(fromPeerId, mids.slice()); }, ['dialogs', 'contacts'], () => { this.show(); this.selector.checkForTriggers(); // ! due to zero height before mounting diff --git a/src/components/popupNewMedia.ts b/src/components/popupNewMedia.ts index d1a6fde9..a6c1d5c1 100644 --- a/src/components/popupNewMedia.ts +++ b/src/components/popupNewMedia.ts @@ -224,7 +224,7 @@ export default class PopupNewMedia extends PopupElement { params.objectURL = URL.createObjectURL(file); } - const docDiv = wrapDocument({ + const docDiv = wrapDocument(0, { file: file, file_name: file.name || '', size: file.size, diff --git a/src/components/sidebarRight/tabs/forward.ts b/src/components/sidebarRight/tabs/forward.ts index 7732a2b9..135318a3 100644 --- a/src/components/sidebarRight/tabs/forward.ts +++ b/src/components/sidebarRight/tabs/forward.ts @@ -45,7 +45,7 @@ export default class AppForwardTab implements SliderTab { let s = () => { let promises = peerIds.splice(0, 3).map(peerId => { - return appMessagesManager.forwardMessages(peerId, this.mids); + return appMessagesManager.forwardMessages(peerId, 0, this.mids); }); Promise.all(promises).then(() => { diff --git a/src/components/sidebarRight/tabs/pollResults.ts b/src/components/sidebarRight/tabs/pollResults.ts index 9c04b9fc..dba8f4cf 100644 --- a/src/components/sidebarRight/tabs/pollResults.ts +++ b/src/components/sidebarRight/tabs/pollResults.ts @@ -14,6 +14,7 @@ export default class AppPollResultsTab implements SliderTab { private resultsDiv = this.contentDiv.firstElementChild as HTMLDivElement; private scrollable: Scrollable; + private peerId: number; private pollId: string; private mid: number; @@ -31,11 +32,12 @@ export default class AppPollResultsTab implements SliderTab { this.cleanup(); } - public init(pollId: string, mid: number) { - if(this.pollId == pollId && this.mid == mid) return; + public init(peerId: number, pollId: string, mid: number) { + if(this.peerId == peerId && this.pollId == pollId && this.mid == mid) return; this.cleanup(); + this.peerId = peerId; this.pollId = pollId; this.mid = mid; @@ -86,7 +88,7 @@ export default class AppPollResultsTab implements SliderTab { if(loading) return; loading = true; - appPollsManager.getVotes(mid, answer.option, offset, limit).then(votesList => { + appPollsManager.getVotes(peerId, mid, answer.option, offset, limit).then(votesList => { votesList.votes.forEach(vote => { const {dom} = appDialogsManager.addDialogNew({ dialog: vote.user_id, diff --git a/src/components/sidebarRight/tabs/sharedMedia.ts b/src/components/sidebarRight/tabs/sharedMedia.ts index 44d90a35..42898c8c 100644 --- a/src/components/sidebarRight/tabs/sharedMedia.ts +++ b/src/components/sidebarRight/tabs/sharedMedia.ts @@ -179,7 +179,7 @@ export default class AppSharedMediaTab implements SliderTab { return; } - const message = appMessagesManager.getMessage(messageId); + const message = appMessagesManager.getMessageByPeer(this.peerId, messageId); const ids = Object.keys(this.mediaDivsByIds).map(k => +k).sort((a, b) => a - b); const idx = ids.findIndex(i => i == messageId); @@ -331,11 +331,11 @@ export default class AppSharedMediaTab implements SliderTab { if(type != 'inputMessagesFilterUrl') { for(let mid of ids) { - let message = appMessagesManager.getMessage(mid); + let message = appMessagesManager.getMessageByPeer(this.peerId, mid); if(message.media) messages.push(message); } } else { - messages = ids.slice().map(mid => appMessagesManager.getMessage(mid)); + messages = ids.slice().map(mid => appMessagesManager.getMessageByPeer(this.peerId, mid)); } let filtered: any[] = []; @@ -528,7 +528,7 @@ export default class AppSharedMediaTab implements SliderTab { case 'inputMessagesFilterMusic': case 'inputMessagesFilterDocument': { for(const message of messages) { - const div = wrapDocument(message.media.document, true, false, message.mid, 400); + const div = wrapDocument(this.peerId, message.media.document, true, false, message.mid, 400); div.dataset.mid = '' + message.mid; elemsToAppend.push(div); } diff --git a/src/components/wrappers.ts b/src/components/wrappers.ts index 45b2aa81..5f4f8c0d 100644 --- a/src/components/wrappers.ts +++ b/src/components/wrappers.ts @@ -27,6 +27,7 @@ import './middleEllipsis'; import { nextRandomInt } from '../helpers/random'; import RichTextProcessor from '../lib/richtextprocessor'; import appImManager from '../lib/appManagers/appImManager'; +import Chat from './chat/chat'; const MAX_VIDEO_AUTOPLAY_SIZE = 50 * 1024 * 1024; // 50 MB @@ -93,7 +94,7 @@ export function wrapVideo({doc, container, message, boxWidth, boxHeight, withTai video.setAttribute('playsinline', 'true'); if(doc.type == 'round') { //video.muted = true; - const globalVideo = appMediaPlaybackController.addMedia(doc, message.mid); + const globalVideo = appMediaPlaybackController.addMedia(message.peerId, doc, message.mid); video.addEventListener('canplay', () => { if(globalVideo.currentTime > 0) { @@ -342,9 +343,9 @@ export const formatDate = (timestamp: number, monthShort = false, withYear = tru return str + ' at ' + date.getHours() + ':' + ('0' + date.getMinutes()).slice(-2); }; -export function wrapDocument(doc: MyDocument, withTime = false, uploading = false, mid?: number, fontWeight = 500): HTMLElement { +export function wrapDocument(peerId: number, doc: MyDocument, withTime = false, uploading = false, mid?: number, fontWeight = 500): HTMLElement { if(doc.type == 'audio' || doc.type == 'voice') { - const audioElement = wrapAudio(doc, withTime, mid); + const audioElement = wrapAudio(peerId, doc, withTime, mid); audioElement.dataset.fontWeight = '' + fontWeight; return audioElement; } @@ -437,8 +438,9 @@ export function wrapDocument(doc: MyDocument, withTime = false, uploading = fals return docDiv; } -export function wrapAudio(doc: MyDocument, withTime = false, mid?: number): HTMLElement { +export function wrapAudio(peerId: number, doc: MyDocument, withTime = false, mid?: number): HTMLElement { let elem = new AudioElement(); + elem.setAttribute('peer-id', '' + peerId); elem.setAttribute('doc-id', doc.id); elem.setAttribute('with-time', '' + +withTime); elem.setAttribute('message-id', '' + mid); @@ -898,20 +900,21 @@ export function prepareAlbum(options: { } */ } -export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLoadQueue, isOut}: { +export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLoadQueue, isOut, chat}: { groupId: string, attachmentDiv: HTMLElement, middleware?: () => boolean, lazyLoadQueue?: LazyLoadQueue, uploading?: boolean, - isOut: boolean + isOut: boolean, + chat: Chat }) { const items: {size: PhotoSize.photoSize, media: any, message: any}[] = []; // !lowest msgID will be the FIRST in album const storage = appMessagesManager.getMidsByAlbum(groupId); for(const mid of storage) { - const m = appMessagesManager.getMessage(mid); + const m = chat.getMessage(mid); const media = m.media.photo || m.media.document; const size: any = media._ == 'photo' ? appPhotosManager.choosePhotoSize(media, 480, 480) : {w: media.w, h: media.h}; @@ -966,24 +969,25 @@ export function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLo }); } -export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble, messageDiv}: { +export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble, messageDiv, chat}: { albumMustBeRenderedFull: boolean, message: any, messageDiv: HTMLElement, bubble: HTMLElement, - uploading?: boolean + uploading?: boolean, + chat: Chat }) { let nameContainer: HTMLDivElement; - const mids = albumMustBeRenderedFull ? appMessagesManager.getMidsByMid(message.mid) : [message.mid]; + const mids = albumMustBeRenderedFull ? chat.getMidsByMid(message.mid) : [message.mid]; const isPending = message.mid < 0; if(isPending) { mids.reverse(); } mids.forEach((mid, idx) => { - const message = appMessagesManager.getMessage(mid); + const message = chat.getMessage(mid); const doc = message.media.document; - const div = wrapDocument(doc, false, isPending, mid); + const div = wrapDocument(chat.peerId, doc, false, isPending, mid); const container = document.createElement('div'); container.classList.add('document-container'); @@ -1037,8 +1041,9 @@ export function wrapGroupedDocuments({albumMustBeRenderedFull, message, bubble, return nameContainer; } -export function wrapPoll(pollId: string, mid: number) { +export function wrapPoll(peerId: number, pollId: string, mid: number) { const elem = new PollElement(); + elem.setAttribute('peer-id', '' + peerId); elem.setAttribute('poll-id', pollId); elem.setAttribute('message-id', '' + mid); return elem; diff --git a/src/lib/appManagers/AppInlineBotsManager.ts b/src/lib/appManagers/AppInlineBotsManager.ts index e335c75e..e6d60d35 100644 --- a/src/lib/appManagers/AppInlineBotsManager.ts +++ b/src/lib/appManagers/AppInlineBotsManager.ts @@ -4,8 +4,6 @@ import appPeersManager from "./appPeersManager"; import apiManagerProxy from "../mtproto/mtprotoworker"; import { RichTextProcessor } from "../richtextprocessor"; import appDocsManager from "./appDocsManager"; -import appMessagesIdsManager from "./appMessagesIdsManager"; -import appMessagesManager from "./appMessagesManager"; import appPhotosManager from "./appPhotosManager"; import appUsersManager from "./appUsersManager"; @@ -270,13 +268,10 @@ export class AppInlineBotsManager { }) } */ - public callbackButtonClick(mid: number, button: any) { - let message = appMessagesManager.getMessage(mid); - let peerId = appMessagesManager.getMessagePeer(message); - + public callbackButtonClick(peerId: number, mid: number, button: any) { return apiManagerProxy.invokeApi('messages.getBotCallbackAnswer', { peer: appPeersManager.getInputPeerById(peerId), - msg_id: appMessagesIdsManager.getMessageLocalId(mid), + msg_id: mid, data: button.data }, {timeout: 1, stopTime: -1, noErrorBox: true}).then((callbackAnswer) => { if(typeof callbackAnswer.message === 'string' && callbackAnswer.message.length) { diff --git a/src/lib/appManagers/appDialogsManager.ts b/src/lib/appManagers/appDialogsManager.ts index 65cc82c2..c60a791d 100644 --- a/src/lib/appManagers/appDialogsManager.ts +++ b/src/lib/appManagers/appDialogsManager.ts @@ -939,7 +939,7 @@ export class AppDialogsManager { } if(!lastMessage) { - lastMessage = appMessagesManager.getMessage(dialog.top_message); + lastMessage = appMessagesManager.getMessageByPeer(dialog.peerId, dialog.top_message); } if(lastMessage._ == 'messageEmpty' || (lastMessage._ == 'messageService' && !lastMessage.rReply)) { @@ -1048,7 +1048,7 @@ export class AppDialogsManager { dom.listEl.classList.toggle('is-muted', isMuted); } - const lastMessage = appMessagesManager.getMessage(dialog.top_message); + const lastMessage = appMessagesManager.getMessageByPeer(dialog.peerId, dialog.top_message); if(lastMessage._ != 'messageEmpty' && !lastMessage.deleted && lastMessage.fromId == rootScope.myId && lastMessage.peerId != rootScope.myId/* && dialog.read_outbox_max_id */) { // maybe comment, 06.20.2020 diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index 0d4b429e..d76a1180 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -20,7 +20,7 @@ import appPhotosManager from './appPhotosManager'; import appProfileManager from './appProfileManager'; import appStickersManager from './appStickersManager'; import appWebPagesManager from './appWebPagesManager'; -import { cancelEvent, findUpClassName, generatePathData, getFilesFromEvent, placeCaretAtEnd } from '../../helpers/dom'; +import { cancelEvent, getFilesFromEvent, placeCaretAtEnd } from '../../helpers/dom'; import PopupNewMedia from '../../components/popupNewMedia'; import { TransitionSlider } from '../../components/transition'; import { numberWithCommas } from '../../helpers/number'; @@ -28,9 +28,7 @@ import MarkupTooltip from '../../components/chat/markupTooltip'; import { isTouchSupported } from '../../helpers/touchSupport'; import appPollsManager from './appPollsManager'; import SetTransition from '../../components/singleTransition'; -import { isSafari } from '../../helpers/userAgent'; import ChatDragAndDrop from '../../components/chat/dragAndDrop'; -import appMessagesIdsManager from './appMessagesIdsManager'; //console.log('appImManager included33!'); @@ -154,9 +152,6 @@ export class AppImManager { appUsersManager.resolveUsername(p).then(peer => { const isUser = peer._ == 'user'; const peerId = isUser ? peer.id : -peer.id; - if(postId) { - postId = appMessagesIdsManager.getFullMessageId(postId, -peerId); - } this.setInnerPeer(peerId, postId); }); @@ -212,11 +207,11 @@ export class AppImManager { if(history?.history) { let goodMid: number; for(const mid of history.history) { - const message = appMessagesManager.getMessage(mid); + const message = appMessagesManager.getMessageByPeer(chat.peerId, mid); const good = this.myId == chat.peerId ? message.fromId == this.myId : message.pFlags.out; if(good) { - if(appMessagesManager.canEditMessage(mid, 'text')) { + if(appMessagesManager.canEditMessage(this.chat.peerId, mid, 'text')) { goodMid = mid; } @@ -508,7 +503,7 @@ export class AppImManager { this.createNewChat(); if(type) { - this.chat.type = type; + this.chat.setType(type); } this.chatsSelectTab(this.chat.container); diff --git a/src/lib/appManagers/appMessagesIDsManager.ts b/src/lib/appManagers/appMessagesIDsManager.ts deleted file mode 100644 index 38f2aab7..00000000 --- a/src/lib/appManagers/appMessagesIDsManager.ts +++ /dev/null @@ -1,85 +0,0 @@ -import { MOUNT_CLASS_TO } from "../mtproto/mtproto_config"; -import appStateManager from "./appStateManager"; - -export class AppMessagesIdsManager { - public channelLocals: {[channelId: string]: number} = {}; - public channelsByLocals: {[localStart: string]: number} = {}; - public channelCurLocal = 0; - public fullMsgIdModulus = 4294967296; - - constructor() { - appStateManager.getState().then(state => { - const cached = state.messagesIdsLocals; - if(cached) { - this.channelLocals = cached.channelLocals; - this.channelsByLocals = cached.channelsByLocals; - this.channelCurLocal = cached.channelCurLocal; - } - }); - - appStateManager.addListener('save', () => { - appStateManager.pushToState('messagesIdsLocals', { - channelLocals: this.channelLocals, - channelsByLocals: this.channelsByLocals, - channelCurLocal: this.channelCurLocal - }); - }); - } - - public getFullMessageId(msgId: number, channelId: number): number { - if(!channelId || msgId <= 0) { - return msgId; - } - - msgId = this.getMessageLocalId(msgId); - let localStart = this.channelLocals[channelId]; - if(!localStart) { - localStart = (++this.channelCurLocal) * this.fullMsgIdModulus; - this.channelsByLocals[localStart] = channelId; - this.channelLocals[channelId] = localStart; - } - - return localStart + msgId; - } - - public getMessageIdInfo(fullMsgId: number) { - if(fullMsgId < this.fullMsgIdModulus) { - return [fullMsgId, 0]; - } - - const msgId = fullMsgId % this.fullMsgIdModulus; - const channelId = this.channelsByLocals[fullMsgId - msgId]; - - return [msgId, channelId]; - } - - public getMessageLocalId(fullMsgId: number) { - return fullMsgId ? fullMsgId % this.fullMsgIdModulus : 0; - } - - public splitMessageIdsByChannels(mids: number[]) { - const msgIdsByChannels: {[channelId: number]: number[]} = {}; - const midsByChannels: {[channelId: number]: number[]} = {}; - for(const mid of mids) { - const msgChannel = this.getMessageIdInfo(mid); - const channelId = msgChannel[1]; - - if(msgIdsByChannels[channelId] === undefined) { - msgIdsByChannels[channelId] = []; - midsByChannels[channelId] = []; - } - - msgIdsByChannels[channelId].push(msgChannel[0]); - midsByChannels[channelId].push(mid); - } - - return { - msgIds: msgIdsByChannels, - mids: midsByChannels - }; - } -} - -const appMessagesIdsManager = new AppMessagesIdsManager(); -MOUNT_CLASS_TO && (MOUNT_CLASS_TO.appMessagesIdsManager = appMessagesIdsManager); -export default appMessagesIdsManager; diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 5830abe9..730d73dd 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -1,7 +1,7 @@ import ProgressivePreloader from "../../components/preloader"; import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePromise"; import { tsNow } from "../../helpers/date"; -import { copy, defineNotNumerableProperties, safeReplaceObject, getObjectKeysAndSort } from "../../helpers/object"; +import { copy, defineNotNumerableProperties, getObjectKeysAndSort } from "../../helpers/object"; import { randomLong } from "../../helpers/random"; import { splitStringByLength, limitSymbols } from "../../helpers/string"; import { Dialog as MTDialog, DialogPeer, DocumentAttribute, InputMedia, InputMessage, InputNotifyPeer, InputPeerNotifySettings, InputSingleMedia, Message, MessageAction, MessageEntity, MessagesDialogs, MessagesFilter, MessagesMessages, MessagesPeerDialogs, MethodDeclMap, NotifyPeer, PhotoSize, SendMessageAction, Update } from "../../layer"; @@ -25,7 +25,6 @@ import apiUpdatesManager from "./apiUpdatesManager"; import appChatsManager from "./appChatsManager"; import appDocsManager, { MyDocument } from "./appDocsManager"; import appDownloadManager from "./appDownloadManager"; -import appMessagesIdsManager from "./appMessagesIdsManager"; import appPeersManager from "./appPeersManager"; import appPhotosManager, { MyPhoto } from "./appPhotosManager"; import appPollsManager from "./appPollsManager"; @@ -79,10 +78,11 @@ export type PinnedStorage = Partial<{ count: number, maxId: number }>; +export type MessagesStorage = {[mid: string]: any}; export class AppMessagesManager { - public messagesStorage: {[mid: string]: any} = {}; - public messagesStorageByPeerId: {[peerId: string]: AppMessagesManager['messagesStorage']} = {}; - public groupedMessagesStorage: {[groupId: string]: {[mid: string]: any}} = {}; // will be used for albums + public messagesStorageByPeerId: {[peerId: string]: MessagesStorage} = {}; + public groupedMessagesStorage: {[groupId: string]: MessagesStorage} = {}; // will be used for albums + public scheduledMessagesStorage: {[peerId: string]: MessagesStorage} = {}; public historiesStorage: { [peerId: string]: HistoryStorage } = {}; @@ -111,7 +111,7 @@ export class AppMessagesManager { } } = {}; - public needSingleMessages: number[] = []; + public needSingleMessages: {[peerId: string]: number[]} = {}; private fetchSingleMessagesPromise: Promise = null; public maxSeenId = 0; @@ -147,7 +147,7 @@ export class AppMessagesManager { public filtersStorage: FiltersStorage; constructor() { - this.dialogsStorage = new DialogsStorage(this, appMessagesIdsManager, appChatsManager, appPeersManager, serverTimeManager); + this.dialogsStorage = new DialogsStorage(this, appChatsManager, appPeersManager, serverTimeManager); this.filtersStorage = new FiltersStorage(appPeersManager, appUsersManager, /* apiManager, */ rootScope); rootScope.on('apiUpdate', (e) => { @@ -157,7 +157,7 @@ export class AppMessagesManager { rootScope.on('webpage_updated', (e) => { const eventData = e.detail; eventData.msgs.forEach((mid) => { - const message = this.getMessage(mid) as Message.message; + const message = this.getMessageById(mid) as Message.message; if(!message) return; message.media = { _: 'messageMediaWebPage', @@ -222,7 +222,7 @@ export class AppMessagesManager { dialog = copy(dialog); let removeUnread = 0; for(const mid of history) { - const message = this.getMessage(mid); + const message = this.getMessageByPeer(dialog.peerId, mid); if(/* message._ != 'messageEmpty' && */message.id > 0) { messages.push(message); @@ -254,7 +254,7 @@ export class AppMessagesManager { }); appStateManager.getState().then(state => { - if(state.maxSeenMsgId && !appMessagesIdsManager.getMessageIdInfo(state.maxSeenMsgId)[1]) { + if(state.maxSeenMsgId) { this.maxSeenId = state.maxSeenMsgId; } @@ -291,7 +291,7 @@ export class AppMessagesManager { this.saveConversation(dialog); // ! WARNING, убрать это когда нужно будет делать чтобы pending сообщения сохранялись - const message = this.getMessage(dialog.top_message); + const message = this.getMessageByPeer(dialog.peerId, dialog.top_message); if(message.deleted) { this.reloadConversation(dialog.peerId); } @@ -320,7 +320,7 @@ export class AppMessagesManager { return obj.deferred; } - public editMessage(mid: number, text: string, options: Partial<{ + public editMessage(peerId: number, mid: number, text: string, options: Partial<{ noWebPage: true, newMedia: any }> = {}): Promise { @@ -331,7 +331,7 @@ export class AppMessagesManager { if(mid < 0) { return this.invokeAfterMessageIsSent(mid, 'edit', (mid) => { this.log('invoke editMessage callback', mid); - return this.editMessage(mid, text, options); + return this.editMessage(peerId, mid, text, options); }); } @@ -341,12 +341,9 @@ export class AppMessagesManager { text = RichTextProcessor.parseMarkdown(text, entities); } - const message = this.getMessage(mid); - const peerId = this.getMessagePeer(message); - return apiManager.invokeApi('messages.editMessage', { peer: appPeersManager.getInputPeerById(peerId), - id: appMessagesIdsManager.getMessageLocalId(mid), + id: mid, message: text, media: options.newMedia, entities: entities ? this.getInputEntities(entities) : undefined, @@ -483,7 +480,7 @@ export class AppMessagesManager { apiPromise = apiManager.invokeApiAfter('messages.sendInlineBotResult', { peer: appPeersManager.getInputPeerById(peerId), random_id: randomIdS, - reply_to_msg_id: replyToMsgId ? appMessagesIdsManager.getMessageLocalId(replyToMsgId) : undefined, + reply_to_msg_id: replyToMsgId || undefined, query_id: options.queryId, id: options.resultId, clear_draft: options.clearDraft @@ -494,7 +491,7 @@ export class AppMessagesManager { peer: appPeersManager.getInputPeerById(peerId), message: text, random_id: randomIdS, - reply_to_msg_id: replyToMsgId ? appMessagesIdsManager.getMessageLocalId(replyToMsgId) : undefined, + reply_to_msg_id: replyToMsgId || undefined, entities: sendEntites, clear_draft: options.clearDraft }, sentRequestOptions); @@ -863,7 +860,7 @@ export class AppMessagesManager { deferred.resolve(); sentDeferred.reject(err); - this.cancelPendingMessage(randomIdS); + this.cancelPendingMessage(peerId, randomIdS); this.setTyping(peerId, 'sendMessageCancelAction'); } }); @@ -894,7 +891,7 @@ export class AppMessagesManager { media: inputMedia, message: caption, random_id: randomIdS, - reply_to_msg_id: appMessagesIdsManager.getMessageLocalId(replyToMsgId) + reply_to_msg_id: replyToMsgId }).then((updates) => { apiUpdatesManager.processUpdateMessage(updates); }, (error) => { @@ -988,7 +985,7 @@ export class AppMessagesManager { return apiManager.invokeApi('messages.sendMultiMedia', { peer: inputPeer, multi_media: multiMedia, - reply_to_msg_id: appMessagesIdsManager.getMessageLocalId(replyToMsgId) + reply_to_msg_id: replyToMsgId }).then((updates) => { apiUpdatesManager.processUpdateMessage(updates); }, (error) => { @@ -1192,7 +1189,7 @@ export class AppMessagesManager { apiPromise = apiManager.invokeApiAfter('messages.sendInlineBotResult', { peer: appPeersManager.getInputPeerById(peerId), random_id: randomIdS, - reply_to_msg_id: replyToMsgId ? appMessagesIdsManager.getMessageLocalId(replyToMsgId) : undefined, + reply_to_msg_id: replyToMsgId || undefined, query_id: options.queryId, id: options.resultId, clear_draft: options.clearDraft @@ -1202,7 +1199,7 @@ export class AppMessagesManager { peer: appPeersManager.getInputPeerById(peerId), media: inputMedia, random_id: randomIdS, - reply_to_msg_id: replyToMsgId ? appMessagesIdsManager.getMessageLocalId(replyToMsgId) : undefined, + reply_to_msg_id: replyToMsgId || undefined, message: '', clear_draft: options.clearDraft }, sentRequestOptions); @@ -1241,7 +1238,7 @@ export class AppMessagesManager { this.pendingByRandomId[randomIdS] = [peerId, messageId]; } - public cancelPendingMessage(randomId: string) { + public cancelPendingMessage(peerId: number, randomId: string) { const pendingData = this.pendingByRandomId[randomId]; this.log('cancelPendingMessage', randomId, pendingData); @@ -1264,7 +1261,8 @@ export class AppMessagesManager { historyStorage.pending.splice(pos, 1); } - delete this.messagesStorage[tempId]; + const storage = this.getMessagesStorage(peerId); + delete storage[tempId]; return true; } @@ -1390,7 +1388,7 @@ export class AppMessagesManager { flags, folder_id: folderId, offset_date: offsetDate, - offset_id: appMessagesIdsManager.getMessageLocalId(offsetId), + offset_id: offsetId, offset_peer: appPeersManager.getInputPeerById(offsetPeerId), limit, hash: 0 @@ -1474,47 +1472,39 @@ export class AppMessagesManager { }); } - public forwardMessages(peerId: number, mids: number[], options: Partial<{ + public forwardMessages(peerId: number, fromPeerId: number, msgIds: number[], options: Partial<{ withMyScore: true }> = {}) { peerId = appPeersManager.getPeerMigratedTo(peerId) || peerId; - mids = mids.slice().sort((a, b) => a - b); + msgIds = msgIds.slice().sort((a, b) => a - b); - const splitted = appMessagesIdsManager.splitMessageIdsByChannels(mids); - const promises: Promise[] = []; + const randomIds: string[] = msgIds.map(() => randomLong()); - for(const channelId in splitted.msgIds) { - const msgIds = splitted.msgIds[channelId]; - const randomIds: string[] = msgIds.map(() => randomLong()); + const sentRequestOptions: InvokeApiOptions = {}; + if(this.pendingAfterMsgs[peerId]) { + sentRequestOptions.afterMessageId = this.pendingAfterMsgs[peerId].messageId; + } - const sentRequestOptions: InvokeApiOptions = {}; - if(this.pendingAfterMsgs[peerId]) { - sentRequestOptions.afterMessageId = this.pendingAfterMsgs[peerId].messageId; + const promise = apiManager.invokeApiAfter('messages.forwardMessages', { + from_peer: appPeersManager.getInputPeerById(fromPeerId), + id: msgIds, + random_id: randomIds, + to_peer: appPeersManager.getInputPeerById(peerId), + with_my_score: options.withMyScore + }, sentRequestOptions).then((updates) => { + apiUpdatesManager.processUpdateMessage(updates); + }, () => {}).then(() => { + if(this.pendingAfterMsgs[peerId] === sentRequestOptions) { + delete this.pendingAfterMsgs[peerId]; } + }); - const promise = apiManager.invokeApiAfter('messages.forwardMessages', { - from_peer: appPeersManager.getInputPeerById(-channelId), - id: msgIds, - random_id: randomIds, - to_peer: appPeersManager.getInputPeerById(peerId), - with_my_score: options.withMyScore - }, sentRequestOptions).then((updates) => { - apiUpdatesManager.processUpdateMessage(updates); - }, () => {}).then(() => { - if(this.pendingAfterMsgs[peerId] === sentRequestOptions) { - delete this.pendingAfterMsgs[peerId]; - } - }); - - this.pendingAfterMsgs[peerId] = sentRequestOptions; - promises.push(promise); - } - - return Promise.all(promises); + this.pendingAfterMsgs[peerId] = sentRequestOptions; + return promise; } - public getMessage(messageId: number)/* : Message */ { - return this.messagesStorage[messageId] || { + public getMessageFromStorage(storage: MessagesStorage, messageId: number) { + return storage && storage[messageId] || { _: 'messageEmpty', id: messageId, deleted: true, @@ -1522,6 +1512,31 @@ export class AppMessagesManager { }; } + public getMessagesStorage(peerId: number) { + return this.messagesStorageByPeerId[peerId] ?? (this.messagesStorageByPeerId[peerId] = {}); + } + + public getMessageById(messageId: number) { + for(const peerId in this.messagesStorageByPeerId) { + if(appPeersManager.isChannel(-+peerId)) { + continue; + } + + const storage = this.messagesStorageByPeerId[peerId]; + return this.getMessageFromStorage(storage, messageId); + } + + return this.getMessageFromStorage(null, messageId); + } + + public getMessageByPeer(peerId: number, messageId: number) { + if(!peerId) { + return this.getMessageById(messageId); + } + + return this.getMessageFromStorage(this.getMessagesStorage(peerId), messageId); + } + public getMessagePeer(message: any): number { const toId = message.peer_id && appPeersManager.getPeerId(message.peer_id) || 0; @@ -1586,7 +1601,7 @@ export class AppMessagesManager { let historyResult = promise instanceof Promise ? await promise : promise; let channelId = -peerId; - let maxId = appMessagesIdsManager.getMessageLocalId(historyResult.history[0] || 0); + let maxId = historyResult.history[0] || 0; return apiManager.invokeApi('channels.deleteHistory', { channel: appChatsManager.getChannelInput(channelId), max_id: maxId @@ -1606,12 +1621,7 @@ export class AppMessagesManager { return this.doFlushHistory(appPeersManager.getInputPeerById(peerId), justClear).then(() => { delete this.historiesStorage[peerId]; - for(let mid in this.messagesStorage) { - let message = this.messagesStorage[mid]; - if(message.peerId == peerId) { - delete this.messagesStorage[mid]; - } - } + delete this.messagesStorageByPeerId[peerId]; if(justClear) { rootScope.broadcast('dialog_flush', {peerId}); @@ -1675,7 +1685,7 @@ export class AppMessagesManager { }); if(!affectedHistory.offset) { - const storage = this.messagesStorageByPeerId[peerId]; + const storage = this.getMessagesStorage(peerId); for(const mid in storage) { const message = storage[mid]; if(message.pFlags.pinned) { @@ -1694,7 +1704,7 @@ export class AppMessagesManager { } public getAlbumText(grouped_id: string) { - const group = appMessagesManager.groupedMessagesStorage[grouped_id]; + const group = this.groupedMessagesStorage[grouped_id]; let foundMessages = 0, message: string, totalEntities: MessageEntity[], entities: MessageEntity[]; for(const i in group) { const m = group[i]; @@ -1720,14 +1730,17 @@ export class AppMessagesManager { //return Object.keys(this.groupedMessagesStorage[grouped_id]).map(id => +id).sort((a, b) => a - b); } - public getMidsByMid(mid: number) { - const message = this.messagesStorage[mid]; + public getMidsByMid(peerId: number, mid: number) { + const message = this.getMessageByPeer(peerId, mid); if(message?.grouped_id) return this.getMidsByAlbum(message.grouped_id); else return [mid]; } - public saveMessages(messages: any[]) { - let albums: Set; + public saveMessages(messages: any[], options: Partial<{ + storage: MessagesStorage, + isScheduled: true + }> = {}) { + let groups: Map; messages.forEach((message) => { if(message.pFlags === undefined) { message.pFlags = {}; @@ -1745,7 +1758,11 @@ export class AppMessagesManager { const channelId = isChannel ? -peerId : 0; const isBroadcast = isChannel && appChatsManager.isBroadcast(channelId); - const mid = appMessagesIdsManager.getFullMessageId(message.id, channelId); + const mid = message.id; + if(options.isScheduled) { + message.pFlags.is_scheduled = true; + } + message.mid = mid; if(message.grouped_id) { @@ -1764,7 +1781,7 @@ export class AppMessagesManager { // this.log(dT(), 'msg unread', mid, apiMessage.pFlags.out, dialog && dialog[apiMessage.pFlags.out ? 'read_outbox_max_id' : 'read_inbox_max_id']) if(message.reply_to && message.reply_to.reply_to_msg_id) { - message.reply_to_mid = appMessagesIdsManager.getFullMessageId(message.reply_to.reply_to_msg_id, channelId); + message.reply_to_mid = message.reply_to.reply_to_msg_id; } message.date -= serverTimeManager.serverTimeOffset; @@ -1783,8 +1800,7 @@ export class AppMessagesManager { //if(peerId == myID) { if(fwdHeader.saved_from_peer && fwdHeader.saved_from_msg_id) { const savedFromPeerId = appPeersManager.getPeerId(fwdHeader.saved_from_peer); - const savedFromMid = appMessagesIdsManager.getFullMessageId(fwdHeader.saved_from_msg_id, - appPeersManager.isChannel(savedFromPeerId) ? -savedFromPeerId : 0); + const savedFromMid = fwdHeader.saved_from_msg_id; message.savedFrom = savedFromPeerId + '_' + savedFromMid; } @@ -1806,6 +1822,7 @@ export class AppMessagesManager { const mediaContext: ReferenceContext = { type: 'message', + peerId, messageId: mid }; @@ -1819,7 +1836,6 @@ export class AppMessagesManager { message.media = {_: 'messageMediaUnsupportedWeb'}; } else { message.media.photo = appPhotosManager.savePhoto(message.media.photo, mediaContext); - //appPhotosManager.savePhoto(apiMessage.media.photo, mediaContext); } break; case 'messageMediaPoll': @@ -1832,13 +1848,9 @@ export class AppMessagesManager { message.media.document = appDocsManager.saveDoc(message.media.document, mediaContext); // 11.04.2020 warning } - break; case 'messageMediaWebPage': - /* if(apiMessage.media.webpage.document) { - appDocsManager.saveDoc(apiMessage.media.webpage.document, mediaContext); - } */ - appWebPagesManager.saveWebPage(message.media.webpage, message.mid, mediaContext); + message.media.webpage = appWebPagesManager.saveWebPage(message.media.webpage, message.mid, mediaContext); break; /*case 'messageMediaGame': AppGamesManager.saveGame(apiMessage.media.game, apiMessage.mid, mediaContext); @@ -1860,7 +1872,6 @@ export class AppMessagesManager { //case 'messageActionChannelEditPhoto': case 'messageActionChatEditPhoto': message.action.photo = appPhotosManager.savePhoto(message.action.photo, mediaContext); - //appPhotosManager.savePhoto(apiMessage.action.photo, mediaContext); if(isBroadcast) { // ! messageActionChannelEditPhoto не существует в принципе, это используется для перевода. message.action._ = 'messageActionChannelEditPhoto'; } @@ -1938,40 +1949,33 @@ export class AppMessagesManager { } if(message.grouped_id) { - if(!albums) { - albums = new Set(); + if(!groups) { + groups = new Map(); } - albums.add(message.grouped_id); + groups.set(peerId, message.grouped_id); } else { message.rReply = this.getRichReplyText(message); } if(message.message && message.message.length && !message.totalEntities) { - //message.totalEntities = (message.entities || []).slice(); const myEntities = RichTextProcessor.parseEntities(message.message); const apiEntities = message.entities || []; - //message.totalEntities = RichTextProcessor.mergeEntitiesNew(myEntities, apiEntities, !message.pending); message.totalEntities = RichTextProcessor.mergeEntities(apiEntities, myEntities, !message.pending); // ! only in this order, otherwise bold and emoji formatting won't work - /* message.totalEntities = RichTextProcessor.mergeEntities(apiEntities, apiEntities, !message.pending); - message.totalEntities = RichTextProcessor.mergeEntities(myEntities, message.totalEntities, !message.pending); */ - //message.totalEntities = RichTextProcessor.mergeEntities(myEntities, apiEntities, !message.pending); } - //if(!options.isEdited) { - this.messagesStorage[mid] = message; - (this.messagesStorageByPeerId[peerId] ?? (this.messagesStorageByPeerId[peerId] = {}))[mid] = message; - //} + const storage = options.storage || this.getMessagesStorage(peerId); + storage[mid] = message; }); - if(albums) { - albums.forEach(groupId => { + if(groups) { + for(const [peerId, groupId] of groups) { const mids = this.groupedMessagesStorage[groupId]; for(const mid in mids) { - const message = this.messagesStorage[mid]; + const message = (options.storage || this.getMessagesStorage(peerId))[mid]; message.rReply = this.getRichReplyText(message); } - }); + } } } @@ -2239,12 +2243,13 @@ export class AppMessagesManager { return true; } - public canEditMessage(messageId: number, kind: 'text' | 'poll' = 'text') { - if(!this.messagesStorage[messageId]) { + public canEditMessage(peerId: number, messageId: number, kind: 'text' | 'poll' = 'text') { + const storage = this.getMessagesStorage(peerId); + if(!storage[messageId]) { return false; } - const message = this.messagesStorage[messageId]; + const message = storage[messageId]; if(!message || !this.canMessageBeEdited(message, kind)) { return false; } @@ -2261,8 +2266,8 @@ export class AppMessagesManager { return true; } - public canDeleteMessage(messageId: number) { - const message = this.messagesStorage[messageId]; + public canDeleteMessage(peerId: number, messageId: number) { + const message = this.getMessagesStorage(peerId)[messageId]; return message && ( message.peerId > 0 || message.fromId == rootScope.myId @@ -2294,7 +2299,7 @@ export class AppMessagesManager { const topPendingMessage = this.pendingTopMsgs[peerId]; if(topPendingMessage) { if(!topMessage - || (this.getMessage(topPendingMessage) as MyMessage).date > (this.getMessage(topMessage) as MyMessage).date) { + || (this.getMessageByPeer(peerId, topPendingMessage) as MyMessage).date > (this.getMessageByPeer(peerId, topMessage) as MyMessage).date) { dialog.top_message = topMessage = topPendingMessage; } } @@ -2361,8 +2366,8 @@ export class AppMessagesManager { let mid: number, message; if(dialog.top_message) { - mid = appMessagesIdsManager.getFullMessageId(dialog.top_message, channelId); - message = this.getMessage(mid); + mid = dialog.top_message; + message = this.getMessageByPeer(peerId, mid); } else { mid = this.tempId--; message = { @@ -2394,8 +2399,6 @@ export class AppMessagesManager { } dialog.top_message = mid; - dialog.read_inbox_max_id = appMessagesIdsManager.getFullMessageId(dialog.read_inbox_max_id, channelId); - dialog.read_outbox_max_id = appMessagesIdsManager.getFullMessageId(dialog.read_outbox_max_id, channelId); if(!dialog.hasOwnProperty('folder_id')) { if(dialog._ == 'dialog') { @@ -2636,8 +2639,9 @@ export class AppMessagesManager { } if(filtering) { + const storage = this.getMessagesStorage(peerId); for(let i = 0, length = history.length; i < length; i++) { - const message = this.messagesStorage[history[i]]; + const message = storage[history[i]]; //|| (neededContents['mentioned'] && message.totalEntities.find((e: any) => e._ == 'messageEntityMention')); @@ -2703,7 +2707,7 @@ export class AppMessagesManager { min_date: 0, max_date: 0, limit, - offset_id: appMessagesIdsManager.getMessageLocalId(maxId) || 0, + offset_id: maxId || 0, add_offset: backLimit ? -backLimit : 0, max_id: 0, min_id: 0, @@ -2716,7 +2720,7 @@ export class AppMessagesManager { var offsetDate = 0; var offsetPeerId = 0; var offsetId = 0; - var offsetMessage = maxId && this.getMessage(maxId); + var offsetMessage = maxId && this.getMessageByPeer(peerId, maxId); if(offsetMessage && offsetMessage.date) { offsetDate = offsetMessage.date + serverTimeManager.serverTimeOffset; @@ -2731,7 +2735,7 @@ export class AppMessagesManager { max_date: 0, offset_rate: offsetRate, offset_peer: appPeersManager.getInputPeerById(offsetPeerId), - offset_id: appMessagesIdsManager.getMessageLocalId(offsetId), + offset_id: offsetId, limit }, { //timeout: APITIMEOUT, @@ -2819,70 +2823,63 @@ export class AppMessagesManager { } } - public deleteMessages(messageIds: number[], revoke: boolean) { - const splitted = appMessagesIdsManager.splitMessageIdsByChannels(messageIds); - const promises: Promise[] = []; - for(const channelIdStr in splitted.msgIds) { - const channelId = +channelIdStr; - let msgIds = splitted.msgIds[channelId]; - - let promise: Promise; - if(channelId > 0) { - const channel = appChatsManager.getChat(channelId); - if(!channel.pFlags.creator && !(channel.pFlags.editor && channel.pFlags.megagroup)) { - const goodMsgIds: number[] = []; - if (channel.pFlags.editor || channel.pFlags.megagroup) { - msgIds.forEach((msgId, i) => { - const message = this.getMessage(splitted.mids[channelId][i]); - if(message.pFlags.out) { - goodMsgIds.push(msgId); - } - }); - } - - if(!goodMsgIds.length) { - return; - } + public deleteMessages(peerId: number, msgIds: number[], revoke: boolean) { + let promise: Promise; + + if(peerId < 0 && appPeersManager.isChannel(-peerId)) { + const channelId = -peerId; + const channel = appChatsManager.getChat(channelId); + if(!channel.pFlags.creator && !(channel.pFlags.editor && channel.pFlags.megagroup)) { + const goodMsgIds: number[] = []; + if (channel.pFlags.editor || channel.pFlags.megagroup) { + msgIds.forEach((msgId, i) => { + const message = this.getMessageByPeer(peerId, msgIds[i]); + if(message.pFlags.out) { + goodMsgIds.push(msgId); + } + }); + } - msgIds = goodMsgIds; + if(!goodMsgIds.length) { + return; } - promise = apiManager.invokeApi('channels.deleteMessages', { - channel: appChatsManager.getChannelInput(channelId), - id: msgIds - }).then((affectedMessages) => { - apiUpdatesManager.processUpdateMessage({ - _: 'updateShort', - update: { - _: 'updateDeleteChannelMessages', - channel_id: channelId, - messages: msgIds, - pts: affectedMessages.pts, - pts_count: affectedMessages.pts_count - } - }); - }); - } else { - promise = apiManager.invokeApi('messages.deleteMessages', { - revoke: revoke || undefined, - id: msgIds - }).then((affectedMessages) => { - apiUpdatesManager.processUpdateMessage({ - _: 'updateShort', - update: { - _: 'updateDeleteMessages', - messages: msgIds, - pts: affectedMessages.pts, - pts_count: affectedMessages.pts_count - } - }); - }); + msgIds = goodMsgIds; } - promises.push(promise); + promise = apiManager.invokeApi('channels.deleteMessages', { + channel: appChatsManager.getChannelInput(channelId), + id: msgIds + }).then((affectedMessages) => { + apiUpdatesManager.processUpdateMessage({ + _: 'updateShort', + update: { + _: 'updateDeleteChannelMessages', + channel_id: channelId, + messages: msgIds, + pts: affectedMessages.pts, + pts_count: affectedMessages.pts_count + } + }); + }); + } else { + promise = apiManager.invokeApi('messages.deleteMessages', { + revoke: revoke || undefined, + id: msgIds + }).then((affectedMessages) => { + apiUpdatesManager.processUpdateMessage({ + _: 'updateShort', + update: { + _: 'updateDeleteMessages', + messages: msgIds, + pts: affectedMessages.pts, + pts_count: affectedMessages.pts_count + } + }); + }); } - return Promise.all(promises); + return promise; } public readHistory(peerId: number, maxId = 0) { @@ -2897,7 +2894,7 @@ export class AppMessagesManager { } let foundUnread = !!historyStorage.history.find(messageId => { - const message = this.messagesStorage[messageId]; + const message = this.getMessagesStorage(peerId)[messageId]; return message && !message.pFlags.out && message.pFlags.unread; }); @@ -2906,10 +2903,6 @@ export class AppMessagesManager { } } - if(isChannel) { - maxId = appMessagesIdsManager.getMessageLocalId(maxId); - } - if(!historyStorage.readMaxId || maxId > historyStorage.readMaxId) { historyStorage.readMaxId = maxId; } @@ -2975,42 +2968,37 @@ export class AppMessagesManager { return historyStorage.readPromise = apiPromise; } - public readMessages(messageIds: number[]) { - const splitted = appMessagesIdsManager.splitMessageIdsByChannels(messageIds); - Object.keys(splitted.msgIds).forEach((channelId: number | string) => { - channelId = +channelId; - const msgIds = splitted.msgIds[channelId]; - - if(channelId > 0) { - apiManager.invokeApi('channels.readMessageContents', { - channel: appChatsManager.getChannelInput(channelId), - id: msgIds - }).then(() => { - apiUpdatesManager.processUpdateMessage({ - _: 'updateShort', - update: { - _: 'updateChannelReadMessagesContents', - channel_id: channelId, - messages: msgIds - } - }); + public readMessages(peerId: number, msgIds: number[]) { + if(peerId < 0 && appPeersManager.isChannel(peerId)) { + const channelId = -peerId; + apiManager.invokeApi('channels.readMessageContents', { + channel: appChatsManager.getChannelInput(channelId), + id: msgIds + }).then(() => { + apiUpdatesManager.processUpdateMessage({ + _: 'updateShort', + update: { + _: 'updateChannelReadMessagesContents', + channel_id: channelId, + messages: msgIds + } }); - } else { - apiManager.invokeApi('messages.readMessageContents', { - id: msgIds - }).then((affectedMessages) => { - apiUpdatesManager.processUpdateMessage({ - _: 'updateShort', - update: { - _: 'updateReadMessagesContents', - messages: msgIds, - pts: affectedMessages.pts, - pts_count: affectedMessages.pts_count - } - }); + }); + } else { + apiManager.invokeApi('messages.readMessageContents', { + id: msgIds + }).then((affectedMessages) => { + apiUpdatesManager.processUpdateMessage({ + _: 'updateShort', + update: { + _: 'updateReadMessagesContents', + messages: msgIds, + pts: affectedMessages.pts, + pts_count: affectedMessages.pts_count + } }); - } - }); + }); + } } public handleUpdate(update: Update) { @@ -3023,18 +3011,18 @@ export class AppMessagesManager { if(pendingData) { const peerId: number = pendingData[0]; const tempId = pendingData[1]; - const channelId = appPeersManager.isChannel(peerId) ? -peerId : 0; - const mid = appMessagesIdsManager.getFullMessageId(update.id, channelId); - const message = this.messagesStorage[mid]; + const mid = update.id; + const storage = this.getMessagesStorage(peerId); + const message = storage[mid]; if(message) { const historyStorage = this.historiesStorage[peerId]; const pos = historyStorage.pending.indexOf(tempId); if(pos != -1) { historyStorage.pending.splice(pos, 1); } - delete this.messagesStorage[tempId]; + delete storage[tempId]; - this.finalizePendingMessageCallbacks(tempId, mid); + this.finalizePendingMessageCallbacks(peerId, tempId, mid); } else { this.pendingByMessageId[mid] = randomId; } @@ -3106,7 +3094,7 @@ export class AppMessagesManager { let pendingMessage: any; if(randomId) { - if(pendingMessage = this.finalizePendingMessage(randomId, message)) { + if(pendingMessage = this.finalizePendingMessage(peerId, randomId, message)) { rootScope.broadcast('history_update', {peerId, mid: message.mid}); } @@ -3307,13 +3295,13 @@ export class AppMessagesManager { case 'updateEditChannelMessage': { const message = update.message as MyMessage; const peerId = this.getMessagePeer(message); - const channelId = message.peer_id._ == 'peerChannel' ? -peerId : 0; - const mid = appMessagesIdsManager.getFullMessageId(message.id, channelId); - if(this.messagesStorage[mid] === undefined) { + const mid = message.id; + const storage = this.getMessagesStorage(peerId); + if(storage[mid] === undefined) { break; } - const oldMessage = this.messagesStorage[mid]; + const oldMessage = storage[mid]; if(oldMessage.media?.webpage) { appWebPagesManager.deleteWebPageFromPending(oldMessage.media.webpage, mid); } @@ -3364,11 +3352,11 @@ export class AppMessagesManager { case 'updateReadChannelInbox': case 'updateReadChannelOutbox': { const channelId: number = (update as Update.updateReadChannelInbox).channel_id; - const maxId = appMessagesIdsManager.getFullMessageId(update.max_id, channelId); + const maxId = update.max_id; const peerId = channelId ? -channelId : appPeersManager.getPeerId((update as Update.updateReadHistoryInbox).peer); const isOut = update._ == 'updateReadHistoryOutbox' || update._ == 'updateReadChannelOutbox' ? true : undefined; const foundDialog = this.getDialogByPeerId(peerId)[0]; - const history = getObjectKeysAndSort(this.messagesStorageByPeerId[peerId] || {}, 'desc'); + const history = getObjectKeysAndSort(this.getMessagesStorage(peerId), 'desc'); let newUnreadCount = 0; let foundAffected = false; @@ -3378,13 +3366,14 @@ export class AppMessagesManager { appUsersManager.forceUserOnline(peerId); } + const storage = this.getMessagesStorage(peerId); for(let i = 0, length = history.length; i < length; i++) { const messageId = history[i]; if(messageId > maxId) { continue; } - const message = this.messagesStorage[messageId]; + const message = storage[messageId]; if(!message) { continue; } @@ -3433,25 +3422,19 @@ export class AppMessagesManager { break; } - case 'updateChannelReadMessagesContents': { - const channelId: number = update.channel_id; - const newMessages: number[] = []; - update.messages.forEach((msgId: number) => { - newMessages.push(appMessagesIdsManager.getFullMessageId(msgId, channelId)); - }); - update.messages = newMessages; - } - + case 'updateChannelReadMessagesContents': case 'updateReadMessagesContents': { + const channelId = (update as Update.updateChannelReadMessagesContents).channel_id; + const peerId = channelId ? -channelId : this.getMessageById(update.messages[0]).peerId; const messages: number[] = update.messages; for(const messageId of messages) { - const message = this.messagesStorage[messageId]; + const message = this.getMessageByPeer(peerId, messageId); if(message) { delete message.pFlags.media_unread; } } - rootScope.broadcast('messages_media_read', messages); + rootScope.broadcast('messages_media_read', {peerId, mids: messages}); break; } @@ -3462,8 +3445,7 @@ export class AppMessagesManager { const history = (this.historiesStorage[peerId] || {}).history || []; if(history.length) { history.forEach((msgId: number) => { - if(!update.available_min_id || - appMessagesIdsManager.getMessageLocalId(msgId) <= update.available_min_id) { + if(!update.available_min_id || msgId <= update.available_min_id) { messages.push(msgId); } }); @@ -3486,8 +3468,8 @@ export class AppMessagesManager { const messages = (update as any as Update.updateDeleteChannelMessages).messages; for(const _messageId of messages) { - const mid = appMessagesIdsManager.getFullMessageId(_messageId, channelId); - const message: MyMessage = this.messagesStorage[mid]; + const mid = _messageId; + const message: MyMessage = this.getMessageByPeer(-channelId, mid); if(message) { const peerId = this.getMessagePeer(message); const history = historiesUpdated[peerId] || (historiesUpdated[peerId] = {count: 0, unread: 0, msgs: {}}); @@ -3498,7 +3480,7 @@ export class AppMessagesManager { const smth = c.photo || c.document; if(smth?.file_reference) { - referenceDatabase.deleteContext(smth.file_reference, {type: 'message', messageId: mid}); + referenceDatabase.deleteContext(smth.file_reference, {type: 'message', peerId, messageId: mid}); } // @ts-ignore @@ -3516,8 +3498,7 @@ export class AppMessagesManager { message.deleted = true; - delete this.messagesStorage[mid]; - delete this.messagesStorageByPeerId[peerId][mid]; + delete this.getMessagesStorage(peerId)[mid]; if(message._ != 'messageService' && message.grouped_id) { const groupedStorage = this.groupedMessagesStorage[message.grouped_id]; @@ -3650,8 +3631,8 @@ export class AppMessagesManager { case 'updateChannelMessageViews': { const views = update.views; - const mid = appMessagesIdsManager.getFullMessageId(update.id, update.channel_id); - const message = this.getMessage(mid); + const mid = update.id; + const message = this.getMessageByPeer(-update.channel_id, mid); if(message && message.views && message.views < views) { message.views = views; rootScope.broadcast('message_views', {mid, views}); @@ -3713,16 +3694,17 @@ export class AppMessagesManager { break; } */ - const messages = channelId ? update.messages.map(messageId => appMessagesIdsManager.getFullMessageId(messageId, channelId)) : update.messages; + const messages = update.messages; - const missingMessages = messages.filter(mid => !this.messagesStorage[mid]); - const getMissingPromise = missingMessages.length ? Promise.all(missingMessages.map(mid => this.wrapSingleMessage(mid))) : Promise.resolve(); + const storage = this.getMessagesStorage(peerId); + const missingMessages = messages.filter(mid => !storage[mid]); + const getMissingPromise = missingMessages.length ? Promise.all(missingMessages.map(mid => this.wrapSingleMessage(peerId, mid))) : Promise.resolve(); getMissingPromise.finally(() => { const werePinned = update.pFlags?.pinned; if(werePinned) { for(const mid of messages) { //storage.history.push(mid); - const message = this.messagesStorage[mid]; + const message = storage[mid]; message.pFlags.pinned = true; } @@ -3735,7 +3717,7 @@ export class AppMessagesManager { } else { for(const mid of messages) { //storage.history.findAndSplice(_mid => _mid == mid); - const message = this.messagesStorage[mid]; + const message = storage[mid]; delete message.pFlags.pinned; } } @@ -3764,6 +3746,37 @@ export class AppMessagesManager { /////this.log('updateNotifySettings', peerId, notify_settings); break; } + + // * https://core.telegram.org/api/scheduled-messages + case 'updateNewScheduledMessage': { + const message = update.message as Message.message; + + const peerId = this.getMessagePeer(message); + const storage = this.scheduledMessagesStorage[peerId]; + if(storage) { + this.saveMessages([message]); + storage.unshift(message.mid); + + rootScope.broadcast('scheduled_new', {peerId, mid: message.mid}); + } + + break; + } + + case 'updateDeleteScheduledMessages': { + const peerId = appPeersManager.getPeerId(update.peer); + + const storage = this.scheduledMessagesStorage[peerId]; + if(storage) { + for(const mid of update.messages) { + delete storage[mid]; + } + + rootScope.broadcast('scheduled_delete', {peerId, mids: update.messages}); + } + + break; + } } } @@ -3818,22 +3831,6 @@ export class AppMessagesManager { }); } }); - - /* return apiManager.invokeApi('account.getNotifySettings', { - peer: inputNotifyPeer - }).then((settings: any) => { - settings.mute_until = 2000000000; // 2147483646 - - return apiManager.invokeApi('account.updateNotifySettings', { - peer: inputNotifyPeer, - settings: Object.assign(settings, { - _: 'inputPeerNotifySettings' - }) - }).then(res => { - this.log('mute result:', res); - }); - }); */ - } public canWriteToPeer(peerId: number) { @@ -3843,7 +3840,7 @@ export class AppMessagesManager { return (!isChannel || hasRights) && (peerId < 0 || appUsersManager.canSendToUser(peerId)); } - public finalizePendingMessage(randomId: number, finalMessage: any) { + public finalizePendingMessage(peerId: number, randomId: number, finalMessage: any) { const pendingData = this.pendingByRandomId[randomId]; // this.log('pdata', randomID, pendingData) @@ -3858,7 +3855,8 @@ export class AppMessagesManager { historyStorage.pending.splice(pos, 1); } - const message = this.messagesStorage[tempId]; + const storage = this.getMessagesStorage(peerId); + const message = storage[tempId]; if(message) { delete message.pending; delete message.error; @@ -3868,9 +3866,9 @@ export class AppMessagesManager { rootScope.broadcast('messages_pending'); } - delete this.messagesStorage[tempId]; + delete storage[tempId]; - this.finalizePendingMessageCallbacks(tempId, finalMessage.mid); + this.finalizePendingMessageCallbacks(peerId, tempId, finalMessage.mid); return message; } @@ -3878,7 +3876,7 @@ export class AppMessagesManager { return false; } - public finalizePendingMessageCallbacks(tempId: number, mid: number) { + public finalizePendingMessageCallbacks(peerId: number, tempId: number, mid: number) { const callbacks = this.tempFinalizeCallbacks[tempId]; this.log.warn(callbacks, tempId); if(callbacks !== undefined) { @@ -3892,7 +3890,7 @@ export class AppMessagesManager { } // set cached url to media - const message = appMessagesManager.getMessage(mid); + const message = this.getMessageByPeer(peerId, mid); if(message.media) { if(message.media.photo) { const photo = appPhotosManager.getPhoto('' + tempId); @@ -3933,6 +3931,35 @@ export class AppMessagesManager { }); } + public getScheduledMessagesStorage(peerId: number) { + return this.scheduledMessagesStorage[peerId] ?? (this.scheduledMessagesStorage[peerId] = {}); + } + + public getScheduledMessages(peerId: number): Promise { + if(!this.canWriteToPeer(peerId)) return Promise.resolve([]); + + const storage = this.scheduledMessagesStorage[peerId]; + if(storage?.length) { + return Promise.resolve(Object.keys(storage).map(id => +id)); + } + + return apiManager.invokeApi('messages.getScheduledHistory', { + peer: appPeersManager.getInputPeerById(peerId), + hash: 0 + }).then(historyResult => { + if(historyResult._ !== 'messages.messagesNotModified') { + appUsersManager.saveApiUsers(historyResult.users); + appChatsManager.saveApiChats(historyResult.chats); + + const storage = this.getScheduledMessagesStorage(peerId); + this.saveMessages(historyResult.messages, {storage, isScheduled: true}); + return Object.keys(storage).map(id => +id); + } + + return []; + }); + } + public getHistory(peerId: number, maxId = 0, limit: number, backLimit?: number) { if(this.migratedFromTo[peerId]) { peerId = this.migratedFromTo[peerId]; @@ -3949,9 +3976,9 @@ export class AppMessagesManager { let reqPeerId = peerId; if(this.migratedToFrom[peerId]) { isMigrated = true; - if(maxId && maxId < appMessagesIdsManager.fullMsgIdModulus) { + /* if(maxId && maxId < appMessagesIdsManager.fullMsgIdModulus) { reqPeerId = this.migratedToFrom[peerId]; - } + } */ } if(maxId > 0) { @@ -3981,7 +4008,7 @@ export class AppMessagesManager { history = historyStorage.pending.slice().concat(history); } - return this.wrapHistoryResult({ + return this.wrapHistoryResult(peerId, { count: historyStorage.count, history: history, unreadOffset: unreadOffset, @@ -4013,7 +4040,7 @@ export class AppMessagesManager { history = historyStorage.pending.slice().concat(history); } - return this.wrapHistoryResult({ + return this.wrapHistoryResult(peerId, { count: historyStorage.count, history: history, unreadOffset: unreadOffset, @@ -4037,7 +4064,7 @@ export class AppMessagesManager { history = historyStorage.pending.slice().concat(history); } - return this.wrapHistoryResult({ + return this.wrapHistoryResult(peerId, { count: historyStorage.count, history: history, unreadOffset: unreadOffset, @@ -4110,10 +4137,10 @@ export class AppMessagesManager { }); } - public wrapHistoryResult(result: HistoryResult) { + public wrapHistoryResult(peerId: number, result: HistoryResult) { if(result.unreadOffset) { for(let i = result.history.length - 1; i >= 0; i--) { - const message = this.messagesStorage[result.history[i]]; + const message = this.getMessagesStorage(peerId)[result.history[i]]; if(message && !message.pFlags.out && message.pFlags.unread) { result.unreadOffset = i + 1; break; @@ -4132,7 +4159,7 @@ export class AppMessagesManager { return apiManager.invokeApi('messages.getHistory', { peer: appPeersManager.getInputPeerById(peerId), - offset_id: maxId ? appMessagesIdsManager.getMessageLocalId(maxId) : 0, + offset_id: maxId || 0, offset_date: offsetDate, add_offset: offset, limit: limit, @@ -4234,15 +4261,13 @@ export class AppMessagesManager { return this.fetchSingleMessagesPromise = new Promise((resolve) => { setTimeout(() => { - const mids = this.needSingleMessages.slice(); - this.needSingleMessages.length = 0; - - const splitted = appMessagesIdsManager.splitMessageIdsByChannels(mids); let promises: Promise[] = []; - Object.keys(splitted.msgIds).forEach((channelId: number | string) => { - channelId = +channelId; + + for(const peerId in this.needSingleMessages) { + const mids = this.needSingleMessages[peerId]; + delete this.needSingleMessages[peerId]; - const msgIds: InputMessage[] = splitted.msgIds[channelId].map((msgId: number) => { + const msgIds: InputMessage[] = mids.map((msgId: number) => { return { _: 'inputMessageID', id: msgId @@ -4250,9 +4275,9 @@ export class AppMessagesManager { }); let promise: Promise; - if(channelId > 0) { + if(+peerId < 0 && appPeersManager.isChannel(+peerId)) { promise = apiManager.invokeApi('channels.getMessages', { - channel: appChatsManager.getChannelInput(channelId), + channel: appChatsManager.getChannelInput(-+peerId), id: msgIds }); } else { @@ -4268,9 +4293,9 @@ export class AppMessagesManager { this.saveMessages(getMessagesResult.messages); } - rootScope.broadcast('messages_downloaded', splitted.mids[+channelId]); + rootScope.broadcast('messages_downloaded', mids); })); - }); + } Promise.all(promises).finally(() => { this.fetchSingleMessagesPromise = null; @@ -4281,12 +4306,12 @@ export class AppMessagesManager { }); } - public wrapSingleMessage(msgId: number, overwrite = false): Promise { - if(this.messagesStorage[msgId] && !overwrite) { + public wrapSingleMessage(peerId: number, msgId: number, overwrite = false): Promise { + if(this.getMessagesStorage(peerId)[msgId] && !overwrite) { rootScope.broadcast('messages_downloaded', [msgId]); return Promise.resolve(); - } else if(this.needSingleMessages.indexOf(msgId) == -1) { - this.needSingleMessages.push(msgId); + } else if(!this.needSingleMessages[peerId] || this.needSingleMessages[peerId].indexOf(msgId) == -1) { + (this.needSingleMessages[peerId] ?? (this.needSingleMessages[peerId] = [])).push(msgId); return this.fetchSingleMessages(); } else if(this.fetchSingleMessagesPromise) { return this.fetchSingleMessagesPromise; diff --git a/src/lib/appManagers/appPollsManager.ts b/src/lib/appManagers/appPollsManager.ts index 3cd25f74..556b5ba6 100644 --- a/src/lib/appManagers/appPollsManager.ts +++ b/src/lib/appManagers/appPollsManager.ts @@ -159,21 +159,20 @@ export class AppPollsManager { }; } - public sendVote(mid: number, optionIds: number[]): Promise { - const message = appMessagesManager.getMessage(mid); + public sendVote(peerId: number, messageId: number, optionIds: number[]): Promise { + const message = appMessagesManager.getMessageByPeer(peerId, messageId); const poll: Poll = message.media.poll; const options: Uint8Array[] = optionIds.map(index => { return poll.answers[index].option; }); - const inputPeer = appPeersManager.getInputPeerById(message.peerId); - const messageId = message.id; + const inputPeer = appPeersManager.getInputPeerById(peerId); - if(mid < 0) { - return appMessagesManager.invokeAfterMessageIsSent(mid, 'sendVote', (mid) => { + if(messageId < 0) { + return appMessagesManager.invokeAfterMessageIsSent(messageId, 'sendVote', (mid) => { this.log('invoke sendVote callback'); - return this.sendVote(mid, optionIds); + return this.sendVote(peerId, mid, optionIds); }); } @@ -187,10 +186,9 @@ export class AppPollsManager { }); } - public getResults(mid: number) { - const message = appMessagesManager.getMessage(mid); + public getResults(peerId: number, messageId: number) { + const message = appMessagesManager.getMessageByPeer(peerId, messageId); const inputPeer = appPeersManager.getInputPeerById(message.peerId); - const messageId = message.id; return apiManager.invokeApi('messages.getPollResults', { peer: inputPeer, @@ -201,11 +199,7 @@ export class AppPollsManager { }); } - public getVotes(mid: number, option?: Uint8Array, offset?: string, limit = 20) { - const message = appMessagesManager.getMessage(mid); - const inputPeer = appPeersManager.getInputPeerById(message.peerId); - const messageId = message.id; - + public getVotes(peerId: number, messageId: number, option?: Uint8Array, offset?: string, limit = 20) { let flags = 0; if(option) { flags |= 1 << 0; @@ -217,7 +211,7 @@ export class AppPollsManager { return apiManager.invokeApi('messages.getPollVotes', { flags, - peer: inputPeer, + peer: appPeersManager.getInputPeerById(peerId), id: messageId, option, offset, @@ -231,15 +225,15 @@ export class AppPollsManager { }); } - public stopPoll(mid: number) { - const message = appMessagesManager.getMessage(mid); + public stopPoll(peerId: number, messageId: number) { + const message = appMessagesManager.getMessageByPeer(peerId, messageId); const poll: Poll = message.media.poll; if(poll.pFlags.closed) return Promise.resolve(); const newPoll = copy(poll); newPoll.pFlags.closed = true; - return appMessagesManager.editMessage(mid, undefined, { + return appMessagesManager.editMessage(peerId, messageId, undefined, { newMedia: this.getInputMediaPoll(newPoll) }).then(() => { //console.log('stopped poll'); diff --git a/src/lib/appManagers/appStateManager.ts b/src/lib/appManagers/appStateManager.ts index 0c5eeab4..715b49c6 100644 --- a/src/lib/appManagers/appStateManager.ts +++ b/src/lib/appManagers/appStateManager.ts @@ -8,7 +8,6 @@ import { logger } from '../logger'; import type { AppUsersManager } from './appUsersManager'; import type { AppChatsManager } from './appChatsManager'; import type { AuthState } from '../../types'; -import type { AppMessagesIdsManager } from './appMessagesIdsManager'; import type FiltersStorage from '../storages/filters'; import type DialogsStorage from '../storages/dialogs'; @@ -33,11 +32,6 @@ type State = Partial<{ stickerSets: AppStickersManager['stickerSets'], version: typeof STATE_VERSION, authState: AuthState, - messagesIdsLocals: { - channelLocals: AppMessagesIdsManager['channelLocals'], - channelsByLocals: AppMessagesIdsManager['channelsByLocals'], - channelCurLocal: AppMessagesIdsManager['channelCurLocal'], - }, hiddenPinnedMessages: {[peerId: string]: number} }>; diff --git a/src/lib/mtproto/referenceDatabase.ts b/src/lib/mtproto/referenceDatabase.ts index 5e8acf7d..5f276745 100644 --- a/src/lib/mtproto/referenceDatabase.ts +++ b/src/lib/mtproto/referenceDatabase.ts @@ -13,6 +13,7 @@ export namespace ReferenceContext { export type referenceContextMessage = { type: 'message', + peerId: number, messageId: number }; } @@ -80,7 +81,7 @@ class ReferenceDatabase { [context, reference] = this.getContext(reference); switch(context?.type) { case 'message': { - return appMessagesManager.wrapSingleMessage(context.messageId, true); + return appMessagesManager.wrapSingleMessage(context.peerId, context.messageId, true); // .then(() => { // console.log('FILE_REFERENCE_EXPIRED: got message', context, appMessagesManager.getMessage((context as ReferenceContext.referenceContextMessage).messageId).media, reference); // }); diff --git a/src/lib/rootScope.ts b/src/lib/rootScope.ts index 60b86468..174e985c 100644 --- a/src/lib/rootScope.ts +++ b/src/lib/rootScope.ts @@ -44,14 +44,17 @@ type BroadcastEvents = { 'messages_pending': void, 'messages_read': void, 'messages_downloaded': number[], - 'messages_media_read': number[], + 'messages_media_read': {peerId: number, mids: number[]}, + + 'scheduled_new': {peerId: number, mid: number}, + 'scheduled_delete': {peerId: number, mids: number[]}, 'album_edit': {peerId: number, groupId: string, deletedMids: number[]}, 'stickers_installed': StickerSet.stickerSet, 'stickers_deleted': StickerSet.stickerSet, - 'audio_play': {doc: MyDocument, mid: number}, + 'audio_play': {doc: MyDocument, mid: number, peerId: number}, 'audio_pause': void, 'state_synchronized': number, diff --git a/src/lib/storages/dialogs.ts b/src/lib/storages/dialogs.ts index 976f53da..93822f00 100644 --- a/src/lib/storages/dialogs.ts +++ b/src/lib/storages/dialogs.ts @@ -1,7 +1,6 @@ import { tsNow } from "../../helpers/date"; import type { Message } from "../../layer"; import type { AppChatsManager } from "../appManagers/appChatsManager"; -import type { AppMessagesIdsManager } from "../appManagers/appMessagesIdsManager"; import type { AppMessagesManager, Dialog } from "../appManagers/appMessagesManager"; import type { AppPeersManager } from "../appManagers/appPeersManager"; import type { ServerTimeManager } from "../mtproto/serverTimeManager"; @@ -18,7 +17,7 @@ export default class DialogsStorage { }; public dialogsNum = 0; - constructor(private appMessagesManager: AppMessagesManager, private appMessagesIdsManager: AppMessagesIdsManager, private appChatsManager: AppChatsManager, private appPeersManager: AppPeersManager, private serverTimeManager: ServerTimeManager) { + constructor(private appMessagesManager: AppMessagesManager, private appChatsManager: AppChatsManager, private appPeersManager: AppPeersManager, private serverTimeManager: ServerTimeManager) { } @@ -92,8 +91,8 @@ export default class DialogsStorage { public generateIndexForDialog(dialog: Dialog, justReturn = false) { const channelId = this.appPeersManager.isChannel(dialog.peerId) ? -dialog.peerId : 0; - const mid = this.appMessagesIdsManager.getFullMessageId(dialog.top_message, channelId); - const message = this.appMessagesManager.getMessage(mid); + const mid = dialog.top_message; + const message = this.appMessagesManager.getMessageByPeer(dialog.peerId, mid); let topDate = (message as Message.message).date || Date.now() / 1000; if(channelId) { diff --git a/src/scss/partials/_chatBubble.scss b/src/scss/partials/_chatBubble.scss index ada9cd37..d8e29b23 100644 --- a/src/scss/partials/_chatBubble.scss +++ b/src/scss/partials/_chatBubble.scss @@ -189,7 +189,7 @@ $bubble-margin: .25rem; } */ } - .chat:not(.type-pinned) & .bubble__container { + .chat.type-chat & .bubble__container { cursor: pointer; pointer-events: all; } diff --git a/src/scss/partials/_rightSidebar.scss b/src/scss/partials/_rightSidebar.scss index c85ed4ef..0dd6469d 100644 --- a/src/scss/partials/_rightSidebar.scss +++ b/src/scss/partials/_rightSidebar.scss @@ -199,6 +199,7 @@ text-overflow: ellipsis; overflow: hidden; word-break: break-word; + white-space: pre-wrap; } &-bio {