Repository navigation
feat(chat): mark the message a reply-quote jump lands on - #1432
Merged
Merged
Conversation
Tapping a quote scrolled the transcript to the quoted message and left it looking like every other message, at the bottom edge of the screen. Two changes. The target now lands in the middle of the transcript, which is where iOS puts it (ChatViewController.scrollToRow, .centeredVertically), in two scrolls: one to park its leading edge mid-band and measure it, one to correct by where it came to rest. Correcting from the measured position is what makes the ends behave — a target the list cannot scroll to the middle stops short on the first pass, and the correction follows it there. Then it flashes: white at 14% over the bubble, held and faded off. Drawn in Bubble, so every variant gets it from one place and the scrim is clipped by the shape the bubble already has. The strength arrives as a lambda read in the draw phase, so the fade repaints the one bubble and recomposes nothing.
The jump ran two animations: one to put the target's leading edge at the middle of the viewport, then a corrective scroll for half the message's height once it had been laid out. The second one starts from a standstill, so the list moved, stopped, and moved again. Measure the target instead. `measuredHeight` scrolls it on screen and straight back off, both with the non-animated `scrollToItem`, which forces a remeasure and returns without suspending — the pair runs inside one dispatch, so nothing is drawn at the intermediate position. With the height known up front, the whole jump is one `animateScrollToItem` and the `scrollOffset` that centres it. Frame-differencing a screen recording of the tap shows the change: the old build's per-frame motion is 16.4, 11.5, 13.5, 8.8, 7.3, 7.8, 4.4 — two acceleration bumps — against 18.2, 12.9, 4.4, 1.1, 1.1 now, a single decay.
bmc08gt
force-pushed
the
feat/chat-jump-attention
branch
from
September 8, 2026 19:03
3b2a426 to
fa4d110
Compare
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.
Tapping a reply quote scrolled the transcript to the quoted message and left it looking like every other message, sitting at the bottom edge of the screen. Nothing said which one you had just been taken to.
Where it lands
The target now comes to rest in the middle of the transcript, which is where iOS puts it (
ChatViewController.scrollToRow,.centeredVertically).Centring needs the message's height, and nothing knows that until it has been laid out.
measuredHeightgets it by scrolling the target on screen and straight back off — both with the non-animatedscrollToItem, which forces a remeasure and returns without suspending, so the pair runs inside one dispatch and the intermediate position is never drawn. The jump itself is then a singleanimateScrollToItemwith thescrollOffsetthat centres it.One animation, and it has to be one: a second scroll to correct the first starts from a standstill, so it reads as the list moving, stopping, and moving again rather than as one move to the message. Frame-differencing a screen recording of the tap shows the difference — a two-phase version measures 16.4, 11.5, 13.5, 8.8, 7.3, 7.8, 4.4 per frame, two acceleration bumps, against 18.2, 12.9, 4.4, 1.1, 1.1 for the single scroll.
A message taller than the band cannot be centred, so it is aligned to the band's start instead — the edge the jump arrives from, since the reply that quoted it sits below it.
The flash
The message it lands on goes white at 14% over the bubble, holds for 250ms, then fades off over 750ms linearly. Drawn in
Bubble, so every variant gets it from one place and the scrim is clipped by the shape the bubble already has. The strength arrives as a lambda read in the draw phase, so the fade repaints the one bubble and recomposes nothing.A tween rather than a spring, unlike the rest of
ChatAnimations: this one runs from full to nothing with no competing target to be interrupted by, so there is no settling for a spring to express — only a rate, and a linear one is what reads as a light going out.Tests
JumpCenteringTestcovers the one number the jump now consists of: thescrollOffsethanded toanimateScrollToItem, across a message that fits, one that fills half the band, one too tall to centre, and a height that could not be measured.ChatJumpAttentionScreenshotTestrenders the bubble at full attention and settled.