From 7b0ab072559fbc2d36fa019c09c7d968737d80d7 Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Sun, 19 Jun 2022 20:18:46 +0400 Subject: [PATCH] Fix broken profile & shared media --- src/components/chat/bubbles.ts | 53 +++++++++++-------- src/components/chat/chat.ts | 89 ++++++++++++++------------------ src/helpers/middlewarePromise.ts | 8 +++ 3 files changed, 79 insertions(+), 71 deletions(-) diff --git a/src/components/chat/bubbles.ts b/src/components/chat/bubbles.ts index e6306669..5a4dc45a 100644 --- a/src/components/chat/bubbles.ts +++ b/src/components/chat/bubbles.ts @@ -32,11 +32,11 @@ import ListenerSetter from "../../helpers/listenerSetter"; import PollElement from "../poll"; import AudioElement from "../audio"; import { ChatInvite, Document, Message, MessageEntity, MessageMedia, MessageReplyHeader, Photo, PhotoSize, ReactionCount, ReplyMarkup, SponsoredMessage, Update, User, WebPage } from "../../layer"; -import { NULL_PEER_ID, REPLIES_PEER_ID } from "../../lib/mtproto/mtproto_config"; +import { BOT_START_PARAM, NULL_PEER_ID, REPLIES_PEER_ID } from "../../lib/mtproto/mtproto_config"; import { FocusDirection, ScrollStartCallbackDimensions } from "../../helpers/fastSmoothScroll"; import useHeavyAnimationCheck, { getHeavyAnimationPromise, dispatchHeavyAnimationEvent, interruptHeavyAnimation } from "../../hooks/useHeavyAnimationCheck"; import { fastRaf, fastRafPromise } from "../../helpers/schedulers"; -import deferredPromise from "../../helpers/cancellablePromise"; +import deferredPromise, { CancellablePromise } from "../../helpers/cancellablePromise"; import RepliesElement from "./replies"; import DEBUG from "../../config/debug"; import { SliceEnd } from "../../helpers/slicedArray"; @@ -251,6 +251,8 @@ export default class ChatBubbles { private updatePlaceholderPosition: () => void; private setPeerOptions: {lastMsgId: number; topMessage: number;}; + private setPeerTempId: number = 0; + // private reactions: Map; constructor( @@ -2522,6 +2524,8 @@ export default class ChatBubbles { } public async setPeer(samePeer: boolean, peerId: PeerId, lastMsgId?: number, startParam?: string): Promise<{cached?: boolean, promise: Chat['setPeerPromise']}> { + const tempId = ++this.setPeerTempId; + if(!peerId) { this.cleanup(true); this.preloader.detach(); @@ -2532,6 +2536,16 @@ export default class ChatBubbles { const log = this.log.bindPrefix('setPeer'); log.warn('start'); + const middleware = () => { + return this.setPeerTempId === tempId; + }; + + const m = middlewarePromise(middleware, PEER_CHANGED_ERROR); + + if(!samePeer) { + await m(this.chat.onChangePeer(m)); + } + /* if(samePeer && this.chat.setPeerPromise) { return {cached: true, promise: this.chat.setPeerPromise}; } */ @@ -2542,8 +2556,8 @@ export default class ChatBubbles { lastMsgId = 0; } - const historyStorage = await this.chat.getHistoryStorage(); - let topMessage = chatType === 'pinned' ? await this.managers.appMessagesManager.getPinnedMessagesMaxId(peerId) : historyStorage.maxId ?? 0; + const historyStorage = await m(this.chat.getHistoryStorage()); + let topMessage = chatType === 'pinned' ? await m(this.managers.appMessagesManager.getPinnedMessagesMaxId(peerId)) : historyStorage.maxId ?? 0; const isTarget = lastMsgId !== undefined; // * this one will fix topMessage for null message in history (e.g. channel comments with only 1 comment and it is a topMessage) @@ -2561,8 +2575,8 @@ export default class ChatBubbles { if(savedPosition) { } else if(topMessage) { - readMaxId = await this.managers.appMessagesManager.getReadMaxIdIfUnread(peerId, this.chat.threadId); - const dialog = await this.managers.appMessagesManager.getDialogOnly(peerId); + readMaxId = await m(this.managers.appMessagesManager.getReadMaxIdIfUnread(peerId, this.chat.threadId)); + const dialog = await m(this.managers.appMessagesManager.getDialogOnly(peerId)); if(/* dialog.unread_count */readMaxId && !samePeer && (!dialog || dialog.unread_count !== 1)) { const foundSlice = historyStorage.history.findSliceOffset(readMaxId); if(foundSlice && foundSlice.slice.isEnd(SliceEnd.Bottom)) { @@ -2580,8 +2594,12 @@ export default class ChatBubbles { const isJump = lastMsgId !== topMessage/* && overrideAdditionMsgId === undefined */; + if(startParam === undefined && await m(this.chat.isStartButtonNeeded())) { + startParam = BOT_START_PARAM; + } + if(samePeer) { - const mounted = await this.getMountedBubble(lastMsgId); + const mounted = await m(this.getMountedBubble(lastMsgId)); if(mounted) { if(isTarget) { this.scrollToBubble(mounted.bubble, 'center'); @@ -2609,7 +2627,7 @@ export default class ChatBubbles { this.replyFollowHistory.length = 0; this.passEntities = { - messageEntityBotCommand: await this.managers.appPeersManager.isAnyGroup(peerId) || await this.managers.appUsersManager.isBot(peerId) + messageEntityBotCommand: await m(this.managers.appPeersManager.isAnyGroup(peerId)) || await m(this.managers.appUsersManager.isBot(peerId)) }; } @@ -2636,13 +2654,9 @@ export default class ChatBubbles { this.destroyResizeObserver(); } - // const oldContainer = this.container; const oldChatInner = this.chatInner; const oldPlaceholderBubble = this.emptyPlaceholderBubble; this.cleanup(); - // this.constructBubbles(); - // const container = this.container; - // const chatInner = this.chatInner;/* = document.createElement('div'); */ const chatInner = this.chatInner = document.createElement('div'); if(samePeer) { chatInner.className = oldChatInner.className; @@ -2667,7 +2681,7 @@ export default class ChatBubbles { let result: Awaited>; if(!savedPosition) { - result = await this.getHistory1(lastMsgId, true, isJump, additionMsgId); + result = await m(this.getHistory1(lastMsgId, true, isJump, additionMsgId)); } else { result = { promise: getHeavyAnimationPromise().then(() => { @@ -2684,10 +2698,8 @@ export default class ChatBubbles { const {promise, cached} = result; - const middleware = this.getMiddleware(); if(!cached && !samePeer) { - await this.chat.finishPeerChange(isTarget, isJump, lastMsgId, startParam); - if(!middleware()) throw PEER_CHANGED_ERROR; + await m(this.chat.finishPeerChange(isTarget, isJump, lastMsgId, startParam)); this.scrollable.container.textContent = ''; // oldContainer.textContent = ''; //oldChatInner.remove(); @@ -2698,14 +2710,13 @@ export default class ChatBubbles { this.ladderDeferred = deferredPromise(); */ animationIntersector.lockGroup(CHAT_ANIMATION_GROUP); - const setPeerPromise = promise.then(async() => { + const setPeerPromise = m(promise).then(async() => { log.warn('promise fulfilled'); - let mountedByLastMsgId = haveToScrollToBubble ? await (lastMsgId ? this.getMountedBubble(lastMsgId) : {bubble: this.getLastBubble()}) : undefined; + let mountedByLastMsgId = haveToScrollToBubble ? await m(lastMsgId ? this.getMountedBubble(lastMsgId) : {bubble: this.getLastBubble()}) : undefined; if(cached && !samePeer) { log.warn('finishing peer change'); - await this.chat.finishPeerChange(isTarget, isJump, lastMsgId, startParam); // * костыль - if(!middleware()) throw PEER_CHANGED_ERROR; + await m(this.chat.finishPeerChange(isTarget, isJump, lastMsgId, startParam)); // * костыль log.warn('finished peer change'); } @@ -2838,7 +2849,7 @@ export default class ChatBubbles { } if(chatType === 'chat') { - const dialog = await this.managers.appMessagesManager.getDialogOnly(peerId); + const dialog = await m(this.managers.appMessagesManager.getDialogOnly(peerId)); if(dialog?.pFlags.unread_mark) { this.managers.appMessagesManager.markDialogUnread(peerId, true); } diff --git a/src/components/chat/chat.ts b/src/components/chat/chat.ts index 11e92819..321007b4 100644 --- a/src/components/chat/chat.ts +++ b/src/components/chat/chat.ts @@ -16,7 +16,7 @@ import ChatContextMenu from "./contextMenu"; import ChatInput from "./input"; import ChatSelection from "./selection"; import ChatTopbar from "./topbar"; -import { BOT_START_PARAM, NULL_PEER_ID, REPLIES_PEER_ID } from "../../lib/mtproto/mtproto_config"; +import { NULL_PEER_ID, REPLIES_PEER_ID } from "../../lib/mtproto/mtproto_config"; import SetTransition from "../singleTransition"; import AppPrivateSearchTab from "../sidebarRight/tabs/search"; import renderImageFromUrl from "../../helpers/dom/renderImageFromUrl"; @@ -32,6 +32,8 @@ import SlicedArray from "../../helpers/slicedArray"; import themeController from "../../helpers/themeController"; import AppSharedMediaTab from "../sidebarRight/tabs/sharedMedia"; import noop from "../../helpers/noop"; +import middlewarePromise from "../../helpers/middlewarePromise"; +import indexOfAndSplice from "../../helpers/array/indexOfAndSplice"; export type ChatType = 'chat' | 'pinned' | 'replies' | 'discussion' | 'scheduled'; @@ -74,6 +76,7 @@ export default class Chat extends EventListenerBase<{ public backgroundTempId: number; public setBackgroundPromise: Promise; public sharedMediaTab: AppSharedMediaTab; + public sharedMediaTabs: AppSharedMediaTab[]; // public renderDarkPattern: () => Promise; public isAnyGroup: boolean; @@ -103,6 +106,7 @@ export default class Chat extends EventListenerBase<{ this.appImManager.chatsContainer.append(this.container); this.backgroundTempId = 0; + this.sharedMediaTabs = []; } public setBackground(url: string, skipAnimation?: boolean): Promise { @@ -388,6 +392,35 @@ export default class Chat extends EventListenerBase<{ this.topbar.cleanup(); this.selection.cleanup(); } + + public async onChangePeer(m: ReturnType) { + const {peerId} = this; + + const searchTab = appSidebarRight.getTab(AppPrivateSearchTab); + if(searchTab) { + searchTab.close(); + } + + const [noForwards, isRestricted, isAnyGroup] = await m(Promise.all([ + this.managers.appPeersManager.noForwards(peerId), + this.managers.appPeersManager.isRestricted(peerId), + this._isAnyGroup(peerId), + this.setAutoDownloadMedia() + ])); + + this.noForwards = noForwards; + this.isRestricted = isRestricted; + this.isAnyGroup = isAnyGroup; + + this.container.classList.toggle('no-forwards', this.noForwards); + + this.sharedMediaTab = appSidebarRight.createSharedMediaTab(); + this.sharedMediaTabs.push(this.sharedMediaTab); + + this.sharedMediaTab.setPeer(peerId, this.threadId); + this.input.clearHelper(); // костыль + this.selection.cleanup(); // TODO: REFACTOR !!!!!! + } public async setPeer(peerId: PeerId, lastMsgId?: number, startParam?: string) { if(!peerId) { @@ -418,59 +451,11 @@ export default class Chat extends EventListenerBase<{ return; } - let sharedMediaTabs: AppSharedMediaTab[], sharedMediaTab = this.sharedMediaTab; - // set new - if(!samePeer) { - const searchTab = appSidebarRight.getTab(AppPrivateSearchTab); - if(searchTab) { - searchTab.close(); - } - - const [noForwards, isRestricted, isAnyGroup] = await Promise.all([ - this.managers.appPeersManager.noForwards(peerId), - this.managers.appPeersManager.isRestricted(peerId), - this._isAnyGroup(peerId), - this.setAutoDownloadMedia() - ]); - - this.noForwards = noForwards; - this.isRestricted = isRestricted; - this.isAnyGroup = isAnyGroup; - - this.container.classList.toggle('no-forwards', this.noForwards); - - sharedMediaTabs = [ - sharedMediaTab, - sharedMediaTab = this.sharedMediaTab = appSidebarRight.createSharedMediaTab() - ]; - - sharedMediaTab.setPeer(peerId, this.threadId); - this.input.clearHelper(); // костыль - this.selection.cleanup(); // TODO: REFACTOR !!!!!! - } - this.peerChanged = samePeer; - if(startParam === undefined && await this.isStartButtonNeeded()) { - startParam = BOT_START_PARAM; - } - const bubblesSetPeerPromise = this.bubbles.setPeer(samePeer, peerId, lastMsgId, startParam); const setPeerPromise = this.setPeerPromise = bubblesSetPeerPromise.then((result) => { - if(!result) { - return; - } - - if(!samePeer) { - sharedMediaTab.setLoadMutex(setPeerPromise); - sharedMediaTab.loadSidebarMedia(true); - } - - return result.promise.catch(noop).finally(() => { - if(sharedMediaTabs) { - sharedMediaTabs.filter((tab) => tab && this.sharedMediaTab !== tab).forEach((tab) => this.destroySharedMediaTab(tab)); - } - }); + return result.promise; }).catch(noop).finally(() => { if(this.setPeerPromise === setPeerPromise) { this.setPeerPromise = null; @@ -481,6 +466,7 @@ export default class Chat extends EventListenerBase<{ } public destroySharedMediaTab(tab = this.sharedMediaTab) { + indexOfAndSplice(this.sharedMediaTabs, tab); tab.destroy(); } @@ -504,6 +490,7 @@ export default class Chat extends EventListenerBase<{ this.cleanup(false); const sharedMediaTab = this.sharedMediaTab; + sharedMediaTab.loadSidebarMedia(true); const callbacksPromise = Promise.all([ this.topbar.finishPeerChange(isTarget), @@ -526,6 +513,8 @@ export default class Chat extends EventListenerBase<{ appSidebarRight.replaceSharedMediaTab(sharedMediaTab); + this.sharedMediaTabs.filter((tab) => tab !== sharedMediaTab).forEach((tab) => this.destroySharedMediaTab(tab)); + this.log.setPrefix('CHAT-' + peerId + '-' + this.type); this.appImManager.dispatchEvent('peer_changed', peerId); diff --git a/src/helpers/middlewarePromise.ts b/src/helpers/middlewarePromise.ts index 6c330268..26d9563b 100644 --- a/src/helpers/middlewarePromise.ts +++ b/src/helpers/middlewarePromise.ts @@ -6,6 +6,14 @@ export default function middlewarePromise(middleware: () => boolean, throwWhat: any = '') { return (promise: T): T => { + if(!(promise instanceof Promise)) { + if(promise instanceof Error) { + throw promise; + } else { + return promise; + } + } + return (promise as any as Promise).then((result) => { if(!middleware()) { throw throwWhat;