| 1 | import { useAtomRefresh, useAtomValue } from "@effect/atom-react"; |
| 2 | import * as Cause from "effect/Cause"; |
| 3 | import * as Option from "effect/Option"; |
| 4 | import { AsyncResult, Atom } from "effect/reactivity"; |
| 5 | |
| 6 | const EMPTY_ASYNC_RESULT_ATOM = Atom.make(AsyncResult.initial<never, never>(false)).pipe( |
| 7 | Atom.withLabel("web-environment-query:empty"), |
| 8 | ); |
| 9 | |
| 10 | export interface EnvironmentQueryView<A, E = unknown> { |
| 11 | readonly data: A | null; |
| 12 | readonly dataUpdatedAt: number; |
| 13 | readonly error: string | null; |
| 14 | readonly failure: E | null; |
| 15 | readonly isPending: boolean; |
| 16 | readonly isSuccess: boolean; |
| 17 | readonly refresh: () => void; |
| 18 | } |
| 19 | |
| 20 | export function formatEnvironmentQueryError(cause: Cause.Cause<unknown>): string { |
| 21 | const error = Cause.squash(cause); |
| 22 | return error instanceof Error && error.message.trim().length > 0 |
| 23 | ? error.message |
| 24 | : "The environment request failed."; |
| 25 | } |
| 26 | |
| 27 | export function useEnvironmentQuery<A, E>( |
| 28 | atom: Atom.Atom<AsyncResult.AsyncResult<A, E>> | null, |
| 29 | ): EnvironmentQueryView<A, E> { |
| 30 | const selectedAtom = atom ?? EMPTY_ASYNC_RESULT_ATOM; |
| 31 | const result = useAtomValue(selectedAtom); |
| 32 | const refresh = useAtomRefresh(selectedAtom); |
| 33 | return { |
| 34 | data: Option.getOrNull(AsyncResult.value(result)), |
| 35 | dataUpdatedAt: |
| 36 | result._tag === "Success" |
| 37 | ? result.timestamp |
| 38 | : result._tag === "Failure" |
| 39 | ? (Option.getOrNull(result.previousSuccess)?.timestamp ?? 0) |
| 40 | : 0, |
| 41 | error: result._tag === "Failure" ? formatEnvironmentQueryError(result.cause) : null, |
| 42 | failure: |
| 43 | result._tag === "Failure" ? Option.getOrNull(Cause.findErrorOption(result.cause)) : null, |
| 44 | isPending: atom !== null && result.waiting, |
| 45 | isSuccess: result._tag === "Success", |
| 46 | refresh, |
| 47 | }; |
| 48 | } |