authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-08-31 11:40:53-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-31 11:40:53-07:00
log746c932e164d49ddb4ac98a42176194b11bf0699
tree295b2810a1381b9514bb7423e776dc2d2e6b8c19
parentbba79cc254b65969bde6b6bfc3032c3b5b9316ae
signature Signed by PGP key B5690EEEBB952194

fix(mobile): defer draft navigation until submission completes (#8914)


1 files changed, 23 insertions(+), 7 deletions(-)

apps/mobile/src/features/threads/NewTaskDraftScreen.tsx+23-7
......@@ -1,9 +1,11 @@
11import { NativeHeaderToolbar, NativeStackScreenOptions } from "../../native/StackHeader";
22import {
3 CommonActions,
34 StackActions,
45 useFocusEffect,
56 useNavigation,
67 usePreventRemove,
8 type NavigationAction,
79} from "@react-navigation/native";
810import { useCallback, useEffect, useRef, useState } from "react";
911import { Alert, Platform, Pressable, ScrollView, View } from "react-native";
......@@ -212,6 +214,9 @@ export function NewTaskDraftScreen(props: {
212214 const [isCancellingShareImport, setIsCancellingShareImport] = useState(false);
213215 const [cancelledIncomingShareId, setCancelledIncomingShareId] = useState<string | null>(null);
214216 const [isReturningToProjectPicker, setIsReturningToProjectPicker] = useState(false);
217 const [submitNavigationAction, setSubmitNavigationAction] = useState<NavigationAction | null>(
218 null,
219 );
215220 const [shareImportAttempt, setShareImportAttempt] = useState(0);
216221 const startedShareImportKeyRef = useRef<string | null>(null);
217222 const cancellingShareImportKeyRef = useRef<string | null>(null);
......@@ -275,12 +280,23 @@ export function NewTaskDraftScreen(props: {
275280 voiceInput.elapsedSeconds,
276281 );
277282 const isVoiceInputPresented = voicePresentation.statusLabel !== null;
278 usePreventRemove(
283 const preventRemove =
279284 (isIncomingShareTransferPending && !isProjectPickerReturnActive) ||
280 isCancellingShareImport ||
281 flow.submitting,
282 () => undefined,
283 );
285 isCancellingShareImport ||
286 flow.submitting;
287 usePreventRemove(preventRemove, () => undefined);
288 useEffect(() => {
289 if (preventRemove || submitNavigationAction === null) {
290 return;
291 }
292 // Give the guard update a frame to reach the parent sheet before navigating,
293 // just like the project-picker fallback below.
294 const frame = requestAnimationFrame(() => {
295 setSubmitNavigationAction(null);
296 (navigation.getParent() ?? navigation).dispatch(submitNavigationAction);
297 });
298 return () => cancelAnimationFrame(frame);
299 }, [navigation, preventRemove, submitNavigationAction]);
284300 const hasImportedIncomingShare = Boolean(
285301 props.incomingShareId &&
286302 flow.draftKey &&
......@@ -872,7 +888,7 @@ export function NewTaskDraftScreen(props: {
872888 clearWorkspaceSelection: true,
873889 });
874890 }
875 navigation.getParent()?.goBack();
891 setSubmitNavigationAction(CommonActions.goBack());
876892 return;
877893 }
878894
......@@ -943,7 +959,7 @@ export function NewTaskDraftScreen(props: {
943959 clearWorkspaceSelection: true,
944960 });
945961 }
946 navigation.dispatch(
962 setSubmitNavigationAction(
947963 StackActions.replace("Thread", {
948964 environmentId: String(result.value.environmentId),
949965 threadId: String(result.value.threadId),