| 1 | import { resolveDefaultBranchActionDialogCopy } from "@t3tools/client-runtime/state/vcs"; |
| 2 | import { resolveAutoFeatureBranchName } from "@t3tools/shared/git"; |
| 3 | import * as Arr from "effect/Array"; |
| 4 | import * as Result from "effect/Result"; |
| 5 | import { StackActions, useNavigation, type StaticScreenProps } from "@react-navigation/native"; |
| 6 | import { useCallback, useMemo } from "react"; |
| 7 | import { Platform, ScrollView, useWindowDimensions, View } from "react-native"; |
| 8 | |
| 9 | import { useSafeAreaInsets } from "react-native-safe-area-context"; |
| 10 | |
| 11 | import { AndroidSheetHeader } from "../../../components/AndroidScreenHeader"; |
| 12 | import { MaterialScreenContent } from "../../../components/MaterialScreenContent"; |
| 13 | import { NativeStackScreenOptions } from "../../../native/StackHeader"; |
| 14 | import { AppText as Text } from "../../../components/AppText"; |
| 15 | import { useSelectedThreadGitActions } from "../../../state/use-selected-thread-git-actions"; |
| 16 | import { useSelectedThreadGitState } from "../../../state/use-selected-thread-git-state"; |
| 17 | import { SheetActionButton } from "./gitSheetComponents"; |
| 18 | |
| 19 | type GitConfirmSheetProps = StaticScreenProps<{ |
| 20 | readonly environmentId: string; |
| 21 | readonly threadId: string; |
| 22 | readonly confirmAction?: string; |
| 23 | readonly branchName?: string; |
| 24 | readonly includesCommit?: string; |
| 25 | readonly commitMessage?: string; |
| 26 | readonly filePaths?: string; |
| 27 | }>; |
| 28 | |
| 29 | export function GitConfirmSheet(props: GitConfirmSheetProps) { |
| 30 | const navigation = useNavigation(); |
| 31 | const insets = useSafeAreaInsets(); |
| 32 | const { height: windowHeight } = useWindowDimensions(); |
| 33 | const gitState = useSelectedThreadGitState(); |
| 34 | const gitActions = useSelectedThreadGitActions(); |
| 35 | const { canWriteSourceControl, canChangeThreadBranch } = gitActions; |
| 36 | |
| 37 | const params = props.route.params; |
| 38 | |
| 39 | const confirmAction = params.confirmAction as |
| 40 | | "push" |
| 41 | | "create_pr" |
| 42 | | "commit_push" |
| 43 | | "commit_push_pr" |
| 44 | | undefined; |
| 45 | const branchName = params.branchName ?? ""; |
| 46 | const includesCommit = params.includesCommit === "true"; |
| 47 | const environmentId = params.environmentId ?? ""; |
| 48 | const threadId = params.threadId ?? ""; |
| 49 | |
| 50 | const copy = useMemo( |
| 51 | () => |
| 52 | confirmAction |
| 53 | ? resolveDefaultBranchActionDialogCopy({ |
| 54 | action: confirmAction, |
| 55 | branchName, |
| 56 | includesCommit, |
| 57 | }) |
| 58 | : null, |
| 59 | [branchName, confirmAction, includesCommit], |
| 60 | ); |
| 61 | |
| 62 | const continuePendingAction = useCallback(async () => { |
| 63 | if (!canWriteSourceControl || !confirmAction) return; |
| 64 | navigation.dispatch(StackActions.replace("Thread", { environmentId, threadId })); |
| 65 | await gitActions.onRunSelectedThreadGitAction({ |
| 66 | action: confirmAction, |
| 67 | ...(params.commitMessage ? { commitMessage: params.commitMessage } : {}), |
| 68 | ...(params.filePaths ? { filePaths: params.filePaths.split(",") } : {}), |
| 69 | }); |
| 70 | }, [ |
| 71 | canWriteSourceControl, |
| 72 | confirmAction, |
| 73 | environmentId, |
| 74 | gitActions, |
| 75 | params, |
| 76 | navigation, |
| 77 | threadId, |
| 78 | ]); |
| 79 | |
| 80 | const movePendingActionToFeatureBranch = useCallback(async () => { |
| 81 | if (!canChangeThreadBranch || !confirmAction) return; |
| 82 | navigation.dispatch(StackActions.replace("Thread", { environmentId, threadId })); |
| 83 | |
| 84 | if (includesCommit) { |
| 85 | await gitActions.onRunSelectedThreadGitAction({ |
| 86 | action: confirmAction, |
| 87 | featureBranch: true, |
| 88 | ...(params.commitMessage ? { commitMessage: params.commitMessage } : {}), |
| 89 | ...(params.filePaths ? { filePaths: params.filePaths.split(",") } : {}), |
| 90 | }); |
| 91 | return; |
| 92 | } |
| 93 | |
| 94 | const branches = |
| 95 | gitState.selectedThreadBranches.length > 0 |
| 96 | ? gitState.selectedThreadBranches |
| 97 | : await gitActions.refreshSelectedThreadBranches(); |
| 98 | const newBranchName = resolveAutoFeatureBranchName( |
| 99 | Arr.filterMap(branches, (branch) => |
| 100 | branch.isRemote ? Result.failVoid : Result.succeed(branch.name), |
| 101 | ), |
| 102 | ); |
| 103 | const created = await gitActions.onCreateSelectedThreadBranch(newBranchName); |
| 104 | if (created === null) return; |
| 105 | await gitActions.onRunSelectedThreadGitAction({ action: confirmAction }); |
| 106 | }, [ |
| 107 | canChangeThreadBranch, |
| 108 | confirmAction, |
| 109 | gitActions, |
| 110 | gitState.selectedThreadBranches, |
| 111 | includesCommit, |
| 112 | params, |
| 113 | navigation, |
| 114 | environmentId, |
| 115 | threadId, |
| 116 | ]); |
| 117 | |
| 118 | return ( |
| 119 | <View |
| 120 | collapsable={false} |
| 121 | className="bg-sheet ios:flex-1" |
| 122 | style={Platform.OS === "android" ? { maxHeight: windowHeight * 0.92 } : undefined} |
| 123 | > |
| 124 | {Platform.OS === "android" ? ( |
| 125 | <NativeStackScreenOptions |
| 126 | options={{ |
| 127 | sheetCornerRadius: 28, |
| 128 | sheetAllowedDetents: "fitToContents", |
| 129 | }} |
| 130 | /> |
| 131 | ) : null} |
| 132 | {Platform.OS === "android" ? ( |
| 133 | <AndroidSheetHeader |
| 134 | title="Confirm action" |
| 135 | onBack={() => navigation.goBack()} |
| 136 | hideBottomBorder |
| 137 | /> |
| 138 | ) : ( |
| 139 | <View className="min-h-4 pt-2" /> |
| 140 | )} |
| 141 | |
| 142 | <MaterialScreenContent fitToContents> |
| 143 | <ScrollView |
| 144 | nestedScrollEnabled |
| 145 | className="android:shrink android:grow-0 ios:flex-1" |
| 146 | showsVerticalScrollIndicator={false} |
| 147 | contentContainerClassName="android:gap-2 android:p-2" |
| 148 | contentContainerStyle={ |
| 149 | Platform.OS === "android" |
| 150 | ? { paddingBottom: Math.max(insets.bottom, 18) + 8 } |
| 151 | : undefined |
| 152 | } |
| 153 | > |
| 154 | <View className="android:gap-2 android:rounded-[20px] android:bg-card android:p-3 ios:items-center ios:gap-1 ios:px-5 ios:pb-3 ios:pt-4"> |
| 155 | {Platform.OS !== "android" ? ( |
| 156 | <Text className="text-xs font-t3-bold tracking-[1px] uppercase text-foreground-muted"> |
| 157 | Confirm |
| 158 | </Text> |
| 159 | ) : null} |
| 160 | <Text className="android:text-xl android:font-t3-medium ios:text-center ios:text-3xl ios:font-t3-bold"> |
| 161 | {copy?.title ?? "Run action on default branch?"} |
| 162 | </Text> |
| 163 | <Text className="text-foreground-secondary leading-normal android:text-base ios:text-center ios:text-sm ios:font-medium"> |
| 164 | {copy?.description ?? "Choose how to continue."} |
| 165 | </Text> |
| 166 | </View> |
| 167 | |
| 168 | <View |
| 169 | className="android:gap-2 ios:gap-3 ios:px-5 ios:pt-2" |
| 170 | style={ |
| 171 | Platform.OS === "android" |
| 172 | ? undefined |
| 173 | : { paddingBottom: Math.max(insets.bottom, 18) + 8 } |
| 174 | } |
| 175 | > |
| 176 | <SheetActionButton |
| 177 | icon="arrow.right.circle" |
| 178 | label={copy?.continueLabel ?? "Continue"} |
| 179 | disabled={!canWriteSourceControl} |
| 180 | onPress={() => void continuePendingAction()} |
| 181 | /> |
| 182 | <SheetActionButton |
| 183 | icon="arrow.branch" |
| 184 | label="Feature branch & continue" |
| 185 | disabled={!canChangeThreadBranch} |
| 186 | tone="primary" |
| 187 | onPress={() => void movePendingActionToFeatureBranch()} |
| 188 | /> |
| 189 | </View> |
| 190 | </ScrollView> |
| 191 | </MaterialScreenContent> |
| 192 | </View> |
| 193 | ); |
| 194 | } |