1import { resolveDefaultBranchActionDialogCopy } from "@t3tools/client-runtime/state/vcs";
2import { resolveAutoFeatureBranchName } from "@t3tools/shared/git";
3import * as Arr from "effect/Array";
4import * as Result from "effect/Result";
5import { StackActions, useNavigation, type StaticScreenProps } from "@react-navigation/native";
6import { useCallback, useMemo } from "react";
7import { Platform, ScrollView, useWindowDimensions, View } from "react-native";
8
9import { useSafeAreaInsets } from "react-native-safe-area-context";
10
11import { AndroidSheetHeader } from "../../../components/AndroidScreenHeader";
12import { MaterialScreenContent } from "../../../components/MaterialScreenContent";
13import { NativeStackScreenOptions } from "../../../native/StackHeader";
14import { AppText as Text } from "../../../components/AppText";
15import { useSelectedThreadGitActions } from "../../../state/use-selected-thread-git-actions";
16import { useSelectedThreadGitState } from "../../../state/use-selected-thread-git-state";
17import { SheetActionButton } from "./gitSheetComponents";
18
19type 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
29export 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}