1import { useAtomValue } from "@effect/atom-react";
2import { resolveFilesystemReadAccess } from "@t3tools/client-runtime/state/filesystem";
3import {
4 assetUrlStateFromResult,
5 blockedAssetUrlState,
6 createAssetEnvironmentAtoms,
7 createProjectFaviconUrlAtomFamily,
8 EMPTY_ASSET_URL_ATOM,
9} from "@t3tools/client-runtime/state/assets";
10import type { AssetResource, EnvironmentId } from "@t3tools/contracts";
11import { useCallback } from "react";
12
13import { connectionAtomRuntime } from "../connection/runtime";
14import { projectFaviconDatabaseCache } from "../lib/projectFaviconDatabaseCache";
15import { type AssetUrlState, deriveAssetUrlState } from "./asset-url-state";
16import { environmentProjectCloneListAtom } from "./projectClones";
17import { environmentSession, usePreparedConnection } from "./session";
18import { useEnvironmentPresentation } from "./presentation";
19import { useEnvironmentQuery } from "./query";
20import { useAtomQueryRunner } from "./use-atom-query-runner";
21
22export type { AssetUrlFailureReason, AssetUrlState } from "./asset-url-state";
23
24export const assetEnvironment = createAssetEnvironmentAtoms(connectionAtomRuntime);
25
26export const projectFaviconUrlAtom = createProjectFaviconUrlAtomFamily({
27 imageCache: projectFaviconDatabaseCache,
28 createUrl: assetEnvironment.createUrl,
29 preparedConnection: environmentSession.preparedConnectionValueAtom,
30 projectClones: environmentProjectCloneListAtom,
31});
32
33export function useAssetUrlState(
34 environmentId: EnvironmentId | null,
35 resource: AssetResource | null,
36): AssetUrlState {
37 const fileAccessSession = useEnvironmentQuery(
38 environmentId === null ? null : environmentSession.sessionStateAtom(environmentId),
39 );
40 const fileEnvironment = useEnvironmentPresentation(environmentId);
41 const fileAccess = resolveFilesystemReadAccess({
42 isCatalogReady: fileEnvironment.isReady,
43 connection: fileEnvironment.presentation?.connection ?? null,
44 session: fileAccessSession.data,
45 sessionError: fileAccessSession.error,
46 });
47 const blocked = blockedAssetUrlState(resource, fileAccess);
48 const preparedConnection = usePreparedConnection(environmentId);
49 const connectionPhase = fileEnvironment.presentation?.connection.phase ?? "available";
50 const result = useAtomValue(
51 blocked !== null || environmentId === null || resource === null
52 ? EMPTY_ASSET_URL_ATOM
53 : assetEnvironment.createUrl({ environmentId, input: { resource } }),
54 );
55 const shared =
56 blocked ??
57 assetUrlStateFromResult(
58 result,
59 preparedConnection._tag === "Some" ? preparedConnection.value.httpBaseUrl : null,
60 );
61 return deriveAssetUrlState({
62 connectionPhase,
63 // A failure left over from an outage is re-queried as soon as the
64 // connection returns. While that re-query is in flight it is not a verdict
65 // on the file, so it reads as loading rather than a false "unavailable".
66 shared: shared._tag === "Failure" && result.waiting ? { _tag: "Loading" } : shared,
67 });
68}
69
70export function useAssetUrl(
71 environmentId: EnvironmentId | null,
72 resource: AssetResource | null,
73): string | null {
74 const state = useAssetUrlState(environmentId, resource);
75 return state._tag === "Success" ? state.url : null;
76}
77
78/** Explicit playback and sharing must reauthorize files that may have been replaced on disk. */
79export function useRefreshAssetUrl(
80 environmentId: EnvironmentId | null,
81 resource: AssetResource | null,
82): () => Promise<string | null> {
83 const connection = usePreparedConnection(environmentId);
84 const httpBaseUrl = connection._tag === "Some" ? connection.value.httpBaseUrl : null;
85 const createUrl = useAtomQueryRunner(assetEnvironment.createUrl, {
86 refresh: true,
87 reportFailure: false,
88 });
89 return useCallback(async () => {
90 if (environmentId === null || resource === null || httpBaseUrl === null) return null;
91 const state = assetUrlStateFromResult(
92 await createUrl({ environmentId, input: { resource } }),
93 httpBaseUrl,
94 );
95 return state._tag === "Success" ? state.url : null;
96 }, [createUrl, environmentId, httpBaseUrl, resource]);
97}