1import { resolveAssetUrl } from "@t3tools/client-runtime/state/assets";
2import type { EnvironmentId, ThreadId } from "@t3tools/contracts";
3import * as Option from "effect/Option";
4import { useCallback, useEffect, useLayoutEffect, useRef } from "react";
5import { Alert } from "react-native";
6
7import { downloadAndShareAttachment } from "../../lib/attachmentDownload";
8import { assetEnvironment } from "../../state/assets";
9import { usePreparedConnection } from "../../state/session";
10import { useAtomQueryRunner } from "../../state/use-atom-query-runner";
11import { fileChipShareSource, type FileChipTarget } from "./fileChipMenu";
12
13/** Fetches host files through the selected environment before opening the native save/share sheet. */
14export function useFileChipShare(
15 environmentId: EnvironmentId,
16 threadId: ThreadId,
17 sourceIdentifier: string,
18) {
19 const connection = usePreparedConnection(environmentId);
20 const httpBaseUrl = Option.isSome(connection) ? connection.value.httpBaseUrl : null;
21 const createUrl = useAtomQueryRunner(assetEnvironment.createUrl, {
22 refresh: true,
23 reportFailure: false,
24 });
25 const connectionRef = useRef(httpBaseUrl);
26 useLayoutEffect(() => {
27 connectionRef.current = httpBaseUrl;
28 }, [httpBaseUrl]);
29 const requestRef = useRef<AbortController | null>(null);
30 useEffect(() => () => requestRef.current?.abort(), []);
31
32 const share = useCallback(
33 (target: FileChipTarget) => {
34 const source = fileChipShareSource(target, threadId);
35 if (!source || requestRef.current) return;
36 const request = new AbortController();
37 requestRef.current = request;
38 const httpBaseUrl = connectionRef.current;
39 void (async () => {
40 if (httpBaseUrl === null) throw new Error("Reconnect to the environment and try again.");
41 const result = await createUrl({ environmentId, input: { resource: source.resource } });
42 if (request.signal.aborted) return;
43 const url =
44 result._tag === "Success" ? resolveAssetUrl(httpBaseUrl, result.value.relativeUrl) : null;
45 if (url === null) throw new Error("The file could not be loaded. Reconnect and try again.");
46 await downloadAndShareAttachment({
47 url,
48 attachment: source,
49 signal: request.signal,
50 sourceIdentifier,
51 });
52 })()
53 .catch((error: unknown) => {
54 if (!request.signal.aborted) {
55 Alert.alert(
56 "Could not share file",
57 error instanceof Error ? error.message : "Try again.",
58 );
59 }
60 })
61 .finally(() => {
62 if (requestRef.current === request) {
63 requestRef.current = null;
64 }
65 });
66 },
67 [createUrl, environmentId, sourceIdentifier, threadId],
68 );
69 return share;
70}