feat(mobile): surface "Link an existing PR" in PR sidebar empty state (#5963)
* feat(mobile): surface "Link an existing PR" in PR sidebar empty state The mobile link-PR building blocks (MobileLinkPrForm, linkMobilePr, parseGitHubPrReference) existed and were tested, but had no entry point — only unlink was wired up. Add a "Link an existing PR" action to the no-PR empty state that opens MobileLinkPrForm and refetches the sidebar on success, mirroring desktop's link flow (GitHub-scoped via worktree.set). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * Polish mobile PR link empty state --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Jinwoo-H <jinwoo0825@gmail.com>
This commit is contained in:
parent
20a51978e5
commit
375c4df8b3
|
|
@ -1,12 +1,13 @@
|
|||
import { useEffect, useState } from 'react'
|
||||
import { ActivityIndicator, Pressable, Text, View } from 'react-native'
|
||||
import { GitPullRequestArrow, RefreshCw } from 'lucide-react-native'
|
||||
import { GitPullRequestArrow, Link2, RefreshCw } from 'lucide-react-native'
|
||||
import { colors } from '../../theme/mobile-theme'
|
||||
import type { RpcClient } from '../../transport/rpc-client'
|
||||
import { resolveMobilePrPrefill, type MobilePrPrefill } from '../../source-control/mobile-pr-create'
|
||||
import { fetchWorktreeLinkedPR } from '../../source-control/mobile-pr-link'
|
||||
import { openMobilePrUrl } from '../MobilePrComposeSheet'
|
||||
import { MobilePrComposeForm } from './MobilePrComposeForm'
|
||||
import { MobileLinkPrForm } from './MobileLinkPrForm'
|
||||
import { prCreateEmptyStateStyles as styles } from './pr-create-empty-state-styles'
|
||||
|
||||
type Props = {
|
||||
|
|
@ -17,18 +18,18 @@ type Props = {
|
|||
onCreated: () => void
|
||||
}
|
||||
|
||||
type Mode = 'choose' | 'create'
|
||||
type Mode = 'choose' | 'create' | 'link'
|
||||
|
||||
// Empty state for a branch with no PR. Keep this scoped to desktop's no-PR
|
||||
// surface: create/refresh here; linked-PR edits belong outside this panel.
|
||||
// Empty state for a branch with no PR: create a new PR, or link an existing one
|
||||
// (the no-PR surface is the natural home for linking — desktop's link entry lives
|
||||
// on its PR card, but on mobile this is where a user lands with nothing linked).
|
||||
export function PrSidebarCreateEmptyState({ client, worktreeId, gitBranch, onCreated }: Props) {
|
||||
const [prefill, setPrefill] = useState<MobilePrPrefill | null>(null)
|
||||
const [mode, setMode] = useState<Mode>('choose')
|
||||
const [loading, setLoading] = useState(false)
|
||||
const [createWarning, setCreateWarning] = useState<string | null>(null)
|
||||
// A persisted linkedPR while the branch shows no PR means the linked PR could
|
||||
// not be resolved. Mention it, but keep link editing out of this desktop-parity
|
||||
// create surface.
|
||||
// not be resolved. Mention it while still allowing the user to relink.
|
||||
const [orphanLinkedPR, setOrphanLinkedPR] = useState<number | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -103,6 +104,22 @@ export function PrSidebarCreateEmptyState({ client, worktreeId, gitBranch, onCre
|
|||
)
|
||||
}
|
||||
|
||||
if (mode === 'link') {
|
||||
return (
|
||||
<View style={styles.composerArea}>
|
||||
<MobileLinkPrForm
|
||||
client={client}
|
||||
worktreeId={worktreeId}
|
||||
onCancel={() => setMode('choose')}
|
||||
onLinked={() => {
|
||||
setMode('choose')
|
||||
onCreated()
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={styles.section}>
|
||||
<View style={styles.header}>
|
||||
|
|
@ -148,6 +165,26 @@ export function PrSidebarCreateEmptyState({ client, worktreeId, gitBranch, onCre
|
|||
: 'The current branch is not linked to an open PR.'}
|
||||
</Text>
|
||||
{createWarning ? <Text style={styles.bodyText}>{createWarning}</Text> : null}
|
||||
<Pressable
|
||||
style={({ pressed }) => [
|
||||
styles.linkButton,
|
||||
!client && styles.linkButtonDisabled,
|
||||
pressed && styles.linkButtonPressed
|
||||
]}
|
||||
onPress={() => setMode('link')}
|
||||
disabled={!client}
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel="Link an existing pull request"
|
||||
accessibilityState={{ disabled: !client }}
|
||||
hitSlop={6}
|
||||
>
|
||||
<Link2
|
||||
size={14}
|
||||
color={client ? colors.textSecondary : colors.textMuted}
|
||||
strokeWidth={2.2}
|
||||
/>
|
||||
<Text style={styles.linkButtonText}>Link an existing PR</Text>
|
||||
</Pressable>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -83,5 +83,25 @@ export const prCreateEmptyStateStyles = StyleSheet.create({
|
|||
borderBottomWidth: StyleSheet.hairlineWidth,
|
||||
borderBottomColor: colors.borderSubtle,
|
||||
padding: spacing.md
|
||||
},
|
||||
// Secondary link-an-existing-PR affordance, set apart from the body copy.
|
||||
linkButton: {
|
||||
marginTop: spacing.xs,
|
||||
minHeight: 32,
|
||||
alignSelf: 'flex-start',
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: spacing.xs
|
||||
},
|
||||
linkButtonDisabled: {
|
||||
opacity: 0.5
|
||||
},
|
||||
linkButtonPressed: {
|
||||
opacity: 0.6
|
||||
},
|
||||
linkButtonText: {
|
||||
color: colors.textSecondary,
|
||||
fontSize: typography.metaSize,
|
||||
fontWeight: '600'
|
||||
}
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue