fix(feed): width-change measurement invalidation + best-effort SSE counts

Post-commit review follow-ups for the batch-3 feed work.

Frontend — VirtualFeed: the guarded setOptions keyed only on (count, scrollMargin),
so a rotation (or the first-paint 0->real container width) changed colWidth / card
heights without invalidating the cached measurements, leaving getTotalSize and the
scrollbar stale until each row scrolled back through the window. Track appliedWidth
and call virtualizer.measure() on a width delta so heights re-estimate at the new
width (rendered rows re-measure immediately via their ResizeObserver). Covers list +
grid and the first-paint 120px-estimate case.

Backend — social.rs: the fresh like/comment count SELECT used `.await?`, so a
transient DB failure would 500 the request after the like/comment had already
committed. Make the count + broadcast best-effort (if let Ok { ... }); the mutation
succeeds regardless and the next event / pull-to-refresh reconciles the count.

Docs — FOLLOWUPS: record the review findings left as-is (grid-prepend tile reflow,
filtered-grid auto-load which is pre-existing, comment-delete stale count, and the
last-write-wins count ordering note).

Verified: svelte-check 0 errors, cargo build clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
fabi
2026-06-30 19:32:32 +02:00
parent e79e020566
commit 0d7938aff5
3 changed files with 79 additions and 27 deletions

View File

@@ -106,23 +106,33 @@
// this effect (that would loop).
let appliedCount = -1;
let appliedMargin = Number.NaN;
let appliedWidth = Number.NaN;
function applyOptions() {
const count = rowCount;
const width = containerWidth;
const margin = listEl ? listEl.getBoundingClientRect().top + window.scrollY : 0;
scrollMargin = margin; // drives the template transforms
if (count === appliedCount && Math.abs(margin - appliedMargin) < 0.5) return;
// A width change (rotation, or the first 0→real clientWidth landing) changes
// every card's / tile-row's real height, so the heights cached from the old
// width are stale even when count and margin are unchanged. Invalidate the
// measurement cache so getTotalSize + positions recompute (rendered rows then
// re-measure immediately via their ResizeObserver) instead of trusting them.
const widthChanged = width > 0 && Math.abs(width - appliedWidth) > 0.5;
if (count === appliedCount && Math.abs(margin - appliedMargin) < 0.5 && !widthChanged) return;
appliedCount = count;
appliedMargin = margin;
appliedWidth = width;
get(virtualizer).setOptions({ count, scrollMargin: margin });
if (widthChanged) get(virtualizer).measure();
}
// Re-apply when the row count or column width changes (load-more, prepend,
// delete, filter, rotate). `colWidth`/`rowCount` are the only tracked reads, so a
// length-stable like/comment patch doesn't re-run this.
// Re-apply when the row count or container width changes (load-more, prepend,
// delete, filter, rotate). `containerWidth`/`rowCount` are the only tracked
// reads, so a length-stable like/comment patch doesn't re-run this.
$effect(() => {
void rowCount;
void colWidth;
void containerWidth;
applyOptions();
});