Skip to content

feat(mobile): diff review surface — inline comments, native review toolbar, rebuilt diff renderer - #5838

Merged
saddlepaddle merged 49 commits into
mainfrom
setup-mobile-dev-servers
Jul 21, 2026
Merged

saddlepaddle merged 49 commits into
mainfrom
setup-mobile-dev-servers

Conversation

@saddlepaddle

@saddlepaddle saddlepaddle commented Jul 21, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Second wave of the mobile workspace UX work (follow-up to #5635), centered on turning the files-changed screen into a full review surface:

  • Review flow — long-press a diff line or file to draft comments; drafts persist per workspace, show as counts in the jump-to-file sheet, and "Send to chat" packages the review into an ACP session via the finish-review sheet. Native bottom toolbar (prominent green CTA + jump-to-file bar button).
  • Diff renderer rebuilt — hunk-segment text blobs on FlashList v2 with exact fixed row heights and a per-file item cache; syntax highlighting via the code-block tokenizer; eager pipelined diff loading; horizontal panning with edge-only back gesture so scrolling left doesn't pop the screen.
  • Files-changed chrome — native pinned header, per-file bars and tap menus, animated section collapse, viewed-file tracking, GitHub-style file status badges matching the desktop changes-view language (shared FileStatusBadge).
  • Commits sheet — native pinned sheet header, continuous opaque timeline rail (TimelineRow), author avatars (org member → GitHub noreply → monogram), vertical commit glyph.
  • Composer polish — whole-pill tap-to-focus, glass "View changes" chip that glides on a layout spring with proper clearance, tighter SwiftUI edge padding.
  • Navigation — stable human route titles for back-button menus (Home / Workspace / Chat), file viewer route, jump-to-file sheet.
  • Fixes along the way: zustand unstable-snapshot render loops (two sites), diff query cache-shape collision, large-changeset fetch stampede, sticky-header reanimated crash, dev-client worklets pan fallback.

Test plan

  • Full review loop in sim against local host: draft line + file comments → jump sheet counts → send to chat → lands in ACP thread
  • Large changeset (100+ files) opens without relay stampede; sections collapse/expand smoothly
  • Horizontal diff scroll both directions; back only from the left edge on diff screens
  • Commits sheet cold-opened post-relaunch (formSheet cold-mount check)
  • bun run lint and turbo typecheck green (mobile, host-client, host-service)
  • CI full suite + Maestro E2E on this PR

Summary by cubic

Turns the mobile files‑changed view into a full review surface with inline comments, a native finish‑review toolbar, and a rebuilt FlashList‑based diff renderer. Also adds a consolidated workspace screen, ACP‑backed chat lists, a commits sheet, and native per‑file headers and menus.

  • New Features

    • Review flow: long‑press a line or file to draft comments; drafts persist per workspace and show counts in Jump‑to‑file; Finish Review sends the package to an ACP thread via a native bottom toolbar.
    • Rebuilt diff renderer: @shopify/flash-list v2 with exact row heights and per‑file item cache; syntax‑highlighted lines, context expansion, and pipelined loading; horizontal pan with edge‑only back gesture.
    • Files‑changed UI: solid native header; per‑file bars with viewed state, tap menus (SwiftUI UIMenu), animated collapse/expand, GitHub‑style file status badges; Jump‑to‑file sheet; file viewer with highlight and share/copy.
    • Commits sheet: pinned header, continuous timeline rail with avatars, and a vertical commit glyph.
    • Workspace & navigation: new workspace landing (chat list + diff); chat threads live in the root stack with human route titles (Home / Workspace / Chat); home lists ACP sessions via useHostAcpSessions; host‑client listSessions input is now optional. Dependencies: added @shopify/flash-list and diff.
  • Bug Fixes

    • Fixed diff cache correctness and pull‑to‑refresh invalidation; collapsed large changesets by default; stabilized sticky headers and list animations.
    • Corrected monospace char width and scroll clamping; wide characters (CJK/emoji) no longer wrap; drafts persist line type/tokens for accurate styling.
    • Edge‑only back gesture on diff screens; stable zustand selectors; PressableScale now respects caller styles so fixed row heights apply; reset collapse when viewed baselines flip; Jump‑to‑file force‑expands unviewed targets.

Written for commit 58f5b6a. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • New Features
    • Added a redesigned mobile workspace diff viewer with syntax highlighting, expandable sections, file status indicators, viewed-file tracking, inline comments, and review actions.
    • Added sheets for line comments, finishing reviews, and jumping directly to changed files.
    • Added file sharing and expanded copy options in the file viewer.
  • Bug Fixes
    • Improved navigation headers, titles, gestures, and presentation behavior across workspace screens.
  • Style
    • Added smoother widget transitions and more consistent pressable component styling.

…sions, restyled home list

- Home list: drop the chat-card background; sessions hang off a thread
  rail under their workspace, date sections separate on whitespace alone
- Workspace rows navigate to a new workspace page; a trailing secondary
  icon Button takes over the start-chat interaction
- New workspace/[id] landing screen: chat list default tab + placeholder
  diff tab in a bottom tab bar
- Home sessions and the workspace page list ACP sessions host-wide via
  acpSessions.list (new useHostAcpSessions hook); listSessions input is
  now optional/host-wide on the client
- Composer target flow creates an ACP session and prompts it, landing in
  the ACP thread
…ss tabs

- workspace/[id] uses the native stack header: floating back circle,
  centered GlassHeaderTitle pill with the workspace name, and a glass
  ellipsis menu (copy branch / rename) on the right
- tabs move to expo-router NativeTabs ((tabs)/index + (tabs)/diff) so
  the bottom bar is the native liquid glass one
- branch shown as a small centered line under the header on the chats
  tab; custom WorkspaceTabBar/DiffPlaceholder components removed
…itle + toolbar menu

- plain native centered title (no GlassHeaderTitle pill) on the
  transparent header
- ellipsis becomes a native Stack.Toolbar.Menu (copy branch / rename)
  composed on the (tabs) Stack.Screen, replacing the custom GlassView
  button + ActionSheetIOS
… subtitle, compose & menu toolbar

- flatten navigation: the (tabs) screen now lives directly in the root
  authenticated stack (intermediate workspace Stack removed), so pushing
  from home gets the native back chevron for free
- header config moves into the (tabs) layout via the Stack.Screen
  composition API (Stack.Title asChild + Stack.Toolbar must be DIRECT
  children — wrapper components are dropped by the matcher)
- title view is name + branch subtitle, width-capped so it never slides
  under the trailing buttons
- right side: native toolbar with a compose button (creates an ACP
  session, lands in the thread) and an ellipsis menu (copy branch,
  rename), sharing one glass capsule on iOS 26
…ons surface

- tridot mirrors the home row menu: rename, copy branch / copy ID /
  share (inline group), destructive delete with the force-delete
  recovery flow (pops back to home on success); delete hidden for main
- drop the Live sessions screen, route, and row-menu entry — ACP
  sessions are the chat list now
…composer polish

- Changes screen (files/commits/PR checks), files-changed + per-file
  views, commits formSheet with native pinned header and a continuous
  opaque TimelineRow rail
- workspace page consolidated to a single screen: chat list, pinned
  composer with glass View-changes chip, full-viewport scroll surface
- chat threads flattened into the root stack (native back chevron) with
  the session-title + workspace-name glass header
- composer: whole-pill tap-to-focus (contentShape + onTapGesture),
  tighter SwiftUI edge padding; git router gains commit-files support
- files-changed and file-viewer cached different shapes (computed rows
  vs raw pair) under the same query key with infinite staleTime —
  whichever screen ran first poisoned the other; keys are now distinct
- diff-row keys include the file's +/− stats so fresh edits bust the
  otherwise-immortal cache; viewer contents get a finite staleTime
- changesets over 15 files rest collapsed so first open doesn't fire a
  full-contents git.getDiff per file through the relay
…zontal pan, sticky headers, context expansion, jump-to-file
- CharWidthProbe: absoluteFill stretched the probe to screen width, so the
  measured char advance came out ~2x too small; measure intrinsic width with a
  probe short enough to never clamp
- reset a file's collapse toggle when its viewed baseline flips, so marking a
  collapsed file viewed no longer re-expands it
- pull-to-refresh invalidates the per-file diff queries (staleTime Infinity +
  count-keyed queryKey served stale diffs for same-count edits)
- persist lineType/tokens on draft comments so editing keeps add/del styling
  and syntax highlighting in the anchor card
- drop the phantom trailing line from split("\n") on newline-terminated
  files (spurious 1-line expander at EOF)
- move the maxScrollX write out of useMemo into an effect and clamp scrollX
  when the max shrinks
- jump-to-file force-expands manually-collapsed unviewed targets
- RefreshControl progressViewOffset so the spinner clears the glass header
- weight CJK/emoji as two monospace cells in maxLineChars so wide lines don't
  wrap inside fixed-height segments
- solid themed nav header replacing glass; drops all transparent-header
  inset/offset compensation from the list
- file bars restyled to the native reference: chevron, monospace path,
  divider, ellipsis, viewed circle; height 56; +/- counts removed from bar
- per-file tridot now opens a tap-triggered native UIMenu (@expo/ui SwiftUI
  Menu) with SF symbols and destructive Delete, replacing ActionSheetIOS
- PressableScale no longer discards the caller's style prop, so fixed row
  heights (file bar 56, expander 38) actually apply
…dentity

- collapse/expand and mark-viewed animate via LayoutAnimation +
  FlashList prepareForLayoutAnimationRender (flag auto-resets on commit)
- useChangesetListItems now keeps stable object identity for file-header,
  note, and comment items (per-path/id caches), so toggling one section no
  longer re-renders every mounted file bar and its SwiftUI menu host
- FileHeaderRow memoized so renderItem identity changes skip the host subtree
- white +/- signs and add/del gutter numbers; anchor card sign matches
@greptile-apps

greptile-apps Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

Too many files changed for review. (101 files found, 100 file limit)

Bypass the limit by tagging @greptile-apps to review.

@coderabbitai

coderabbitai Bot commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The mobile workspace review flow now includes virtualized tokenized diffs, expandable file context, draft line comments, viewed-file navigation, review submission sheets, updated workspace routing, and related file-viewing and shared UI improvements.

Changes

Mobile workspace review flow

Layer / File(s) Summary
Workspace navigation and sheet routes
apps/mobile/app/(authenticated)/*, apps/mobile/app/(authenticated)/workspace/[id]/*, apps/mobile/screens/(authenticated)/workspace/[id]/WorkspaceScreen/*
Workspace headers, gestures, titles, route entry points, and form-sheet presentations were updated for review-related screens.
Diff data and virtualized rendering
apps/mobile/package.json, apps/mobile/components/ai-elements/code-block.tsx, apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/*
Diff computation now includes hunk, truncation, token, and width metadata, then builds cached list items for FlashList rendering with expansion, tokenized lines, horizontal scrolling, and animated cells.
Comments, expansion, and file review actions
apps/mobile/screens/(authenticated)/workspace/[id]/stores/*, apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/components/*, apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/FilesChangedScreen.tsx
Draft comments, expansion ranges, viewed files, line anchors, file actions, review overlays, and comment rows were added and connected to the diff screen.
Review submission and file navigation
apps/mobile/screens/(authenticated)/workspace/[id]/finish-review/*, jump-to-file/*, line-comment/*
New sheets compose drafted comments, submit them to new or existing sessions, list changed files, and add or edit anchored comments.
Workspace display and shared UI updates
apps/mobile/screens/(authenticated)/workspace/[id]/file/*, utils/languageForPath/*, components/PressableScale/*, NewChatWidget/*, commits/*, diff/*
File sharing and toolbar actions, language detection, status/icon presentation, pressable styles, and chat-widget transitions were updated.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant FilesChangedScreen
  participant DiffAPI
  participant computeFileDiff
  participant useChangesetListItems
  participant HunkSegmentCell
  participant LineCommentSheet
  participant FinishReviewSheet
  FilesChangedScreen->>DiffAPI: fetch changed file contents
  FilesChangedScreen->>computeFileDiff: compute and tokenize file diff
  FilesChangedScreen->>useChangesetListItems: build virtualized review items
  useChangesetListItems->>HunkSegmentCell: render diff segments
  HunkSegmentCell->>LineCommentSheet: open anchored comment composer
  FinishReviewSheet->>DiffAPI: submit composed review prompt
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise, conventional, and accurately summarizes the main change: a rebuilt mobile diff review surface with inline comments and review tools.
Description check ✅ Passed The description covers the main change and testing, but it does not follow the template exactly and the checklist section is incomplete.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch setup-mobile-dev-servers

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

# Conflicts:
#	apps/mobile/app/(authenticated)/_layout.tsx
#	apps/mobile/screens/(authenticated)/(home)/home/HomeScreen.tsx
#	apps/mobile/screens/(authenticated)/(home)/home/components/NewChatWidget/NewChatWidget.tsx
#	apps/mobile/screens/(authenticated)/workspace/[id]/WorkspaceScreen/WorkspaceScreen.tsx
#	apps/mobile/screens/(authenticated)/workspace/[id]/commits/CommitsSheet.tsx
#	apps/mobile/screens/(authenticated)/workspace/[id]/diff/WorkspaceDiffScreen.tsx
#	apps/mobile/screens/(authenticated)/workspace/[id]/file/FileViewerScreen.tsx
#	apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/FilesChangedScreen.tsx
#	apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/utils/computeFileDiff/computeFileDiff.ts
#	apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/utils/computeFileDiff/index.ts
#	apps/mobile/screens/(authenticated)/workspace/[id]/hooks/useWorkspacePullRequest/useWorkspacePullRequest.ts
@github-actions

github-actions Bot commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor

🧹 Preview Cleanup Complete

The following preview resources have been cleaned up:

  • ✅ Neon database branch

Thank you for your contribution! 🎉

@saddlepaddle
saddlepaddle merged commit 9f28482 into main Jul 21, 2026
17 of 18 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/FilesChangedScreen.tsx (1)

195-252: 🩺 Stability & Availability | 🔵 Trivial

Thread the signal through the diff queries. The staged pipeline only advances once the current batch settles, so a few slow host requests can keep later files on the loading placeholder. Passing the TanStack Query signal into git.getDiff.query will let cancellations bound that wait.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/mobile/screens/`(authenticated)/workspace/[id]/files-changed/FilesChangedScreen.tsx
around lines 195 - 252, Update the queryFn in the diffQueries useQueries
configuration to accept TanStack Query’s AbortSignal and pass it through to
git.getDiff.query. Preserve the existing staged pipeline and diff-processing
behavior while ensuring canceled requests receive the signal.
apps/mobile/screens/(authenticated)/workspace/[id]/finish-review/FinishReviewSheet.tsx (1)

183-222: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract TargetRow into its own component folder.

TargetRow is a second component defined in this file. As per coding guidelines, "Use one folder per component with ComponentName/ComponentName.tsx and index.ts; use one component per file" and single-use items should be "nested under the parent." Move it to something like finish-review/components/TargetRow/TargetRow.tsx (+ index.ts) since it's only used by FinishReviewSheet.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/mobile/screens/`(authenticated)/workspace/[id]/finish-review/FinishReviewSheet.tsx
around lines 183 - 222, Extract TargetRow from FinishReviewSheet.tsx into
finish-review/components/TargetRow/TargetRow.tsx with a matching index.ts
re-export, preserving its props and rendered behavior. Update FinishReviewSheet
to import TargetRow from the new component entry point and remove the local
definition.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@apps/mobile/screens/`(authenticated)/workspace/[id]/finish-review/FinishReviewSheet.tsx:
- Around line 70-90: Update submit in the target === "new" branch to await the
startWorkspaceChat mutation before clearing the draft and navigating away.
Remove the mutate-call onSuccess callback, call clearWorkspace(workspaceId) only
after the mutation succeeds, then invoke router.back() so cleanup is not skipped
when the sheet unmounts.

---

Nitpick comments:
In
`@apps/mobile/screens/`(authenticated)/workspace/[id]/files-changed/FilesChangedScreen.tsx:
- Around line 195-252: Update the queryFn in the diffQueries useQueries
configuration to accept TanStack Query’s AbortSignal and pass it through to
git.getDiff.query. Preserve the existing staged pipeline and diff-processing
behavior while ensuring canceled requests receive the signal.

In
`@apps/mobile/screens/`(authenticated)/workspace/[id]/finish-review/FinishReviewSheet.tsx:
- Around line 183-222: Extract TargetRow from FinishReviewSheet.tsx into
finish-review/components/TargetRow/TargetRow.tsx with a matching index.ts
re-export, preserving its props and rendered behavior. Update FinishReviewSheet
to import TargetRow from the new component entry point and remove the local
definition.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f8e634a0-5466-4197-94a4-42e2e9b39994

📥 Commits

Reviewing files that changed from the base of the PR and between cdf55f9 and 58f5b6a.

⛔ Files ignored due to path filters (1)
  • bun.lock is excluded by !**/*.lock
📒 Files selected for processing (54)
  • apps/mobile/app/(authenticated)/_layout.tsx
  • apps/mobile/app/(authenticated)/workspace/[id]/finish-review.tsx
  • apps/mobile/app/(authenticated)/workspace/[id]/jump-to-file.tsx
  • apps/mobile/app/(authenticated)/workspace/[id]/line-comment.tsx
  • apps/mobile/components/ai-elements/code-block.tsx
  • apps/mobile/package.json
  • apps/mobile/screens/(authenticated)/(home)/home/components/NewChatWidget/NewChatWidget.tsx
  • apps/mobile/screens/(authenticated)/components/PressableScale/PressableScale.tsx
  • apps/mobile/screens/(authenticated)/workspace/[id]/WorkspaceScreen/WorkspaceScreen.tsx
  • apps/mobile/screens/(authenticated)/workspace/[id]/commits/CommitsSheet.tsx
  • apps/mobile/screens/(authenticated)/workspace/[id]/components/FileStatusBadge/FileStatusBadge.tsx
  • apps/mobile/screens/(authenticated)/workspace/[id]/components/FileStatusBadge/index.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/diff/WorkspaceDiffScreen.tsx
  • apps/mobile/screens/(authenticated)/workspace/[id]/file/FileViewerScreen.tsx
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/FilesChangedScreen.tsx
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/components/AnimatedCellContainer/AnimatedCellContainer.tsx
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/components/AnimatedCellContainer/index.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/components/CommentCardRow/CommentCardRow.tsx
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/components/CommentCardRow/index.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/components/ExpanderRow/ExpanderRow.tsx
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/components/ExpanderRow/index.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/components/FileHeaderRow/FileHeaderRow.tsx
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/components/FileHeaderRow/index.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/components/HunkSegmentCell/HunkSegmentCell.tsx
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/components/HunkSegmentCell/index.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/components/ReviewOverlay/ReviewOverlay.tsx
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/components/ReviewOverlay/index.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/hooks/useChangesetListItems/index.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/hooks/useChangesetListItems/useChangesetListItems.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/utils/buildDisplayRows/buildDisplayRows.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/utils/buildDisplayRows/index.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/utils/buildListItems/buildListItems.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/utils/buildListItems/index.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/utils/computeFileDiff/computeFileDiff.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/utils/computeFileDiff/index.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/utils/diffMetrics/CharWidthProbe.tsx
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/utils/diffMetrics/diffMetrics.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/files-changed/utils/diffMetrics/index.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/finish-review/FinishReviewSheet.tsx
  • apps/mobile/screens/(authenticated)/workspace/[id]/finish-review/index.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/jump-to-file/JumpToFileSheet.tsx
  • apps/mobile/screens/(authenticated)/workspace/[id]/jump-to-file/index.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/line-comment/LineCommentSheet.tsx
  • apps/mobile/screens/(authenticated)/workspace/[id]/line-comment/components/AnchorLineRow/AnchorLineRow.tsx
  • apps/mobile/screens/(authenticated)/workspace/[id]/line-comment/components/AnchorLineRow/index.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/line-comment/index.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/stores/commentComposerStore/commentComposerStore.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/stores/commentComposerStore/index.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/stores/diffViewStore/diffViewStore.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/stores/diffViewStore/index.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/stores/draftCommentsStore/draftCommentsStore.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/stores/draftCommentsStore/index.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/utils/languageForPath/index.ts
  • apps/mobile/screens/(authenticated)/workspace/[id]/utils/languageForPath/languageForPath.ts

Comment on lines +70 to +90
const submit = async () => {
if (!workspace || !host || comments.length === 0 || sending) return;
const prompt = composeReviewPrompt(message, comments);
setSending(true);
try {
if (target === "new") {
startWorkspaceChat.mutate(
{
target: {
workspaceId: workspace.id,
workspaceName: workspace.name,
branch: workspace.branch,
hostId: workspace.hostId,
},
message: { text: prompt, attachments: [] },
},
{ onSuccess: () => clearWorkspace(workspaceId) },
);
router.back();
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

TanStack Query v5 mutate callback onSuccess passed to mutate vs useMutation both called

💡 Result:

In TanStack Query v5, both the onSuccess callback provided to useMutation and the one passed directly to the mutate function are designed to execute, but they serve different purposes and operate with different behaviors [1][2][3]. When both are defined, the library executes them in sequence: first the onSuccess callback from useMutation, and then the onSuccess callback from the mutate call [3]. Key distinctions to keep in mind: 1. Lifecycle and Execution: The onSuccess callback in useMutation is considered global to that mutation instance and will always fire when the mutation succeeds [4][1][2]. Conversely, the onSuccess callback passed to mutate is intended for component-specific side effects. Because these callbacks rely on the component remaining mounted, they will not execute if the component unmounts before the mutation completes [1][2][5]. 2. Consecutive Mutations: If you trigger multiple mutations rapidly, callbacks passed directly to mutate exhibit different behavior than those defined in useMutation [1][2]. The useMutation callbacks execute for every mutation call [1][2]. However, callbacks passed to mutate are bound to the specific mutation request; if a new mutation is triggered before a previous one finishes, the "observer" for the previous request may be removed, which can cause the mutate-specific onSuccess callback to be skipped [4][1][6]. 3. Recommended Usage: Due to the potential for component-specific callbacks to be skipped during unmounting or rapid consecutive calls, it is generally recommended to place core mutation logic (like query invalidation or cache updates) inside the useMutation hook [4][6]. For component-specific side effects (like navigation or UI alerts), if you need them to be more reliable, consider using mutateAsync, which returns a promise you can await, rather than relying on the callback passed to mutate [4][5]. Example of execution order in source code: await latestConfig.onSuccess?.(data, variables!) // useMutation onSuccess await mutateConfig.onSuccess?.(data, variables!) // mutate onSuccess [3]

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the reviewed component and the related mutation hook.
printf '\n== FinishReviewSheet.tsx ==\n'
ast-grep outline apps/mobile/screens/(authenticated)/workspace/[id]/finish-review/FinishReviewSheet.tsx --view expanded || true
printf '\n--- file excerpt ---\n'
sed -n '1,220p' 'apps/mobile/screens/(authenticated)/workspace/[id]/finish-review/FinishReviewSheet.tsx'

printf '\n== useStartWorkspaceChat references ==\n'
rg -n "useStartWorkspaceChat|startWorkspaceChat" apps/mobile -g '!**/node_modules/**'

Repository: superset-sh/superset

Length of output: 240


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file='apps/mobile/screens/(authenticated)/workspace/[id]/finish-review/FinishReviewSheet.tsx'

printf '\n== outline ==\n'
ast-grep outline "$file" --view expanded || true

printf '\n== file excerpt ==\n'
sed -n '1,240p' "$file"

printf '\n== hook references ==\n'
rg -n "useStartWorkspaceChat|startWorkspaceChat" apps/mobile -g '!**/node_modules/**'

Repository: superset-sh/superset

Length of output: 8607


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file='apps/mobile/screens/(authenticated)/(home)/home/components/NewChatWidget/hooks/useStartWorkspaceChat/useStartWorkspaceChat.ts'

printf '\n== outline ==\n'
ast-grep outline "$file" --view expanded || true

printf '\n== file excerpt ==\n'
sed -n '1,240p' "$file"

Repository: superset-sh/superset

Length of output: 2319


Avoid relying on the mutate-call onSuccess here.

router.back() unmounts this sheet immediately, so clearWorkspace(workspaceId) can be skipped if the mutation settles after unmount. That leaves the draft attached and makes it easy to resend duplicates later. Await the mutation first, then clear the draft and navigate away.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@apps/mobile/screens/`(authenticated)/workspace/[id]/finish-review/FinishReviewSheet.tsx
around lines 70 - 90, Update submit in the target === "new" branch to await the
startWorkspaceChat mutation before clearing the draft and navigating away.
Remove the mutate-call onSuccess callback, call clearWorkspace(workspaceId) only
after the mutation succeeds, then invoke router.back() so cleanup is not skipped
when the sheet unmounts.

This branch was previously deployed

1 inactive deployment
preview — 58f5b6af Deployed Jul 21, 2026 by saddlepaddle via Deploy Admin #6738
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