From 89d022a3a336b42e77a58b769bd4cc193e93ec05 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Thu, 9 Jul 2026 18:04:07 -0700 Subject: [PATCH] Improve mobile git views and concurrent rendering safety - Pass the `origin` parameter through history and PR redirect routes. - Move source control panel ref updates to `useEffect` to prevent side effects during concurrent renders. - Resolve commit file changes to empty if disconnected to avoid a stuck loading spinner. - Standardize PR sidebar header button styling and accessibility labels. --- mobile/app/h/[hostId]/history/[worktreeId].tsx | 2 ++ mobile/app/h/[hostId]/pr/[worktreeId].tsx | 2 ++ .../components/pr-sidebar/PRSidebarHeader.tsx | 4 ++-- .../pr-sidebar/mobile-pr-sidebar-styles.ts | 7 ------- .../src/source-control/MobileGitHistoryList.tsx | 9 ++++++++- .../source-control/MobileSourceControlPanel.tsx | 16 ++++++++++------ 6 files changed, 24 insertions(+), 16 deletions(-) diff --git a/mobile/app/h/[hostId]/history/[worktreeId].tsx b/mobile/app/h/[hostId]/history/[worktreeId].tsx index 60fc5205dcf..19198e4e5f1 100644 --- a/mobile/app/h/[hostId]/history/[worktreeId].tsx +++ b/mobile/app/h/[hostId]/history/[worktreeId].tsx @@ -9,6 +9,7 @@ export default function HistoryRedirect() { hostId?: string | string[] worktreeId?: string | string[] name?: string | string[] + origin?: string | string[] }>() return ( () return ( [styles.openWebButton, pressed && { opacity: 0.6 }]} + accessibilityLabel={`Open pull request #${pr.number} in browser`} + style={({ pressed }) => [styles.iconButton, pressed && { opacity: 0.6 }]} > diff --git a/mobile/src/components/pr-sidebar/mobile-pr-sidebar-styles.ts b/mobile/src/components/pr-sidebar/mobile-pr-sidebar-styles.ts index 6a30f414533..1c66ed7a48b 100644 --- a/mobile/src/components/pr-sidebar/mobile-pr-sidebar-styles.ts +++ b/mobile/src/components/pr-sidebar/mobile-pr-sidebar-styles.ts @@ -75,13 +75,6 @@ export const mobilePrSidebarStyles = StyleSheet.create({ fontSize: typography.metaSize, fontWeight: '700' }, - openWebButton: { - minWidth: 32, - minHeight: 32, - alignItems: 'center', - justifyContent: 'center', - borderRadius: radii.button - }, prTitle: { color: colors.textPrimary, fontSize: typography.titleSize, diff --git a/mobile/src/source-control/MobileGitHistoryList.tsx b/mobile/src/source-control/MobileGitHistoryList.tsx index db12d3117f1..d88fae540d8 100644 --- a/mobile/src/source-control/MobileGitHistoryList.tsx +++ b/mobile/src/source-control/MobileGitHistoryList.tsx @@ -99,7 +99,14 @@ export const MobileGitHistoryList = memo(function MobileGitHistoryList({ (row: MobileCommitRow) => { const next = expanded === row.id ? null : row.id setExpanded(next) - if (next && client && !filesById[row.id]) { + if (next && !filesById[row.id]) { + // No client (disconnected while cached rows stay visible): resolve to an + // empty file list so the row shows "No file changes" instead of a spinner + // that never completes — no request can be made. + if (!client) { + setFilesById((prev) => ({ ...prev, [row.id]: [] })) + return + } setFilesById((prev) => ({ ...prev, [row.id]: 'loading' })) void client .sendRequest('git.commitCompare', { worktree: `id:${worktreeId}`, commitId: row.id }) diff --git a/mobile/src/source-control/MobileSourceControlPanel.tsx b/mobile/src/source-control/MobileSourceControlPanel.tsx index e16c2d05388..6936041a44a 100644 --- a/mobile/src/source-control/MobileSourceControlPanel.tsx +++ b/mobile/src/source-control/MobileSourceControlPanel.tsx @@ -133,12 +133,16 @@ export function MobileSourceControlPanel({ }, [worktreeId]) const statusBranch = status?.branch ?? null const statusHead = status?.head ?? branchCompareResult?.summary.headOid ?? null - if (statusBranch) { - lastPrBranchRef.current = statusBranch - } - if (statusHead) { - lastPrHeadRef.current = statusHead - } + // Write last-known identity in an effect, not the render body: a discarded + // concurrent render must not leave the fallback holding a never-committed value. + useEffect(() => { + if (statusBranch) { + lastPrBranchRef.current = statusBranch + } + if (statusHead) { + lastPrHeadRef.current = statusHead + } + }, [statusBranch, statusHead]) const prBranch = statusBranch ?? lastPrBranchRef.current const prHeadSha = statusHead ?? lastPrHeadRef.current const prController = useMobilePrSidebarController({