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.
This commit is contained in:
Jinjing
2026-07-09 18:04:29 -07:00
parent a91ab04b19
commit 89d022a3a3
6 changed files with 24 additions and 16 deletions
@@ -9,6 +9,7 @@ export default function HistoryRedirect() {
hostId?: string | string[]
worktreeId?: string | string[]
name?: string | string[]
origin?: string | string[]
}>()
return (
<Redirect
@@ -18,6 +19,7 @@ export default function HistoryRedirect() {
hostId: firstParam(params.hostId),
worktreeId: firstParam(params.worktreeId),
name: firstParam(params.name),
origin: firstParam(params.origin),
tab: 'history'
}
}}
@@ -10,6 +10,7 @@ export default function PrRedirect() {
hostId?: string | string[]
worktreeId?: string | string[]
name?: string | string[]
origin?: string | string[]
}>()
return (
<Redirect
@@ -19,6 +20,7 @@ export default function PrRedirect() {
hostId: firstParam(params.hostId),
worktreeId: firstParam(params.worktreeId),
name: firstParam(params.name),
origin: firstParam(params.origin),
tab: 'pr'
}
}}
@@ -55,8 +55,8 @@ export function PRSidebarHeader({ pr, details, titleAction }: Props) {
onPress={openPr}
hitSlop={8}
accessibilityRole="link"
accessibilityLabel={`Open pull request #${pr.number} on the web`}
style={({ pressed }) => [styles.openWebButton, pressed && { opacity: 0.6 }]}
accessibilityLabel={`Open pull request #${pr.number} in browser`}
style={({ pressed }) => [styles.iconButton, pressed && { opacity: 0.6 }]}
>
<ExternalLink size={16} color={colors.textSecondary} strokeWidth={2.2} />
</Pressable>
@@ -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,
@@ -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 })
@@ -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({