From 89e4504b2c93ef0192a90834e09b87f69d628272 Mon Sep 17 00:00:00 2001 From: Carson Loyal Date: Thu, 30 Jul 2026 13:58:44 -0500 Subject: [PATCH] feat(mobile): add a refresh-all button to the environments screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds an `arrow.clockwise` button to the Environments settings header, alongside the existing add-environment button, that retries every registered environment at once. This is not a placebo refresh. `onReconnectEnvironment` bottoms out in `environmentCatalog.retryNow`, which short-circuits the reconnect backoff — so this is the one way to escape a long backoff delay without waiting it out, which is exactly the moment a user is staring at a stale list wanting to do something about it. It pairs with the workspace status indicator, which already navigates here: the status reports that something is wrong, and this is the remedy. Disabled when there are no environments registered, so it can never be a dead tap. Wired into both header styles — NativeHeaderToolbar on iOS and AndroidScreenHeader actions on Android. Co-Authored-By: Claude Opus 5 (1M context) --- .../SettingsEnvironmentsRouteScreen.tsx | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/apps/mobile/src/features/settings/SettingsEnvironmentsRouteScreen.tsx b/apps/mobile/src/features/settings/SettingsEnvironmentsRouteScreen.tsx index 93b806f6487..d52cc6453ac 100644 --- a/apps/mobile/src/features/settings/SettingsEnvironmentsRouteScreen.tsx +++ b/apps/mobile/src/features/settings/SettingsEnvironmentsRouteScreen.tsx @@ -58,6 +58,16 @@ export function SettingsEnvironmentsRouteScreen() { const handleToggle = useCallback((environmentId: EnvironmentId) => { setExpandedId((prev) => (prev === environmentId ? null : environmentId)); }, []); + // Retries every environment at once. This is not a placebo refresh: the + // underlying command is `retryNow`, which short-circuits the reconnect + // backoff, so it is the one way to escape a long backoff delay without + // waiting it out. + const handleRefreshAll = useCallback(() => { + for (const environment of connectedEnvironments) { + onReconnectEnvironment(environment.environmentId); + } + }, [connectedEnvironments, onReconnectEnvironment]); + const canRefreshAll = connectedEnvironments.length > 0; const handleUpdateEnvironment = useCallback( ( environmentId: EnvironmentId, @@ -94,7 +104,15 @@ export function SettingsEnvironmentsRouteScreen() { navigation.goBack()} + // Rendered in array order, left to right — the reverse of the iOS + // toolbar below. Same result on screen: (refresh, add). actions={[ + { + accessibilityLabel: "Refresh all environments", + disabled: !canRefreshAll, + icon: "arrow.clockwise", + onPress: handleRefreshAll, + }, { accessibilityLabel: "Add environment", icon: "plus", @@ -105,6 +123,9 @@ export function SettingsEnvironmentsRouteScreen() { /> ) : ( + // UIKit orders right-side bar items from the right edge inward, so + // this reads right-to-left on screen: add stays outermost, refresh + // sits inside it. Visually: (refresh, add). + )}