visualize pdf

This commit is contained in:
m
2026-07-14 10:49:17 +02:00
parent 333387ad98
commit 79c743722d
3 changed files with 150 additions and 2 deletions
+58 -2
View File
@@ -1,4 +1,4 @@
import React, { useRef, useState } from 'react'; import React, { useRef, useState, useEffect } from 'react';
import { import {
View, View,
Text, Text,
@@ -10,7 +10,10 @@ import {
ScrollView, ScrollView,
} from 'react-native'; } from 'react-native';
import { RouteProp, useRoute } from '@react-navigation/native'; import { RouteProp, useRoute } from '@react-navigation/native';
import Pdf from 'react-native-pdf';
import { downloadAsync, documentDirectory, getInfoAsync } from 'expo-file-system/legacy';
import { useFile, useFileImage } from '../hooks/useFiles'; import { useFile, useFileImage } from '../hooks/useFiles';
import { apiClient } from '../api/client';
import { TagChip } from '../components/TagChip'; import { TagChip } from '../components/TagChip';
import { FileThumbnail } from '../components/FileThumbnail'; import { FileThumbnail } from '../components/FileThumbnail';
@@ -27,11 +30,59 @@ function DetailItem({ fileId }: { fileId: string }) {
const { data: fileData } = useFile(fileId); const { data: fileData } = useFile(fileId);
const uri = imageData?.data?.url; const uri = imageData?.data?.url;
const file = fileData as any; const file = fileData as any;
const isPdf = file?.data?.mimeType === 'application/pdf';
const [localPdfUri, setLocalPdfUri] = useState<string | null>(null);
const [pdfLoading, setPdfLoading] = useState(false);
useEffect(() => {
if (!isPdf || !uri) return;
let cancelled = false;
async function downloadPdf() {
if (!uri) return;
setPdfLoading(true);
const localUri = `${documentDirectory}pdf_${fileId}.pdf`;
try {
const info = await getInfoAsync(localUri);
if (info.exists && info.size > 100) {
if (!cancelled) setLocalPdfUri(localUri);
return;
}
const headers: Record<string, string> = {};
const token = apiClient.getAccessToken();
if (token) headers['Authorization'] = `Bearer ${token}`;
await downloadAsync(uri, localUri, { headers });
if (!cancelled) setLocalPdfUri(localUri);
} catch (e) {
console.log('PDF download error:', e);
} finally {
if (!cancelled) setPdfLoading(false);
}
}
downloadPdf();
return () => { cancelled = true; };
}, [isPdf, uri, fileId]);
return ( return (
<ScrollView style={styles.page} contentContainerStyle={styles.pageContent}> <ScrollView style={styles.page} contentContainerStyle={styles.pageContent}>
<View style={styles.imageContainer}> <View style={styles.imageContainer}>
{uri ? ( {isPdf ? (
pdfLoading || !localPdfUri ? (
<ActivityIndicator size="large" color="#fff" />
) : (
<Pdf
source={{ uri: localPdfUri }}
style={styles.pdf}
onLoadComplete={(numberOfPages) => {
console.log(`PDF loaded: ${numberOfPages} pages`);
}}
onError={(error) => {
console.log('PDF error:', error);
}}
/>
)
) : uri ? (
<Image source={{ uri }} style={styles.image} resizeMode="contain" /> <Image source={{ uri }} style={styles.image} resizeMode="contain" />
) : file ? ( ) : file ? (
<FileThumbnail <FileThumbnail
@@ -150,6 +201,11 @@ const styles = StyleSheet.create({
width: SCREEN_WIDTH, width: SCREEN_WIDTH,
height: SCREEN_WIDTH, height: SCREEN_WIDTH,
}, },
pdf: {
width: SCREEN_WIDTH,
height: SCREEN_WIDTH,
backgroundColor: '#fff',
},
placeholder: { placeholder: {
width: SCREEN_WIDTH, width: SCREEN_WIDTH,
height: SCREEN_WIDTH, height: SCREEN_WIDTH,
+91
View File
@@ -24,6 +24,7 @@
"react-native-mmkv": "^4.3.2", "react-native-mmkv": "^4.3.2",
"react-native-nitro-image": "^0.15.1", "react-native-nitro-image": "^0.15.1",
"react-native-nitro-modules": "^0.36.1", "react-native-nitro-modules": "^0.36.1",
"react-native-pdf": "^7.0.4",
"react-native-safe-area-context": "~5.7.0", "react-native-safe-area-context": "~5.7.0",
"react-native-screens": "4.25.2", "react-native-screens": "4.25.2",
"react-native-vision-camera": "^5.1.0" "react-native-vision-camera": "^5.1.0"
@@ -1668,6 +1669,12 @@
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
} }
}, },
"node_modules/@react-native/normalize-color": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/@react-native/normalize-color/-/normalize-color-2.1.0.tgz",
"integrity": "sha512-Z1jQI2NpdFJCVgpY+8Dq/Bt3d+YUi1928Q+/CZm/oh66fzM0RUl54vvuXlPJKybH4pdCZey1eDTPaLHkMPNgWA==",
"license": "MIT"
},
"node_modules/@react-native/normalize-colors": { "node_modules/@react-native/normalize-colors": {
"version": "0.86.0", "version": "0.86.0",
"resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.86.0.tgz", "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.86.0.tgz",
@@ -2134,6 +2141,13 @@
"node": "18 || 20 || >=22" "node": "18 || 20 || >=22"
} }
}, },
"node_modules/base-64": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz",
"integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==",
"license": "MIT",
"peer": true
},
"node_modules/base64-js": { "node_modules/base64-js": {
"version": "1.5.1", "version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@@ -2576,6 +2590,12 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/crypto-js": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/crypto-js/-/crypto-js-4.2.0.tgz",
"integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
"license": "MIT"
},
"node_modules/csstype": { "node_modules/csstype": {
"version": "3.2.3", "version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
@@ -2639,6 +2659,17 @@
"node": ">= 0.8" "node": ">= 0.8"
} }
}, },
"node_modules/deprecated-react-native-prop-types": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/deprecated-react-native-prop-types/-/deprecated-react-native-prop-types-2.3.0.tgz",
"integrity": "sha512-pWD0voFtNYxrVqvBMYf5gq3NA2GCpfodS1yNynTPc93AYA/KEMGeWDqqeUB6R2Z9ZofVhks2aeJXiuQqKNpesA==",
"license": "MIT",
"dependencies": {
"@react-native/normalize-color": "*",
"invariant": "*",
"prop-types": "*"
}
},
"node_modules/destroy": { "node_modules/destroy": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
@@ -4798,6 +4829,15 @@
"node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0" "node": "^20.19.4 || ^22.13.0 || ^24.3.0 || >= 25.0.0"
} }
}, },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/on-finished": { "node_modules/on-finished": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
@@ -5161,6 +5201,23 @@
"node": ">= 6" "node": ">= 6"
} }
}, },
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"license": "MIT",
"dependencies": {
"loose-envify": "^1.4.0",
"object-assign": "^4.1.1",
"react-is": "^16.13.1"
}
},
"node_modules/prop-types/node_modules/react-is": {
"version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"license": "MIT"
},
"node_modules/query-string": { "node_modules/query-string": {
"version": "7.1.3", "version": "7.1.3",
"resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz", "resolved": "https://registry.npmjs.org/query-string/-/query-string-7.1.3.tgz",
@@ -5293,6 +5350,25 @@
} }
} }
}, },
"node_modules/react-native-blob-util": {
"version": "0.24.10",
"resolved": "https://registry.npmjs.org/react-native-blob-util/-/react-native-blob-util-0.24.10.tgz",
"integrity": "sha512-4yazgoCstXgt2/CRURtowGP3furAzbWdfMQ4jyUPDo5xtLx7KE2F/xMz3oU3shLwfL4id0iEKsBywoxvJSHPzQ==",
"license": "MIT",
"peer": true,
"dependencies": {
"base-64": "1.0.0",
"glob": "13.0.6"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ronradtke"
},
"peerDependencies": {
"react": "*",
"react-native": "*"
}
},
"node_modules/react-native-mmkv": { "node_modules/react-native-mmkv": {
"version": "4.3.2", "version": "4.3.2",
"resolved": "https://registry.npmjs.org/react-native-mmkv/-/react-native-mmkv-4.3.2.tgz", "resolved": "https://registry.npmjs.org/react-native-mmkv/-/react-native-mmkv-4.3.2.tgz",
@@ -5328,6 +5404,21 @@
"react-native": "*" "react-native": "*"
} }
}, },
"node_modules/react-native-pdf": {
"version": "7.0.4",
"resolved": "https://registry.npmjs.org/react-native-pdf/-/react-native-pdf-7.0.4.tgz",
"integrity": "sha512-Uhxn5SSguMKvbwD50iIZfYPmYeKcX+9I3tc4J3HCbRExAN0uqXTAKx2pvbR2Y62umAj0JS274FcbSldQfoQfyg==",
"license": "MIT",
"dependencies": {
"crypto-js": "4.2.0",
"deprecated-react-native-prop-types": "^2.3.0"
},
"peerDependencies": {
"react": "*",
"react-native": "*",
"react-native-blob-util": ">=0.13.7"
}
},
"node_modules/react-native-safe-area-context": { "node_modules/react-native-safe-area-context": {
"version": "5.7.0", "version": "5.7.0",
"resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.7.0.tgz", "resolved": "https://registry.npmjs.org/react-native-safe-area-context/-/react-native-safe-area-context-5.7.0.tgz",
+1
View File
@@ -19,6 +19,7 @@
"react-native-mmkv": "^4.3.2", "react-native-mmkv": "^4.3.2",
"react-native-nitro-image": "^0.15.1", "react-native-nitro-image": "^0.15.1",
"react-native-nitro-modules": "^0.36.1", "react-native-nitro-modules": "^0.36.1",
"react-native-pdf": "^7.0.4",
"react-native-safe-area-context": "~5.7.0", "react-native-safe-area-context": "~5.7.0",
"react-native-screens": "4.25.2", "react-native-screens": "4.25.2",
"react-native-vision-camera": "^5.1.0" "react-native-vision-camera": "^5.1.0"