import { useAuth } from "@clerk/expo"; import { Stack, useRouter } from "expo-router"; import { SymbolView } from "expo-symbols"; import type { EnvironmentId } from "@t3tools/contracts"; import type { RelayClientEnvironmentRecord } from "@t3tools/contracts/relay"; import * as Effect from "effect/Effect"; import { useCallback, useMemo, useState } 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 { connectCloudEnvironment } from "../../features/cloud/linkEnvironment"; import { hasCloudPublicConfig, resolveRelayClerkTokenOptions, } from "../../features/cloud/publicConfig"; import { useManagedRelayEnvironments, useManagedRelayEnvironmentStatus, } from "../../features/cloud/managedRelayState"; import { ConnectionEnvironmentRow } from "../../features/connection/ConnectionEnvironmentRow"; import { cn } from "../../lib/cn"; import { mobileRuntime } from "../../lib/runtime"; import { useThemeColor } from "../../lib/useThemeColor"; import { connectSavedEnvironment, useRemoteConnections, useRemoteEnvironmentState, } from "../../state/use-remote-environment-registry"; export default function SettingsEnvironmentsRouteScreen() { const { connectedEnvironments, onReconnectEnvironment, onRemoveEnvironmentPress, onUpdateEnvironment, } = useRemoteConnections(); const router = useRouter(); const insets = useSafeAreaInsets(); const hasEnvironments = connectedEnvironments.length > 0; const [expandedId, setExpandedId] = useState(null); const accentColor = useThemeColor("--color-icon-muted"); const handleToggle = useCallback((environmentId: EnvironmentId) => { setExpandedId((prev) => (prev === environmentId ? null : environmentId)); }, []); return ( router.push("/settings/environment-new")} separateBackground /> {hasEnvironments ? ( {connectedEnvironments.map((environment, index) => ( handleToggle(environment.environmentId)} onReconnect={onReconnectEnvironment} onRemove={onRemoveEnvironmentPress} onUpdate={onUpdateEnvironment} /> ))} ) : ( No environments connected yet.{"\n"}Tap{" "} + to add one. )} {hasCloudPublicConfig() ? : null} ); } function ConfiguredCloudEnvironmentRows() { const { getToken, isSignedIn } = useAuth({ treatPendingAsSignedOut: false }); const { savedConnectionsById } = useRemoteEnvironmentState(); const cloudEnvironmentsState = useManagedRelayEnvironments(); const [connectingCloudEnvironmentId, setConnectingCloudEnvironmentId] = useState( null, ); const iconColor = useThemeColor("--color-icon"); const availableCloudEnvironments = useMemo( () => (cloudEnvironmentsState.data ?? []).filter( (environment) => savedConnectionsById[environment.environmentId] === undefined, ), [cloudEnvironmentsState.data, savedConnectionsById], ); const handleConnectCloudEnvironment = useCallback( async (environment: RelayClientEnvironmentRecord) => { setConnectingCloudEnvironmentId(environment.environmentId); try { const token = await getToken(resolveRelayClerkTokenOptions()); if (!token) { throw new Error("Sign in to T3 Cloud before connecting."); } await mobileRuntime.runPromise( connectCloudEnvironment({ clerkToken: token, environment, }).pipe(Effect.flatMap(connectSavedEnvironment)), ); } catch (error) { Alert.alert( "Connect failed", error instanceof Error ? error.message : "Could not connect to this environment.", ); } finally { setConnectingCloudEnvironmentId(null); } }, [getToken], ); if (!isSignedIn) return null; return ( T3 Cloud {cloudEnvironmentsState.isPending ? ( ) : ( )} {availableCloudEnvironments.length > 0 ? ( {availableCloudEnvironments.map((environment, index) => ( handleConnectCloudEnvironment(environment)} /> ))} ) : cloudEnvironmentsState.data === null ? ( Loading linked cloud environments. ) : cloudEnvironmentsState.error ? ( Could not load T3 Cloud environments {cloudEnvironmentsState.error} ) : ( No additional linked cloud environments. )} ); } function CloudEnvironmentRow(props: { readonly environment: RelayClientEnvironmentRecord; readonly borderTop: boolean; readonly isConnecting: boolean; readonly onConnect: () => void; }) { const mutedColor = useThemeColor("--color-icon-muted"); const statusState = useManagedRelayEnvironmentStatus(props.environment); const status = statusState.data; const disabled = props.isConnecting; const statusText = status === null ? (statusState.error ?? (statusState.isPending ? "Checking status..." : "Status unavailable")) : status.status === "online" ? "Online" : (status.error ?? "Offline"); return ( {props.environment.label} {props.environment.endpoint.httpBaseUrl} {statusText} {props.isConnecting ? "Connecting" : "Connect"} ); }