import { ActivityIndicator, Pressable, ScrollView, Text, View } from 'react-native'
import { RotateCw } from 'lucide-react-native'
import { colors } from '../theme/mobile-theme'
import type { PrSidebarState } from '../session/mobile-pr-sidebar-state'
import type { ConnectionState } from '../transport/types'
import type { RpcClient } from '../transport/rpc-client'
import { useMobilePrActions, type MobilePrActions } from '../session/use-mobile-pr-actions'
import {
useMobilePrCommentActions,
type MobilePrCommentActions
} from '../session/use-mobile-pr-comment-actions'
import {
useMobilePrTitleAction,
type MobilePrTitleAction
} from '../session/use-mobile-pr-title-action'
import { useMobilePrAiTriage, type MobilePrAiTriage } from '../session/use-mobile-pr-ai-triage'
import { buildFixChecksPrompt, buildResolveConflictsPrompt } from '../session/pr-ai-triage-prompt'
import { prSidebarRenderBranch } from './mobile-pr-sidebar-presentation'
import { mobilePrSidebarStyles as styles } from './pr-sidebar/mobile-pr-sidebar-styles'
import type { MobileGitStatusResult } from '../source-control/mobile-git-status'
import { PRSidebarHeader } from './pr-sidebar/PRSidebarHeader'
import { PRConflictingFilesSection } from './pr-sidebar/PRConflictingFilesSection'
import { PRActionsSection } from './pr-sidebar/PRActionsSection'
import { PRReviewersSection } from './pr-sidebar/PRReviewersSection'
import { PRChecksSection } from './pr-sidebar/PRChecksSection'
import { PRCommentsSection } from './pr-sidebar/PRCommentsSection'
import { PrSidebarCreateEmptyState } from './pr-sidebar/PrSidebarCreateEmptyState'
type Props = {
state: PrSidebarState
onRetry: () => void
// Re-fetches authoritative PR data after a successful mutation (U3/U6) or create.
refetch: () => void
// Threaded to sections for github.* fetches + mutations.
client: RpcClient | null
connState: ConnectionState
worktreeId: string
gitBranch: string | null
gitStatus: MobileGitStatusResult | null
headSha: string | null
bottomInset?: number
}
// Mutation hooks run unconditionally here and gate internally until a PR is ready.
export function MobilePRSidebar({
state,
onRetry,
refetch,
client,
connState,
worktreeId,
gitBranch,
gitStatus,
headSha,
bottomInset = 0
}: Props) {
const branch = prSidebarRenderBranch(state)
// prNumber is 0 until ready; the hook gates on `ready` so it never fires early.
const prNumber = state.kind === 'ready' ? state.data.pr.number : 0
const prRepo =
state.kind === 'ready'
? state.data.pr.prRepo
? { owner: state.data.pr.prRepo.owner, repo: state.data.pr.prRepo.repo }
: null
: null
const actions = useMobilePrActions({
client,
connState,
worktreeId,
prNumber,
headSha,
prRepo,
refetch
})
const commentActions = useMobilePrCommentActions({
client,
connState,
worktreeId,
prNumber,
prRepo,
refetch
})
const titleAction = useMobilePrTitleAction({
client,
connState,
worktreeId,
prNumber,
prRepo,
refetch
})
const triage = useMobilePrAiTriage({ client, connState, worktreeId })
return (
)
}
function PrSidebarContent({
branch,
state,
onRetry,
refetch,
client,
connState,
worktreeId,
gitBranch,
gitStatus,
actions,
commentActions,
titleAction,
triage
}: {
branch: ReturnType
state: PrSidebarState
onRetry: () => void
refetch: () => void
client: RpcClient | null
connState: ConnectionState
worktreeId: string
gitBranch: string | null
gitStatus: MobileGitStatusResult | null
actions: MobilePrActions
commentActions: MobilePrCommentActions
titleAction: MobilePrTitleAction
triage: MobilePrAiTriage
}) {
if (branch === 'loading') {
return (
Loading pull request…
)
}
if (branch === 'error') {
const message = state.kind === 'error' ? state.message : 'Something went wrong.'
return (
{message}
Retry
)
}
if (branch === 'blocked' || actions.blocked) {
// Permanent failure (R9): explanatory, no retry-encouragement styling. A
// mutation-time block (actions.blocked) routes here even from a ready state.
const message =
actions.blocked ??
(state.kind === 'blocked'
? state.message
: 'Not permitted — your GitHub account is not connected.')
return (
{message}
)
}
if (branch === 'none') {
// GitHub repo, but the current branch has no open PR — offer to create one
// (desktop parity) rather than showing a dead-end message.
return (
)
}
if (branch === 'ready' && state.kind === 'ready') {
return (
)
}
return null
}
function PrSidebarSections({
data,
client,
worktreeId,
actions,
commentActions,
titleAction,
triage,
refetch
}: {
data: Extract['data']
client: RpcClient | null
worktreeId: string
actions: MobilePrActions
commentActions: MobilePrCommentActions
titleAction: MobilePrTitleAction
triage: MobilePrAiTriage
refetch: () => void
}) {
const pr = data.pr
// Bind the triage launchers to this PR's data; the prompt builders are pure so
// building lazily inside launch() keeps a stale capture from leaking in.
const checksTriage = {
fixChecks: () =>
void triage.launch('fix-checks', () =>
buildFixChecksPrompt({
prNumber: pr.number,
prTitle: pr.title,
prUrl: pr.url,
checks: data.checks
})
),
isBusy: triage.isBusy('fix-checks'),
error: triage.error
}
const conflictsTriage = {
resolveConflicts: () =>
void triage.launch('resolve-conflicts', () =>
buildResolveConflictsPrompt({
prNumber: pr.number,
baseRef: pr.conflictSummary?.baseRef ?? pr.baseRefName ?? null,
files: pr.conflictSummary?.files ?? []
})
),
isBusy: triage.isBusy('resolve-conflicts'),
error: triage.error
}
return (
<>
{/* Conflicting-files section mirrors desktop order: directly below the header,
before actions/checks. Renders only when the PR has merge conflicts. */}
>
)
}