From 045199ccccd6e40f142b723ad2ea6192e2ae714e Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Mon, 20 Jun 2022 19:06:02 +0400 Subject: [PATCH] Fix saving scroll once again Fix detecting pinned messages position --- src/components/chat/bubbleGroups.ts | 6 +- src/components/chat/bubbles.ts | 6 +- src/components/chat/chat.ts | 2 +- src/components/chat/input.ts | 10 +++- src/components/chat/pinnedMessage.ts | 37 +++++++----- src/components/chat/topbar.ts | 18 ++---- src/helpers/scrollSaver.ts | 88 +++++++++++++++------------- 7 files changed, 92 insertions(+), 75 deletions(-) diff --git a/src/components/chat/bubbleGroups.ts b/src/components/chat/bubbleGroups.ts index fe9b6ec9..9e11ea0d 100644 --- a/src/components/chat/bubbleGroups.ts +++ b/src/components/chat/bubbleGroups.ts @@ -80,6 +80,10 @@ class BubbleGroup { return this.firstItem.timestamp; } + get firstMid() { + return this.firstItem.mid; + } + get firstItem() { return this.items[this.items.length - 1]; } @@ -145,7 +149,7 @@ class BubbleGroup { } if(items.length === 1) { - insertInDescendSortedArray(this.groups.groups, this, 'firstTimestamp'); + insertInDescendSortedArray(this.groups.groups, this, 'firstMid'); } } diff --git a/src/components/chat/bubbles.ts b/src/components/chat/bubbles.ts index 5a4dc45a..5206baf6 100644 --- a/src/components/chat/bubbles.ts +++ b/src/components/chat/bubbles.ts @@ -944,7 +944,7 @@ export default class ChatBubbles { } private createScrollSaver(reverse = true) { - const scrollSaver = new ScrollSaver(this.scrollable, '.bubbles-group .bubble', reverse); + const scrollSaver = new ScrollSaver(this.scrollable, '.bubble:not(.is-date)', reverse); return scrollSaver; } @@ -2745,6 +2745,10 @@ export default class ChatBubbles { this.attachPlaceholderOnRender(); } + if(!isTarget && this.chat.type === 'chat') { + this.chat.topbar.pinnedMessage.setCorrectIndex(0); + } + this.container.classList.toggle('has-groups', !!Object.keys(this.dateMessages).length); log.warn('mounted chat', this.chatInner === chatInner, this.chatInner.parentElement, performance.now() - perf); diff --git a/src/components/chat/chat.ts b/src/components/chat/chat.ts index 321007b4..3b239c9e 100644 --- a/src/components/chat/chat.ts +++ b/src/components/chat/chat.ts @@ -422,7 +422,7 @@ export default class Chat extends EventListenerBase<{ this.selection.cleanup(); // TODO: REFACTOR !!!!!! } - public async setPeer(peerId: PeerId, lastMsgId?: number, startParam?: string) { + public setPeer(peerId: PeerId, lastMsgId?: number, startParam?: string) { if(!peerId) { this.inited = undefined; } else if(!this.inited) { diff --git a/src/components/chat/input.ts b/src/components/chat/input.ts index c830f103..bf4025e1 100644 --- a/src/components/chat/input.ts +++ b/src/components/chat/input.ts @@ -1386,6 +1386,7 @@ export default class ChatInput { } private filterAttachMenuButtons() { + if(!this.attachMenuButtons) return; const {peerId, threadId} = this.chat; return filterAsync(this.attachMenuButtons, (button) => { return button.verify(peerId, threadId); @@ -1406,7 +1407,7 @@ export default class ChatInput { this.updateMessageInputPlaceholder(placeholderKey); - this.attachMenuButtons.forEach((button) => { + this.attachMenuButtons && this.attachMenuButtons.forEach((button) => { button.element.classList.toggle('hide', !visible.includes(button)); }); @@ -1421,8 +1422,11 @@ export default class ChatInput { } } - attachMenu.toggleAttribute('disabled', !visible.length); - attachMenu.classList.toggle('btn-disabled', !visible.length); + if(attachMenu) { + attachMenu.toggleAttribute('disabled', !visible.length); + attachMenu.classList.toggle('btn-disabled', !visible.length); + } + this.updateSendBtn(); } diff --git a/src/components/chat/pinnedMessage.ts b/src/components/chat/pinnedMessage.ts index 2979dabc..416bcfff 100644 --- a/src/components/chat/pinnedMessage.ts +++ b/src/components/chat/pinnedMessage.ts @@ -4,8 +4,6 @@ * https://github.com/morethanwords/tweb/blob/master/LICENSE */ -import type { AppMessagesManager } from "../../lib/appManagers/appMessagesManager"; -import type { AppPeersManager } from "../../lib/appManagers/appPeersManager"; import type ChatTopbar from "./topbar"; import PopupPinMessage from "../popups/unpinMessage"; import PinnedContainer from "./pinnedContainer"; @@ -24,6 +22,7 @@ import debounce from "../../helpers/schedulers/debounce"; import throttle from "../../helpers/schedulers/throttle"; import { AppManagers } from "../../lib/appManagers/managers"; import { Message } from "../../layer"; +import { logger } from "../../lib/logger"; class AnimatedSuper { static DURATION = 200; @@ -253,14 +252,19 @@ export default class ChatPinnedMessage { private setPinnedMessage: () => void; - private isStatic = false; + private isStatic: boolean; - private debug = false; + private debug: boolean; public setCorrectIndexThrottled: (lastScrollDirection?: number) => void; + + private log: ReturnType; constructor(private topbar: ChatTopbar, private chat: Chat, private managers: AppManagers) { this.listenerSetter = new ListenerSetter(); + this.log = logger('PM'); + this.debug = true; + this.isStatic = false; const dAC = new ReplyContainer('pinned-message'); this.pinnedMessageContainer = new PinnedContainer({ @@ -361,14 +365,14 @@ export default class ChatPinnedMessage { //const perf = performance.now(); let el = this.chat.bubbles.getBubbleByPoint('bottom'); - //this.chat.log('[PM]: setCorrectIndex: get last element perf:', performance.now() - perf, el); + //this.log('setCorrectIndex: get last element perf:', performance.now() - perf, el); if(!el) return; //return; const mid = el.dataset.mid; if(el && mid !== undefined) { - //this.chat.log('[PM]: setCorrectIndex will test mid:', mid); + //this.log('setCorrectIndex will test mid:', mid); this.testMid(+mid, lastScrollDirection); } } @@ -379,7 +383,7 @@ export default class ChatPinnedMessage { //if(lastScrollDirection !== undefined) return; if(this.hidden) return; - //this.chat.log('[PM]: testMid', mid); + //this.log('testMid', mid); let currentIndex: number = this.mids.findIndex((_mid) => _mid <= mid); if(currentIndex !== -1 && !this.isNeededMore(currentIndex)) { @@ -401,7 +405,7 @@ export default class ChatPinnedMessage { currentIndex = 0; } */ - //this.chat.log('[PM]: testMid: pinned currentIndex', currentIndex, mid); + //this.log('testMid: pinned currentIndex', currentIndex, mid); const changed = this.pinnedIndex !== currentIndex; if(changed) { @@ -431,6 +435,9 @@ export default class ChatPinnedMessage { this.loading = true; try { + const log = this.debug ? this.log.bindPrefix('getCurrentIndex') : undefined; + log && log('start', mid, correctAfter); + let gotRest = false; const promises = [ this.managers.appMessagesManager.getSearch({ @@ -484,9 +491,9 @@ export default class ChatPinnedMessage { this.loadedTop = (this.offsetIndex + this.mids.length) === this.count; this.loadedBottom = !this.offsetIndex; - this.debug && this.chat.log('[PM]: getCurrentIndex result:', mid, result, backLimited, this.offsetIndex, this.loadedTop, this.loadedBottom); + log && log('result', mid, result, backLimited, this.offsetIndex, this.loadedTop, this.loadedBottom); } catch(err) { - this.chat.log.error('[PM]: getCurrentIndex error', err); + this.log.error('getCurrentIndex error', err); } this.loading = false; @@ -528,7 +535,7 @@ export default class ChatPinnedMessage { public async handleFollowingPinnedMessage() { this.locked = true; - this.debug && this.chat.log('[PM]: handleFollowingPinnedMessage'); + this.debug && this.log('handleFollowingPinnedMessage'); try { this.setScrollDownListener(); @@ -544,16 +551,16 @@ export default class ChatPinnedMessage { await this.getCurrentIndexPromise; } - this.debug && this.chat.log('[PM]: handleFollowingPinnedMessage: unlock'); + this.debug && this.log('handleFollowingPinnedMessage: unlock'); this.locked = false; /* // подождём, пока скролл остановится setTimeout(() => { - this.chat.log('[PM]: handleFollowingPinnedMessage: unlock'); + this.log('handleFollowingPinnedMessage: unlock'); this.locked = false; }, 50); */ } catch(err) { - this.chat.log.error('[PM]: handleFollowingPinnedMessage error:', err); + this.log.error('handleFollowingPinnedMessage error:', err); this.locked = false; this.waitForScrollBottom = false; @@ -603,7 +610,7 @@ export default class ChatPinnedMessage { const fromTop = pinnedIndex > this.wasPinnedIndex; - this.debug && this.chat.log('[PM]: setPinnedMessage: fromTop', fromTop, pinnedIndex, this.wasPinnedIndex); + this.debug && this.log('setPinnedMessage: fromTop', fromTop, pinnedIndex, this.wasPinnedIndex); const writeTo = this.animatedSubtitle.getRow(pinnedIndex); const writeMediaTo = this.animatedMedia.getRow(pinnedIndex); diff --git a/src/components/chat/topbar.ts b/src/components/chat/topbar.ts index d3d3999f..43b3364a 100644 --- a/src/components/chat/topbar.ts +++ b/src/components/chat/topbar.ts @@ -697,13 +697,14 @@ export default class ChatTopbar { newAvatar = this.constructAvatar(); } - const [isBroadcast, isAnyChat, chat, _, setTitleCallback, setStatusCallback] = await Promise.all([ + const [isBroadcast, isAnyChat, chat, _, setTitleCallback, setStatusCallback, state] = await Promise.all([ this.managers.appPeersManager.isBroadcast(peerId), this.managers.appPeersManager.isAnyChat(peerId), peerId.isAnyChat() ? this.managers.appChatsManager.getChat(peerId.toChatId()) : undefined, newAvatar ? newAvatar.updateWithOptions({peerId}) : undefined, this.setTitleManual(), this.setPeerStatusManual(true), + apiManagerProxy.getState() ]); return () => { @@ -726,7 +727,6 @@ export default class ChatTopbar { this.verifyButtons(); - const middleware = this.chat.bubbles.getMiddleware(); if(this.pinnedMessage) { // * replace with new one if(this.chat.type === 'chat') { if(this.chat.wasAlreadyUsed) { // * change @@ -737,15 +737,7 @@ export default class ChatTopbar { this.pinnedMessage = newPinnedMessage; } - apiManagerProxy.getState().then((state) => { - if(!middleware()) return; - - this.pinnedMessage.hidden = !!state.hiddenPinnedMessages[peerId]; - - if(!isTarget) { - this.pinnedMessage.setCorrectIndex(0); - } - }); + this.pinnedMessage.hidden = !!state.hiddenPinnedMessages[peerId]; } else if(this.chat.type === 'discussion') { this.pinnedMessage.pinnedMid = this.chat.threadId; this.pinnedMessage.count = 1; @@ -756,9 +748,7 @@ export default class ChatTopbar { setTitleCallback(); setStatusCallback && setStatusCallback(); - fastRaf(() => { - this.setMutedState(); - }); + this.setMutedState(); this.container.classList.remove('hide'); }; diff --git a/src/helpers/scrollSaver.ts b/src/helpers/scrollSaver.ts index 08160f07..4dfc7b1a 100644 --- a/src/helpers/scrollSaver.ts +++ b/src/helpers/scrollSaver.ts @@ -12,7 +12,7 @@ import reflowScrollableElement from "./dom/reflowScrollableElement"; export default class ScrollSaver { private scrollHeight: number; - // private scrollHeightMinusTop: number; + private scrollHeightMinusTop: number; private scrollTop: number; private clientHeight: number; private anchor: HTMLElement; @@ -60,6 +60,14 @@ export default class ScrollSaver { } } + if(!rect) { + const bubble = bubbles[0]; + if(bubble) { + rect = bubble.getBoundingClientRect(); + anchor = bubble; + } + } + return {rect, anchor}; } @@ -83,7 +91,7 @@ export default class ScrollSaver { this.scrollHeight = scrollHeight; this.scrollTop = scrollTop; this.clientHeight = clientHeight; - // this.scrollHeightMinusTop = this.reverse ? scrollHeight - scrollTop : scrollTop; + this.scrollHeightMinusTop = this.reverse ? scrollHeight - scrollTop : scrollTop; //this.chatInner.style.paddingTop = padding + 'px'; /* if(reverse) { @@ -113,13 +121,13 @@ export default class ScrollSaver { const {scrollTop, scrollHeight} = this.scrollable; this.scrollHeight = scrollHeight; - // if(!this.anchor.parentElement) { // fallback to old method if element has disappeared (e.g. edited) - // this._restore(useReflow); - // return; - // } - - if(!this.anchor.parentElement) { // try to find new anchor + if(!this.anchor?.parentElement) { // try to find new anchor this.findAndSetAnchor(); + + if(!this.anchor) { // fallback to old method if smth is really strange + this._restore(useReflow); + return; + } } const rect = this.rect; @@ -129,47 +137,47 @@ export default class ScrollSaver { // console.warn('scroll restore', rect, diff, newRect); } - // public _restore(useReflow?: boolean) { - // const {scrollHeightMinusTop: previousScrollHeightMinusTop, scrollable} = this; - // // if(previousScrollHeightMinusTop === undefined) { - // // throw new Error('scroll was not saved'); - // // } + public _restore(useReflow?: boolean) { + const {scrollHeightMinusTop: previousScrollHeightMinusTop, scrollable} = this; + // if(previousScrollHeightMinusTop === undefined) { + // throw new Error('scroll was not saved'); + // } - // // const scrollHeight = container.scrollHeight; - // const scrollHeight = this.scrollHeight; - // // if(scrollHeight === this.scrollHeight) { - // // return; - // // } + // const scrollHeight = container.scrollHeight; + const scrollHeight = this.scrollHeight; + // if(scrollHeight === this.scrollHeight) { + // return; + // } - // // this.scrollHeight = scrollHeight; + // this.scrollHeight = scrollHeight; - // /* const scrollHeight = container.scrollHeight; - // const addedHeight = scrollHeight - previousScrollHeight; + /* const scrollHeight = container.scrollHeight; + const addedHeight = scrollHeight - previousScrollHeight; - // this.chatInner.style.paddingTop = (10000 - addedHeight) + 'px'; */ - // /* const scrollHeight = scrollHeight; - // const addedHeight = scrollHeight - previousScrollHeight; + this.chatInner.style.paddingTop = (10000 - addedHeight) + 'px'; */ + /* const scrollHeight = scrollHeight; + const addedHeight = scrollHeight - previousScrollHeight; - // this.chatInner.style.paddingTop = (padding - addedHeight) + 'px'; + this.chatInner.style.paddingTop = (padding - addedHeight) + 'px'; - // //const newScrollTop = reverse ? scrollHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop; - // const newScrollTop = reverse ? scrollHeight - addedHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop; - // this.log('performHistoryResult: will set scrollTop', - // previousScrollHeightMinusTop, scrollHeight, - // newScrollTop, container.container.clientHeight); */ - // //const newScrollTop = reverse ? scrollHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop; - // const newScrollTop = this.reverse ? scrollHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop; + //const newScrollTop = reverse ? scrollHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop; + const newScrollTop = reverse ? scrollHeight - addedHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop; + this.log('performHistoryResult: will set scrollTop', + previousScrollHeightMinusTop, scrollHeight, + newScrollTop, container.container.clientHeight); */ + //const newScrollTop = reverse ? scrollHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop; + const newScrollTop = this.reverse ? scrollHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop; - // /* if(DEBUG) { - // this.log('performHistoryResult: will set up scrollTop:', newScrollTop, this.isHeavyAnimationInProgress); - // } */ + /* if(DEBUG) { + this.log('performHistoryResult: will set up scrollTop:', newScrollTop, this.isHeavyAnimationInProgress); + } */ - // this.setScrollTop(newScrollTop, useReflow); + this.setScrollTop(newScrollTop, useReflow); - // /* if(DEBUG) { - // this.log('performHistoryResult: have set up scrollTop:', newScrollTop, container.scrollTop, container.scrollHeight, this.isHeavyAnimationInProgress); - // } */ - // } + /* if(DEBUG) { + this.log('performHistoryResult: have set up scrollTop:', newScrollTop, container.scrollTop, container.scrollHeight, this.isHeavyAnimationInProgress); + } */ + } } MOUNT_CLASS_TO && (MOUNT_CLASS_TO.ScrollSaver = ScrollSaver);