Skip to content
Source
Sign inSign up

assetUrls.ts

112 lines4.0 KB

1import { useAtomValue } from "@effect/atom-react";
2import {
3 type AssetUrlState as RuntimeAssetUrlState,
4 assetUrlStateFromResult,
5 blockedAssetUrlState,
6 EMPTY_ASSET_URL_ATOM,
7 makeRevisionedAssetUrlFamily,
8 resolveAssetUrl,
9} from "@t3tools/client-runtime/state/assets";
10import { squashAtomCommandFailure } from "@t3tools/client-runtime/state/runtime";
11import { AssetResource, EnvironmentId } from "@t3tools/contracts";
12import { AsyncResult } from "effect/reactivity";
13​
14import { assetEnvironment } from "~/state/assets";
15import { useFilesystemReadAccess } from "~/state/filesystem";
16import { usePreparedConnection } from "~/state/session";
17import { useAtomQueryRunner } from "~/state/use-atom-query-runner";
18​
19export { resolveAssetUrl } from "@t3tools/client-runtime/state/assets";
20​
21export type AssetUrlState = RuntimeAssetUrlState & { readonly waiting?: boolean };
22​
23const revisionedAssetUrl = makeRevisionedAssetUrlFamily(assetEnvironment.createUrl);
24​
25/**
26 * Pass `revision` for a workspace file whose bytes can change under the same
27 * path; each new revision gets a freshly minted URL.
28 */
29export function useAssetUrlState(
30 environmentId: EnvironmentId | null,
31 resource: AssetResource | null,
32 revision?: string | null,
33): AssetUrlState {
34 const blocked = blockedAssetUrlState(resource, useFilesystemReadAccess(environmentId));
35 const preparedConnection = usePreparedConnection(environmentId);
36 const result = useAtomValue(
37 blocked !== null || environmentId === null || resource === null
38 ? EMPTY_ASSET_URL_ATOM
39 : revision === undefined
40 ? assetEnvironment.createUrl({ environmentId, input: { resource } })
41 : revisionedAssetUrl({ environmentId, input: { resource }, revision }),
42 );
43 if (blocked !== null) return blocked;
44 return {
45 ...assetUrlStateFromResult(
46 result,
47 preparedConnection._tag === "Some" ? preparedConnection.value.httpBaseUrl : null,
48 ),
49 ...(result.waiting ? { waiting: true } : {}),
50 };
52​
53export function useAssetUrlRefresh(
54 environmentId: EnvironmentId | null,
55 resource: AssetResource | null,
56): () => Promise<string | null> {
57 const connection = usePreparedConnection(environmentId);
58 const httpBaseUrl = connection._tag === "Some" ? connection.value.httpBaseUrl : null;
59 const refresh = useAtomQueryRunner(assetEnvironment.createUrl, {
60 reportFailure: false,
61 refresh: true,
62 });
63 return async () => {
64 if (environmentId === null || resource === null || httpBaseUrl === null) return null;
65 const result = await refresh({ environmentId, input: { resource } });
66 if (result._tag === "Failure") throw squashAtomCommandFailure(result);
67 return resolveAssetUrl(httpBaseUrl, result.value.relativeUrl);
68 };
70​
71export function useAssetUrls(
72 environmentId: EnvironmentId,
73 resources: ReadonlyArray<AssetResource>,
74): ReadonlyArray<string | null> {
75 const preparedConnection = usePreparedConnection(environmentId);
76 const { canReadFiles } = useFilesystemReadAccess(environmentId);
77 const allowedResources = canReadFiles
78 ? resources
79 : resources.filter((resource) => !isFileResource(resource));
80 const results = useAtomValue(
81 assetEnvironment.createUrls({
82 environmentId,
83 resources: allowedResources,
84 }),
85 );
86 if (preparedConnection._tag === "None") return resources.map(() => null);
87 const urls: Array<string | null> = [];
88 let resultIndex = 0;
89 for (const resource of resources) {
90 if (!canReadFiles && isFileResource(resource)) {
91 urls.push(null);
92 continue;
93 }
94 const result = results[resultIndex];
95 resultIndex += 1;
96 urls.push(
97 result && AsyncResult.isSuccess(result)
98 ? resolveAssetUrl(preparedConnection.value.httpBaseUrl, result.value.relativeUrl)
99 : null,
100 );
101 }
102 return urls;
104​
105function isFileResource(resource: AssetResource): boolean {
106 return (
107 resource._tag === "workspace-file" ||
108 resource._tag === "media-file" ||
109 resource._tag === "draft-workspace-file" ||
110 (resource._tag === "native-app-icon" && resource.app._tag === "app-path")
111 );