Replace workspace reconnect banner with persistent agent panel toast

The workspace reconnect banner ignored safe-area insets on Android,
overlapping the system status bar. The agent panel already shows a
"Reconnecting..." toast on disconnect, so consolidate on that and drop
the banner entirely. Add persistent toast support (durationMs: null)
and dismiss the toast when the connection returns to online instead of
auto-dismissing after 2.2s.
This commit is contained in:
Mohamed Boudra
2026-05-03 17:17:02 +07:00
parent f33a5191df
commit 1daa131480
5 changed files with 21 additions and 91 deletions
@@ -41,16 +41,6 @@ export function renderWorkspaceRouteGate(input: {
}
}
export function renderWorkspaceReconnectIndicator(input: {
state: WorkspaceRouteState;
onRetryHost: () => void;
}): React.ReactNode {
if (input.state.kind !== "reconnecting") {
return null;
}
return <WorkspaceReconnectBanner state={input.state} onRetry={input.onRetryHost} />;
}
function getWorkspaceHostStateTitle(
state: Extract<WorkspaceRouteState, { kind: "unreachable" }>,
): string {
@@ -63,15 +53,6 @@ function getWorkspaceHostStateTitle(
return `Cannot reach ${state.hostName}`;
}
function getReconnectBannerCopy(
state: Extract<WorkspaceRouteState, { kind: "reconnecting" }>,
): string {
if (state.connectionStatus === "connecting" || state.connectionStatus === "idle") {
return `Reconnecting to ${state.hostName}...`;
}
return `${state.hostName} is offline`;
}
function WorkspaceConnecting({ hostName }: { hostName: string }) {
const { theme } = useUnistyles();
@@ -135,33 +116,6 @@ function WorkspaceUnreachable({
);
}
function WorkspaceReconnectBanner({
state,
onRetry,
}: {
state: Extract<WorkspaceRouteState, { kind: "reconnecting" }>;
onRetry: () => void;
}) {
const { theme } = useUnistyles();
const canRetry = state.connectionStatus === "offline" || state.connectionStatus === "error";
const showSpinner = state.connectionStatus === "connecting" || state.connectionStatus === "idle";
const isErrorState = state.connectionStatus === "offline" || state.connectionStatus === "error";
return (
<View style={styles.reconnectBanner} testID="workspace-reconnect-banner">
{showSpinner ? <LoadingSpinner size="small" color={theme.colors.foregroundMuted} /> : null}
<Text style={isErrorState ? styles.reconnectTextDestructive : styles.reconnectText}>
{getReconnectBannerCopy(state)}
</Text>
{canRetry ? (
<Button size="sm" variant="outline" leftIcon={RotateCw} onPress={onRetry}>
Retry
</Button>
) : null}
</View>
);
}
function WorkspaceMissing({ hostName, onDismiss }: { hostName: string; onDismiss: () => void }) {
return (
<View style={styles.emptyState}>
@@ -220,28 +174,4 @@ const styles = StyleSheet.create((theme) => ({
flexWrap: "wrap",
gap: theme.spacing[2],
},
reconnectBanner: {
minHeight: 32,
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
gap: theme.spacing[2],
paddingHorizontal: theme.spacing[3],
paddingVertical: theme.spacing[1],
borderBottomWidth: StyleSheet.hairlineWidth,
borderBottomColor: theme.colors.border,
backgroundColor: theme.colors.surface1,
},
reconnectText: {
minWidth: 0,
color: theme.colors.foregroundMuted,
fontSize: theme.fontSize.sm,
flexShrink: 1,
},
reconnectTextDestructive: {
minWidth: 0,
color: theme.colors.destructive,
fontSize: theme.fontSize.sm,
flexShrink: 1,
},
}));
@@ -129,10 +129,7 @@ import {
resolveWorkspaceRouteState,
type WorkspaceRouteState,
} from "@/screens/workspace/workspace-route-state";
import {
renderWorkspaceReconnectIndicator,
renderWorkspaceRouteGate,
} from "@/screens/workspace/workspace-route-state-views";
import { renderWorkspaceRouteGate } from "@/screens/workspace/workspace-route-state-views";
import {
deriveWorkspaceAgentVisibility,
workspaceAgentVisibilityEqual,
@@ -2929,10 +2926,6 @@ function WorkspaceScreenContent({
gate: workspaceScreenGate,
workspaceKey: persistenceKey,
});
const reconnectBanner = renderWorkspaceReconnectIndicator({
state: workspaceRouteState,
onRetryHost: handleRetryHost,
});
const headerRight = useMemo(
() => (
@@ -3175,7 +3168,6 @@ function WorkspaceScreenContent({
/>
<View style={styles.threePaneRow}>
<View style={styles.centerColumn}>
{reconnectBanner}
{showScreenHeader && (
<ScreenHeader
onRowLayout={onHeaderLayout}