diff --git a/src/helpers/object.ts b/src/helpers/object.ts index 87ec0c19..4349c09e 100644 --- a/src/helpers/object.ts +++ b/src/helpers/object.ts @@ -48,6 +48,7 @@ export function defineNotNumerableProperties(obj: {[key: string]: any}, names: s } export function getObjectKeysAndSort(object: any, sort: 'asc' | 'desc' = 'asc') { + if(!object) return []; const ids = Object.keys(object).map(i => +i); if(sort == 'asc') return ids.sort((a, b) => a - b); else return ids.sort((a, b) => b - a); diff --git a/src/lib/appManagers/appDocsManager.ts b/src/lib/appManagers/appDocsManager.ts index 9dcf5896..3530f7df 100644 --- a/src/lib/appManagers/appDocsManager.ts +++ b/src/lib/appManagers/appDocsManager.ts @@ -1,7 +1,6 @@ import { FileURLType, getFileNameByLocation, getFileURL } from '../../helpers/fileName'; -import { safeReplaceArrayInObject, defineNotNumerableProperties } from '../../helpers/object'; +import { safeReplaceArrayInObject, defineNotNumerableProperties, isObject } from '../../helpers/object'; import { Document, InputFileLocation, PhotoSize } from '../../layer'; -import { isObject } from '../mtproto/bin_utils'; import { MOUNT_CLASS_TO } from '../mtproto/mtproto_config'; import referenceDatabase, { ReferenceContext } from '../mtproto/referenceDatabase'; import opusDecodeController from '../opusDecodeController'; diff --git a/src/lib/appManagers/appImManager.ts b/src/lib/appManagers/appImManager.ts index 30e61ca7..37ccb1d0 100644 --- a/src/lib/appManagers/appImManager.ts +++ b/src/lib/appManagers/appImManager.ts @@ -392,6 +392,18 @@ export class AppImManager { if(!mounted) return; this.renderMessage(mounted.message, true, false, mounted.bubble, false); }); + + $rootScope.$on('album_edit', (e) => { + const {peerID, groupID, deletedMids} = e.detail; + + if(peerID != this.peerID) return; + const mids = appMessagesManager.getMidsByAlbum(groupID); + const maxID = Math.max(...mids.concat(deletedMids)); + if(!this.bubbles[maxID]) return; + + const renderMaxID = getObjectKeysAndSort(appMessagesManager.groupedMessagesStorage[groupID], 'asc').pop(); + this.renderMessage(appMessagesManager.getMessage(renderMaxID), true, false, this.bubbles[maxID], false); + }); $rootScope.$on('peer_pinned_message', (e) => { const peerID = e.detail; @@ -1439,18 +1451,21 @@ export class AppImManager { } } - public deleteMessagesByIDs(msgIDs: number[]) { - msgIDs.forEach(id => { - if(!(id in this.bubbles)) return; + public deleteMessagesByIDs(mids: number[]) { + mids.forEach(mid => { + if(!(mid in this.bubbles)) return; - let bubble = this.bubbles[id]; - delete this.bubbles[id]; + /* const mounted = this.getMountedBubble(mid); + if(!mounted) return; */ + + const bubble = this.bubbles[mid]; + delete this.bubbles[mid]; if(this.firstUnreadBubble == bubble) { this.firstUnreadBubble = null; } - this.bubbleGroups.removeBubble(bubble, id); + this.bubbleGroups.removeBubble(bubble, mid); this.unreadedObserver.unobserve(bubble); //this.unreaded.findAndSplice(mid => mid == id); this.scrollable.removeElement(bubble); @@ -1664,17 +1679,17 @@ export class AppImManager { //return; if(message.deleted) return; else if(message.grouped_id) { // will render only last album's message - let storage = appMessagesManager.groupedMessagesStorage[message.grouped_id]; - let maxID = Math.max(...Object.keys(storage).map(i => +i)); + const storage = appMessagesManager.groupedMessagesStorage[message.grouped_id]; + const maxID = Math.max(...Object.keys(storage).map(i => +i)); if(message.mid < maxID) { return; } } - let peerID = this.peerID; - let our = message.fromID == this.myID; + const peerID = this.peerID; + const our = message.fromID == this.myID; - let messageDiv = document.createElement('div'); + const messageDiv = document.createElement('div'); messageDiv.classList.add('message'); //messageDiv.innerText = message.message; @@ -1689,7 +1704,6 @@ export class AppImManager { bubble = document.createElement('div'); bubble.classList.add('bubble'); bubble.appendChild(bubbleContainer); - this.bubbles[+message.mid] = bubble; } else { const save = ['is-highlighted']; const wasClassNames = bubble.className.split(' '); @@ -1703,9 +1717,14 @@ export class AppImManager { if(bubble == this.firstUnreadBubble) { bubble.classList.add('is-first-unread'); } + + const originalMid = +bubble.dataset.mid; + delete this.bubbles[originalMid]; //bubble.innerHTML = ''; } + // ! reset due to album edit or delete item + this.bubbles[+message.mid] = bubble; bubble.dataset.mid = message.mid; if(this.chatSelection.isSelecting) { @@ -1732,21 +1751,9 @@ export class AppImManager { let messageMessage: string, totalEntities: any[]; if(message.grouped_id) { - let group = appMessagesManager.groupedMessagesStorage[message.grouped_id]; - let foundMessages = 0; - for(let i in group) { - let m = group[i]; - if(m.message) { - if(++foundMessages > 1) break; - messageMessage = m.message; - totalEntities = m.totalEntities; - } - } - - if(foundMessages > 1) { - messageMessage = undefined; - totalEntities = undefined; - } + const t = appMessagesManager.getAlbumText(message.grouped_id); + messageMessage = t.message; + totalEntities = t.totalEntities; } else { messageMessage = message.message; totalEntities = message.totalEntities; @@ -1993,28 +2000,25 @@ export class AppImManager { } case 'messageMediaPhoto': { - let photo = messageMedia.photo; + const photo = messageMedia.photo; ////////this.log('messageMediaPhoto', photo); bubble.classList.add('hide-name', 'photo'); const tailSupported = !isAndroid; if(tailSupported) bubble.classList.add('with-media-tail'); - if(message.grouped_id) { + const storage = appMessagesManager.groupedMessagesStorage[message.grouped_id]; + if(message.grouped_id && Object.keys(storage).length != 1) { bubble.classList.add('is-album'); + wrapAlbum({ + groupID: message.grouped_id, + attachmentDiv, + middleware: this.getMiddleware(), + isOut: our, + lazyLoadQueue: this.lazyLoadQueue + }); - let storage = appMessagesManager.groupedMessagesStorage[message.grouped_id]; - if(Object.keys(storage).length != 1) { - wrapAlbum({ - groupID: message.grouped_id, - attachmentDiv, - middleware: this.getMiddleware(), - isOut: our, - lazyLoadQueue: this.lazyLoadQueue - }); - - break; - } + break; } wrapPhoto(photo, message, attachmentDiv, undefined, undefined, tailSupported, isOut, this.lazyLoadQueue, this.getMiddleware()); @@ -2167,7 +2171,8 @@ export class AppImManager { //this.log('never get free 2', doc); bubble.classList.add('hide-name', doc.type == 'round' ? 'round' : 'video'); - if(message.grouped_id) { + const storage = appMessagesManager.groupedMessagesStorage[message.grouped_id]; + if(message.grouped_id && Object.keys(storage).length != 1) { bubble.classList.add('is-album'); wrapAlbum({ diff --git a/src/lib/appManagers/appMessagesManager.ts b/src/lib/appManagers/appMessagesManager.ts index 9f078c37..2eaf5bf1 100644 --- a/src/lib/appManagers/appMessagesManager.ts +++ b/src/lib/appManagers/appMessagesManager.ts @@ -1,3 +1,4 @@ +import { MessageRender } from "../../components/chat/messageRender"; import ProgressivePreloader from "../../components/preloader"; import { listMergeSorted } from "../../helpers/array"; import { CancellablePromise, deferredPromise } from "../../helpers/cancellablePromise"; @@ -5,7 +6,7 @@ import { tsNow } from "../../helpers/date"; import { copy, defineNotNumerableProperties, deepEqual, safeReplaceObject, getObjectKeysAndSort } from "../../helpers/object"; import { randomLong } from "../../helpers/random"; import { splitStringByLength, limitSymbols } from "../../helpers/string"; -import { Dialog as MTDialog, DialogFilter, DialogPeer, DocumentAttribute, InputMessage, Message, MessageAction, MessagesDialogs, MessagesFilter, MessagesMessages, MessagesPeerDialogs, MethodDeclMap, PhotoSize, SendMessageAction, Update } from "../../layer"; +import { Dialog as MTDialog, DialogFilter, DialogPeer, DocumentAttribute, InputMessage, Message, MessageAction, MessageEntity, MessagesDialogs, MessagesFilter, MessagesMessages, MessagesPeerDialogs, MethodDeclMap, PhotoSize, SendMessageAction, Update } from "../../layer"; import { InvokeApiOptions, Modify } from "../../types"; import { langPack } from "../langPack"; import { logger, LogLevels } from "../logger"; @@ -533,7 +534,6 @@ export class AppMessagesManager { $rootScope.$broadcast('message_edit', { peerID: this.getMessagePeer(message), - id: message.id, mid: msgID, justMedia: true }); @@ -2206,8 +2206,29 @@ export class AppMessagesManager { }); } + public getAlbumText(grouped_id: string) { + const group = appMessagesManager.groupedMessagesStorage[grouped_id]; + let foundMessages = 0, message: string, totalEntities: MessageEntity[]; + for(const i in group) { + const m = group[i]; + if(m.message) { + if(++foundMessages > 1) break; + message = m.message; + totalEntities = m.totalEntities; + } + } + + if(foundMessages > 1) { + message = undefined; + totalEntities = undefined; + } + + return {message, totalEntities}; + } + public getMidsByAlbum(grouped_id: string) { - return Object.keys(this.groupedMessagesStorage[grouped_id]).map(id => +id).sort((a, b) => a - b); + return getObjectKeysAndSort(this.groupedMessagesStorage[grouped_id], 'asc'); + //return Object.keys(this.groupedMessagesStorage[grouped_id]).map(id => +id).sort((a, b) => a - b); } public getMidsByMid(mid: number) { @@ -2219,6 +2240,7 @@ export class AppMessagesManager { public saveMessages(messages: any[], options: { isEdited?: boolean } = {}) { + let albums: Set; messages.forEach((message) => { if(message.pFlags === undefined) { message.pFlags = {}; @@ -2428,7 +2450,15 @@ export class AppMessagesManager { } } - message.rReply = this.getRichReplyText(message); + if(message.grouped_id) { + if(!albums) { + albums = new Set(); + } + + albums.add(message.grouped_id); + } else { + message.rReply = this.getRichReplyText(message); + } if(message.message && message.message.length && !message.totalEntities) { const myEntities = RichTextProcessor.parseEntities(message.message); @@ -2441,6 +2471,16 @@ export class AppMessagesManager { (this.messagesStorageByPeerID[peerID] ?? (this.messagesStorageByPeerID[peerID] = {}))[mid] = message; } }); + + if(albums) { + albums.forEach(groupID => { + const mids = this.groupedMessagesStorage[groupID]; + for(const mid in mids) { + const message = this.messagesStorage[mid]; + message.rReply = this.getRichReplyText(message); + } + }); + } } public getRichReplyText(message: any, text: string = message.message) { @@ -2448,7 +2488,8 @@ export class AppMessagesManager { if(message.media) { if(message.grouped_id) { - messageText += 'Album' + (message.message ? ', ' : '') + ''; + text = this.getAlbumText(message.grouped_id).message; + messageText += 'Album' + (text ? ', ' : '') + ''; } else switch(message.media._) { case 'messageMediaPhoto': messageText += 'Photo' + (message.message ? ', ' : '') + ''; @@ -3742,13 +3783,27 @@ export class AppMessagesManager { } } else { $rootScope.$broadcast('message_edit', { - peerID: peerID, - id: message.id, - mid: mid, + peerID, + mid, justMedia: false }); - if(isTopMessage) { + const groupID = (message as Message.message).grouped_id; + if(this.pinnedMessages[peerID]) { + let pinnedMid: number; + if(groupID) { + const mids = this.getMidsByAlbum(groupID); + pinnedMid = mids.find(mid => this.pinnedMessages[peerID] == mid); + } else if(this.pinnedMessages[peerID] == mid) { + pinnedMid = mid; + } + + if(pinnedMid) { + $rootScope.$broadcast('peer_pinned_message', peerID); + } + } + + if(isTopMessage || groupID) { const updatedDialogs: {[peerID: number]: Dialog} = {}; updatedDialogs[peerID] = dialog; $rootScope.$broadcast('dialogs_multiupdate', updatedDialogs); @@ -3872,7 +3927,14 @@ export class AppMessagesManager { case 'updateDeleteMessages': case 'updateDeleteChannelMessages': { - const historiesUpdated: {[peerID: number]: {count: number, unread: number, msgs: {[mid: number]: true}}} = {}; + const historiesUpdated: { + [peerID: number]: { + count: number, + unread: number, + msgs: {[mid: number]: true}, + albums?: {[groupID: string]: Set}, + } + } = {}; const channelID: number = (update as Update.updateDeleteChannelMessages).channel_id; const messages = (update as any as Update.updateDeleteChannelMessages).messages; @@ -3909,7 +3971,11 @@ export class AppMessagesManager { if(groupedStorage) { delete groupedStorage[mid]; + if(!history.albums) history.albums = {}; + (history.albums[message.grouped_id] || (history.albums[message.grouped_id] = new Set())).add(mid); + if(!Object.keys(groupedStorage).length) { + delete history.albums; delete this.groupedMessagesStorage[message.grouped_id]; } } @@ -3932,6 +3998,18 @@ export class AppMessagesManager { Object.keys(historiesUpdated).forEach(_peerID => { const peerID = +_peerID; const updatedData = historiesUpdated[peerID]; + + if(updatedData.albums) { + for(const groupID in updatedData.albums) { + $rootScope.$broadcast('album_edit', {peerID, groupID, deletedMids: [...updatedData.albums[groupID]]}); + /* const mids = this.getMidsByAlbum(groupID); + if(mids.length) { + const mid = Math.max(...mids); + $rootScope.$broadcast('message_edit', {peerID, mid, justMedia: false}); + } */ + } + } + const historyStorage = this.historiesStorage[peerID]; if(historyStorage !== undefined) { const newHistory = historyStorage.history.filter(mid => !updatedData.msgs[mid]); diff --git a/src/lib/rootScope.ts b/src/lib/rootScope.ts index afa6d4f2..127e5535 100644 --- a/src/lib/rootScope.ts +++ b/src/lib/rootScope.ts @@ -32,7 +32,7 @@ type BroadcastEvents = { 'history_reload': number, 'history_request': void, - 'message_edit': {peerID: number, id: number, mid: number, justMedia: boolean}, + 'message_edit': {peerID: number, mid: number, justMedia: boolean}, 'message_views': {mid: number, views: number}, 'message_sent': {tempID: number, mid: number}, 'messages_pending': void, @@ -40,6 +40,8 @@ type BroadcastEvents = { 'messages_downloaded': number[], 'messages_media_read': number[], + 'album_edit': {peerID: number, groupID: string, deletedMids: number[]}, + 'stickers_installed': StickerSet.stickerSet, 'stickers_deleted': StickerSet.stickerSet,