| 1 | import { useAtomValue } from "@effect/atom-react"; |
| 2 | import { parseScopedProjectKey, scopedProjectKey } from "@t3tools/client-runtime/environment"; |
| 3 | import type { EnvironmentId, ProjectCloneSnapshot, ScopedProjectRef } from "@t3tools/contracts"; |
| 4 | import * as Option from "effect/Option"; |
| 5 | import { AsyncResult, Atom } from "effect/reactivity"; |
| 6 | |
| 7 | import { environmentServerConfigsAtom } from "./server"; |
| 8 | import { sourceControlEnvironment } from "./sourceControl"; |
| 9 | |
| 10 | const EMPTY_CLONES: ReadonlyArray<ProjectCloneSnapshot> = []; |
| 11 | const EMPTY_CLONE_ATOM = Atom.make<ProjectCloneSnapshot | null>(null).pipe( |
| 12 | Atom.withLabel("web-project-clone:empty"), |
| 13 | ); |
| 14 | |
| 15 | /** |
| 16 | * Latest clone list an environment has streamed; empty until the subscription |
| 17 | * delivers, and never subscribed on servers that predate clone tracking. |
| 18 | */ |
| 19 | export const environmentProjectClonesAtom = Atom.family((environmentId: EnvironmentId) => |
| 20 | Atom.make((get): ReadonlyArray<ProjectCloneSnapshot> => { |
| 21 | const supported = |
| 22 | get(environmentServerConfigsAtom).get(environmentId)?.environment.capabilities |
| 23 | .projectCloneTracking === true; |
| 24 | if (!supported) return EMPTY_CLONES; |
| 25 | const result = get(sourceControlEnvironment.projectClones({ environmentId, input: {} })); |
| 26 | return Option.getOrElse(AsyncResult.value(result), () => EMPTY_CLONES); |
| 27 | }).pipe(Atom.withLabel(`web-project-clones:${environmentId}`)), |
| 28 | ); |
| 29 | |
| 30 | const projectCloneAtom = Atom.family((key: string) => { |
| 31 | const ref = parseScopedProjectKey(key); |
| 32 | return Atom.make((get): ProjectCloneSnapshot | null => { |
| 33 | if (ref === null) return null; |
| 34 | const clones = get(environmentProjectClonesAtom(ref.environmentId)); |
| 35 | return clones.find((clone) => clone.projectId === ref.projectId) ?? null; |
| 36 | }).pipe(Atom.withLabel(`web-project-clone:${key}`)); |
| 37 | }); |
| 38 | |
| 39 | /** |
| 40 | * The tracked clone for a project, or null once it finished (or never |
| 41 | * existed). Subscribing here opens the environment's clone stream, which is |
| 42 | * cheap: the server sends an empty list and stays quiet until a clone starts. |
| 43 | */ |
| 44 | export function useProjectClone(ref: ScopedProjectRef | null): ProjectCloneSnapshot | null { |
| 45 | return useAtomValue(ref === null ? EMPTY_CLONE_ATOM : projectCloneAtom(scopedProjectKey(ref))); |
| 46 | } |
| 47 | |
| 48 | export function useEnvironmentProjectClones( |
| 49 | environmentId: EnvironmentId, |
| 50 | ): ReadonlyArray<ProjectCloneSnapshot> { |
| 51 | return useAtomValue(environmentProjectClonesAtom(environmentId)); |
| 52 | } |