Open History to see the last commit
1import { useId, useState } from "react";
2import { Image, Pressable, View } from "react-native";
3
4import { FilePreviewLoading } from "./FilePreviewFeedback";
5import { EmptyState } from "../../components/ui/EmptyState";
6import { FilePreviewModal, type FilePreviewSource } from "../../components/ui/FilePreviewModal";
7import { PresentationSource } from "../../components/ui/NativePresentation";
8import { useMediaActions } from "../../state/mediaActions";
9import { type MediaActionsSource } from "../../lib/mediaActionsSource";
10import { MediaActionsMenu } from "../../components/MediaActionsMenu";
11
12function ResolvedWorkspaceFileImagePreview(props: {
13 readonly accessibilityLabel: string;
14 readonly uri: string;
15 readonly actionsSource?: MediaActionsSource;
16}) {
17 const [loadError, setLoadError] = useState<string | null>(null);
18 const [preview, setPreview] = useState<FilePreviewSource | null>(null);
19 const sourceIdentifier = useId();
20 const mediaActions = useMediaActions(props.actionsSource);
21 const imageSource = { uri: props.uri, cache: "force-cache" as const };
22
23 return (
24 <View className="relative flex-1 bg-subtle">
25 <MediaActionsMenu media={mediaActions} style={{ flex: 1 }}>
26 <Pressable
27 accessibilityRole="button"
28 accessibilityLabel={`Open full-screen preview of ${props.accessibilityLabel}`}
29 accessibilityHint={
30 mediaActions.actions.length > 0 ? "Touch and hold for media actions" : undefined
31 }
32 disabled={loadError !== null}
33 className="flex-1 p-4 active:bg-subtle-strong"
34 onPress={() =>
35 setPreview({
36 kind: "image",
37 uri: props.uri,
38 name: props.accessibilityLabel,
39 sourceIdentifier,
40 actionsSource: props.actionsSource,
41 })
42 }
43 >
44 <PresentationSource identifier={sourceIdentifier} style={{ flex: 1 }}>
45 <Image
46 accessible={false}
47 source={imageSource}
48 className="h-full w-full"
49 resizeMode="contain"
50 onLoadStart={() => setLoadError(null)}
51 onError={(event) => {
52 setLoadError(event.nativeEvent.error || "The image could not be rendered.");
53 }}
54 />
55 </PresentationSource>
56 </Pressable>
57 </MediaActionsMenu>
58 {loadError !== null ? (
59 <View
60 pointerEvents="none"
61 className="absolute inset-0 items-center justify-center bg-card px-6"
62 >
63 <EmptyState title="Image unavailable" detail={loadError} />
64 </View>
65 ) : null}
66 <FilePreviewModal source={preview} onRequestClose={() => setPreview(null)} />
67 </View>
68 );
69}
70
71export function WorkspaceFileImagePreview(props: {
72 readonly accessibilityLabel: string;
73 readonly uri: string | null;
74 readonly actionsSource?: MediaActionsSource;
75}) {
76 if (props.uri === null) {
77 return <FilePreviewLoading message="Preparing image preview..." background="card" />;
78 }
79
80 return (
81 <ResolvedWorkspaceFileImagePreview
82 accessibilityLabel={props.accessibilityLabel}
83 uri={props.uri}
84 actionsSource={props.actionsSource}
85 />
86 );
87}