fix(mobile): sent messages rise into place instead of dropping in#28660

merged1
main
GitHub fork: fix/mobile-sent-message-enter-direction
opened3d agobyMmozzius
View on GitHubTwo-way sync enabledPR last imported 3d ago

Problem

A message you send from the mobile composer drops into the feed from above instead of rising up from the composer.

Reanimated names its entering presets after the side they start from. In react-native-reanimated 4.7.1 (src/layoutReanimation/defaultAnimations/Fade.ts), FadeInUp starts at translateY: -25 and moves down to 0, while FadeInDown starts at translateY: 25 and moves up to 0. The user-message bubble in ThreadFeed.tsx used FadeInUp.duration(220), so it enters from above its resting position, away from the composer it was just sent from.

Repro: open any thread in the iOS app, send any message, and watch the new bubble.

Change

  • apps/mobile/src/features/threads/ThreadFeed.tsx: use FadeInDown.duration(220) for the user-message bubble entering animation and swap the import.
  • apps/mobile/src/lib/filePreview.test.ts: add FadeInDown to the react-native-reanimated mock. That test renders ThreadFeed, and its mock only defined FadeIn and FadeInUp.

The composer-adjacent elements in ThreadDetailScreen.tsx already enter with FadeInDown, so this makes the sent bubble consistent with them. PendingUserInputCard.tsx uses a FadeInUp/FadeOutDown pair on purpose and is intentionally untouched.

Scope and approval

This is a very small, focused fix for an obvious bug: one preset swap plus the matching test mock, nothing else. It does not change a product default or add behavior, only which way an existing 220 ms animation travels, so it qualifies for the very-small-fix exception without a prior issue or discussion.

Verification

Setup. iPhone 18 Pro simulator, iOS 27.2. Debug dev-client build of the development app variant against Metro, paired to a seeded showcase environment (scripts/mobile-showcase.ts). I opened a seeded thread and sent short messages. Screen recordings are 30 fps, so the 220 ms animation spans about 7 frames. I stepped through frames with ffmpeg.

Method note. In the normal flow (keyboard up) the keyboard dismisses and the list repositions at the same instant as the send, so the bubble's net on-screen motion is dominated by that and does not isolate the preset. I therefore also recorded a controlled flow: type the message, dismiss the keyboard, scroll the list to the end, then tap Send. Both builds were recorded in the same way, the unmodified code first and then this change, with a full JS reload each time.

Before (unmodified main).

  • Keyboard up, two takes: the new bubble first appears faint with a "Pending" label near the top of the list, well above its final slot, and travels down while fading in.
  • Keyboard dismissed first (controlled flow): the bubble first appears about 25 pt above its resting slot (relative to the previous message) and moves down into place.

After (this change).

  • Keyboard dismissed first (controlled flow): the bubble first appears about 23 pt below its resting slot and rises into place, a mirror image of the before take. These are approximate offsets read from the frames, accurate to a few points.
  • Keyboard up: the starting offset is smaller than before, but the bubble still travels downward on screen because of the keyboard and list movement described above. I could not show a clean "rises" in that flow, only in the controlled one.

Tests and checks.

  • vp test run src/lib/filePreview.test.ts (from apps/mobile): 1 file, 89 tests passed.
  • vp run check:changed: format, lint, knip, the changed test file and the React Compiler check passed. Typecheck failed in my local environment: 404 tsc errors, mostly "could not find a declaration file for module 'react'" from my local install. The error set is identical on unmodified main (I diffed the two outputs), so the change adds none. I could not get a clean local typecheck.

Not checked. Android, a physical device, reduced-motion settings, and messages that arrive from another client (I only exercised messages sent from this device).

Before/after recordings:

Maintainer verification: Pixel 9, keyboard dismissed before Send, ⅓ speed. Compare the bubble to the preceding row: the whole feed can move independently. A separate iPhone 18 Pro capture confirms the keyboard-open send still repositions the feed as described above.

Sent bubble entering from above before, and from below after.

Claude Sonnet 5.5 and Claude Opus 5.5 in Claude Code

🤖 Generated with Claude Code

Checks

24 successful · 13 skipped
All checks passed
7c66c82

Added 1 commit

  • fix(mobile): sent messages rise into place instead of dropping in7c66c82
Synced from GitHub

Approvability

Verdict: Approved at 7c66c82

Macroscope's review found this PR approvable — Fresh messages now rise into place rather than drop in, preserving the existing animation duration and eligibility. The change is presentation-only and does not affect message sending or stored data.

You can add or adjust custom eligibility rules. Learn more.

Synced from GitHub

Review in Change Stack →

No actionable comments were generated in the recent review.

External image Recent review info
External image Run configuration
External image Commits

Reviewing files that changed from the base of the PR and between 8760d063e903406c8e8f695b0852c0a15fc1e00f and 7c66c82fb2f10a28ae592a90b7da90ad65ef534f.

External image Files selected for processing: 2
  • apps/mobile/src/features/threads/ThreadFeed.tsx
  • apps/mobile/src/lib/filePreview.test.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

Walkthrough

Fresh user messages in the thread feed now enter with FadeInDown instead of FadeInUp. The freshness condition and 220 ms duration remain unchanged. The test mock adds FadeInDown.

Changes: Thread message entry animation

Layer / File(s)Summary
Fresh-message animation
apps/mobile/src/features/threads/ThreadFeed.tsx, apps/mobile/src/lib/filePreview.test.ts
Fresh user messages use FadeInDown instead of FadeInUp. The test mock adds FadeInDown using the shared transition stub.

Estimated code review effort: 2 (Simple) | ~5 minutes

Change: Bug fix

Suggested reviewers: juliusmarminge

Merge Risk: External image Minimal · up to 7c66c

The animation update is localized and carries no identified merge-blocking risk.

Pre-merge checks: Passed 4
Check nameStatusExplanation
Title checkPassedThe title clearly describes the main change: correcting the mobile sent-message animation so it rises into place. It is concise and uses a conventional commit format.
Description checkPassedThe description includes all required sections. It explains the problem and reproduction steps, identifies the code changes and scope, documents controlled before/after animation checks, reports test …
Linked Issues checkPassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes checkPassedCheck skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description includes all required sections. It explains the problem and reproduction steps, identifies the code changes and scope, documents controlled before/after animation checks, reports test results and the local typecheck limitation, and lists unverified scenarios.

Finishing Touches
Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts
JJulius Marmingeapproved these changes3d ago

Verified the preset change on the reviewed head against its parent. On the Pixel recording the bubble now rises relative to the previous row; the iPhone keyboard-open flow still moves the feed during dismissal, which is separate from this fix. The focused mobile suite passed all 89 tests.