1import { useAtomValue } from "@effect/atom-react";
2import {
3 RelayConnectionRegistration,
4 RelayConnectionTarget,
5} from "@t3tools/client-runtime/connection";
6import type { EnvironmentId } from "@t3tools/contracts";
7import type {
8 RelayClientEnvironmentRecord,
9 RelayEnvironmentStatusResponse,
10} from "@t3tools/contracts/relay";
11import * as Option from "effect/Option";
12import { useCallback, useMemo } from "react";
13
14import { environmentCatalog } from "../../connection/catalog";
15import {
16 connectPairingUrl as connectPairingUrlAtom,
17 updateBearerConnection,
18} from "../../connection/onboarding";
19import { useWorkspaceEnvironments } from "../../state/workspace";
20import { relayEnvironmentDiscovery } from "../../state/relay";
21import { useAtomCommand } from "../../state/use-atom-command";
22import { relayManagedEnvironmentIds } from "./environmentSections";
23
24export interface RelayEnvironmentView {
25 readonly environment: RelayClientEnvironmentRecord;
26 readonly availability: "checking" | "online" | "offline" | "error";
27 readonly status: RelayEnvironmentStatusResponse | null;
28 readonly error: string | null;
29 readonly traceId: string | null;
30}
31
32export function useConnectionController() {
33 const connectedEnvironments = useWorkspaceEnvironments();
34 const discovery = useAtomValue(relayEnvironmentDiscovery.stateValueAtom);
35 const connectPairingUrlMutation = useAtomCommand(connectPairingUrlAtom, {
36 reportFailure: false,
37 });
38 const updateBearer = useAtomCommand(updateBearerConnection, { reportFailure: false });
39 const registerEnvironment = useAtomCommand(environmentCatalog.register, "environment register");
40 const removeEnvironmentMutation = useAtomCommand(environmentCatalog.remove, "environment remove");
41 const retryEnvironmentMutation = useAtomCommand(environmentCatalog.retryNow, "environment retry");
42 const setEnvironmentEnabledMutation = useAtomCommand(
43 environmentCatalog.setEnabled,
44 "environment toggle",
45 );
46 const refreshRelayEnvironments = useAtomCommand(
47 relayEnvironmentDiscovery.refresh,
48 "relay environment refresh",
49 );
50
51 const registeredIds = useMemo(
52 () => relayManagedEnvironmentIds(connectedEnvironments),
53 [connectedEnvironments],
54 );
55 const relayEnvironments = useMemo<ReadonlyArray<RelayEnvironmentView>>(
56 () =>
57 [...discovery.environments.values()].map((entry) => ({
58 environment: entry.environment,
59 availability: entry.availability,
60 status: Option.getOrNull(entry.status),
61 error: Option.getOrNull(entry.error)?.message ?? null,
62 traceId: Option.getOrNull(entry.error)?.traceId ?? null,
63 })),
64 [discovery.environments],
65 );
66 const availableRelayEnvironments = useMemo(
67 () => relayEnvironments.filter((entry) => !registeredIds.has(entry.environment.environmentId)),
68 [registeredIds, relayEnvironments],
69 );
70
71 const connectPairingUrl = useCallback(
72 (pairingUrl: string, expectedEnvironmentId?: EnvironmentId) =>
73 connectPairingUrlMutation({
74 pairingUrl,
75 ...(expectedEnvironmentId === undefined ? {} : { expectedEnvironmentId }),
76 }),
77 [connectPairingUrlMutation],
78 );
79 const connectRelayEnvironment = useCallback(
80 (environment: RelayClientEnvironmentRecord) =>
81 registerEnvironment(
82 new RelayConnectionRegistration({
83 target: new RelayConnectionTarget({
84 environmentId: environment.environmentId,
85 label: environment.label,
86 }),
87 }),
88 ),
89 [registerEnvironment],
90 );
91 const removeEnvironment = useCallback(
92 (environmentId: EnvironmentId) => removeEnvironmentMutation(environmentId),
93 [removeEnvironmentMutation],
94 );
95 const retryEnvironment = useCallback(
96 (environmentId: EnvironmentId) => retryEnvironmentMutation(environmentId),
97 [retryEnvironmentMutation],
98 );
99 const setEnvironmentEnabled = useCallback(
100 (environmentId: EnvironmentId, enabled: boolean) =>
101 setEnvironmentEnabledMutation({ environmentId, enabled }),
102 [setEnvironmentEnabledMutation],
103 );
104 const updateEnvironment = useCallback(
105 (
106 environmentId: EnvironmentId,
107 updates: { readonly label: string; readonly displayUrl: string },
108 ) =>
109 updateBearer({
110 environmentId,
111 label: updates.label,
112 httpBaseUrl: updates.displayUrl,
113 }),
114 [updateBearer],
115 );
116
117 return {
118 connectedEnvironments,
119 relayEnvironments,
120 availableRelayEnvironments,
121 relayDiscovery: {
122 isRefreshing: discovery.refreshing,
123 isOffline: discovery.offline,
124 error: Option.getOrNull(discovery.error)?.message ?? null,
125 errorTraceId: Option.getOrNull(discovery.error)?.traceId ?? null,
126 },
127 connectPairingUrl,
128 connectRelayEnvironment,
129 removeEnvironment,
130 retryEnvironment,
131 setEnvironmentEnabled,
132 updateEnvironment,
133 refreshRelayEnvironments,
134 };
135}