Browse Source

Merge branch 'text-split'

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

4
src/components/chat/input.ts

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

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

@ -21,6 +21,7 @@ import PopupPeer, { PopupPeerButtonCallbackCheckboxes, PopupPeerCheckboxOptions @@ -21,6 +21,7 @@ import PopupPeer, { PopupPeerButtonCallbackCheckboxes, PopupPeerCheckboxOptions
import ButtonCorner from "../../buttonCorner";
import { attachClickEvent } from "../../../helpers/dom/clickEvent";
import PeerProfile from "../../peerProfile";
import { Message } from "../../../layer";
const historiesStorage: {
[peerId: PeerId]: Partial<{
@ -156,10 +157,8 @@ export default class AppSharedMediaTab extends SliderSuperTab { @@ -156,10 +157,8 @@ export default class AppSharedMediaTab extends SliderSuperTab {
}
});
this.listenerSetter.add(rootScope)('history_multiappend', (msgIdsByPeer) => {
for(const peerId in msgIdsByPeer) {
this.renderNewMessages(peerId.toPeerId(), Array.from(msgIdsByPeer[peerId]));
}
this.listenerSetter.add(rootScope)('history_multiappend', (message) => {
this.renderNewMessages(message);
});
this.listenerSetter.add(rootScope)('history_delete', ({peerId, msgs}) => {
@ -168,7 +167,7 @@ export default class AppSharedMediaTab extends SliderSuperTab { @@ -168,7 +167,7 @@ export default class AppSharedMediaTab extends SliderSuperTab {
// Calls when message successfully sent and we have an id
this.listenerSetter.add(rootScope)('message_sent', ({message}) => {
this.renderNewMessages(message.peerId, [message.mid]);
this.renderNewMessages(message);
});
//this.container.prepend(this.closeBtn.parentElement);
@ -321,14 +320,12 @@ export default class AppSharedMediaTab extends SliderSuperTab { @@ -321,14 +320,12 @@ export default class AppSharedMediaTab extends SliderSuperTab {
//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
const {peerId} = message;
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) {
const inputFilter = mediaTab.inputFilter;
const history = historiesStorage[peerId][inputFilter];
@ -336,7 +333,7 @@ export default class AppSharedMediaTab extends SliderSuperTab { @@ -336,7 +333,7 @@ export default class AppSharedMediaTab extends SliderSuperTab {
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) {
history.unshift(...filtered.map((message) => ({mid: message.mid, peerId: message.peerId})));

17
src/components/wrappers/album.ts

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

2
src/helpers/object/getObjectKeysAndSort.ts

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

346
src/lib/appManagers/appMessagesManager.ts

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

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

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

63
src/tests/splitString.test.ts

@ -0,0 +1,63 @@ @@ -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