Browse Source

Fix jumping scroll

Enable reactions & views updates
master
Eduard Kuzmenko 2 years ago
parent
commit
c805347e83
  1. 265
      src/components/chat/bubbles.ts
  2. 8
      src/components/chat/messageRender.ts
  3. 2
      src/components/chat/selection.ts
  4. 150
      src/helpers/scrollSaver.ts
  5. 2
      src/lib/rootScope.ts
  6. 3
      src/scss/partials/_chatBubble.scss
  7. 16
      src/test_scroll_saving.js

265
src/components/chat/bubbles.ts

@ -136,6 +136,12 @@ export const STICKY_OFFSET = 3;
const SCROLLED_DOWN_THRESHOLD = 300; const SCROLLED_DOWN_THRESHOLD = 300;
const PEER_CHANGED_ERROR = new Error('peer changed'); 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 = { type Bubble = {
bubble: HTMLElement, bubble: HTMLElement,
mids: Set<number>, mids: Set<number>,
@ -510,63 +516,50 @@ export default class ChatBubbles {
this.safeRenderMessage(message, true, bubble); this.safeRenderMessage(message, true, bubble);
}); });
// if(this.chat.type !== 'scheduled' && false) { if(this.chat.type !== 'scheduled' && !DO_NOT_UPDATE_MESSAGE_REACTIONS/* && false */) {
// this.listenerSetter.add(rootScope)('missed_reactions_element', async({message, changedResults}) => { this.listenerSetter.add(rootScope)('messages_reactions', async(arr) => {
// if(this.peerId !== message.peerId || !message.reactions || !message.reactions.results.length) { let scrollSaver: ScrollSaver;
// return;
// } const a = arr.map(async({message, changedResults}) => {
if(this.peerId !== message.peerId) {
// const bubble = await this.getBubbleByMessage(message); return;
// if(!bubble) { }
// return;
// } const result = await this.getMountedBubble(message.mid, message);
if(!result) {
// if(message.grouped_id) { return;
// const grouped = await this.getGroupedBubble(message.grouped_id); }
// message = grouped.message;
// } return {bubble: result.bubble, message, changedResults};
});
// this.appendReactionsElementToBubble(bubble, message, changedResults);
// }); let top: number;
(await Promise.all(a)).filter(Boolean).forEach(({bubble, message, changedResults}) => {
// this.listenerSetter.add(rootScope)('messages_reactions', async(arr) => { if(!scrollSaver) {
// let scrollSaver: ScrollSaver; scrollSaver = this.createScrollSaver(false);
scrollSaver.save();
// 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();
// }
// const key = message.peerId + '_' + message.mid; const key = message.peerId + '_' + message.mid;
// const set = REACTIONS_ELEMENTS.get(key); const set = REACTIONS_ELEMENTS.get(key);
// if(set) { if(set) {
// for(const element of set) { for(const element of set) {
// element.update(message, changedResults); element.update(message, changedResults);
// } }
// } else { } else if(!message.reactions || !message.reactions.results.length) {
// rootScope.dispatchEvent('missed_reactions_element', {message, changedResults}); return;
// } } else {
// }); this.appendReactionsElementToBubble(bubble, message, message, changedResults);
}
// await Promise.all(promises); });
// if(scrollSaver) { if(scrollSaver) {
// scrollSaver.restore(); 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(); const middleware = this.getMiddleware();
await getHeavyAnimationPromise(); await getHeavyAnimationPromise();
if(!middleware()) return; if(!middleware()) return;
@ -885,38 +878,38 @@ export default class ChatBubbles {
} }
}); });
// this.listenerSetter.add(rootScope)('messages_views', (arr) => { !DO_NOT_UPDATE_MESSAGE_VIEWS && this.listenerSetter.add(rootScope)('messages_views', (arr) => {
// fastRaf(() => { fastRaf(() => {
// let scrollSaver: ScrollSaver; let scrollSaver: ScrollSaver;
// for(const {peerId, views, mid} of arr) { for(const {peerId, views, mid} of arr) {
// if(this.peerId !== peerId) return; if(this.peerId !== peerId) continue;
// const bubble = this.bubbles[mid]; const bubble = this.bubbles[mid];
// if(!bubble) return; if(!bubble) continue;
// const postViewsElements = Array.from(bubble.querySelectorAll('.post-views')) as HTMLElement[]; const postViewsElements = Array.from(bubble.querySelectorAll('.post-views')) as HTMLElement[];
// if(postViewsElements.length) { if(!postViewsElements.length) continue;
// const str = formatNumber(views, 1);
// let different = false; const str = formatNumber(views, 1);
// postViewsElements.forEach((postViews) => { let different = false;
// if(different || postViews.innerHTML !== str) { postViewsElements.forEach((postViews) => {
// if(!scrollSaver) { if(different || postViews.textContent !== str) {
// scrollSaver = new ScrollSaver(this.scrollable, true); if(!scrollSaver) {
// scrollSaver.save(); scrollSaver = this.createScrollSaver(true);
// } scrollSaver.save();
}
// different = true;
// postViews.innerHTML = str; different = true;
// } postViews.textContent = str;
// }); }
// } });
// } }
// if(scrollSaver) { if(scrollSaver) {
// scrollSaver.restore(); scrollSaver.restore();
// } }
// }); });
// }); });
this.observer = new SuperIntersectionObserver({root: this.scrollable.container}); this.observer = new SuperIntersectionObserver({root: this.scrollable.container});
@ -948,6 +941,11 @@ export default class ChatBubbles {
return this.chat.peerId; return this.chat.peerId;
} }
private createScrollSaver(reverse = true) {
const scrollSaver = new ScrollSaver(this.scrollable, '.bubbles-group .bubble', reverse);
return scrollSaver;
}
private unreadedObserverCallback = (entry: IntersectionObserverEntry) => { private unreadedObserverCallback = (entry: IntersectionObserverEntry) => {
if(entry.isIntersecting) { if(entry.isIntersecting) {
const target = entry.target as HTMLElement; const target = entry.target as HTMLElement;
@ -1351,8 +1349,12 @@ export default class ChatBubbles {
bubble = findUpClassName(target, 'bubble'); bubble = findUpClassName(target, 'bubble');
} catch(err) {} } catch(err) {}
if(!bubble) { if(!bubble && !this.chat.selection.isSelecting) {
const avatar = findUpClassName(target, 'user-avatar'); const avatar = findUpClassName(target, 'user-avatar');
if(!avatar) {
return;
}
const peerId = avatar.dataset.peerId.toPeerId(); const peerId = avatar.dataset.peerId.toPeerId();
if(peerId !== NULL_PEER_ID) { if(peerId !== NULL_PEER_ID) {
this.chat.appImManager.setInnerPeer({peerId}); 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) { public getBubbleGroupedItems(bubble: HTMLElement) {
return Array.from(bubble.querySelectorAll('.grouped-item')) as HTMLElement[]; return Array.from(bubble.querySelectorAll('.grouped-item')) as HTMLElement[];
} }
@ -2184,10 +2180,8 @@ export default class ChatBubbles {
} }
public getLastBubble() { public getLastBubble() {
const lastDateGroup = this.getLastDateGroup(); const group = this.bubbleGroups.getLastGroup();
if(lastDateGroup) { return group?.lastItem?.bubble;
return lastDateGroup.lastElementChild as HTMLElement;
}
} }
public scrollToBubble( public scrollToBubble(
@ -2200,13 +2194,16 @@ export default class ChatBubbles {
let fallbackToElementStartWhenCentering: HTMLElement; let fallbackToElementStartWhenCentering: HTMLElement;
// * if it's a start, then scroll to start of the group // * 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 */) { if(bubble && position !== 'end') {
const dateGroup = bubble.parentElement; const item = this.bubbleGroups.getItemByBubble(bubble);
// if(whichChild(dateGroup) === 0) { if(item.group.firstItem === item && whichChild(item.group.container) === (this.stickyIntersector ? STICKY_OFFSET : 1)) {
fallbackToElementStartWhenCentering = dateGroup; const dateGroup = item.group.container.parentElement;
// position = 'start'; // if(whichChild(dateGroup) === 0) {
// element = dateGroup; fallbackToElementStartWhenCentering = dateGroup;
// } // position = 'start';
// element = dateGroup;
// }
}
} }
// const isLastBubble = this.getLastBubble() === bubble; // 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... // ! can't get it by chatInner.lastElementChild because placeholder can be the last...
private getLastDateGroup() { // private getLastDateGroup() {
let lastTime = 0, lastElem: HTMLElement; // let lastTime = 0, lastElem: HTMLElement;
for(const i in this.dateMessages) { // for(const i in this.dateMessages) {
const dateMessage = this.dateMessages[i]; // const dateMessage = this.dateMessages[i];
if(dateMessage.firstTimestamp > lastTime) { // if(dateMessage.firstTimestamp > lastTime) {
lastElem = dateMessage.container; // lastElem = dateMessage.container;
lastTime = dateMessage.firstTimestamp; // lastTime = dateMessage.firstTimestamp;
} // }
} // }
return lastElem; // return lastElem;
} // }
public async scrollToBubbleIfLast(bubble: HTMLElement) { public async scrollToBubbleIfLast(bubble: HTMLElement) {
if(this.getLastBubble() === bubble) { if(this.getLastBubble() === bubble) {
@ -3205,7 +3202,7 @@ export default class ChatBubbles {
const isMessage = message._ === 'message'; const isMessage = message._ === 'message';
const groupedId = isMessage && message.grouped_id; const groupedId = isMessage && message.grouped_id;
let albumMids: number[]; let albumMids: number[], reactionsMessage: Message.message;
const albumMustBeRenderedFull = this.chat.type !== 'pinned'; const albumMustBeRenderedFull = this.chat.type !== 'pinned';
if(groupedId && albumMustBeRenderedFull) { // will render only last album's message if(groupedId && albumMustBeRenderedFull) { // will render only last album's message
@ -3215,6 +3212,10 @@ export default class ChatBubbles {
return; return;
} }
} }
if(isMessage) {
reactionsMessage = groupedId ? await this.managers.appMessagesManager.getGroupsFirstMessage(message) : message;
}
const peerId = this.peerId; const peerId = this.peerId;
// * can't use 'message.pFlags.out' here because this check will be used to define side of message (left-right) // * can't use 'message.pFlags.out' here because this check will be used to define side of message (left-right)
@ -3420,9 +3421,10 @@ export default class ChatBubbles {
setInnerHTML(messageDiv, richText); setInnerHTML(messageDiv, richText);
} }
const timeSpan = await MessageRender.setTime({ const timeSpan = MessageRender.setTime({
chatType: this.chat.type, chatType: this.chat.type,
message message,
reactionsMessage
}); });
messageDiv.append(timeSpan); messageDiv.append(timeSpan);
bubbleContainer.prepend(messageDiv); bubbleContainer.prepend(messageDiv);
@ -4224,7 +4226,7 @@ export default class ChatBubbles {
} }
if(isMessage) { if(isMessage) {
this.appendReactionsElementToBubble(bubble, message); this.appendReactionsElementToBubble(bubble, message, reactionsMessage);
} }
/* if(isMessage) { /* if(isMessage) {
@ -4242,13 +4244,12 @@ export default class ChatBubbles {
return ret; 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 */) { if(this.peerId.isUser()/* || true */) {
return; return;
} }
const reactionsMessage = await this.managers.appMessagesManager.getGroupsFirstMessage(message); if(!reactionsMessage?.reactions || !reactionsMessage.reactions.results.length) {
if(!reactionsMessage.reactions || !reactionsMessage.reactions.results.length) {
return; return;
} }
@ -4268,9 +4269,10 @@ export default class ChatBubbles {
let timeSpan: HTMLElement = documentMessageDiv && documentMessageDiv.querySelector('.time'); let timeSpan: HTMLElement = documentMessageDiv && documentMessageDiv.querySelector('.time');
if(!timeSpan) { if(!timeSpan) {
timeSpan = await MessageRender.setTime({ timeSpan = MessageRender.setTime({
chatType: this.chat.type, chatType: this.chat.type,
message message,
reactionsMessage
}); });
} }
@ -4299,15 +4301,16 @@ export default class ChatBubbles {
} }
this.log.warn('onRender'); 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) { if(this.getRenderedLength() && !this.chat.setPeerPromise) {
const viewportSlice = this.getViewportSlice(); const viewportSlice = this.getViewportSlice();
this.deleteViewportSlice(viewportSlice, true); this.deleteViewportSlice(viewportSlice, true);
} }
scrollSaver.save(); // scrollSaver.save(); // ! slicing will corrupt scroll position
const saved = scrollSaver.getSaved(); // const saved = scrollSaver.getSaved();
// const hadScroll = saved.scrollHeight !== saved.clientHeight; // const hadScroll = saved.scrollHeight !== saved.clientHeight;
(this.messagesQueuePromise || Promise.resolve()).then(() => { (this.messagesQueuePromise || Promise.resolve()).then(() => {
@ -4957,7 +4960,9 @@ export default class ChatBubbles {
} }
public deleteViewportSlice(slice: ReturnType<ChatBubbles['getViewportSlice']>, ignoreScrollSaving?: boolean) { public deleteViewportSlice(slice: ReturnType<ChatBubbles['getViewportSlice']>, ignoreScrollSaving?: boolean) {
// return; if(DO_NOT_SLICE_VIEWPORT_ON_RENDER) {
return;
}
const {invisibleTop, invisibleBottom} = slice; const {invisibleTop, invisibleBottom} = slice;
const invisible = invisibleTop.concat(invisibleBottom); const invisible = invisibleTop.concat(invisibleBottom);
@ -4979,7 +4984,7 @@ export default class ChatBubbles {
let scrollSaver: ScrollSaver; let scrollSaver: ScrollSaver;
if(!!invisibleTop.length !== !!invisibleBottom.length && !ignoreScrollSaving) { if(!!invisibleTop.length !== !!invisibleBottom.length && !ignoreScrollSaving) {
scrollSaver = new ScrollSaver(this.scrollable, !!invisibleTop.length); scrollSaver = this.createScrollSaver(!!invisibleTop.length);
scrollSaver.save(); scrollSaver.save();
} }
@ -4994,7 +4999,7 @@ export default class ChatBubbles {
public sliceViewport(ignoreHeavyAnimation?: boolean) { public sliceViewport(ignoreHeavyAnimation?: boolean) {
// Safari cannot reset the scroll. // Safari cannot reset the scroll.
if(IS_SAFARI || (this.isHeavyAnimationInProgress && !ignoreHeavyAnimation)/* || true */) { if(IS_SAFARI || (this.isHeavyAnimationInProgress && !ignoreHeavyAnimation) || DO_NOT_SLICE_VIEWPORT) {
return; return;
} }

8
src/components/chat/messageRender.ts

@ -35,9 +35,10 @@ export namespace MessageRender {
}; */ }; */
export const setTime = async(options: { export const setTime = (options: {
chatType: ChatType, chatType: ChatType,
message: Message.message | Message.messageService message: Message.message | Message.messageService,
reactionsMessage?: Message.message
}) => { }) => {
const {chatType, message} = options; const {chatType, message} = options;
const date = new Date(message.date * 1000); const date = new Date(message.date * 1000);
@ -83,8 +84,7 @@ export namespace MessageRender {
if(message.peer_id._ === 'peerUser'/* && message.reactions?.results?.length */) { if(message.peer_id._ === 'peerUser'/* && message.reactions?.results?.length */) {
hasReactions = true; hasReactions = true;
reactionsMessage = await rootScope.managers.appMessagesManager.getGroupsFirstMessage(message); reactionsMessage = options.reactionsMessage;
reactionsElement = new ReactionsElement(); reactionsElement = new ReactionsElement();
reactionsElement.init(reactionsMessage, 'inline', true); reactionsElement.init(reactionsMessage, 'inline', true);
reactionsElement.render(); reactionsElement.render();

2
src/components/chat/selection.ts

@ -817,7 +817,7 @@ export default class ChatSelection extends AppSelection {
} }
protected getMidsFromGroupContainer(groupContainer: HTMLElement) { protected getMidsFromGroupContainer(groupContainer: HTMLElement) {
const elements = Array.from(groupContainer.querySelectorAll('.grouped-item')) as HTMLElement[]; const elements = this.chat.bubbles.getBubbleGroupedItems(groupContainer);
if(!elements.length) { if(!elements.length) {
elements.push(groupContainer); elements.push(groupContainer);
} }

150
src/helpers/scrollSaver.ts

@ -5,14 +5,18 @@
*/ */
import Scrollable from "../components/scrollable"; import Scrollable from "../components/scrollable";
import { MOUNT_CLASS_TO } from "../config/debug";
import { IS_SAFARI } from "../environment/userAgent"; import { IS_SAFARI } from "../environment/userAgent";
import getVisibleRect from "./dom/getVisibleRect";
import reflowScrollableElement from "./dom/reflowScrollableElement"; import reflowScrollableElement from "./dom/reflowScrollableElement";
export default class ScrollSaver { export default class ScrollSaver {
private scrollHeight: number; private scrollHeight: number;
private scrollHeightMinusTop: number; // private scrollHeightMinusTop: number;
private scrollTop: number; private scrollTop: number;
private clientHeight: number; private clientHeight: number;
private anchor: HTMLElement;
private rect: DOMRect;
/** /**
* *
@ -21,6 +25,7 @@ export default class ScrollSaver {
*/ */
constructor( constructor(
private scrollable: Scrollable, private scrollable: Scrollable,
private query: string,
private reverse: boolean 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() { public save() {
this.findAndSetAnchor();
// console.warn('scroll save', this.anchor, this.rect);
this._save();
}
public _save() {
const {scrollTop, scrollHeight, clientHeight} = this.container; const {scrollTop, scrollHeight, clientHeight} = this.container;
//previousScrollHeight = scrollHeight; //previousScrollHeight = scrollHeight;
@ -46,7 +83,7 @@ export default class ScrollSaver {
this.scrollHeight = scrollHeight; this.scrollHeight = scrollHeight;
this.scrollTop = scrollTop; this.scrollTop = scrollTop;
this.clientHeight = clientHeight; this.clientHeight = clientHeight;
this.scrollHeightMinusTop = this.reverse ? scrollHeight - scrollTop : scrollTop; // this.scrollHeightMinusTop = this.reverse ? scrollHeight - scrollTop : scrollTop;
//this.chatInner.style.paddingTop = padding + 'px'; //this.chatInner.style.paddingTop = padding + 'px';
/* if(reverse) { /* if(reverse) {
@ -54,60 +91,85 @@ export default class ScrollSaver {
} else { } else {
previousScrollHeightMinusTop = scrollTop; previousScrollHeightMinusTop = scrollTop;
} */ } */
}
/* if(DEBUG) { private onRestore(useReflow?: boolean) {
this.log('performHistoryResult: messagesQueueOnRender, scrollTop:', scrollTop, scrollHeight, previousScrollHeightMinusTop); 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) { public restore(useReflow?: boolean) {
const {container, scrollHeightMinusTop: previousScrollHeightMinusTop, scrollable} = this; const {scrollTop, scrollHeight} = this.scrollable;
if(previousScrollHeightMinusTop === undefined) { this.scrollHeight = scrollHeight;
throw new Error('scroll was not saved');
} // if(!this.anchor.parentElement) { // fallback to old method if element has disappeared (e.g. edited)
// this._restore(useReflow);
// return;
// }
const scrollHeight = container.scrollHeight; if(!this.anchor.parentElement) { // try to find new anchor
if(scrollHeight === this.scrollHeight) { this.findAndSetAnchor();
return;
} }
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 scrollHeight = container.scrollHeight;
const addedHeight = scrollHeight - previousScrollHeight; // 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'; */ // this.chatInner.style.paddingTop = (10000 - addedHeight) + 'px'; */
/* const scrollHeight = scrollHeight; // /* const scrollHeight = scrollHeight;
const addedHeight = scrollHeight - previousScrollHeight; // 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 - previousScrollHeightMinusTop : previousScrollHeightMinusTop;
const newScrollTop = reverse ? scrollHeight - addedHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop; // const newScrollTop = reverse ? scrollHeight - addedHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop;
this.log('performHistoryResult: will set scrollTop', // this.log('performHistoryResult: will set scrollTop',
previousScrollHeightMinusTop, scrollHeight, // previousScrollHeightMinusTop, scrollHeight,
newScrollTop, container.container.clientHeight); */ // newScrollTop, container.container.clientHeight); */
//const newScrollTop = reverse ? scrollHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop; // //const newScrollTop = reverse ? scrollHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop;
const newScrollTop = this.reverse ? scrollHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop; // const newScrollTop = this.reverse ? scrollHeight - previousScrollHeightMinusTop : previousScrollHeightMinusTop;
/* if(DEBUG) { // /* if(DEBUG) {
this.log('performHistoryResult: will set up scrollTop:', newScrollTop, this.isHeavyAnimationInProgress); // 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.setScrollTop(newScrollTop, useReflow);
this.log('performHistoryResult: have set up scrollTop:', newScrollTop, container.scrollTop, container.scrollHeight, this.isHeavyAnimationInProgress);
} */
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);

2
src/lib/rootScope.ts

@ -148,8 +148,6 @@ export type BroadcastEvents = {
'quick_reaction': string, 'quick_reaction': string,
'missed_reactions_element': {message: Message.message, changedResults: ReactionCount[]},
'service_notification': Update.updateServiceNotification, 'service_notification': Update.updateServiceNotification,
'logging_out': void 'logging_out': void

3
src/scss/partials/_chatBubble.scss

@ -987,11 +987,12 @@ $bubble-beside-button-width: 38px;
} }
.bubbles.has-groups & { .bubbles.has-groups & {
pointer-events: none;
.bubble-content-wrapper { .bubble-content-wrapper {
transform: scale3d(.8, .8, 1) translateX(0); transform: scale3d(.8, .8, 1) translateX(0);
//transform: scale(.8) translateX(0); //transform: scale(.8) translateX(0);
opacity: 0; opacity: 0;
pointer-events: none;
} }
} }

16
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);
Loading…
Cancel
Save