镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

feat(desktop): make sending a message smooth - #8215

Open
brucesantos wants to merge 4 commits into
block:mainfrom
brucesantos:feat/desktop-send-entrance
Open

brucesantos wants to merge 4 commits into
block:mainfrom
brucesantos:feat/desktop-send-entrance

Conversation

@brucesantos

@brucesantos brucesantos commented Oct 9, 2026 •

Copy link
Copy Markdown

Stacked on #8195. This branch contains #8195's three commits; review only the last commit, feat(desktop): open the user's sent message into the timeline. I'll rebase once #8195 lands.

Summary

A sent message appeared in a single frame, and the bottom-pinned timeline jumped up by a whole row at the same moment, so every send read as a pop (#8214).

A row that first mounts while its send is pending now opens into the timeline once:

  • SendEntrance (new, desktop/src/features/messages/ui/SendEntrance.tsx) wraps the row. The row grows from zero height (grid-template-rows: 0fr → 1fr) while it fades in, over the shared --motion-duration-standard / --motion-ease-standard tokens. The timeline re-pins to the bottom on each resize, so earlier messages glide up instead of jumping.
  • No transforms or filters. Grid track sizing and opacity only, because transform/filter animations inside virtualized rows leave stale paint in WKWebView (Prevent ghost-painted timeline messages in the desktop app #2441).
  • Clipping is temporary. The child clips only while growing. The class is dropped on animationend or animationcancel, so the hover action rail is never cut off afterwards.
  • Reduced motion: the class is never applied. The pending → sent body dim from fix(desktop): keep pending messages grouped so sends don't shift the timeline #8195 also gets motion-reduce:transition-none.
  • MessageRow only decides when: mountedWhilePending is captured at mount. The row keeps its render key through the acknowledgement, so the entrance never replays when the send lands, and rows that arrive already sent (other people's messages, history) never play it.

Measured first, so the scope stays small:

  • The composer → optimistic-insert gap is 5–15 ms for plain messages, so the send path is unchanged.
  • Messages with links wait ~440 ms for link-preview resolution before the optimistic insert. That's a separate change and is not included here.

Not covered / follow-ups

  • Same-second acknowledged order (Desktop: messages sent within the same second can swap places when acknowledged #8192) can still move a row just after its entrance.
  • Remounting a still-pending row (scrolling it out of the virtualizer window and back during a slow send) replays the entrance. It's low impact at typical acknowledgement latency, and a per-localKey one-shot set would fix it.
  • The entrance tests inject pending rows through the mock bridge. A composer-driven test needs a delayed mock acknowledgement, which the bridge doesn't offer yet.

Related issue

Closes #8214. Related: #8194 / #8195, #8192, #2441.

Testing

  • motion.test.mjs: the send entrance uses the shared tokens, its keyframes contain no transform/filter, and it has a reduced-motion rule.

  • message-feedback-snapshots.spec.ts, 3 new e2e tests:

    • an own pending message opens once, drops its clipping class afterwards, and neither replays nor resizes on acknowledgement;
    • already-sent messages don't play it;
    • no animation with reduced motion.

    The first and third fail without the feature. All 5 tests in the spec passed 3× in a row.

  • Desktop unit suite and tsc --noEmit. Smoke e2e for messaging, virtualization, scroll-history, timeline-no-shift and the feedback spec: 130/131. The one failure, "editing a broadcast reply from a thread returns to the main composer", is pre-existing flakiness: 3/10 failures without this change vs 1/10 with it.

  • Manual: rapid sends in a DM on the macOS app, verified frame by frame in a 120 fps recording. The timeline glides monotonically over ~5 frames with no jitter.

Before / after, real speed:

smooth-send-before-after.mp4

buzz-review-completed

🤖 Generated with Claude Code

brucesantos and others added 4 commits October 8, 2026 18:19
…timeline

A pending message always rendered with its own avatar, author, and
"Sending…" header, then collapsed into its author group once the relay
acknowledged it. Every send therefore flashed a header and shifted the
timeline by one header height.

Group pending rows exactly like acknowledged ones. A grouped pending row
dims its body and keeps the send status for screen readers only, so the
acknowledgement changes opacity but never the row's height.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: brucesantos <brucesantos@users.noreply.github.com>
Relay order breaks created_at ties by event id. An optimistic id sorts
after every hex event id, so a message sent within the same second as the
previous one rendered above it, took over the author header, and dropped
back into place on acknowledgement.

Treat a pending send as the newest event in its second.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: brucesantos <brucesantos@users.noreply.github.com>
The live-overlay test binds the send mutation's path. Keep only the
pageless projection case from the earlier test, which it does not cover.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: brucesantos <brucesantos@users.noreply.github.com>
A sent message appeared in a single frame, and the bottom-pinned timeline
jumped up by a whole row at the same moment, so every send read as a pop.

A row that first mounts while its send is pending now opens once: it grows
from zero height (grid track sizing) while fading in, over the shared
standard motion tokens. The pinned timeline re-pins on each resize, so the
earlier messages glide up instead of jumping. The animation avoids
transforms and filters, which leave stale paint inside virtualized rows in
WKWebView, and is skipped entirely under reduced motion. The row keeps its
render key through the acknowledgement, so the entrance never replays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: brucesantos <brucesantos@users.noreply.github.com>
@github-actions

github-actions Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

🔐 Codex Security Review

Status: review required for the current range.

The current range is 1e80066c932250b87bd8a794f4143c0a88021c30...788fae04a74268092aae2cb9cd3be81cf9eae6b5.
A new review must complete for this exact range. When manual authorization
is required, a user with write access must comment exactly
@buzz-security-review 788fae04a74268092aae2cb9cd3be81cf9eae6b5 to authorize a new review.
Any previous review applies only to its recorded range.

@brucesantos brucesantos changed the title feat(desktop): open the user's sent message into the timeline feat(desktop): make sending a message smooth Oct 9, 2026
@brucesantos
brucesantos marked this pull request as ready for review October 9, 2026 20:14
@brucesantos
brucesantos requested a review from a team as a code owner October 9, 2026 20:14

This branch has not been deployed

No deployments
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.

Desktop: animate your own message into the timeline instead of popping it in

1 participant