1import { AppActivityIndicator } from "../../components/AppActivityIndicator";
2import { ConnectionTraceId } from "./ConnectionTraceId";
3import {
4 type EnvironmentConnectionPhase,
5 type EnvironmentConnectionPresentation,
6} from "@t3tools/client-runtime/connection";
7import { SymbolView } from "../../components/AppSymbol";
8import { Pressable, View } from "react-native";
9
10import { AppText as Text } from "../../components/AppText";
11
12function noticeTitle(phase: EnvironmentConnectionPhase, environmentLabel: string): string {
13 switch (phase) {
14 case "offline":
15 return "You are offline";
16 case "connecting":
17 return `Connecting to ${environmentLabel}...`;
18 case "reconnecting":
19 return `Reconnecting to ${environmentLabel}...`;
20 case "unsupported":
21 return "Client not supported";
22 case "error":
23 return `${environmentLabel} is unavailable`;
24 case "available":
25 return `${environmentLabel} is disconnected`;
26 case "connected":
27 return "";
28 }
29}
30
31function noticeDetail(
32 phase: EnvironmentConnectionPhase,
33 resourceName: string,
34 error: string | null,
35): string {
36 if (error) {
37 return phase === "reconnecting" ? `The app will keep retrying automatically. ${error}` : error;
38 }
39
40 switch (phase) {
41 case "offline":
42 return `Cached data remains available. The ${resourceName} will load when your connection returns.`;
43 case "connecting":
44 case "reconnecting":
45 return `The ${resourceName} will load as soon as the environment is ready.`;
46 case "unsupported":
47 return "Use compatible versions of the app and server to connect.";
48 case "available":
49 case "error":
50 return `Reconnect the environment to load the ${resourceName}.`;
51 case "connected":
52 return "";
53 }
54}
55
56export function EnvironmentConnectionNotice(props: {
57 readonly environmentLabel: string;
58 readonly connection: EnvironmentConnectionPresentation;
59 readonly resourceName: string;
60 readonly onRetry: () => void;
61}) {
62 const isRetrying =
63 props.connection.phase === "connecting" || props.connection.phase === "reconnecting";
64
65 return (
66 <View className="flex-1 items-center justify-center px-8">
67 <View className="max-w-[320px] items-center gap-3">
68 {isRetrying ? (
69 <AppActivityIndicator size="small" colorClassName={"accent-icon-muted"} />
70 ) : (
71 <SymbolView
72 name={props.connection.phase === "offline" ? "wifi.slash" : "bolt.horizontal.circle"}
73 size={24}
74 tintColorClassName={"accent-icon-muted"}
75 type="monochrome"
76 />
77 )}
78
79 <Text className="text-center text-lg font-t3-bold text-foreground">
80 {noticeTitle(props.connection.phase, props.environmentLabel)}
81 </Text>
82 <Text className="text-center text-sm leading-normal text-foreground-muted">
83 {noticeDetail(props.connection.phase, props.resourceName, props.connection.error)}
84 {props.connection.traceId ? (
85 <ConnectionTraceId traceId={props.connection.traceId} />
86 ) : null}
87 </Text>
88
89 {props.connection.phase !== "offline" && props.connection.phase !== "unsupported" ? (
90 <Pressable
91 accessibilityRole="button"
92 className="mt-1 rounded-full bg-subtle px-4 py-2.5 active:opacity-70"
93 onPress={props.onRetry}
94 >
95 <Text className="text-sm font-t3-bold text-foreground">Retry now</Text>
96 </Pressable>
97 ) : null}
98 </View>
99 </View>
100 );
101}