| author | |
| committer | |
| log | 4bd0ecca19934dce38e16cd42eba5bf81c170d91 |
| tree | dd4f68e9e6a88dd31f1d2bdec4fc5bb512b7d810 |
| parent | 328dc27d63dd4b164d0211b56c8bc9100b74f3fe |
| signature | Signed by PGP key B5690EEEBB952194 |
8 files changed, 39 insertions(+), 18 deletions(-)
apps/mobile/src/features/cloud/ConnectOnboardingRouteScreen.tsx+22-10| ... | @@ -54,15 +54,15 @@ function ConfiguredConnectOnboardingRouteScreen() { | ... | @@ -54,15 +54,15 @@ function ConfiguredConnectOnboardingRouteScreen() { |
| 54 | cloudEnvironments: null, | 54 | cloudEnvironments: null, |
| 55 | }); | 55 | }); |
| 56 | 56 | ||
| 57 | // Pull-to-refresh tracks its own spinner instead of discovery's refreshing | 57 | const [isRefreshing, setIsRefreshing] = useState(false); |
| 58 | // flag, so background refreshes (e.g. the sign-in one) don't yank the | 58 | const handleRefresh = useCallback(() => { |
| 59 | // content down. | ||
| 60 | const [isPullRefreshing, setIsPullRefreshing] = useState(false); | ||
| 61 | const handlePullRefresh = useCallback(() => { | ||
| 62 | void (async () => { | 59 | void (async () => { |
| 63 | setIsPullRefreshing(true); | 60 | setIsRefreshing(true); |
| 64 | await refreshRelayEnvironments(); | 61 | try { |
| 65 | setIsPullRefreshing(false); | 62 | await refreshRelayEnvironments(); |
| 63 | } finally { | ||
| 64 | setIsRefreshing(false); | ||
| 65 | } | ||
| 66 | })(); | 66 | })(); |
| 67 | }, [refreshRelayEnvironments]); | 67 | }, [refreshRelayEnvironments]); |
| 68 | 68 | ||
| ... | @@ -87,7 +87,16 @@ function ConfiguredConnectOnboardingRouteScreen() { | ... | @@ -87,7 +87,16 @@ function ConfiguredConnectOnboardingRouteScreen() { |
| 87 | {Platform.OS === "android" ? ( | 87 | {Platform.OS === "android" ? ( |
| 88 | <AndroidSheetHeader | 88 | <AndroidSheetHeader |
| 89 | title="Set up T3 Connect" | 89 | title="Set up T3 Connect" |
| 90 | actions={[{ accessibilityLabel: "Close", icon: "xmark", onPress: handleClose }]} | 90 | actions={[ |
| 91 | { | ||
| 92 | accessibilityLabel: "Refresh environments", | ||
| 93 | disabled: isRefreshing, | ||
| 94 | icon: "arrow.clockwise", | ||
| 95 | loading: isRefreshing, | ||
| 96 | onPress: handleRefresh, | ||
| 97 | }, | ||
| 98 | { accessibilityLabel: "Close", icon: "xmark", onPress: handleClose }, | ||
| 99 | ]} | ||
| 91 | /> | 100 | /> |
| 92 | ) : ( | 101 | ) : ( |
| 93 | <NativeHeaderToolbar placement="right"> | 102 | <NativeHeaderToolbar placement="right"> |
| ... | @@ -95,6 +104,7 @@ function ConfiguredConnectOnboardingRouteScreen() { | ... | @@ -95,6 +104,7 @@ function ConfiguredConnectOnboardingRouteScreen() { |
| 95 | </NativeHeaderToolbar> | 104 | </NativeHeaderToolbar> |
| 96 | )} | 105 | )} |
| 97 | <ScrollView | 106 | <ScrollView |
| 107 | nestedScrollEnabled | ||
| 98 | alwaysBounceVertical | 108 | alwaysBounceVertical |
| 99 | contentInsetAdjustmentBehavior="automatic" | 109 | contentInsetAdjustmentBehavior="automatic" |
| 100 | showsVerticalScrollIndicator={false} | 110 | showsVerticalScrollIndicator={false} |
| ... | @@ -106,7 +116,9 @@ function ConfiguredConnectOnboardingRouteScreen() { | ... | @@ -106,7 +116,9 @@ function ConfiguredConnectOnboardingRouteScreen() { |
| 106 | paddingTop: 16, | 116 | paddingTop: 16, |
| 107 | }} | 117 | }} |
| 108 | refreshControl={ | 118 | refreshControl={ |
| 109 | <RefreshControl refreshing={isPullRefreshing} onRefresh={handlePullRefresh} /> | 119 | Platform.OS === "ios" ? ( |
| 120 | <RefreshControl refreshing={isRefreshing} onRefresh={handleRefresh} /> | ||
| 121 | ) : undefined | ||
| 110 | } | 122 | } |
| 111 | > | 123 | > |
| 112 | {isSignedIn ? ( | 124 | {isSignedIn ? ( |
apps/mobile/src/features/connection/ConnectionsNewRouteScreen.tsx+1| ... | @@ -212,6 +212,7 @@ export function ConnectionsNewRouteScreen({ | ... | @@ -212,6 +212,7 @@ export function ConnectionsNewRouteScreen({ |
| 212 | ]} | 212 | ]} |
| 213 | > | 213 | > |
| 214 | <ScrollView | 214 | <ScrollView |
| 215 | nestedScrollEnabled | ||
| 215 | contentInsetAdjustmentBehavior="automatic" | 216 | contentInsetAdjustmentBehavior="automatic" |
| 216 | keyboardShouldPersistTaps="handled" | 217 | keyboardShouldPersistTaps="handled" |
| 217 | showsVerticalScrollIndicator={false} | 218 | showsVerticalScrollIndicator={false} |
apps/mobile/src/features/threads/ThreadAgentsSheet.tsx+1| ... | @@ -56,6 +56,7 @@ export function ThreadAgentsSheet({ route }: StaticScreenProps<AgentsTarget>) { | ... | @@ -56,6 +56,7 @@ export function ThreadAgentsSheet({ route }: StaticScreenProps<AgentsTarget>) { |
| 56 | 56 | ||
| 57 | const content = ( | 57 | const content = ( |
| 58 | <ScrollView | 58 | <ScrollView |
| 59 | nestedScrollEnabled | ||
| 59 | className="flex-1" | 60 | className="flex-1" |
| 60 | // The iOS header is translucent and floats over this view; UIKit has to | 61 | // The iOS header is translucent and floats over this view; UIKit has to |
| 61 | // inset the content or the first row sits underneath the title. | 62 | // inset the content or the first row sits underneath the title. |
apps/mobile/src/features/threads/ThreadQueueControl.tsx+1| ... | @@ -179,6 +179,7 @@ export function ThreadQueueSheet({ route }: StaticScreenProps<QueueTarget>) { | ... | @@ -179,6 +179,7 @@ export function ThreadQueueSheet({ route }: StaticScreenProps<QueueTarget>) { |
| 179 | queuedRuns.map(({ run }) => ({ id: run.id, ...rowLayouts.current.get(run.id) })); | 179 | queuedRuns.map(({ run }) => ({ id: run.id, ...rowLayouts.current.get(run.id) })); |
| 180 | const content = ( | 180 | const content = ( |
| 181 | <ScrollView | 181 | <ScrollView |
| 182 | nestedScrollEnabled | ||
| 182 | className="flex-1" | 183 | className="flex-1" |
| 183 | scrollEnabled={draggedRunId === null} | 184 | scrollEnabled={draggedRunId === null} |
| 184 | // The iOS header is translucent and floats over this view; UIKit has to | 185 | // The iOS header is translucent and floats over this view; UIKit has to |
apps/mobile/src/features/threads/git/GitBranchesSheet.tsx+1| ... | @@ -84,6 +84,7 @@ export function GitBranchesSheet(_props: GitBranchesSheetProps) { | ... | @@ -84,6 +84,7 @@ export function GitBranchesSheet(_props: GitBranchesSheetProps) { |
| 84 | ) : null} | 84 | ) : null} |
| 85 | <MaterialScreenContent fitToContents> | 85 | <MaterialScreenContent fitToContents> |
| 86 | <ScrollView | 86 | <ScrollView |
| 87 | nestedScrollEnabled | ||
| 87 | className="android:shrink android:grow-0 ios:flex-1" | 88 | className="android:shrink android:grow-0 ios:flex-1" |
| 88 | keyboardShouldPersistTaps="handled" | 89 | keyboardShouldPersistTaps="handled" |
| 89 | showsVerticalScrollIndicator={false} | 90 | showsVerticalScrollIndicator={false} |
apps/mobile/src/features/threads/git/GitCommitSheet.tsx+1| ... | @@ -103,6 +103,7 @@ export function GitCommitSheet(_props: GitCommitSheetProps) { | ... | @@ -103,6 +103,7 @@ export function GitCommitSheet(_props: GitCommitSheetProps) { |
| 103 | ) : null} | 103 | ) : null} |
| 104 | <MaterialScreenContent fitToContents> | 104 | <MaterialScreenContent fitToContents> |
| 105 | <ScrollView | 105 | <ScrollView |
| 106 | nestedScrollEnabled | ||
| 106 | className="android:shrink android:grow-0 ios:flex-1" | 107 | className="android:shrink android:grow-0 ios:flex-1" |
| 107 | keyboardShouldPersistTaps="handled" | 108 | keyboardShouldPersistTaps="handled" |
| 108 | showsVerticalScrollIndicator={false} | 109 | showsVerticalScrollIndicator={false} |
apps/mobile/src/features/threads/git/GitConfirmSheet.tsx+1| ... | @@ -141,6 +141,7 @@ export function GitConfirmSheet(props: GitConfirmSheetProps) { | ... | @@ -141,6 +141,7 @@ export function GitConfirmSheet(props: GitConfirmSheetProps) { |
| 141 | 141 | ||
| 142 | <MaterialScreenContent fitToContents> | 142 | <MaterialScreenContent fitToContents> |
| 143 | <ScrollView | 143 | <ScrollView |
| 144 | nestedScrollEnabled | ||
| 144 | className="android:shrink android:grow-0 ios:flex-1" | 145 | className="android:shrink android:grow-0 ios:flex-1" |
| 145 | showsVerticalScrollIndicator={false} | 146 | showsVerticalScrollIndicator={false} |
| 146 | contentContainerClassName="android:gap-2 android:p-2" | 147 | contentContainerClassName="android:gap-2 android:p-2" |
apps/mobile/src/features/threads/git/GitOverviewSheet.tsx+11-8| ... | @@ -252,21 +252,22 @@ function GitOverviewSheetContent(props: GitOverviewSheetProps) { | ... | @@ -252,21 +252,22 @@ function GitOverviewSheetContent(props: GitOverviewSheetProps) { |
| 252 | 252 | ||
| 253 | const behindCount = gitStatus.data?.behindCount ?? 0; | 253 | const behindCount = gitStatus.data?.behindCount ?? 0; |
| 254 | 254 | ||
| 255 | // Deterministic pull-to-refresh state. Tying RefreshControl to the query's | 255 | // Deterministic refresh state. Tying RefreshControl to the query's |
| 256 | // isPending flag left the spinner stuck (the status query reports pending | 256 | // isPending flag left the spinner stuck (the status query reports pending |
| 257 | // during quiet background refreshes too). | 257 | // during quiet background refreshes too). |
| 258 | const [isPullRefreshing, setIsPullRefreshing] = useState(false); | 258 | const [isRefreshing, setIsRefreshing] = useState(false); |
| 259 | const handlePullRefresh = useCallback(async () => { | 259 | const handleRefresh = useCallback(async () => { |
| 260 | setIsPullRefreshing(true); | 260 | setIsRefreshing(true); |
| 261 | try { | 261 | try { |
| 262 | await gitActions.refreshSelectedThreadGitStatus(); | 262 | await gitActions.refreshSelectedThreadGitStatus(); |
| 263 | } finally { | 263 | } finally { |
| 264 | setIsPullRefreshing(false); | 264 | setIsRefreshing(false); |
| 265 | } | 265 | } |
| 266 | }, [gitActions]); | 266 | }, [gitActions]); |
| 267 | 267 | ||
| 268 | const content = ( | 268 | const content = ( |
| 269 | <ScrollView | 269 | <ScrollView |
| 270 | nestedScrollEnabled | ||
| 270 | alwaysBounceVertical | 271 | alwaysBounceVertical |
| 271 | className="flex-1 android:bg-sheet-solid ios:bg-screen" | 272 | className="flex-1 android:bg-sheet-solid ios:bg-screen" |
| 272 | contentInsetAdjustmentBehavior={Platform.OS === "ios" ? "automatic" : "never"} | 273 | contentInsetAdjustmentBehavior={Platform.OS === "ios" ? "automatic" : "never"} |
| ... | @@ -278,7 +279,9 @@ function GitOverviewSheetContent(props: GitOverviewSheetProps) { | ... | @@ -278,7 +279,9 @@ function GitOverviewSheetContent(props: GitOverviewSheetProps) { |
| 278 | gap: Platform.OS === "android" ? 8 : 14, | 279 | gap: Platform.OS === "android" ? 8 : 14, |
| 279 | }} | 280 | }} |
| 280 | refreshControl={ | 281 | refreshControl={ |
| 281 | <RefreshControl refreshing={isPullRefreshing} onRefresh={() => void handlePullRefresh()} /> | 282 | Platform.OS === "ios" || isInspector ? ( |
| 283 | <RefreshControl refreshing={isRefreshing} onRefresh={() => void handleRefresh()} /> | ||
| 284 | ) : undefined | ||
| 282 | } | 285 | } |
| 283 | > | 286 | > |
| 284 | <View | 287 | <View |
| ... | @@ -474,11 +477,11 @@ function GitOverviewSheetContent(props: GitOverviewSheetProps) { | ... | @@ -474,11 +477,11 @@ function GitOverviewSheetContent(props: GitOverviewSheetProps) { |
| 474 | { | 477 | { |
| 475 | id: "refresh", | 478 | id: "refresh", |
| 476 | title: "Refresh repository status", | 479 | title: "Refresh repository status", |
| 477 | attributes: { disabled: busy || isPullRefreshing }, | 480 | attributes: { disabled: busy || isRefreshing }, |
| 478 | }, | 481 | }, |
| 479 | ]} | 482 | ]} |
| 480 | onPressAction={({ nativeEvent }) => { | 483 | onPressAction={({ nativeEvent }) => { |
| 481 | if (nativeEvent.event === "refresh") void handlePullRefresh(); | 484 | if (nativeEvent.event === "refresh") void handleRefresh(); |
| 482 | }} | 485 | }} |
| 483 | > | 486 | > |
| 484 | {(open) => ( | 487 | {(open) => ( |