From c805347e837ff255b4c8a8f03f796d6fbfa5c0f3 Mon Sep 17 00:00:00 2001 From: Eduard Kuzmenko Date: Sun, 19 Jun 2022 16:49:33 +0400 Subject: [PATCH] Fix jumping scroll Enable reactions & views updates --- src/components/chat/bubbles.ts | 265 ++++++++++++++------------- src/components/chat/messageRender.ts | 8 +- src/components/chat/selection.ts | 2 +- src/helpers/scrollSaver.ts | 150 ++++++++++----- src/lib/rootScope.ts | 2 - src/scss/partials/_chatBubble.scss | 3 +- src/test_scroll_saving.js | 16 ++ 7 files changed, 264 insertions(+), 182 deletions(-) create mode 100644 src/test_scroll_saving.js diff --git a/src/components/chat/bubbles.ts b/src/components/chat/bubbles.ts index a72e4fff..e6306669 100644 --- a/src/components/chat/bubbles.ts +++ b/src/components/chat/bubbles.ts @@ -136,6 +136,12 @@ export const STICKY_OFFSET = 3; const SCROLLED_DOWN_THRESHOLD = 300; const PEER_CHANGED_ERROR = new Error('peer changed'); +const DO_NOT_SLICE_VIEWPORT = false; +const DO_NOT_SLICE_VIEWPORT_ON_RENDER = false; +const DO_NOT_UPDATE_MESSAGE_VIEWS = false; +const DO_NOT_UPDATE_MESSAGE_REACTIONS = false; +const DO_NOT_UPDATE_MESSAGE_REPLY = false; + type Bubble = { bubble: HTMLElement, mids: Set, @@ -510,63 +516,50 @@ export default class ChatBubbles { this.safeRenderMessage(message, true, bubble); }); - // if(this.chat.type !== 'scheduled' && false) { - // this.listenerSetter.add(rootScope)('missed_reactions_element', async({message, changedResults}) => { - // if(this.peerId !== message.peerId || !message.reactions || !message.reactions.results.length) { - // return; - // } - - // const bubble = await this.getBubbleByMessage(message); - // if(!bubble) { - // return; - // } - - // if(message.grouped_id) { - // const grouped = await this.getGroupedBubble(message.grouped_id); - // message = grouped.message; - // } - - // this.appendReactionsElementToBubble(bubble, message, changedResults); - // }); - - // this.listenerSetter.add(rootScope)('messages_reactions', async(arr) => { - // let scrollSaver: ScrollSaver; - - // const promises = arr.map(async({message, changedResults}) => { - // if(this.peerId !== message.peerId) { - // return; - // } - - // const bubble = await this.getBubbleByMessage(message); - // if(!bubble) { - // return; - // } - - // if(!scrollSaver) { - // scrollSaver = new ScrollSaver(this.scrollable, true); - // scrollSaver.save(); - // } + if(this.chat.type !== 'scheduled' && !DO_NOT_UPDATE_MESSAGE_REACTIONS/* && false */) { + this.listenerSetter.add(rootScope)('messages_reactions', async(arr) => { + let scrollSaver: ScrollSaver; + + const a = arr.map(async({message, changedResults}) => { + if(this.peerId !== message.peerId) { + return; + } + + const result = await this.getMountedBubble(message.mid, message); + if(!result) { + return; + } + + return {bubble: result.bubble, message, changedResults}; + }); + + let top: number; + (await Promise.all(a)).filter(Boolean).forEach(({bubble, message, changedResults}) => { + if(!scrollSaver) { + scrollSaver = this.createScrollSaver(false); + scrollSaver.save(); + } - // const key = message.peerId + '_' + message.mid; - // const set = REACTIONS_ELEMENTS.get(key); - // if(set) { - // for(const element of set) { - // element.update(message, changedResults); - // } - // } else { - // rootScope.dispatchEvent('missed_reactions_element', {message, changedResults}); - // } - // }); - - // await Promise.all(promises); - - // if(scrollSaver) { - // scrollSaver.restore(); - // } - // }); - // } + const key = message.peerId + '_' + message.mid; + const set = REACTIONS_ELEMENTS.get(key); + if(set) { + for(const element of set) { + element.update(message, changedResults); + } + } else if(!message.reactions || !message.reactions.results.length) { + return; + } else { + this.appendReactionsElementToBubble(bubble, message, message, changedResults); + } + }); + + if(scrollSaver) { + scrollSaver.restore(); + } + }); + } - this.listenerSetter.add(rootScope)('messages_downloaded', async({peerId, mids}) => { + !DO_NOT_UPDATE_MESSAGE_REPLY && this.listenerSetter.add(rootScope)('messages_downloaded', async({peerId, mids}) => { const middleware = this.getMiddleware(); await getHeavyAnimationPromise(); if(!middleware()) return; @@ -885,38 +878,38 @@ export default class ChatBubbles { } }); - // this.listenerSetter.add(rootScope)('messages_views', (arr) => { - // fastRaf(() => { - // let scrollSaver: ScrollSaver; - // for(const {peerId, views, mid} of arr) { - // if(this.peerId !== peerId) return; + !DO_NOT_UPDATE_MESSAGE_VIEWS && this.listenerSetter.add(rootScope)('messages_views', (arr) => { + fastRaf(() => { + let scrollSaver: ScrollSaver; + for(const {peerId, views, mid} of arr) { + if(this.peerId !== peerId) continue; - // const bubble = this.bubbles[mid]; - // if(!bubble) return; + const bubble = this.bubbles[mid]; + if(!bubble) continue; - // const postViewsElements = Array.from(bubble.querySelectorAll('.post-views')) as HTMLElement[]; - // if(postViewsElements.length) { - // const str = formatNumber(views, 1); - // let different = false; - // postViewsElements.forEach((postViews) => { - // if(different || postViews.innerHTML !== str) { - // if(!scrollSaver) { - // scrollSaver = new ScrollSaver(this.scrollable, true); - // scrollSaver.save(); - // } - - // different = true; - // postViews.innerHTML = str; - // } - // }); - // } - // } - - // if(scrollSaver) { - // scrollSaver.restore(); - // } - // }); - // }); + const postViewsElements = Array.from(bubble.querySelectorAll('.post-views')) as HTMLElement[]; + if(!postViewsElements.length) continue; + + const str = formatNumber(views, 1); + let different = false; + postViewsElements.forEach((postViews) => { + if(different || postViews.textContent !== str) { + if(!scrollSaver) { + scrollSaver = this.createScrollSaver(true); + scrollSaver.save(); + } + + different = true; + postViews.textContent = str; + } + }); + } + + if(scrollSaver) { + scrollSaver.restore(); + } + }); + }); this.observer = new SuperIntersectionObserver({root: this.scrollable.container}); @@ -948,6 +941,11 @@ export default class ChatBubbles { return this.chat.peerId; } + private createScrollSaver(reverse = true) { + const scrollSaver = new ScrollSaver(this.scrollable, '.bubbles-group .bubble', reverse); + return scrollSaver; + } + private unreadedObserverCallback = (entry: IntersectionObserverEntry) => { if(entry.isIntersecting) { const target = entry.target as HTMLElement; @@ -1351,8 +1349,12 @@ export default class ChatBubbles { bubble = findUpClassName(target, 'bubble'); } catch(err) {} - if(!bubble) { + if(!bubble && !this.chat.selection.isSelecting) { const avatar = findUpClassName(target, 'user-avatar'); + if(!avatar) { + return; + } + const peerId = avatar.dataset.peerId.toPeerId(); if(peerId !== NULL_PEER_ID) { this.chat.appImManager.setInnerPeer({peerId}); @@ -1803,12 +1805,6 @@ export default class ChatBubbles { } } - // public async getBubbleByMessage(message: Message.message | Message.messageService) { - // if(!(message as Message.message).grouped_id) return this.bubbles[message.mid]; - // const grouped = await this.getGroupedBubble((message as Message.message).grouped_id); - // return grouped?.bubble; - // } - public getBubbleGroupedItems(bubble: HTMLElement) { return Array.from(bubble.querySelectorAll('.grouped-item')) as HTMLElement[]; } @@ -2184,10 +2180,8 @@ export default class ChatBubbles { } public getLastBubble() { - const lastDateGroup = this.getLastDateGroup(); - if(lastDateGroup) { - return lastDateGroup.lastElementChild as HTMLElement; - } + const group = this.bubbleGroups.getLastGroup(); + return group?.lastItem?.bubble; } public scrollToBubble( @@ -2200,13 +2194,16 @@ export default class ChatBubbles { let fallbackToElementStartWhenCentering: HTMLElement; // * if it's a start, then scroll to start of the group - if(bubble && position !== 'end' && whichChild(bubble) === (this.stickyIntersector ? STICKY_OFFSET : 1)/* && this.chat.setPeerPromise */) { - const dateGroup = bubble.parentElement; - // if(whichChild(dateGroup) === 0) { - fallbackToElementStartWhenCentering = dateGroup; - // position = 'start'; - // element = dateGroup; - // } + if(bubble && position !== 'end') { + const item = this.bubbleGroups.getItemByBubble(bubble); + if(item.group.firstItem === item && whichChild(item.group.container) === (this.stickyIntersector ? STICKY_OFFSET : 1)) { + const dateGroup = item.group.container.parentElement; + // if(whichChild(dateGroup) === 0) { + fallbackToElementStartWhenCentering = dateGroup; + // position = 'start'; + // element = dateGroup; + // } + } } // const isLastBubble = this.getLastBubble() === bubble; @@ -2274,18 +2271,18 @@ export default class ChatBubbles { } // ! can't get it by chatInner.lastElementChild because placeholder can be the last... - private getLastDateGroup() { - let lastTime = 0, lastElem: HTMLElement; - for(const i in this.dateMessages) { - const dateMessage = this.dateMessages[i]; - if(dateMessage.firstTimestamp > lastTime) { - lastElem = dateMessage.container; - lastTime = dateMessage.firstTimestamp; - } - } - - return lastElem; - } + // private getLastDateGroup() { + // let lastTime = 0, lastElem: HTMLElement; + // for(const i in this.dateMessages) { + // const dateMessage = this.dateMessages[i]; + // if(dateMessage.firstTimestamp > lastTime) { + // lastElem = dateMessage.container; + // lastTime = dateMessage.firstTimestamp; + // } + // } + + // return lastElem; + // } public async scrollToBubbleIfLast(bubble: HTMLElement) { if(this.getLastBubble() === bubble) { @@ -3205,7 +3202,7 @@ export default class ChatBubbles { const isMessage = message._ === 'message'; const groupedId = isMessage && message.grouped_id; - let albumMids: number[]; + let albumMids: number[], reactionsMessage: Message.message; const albumMustBeRenderedFull = this.chat.type !== 'pinned'; if(groupedId && albumMustBeRenderedFull) { // will render only last album's message @@ -3215,6 +3212,10 @@ export default class ChatBubbles { return; } } + + if(isMessage) { + reactionsMessage = groupedId ? await this.managers.appMessagesManager.getGroupsFirstMessage(message) : message; + } const peerId = this.peerId; // * can't use 'message.pFlags.out' here because this check will be used to define side of message (left-right) @@ -3420,9 +3421,10 @@ export default class ChatBubbles { setInnerHTML(messageDiv, richText); } - const timeSpan = await MessageRender.setTime({ + const timeSpan = MessageRender.setTime({ chatType: this.chat.type, - message + message, + reactionsMessage }); messageDiv.append(timeSpan); bubbleContainer.prepend(messageDiv); @@ -4224,7 +4226,7 @@ export default class ChatBubbles { } if(isMessage) { - this.appendReactionsElementToBubble(bubble, message); + this.appendReactionsElementToBubble(bubble, message, reactionsMessage); } /* if(isMessage) { @@ -4242,13 +4244,12 @@ export default class ChatBubbles { return ret; } - private async appendReactionsElementToBubble(bubble: HTMLElement, message: Message.message, changedResults?: ReactionCount[]) { + private appendReactionsElementToBubble(bubble: HTMLElement, message: Message.message, reactionsMessage: Message.message, changedResults?: ReactionCount[]) { if(this.peerId.isUser()/* || true */) { return; } - const reactionsMessage = await this.managers.appMessagesManager.getGroupsFirstMessage(message); - if(!reactionsMessage.reactions || !reactionsMessage.reactions.results.length) { + if(!reactionsMessage?.reactions || !reactionsMessage.reactions.results.length) { return; } @@ -4268,9 +4269,10 @@ export default class ChatBubbles { let timeSpan: HTMLElement = documentMessageDiv && documentMessageDiv.querySelector('.time'); if(!timeSpan) { - timeSpan = await MessageRender.setTime({ + timeSpan = MessageRender.setTime({ chatType: this.chat.type, - message + message, + reactionsMessage }); } @@ -4299,15 +4301,16 @@ export default class ChatBubbles { } this.log.warn('onRender'); - const scrollSaver = new ScrollSaver(this.scrollable, reverse); + const scrollSaver = this.createScrollSaver(reverse); + scrollSaver.save(); // * let's save scroll position by point before the slicing, not after if(this.getRenderedLength() && !this.chat.setPeerPromise) { const viewportSlice = this.getViewportSlice(); this.deleteViewportSlice(viewportSlice, true); } - scrollSaver.save(); - const saved = scrollSaver.getSaved(); + // scrollSaver.save(); // ! slicing will corrupt scroll position + // const saved = scrollSaver.getSaved(); // const hadScroll = saved.scrollHeight !== saved.clientHeight; (this.messagesQueuePromise || Promise.resolve()).then(() => { @@ -4957,7 +4960,9 @@ export default class ChatBubbles { } public deleteViewportSlice(slice: ReturnType, ignoreScrollSaving?: boolean) { - // return; + if(DO_NOT_SLICE_VIEWPORT_ON_RENDER) { + return; + } const {invisibleTop, invisibleBottom} = slice; const invisible = invisibleTop.concat(invisibleBottom); @@ -4979,7 +4984,7 @@ export default class ChatBubbles { let scrollSaver: ScrollSaver; if(!!invisibleTop.length !== !!invisibleBottom.length && !ignoreScrollSaving) { - scrollSaver = new ScrollSaver(this.scrollable, !!invisibleTop.length); + scrollSaver = this.createScrollSaver(!!invisibleTop.length); scrollSaver.save(); } @@ -4994,7 +4999,7 @@ export default class ChatBubbles { public sliceViewport(ignoreHeavyAnimation?: boolean) { // Safari cannot reset the scroll. - if(IS_SAFARI || (this.isHeavyAnimationInProgress && !ignoreHeavyAnimation)/* || true */) { + if(IS_SAFARI || (this.isHeavyAnimationInProgress && !ignoreHeavyAnimation) || DO_NOT_SLICE_VIEWPORT) { return; } diff --git a/src/components/chat/messageRender.ts b/src/components/chat/messageRender.ts index 2355097c..c5da6c5d 100644 --- a/src/components/chat/messageRender.ts +++ b/src/components/chat/messageRender.ts @@ -35,9 +35,10 @@ export namespace MessageRender { }; */ - export const setTime = async(options: { + export const setTime = (options: { chatType: ChatType, - message: Message.message | Message.messageService + message: Message.message | Message.messageService, + reactionsMessage?: Message.message }) => { const {chatType, message} = options; const date = new Date(message.date * 1000); @@ -83,8 +84,7 @@ export namespace MessageRender { if(message.peer_id._ === 'peerUser'/* && message.reactions?.results?.length */) { hasReactions = true; - reactionsMessage = await rootScope.managers.appMessagesManager.getGroupsFirstMessage(message); - + reactionsMessage = options.reactionsMessage; reactionsElement = new ReactionsElement(); reactionsElement.init(reactionsMessage, 'inline', true); reactionsElement.render(); diff --git a/src/components/chat/selection.ts b/src/components/chat/selection.ts index c89a05f0..67b44b35 100644 --- a/src/components/chat/selection.ts +++ b/src/components/chat/selection.ts @@ -817,7 +817,7 @@ export default class ChatSelection extends AppSelection { } protected getMidsFromGroupContainer(groupContainer: HTMLElement) { - const elements = Array.from(groupContainer.querySelectorAll('.grouped-item')) as HTMLElement[]; + const elements = this.chat.bubbles.getBubbleGroupedItems(groupContainer); if(!elements.length) { elements.push(groupContainer); } diff --git a/src/helpers/scrollSaver.ts b/src/helpers/scrollSaver.ts index 6dbee082..08160f07 100644 --- a/src/helpers/scrollSaver.ts +++ b/src/helpers/scrollSaver.ts @@ -5,14 +5,18 @@ */ import Scrollable from "../components/scrollable"; +import { MOUNT_CLASS_TO } from "../config/debug"; import { IS_SAFARI } from "../environment/userAgent"; +import getVisibleRect from "./dom/getVisibleRect"; 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; + private rect: DOMRect; /** * @@ -21,6 +25,7 @@ export default class ScrollSaver { */ constructor( private scrollable: Scrollable, + private query: string, private reverse: boolean ) { @@ -38,7 +43,39 @@ export default class ScrollSaver { }; } + public findAnchor() { + const {container} = this; + const containerRect = container.getBoundingClientRect(); + const bubbles = Array.from(container.querySelectorAll(this.query)) as HTMLElement[]; + let rect: DOMRect, anchor: HTMLElement; + for(const bubble of bubbles) { + const elementRect = bubble.getBoundingClientRect(); + const visibleRect = getVisibleRect(bubble, container, undefined, elementRect, containerRect); + if(visibleRect) { + rect = elementRect; + anchor = bubble; + // break; // find first + } else if(anchor) { // find last + break; + } + } + + return {rect, anchor}; + } + + public findAndSetAnchor() { + const {rect, anchor} = this.findAnchor(); + this.rect = rect; + this.anchor = anchor; + } + public save() { + this.findAndSetAnchor(); + // console.warn('scroll save', this.anchor, this.rect); + this._save(); + } + + public _save() { const {scrollTop, scrollHeight, clientHeight} = this.container; //previousScrollHeight = scrollHeight; @@ -46,7 +83,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) { @@ -54,60 +91,85 @@ export default class ScrollSaver { } else { previousScrollHeightMinusTop = scrollTop; } */ + } - /* if(DEBUG) { - this.log('performHistoryResult: messagesQueueOnRender, scrollTop:', scrollTop, scrollHeight, previousScrollHeightMinusTop); - } */ + private onRestore(useReflow?: boolean) { + if(IS_SAFARI && useReflow/* && !isAppleMobile */) { // * fix blinking and jumping + reflowScrollableElement(this.container); + } + } + + private setScrollTop(newScrollTop: number, useReflow?: boolean) { + // touchSupport for safari iOS + //isTouchSupported && isApple && (container.container.style.overflow = 'hidden'); + this.scrollable.setScrollTopSilently(this.scrollTop = newScrollTop); + //container.scrollTop = scrollHeight; + //isTouchSupported && isApple && (container.container.style.overflow = ''); + + this.onRestore(useReflow); } public restore(useReflow?: boolean) { - const {container, scrollHeightMinusTop: previousScrollHeightMinusTop, scrollable} = this; - if(previousScrollHeightMinusTop === undefined) { - throw new Error('scroll was not saved'); - } + 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; + // } - const scrollHeight = container.scrollHeight; - if(scrollHeight === this.scrollHeight) { - return; + if(!this.anchor.parentElement) { // try to find new anchor + this.findAndSetAnchor(); } - this.scrollHeight = scrollHeight; + const rect = this.rect; + const newRect = this.anchor.getBoundingClientRect(); + const diff = newRect.bottom - rect.bottom; + this.setScrollTop(scrollTop + diff, useReflow); + // 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'); + // // } - /* const scrollHeight = container.scrollHeight; - const addedHeight = scrollHeight - previousScrollHeight; + // // const scrollHeight = container.scrollHeight; + // const scrollHeight = this.scrollHeight; + // // if(scrollHeight === this.scrollHeight) { + // // return; + // // } + + // // this.scrollHeight = scrollHeight; + + // /* 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); - } */ - - // touchSupport for safari iOS - //isTouchSupported && isApple && (container.container.style.overflow = 'hidden'); - this.scrollable.setScrollTopSilently(this.scrollTop = newScrollTop); - //container.scrollTop = scrollHeight; - //isTouchSupported && isApple && (container.container.style.overflow = ''); - - if(IS_SAFARI && useReflow/* && !isAppleMobile */) { // * fix blinking and jumping - reflowScrollableElement(container); - } + // /* if(DEBUG) { + // this.log('performHistoryResult: will set up scrollTop:', newScrollTop, this.isHeavyAnimationInProgress); + // } */ - /* if(DEBUG) { - this.log('performHistoryResult: have set up scrollTop:', newScrollTop, container.scrollTop, container.scrollHeight, this.isHeavyAnimationInProgress); - } */ + // this.setScrollTop(newScrollTop, useReflow); - return; - } + // /* if(DEBUG) { + // this.log('performHistoryResult: have set up scrollTop:', newScrollTop, container.scrollTop, container.scrollHeight, this.isHeavyAnimationInProgress); + // } */ + // } } + +MOUNT_CLASS_TO && (MOUNT_CLASS_TO.ScrollSaver = ScrollSaver); diff --git a/src/lib/rootScope.ts b/src/lib/rootScope.ts index 501eca9d..6a3602cd 100644 --- a/src/lib/rootScope.ts +++ b/src/lib/rootScope.ts @@ -148,8 +148,6 @@ export type BroadcastEvents = { 'quick_reaction': string, - 'missed_reactions_element': {message: Message.message, changedResults: ReactionCount[]}, - 'service_notification': Update.updateServiceNotification, 'logging_out': void diff --git a/src/scss/partials/_chatBubble.scss b/src/scss/partials/_chatBubble.scss index a34f6339..2607da1a 100644 --- a/src/scss/partials/_chatBubble.scss +++ b/src/scss/partials/_chatBubble.scss @@ -987,11 +987,12 @@ $bubble-beside-button-width: 38px; } .bubbles.has-groups & { + pointer-events: none; + .bubble-content-wrapper { transform: scale3d(.8, .8, 1) translateX(0); //transform: scale(.8) translateX(0); opacity: 0; - pointer-events: none; } } diff --git a/src/test_scroll_saving.js b/src/test_scroll_saving.js new file mode 100644 index 00000000..0e0e767a --- /dev/null +++ b/src/test_scroll_saving.js @@ -0,0 +1,16 @@ +var chatInner = appImManager.chat.bubbles.chatInner; +var dateGroup = chatInner.firstElementChild; +var topBubble = chatInner.querySelector('[data-mid="6318129151"]').parentElement; +var bottomBubble = chatInner.querySelector('[data-mid="6318587903"]').parentElement; +topBubble.remove(); +bottomBubble.remove(); +var f = () => { + var scrollSaver = appImManager.chat.bubbles.createScrollSaver(); + scrollSaver.save(); + + dateGroup.prepend(topBubble); + dateGroup.append(bottomBubble); + scrollSaver.restore(); +}; +// f(); +setTimeout(() => f(), 1000); \ No newline at end of file