import { useAtomSet, useAtomValue } from "@effect/atom-react"; import { type EnvironmentMachineKind, resolveEnvironmentMachineKind } from "@t3tools/contracts"; import { AsyncResult } from "effect/unstable/reactivity"; import { useMemo } from "react"; import { ActivityIndicator, Alert, Pressable, ScrollView, View } from "react-native"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { AppText as Text } from "../../components/AppText"; import { SymbolView } from "../../components/AppSymbol"; import { EnvironmentMachineSymbol } from "../../components/EnvironmentMachineSymbol"; import { clearClientCacheAtom, clientCacheSummaryAtom, type EnvironmentClientCacheSummary, } from "../../state/client-cache-state"; import { useServerConfigs } from "../../state/entities"; import { useSavedRemoteConnections } from "../../state/use-remote-environment-registry"; import { SettingsSection } from "./components/SettingsSection"; export function SettingsClientStorageRouteScreen() { const insets = useSafeAreaInsets(); const summaryResult = useAtomValue(clientCacheSummaryAtom); const clearResult = useAtomValue(clearClientCacheAtom); const clearCache = useAtomSet(clearClientCacheAtom); const { savedConnectionsById } = useSavedRemoteConnections(); const serverConfigs = useServerConfigs(); const isClearing = clearResult.waiting; const summary = AsyncResult.isSuccess(summaryResult) ? summaryResult.value : null; const environmentSummaries = useMemo( () => [...(summary?.environments ?? [])].sort((left, right) => { const leftLabel = savedConnectionsById[left.environmentId]?.environmentLabel ?? ""; const rightLabel = savedConnectionsById[right.environmentId]?.environmentLabel ?? ""; return leftLabel.localeCompare(rightLabel); }), [savedConnectionsById, summary?.environments], ); const confirmClearEnvironment = (environment: EnvironmentClientCacheSummary) => { const label = savedConnectionsById[environment.environmentId]?.environmentLabel ?? environment.environmentId; Alert.alert( `Clear cache for ${label}?`, "This removes offline threads, server metadata, and cached branches for this environment. The saved connection and credentials stay intact.", [ { text: "Cancel", style: "cancel" }, { text: "Clear Cache", style: "destructive", onPress: () => clearCache({ type: "environment", environmentId: environment.environmentId }), }, ], ); }; const confirmClearAll = () => { Alert.alert( "Clear all client caches?", "This removes offline data for every environment. Connections, credentials, account data, and app preferences stay intact.", [ { text: "Cancel", style: "cancel" }, { text: "Clear All Caches", style: "destructive", onPress: () => clearCache({ type: "all" }), }, ], ); }; return ( {AsyncResult.isFailure(summaryResult) ? ( Storage unavailable Restart the app and try again. ) : !summary ? ( Inspecting cached data… ) : environmentSummaries.length > 0 ? ( environmentSummaries.map((environment, index) => ( confirmClearEnvironment(environment)} /> )) ) : ( No cached data Offline cache records will appear here after environments are used. )} {summary ? `Clear ${formatBytes(summary.payloadBytes)}` : "Clear caches"} {isClearing ? ( ) : null} Clearing caches never removes environment connections, credentials, account data, or appearance preferences. {AsyncResult.isFailure(summaryResult) || AsyncResult.isFailure(clearResult) ? ( Client storage is temporarily unavailable. Try again after restarting the app. ) : null} ); } function CacheEnvironmentRow(props: { readonly environment: EnvironmentClientCacheSummary; readonly environmentLabel: string; readonly machine: EnvironmentMachineKind; readonly disabled: boolean; readonly first: boolean; readonly onClear: () => void; }) { return ( {props.environmentLabel} Clear {formatBytes(props.environment.payloadBytes)} ); } function formatBytes(bytes: number): string { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(bytes < 10 * 1024 ? 1 : 0)} KB`; return `${(bytes / (1024 * 1024)).toFixed(bytes < 10 * 1024 * 1024 ? 1 : 0)} MB`; }