150 lines
6.5 KiB
TypeScript
150 lines
6.5 KiB
TypeScript
import { useCallback, useEffect, useState } from 'react'
|
|
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
import { Text, View, type LayoutChangeEvent } from 'react-native'
|
|
import type { useMobileDiffReviewController } from '../session/use-mobile-diff-review-controller'
|
|
import { useResponsiveLayout } from '../layout/responsive-layout'
|
|
import { MobileDiffReviewBody } from './MobileDiffReviewBody'
|
|
import { MobileDiffReviewDrawers } from './MobileDiffReviewDrawers'
|
|
import { MobileDiffReviewFileSummary } from './MobileDiffReviewFileSummary'
|
|
import { MobileDiffReviewFooter } from './MobileDiffReviewFooter'
|
|
import { MobileDiffReviewHeader } from './MobileDiffReviewHeader'
|
|
import { MobilePRSidebar } from './MobilePRSidebar'
|
|
import { RightDrawer } from './RightDrawer'
|
|
import { mobilePrSidebarStyles, PR_SIDEBAR_DOCK_WIDTH } from './pr-sidebar/mobile-pr-sidebar-styles'
|
|
import { canDockPrSidebar, resolvePresentationMode } from './mobile-pr-sidebar-presentation'
|
|
import { mobileDiffReviewStyles as styles } from './mobile-diff-review-screen-styles'
|
|
|
|
type Props = {
|
|
controller: ReturnType<typeof useMobileDiffReviewController>
|
|
onBack: () => void
|
|
}
|
|
|
|
export function MobileDiffReviewScreenView({ controller, onBack }: Props) {
|
|
const { isWideLayout } = useResponsiveLayout()
|
|
const insets = useSafeAreaInsets()
|
|
const [contentRowWidth, setContentRowWidth] = useState(0)
|
|
const canDockSidebar = canDockPrSidebar({
|
|
isWideLayout,
|
|
availableWidth: contentRowWidth,
|
|
dockWidth: PR_SIDEBAR_DOCK_WIDTH
|
|
})
|
|
const presentationMode = resolvePresentationMode(isWideLayout, canDockSidebar)
|
|
// Inline-dock the sidebar only when wide and the repo is GitHub; otherwise it
|
|
// lives in the RightDrawer overlay toggled by showPRSidebar.
|
|
const showInlineDock = presentationMode === 'inline' && controller.prSidebarIsGithubRepo
|
|
|
|
// The docked sidebar has no trigger to tap, so load its PR data once it becomes
|
|
// visible (the overlay loads on trigger press instead).
|
|
const prSidebarKind = controller.prSidebarState.kind
|
|
const loadPRSidebar = controller.refetchPRSidebar
|
|
useEffect(() => {
|
|
if (showInlineDock && prSidebarKind === 'hidden') {
|
|
loadPRSidebar()
|
|
}
|
|
}, [showInlineDock, prSidebarKind, loadPRSidebar])
|
|
|
|
const handleContentRowLayout = useCallback((event: LayoutChangeEvent) => {
|
|
const width = Math.round(event.nativeEvent.layout.width)
|
|
setContentRowWidth((prev) => (prev === width ? prev : width))
|
|
}, [])
|
|
|
|
return (
|
|
<SafeAreaView style={styles.safeArea} edges={['top']}>
|
|
<MobileDiffReviewHeader
|
|
filter={controller.filter}
|
|
isWideLayout={isWideLayout}
|
|
prSidebarIsGithubRepo={controller.prSidebarIsGithubRepo}
|
|
prSidebarCanDock={presentationMode === 'inline'}
|
|
queueLength={controller.queue.length}
|
|
reviewedCount={controller.reviewedCount}
|
|
unsentCount={controller.unsentComments.length}
|
|
worktreeLabel={controller.worktreeLabel}
|
|
onBack={onBack}
|
|
onOpenActions={() => controller.setShowOverflow(true)}
|
|
onOpenPRSidebar={controller.openPRSidebar}
|
|
onSelectFilter={controller.selectFilter}
|
|
/>
|
|
<View style={{ flex: 1, flexDirection: 'row' }} onLayout={handleContentRowLayout}>
|
|
{/* Diff column keeps its full layout; in wide mode the docked sidebar sits
|
|
beside it and each column scrolls independently. */}
|
|
<View style={{ flex: 1, minWidth: 0 }}>
|
|
{controller.currentItem ? (
|
|
<MobileDiffReviewFileSummary
|
|
currentIndex={controller.currentIndex}
|
|
diffState={controller.diffState}
|
|
fileNotes={controller.fileNotes}
|
|
filteredCount={controller.filteredQueue.length}
|
|
item={controller.currentItem}
|
|
staleCommentIds={controller.staleCommentIds}
|
|
onEditNote={controller.openEditComposer}
|
|
onJumpHunk={controller.jumpHunk}
|
|
/>
|
|
) : null}
|
|
{controller.actionError ? (
|
|
<View style={styles.actionError}>
|
|
<Text style={styles.actionErrorText}>{controller.actionError}</Text>
|
|
</View>
|
|
) : null}
|
|
<MobileDiffReviewBody
|
|
activeHunkIndex={controller.activeHunkIndex}
|
|
commentsByLine={controller.commentsByLine}
|
|
currentItem={controller.currentItem}
|
|
diffState={controller.diffState}
|
|
filteredCount={controller.filteredQueue.length}
|
|
listRef={controller.listRef}
|
|
screenState={controller.screenState}
|
|
staleCommentIds={controller.staleCommentIds}
|
|
onAddNote={controller.openComposer}
|
|
onEditNote={controller.openEditComposer}
|
|
onRetry={controller.retryAction}
|
|
/>
|
|
{controller.currentItem ? (
|
|
<MobileDiffReviewFooter
|
|
busyAction={controller.busyAction}
|
|
item={controller.currentItem}
|
|
onAddFileNote={() => controller.openComposer(0)}
|
|
onDiscard={controller.setDiscardTarget}
|
|
onGitMutation={(method, item) => void controller.runGitMutation(method, item)}
|
|
onMarkReviewed={() => void controller.markReviewed()}
|
|
onMoveFile={controller.moveFile}
|
|
/>
|
|
) : null}
|
|
</View>
|
|
{showInlineDock ? (
|
|
<View style={mobilePrSidebarStyles.dockColumn}>
|
|
<MobilePRSidebar
|
|
state={controller.prSidebarState}
|
|
onRetry={controller.retryPRSidebar}
|
|
refetch={controller.refetchPRSidebar}
|
|
client={controller.client}
|
|
connState={controller.connState}
|
|
worktreeId={controller.worktreeId}
|
|
gitBranch={controller.prSidebarBranch}
|
|
headSha={controller.prSidebarHeadSha}
|
|
bottomInset={insets.bottom}
|
|
/>
|
|
</View>
|
|
) : null}
|
|
</View>
|
|
<MobileDiffReviewDrawers controller={controller} />
|
|
{presentationMode === 'overlay' ? (
|
|
<RightDrawer
|
|
visible={controller.showPRSidebar}
|
|
onClose={() => controller.setShowPRSidebar(false)}
|
|
>
|
|
<MobilePRSidebar
|
|
state={controller.prSidebarState}
|
|
onRetry={controller.retryPRSidebar}
|
|
refetch={controller.refetchPRSidebar}
|
|
client={controller.client}
|
|
connState={controller.connState}
|
|
worktreeId={controller.worktreeId}
|
|
gitBranch={controller.prSidebarBranch}
|
|
headSha={controller.prSidebarHeadSha}
|
|
/>
|
|
</RightDrawer>
|
|
) : null}
|
|
</SafeAreaView>
|
|
)
|
|
}
|