81 lines · 2.8 KB · TypeScript
RawDownload
import { useMemo } from "react";import { useAtomValue } from "@effect/atom-react";import { createDeviceEnvironmentAtoms } from "@t3tools/client-runtime/state/device";import {  type DeviceHubAccess,  resolveDeviceHubAccess,} from "@t3tools/client-runtime/state/deviceHubAccess";import type { DeviceServiceState, EnvironmentId } from "@t3tools/contracts";import * as Effect from "effect/Effect";import * as Option from "effect/Option";import { AsyncResult, Atom } from "effect/unstable/reactivity"; import { connectionAtomRuntime } from "../connection/runtime";import { appAtomRegistry } from "../rpc/atomRegistry";import { environmentSession } from "./session";import { useEnvironmentQuery } from "./query"; export const deviceEnvironment = createDeviceEnvironmentAtoms(connectionAtomRuntime); const EMPTY_DEVICE_STATE: DeviceServiceState = {  hosts: [],  hostStatus: "disabled",  hostStatuses: {},  devices: [],  sessions: [],  onboardingCompleted: false,  agentAccessEnabled: false,  hubBasePath: "/api/device-hub",  revision: 0,}; export function useDeviceState(environmentId: EnvironmentId | null): {  readonly state: DeviceServiceState;  readonly loaded: boolean;} {  const query = useEnvironmentQuery(    environmentId === null ? null : deviceEnvironment.state({ environmentId, input: {} }),  );  return { state: query.data ?? EMPTY_DEVICE_STATE, loaded: query.data !== undefined };} /** * Hub access for one environment. Bearer and DPoP connections mint a ticket * here; a stream that gets a 401 back refreshes this atom and reconnects. * Keyed on the prepared connection so a re-pair produces new credentials. */const deviceHubAccessAtom = Atom.family((environmentId: EnvironmentId) =>  connectionAtomRuntime    .atom((get) => {      const prepared = Option.getOrNull(        get(environmentSession.preparedConnectionValueAtom(environmentId)),      );      if (prepared === null) return Effect.never;      return resolveDeviceHubAccess({ prepared, hubBasePath: EMPTY_DEVICE_STATE.hubBasePath });    })    .pipe(Atom.setIdleTTL(60_000), Atom.withLabel(`device-hub-access:${environmentId}`)),); export function useDeviceHubAccess(  environmentId: EnvironmentId | null,  hostId = "local",): DeviceHubAccess | null {  const result = useAtomValue(    environmentId === null ? EMPTY_ACCESS_ATOM : deviceHubAccessAtom(environmentId),  );  return useMemo(    () =>      AsyncResult.isSuccess(result)        ? { ...result.value, query: { ...result.value.query, hostId } }        : null,    [result, hostId],  );} const EMPTY_ACCESS_ATOM = Atom.make(AsyncResult.initial<DeviceHubAccess, never>()).pipe(  Atom.withLabel("device-hub-access:empty"),); export function refreshDeviceHubAccess(environmentId: EnvironmentId): void {  appAtomRegistry.refresh(deviceHubAccessAtom(environmentId));}