Browse Source

Fix displaying forwarded messages

Fix order of forward's caption
master
Eduard Kuzmenko 2 years ago
parent
commit
66850dd735
  1. 103
      src/components/chat/bubbles.ts
  2. 4
      src/components/chat/input.ts
  3. 17
      src/components/sidebarRight/tabs/sharedMedia.ts
  4. 17
      src/components/wrappers/album.ts
  5. 2
      src/helpers/object/getObjectKeysAndSort.ts
  6. 14
      src/helpers/searchListLoader.ts
  7. 346
      src/lib/appManagers/appMessagesManager.ts
  8. 23
      src/lib/appManagers/utils/messages/getAlbumText.ts
  9. 10
      src/lib/rootScope.ts
  10. 63
      src/tests/splitString.test.ts

103
src/components/chat/bubbles.ts

@ -36,7 +36,7 @@ import { BOT_START_PARAM, NULL_PEER_ID, REPLIES_PEER_ID } from "../../lib/mtprot
import { FocusDirection, ScrollStartCallbackDimensions } from "../../helpers/fastSmoothScroll"; import { FocusDirection, ScrollStartCallbackDimensions } from "../../helpers/fastSmoothScroll";
import useHeavyAnimationCheck, { getHeavyAnimationPromise, dispatchHeavyAnimationEvent, interruptHeavyAnimation } from "../../hooks/useHeavyAnimationCheck"; import useHeavyAnimationCheck, { getHeavyAnimationPromise, dispatchHeavyAnimationEvent, interruptHeavyAnimation } from "../../hooks/useHeavyAnimationCheck";
import { fastRaf, fastRafPromise } from "../../helpers/schedulers"; import { fastRaf, fastRafPromise } from "../../helpers/schedulers";
import deferredPromise, { CancellablePromise } from "../../helpers/cancellablePromise"; import deferredPromise from "../../helpers/cancellablePromise";
import RepliesElement from "./replies"; import RepliesElement from "./replies";
import DEBUG from "../../config/debug"; import DEBUG from "../../config/debug";
import { SliceEnd } from "../../helpers/slicedArray"; import { SliceEnd } from "../../helpers/slicedArray";
@ -49,7 +49,6 @@ import { getMiddleware } from "../../helpers/middleware";
import cancelEvent from "../../helpers/dom/cancelEvent"; import cancelEvent from "../../helpers/dom/cancelEvent";
import { attachClickEvent, simulateClickEvent } from "../../helpers/dom/clickEvent"; import { attachClickEvent, simulateClickEvent } from "../../helpers/dom/clickEvent";
import htmlToDocumentFragment from "../../helpers/dom/htmlToDocumentFragment"; import htmlToDocumentFragment from "../../helpers/dom/htmlToDocumentFragment";
import positionElementByIndex from "../../helpers/dom/positionElementByIndex";
import reflowScrollableElement from "../../helpers/dom/reflowScrollableElement"; import reflowScrollableElement from "../../helpers/dom/reflowScrollableElement";
import replaceContent from "../../helpers/dom/replaceContent"; import replaceContent from "../../helpers/dom/replaceContent";
import setInnerHTML from "../../helpers/dom/setInnerHTML"; import setInnerHTML from "../../helpers/dom/setInnerHTML";
@ -98,7 +97,6 @@ import getPeerId from "../../lib/appManagers/utils/peers/getPeerId";
import getServerMessageId from "../../lib/appManagers/utils/messageId/getServerMessageId"; import getServerMessageId from "../../lib/appManagers/utils/messageId/getServerMessageId";
import generateMessageId from "../../lib/appManagers/utils/messageId/generateMessageId"; import generateMessageId from "../../lib/appManagers/utils/messageId/generateMessageId";
import { AppManagers } from "../../lib/appManagers/managers"; import { AppManagers } from "../../lib/appManagers/managers";
import filterAsync from "../../helpers/array/filterAsync";
import { Awaited } from "../../types"; import { Awaited } from "../../types";
import idleController from "../../helpers/idleController"; import idleController from "../../helpers/idleController";
import overlayCounter from "../../helpers/overlayCounter"; import overlayCounter from "../../helpers/overlayCounter";
@ -106,10 +104,10 @@ import { cancelContextMenuOpening } from "../../helpers/dom/attachContextMenuLis
import contextMenuController from "../../helpers/contextMenuController"; import contextMenuController from "../../helpers/contextMenuController";
import { AckedResult } from "../../lib/mtproto/superMessagePort"; import { AckedResult } from "../../lib/mtproto/superMessagePort";
import middlewarePromise from "../../helpers/middlewarePromise"; import middlewarePromise from "../../helpers/middlewarePromise";
import findAndSplice from "../../helpers/array/findAndSplice";
import { EmoticonsDropdown } from "../emoticonsDropdown"; import { EmoticonsDropdown } from "../emoticonsDropdown";
import indexOfAndSplice from "../../helpers/array/indexOfAndSplice"; import indexOfAndSplice from "../../helpers/array/indexOfAndSplice";
import noop from "../../helpers/noop"; import noop from "../../helpers/noop";
import getAlbumText from "../../lib/appManagers/utils/messages/getAlbumText";
const USE_MEDIA_TAILS = false; const USE_MEDIA_TAILS = false;
const IGNORE_ACTIONS: Set<Message.messageService['action']['_']> = new Set([ const IGNORE_ACTIONS: Set<Message.messageService['action']['_']> = new Set([
@ -283,11 +281,12 @@ export default class ChatBubbles {
// * events // * events
// will call when sent for update pos // will call when sent for update pos
this.listenerSetter.add(rootScope)('history_update', async({storageKey, mid, message}) => { this.listenerSetter.add(rootScope)('history_update', async({storageKey, sequential, message}) => {
if(this.chat.messagesStorageKey !== storageKey || this.chat.type === 'scheduled') { if(this.chat.messagesStorageKey !== storageKey || this.chat.type === 'scheduled') {
return; return;
} }
const {mid} = message;
const log = false ? this.log.bindPrefix('history_update-' + mid) : undefined; const log = false ? this.log.bindPrefix('history_update-' + mid) : undefined;
log && log('start'); log && log('start');
@ -317,16 +316,22 @@ export default class ChatBubbles {
return; return;
} }
const group = item.group; if(sequential) {
const newItem = this.bubbleGroups.createItem(bubble, message); const group = item.group;
// newItem.mid = item.mid; const newItem = this.bubbleGroups.createItem(bubble, message);
const _items = this.bubbleGroups.itemsArr.slice(); // newItem.mid = item.mid;
indexOfAndSplice(_items, item); const _items = this.bubbleGroups.itemsArr.slice();
const foundItem = this.bubbleGroups.findGroupSiblingByItem(newItem, _items); indexOfAndSplice(_items, item);
if(group === foundItem?.group || (group === this.bubbleGroups.getLastGroup() && group.items.length === 1)/* && false */) { const foundItem = this.bubbleGroups.findGroupSiblingByItem(newItem, _items);
log && log('item has correct position', item); if(
this.bubbleGroups.changeBubbleMid(bubble, mid); group === foundItem?.group
return; || (group === this.bubbleGroups.getLastGroup() && group.items.length === 1 && newItem.dateTimestamp === item.dateTimestamp)
|| (this.peerId === rootScope.myId && sequential && newItem.dateTimestamp === item.dateTimestamp)
) {
log && log('item has correct position', item);
this.bubbleGroups.changeBubbleMid(bubble, mid);
return;
}
} }
// return; // return;
@ -436,12 +441,12 @@ export default class ChatBubbles {
let messages: (Message.message | Message.messageService)[], tempIds: number[]; let messages: (Message.message | Message.messageService)[], tempIds: number[];
const groupedId = (message as Message.message).grouped_id; const groupedId = (message as Message.message).grouped_id;
if(groupedId) { if(groupedId) {
const mids = await this.managers.appMessagesManager.getMidsByMessage(message); messages = await this.managers.appMessagesManager.getMessagesByAlbum(groupedId);
const mids = messages.map(({mid}) => mid);
if(!mids.length || getMainMidForGrouped(mids) !== mid || bubbles[mid] !== _bubble) { if(!mids.length || getMainMidForGrouped(mids) !== mid || bubbles[mid] !== _bubble) {
return; return;
} }
messages = await Promise.all(mids.map((mid) => this.chat.getMessage(mid)));
if(bubbles[mid] !== _bubble) { if(bubbles[mid] !== _bubble) {
return; return;
} }
@ -835,13 +840,13 @@ export default class ChatBubbles {
public constructPeerHelpers() { public constructPeerHelpers() {
// will call when message is sent (only 1) // will call when message is sent (only 1)
this.listenerSetter.add(rootScope)('history_append', async({storageKey, mid}) => { this.listenerSetter.add(rootScope)('history_append', async({storageKey, message}) => {
if(storageKey !== this.chat.messagesStorageKey) return; if(storageKey !== this.chat.messagesStorageKey) return;
if(!this.scrollable.loadedAll.bottom) { if(!this.scrollable.loadedAll.bottom) {
this.chat.setMessageId(); this.chat.setMessageId();
} else { } else {
this.renderNewMessagesByIds([mid], true); this.renderNewMessage(message, true);
} }
if(rootScope.settings.animationsEnabled) { if(rootScope.settings.animationsEnabled) {
@ -852,10 +857,9 @@ export default class ChatBubbles {
} }
}); });
this.listenerSetter.add(rootScope)('history_multiappend', (msgIdsByPeer) => { this.listenerSetter.add(rootScope)('history_multiappend', (message) => {
if(!(this.peerId in msgIdsByPeer)) return; if(this.peerId !== message.peerId) return;
const msgIds = Array.from(msgIdsByPeer[this.peerId]).slice().sort((a, b) => b - a); this.renderNewMessage(message);
this.renderNewMessagesByIds(msgIds);
}); });
this.listenerSetter.add(rootScope)('history_delete', ({peerId, msgs}) => { this.listenerSetter.add(rootScope)('history_delete', ({peerId, msgs}) => {
@ -1377,10 +1381,10 @@ export default class ChatBubbles {
this.chat.topbar.setTitle((await this.managers.appMessagesManager.getScheduledMessagesStorage(this.peerId)).size); this.chat.topbar.setTitle((await this.managers.appMessagesManager.getScheduledMessagesStorage(this.peerId)).size);
}; };
this.listenerSetter.add(rootScope)('scheduled_new', ({peerId, mid}) => { this.listenerSetter.add(rootScope)('scheduled_new', (message) => {
if(peerId !== this.peerId) return; if(message.peerId !== this.peerId) return;
this.renderNewMessagesByIds([mid]); this.renderNewMessage(message);
onUpdate(); onUpdate();
}); });
@ -2162,8 +2166,8 @@ export default class ChatBubbles {
}; };
} }
private renderNewMessagesByIds(mids: number[], scrolledDown?: boolean) { private renderNewMessage(message: MyMessage, scrolledDown?: boolean) {
const promise = this._renderNewMessagesByIds(mids, scrolledDown); const promise = this._renderNewMessage(message, scrolledDown);
this.renderNewPromises.add(promise); this.renderNewPromises.add(promise);
promise.catch(noop).finally(() => { promise.catch(noop).finally(() => {
this.renderNewPromises.delete(promise); this.renderNewPromises.delete(promise);
@ -2171,15 +2175,17 @@ export default class ChatBubbles {
return promise; return promise;
} }
private async _renderNewMessagesByIds(mids: number[], scrolledDown?: boolean) { private async _renderNewMessage(message: MyMessage, scrolledDown?: boolean) {
if(!this.scrollable.loadedAll.bottom) { // seems search active or sliced if(!this.scrollable.loadedAll.bottom) { // seems search active or sliced
//this.log('renderNewMessagesByIds: seems search is active, skipping render:', mids); //this.log('renderNewMessagesByIds: seems search is active, skipping render:', mids);
const setPeerPromise = this.chat.setPeerPromise; const setPeerPromise = this.chat.setPeerPromise;
if(setPeerPromise) { if(setPeerPromise) {
const middleware = this.getMiddleware(); const middleware = this.getMiddleware();
setPeerPromise.then(() => { setPeerPromise.then(async() => {
if(!middleware()) return; if(!middleware()) return;
this.renderNewMessagesByIds(mids); const newMessage = await this.chat.getMessage(message.mid);
if(!middleware()) return;
this.renderNewMessage(newMessage);
}); });
} }
@ -2187,14 +2193,15 @@ export default class ChatBubbles {
} }
if(this.chat.threadId) { if(this.chat.threadId) {
mids = await filterAsync(mids, async(mid) => { const replyTo = message?.reply_to as MessageReplyHeader;
const message = await this.chat.getMessage(mid); if(!(replyTo && (replyTo.reply_to_top_id || replyTo.reply_to_msg_id) === this.chat.threadId)) {
const replyTo = message?.reply_to as MessageReplyHeader; return;
return replyTo && (replyTo.reply_to_top_id || replyTo.reply_to_msg_id) === this.chat.threadId; }
});
} }
mids = mids.filter((mid) => !this.bubbles[mid]); if(this.bubbles[message.mid]) {
return;
}
// ! should scroll even without new messages // ! should scroll even without new messages
/* if(!mids.length) { /* if(!mids.length) {
return; return;
@ -2211,7 +2218,7 @@ export default class ChatBubbles {
const middleware = this.getMiddleware(); const middleware = this.getMiddleware();
const {isPaddingNeeded, unsetPadding} = this.setTopPadding(middleware); const {isPaddingNeeded, unsetPadding} = this.setTopPadding(middleware);
const promise = this.performHistoryResult({history: mids}, false); const promise = this.performHistoryResult({history: [message]}, false);
if(scrolledDown) { if(scrolledDown) {
promise.then(() => { promise.then(() => {
if(!middleware()) return; if(!middleware()) return;
@ -2221,7 +2228,7 @@ export default class ChatBubbles {
let bubble: HTMLElement; let bubble: HTMLElement;
if(this.chat.type === 'scheduled') { if(this.chat.type === 'scheduled') {
bubble = this.bubbles[Math.max(...mids)]; bubble = this.bubbles[message.mid];
} }
const promise = bubble ? this.scrollToBubbleEnd(bubble) : this.scrollToEnd(); const promise = bubble ? this.scrollToBubbleEnd(bubble) : this.scrollToEnd();
@ -3373,10 +3380,12 @@ export default class ChatBubbles {
const isMessage = message._ === 'message'; const isMessage = message._ === 'message';
const groupedId = isMessage && message.grouped_id; const groupedId = isMessage && message.grouped_id;
let albumMids: number[], reactionsMessage: Message.message; let albumMids: number[], reactionsMessage: Message.message;
const albumMessages = groupedId ? await this.managers.appMessagesManager.getMessagesByAlbum(groupedId) : undefined;
const albumMustBeRenderedFull = this.chat.type !== 'pinned'; const albumMustBeRenderedFull = this.chat.type !== 'pinned';
if(groupedId && albumMustBeRenderedFull) { // will render only last album's message if(groupedId && albumMustBeRenderedFull) { // will render only last album's message
albumMids = await this.managers.appMessagesManager.getMidsByAlbum(groupedId); albumMids = albumMessages.map((message) => message.mid);
const mainMid = getMainMidForGrouped(albumMids); const mainMid = getMainMidForGrouped(albumMids);
if(message.mid !== mainMid) { if(message.mid !== mainMid) {
return; return;
@ -3384,7 +3393,7 @@ export default class ChatBubbles {
} }
if(isMessage) { if(isMessage) {
reactionsMessage = groupedId ? await this.managers.appMessagesManager.getGroupsFirstMessage(message) : message; reactionsMessage = groupedId ? albumMessages[0] : message;
} }
// * can't use 'message.pFlags.out' here because this check will be used to define side of message (left-right) // * can't use 'message.pFlags.out' here because this check will be used to define side of message (left-right)
@ -3470,7 +3479,7 @@ export default class ChatBubbles {
!['video', 'gif'].includes(((messageMedia as MessageMedia.messageMediaDocument).document as MyDocument).type)) { !['video', 'gif'].includes(((messageMedia as MessageMedia.messageMediaDocument).document as MyDocument).type)) {
// * just filter these cases for documents caption // * just filter these cases for documents caption
} else if(groupedId && albumMustBeRenderedFull) { } else if(groupedId && albumMustBeRenderedFull) {
const t = await this.managers.appMessagesManager.getAlbumText(groupedId); const t = getAlbumText(albumMessages);
messageMessage = t.message; messageMessage = t.message;
//totalEntities = t.entities; //totalEntities = t.entities;
totalEntities = t.totalEntities; totalEntities = t.totalEntities;
@ -3745,8 +3754,8 @@ export default class ChatBubbles {
if(albumMustBeRenderedFull && groupedId && albumMids.length !== 1) { if(albumMustBeRenderedFull && groupedId && albumMids.length !== 1) {
bubble.classList.add('is-album', 'is-grouped'); bubble.classList.add('is-album', 'is-grouped');
const promise = wrapAlbum({ wrapAlbum({
groupId: groupedId, messages: albumMessages,
attachmentDiv, attachmentDiv,
middleware: this.getMiddleware(), middleware: this.getMiddleware(),
isOut: our, isOut: our,
@ -3756,8 +3765,6 @@ export default class ChatBubbles {
autoDownload: this.chat.autoDownload, autoDownload: this.chat.autoDownload,
}); });
loadPromises.push(promise);
break; break;
} }
@ -4001,8 +4008,8 @@ export default class ChatBubbles {
if(albumMustBeRenderedFull && groupedId && albumMids.length !== 1) { if(albumMustBeRenderedFull && groupedId && albumMids.length !== 1) {
bubble.classList.add('is-album', 'is-grouped'); bubble.classList.add('is-album', 'is-grouped');
await wrapAlbum({ wrapAlbum({
groupId: groupedId, messages: albumMessages,
attachmentDiv, attachmentDiv,
middleware: this.getMiddleware(), middleware: this.getMiddleware(),
isOut: our, isOut: our,

4
src/components/chat/input.ts

@ -2395,7 +2395,7 @@ export default class ChatInput {
// * wait for sendText set messageId for invokeAfterMsg // * wait for sendText set messageId for invokeAfterMsg
if(this.forwarding) { if(this.forwarding) {
const forwarding = copy(this.forwarding); const forwarding = copy(this.forwarding);
setTimeout(() => { // setTimeout(() => {
for(const fromPeerId in forwarding) { for(const fromPeerId in forwarding) {
this.managers.appMessagesManager.forwardMessages(peerId, fromPeerId.toPeerId(), forwarding[fromPeerId], { this.managers.appMessagesManager.forwardMessages(peerId, fromPeerId.toPeerId(), forwarding[fromPeerId], {
...sendingParams, ...sendingParams,
@ -2407,7 +2407,7 @@ export default class ChatInput {
if(!value) { if(!value) {
this.onMessageSent(); this.onMessageSent();
} }
}, 0); // }, 0);
} }
// this.onMessageSent(); // this.onMessageSent();

17
src/components/sidebarRight/tabs/sharedMedia.ts

@ -21,6 +21,7 @@ import PopupPeer, { PopupPeerButtonCallbackCheckboxes, PopupPeerCheckboxOptions
import ButtonCorner from "../../buttonCorner"; import ButtonCorner from "../../buttonCorner";
import { attachClickEvent } from "../../../helpers/dom/clickEvent"; import { attachClickEvent } from "../../../helpers/dom/clickEvent";
import PeerProfile from "../../peerProfile"; import PeerProfile from "../../peerProfile";
import { Message } from "../../../layer";
const historiesStorage: { const historiesStorage: {
[peerId: PeerId]: Partial<{ [peerId: PeerId]: Partial<{
@ -156,10 +157,8 @@ export default class AppSharedMediaTab extends SliderSuperTab {
} }
}); });
this.listenerSetter.add(rootScope)('history_multiappend', (msgIdsByPeer) => { this.listenerSetter.add(rootScope)('history_multiappend', (message) => {
for(const peerId in msgIdsByPeer) { this.renderNewMessages(message);
this.renderNewMessages(peerId.toPeerId(), Array.from(msgIdsByPeer[peerId]));
}
}); });
this.listenerSetter.add(rootScope)('history_delete', ({peerId, msgs}) => { this.listenerSetter.add(rootScope)('history_delete', ({peerId, msgs}) => {
@ -168,7 +167,7 @@ export default class AppSharedMediaTab extends SliderSuperTab {
// Calls when message successfully sent and we have an id // Calls when message successfully sent and we have an id
this.listenerSetter.add(rootScope)('message_sent', ({message}) => { this.listenerSetter.add(rootScope)('message_sent', ({message}) => {
this.renderNewMessages(message.peerId, [message.mid]); this.renderNewMessages(message);
}); });
//this.container.prepend(this.closeBtn.parentElement); //this.container.prepend(this.closeBtn.parentElement);
@ -321,14 +320,12 @@ export default class AppSharedMediaTab extends SliderSuperTab {
//console.log('construct shared media time:', performance.now() - perf); //console.log('construct shared media time:', performance.now() - perf);
} }
public async renderNewMessages(peerId: PeerId, mids: number[]) { public async renderNewMessages(message: Message.message | Message.messageService) {
if(this.init) return; // * not inited yet if(this.init) return; // * not inited yet
const {peerId} = message;
if(!historiesStorage[peerId]) return; if(!historiesStorage[peerId]) return;
const messages = await Promise.all(mids.map((mid) => this.managers.appMessagesManager.getMessageByPeer(peerId, mid)));
mids = mids.slice().reverse(); // ! because it will be ascend sorted array
for(const mediaTab of this.searchSuper.mediaTabs) { for(const mediaTab of this.searchSuper.mediaTabs) {
const inputFilter = mediaTab.inputFilter; const inputFilter = mediaTab.inputFilter;
const history = historiesStorage[peerId][inputFilter]; const history = historiesStorage[peerId][inputFilter];
@ -336,7 +333,7 @@ export default class AppSharedMediaTab extends SliderSuperTab {
continue; continue;
} }
const filtered = this.searchSuper.filterMessagesByType(messages, inputFilter).filter((message) => !history.find((m) => m.mid === message.mid && m.peerId === message.peerId)); const filtered = this.searchSuper.filterMessagesByType([message], inputFilter).filter((message) => !history.find((m) => m.mid === message.mid && m.peerId === message.peerId));
if(filtered.length) { if(filtered.length) {
history.unshift(...filtered.map((message) => ({mid: message.mid, peerId: message.peerId}))); history.unshift(...filtered.map((message) => ({mid: message.mid, peerId: message.peerId})));

17
src/components/wrappers/album.ts

@ -6,7 +6,7 @@
import { ChatAutoDownloadSettings } from "../../helpers/autoDownload"; import { ChatAutoDownloadSettings } from "../../helpers/autoDownload";
import mediaSizes from "../../helpers/mediaSizes"; import mediaSizes from "../../helpers/mediaSizes";
import { PhotoSize } from "../../layer"; import { Message, PhotoSize } from "../../layer";
import { AppManagers } from "../../lib/appManagers/managers"; import { AppManagers } from "../../lib/appManagers/managers";
import getMediaFromMessage from "../../lib/appManagers/utils/messages/getMediaFromMessage"; import getMediaFromMessage from "../../lib/appManagers/utils/messages/getMediaFromMessage";
import choosePhotoSize from "../../lib/appManagers/utils/photos/choosePhotoSize"; import choosePhotoSize from "../../lib/appManagers/utils/photos/choosePhotoSize";
@ -17,8 +17,8 @@ import prepareAlbum from "../prepareAlbum";
import wrapPhoto from "./photo"; import wrapPhoto from "./photo";
import wrapVideo from "./video"; import wrapVideo from "./video";
export default async function wrapAlbum({groupId, attachmentDiv, middleware, uploading, lazyLoadQueue, isOut, chat, loadPromises, autoDownload, managers = rootScope.managers}: { export default function wrapAlbum({messages, attachmentDiv, middleware, uploading, lazyLoadQueue, isOut, chat, loadPromises, autoDownload, managers = rootScope.managers}: {
groupId: string, messages: Message.message[],
attachmentDiv: HTMLElement, attachmentDiv: HTMLElement,
middleware?: () => boolean, middleware?: () => boolean,
lazyLoadQueue?: LazyLoadQueue, lazyLoadQueue?: LazyLoadQueue,
@ -32,8 +32,6 @@ export default async function wrapAlbum({groupId, attachmentDiv, middleware, upl
const items: {size: PhotoSize.photoSize, media: any, message: any}[] = []; const items: {size: PhotoSize.photoSize, media: any, message: any}[] = [];
// !lowest msgID will be the FIRST in album // !lowest msgID will be the FIRST in album
const storage = await managers.appMessagesManager.getMidsByAlbum(groupId);
const messages = await Promise.all(storage.map((mid) => chat.getMessage(mid)));
for(const message of messages) { for(const message of messages) {
const media = getMediaFromMessage(message); const media = getMediaFromMessage(message);
@ -63,8 +61,9 @@ export default async function wrapAlbum({groupId, attachmentDiv, middleware, upl
div.dataset.peerId = '' + message.peerId; div.dataset.peerId = '' + message.peerId;
const mediaDiv = div.firstElementChild as HTMLElement; const mediaDiv = div.firstElementChild as HTMLElement;
const isPhoto = media._ === 'photo'; const isPhoto = media._ === 'photo';
let thumbPromise: Promise<any>;
if(isPhoto) { if(isPhoto) {
wrapPhoto({ thumbPromise = wrapPhoto({
photo: media, photo: media,
message, message,
container: mediaDiv, container: mediaDiv,
@ -79,7 +78,7 @@ export default async function wrapAlbum({groupId, attachmentDiv, middleware, upl
managers managers
}); });
} else { } else {
wrapVideo({ thumbPromise = wrapVideo({
doc: message.media.document, doc: message.media.document,
container: mediaDiv, container: mediaDiv,
message, message,
@ -94,5 +93,9 @@ export default async function wrapAlbum({groupId, attachmentDiv, middleware, upl
managers managers
}); });
} }
if(thumbPromise && loadPromises) {
loadPromises.push(thumbPromise);
}
}); });
} }

2
src/helpers/object/getObjectKeysAndSort.ts

@ -1,4 +1,4 @@
export default function getObjectKeysAndSort(object: {[key: string]: any}, sort: 'asc' | 'desc' = 'asc') { export default function getObjectKeysAndSort(object: {[key: string]: any} | Map<number, any>, sort: 'asc' | 'desc' = 'asc') {
if(!object) return []; if(!object) return [];
const ids = object instanceof Map ? [...object.keys()] : Object.keys(object).map((i) => +i); const ids = object instanceof Map ? [...object.keys()] : Object.keys(object).map((i) => +i);
if(sort === 'asc') return ids.sort((a, b) => a - b); if(sort === 'asc') return ids.sort((a, b) => a - b);

14
src/helpers/searchListLoader.ts

@ -114,9 +114,7 @@ export default class SearchListLoader<Item extends {mid: number, peerId: PeerId}
} }
}; };
protected onHistoryMultiappend = async(obj: { protected onHistoryMultiappend = async(message: Message.message | Message.messageService) => {
[peerId: string]: Set<number>;
}) => {
if(this.searchContext.folderId !== undefined) { if(this.searchContext.folderId !== undefined) {
return; return;
} }
@ -126,13 +124,11 @@ export default class SearchListLoader<Item extends {mid: number, peerId: PeerId}
return; return;
} }
const mids = obj[this.searchContext.peerId]; if(message.peerId !== this.searchContext.peerId) {
if(!mids) {
return; return;
} }
const sorted = Array.from(mids).sort((a, b) => a - b); const filtered = await this.filterMids([message.mid]);
const filtered = await this.filterMids(sorted);
const targets = (await Promise.all(filtered.map((message) => this.processItem(message)))).filter(Boolean); const targets = (await Promise.all(filtered.map((message) => this.processItem(message)))).filter(Boolean);
if(targets.length) { if(targets.length) {
/* const {previous, current, next} = this; /* const {previous, current, next} = this;
@ -159,9 +155,7 @@ export default class SearchListLoader<Item extends {mid: number, peerId: PeerId}
}; };
protected onMessageSent = ({message}: {message: MyMessage}) => { protected onMessageSent = ({message}: {message: MyMessage}) => {
this.onHistoryMultiappend({ this.onHistoryMultiappend(message);
[message.peerId]: new Set([message.mid])
});
}; };
public setSearchContext(context: SearchSuperContext) { public setSearchContext(context: SearchSuperContext) {

346
src/lib/appManagers/appMessagesManager.ts

@ -58,6 +58,8 @@ import type { Progress } from "./appDownloadManager";
import noop from "../../helpers/noop"; import noop from "../../helpers/noop";
import appTabsManager from "./appTabsManager"; import appTabsManager from "./appTabsManager";
import MTProtoMessagePort from "../mtproto/mtprotoMessagePort"; import MTProtoMessagePort from "../mtproto/mtprotoMessagePort";
import getAlbumText from "./utils/messages/getAlbumText";
import pause from "../../helpers/schedulers/pause";
//console.trace('include'); //console.trace('include');
// TODO: если удалить диалог находясь в папке, то он не удалится из папки и будет виден в настройках // TODO: если удалить диалог находясь в папке, то он не удалится из папки и будет виден в настройках
@ -134,6 +136,11 @@ type PendingMessageDetails = {
sequential?: boolean sequential?: boolean
}; };
const processAfter = (cb: () => void) => {
// setTimeout(cb, 0);
cb();
};
export class AppMessagesManager extends AppManager { export class AppMessagesManager extends AppManager {
private messagesStorageByPeerId: {[peerId: string]: MessagesStorage}; private messagesStorageByPeerId: {[peerId: string]: MessagesStorage};
private groupedMessagesStorage: {[groupId: string]: MessagesStorage}; // will be used for albums private groupedMessagesStorage: {[groupId: string]: MessagesStorage}; // will be used for albums
@ -186,8 +193,6 @@ export class AppMessagesManager extends AppManager {
public migratedFromTo: {[peerId: PeerId]: PeerId} = {}; public migratedFromTo: {[peerId: PeerId]: PeerId} = {};
public migratedToFrom: {[peerId: PeerId]: PeerId} = {}; public migratedToFrom: {[peerId: PeerId]: PeerId} = {};
private newMessagesHandleTimeout = 0;
private newMessagesToHandle: {[peerId: PeerId]: Set<number>} = {};
private newDialogsHandlePromise: Promise<any>; private newDialogsHandlePromise: Promise<any>;
private newDialogsToHandle: {[peerId: PeerId]: Dialog} = {}; private newDialogsToHandle: {[peerId: PeerId]: Dialog} = {};
public newUpdatesAfterReloadToHandle: {[peerId: PeerId]: Set<Update>} = {}; public newUpdatesAfterReloadToHandle: {[peerId: PeerId]: Set<Update>} = {};
@ -474,37 +479,31 @@ export class AppMessagesManager extends AppManager {
scheduleDate: number, scheduleDate: number,
silent: true, silent: true,
sendAsPeerId: PeerId, sendAsPeerId: PeerId,
}> = {}) { }> = {}): Promise<void> {
if(!text.trim()) { if(!text.trim()) {
return; return;
} }
options.entities ??= [];
//this.checkSendOptions(options); //this.checkSendOptions(options);
if(options.threadId && !options.replyToMsgId) { if(options.threadId && !options.replyToMsgId) {
options.replyToMsgId = options.threadId; options.replyToMsgId = options.threadId;
} }
const config = await this.apiManager.getConfig(); const config = await this.apiManager.getConfig();
const MAX_LENGTH = config.message_length_max; const MAX_LENGTH = config.message_length_max;
if(text.length > MAX_LENGTH) { const splitted = splitStringByLength(text, MAX_LENGTH);
const splitted = splitStringByLength(text, MAX_LENGTH); text = splitted[0];
text = splitted[0]; if(splitted.length > 1) {
if(options.webPage?._ === 'webPage' && !text.includes(options.webPage.url)) {
if(splitted.length > 1) {
delete options.webPage; delete options.webPage;
} }
for(let i = 1; i < splitted.length; ++i) {
setTimeout(() => {
this.sendText(peerId, splitted[i], options);
}, i);
}
} }
peerId = this.appPeersManager.getPeerMigratedTo(peerId) || peerId; peerId = this.appPeersManager.getPeerMigratedTo(peerId) || peerId;
let entities = options.entities || []; let entities = options.entities;
if(!options.viaBotId) { if(!options.viaBotId) {
text = parseMarkdown(text, entities); text = parseMarkdown(text, entities);
//entities = mergeEntities(entities, parseEntities(text)); //entities = mergeEntities(entities, parseEntities(text));
@ -653,7 +652,12 @@ export class AppMessagesManager extends AppManager {
sequential: true sequential: true
}); });
return message.promise; const promises: ReturnType<AppMessagesManager['sendText']>[] = [message.promise];
for(let i = 1; i < splitted.length; ++i) {
promises.push(this.sendText(peerId, splitted[i], options));
}
return Promise.all(promises).then(noop);
} }
public sendFile(peerId: PeerId, file: File | Blob | MyDocument, options: Partial<{ public sendFile(peerId: PeerId, file: File | Blob | MyDocument, options: Partial<{
@ -684,6 +688,9 @@ export class AppMessagesManager extends AppManager {
noSound: boolean, noSound: boolean,
waveform: Uint8Array, waveform: Uint8Array,
// ! only for internal use
processAfter?: typeof processAfter
}> = {}) { }> = {}) {
peerId = this.appPeersManager.getPeerMigratedTo(peerId) || peerId; peerId = this.appPeersManager.getPeerMigratedTo(peerId) || peerId;
@ -1033,7 +1040,8 @@ export class AppMessagesManager extends AppManager {
isGroupedItem: options.isGroupedItem, isGroupedItem: options.isGroupedItem,
isScheduled: !!options.scheduleDate || undefined, isScheduled: !!options.scheduleDate || undefined,
threadId: options.threadId, threadId: options.threadId,
clearDraft: options.clearDraft clearDraft: options.clearDraft,
processAfter: options.processAfter
}); });
if(!options.isGroupedItem) { if(!options.isGroupedItem) {
@ -1128,6 +1136,11 @@ export class AppMessagesManager extends AppManager {
const groupId = '' + ++this.groupedTempId; const groupId = '' + ++this.groupedTempId;
const callbacks: Array<() => void> = [];
const processAfter = (cb: () => void) => {
callbacks.push(cb);
};
const messages = files.map((file, idx) => { const messages = files.map((file, idx) => {
const details = options.sendFileDetails[idx]; const details = options.sendFileDetails[idx];
const o: Parameters<AppMessagesManager['sendFile']>[2] = { const o: Parameters<AppMessagesManager['sendFile']>[2] = {
@ -1139,6 +1152,7 @@ export class AppMessagesManager extends AppManager {
threadId: options.threadId, threadId: options.threadId,
sendAsPeerId: options.sendAsPeerId, sendAsPeerId: options.sendAsPeerId,
groupId, groupId,
processAfter,
...details ...details
}; };
@ -1152,11 +1166,15 @@ export class AppMessagesManager extends AppManager {
}); });
if(options.clearDraft) { if(options.clearDraft) {
setTimeout(() => { callbacks.push(() => {
this.appDraftsManager.clearDraft(peerId, options.threadId); this.appDraftsManager.clearDraft(peerId, options.threadId);
}, 0); });
} }
callbacks.forEach((callback) => {
callback();
});
// * test pending // * test pending
//return; //return;
@ -1451,18 +1469,19 @@ export class AppMessagesManager extends AppManager {
isScheduled: true, isScheduled: true,
threadId: number, threadId: number,
clearDraft: true, clearDraft: true,
sequential: boolean sequential: boolean,
processAfter?: (cb: () => void) => void
}> = {}) { }> = {}) {
const messageId = message.id; const messageId = message.id;
const peerId = this.getMessagePeer(message); const peerId = this.getMessagePeer(message);
const storage = options.isScheduled ? this.getScheduledMessagesStorage(peerId) : this.getHistoryMessagesStorage(peerId); const storage = options.isScheduled ? this.getScheduledMessagesStorage(peerId) : this.getHistoryMessagesStorage(peerId);
let callbacks: Array<() => void> = [];
if(options.isScheduled) { if(options.isScheduled) {
//if(!options.isGroupedItem) { //if(!options.isGroupedItem) {
this.saveMessages([message], {storage, isScheduled: true, isOutgoing: true}); this.saveMessages([message], {storage, isScheduled: true, isOutgoing: true});
setTimeout(() => { callbacks.push(() => {
this.rootScope.dispatchEvent('scheduled_new', {peerId, mid: messageId}); this.rootScope.dispatchEvent('scheduled_new', message);
}, 0); });
} else { } else {
/* if(options.threadId && this.threadsStorage[peerId]) { /* if(options.threadId && this.threadsStorage[peerId]) {
delete this.threadsStorage[peerId][options.threadId]; delete this.threadsStorage[peerId][options.threadId];
@ -1478,12 +1497,11 @@ export class AppMessagesManager extends AppManager {
} }
} }
//if(!options.isGroupedItem) {
this.saveMessages([message], {storage, isOutgoing: true}); this.saveMessages([message], {storage, isOutgoing: true});
this.setDialogTopMessage(message); this.setDialogTopMessage(message);
setTimeout(() => { callbacks.push(() => {
this.rootScope.dispatchEvent('history_append', {storageKey: storage.key, peerId, mid: messageId}); this.rootScope.dispatchEvent('history_append', {storageKey: storage.key, message});
}, 0); });
} }
const pending: PendingMessageDetails = this.pendingByRandomId[message.random_id] = { const pending: PendingMessageDetails = this.pendingByRandomId[message.random_id] = {
@ -1495,13 +1513,21 @@ export class AppMessagesManager extends AppManager {
}; };
if(!options.isGroupedItem && message.send) { if(!options.isGroupedItem && message.send) {
setTimeout(() => { callbacks.push(() => {
if(options.clearDraft) { if(options.clearDraft) {
this.appDraftsManager.clearDraft(peerId, options.threadId); this.appDraftsManager.clearDraft(peerId, options.threadId);
} }
message.send(); message.send();
}, 0/* 3000 */); });
}
if(callbacks.length) {
(options.processAfter || processAfter)(() => {
for(const callback of callbacks) {
callback();
}
});
} }
return pending; return pending;
@ -2280,51 +2306,47 @@ export class AppMessagesManager extends AppManager {
return promise || this.reloadConversationsPromise; return promise || this.reloadConversationsPromise;
} }
this.reloadConversationsPromise = new Promise((resolve, reject) => { this.reloadConversationsPromise = pause(0).then(() => {
setTimeout(() => { const inputDialogPeers: InputDialogPeer[] = [];
const inputDialogPeers: InputDialogPeer[] = []; const promises: {[peerId: string]: typeof promise} = {};
const promises: {[peerId: string]: typeof promise} = {}; for(const [peerId, {inputDialogPeer, promise}] of this.reloadConversationsPeers) {
for(const [peerId, {inputDialogPeer, promise}] of this.reloadConversationsPeers) { inputDialogPeers.push(inputDialogPeer);
inputDialogPeers.push(inputDialogPeer); promises[peerId] = promise;
promises[peerId] = promise; }
}
this.reloadConversationsPeers.clear();
const fullfillLeft = () => {
for(const peerId in promises) {
promises[peerId].resolve(undefined);
}
};
this.apiManager.invokeApi('messages.getPeerDialogs', {peers: inputDialogPeers}).then((result) => { this.reloadConversationsPeers.clear();
this.dialogsStorage.applyDialogs(result);
result.dialogs.forEach((dialog) => { const fullfillLeft = () => {
const peerId = dialog.peerId; for(const peerId in promises) {
if(peerId) { promises[peerId].resolve(undefined);
promises[peerId].resolve(dialog as Dialog); }
delete promises[peerId]; };
}
});
// fullfillLeft(); return this.apiManager.invokeApi('messages.getPeerDialogs', {peers: inputDialogPeers}).then((result) => {
// resolve(); this.dialogsStorage.applyDialogs(result);
}, (err) => {
// fullfillLeft();
// resolve();
// reject(err);
}).finally(() => {
fullfillLeft();
resolve();
this.reloadConversationsPromise = null;
if(this.reloadConversationsPeers.size) { result.dialogs.forEach((dialog) => {
this.reloadConversation(); const peerId = dialog.peerId;
if(peerId) {
promises[peerId].resolve(dialog as Dialog);
delete promises[peerId];
} }
}); });
}, 0);
// fullfillLeft();
// resolve();
}, (err) => {
// fullfillLeft();
// resolve();
// reject(err);
}).then(() => {
fullfillLeft();
this.reloadConversationsPromise = null;
if(this.reloadConversationsPeers.size) {
this.reloadConversation();
}
});
}); });
return promise || this.reloadConversationsPromise; return promise || this.reloadConversationsPromise;
@ -2506,24 +2528,7 @@ export class AppMessagesManager extends AppManager {
public getAlbumText(grouped_id: string) { public getAlbumText(grouped_id: string) {
const group = this.groupedMessagesStorage[grouped_id]; const group = this.groupedMessagesStorage[grouped_id];
let foundMessages = 0, message: string, totalEntities: MessageEntity[], entities: MessageEntity[]; return getAlbumText(Array.from(group.values()) as Message.message[]);
for(const [mid, m] of group) {
assumeType<Message.message>(m);
if(m.message) {
if(++foundMessages > 1) break;
message = m.message;
totalEntities = m.totalEntities;
entities = m.entities;
}
}
if(foundMessages > 1) {
message = undefined;
totalEntities = undefined;
entities = undefined;
}
return {message, entities, totalEntities};
} }
public getGroupsFirstMessage(message: Message.message) { public getGroupsFirstMessage(message: Message.message) {
@ -2537,11 +2542,17 @@ export class AppMessagesManager extends AppManager {
} }
} }
return storage.get(minMid) as Message.message; return this.getMessageFromStorage(storage, minMid) as Message.message;
}
public getMidsByAlbum(groupedId: string, sort: 'asc' | 'desc' = 'asc') {
return getObjectKeysAndSort(this.groupedMessagesStorage[groupedId], sort);
} }
public getMidsByAlbum(grouped_id: string, sort: 'asc' | 'desc' = 'asc') { public getMessagesByAlbum(groupedId: string) {
return getObjectKeysAndSort(this.groupedMessagesStorage[grouped_id], sort); const mids = this.getMidsByAlbum(groupedId, 'asc');
const storage = this.groupedMessagesStorage[groupedId];
return mids.map((mid) => this.getMessageFromStorage(storage, mid) as Message.message);
} }
public getMidsByMessage(message: Message) { public getMidsByMessage(message: Message) {
@ -3541,22 +3552,10 @@ export class AppMessagesManager extends AppManager {
}); });
} }
private handleNewMessage(peerId: PeerId, mid: number) { private handleNewMessage(message: MyMessage) {
(this.newMessagesToHandle[peerId] ??= new Set()).add(mid); this.rootScope.dispatchEvent('history_multiappend', message);
// if(!this.newMessagesHandleTimeout) {
// this.newMessagesHandleTimeout = ctx.setTimeout(this.handleNewMessages, 0);
// }
this.handleNewMessages();
} }
private handleNewMessages = () => {
clearTimeout(this.newMessagesHandleTimeout);
this.newMessagesHandleTimeout = 0;
this.rootScope.dispatchEvent('history_multiappend', this.newMessagesToHandle);
this.newMessagesToHandle = {};
};
private handleNewDialogs = () => { private handleNewDialogs = () => {
let newMaxSeenId = 0; let newMaxSeenId = 0;
const obj = this.newDialogsToHandle; const obj = this.newDialogsToHandle;
@ -3589,12 +3588,9 @@ export class AppMessagesManager extends AppManager {
} }
if(this.newDialogsHandlePromise) return this.newDialogsHandlePromise; if(this.newDialogsHandlePromise) return this.newDialogsHandlePromise;
return this.newDialogsHandlePromise = new Promise<void>((resolve) => { return this.newDialogsHandlePromise = pause(0).then(() => {
setTimeout(() => { this.newDialogsHandlePromise = undefined;
resolve(); this.handleNewDialogs();
this.newDialogsHandlePromise = undefined;
this.handleNewDialogs();
}, 0);
}); });
} }
@ -4144,7 +4140,7 @@ export class AppMessagesManager extends AppManager {
// commented to render the message if it's been sent faster than history_append came to main thread // commented to render the message if it's been sent faster than history_append came to main thread
// if(!pendingMessage) { // if(!pendingMessage) {
this.handleNewMessage(peerId, message.mid); this.handleNewMessage(message);
// } // }
if(isLocalThreadUpdate) { if(isLocalThreadUpdate) {
@ -4749,7 +4745,7 @@ export class AppMessagesManager extends AppManager {
} else { } else {
const pendingMessage = this.checkPendingMessage(message); const pendingMessage = this.checkPendingMessage(message);
if(!pendingMessage) { if(!pendingMessage) {
this.rootScope.dispatchEvent('scheduled_new', {peerId, mid: message.mid}); this.rootScope.dispatchEvent('scheduled_new', message as Message.message);
} }
} }
}; };
@ -4826,7 +4822,7 @@ export class AppMessagesManager extends AppManager {
if(randomId) { if(randomId) {
const pendingData = this.pendingByRandomId[randomId]; const pendingData = this.pendingByRandomId[randomId];
if(pendingMessage = this.finalizePendingMessage(randomId, message)) { if(pendingMessage = this.finalizePendingMessage(randomId, message)) {
this.rootScope.dispatchEvent('history_update', {storageKey: pendingData.storage.key, peerId: message.peerId, mid: message.mid, message, sequential: pendingData.sequential}); this.rootScope.dispatchEvent('history_update', {storageKey: pendingData.storage.key, message, sequential: pendingData.sequential});
} }
delete this.pendingByMessageId[message.mid]; delete this.pendingByMessageId[message.mid];
@ -5175,7 +5171,7 @@ export class AppMessagesManager extends AppManager {
} }
public getMessageWithReplies(message: Message.message) { public getMessageWithReplies(message: Message.message) {
return this.filterMessages(message, message => !!(message as Message.message).replies)[0] as any; return this.filterMessages(message, (message) => !!(message as Message.message).replies)[0] as any;
} }
public getMessageWithCommentReplies(message: Message.message) { public getMessageWithCommentReplies(message: Message.message) {
@ -5211,7 +5207,7 @@ export class AppMessagesManager extends AppManager {
// if there is no id - then request by first id because cannot request by id 0 with backLimit // if there is no id - then request by first id because cannot request by id 0 with backLimit
const historyResult = await this.getHistory(peerId, slice[0] ?? 1, 0, 50, threadId); const historyResult = await this.getHistory(peerId, slice[0] ?? 1, 0, 50, threadId);
for(let i = 0, length = historyResult.history.length; i < length; ++i) { for(let i = 0, length = historyResult.history.length; i < length; ++i) {
this.handleNewMessage(peerId, historyResult.history[i]); this.handleNewMessage(this.getMessageByPeer(peerId, historyResult.history[i]));
} }
return {isBottomEnd: historyStorage.history.slice.isEnd(SliceEnd.Bottom)}; return {isBottomEnd: historyStorage.history.slice.isEnd(SliceEnd.Bottom)};
@ -5531,70 +5527,67 @@ export class AppMessagesManager extends AppManager {
return this.fetchSingleMessagesPromise; return this.fetchSingleMessagesPromise;
} }
return this.fetchSingleMessagesPromise = new Promise((resolve) => { return this.fetchSingleMessagesPromise = pause(0).then(() => {
setTimeout(() => { const requestPromises: Promise<void>[] = [];
const requestPromises: Promise<void>[] = [];
for(const [peerId, map] of this.needSingleMessages) {
for(const [peerId, map] of this.needSingleMessages) { const mids = [...map.keys()];
const mids = [...map.keys()]; const msgIds: InputMessage[] = mids.map((mid) => {
const msgIds: InputMessage[] = mids.map((mid) => { return {
return { _: 'inputMessageID',
_: 'inputMessageID', id: getServerMessageId(mid)
id: getServerMessageId(mid) };
}; });
let promise: Promise<MethodDeclMap['channels.getMessages']['res'] | MethodDeclMap['messages.getMessages']['res']>;
if(peerId.isAnyChat() && this.appPeersManager.isChannel(peerId)) {
promise = this.apiManager.invokeApiSingle('channels.getMessages', {
channel: this.appChatsManager.getChannelInput(peerId.toChatId()),
id: msgIds
}); });
} else {
let promise: Promise<MethodDeclMap['channels.getMessages']['res'] | MethodDeclMap['messages.getMessages']['res']>; promise = this.apiManager.invokeApiSingle('messages.getMessages', {
if(peerId.isAnyChat() && this.appPeersManager.isChannel(peerId)) { id: msgIds
promise = this.apiManager.invokeApiSingle('channels.getMessages', { });
channel: this.appChatsManager.getChannelInput(peerId.toChatId()), }
id: msgIds
});
} else {
promise = this.apiManager.invokeApiSingle('messages.getMessages', {
id: msgIds
});
}
const after = promise.then((getMessagesResult) => {
assumeType<Exclude<MessagesMessages.messagesMessages, MessagesMessages.messagesMessagesNotModified>>(getMessagesResult);
this.appUsersManager.saveApiUsers(getMessagesResult.users); const after = promise.then((getMessagesResult) => {
this.appChatsManager.saveApiChats(getMessagesResult.chats); assumeType<Exclude<MessagesMessages.messagesMessages, MessagesMessages.messagesMessagesNotModified>>(getMessagesResult);
const messages = this.saveMessages(getMessagesResult.messages);
for(let i = 0; i < messages.length; ++i) { this.appUsersManager.saveApiUsers(getMessagesResult.users);
const message = messages[i]; this.appChatsManager.saveApiChats(getMessagesResult.chats);
if(!message) { const messages = this.saveMessages(getMessagesResult.messages);
continue;
}
const mid = generateMessageId(message.id); for(let i = 0; i < messages.length; ++i) {
const promise = map.get(mid); const message = messages[i];
promise.resolve(message); if(!message) {
map.delete(mid); continue;
} }
if(map.size) { const mid = generateMessageId(message.id);
for(const [mid, promise] of map) { const promise = map.get(mid);
promise.resolve(this.generateEmptyMessage(mid)); promise.resolve(message);
} map.delete(mid);
}
if(map.size) {
for(const [mid, promise] of map) {
promise.resolve(this.generateEmptyMessage(mid));
} }
}).finally(() => { }
this.rootScope.dispatchEvent('messages_downloaded', {peerId, mids}); }).finally(() => {
}); this.rootScope.dispatchEvent('messages_downloaded', {peerId, mids});
});
requestPromises.push(after);
} requestPromises.push(after);
}
this.needSingleMessages.clear(); this.needSingleMessages.clear();
Promise.all(requestPromises).finally(() => { return Promise.all(requestPromises).then(noop, noop).then(() => {
this.fetchSingleMessagesPromise = null; this.fetchSingleMessagesPromise = null;
if(this.needSingleMessages.size) this.fetchSingleMessages(); if(this.needSingleMessages.size) this.fetchSingleMessages();
resolve(); });
});
}, 0);
}); });
} }
@ -5745,11 +5738,6 @@ export class AppMessagesManager extends AppManager {
} }
storage.delete(mid); storage.delete(mid);
const peerMessagesToHandle = this.newMessagesToHandle[peerId];
if(peerMessagesToHandle && peerMessagesToHandle.has(mid)) {
peerMessagesToHandle.delete(mid);
}
} }
if(history.albums) { if(history.albums) {

23
src/lib/appManagers/utils/messages/getAlbumText.ts

@ -0,0 +1,23 @@
import assumeType from "../../../../helpers/assumeType";
import { Message, MessageEntity } from "../../../../layer";
export default function getAlbumText(messages: Message.message[]) {
let foundMessages = 0, message: string, totalEntities: MessageEntity[], entities: MessageEntity[];
for(const m of messages) {
assumeType<Message.message>(m);
if(m.message) {
if(++foundMessages > 1) break;
message = m.message;
totalEntities = m.totalEntities;
entities = m.entities;
}
}
if(foundMessages > 1) {
message = undefined;
totalEntities = undefined;
entities = undefined;
}
return {message, entities, totalEntities};
}

10
src/lib/rootScope.ts

@ -15,12 +15,12 @@ import type { GroupCallId } from "./appManagers/appGroupCallsManager";
import type { AppManagers } from "./appManagers/managers"; import type { AppManagers } from "./appManagers/managers";
import type { State } from "../config/state"; import type { State } from "../config/state";
import type { Progress } from "./appManagers/appDownloadManager"; import type { Progress } from "./appManagers/appDownloadManager";
import type { CallId } from "./appManagers/appCallsManager";
import { NULL_PEER_ID, UserAuth } from "./mtproto/mtproto_config"; import { NULL_PEER_ID, UserAuth } from "./mtproto/mtproto_config";
import EventListenerBase from "../helpers/eventListenerBase"; import EventListenerBase from "../helpers/eventListenerBase";
import { MOUNT_CLASS_TO } from "../config/debug"; import { MOUNT_CLASS_TO } from "../config/debug";
import MTProtoMessagePort from "./mtproto/mtprotoMessagePort"; import MTProtoMessagePort from "./mtproto/mtprotoMessagePort";
import { IS_WORKER } from "../helpers/context"; import { IS_WORKER } from "../helpers/context";
import { CallId } from "./appManagers/appCallsManager";
export type BroadcastEvents = { export type BroadcastEvents = {
'chat_full_update': ChatId, 'chat_full_update': ChatId,
@ -58,10 +58,10 @@ export type BroadcastEvents = {
// 'dialog_order': {dialog: Dialog, pos: number}, // 'dialog_order': {dialog: Dialog, pos: number},
'dialogs_multiupdate': {[peerId: PeerId]: Dialog}, 'dialogs_multiupdate': {[peerId: PeerId]: Dialog},
'history_append': {storageKey: MessagesStorageKey, peerId: PeerId, mid: number}, 'history_append': {storageKey: MessagesStorageKey, message: Message.message},
'history_update': {storageKey: MessagesStorageKey, peerId: PeerId, mid: number, message: MyMessage, sequential?: boolean}, 'history_update': {storageKey: MessagesStorageKey, message: MyMessage, sequential?: boolean},
'history_reply_markup': {peerId: PeerId}, 'history_reply_markup': {peerId: PeerId},
'history_multiappend': AppMessagesManager['newMessagesToHandle'], 'history_multiappend': MyMessage,
'history_delete': {peerId: PeerId, msgs: Set<number>}, 'history_delete': {peerId: PeerId, msgs: Set<number>},
'history_forbidden': PeerId, 'history_forbidden': PeerId,
'history_reload': PeerId, 'history_reload': PeerId,
@ -78,7 +78,7 @@ export type BroadcastEvents = {
'replies_updated': Message.message, 'replies_updated': Message.message,
'scheduled_new': {peerId: PeerId, mid: number}, 'scheduled_new': Message.message,
'scheduled_delete': {peerId: PeerId, mids: number[]}, 'scheduled_delete': {peerId: PeerId, mids: number[]},
'album_edit': {peerId: PeerId, groupId: string, deletedMids: number[], messages: Message.message[]}, 'album_edit': {peerId: PeerId, groupId: string, deletedMids: number[], messages: Message.message[]},

63
src/tests/splitString.test.ts

@ -0,0 +1,63 @@
import pause from "../helpers/schedulers/pause";
import splitStringByLength from "../helpers/string/splitStringByLength";
import { MessageEntity } from "../layer";
const text = 'abc def ghi jkl mno pqr stu vwx yz';
// const text = 'abcdefghijklmnopqrstuvwxyz';
const entities: MessageEntity[] = [];
const maxLength = 3;
const parts = ['abc def ghi', 'jkl mno pqr', 'stu vwx yz'];
async function split(str: string, maxLength: number, entities: MessageEntity[]) {
if(str.length <= maxLength) return [str];
const delimiter = ' ';
const out: {part: string, entities: MessageEntity[]}[] = [];
let offset = 0;
while(str.length) {
const isEnd = (offset + maxLength) >= str.length;
const sliced = str.slice(offset, offset + maxLength);
if(!sliced.length) {
break;
}
const delimiterIndex = !isEnd ? sliced.lastIndexOf(delimiter) : -1;
console.log(`sliced='${sliced}'`);
let good: string;
if(delimiterIndex !== -1) {
offset += delimiter.length;
good = sliced.slice(0, delimiterIndex);
} else {
good = sliced;
}
if(!good.length) {
continue;
}
offset += good.length;
out.push({part: good, entities: []});
console.log(`'${good}'`);
// await pause(1000);
}
return out;
}
describe('Split string', () => {
const splitted = split(text, maxLength, []);
// console.log(parts, splitted);
test('parts', () => {
expect(1).toEqual(1);
});
test('a', async() => {
console.log(await splitted);
});
// test('')
});
Loading…
Cancel
Save