1import { useAtomValue } from "@effect/atom-react";
2import {
3 createManagedRelayQueryManager,
4 deregisterManagedRelayEnvironment,
5 ManagedRelay,
6 managedRelaySessionAtom,
7 readManagedRelaySnapshotState,
8} from "@t3tools/client-runtime/relay";
9import {
10 createAtomCommandScheduler,
11 createRuntimeCommand,
12} from "@t3tools/client-runtime/state/runtime";
13import type {
14 RelayClientDeviceRecord,
15 RelayClientEnvironmentRecord,
16} from "@t3tools/contracts/relay";
17import type { EnvironmentId } from "@t3tools/contracts";
18import * as Context from "effect/Context";
19import * as Effect from "effect/Effect";
20import * as Layer from "effect/Layer";
21import { AsyncResult, Atom } from "effect/reactivity";
22import { useCallback, useEffect } from "react";
23
24import { runtime } from "../lib/runtime";
25import { appAtomRegistry } from "../rpc/atomRegistry";
26
27const managedRelayAtomRuntime = Atom.runtime(
28 Layer.effect(
29 ManagedRelay.ManagedRelayClient,
30 runtime.contextEffect.pipe(
31 Effect.map((context) => Context.get(context, ManagedRelay.ManagedRelayClient)),
32 ),
33 ),
34);
35
36const managedRelayQueryManager = createManagedRelayQueryManager(managedRelayAtomRuntime);
37
38const managedRelayMutationScheduler = createAtomCommandScheduler();
39
40export const deregisterManagedRelayEnvironmentCommand = createRuntimeCommand(
41 managedRelayAtomRuntime,
42 {
43 label: "web:managed-relay:deregister-environment",
44 scheduler: managedRelayMutationScheduler,
45 concurrency: {
46 mode: "serial",
47 key: (input: { readonly accountId: string; readonly environmentId: EnvironmentId }) =>
48 input.accountId,
49 },
50 execute: (input, registry) => deregisterManagedRelayEnvironment(registry, input),
51 },
52);
53
54const EMPTY_ENVIRONMENTS_ATOM = Atom.make(
55 AsyncResult.success<ReadonlyArray<RelayClientEnvironmentRecord>>([]),
56).pipe(Atom.keepAlive, Atom.withLabel("managed-relay:web:environments:null"));
57
58const EMPTY_DEVICES_ATOM = Atom.make(
59 AsyncResult.success<ReadonlyArray<RelayClientDeviceRecord>>([]),
60).pipe(Atom.keepAlive, Atom.withLabel("managed-relay:web:devices:null"));
61
62export function useManagedRelayEnvironments() {
63 const session = useAtomValue(managedRelaySessionAtom);
64 const accountId = session?.accountId ?? null;
65 const atom = accountId
66 ? managedRelayQueryManager.environmentsAtom(accountId)
67 : EMPTY_ENVIRONMENTS_ATOM;
68 const result = useAtomValue(atom);
69 const snapshot = readManagedRelaySnapshotState(result);
70 useEffect(() => {
71 if (snapshot.error) {
72 console.error("[t3-cloud] Relay environment listing failed", {
73 message: snapshot.error,
74 traceId: snapshot.errorTraceId,
75 });
76 }
77 }, [snapshot.error, snapshot.errorTraceId]);
78 const refresh = useCallback(() => {
79 if (accountId) {
80 managedRelayQueryManager.refreshEnvironments(appAtomRegistry, accountId);
81 }
82 }, [accountId]);
83
84 return {
85 ...snapshot,
86 accountId,
87 refresh,
88 };
89}
90
91export function useManagedRelayDevices() {
92 const session = useAtomValue(managedRelaySessionAtom);
93 const accountId = session?.accountId ?? null;
94 const atom = accountId ? managedRelayQueryManager.devicesAtom(accountId) : EMPTY_DEVICES_ATOM;
95 const result = useAtomValue(atom);
96 const snapshot = readManagedRelaySnapshotState(result);
97 useEffect(() => {
98 if (snapshot.error) {
99 console.error("[t3-cloud] Relay device listing failed", {
100 message: snapshot.error,
101 traceId: snapshot.errorTraceId,
102 });
103 }
104 }, [snapshot.error, snapshot.errorTraceId]);
105 const refresh = useCallback(() => {
106 if (accountId) {
107 managedRelayQueryManager.refreshDevices(appAtomRegistry, accountId);
108 }
109 }, [accountId]);
110
111 return {
112 ...snapshot,
113 accountId,
114 refresh,
115 };
116}