Skip to content
Source
Sign inSign up

AttachmentDocumentBody.tsx

111 lines3.8 KB

1import type { EnvironmentId } from "@t3tools/contracts";
2import { View } from "react-native";
3​
4import { EmptyState } from "../../components/ui/EmptyState";
5import type { useAttachmentDocument } from "../../lib/attachmentDocument";
6import { HtmlRenderWebView } from "../threads/HtmlRenderWebView";
7import { AudioFilePreview } from "./AudioFilePreview";
8import { DelimitedFilePreview } from "./DelimitedFilePreview";
9import { FileMarkdownPreview } from "./FileMarkdownPreview";
10import { FilePreviewLoading, FilePreviewNotice } from "./FilePreviewFeedback";
11import { SourceFileSurface } from "./SourceFileSurface";
12import { WorkspaceFileWebPreview } from "./WorkspaceFileWebPreview";
13​
14export function nativeViewerKind(kind: ReturnType<typeof useAttachmentDocument>["kind"]) {
15 if (kind === "image") return "image" as const;
16 if (kind === "pdf") return "pdf" as const;
17 if (kind === "video" || kind === "unsupported") return "document" as const;
18 return null;
20​
21export function AttachmentDocumentBody(props: {
22 readonly document: Pick<
23 ReturnType<typeof useAttachmentDocument>,
24 | "kind"
25 | "activeMode"
26 | "uri"
27 | "content"
28 | "table"
29 | "error"
30 | "needsText"
31 | "retry"
32 | "revision"
33 >;
34 readonly name: string;
35 readonly environmentId: EnvironmentId | null;
36 readonly htmlRender: boolean;
37 readonly nativeViewer: "pending" | "open" | "unavailable" | null;
38 readonly nativeError: string | null;
39 readonly onOpenNative: () => void;
40}) {
41 const { document } = props;
42 if (document.error) {
43 return (
44 <View className="flex-1 items-center justify-center bg-sheet px-6">
45 <EmptyState
46 title="File unavailable"
47 detail={document.error}
48 actionLabel="Try again"
49 onAction={document.retry}
50 />
51 </View>
52 );
53 }
54 if (props.nativeViewer !== null && props.nativeViewer !== "unavailable") {
55 return <FilePreviewLoading message="Opening in file viewer..." />;
56 }
57 // ZIP text entries are read into memory and have no file URI.
58 if (document.needsText ? !document.content : !document.uri) {
59 return <FilePreviewLoading message="Loading file..." />;
60 }
61 if (document.needsText && document.content) {
62 const { content, table } = document;
63 return (
64 <View className="flex-1 bg-sheet">
65 {content.truncated ? (
66 <FilePreviewNotice title="Partial file">
67 Preview limited to the first 1 MB. Save or share the file to read it in full.
68 </FilePreviewNotice>
69 ) : null}
70 {table && document.activeMode === "table" ? (
71 <DelimitedFilePreview table={table} />
72 ) : document.activeMode === "markdown" && props.environmentId ? (
73 <FileMarkdownPreview
74 cwd=""
75 relativePath=""
76 environmentId={props.environmentId}
77 threadId={null}
78 markdown={content.text}
79 captured
80 />
81 ) : (
82 <SourceFileSurface contents={content.text} path={props.name} selectable />
83 )}
84 </View>
85 );
86 }
87 if (!document.uri) return null;
88 if (document.kind === "audio") {
89 return <AudioFilePreview key={document.revision} uri={document.uri} onRetry={document.retry} />;
90 }
91 if (document.kind === "html") {
92 return props.htmlRender ? (
93 <HtmlRenderWebView key={document.uri} uri={document.uri} title={props.name} nested={false} />
94 ) : (
95 <WorkspaceFileWebPreview uri={document.uri} />
96 );
97 }
98 return (
99 <View className="flex-1 items-center justify-center bg-sheet px-6">
100 <EmptyState
101 title="No preview for this file"
102 detail={
103 props.nativeError ??
104 "No app on this device can show this format. Save or share it to open it elsewhere."
105 }
106 actionLabel="Try again"
107 onAction={props.onOpenNative}
108 />
109 </View>
110 );