| 1 | import { AppActivityIndicator } from "../../components/AppActivityIndicator"; |
| 2 | import { ConnectionTraceId } from "./ConnectionTraceId"; |
| 3 | import { |
| 4 | type EnvironmentConnectionPhase, |
| 5 | type EnvironmentConnectionPresentation, |
| 6 | } from "@t3tools/client-runtime/connection"; |
| 7 | import { SymbolView } from "../../components/AppSymbol"; |
| 8 | import { Pressable, View } from "react-native"; |
| 9 | |
| 10 | import { AppText as Text } from "../../components/AppText"; |
| 11 | |
| 12 | function 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 | |
| 31 | function 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 | |
| 56 | export 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 | } |