New timeline panel in the room view (labs) - #34859
Draft
langleyd wants to merge 17 commits into
Draft
Conversation
Patch fixes so the timeline holds its place when rows change size while a scroll is still settling: decisions use the up-to-date scroll position, the stay-at-bottom correction retries if the browser clamped it, and corrections now also apply while scrolling upwards. All candidates for upstreaming.
The list kept the browser's default 40px indent, which pushed every row 40px too wide. Only full-width rows (the read marker line, date separators) showed it.
The new design: a green line with a "New" label at its right-hand end. Opt-in via a new label prop, so the legacy timeline keeps its plain line. Also adds an as="div" option for callers that already provide the list item.
The box meant to hold a photo's space before it downloads was being sized to zero by the browser, so the message grew when the photo arrived and shoved the timeline. Failed images also keep the reserved height now instead of collapsing to one line.
A request to load more history that arrived while a fetch was already running was silently dropped, and the view would sometimes never re-ask — leaving the timeline stuck at the top until the user jiggled the scroll. Such requests are now remembered and run when the current fetch finishes, matching what the old ScrollPanel did.
The question and options are in the event we already have; only votes need fetching. Rendering them immediately stops a freshly loaded poll growing ~180px when its votes arrive and shoving the timeline.
For the new timeline: the quoted message shows immediately when it is already loaded, a fixed-height skeleton stands in while one is fetched, and the rows are pinned to one height so the preview never resizes and shoves the messages around it. Opt-in via a new EventTile prop; the legacy timeline is unchanged.
The new virtualised timeline panel, rendering events through the legacy EventTile for now. Enabled by the "Improved timeline scrolling and navigation" labs setting; the existing timeline is untouched when the flag is off.
Mark component props read-only, hoist the timeline row renderer to module level, pass the quoted event into the reply header fetch instead of re-reading state after the await, and drop a TODO tag.
The clamp stopping a reserved image box from overflowing its container lived in new-timeline CSS, but the box is drawn by a shared component the legacy timeline and thread panel use too, so images could overflow there. Moved it onto the component's own link wrapper, beside the width that makes it necessary: a 757px image in a 350px pane rendered at 757px before and 350px after, and still takes its natural size when there is room. Also, for the new timeline's rows: skip a row whose event has gone from the room rather than letting the tile crash and lose the whole timeline, drop a leftover placeholder that drew the word "Gap", and give the pagination spinner a label screen readers can announce.
…mvvm-3-panel # Conflicts: # apps/web/src/components/views/messages/MPollBody.tsx # apps/web/src/components/views/rooms/EventTile.tsx # pnpm-lock.yaml
The story had no left/right inset, so the label rendered flush against the edge of the frame and was clipped. Pads it like the timeline does, and adds the baseline image the visual test needs.
Covers how the panel draws each kind of row — messages, the read marker, date separators, the loading spinner, gaps — along with the layout fallback, edit state scoping, and skipping a row whose event has gone. Also covers the tile adapter's pass-through and the reply preview's three states (already loaded, skeleton while fetching, and error). The panel's tests stand in a stub for the virtualised TimelineView, which needs real layout the test environment doesn't have and has its own tests.
The stories dropped the marker into a plain list with no room around it, so the browser's list styling came along for the ride: every baseline had a bullet in it, and the rule was pressed against the top edge of the frame, hard to see and liable to be clipped by the marker's own 1px offset. The hidden-marker story was a picture of a bullet and nothing else. Resetting the list and insetting the stories draws the marker the way a timeline does — a hairline, evenly inset, with room above it.
…mvvm-3-panel # Conflicts: # apps/web/src/components/views/elements/ReplyChain.test.tsx
The second media box in that story was being shrunk to fit its content (255px wide) rather than taking the size the event declares; with the reserved box now holding, it renders at 297px and its spinner and timestamp sit inside it instead of overflowing. Every other story in the timeline suite is unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Timeline MVVM 3 — new timeline panel in the room view (labs)
Puts the new virtualised timeline into the room view behind the
"Improved timeline scrolling and navigation" labs flag. With the flag off,
nothing changes.
The integration
NewTimelinePanelrenders the sharedTimelineView+RoomTimelineViewModel(from the previous PRs in this series) in place ofTimelinePanel, switched at a single point inRoomView.EventTilevia a smallLegacyEventTileAdapter, so message appearance is unchanged — tiles will bemigrated to shared views one by one later.
marker uses the new "New" line style from the design system.
Keeping the timeline still
The bulk of the fixes here deal with one theme: rows changing height after
they render, shoving the timeline around. Each cause needed its own fix:
Images — the box reserved for a photo before it downloads was silently
collapsing to zero (a CSS sizing conflict), so the message grew when the
photo arrived. The box now genuinely holds the photo's space from first
paint, and failed images keep the same height instead of collapsing.
Polls — a poll's question and options are already in the event; only the
votes need fetching. They now render immediately at full size, and the votes
fill in without changing the height (previously the tile grew ~180px).
Reply previews — a new compact mode shows the quoted message immediately
when it's already loaded, and stands a fixed-height skeleton in while
fetching one that isn't, so the preview never resizes. Opt-in via an
EventTileprop; the legacy timeline is untouched.Everything else (link previews, late-loading content) — fixes to our
@tanstack/virtual-corepatch so the virtualizer's scroll compensationactually holds the view still when a row off-screen changes size:
be a frame stale during a programmatic scroll;
write (the cause of "the timeline stays still while previews
load and push content off the bottom, so the timline is no longer flush to the bottom");
preview or image that finished loading above the viewport shoved the view
mid-scroll.
All three are candidates for upstreaming to TanStack Virtual.
Content that grows while it is on screen is deliberately left alone (a
message being edited shouldn't drag the view) — that residual movement is the
same as the legacy timeline and shrinks further as fixed-size designs land
(e.g. the new URL preview style).
Other fixes along the way
the user jiggled the scroll — requests arriving mid-fetch are now remembered
and re-run, matching the old ScrollPanel's behaviour.
right-hand edge by the browser's default list indent.