aboutsummaryrefslogtreecommitdiffstats
path: root/src/features/messages.js
diff options
context:
space:
mode:
Diffstat (limited to 'src/features/messages.js')
-rw-r--r--src/features/messages.js23
1 files changed, 22 insertions, 1 deletions
diff --git a/src/features/messages.js b/src/features/messages.js
index 278e66f..bb2d788 100644
--- a/src/features/messages.js
+++ b/src/features/messages.js
@@ -7098,17 +7098,37 @@ export function messagesFeature(ctx) {
if (changed && repaint && c.win && (c.win.top || c.win.bottom)) holdScroll(render);
});
}
+ // A row changes height after it was measured — a picture lands in a row
+ // that painted before its size was known, a clip learns its shape, a link
+ // card arrives. The stale number is what the spacer uses when the row
+ // leaves the window: the page then shrinks or grows by the difference,
+ // the browser's scroll anchoring pulls the reader back by as much, and a
+ // coasting wheel scroll stalled or even reversed right as the media
+ // appeared. Keep the measurements live instead (no layout forced: the
+ // observer runs after layout).
+ const rowSizer = typeof ResizeObserver === 'undefined' ? null : new ResizeObserver((entries) => {
+ for (const en of entries) {
+ const r = en.target, c = r.parentElement && r.parentElement._winC;
+ if (!c || !r.isConnected) continue;
+ const hgt = r.getBoundingClientRect().height, key = r.getAttribute('data-key');
+ if (hgt > 0 && key) (c.heights || (c.heights = new Map())).set(key, hgt);
+ }
+ });
function measureNow(c) {
const list = document.querySelector('.notes-feed');
if (!list) return false;
c.listTop = list.getBoundingClientRect().top + (window.scrollY || 0);
+ list._winC = c;
const heights = c.heights || (c.heights = new Map());
let changed = false;
+ const seen = new Set();
for (const r of list.querySelectorAll(':scope > .row[data-key]')) {
const hgt = r.getBoundingClientRect().height;
const key = r.getAttribute('data-key');
if (hgt > 0 && Math.abs((heights.get(key) || 0) - hgt) > 0.5) { heights.set(key, hgt); changed = true; }
+ if (rowSizer) { seen.add(r); if (!(list._sized ||= new Set()).has(r)) { list._sized.add(r); rowSizer.observe(r); } }
}
+ if (rowSizer && list._sized) for (const r of list._sized) if (!seen.has(r)) { list._sized.delete(r); rowSizer.unobserve(r); }
return changed;
}
// On scroll: replenish the buffer before the reader reaches its edge,
@@ -7897,7 +7917,8 @@ export function messagesFeature(ctx) {
const known = size || mediaReady.get(url);
return h('img', {
src: mediaSrc(url, known), class: 'note-img clickable', loading: feedPaint || known ? 'eager' : 'lazy', alt: label || '',
- decoding: known && known.thumb ? 'sync' : undefined, // a few-ms decode: paint it with the page
+ // a few-ms decode of the copy paints with the page; an original never holds the page for its decode
+ decoding: known && known.thumb ? 'sync' : 'async',
width: size?.width, height: size?.height,
style: size ? 'height:auto;aspect-ratio:' + size.width + '/' + size.height + ';--ar:' + (size.width / size.height).toFixed(4) : undefined,
onClick: (e) => { if (!onPicture(e)) return; e.stopPropagation(); ctx.openImage && ctx.openImage(url); },