import { useAtomValue } from "@effect/atom-react"; import type { DesktopBridge, DesktopUpdateState } from "@t3tools/contracts"; import * as Effect from "effect/Effect"; import * as Queue from "effect/Queue"; import * as Stream from "effect/Stream"; import * as AsyncResult from "effect/unstable/reactivity/AsyncResult"; import { Atom } from "effect/unstable/reactivity"; type DesktopUpdateBridge = Pick; function getDesktopUpdateBridge(): DesktopUpdateBridge | undefined { return typeof window === "undefined" ? undefined : window.desktopBridge; } export function createDesktopUpdateStateAtom(getBridge: () => DesktopUpdateBridge | undefined) { const updates = Stream.callback((queue) => Effect.gen(function* () { const bridge = getBridge(); if (!bridge) { Queue.offerUnsafe(queue, null); return yield* Effect.never; } let receivedUpdate = false; yield* Effect.acquireRelease( Effect.sync(() => bridge.onUpdateState((state) => { receivedUpdate = true; Queue.offerUnsafe(queue, state); }), ), (unsubscribe) => Effect.sync(unsubscribe), ); const initialState = yield* Effect.tryPromise(() => bridge.getUpdateState()).pipe( Effect.retry({ times: 2 }), Effect.orElseSucceed(() => null), ); if (!receivedUpdate && initialState !== null) { Queue.offerUnsafe(queue, initialState); } return yield* Effect.never; }), ); return Atom.make(updates, { initialValue: null }).pipe( Atom.keepAlive, Atom.withLabel("desktop:update-state"), ); } const desktopUpdateStateAtom = createDesktopUpdateStateAtom(getDesktopUpdateBridge); export function useDesktopUpdateState(): DesktopUpdateState | null { return AsyncResult.getOrElse(useAtomValue(desktopUpdateStateAtom), () => null); }