From a887da1a146e1c5a9366f403899290d55fc875bb Mon Sep 17 00:00:00 2001 From: Pixel Perfect Date: Thu, 8 Oct 2026 04:05:28 -0700 Subject: [PATCH 1/2] fix(mobile): let sheet lists scroll before dismissal --- .../cloud/ConnectOnboardingRouteScreen.tsx | 39 ++++++++++++------- .../connection/ConnectionsNewRouteScreen.tsx | 1 + .../features/threads/ThreadAgentsSheet.tsx | 1 + .../features/threads/ThreadQueueControl.tsx | 1 + .../features/threads/git/GitBranchesSheet.tsx | 1 + .../features/threads/git/GitCommitSheet.tsx | 1 + .../features/threads/git/GitConfirmSheet.tsx | 1 + .../features/threads/git/GitOverviewSheet.tsx | 36 +++++++++++------ 8 files changed, 57 insertions(+), 24 deletions(-) diff --git a/apps/mobile/src/features/cloud/ConnectOnboardingRouteScreen.tsx b/apps/mobile/src/features/cloud/ConnectOnboardingRouteScreen.tsx index b0e25c6dcdb9..ec57efbd98f4 100644 --- a/apps/mobile/src/features/cloud/ConnectOnboardingRouteScreen.tsx +++ b/apps/mobile/src/features/cloud/ConnectOnboardingRouteScreen.tsx @@ -3,7 +3,7 @@ import { NativeHeaderToolbar } from "../../native/StackHeader"; import { useAuth } from "@clerk/expo"; import { StackActions, useNavigation } from "@react-navigation/native"; import { useCallback, useEffect, useState } from "react"; -import { Platform, Pressable, RefreshControl, View } from "react-native"; +import { Platform, Pressable, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { reportAtomCommandResult, settlePromise } from "@t3tools/client-runtime/state/runtime"; @@ -54,15 +54,15 @@ function ConfiguredConnectOnboardingRouteScreen() { cloudEnvironments: null, }); - // Pull-to-refresh tracks its own spinner instead of discovery's refreshing - // flag, so background refreshes (e.g. the sign-in one) don't yank the - // content down. - const [isPullRefreshing, setIsPullRefreshing] = useState(false); - const handlePullRefresh = useCallback(() => { + const [isRefreshing, setIsRefreshing] = useState(false); + const handleRefresh = useCallback(() => { void (async () => { - setIsPullRefreshing(true); - await refreshRelayEnvironments(); - setIsPullRefreshing(false); + setIsRefreshing(true); + try { + await refreshRelayEnvironments(); + } finally { + setIsRefreshing(false); + } })(); }, [refreshRelayEnvironments]); @@ -87,14 +87,30 @@ function ConfiguredConnectOnboardingRouteScreen() { {Platform.OS === "android" ? ( ) : ( + )} - } > {isSignedIn ? ( ) { const content = ( ) { queuedRuns.map(({ run }) => ({ id: run.id, ...rowLayouts.current.get(run.id) })); const content = ( { - setIsPullRefreshing(true); + const [isRefreshing, setIsRefreshing] = useState(false); + const handleRefresh = useCallback(async () => { + setIsRefreshing(true); try { await gitActions.refreshSelectedThreadGitStatus(); } finally { - setIsPullRefreshing(false); + setIsRefreshing(false); } }, [gitActions]); const content = ( void handlePullRefresh()} /> + isInspector ? ( + void handleRefresh()} /> + ) : undefined } > @@ -452,6 +454,18 @@ function GitOverviewSheetContent(props: GitOverviewSheetProps) { void handleRefresh(), + sharesBackground: false, + tintColor: foregroundColor, + type: "button", + }, + ]} hideBackButton hideShadow={false} navigationItemStyle="editor" @@ -474,11 +488,11 @@ function GitOverviewSheetContent(props: GitOverviewSheetProps) { { id: "refresh", title: "Refresh repository status", - attributes: { disabled: busy || isPullRefreshing }, + attributes: { disabled: busy || isRefreshing }, }, ]} onPressAction={({ nativeEvent }) => { - if (nativeEvent.event === "refresh") void handlePullRefresh(); + if (nativeEvent.event === "refresh") void handleRefresh(); }} > {(open) => ( From ff6bb2647ed0e2a3ed5a586e421ee3c2bb8fda92 Mon Sep 17 00:00:00 2001 From: Pixel Perfect Date: Thu, 8 Oct 2026 04:18:53 -0700 Subject: [PATCH 2/2] fix(mobile): preserve iOS sheet refresh gestures --- .../cloud/ConnectOnboardingRouteScreen.tsx | 13 ++++++------- .../features/threads/git/GitOverviewSheet.tsx | 19 ++++--------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/apps/mobile/src/features/cloud/ConnectOnboardingRouteScreen.tsx b/apps/mobile/src/features/cloud/ConnectOnboardingRouteScreen.tsx index ec57efbd98f4..7987e8ff6d66 100644 --- a/apps/mobile/src/features/cloud/ConnectOnboardingRouteScreen.tsx +++ b/apps/mobile/src/features/cloud/ConnectOnboardingRouteScreen.tsx @@ -3,7 +3,7 @@ import { NativeHeaderToolbar } from "../../native/StackHeader"; import { useAuth } from "@clerk/expo"; import { StackActions, useNavigation } from "@react-navigation/native"; import { useCallback, useEffect, useState } from "react"; -import { Platform, Pressable, View } from "react-native"; +import { Platform, Pressable, RefreshControl, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { reportAtomCommandResult, settlePromise } from "@t3tools/client-runtime/state/runtime"; @@ -100,12 +100,6 @@ function ConfiguredConnectOnboardingRouteScreen() { /> ) : ( - )} @@ -121,6 +115,11 @@ function ConfiguredConnectOnboardingRouteScreen() { paddingHorizontal: 20, paddingTop: 16, }} + refreshControl={ + Platform.OS === "ios" ? ( + + ) : undefined + } > {isSignedIn ? ( void handleRefresh()} /> ) : undefined } @@ -437,8 +437,9 @@ function GitOverviewSheetContent(props: GitOverviewSheetProps) { if (Platform.OS === "ios") { // Compact form sheet: a plain screen presented as formSheet never renders a // stack header, so — like the Settings sheet — the header must come from a - // nested native stack INSIDE the sheet. Refresh belongs in the header so - // dragging down at the top of the content can dismiss the sheet. + // nested native stack INSIDE the sheet. This reuses the exact structure of the + // inspector branch below: branch as the title, status summary as the native + // subtitle, and content that owns pull-to-refresh. return ( @@ -454,18 +455,6 @@ function GitOverviewSheetContent(props: GitOverviewSheetProps) { void handleRefresh(), - sharesBackground: false, - tintColor: foregroundColor, - type: "button", - }, - ]} hideBackButton hideShadow={false} navigationItemStyle="editor"