diff options
| author | Adam Soltys <[email protected]> | 2026-09-23 22:51:58 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-09-23 22:51:58 -0700 |
| commit | 75679fdd5d24d36e3c54316e88ccefcf954afc82 (patch) | |
| tree | c86a369455da3c2e9532d51d2e72898afda8fccc /src/feed-window.js | |
| parent | fb170b4783c9b49c73e926c08aba9bc4d1bb418e (diff) | |
| download | coinosv3-75679fdd5d24d36e3c54316e88ccefcf954afc82.tar.gz coinosv3-75679fdd5d24d36e3c54316e88ccefcf954afc82.zip | |
Feed: threads cached locally, posts never inserted under the reader, rows keep their first look.
Work by Codex, wrapped up and verified here.
Threads: a viewed conversation is persisted (thread-cache.js, up to 12
threads / 160 events), so a refresh or a cached note link shows the root
and ancestors at once instead of the selected reply and a spinner; relays
are re-asked in the background and an empty answer keeps the cache.
Feed window (feed-window.js): arrivals are merged without inserting
between visible rows — a gap fill under the reader's eyes waits until
that part of the feed is offscreen, prepends anchor the reading position,
the notice counts what went in above, and only its tap goes to the top.
Older pages are prepared a few screens ahead; a page's pictures and faces
are decoded before it is shown.
Rows keep the presentation they were admitted with (feedPaint): background
profile and quote refreshes no longer rewrite text or swap faces mid-read;
media dimensions are known before paint. Thumbnails queue instead of being
dropped past the first three.
Tests: thread-cache, feed-window, feed-stability; feed-live updated for
the new arrival rules (the notice counts up while you read).
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Diffstat (limited to 'src/feed-window.js')
| -rw-r--r-- | src/feed-window.js | 27 |
1 files changed, 27 insertions, 0 deletions
diff --git a/src/feed-window.js b/src/feed-window.js new file mode 100644 index 0000000..88170be --- /dev/null +++ b/src/feed-window.js @@ -0,0 +1,27 @@ +// Merge prepared events without inserting between visible posts or evicting +// the reading position. Row geometry comes from the currently rendered feed. +export function mergeFeedWindow(notes, shown, additions, { rows = [], height = 0, keep = 200, page = 20 } = {}) { + const byId = new Map(rows.map((r) => [r.id, r])); + const first = rows.find((r) => r.bottom > 0 && r.top < height); + const seen = new Set(notes.map((e) => e.id)); + const added = [], deferred = []; + for (const e of additions) { + if (seen.has(e.id)) continue; + seen.add(e.id); + // Equal timestamps sort after existing events. Ignore hidden rows when + // locating the next rendered post: they cannot serve as an anchor. + const next = notes.find((n) => n.created_at < e.created_at && byId.has(n.id)); + const row = next && byId.get(next.id); + // Even a prepend must wait while the header is visible: anchoring just + // the first post would scroll the header away. + const inView = row && row.top < height && (row === first ? row.top > 0 : row.top >= 0); + (inView ? deferred : added).push(e); + } + if (!added.length) return { notes, shown, added, deferred }; + const held = new Set([...notes.slice(0, shown).map((e) => e.id), ...byId.keys()]); + const merged = [...notes, ...added].sort((a, b) => b.created_at - a.created_at); + for (let i = 0; i < merged.length; i++) if (held.has(merged[i].id)) shown = Math.max(shown, i + 1); + // Keep another page beyond the displayed window so paging still advances + // after the reader has reached the normal in-memory cache limit. + return { notes: merged.slice(0, Math.max(keep, shown + page)), shown, added, deferred }; +} |
