1import { videoMimeType } from "@t3tools/shared/video";
2
3import type { FileBackedComposerAttachment } from "./composerImages";
4import { resolveOwnedComposerAttachmentFileUri } from "./composerAttachmentFiles";
5import { shareLocalAttachment, type AttachmentPreviewFile } from "./attachmentDownload";
6import { retainComposerAttachmentFileForPreview } from "./composerAttachmentPreviewRetention";
7
8/** Retains the draft original for preview and gives each outgoing share its own lease. */
9export async function loadLocalAttachmentPreview(
10 attachment: FileBackedComposerAttachment,
11 signal: AbortSignal,
12): Promise<AttachmentPreviewFile | null> {
13 if (signal.aborted) return null;
14 const release = retainComposerAttachmentFileForPreview(attachment);
15 try {
16 const { File, Paths } = await import("expo-file-system");
17 if (signal.aborted) {
18 release();
19 return null;
20 }
21 const uri =
22 resolveOwnedComposerAttachmentFileUri(attachment.fileUri, Paths.document.uri) ??
23 attachment.fileUri;
24 const file = new File(uri);
25 if (!file.exists) {
26 throw new Error("The local attachment file is missing.");
27 }
28 let disposed = false;
29 return {
30 uri: file.uri,
31 dispose: () => {
32 if (disposed) return;
33 disposed = true;
34 release();
35 },
36 share: async (shareSignal, sourceIdentifier) => {
37 if (disposed || shareSignal.aborted) return;
38 const releaseShare = retainComposerAttachmentFileForPreview(attachment);
39 try {
40 await shareLocalAttachment({
41 uri: file.uri,
42 attachment: {
43 name: attachment.name,
44 mimeType:
45 attachment.type === "file"
46 ? (videoMimeType(attachment) ?? attachment.mimeType)
47 : attachment.mimeType,
48 },
49 signal: shareSignal,
50 sourceIdentifier,
51 });
52 } finally {
53 releaseShare();
54 }
55 },
56 };
57 } catch (cause) {
58 release();
59 if (signal.aborted) return null;
60 throw new Error("This attachment is no longer available. Attach the file again.", { cause });
61 }
62}