1import { useAtomRefresh, useAtomValue } from "@effect/atom-react";
2import * as Cause from "effect/Cause";
3import * as Option from "effect/Option";
4import { AsyncResult, Atom } from "effect/reactivity";
5
6const EMPTY_ASYNC_RESULT_ATOM = Atom.make(AsyncResult.initial<never, never>(false)).pipe(
7 Atom.withLabel("web-environment-query:empty"),
8);
9
10export 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
20export 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
27export 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}