Browse Source

Fix restoring scroll position on edited message

master
Eduard Kuzmenko 2 years ago
parent
commit
a7fd334eaa
  1. 2
      src/components/chat/bubbleGroups.ts
  2. 17
      src/components/chat/bubbles.ts
  3. 63
      src/helpers/scrollSaver.ts
  4. 2
      src/scss/partials/_chatBubble.scss

2
src/components/chat/bubbleGroups.ts

@ -503,7 +503,7 @@ export default class BubbleGroups {
prepareForGrouping(bubble: HTMLElement, message: MyMessage) { prepareForGrouping(bubble: HTMLElement, message: MyMessage) {
const foundItem = this.getItemByBubble(bubble); const foundItem = this.getItemByBubble(bubble);
if(foundItem) { // should happen only on edit if(foundItem) { // should happen only on edit
debugger; // debugger;
return; return;
} }

17
src/components/chat/bubbles.ts

@ -248,7 +248,7 @@ export default class ChatBubbles {
private attachPlaceholderOnRender: () => void; private attachPlaceholderOnRender: () => void;
private bubblesToEject: Set<HTMLElement> = new Set(); private bubblesToEject: Set<HTMLElement> = new Set();
private bubblesToReplace: Set<HTMLElement> = new Set(); private bubblesToReplace: Map<HTMLElement, HTMLElement> = new Map(); // TO -> FROM
private updatePlaceholderPosition: () => void; private updatePlaceholderPosition: () => void;
private setPeerOptions: {lastMsgId: number; topMessage: number;}; private setPeerOptions: {lastMsgId: number; topMessage: number;};
@ -3153,7 +3153,7 @@ export default class ChatBubbles {
loadQueue = filterQueue(loadQueue); loadQueue = filterQueue(loadQueue);
const restoreScroll = this.prepareToSaveScroll(reverse); const {restoreScroll, scrollSaver} = this.prepareToSaveScroll(reverse);
// if(this.messagesQueueOnRender) { // if(this.messagesQueueOnRender) {
// this.messagesQueueOnRender(); // this.messagesQueueOnRender();
// } // }
@ -3163,7 +3163,9 @@ export default class ChatBubbles {
} }
this.ejectBubbles(); this.ejectBubbles();
for(const bubble of this.bubblesToReplace) { for(const [bubble, oldBubble] of this.bubblesToReplace) {
scrollSaver.replaceSaved(oldBubble, bubble);
if(!loadQueue.find((details) => details.bubble === bubble)) { if(!loadQueue.find((details) => details.bubble === bubble)) {
continue; continue;
} }
@ -3317,7 +3319,7 @@ export default class ChatBubbles {
this.bubblesToEject.add(bubble); this.bubblesToEject.add(bubble);
this.bubblesToReplace.delete(bubble); this.bubblesToReplace.delete(bubble);
this.bubblesToReplace.add(newBubble); this.bubblesToReplace.set(newBubble, bubble);
this.bubbleGroups.changeBubbleByBubble(bubble, newBubble); this.bubbleGroups.changeBubbleByBubble(bubble, newBubble);
} }
@ -4414,7 +4416,7 @@ export default class ChatBubbles {
private prepareToSaveScroll(reverse?: boolean) { private prepareToSaveScroll(reverse?: boolean) {
const isMounted = !!this.chatInner.parentElement; const isMounted = !!this.chatInner.parentElement;
if(!isMounted) { if(!isMounted) {
return; return {};
} }
const log = this.log.bindPrefix('prepareToSaveScroll'); const log = this.log.bindPrefix('prepareToSaveScroll');
@ -4431,11 +4433,14 @@ export default class ChatBubbles {
// const saved = scrollSaver.getSaved(); // const saved = scrollSaver.getSaved();
// const hadScroll = saved.scrollHeight !== saved.clientHeight; // const hadScroll = saved.scrollHeight !== saved.clientHeight;
return () => { return {
restoreScroll: () => {
log('restore'); log('restore');
// scrollSaver.restore(_history.length === 1 && !reverse ? false : true); // scrollSaver.restore(_history.length === 1 && !reverse ? false : true);
scrollSaver.restore(reverse); scrollSaver.restore(reverse);
this.onRenderScrollSet(scrollSaver.getSaved()); this.onRenderScrollSet(scrollSaver.getSaved());
},
scrollSaver
}; };
} }

63
src/helpers/scrollSaver.ts

@ -15,8 +15,7 @@ export default class ScrollSaver {
private scrollHeightMinusTop: number; private scrollHeightMinusTop: number;
private scrollTop: number; private scrollTop: number;
private clientHeight: number; private clientHeight: number;
private anchor: HTMLElement; private elements: {element: HTMLElement, rect: DOMRect}[];
private rect: DOMRect;
/** /**
* *
@ -43,43 +42,50 @@ export default class ScrollSaver {
}; };
} }
public findAnchor() { public findElements() {
const {container} = this; const {container} = this;
const containerRect = container.getBoundingClientRect(); const containerRect = container.getBoundingClientRect();
const bubbles = Array.from(container.querySelectorAll(this.query)) as HTMLElement[]; const bubbles = Array.from(container.querySelectorAll(this.query)) as HTMLElement[];
let rect: DOMRect, anchor: HTMLElement; const elements: ScrollSaver['elements'] = [];
for(const bubble of bubbles) { for(const bubble of bubbles) {
const elementRect = bubble.getBoundingClientRect(); const elementRect = bubble.getBoundingClientRect();
const visibleRect = getVisibleRect(bubble, container, undefined, elementRect, containerRect); const visibleRect = getVisibleRect(bubble, container, undefined, elementRect, containerRect);
if(visibleRect) { if(visibleRect) {
rect = elementRect; elements.push({element: bubble, rect: elementRect});
anchor = bubble;
// break; // find first // break; // find first
} else if(anchor) { // find last } else if(elements.length) { // find last
break; break;
} }
} }
if(!rect) { if(!elements.length) {
const bubble = bubbles[0]; const bubble = bubbles[0];
if(bubble) { if(bubble) {
rect = bubble.getBoundingClientRect(); elements.push({element: bubble, rect: bubble.getBoundingClientRect()});
anchor = bubble;
} }
} }
return {rect, anchor}; return elements;
} }
public findAndSetAnchor() { public replaceSaved(from: HTMLElement, to: HTMLElement) {
const {rect, anchor} = this.findAnchor(); if(!this.elements) {
this.rect = rect; return;
this.anchor = anchor; }
const idx = this.elements.findIndex(({element}) => from === element);
if(idx !== -1) {
this.elements[idx].element = to;
}
}
public findAndSetElements() {
this.elements = this.findElements();
} }
public save() { public save() {
this.findAndSetAnchor(); this.findAndSetElements();
// console.warn('scroll save', this.anchor, this.rect); console.warn('scroll save', this.elements);
this._save(); this._save();
} }
@ -121,21 +127,32 @@ export default class ScrollSaver {
const {scrollTop, scrollHeight} = this.scrollable; const {scrollTop, scrollHeight} = this.scrollable;
this.scrollHeight = scrollHeight; this.scrollHeight = scrollHeight;
if(!this.anchor?.parentElement) { // try to find new anchor let anchor: ScrollSaver['elements'][0];
this.findAndSetAnchor(); // for(let i = this.elements.length - 1; i >= 0; --i) {
// const _anchor = this.elements[i];
// if(_anchor.element.parentElement) {
// anchor = _anchor;
// break;
// }
// }
anchor = this.elements[this.elements.length - 1];
if(!anchor?.element?.parentElement) { // try to find new anchor
this.findAndSetElements();
anchor = this.elements[this.elements.length - 1];
if(!this.anchor) { // fallback to old method if smth is really strange if(!anchor) { // fallback to old method if smth is really strange
this._restore(useReflow); this._restore(useReflow);
return; return;
} }
} }
const rect = this.rect; const {element, rect} = anchor;
const newRect = this.anchor.getBoundingClientRect(); const newRect = element.getBoundingClientRect();
const diff = newRect.bottom - rect.bottom; const diff = newRect.bottom - rect.bottom;
this.setScrollTop(scrollTop + diff, useReflow); this.setScrollTop(scrollTop + diff, useReflow);
// if(diff) debugger; // if(diff) debugger;
// console.warn('scroll restore', rect, diff, newRect); console.warn('scroll restore', rect, diff, newRect);
} }
public _restore(useReflow?: boolean) { public _restore(useReflow?: boolean) {

2
src/scss/partials/_chatBubble.scss

@ -2367,7 +2367,7 @@ $bubble-beside-button-width: 38px;
color: var(--monospace-text-color); color: var(--monospace-text-color);
} }
.reply.is-overriding-color { &:not(.just-media) .reply.is-overriding-color {
.reply-border { .reply-border {
background-color: rgb(var(--override-color)); background-color: rgb(var(--override-color));
} }

Loading…
Cancel
Save