Skip to content

fix(desktop): hide non-contiguous islands until scrollback heals them#1486

Open
tlongwell-block wants to merge 2 commits into
fix/scrollback-history-frontierfrom
fix/hide-noncontiguous-islands
Open

fix(desktop): hide non-contiguous islands until scrollback heals them#1486
tlongwell-block wants to merge 2 commits into
fix/scrollback-history-frontierfrom
fix/hide-noncontiguous-islands

Conversation

@tlongwell-block

Copy link
Copy Markdown
Collaborator

Stacked on #1483. Fixes the pop-in Tyler hit while hammering the stack: scrolling up sometimes rendered the beginning of a day before its end/middle, which then popped in above the reader.

Mechanism

Out-of-band merges (useLoadMissingAncestors fetching thread roots by id, useThreadReplies fetching subtrees) splice old events into the cache marked nonContiguous. #1483 made the pager ignore them when anchoring its cursor — but nothing filtered them from the render. An island (typically a day-leading thread root) painted instantly at its day position; the rest of that day only arrived when contiguous paging caught up.

Fix

Hide marked events from the main timeline until paging heals them:

  • TimelineMessage carries the nonContiguous mark through formatTimelineMessages
  • buildMainTimelineEntries skips marked entries — the single seam both ChannelPane and TimelineMessageList render through. Island replies still count toward a visible parent's thread summary (the thread index sees the full cache)
  • countTopLevelTimelineRows mirrors the filter, so the pager's row floor counts only rows that actually render
  • the channel unread marker skips hidden islands, so the pill/New-divider never anchor on an invisible row

Healing is unchanged: when contiguous paging reaches an island, the unmarked copy wins the merge and the whole day appears in one commit, in order. Thread panels are unaffected (they derive from the full cache, not the entries).

Validation

  • 3 new regressions, all red on fix(desktop): stop out-of-band ancestor merges from poisoning the scrollback cursor #1483's head / green here: island hidden pre-heal + visible post-heal, hidden islands excluded from the row floor, mark propagation through formatting; plus island-reply-in-summary coverage
  • Full-dataset e2e (real 2,980-event #buzz-bugs export): at every paging commit, the oldest rendered row never precedes the contiguous frontier (no pop-in), and the final render contains all 206 expected top-level rows (nothing lost)
  • desktop suite 1504/1504, pnpm check clean, tsc --noEmit clean

npub1qyvc0c5kl4gqv2fd97fsk46tu378sqgy35vc83rvgfwne90sel7s0ed67d and others added 2 commits July 2, 2026 20:55
Out-of-band merges (thread ancestors fetched by id, thread-panel reply
subtrees) splice old events into the timeline cache marked nonContiguous.
The pager already ignores them when anchoring its cursor, but the render
path did not: an island painted the start of an old day immediately,
while the middle and end of that day only arrived when contiguous paging
caught up — so they visibly popped in above the reader.

Hide marked events from the main timeline until paging heals them:

- TimelineMessage carries the nonContiguous mark through
  formatTimelineMessages,
- buildMainTimelineEntries skips marked entries (the single seam both
  ChannelPane and TimelineMessageList render through); island replies
  still count toward their visible parent's thread summary,
- countTopLevelTimelineRows mirrors the filter so the pager's row floor
  counts only rows that actually render,
- the channel unread marker skips hidden islands so the pill and New
  divider never anchor on an invisible row.

Healing is unchanged: when contiguous paging reaches an island the
unmarked copy wins the merge and the whole day appears in one commit,
in order. Thread panels are unaffected — they derive from the full
cache, not the main-timeline entries.

Reported by Tyler while hammering #1483: scrolling up sometimes loaded
the beginning of a day before its end, then the rest popped in.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>
Wren's #1486 review: the island-reply summary test exercised main-timeline
aggregation, not buildThreadPanelData. Pin the exact panel shape — a marked
island head with a marked descendant renders head + reply in the thread
panel while producing zero main-timeline entries.

Co-authored-by: Tyler Longwell <tlongwell@block.xyz>
Signed-off-by: Tyler Longwell <tlongwell@block.xyz>

@tlongwell-block tlongwell-block left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent delta review clean at 1bc8e24accddfd022d65e765b0807afa9f485869.

Verified:

  • deep-link/search events enter the render list through ordinary mergeMessages, unmarked; a same-ID route target also replaces a cached marked island in the resolved render list;
  • an island thread head and marked descendants remain available through buildThreadPanelData while buildMainTimelineEntries hides only their main-timeline rows; the exact shape is now pinned by 1bc8e24a;
  • pager row-floor and channel unread marker count the same visible top-level set as the render seam;
  • contiguous history still heals a marked event through last-copy-wins without a downgrade race.

Validation on the exact PR head: desktop 1505/1505, pnpm check, pnpm typecheck, and git diff --check all clean.

Scores: Minimalness 9/10, Elegance 9/10, Correctness 9/10.

GitHub credentials resolve to the PR owner, so I cannot submit a formal approval; this comment is my approval-equivalent verdict. Manual scrollback remains the final UX gate.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant