feat: Enhance document management with a first draft of upload and download functionalities (needs revision)

This commit is contained in:
2026-01-12 12:42:33 +01:00
parent 6e5b9cde68
commit 325bfbe19f
7 changed files with 276 additions and 53 deletions

View File

@ -1,5 +1,6 @@
import { Download, FileText, MapPin, Plus, Search, CalendarIcon } from 'lucide-react-native'; import { ArrowLeft, Download, FileText, MapPin, Plus, Search, CalendarIcon } from 'lucide-react-native';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
import { useRouter } from 'expo-router';
import { RangePickerModal } from '@/components/RangePickerModal'; import { RangePickerModal } from '@/components/RangePickerModal';
import { Alert, RefreshControl, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native'; import { Alert, RefreshControl, ScrollView, Text, TextInput, TouchableOpacity, View } from 'react-native';
import { DocumentItem } from '@/types/types'; import { DocumentItem } from '@/types/types';
@ -8,8 +9,10 @@ import dayjs from 'dayjs';
import LoadingScreen from '@/components/LoadingScreen'; import LoadingScreen from '@/components/LoadingScreen';
import { formatTimestamp, parseTimestamp } from '@/utils/dateTime'; import { formatTimestamp, parseTimestamp } from '@/utils/dateTime';
import AddDocumentModal from '@/components/AddDocumentModal'; import AddDocumentModal from '@/components/AddDocumentModal';
import { downloadAndShareDocument, downloadDocumentByUrl, downloadDocumentLegacy, uploadDocument } from '@/utils/documentUtils';
export default function DocumentsScreen() { export default function DocumentsScreen() {
const router = useRouter();
const [documents, setDocuments] = useState<DocumentItem[]>([]); const [documents, setDocuments] = useState<DocumentItem[]>([]);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false); const [refreshing, setRefreshing] = useState(false);
@ -77,25 +80,9 @@ export default function DocumentsScreen() {
// Gestione Caricamento Documento // Gestione Caricamento Documento
const handleUploadDocument = async (file: any, customTitle?: string) => { const handleUploadDocument = async (file: any, customTitle?: string) => {
console.log('Caricamento documento:', file, 'con titolo personalizzato:', customTitle);
setIsUploading(true); setIsUploading(true);
try { try {
// const formData = new FormData(); const response = await uploadDocument(file, null, customTitle);
// formData.append('file', {
// uri: file.uri,
// name: file.name,
// type: file.mimeType || 'application/octet-stream',
// } as any);
// if (customTitle) {
// formData.append('title', customTitle);
// }
// const response = await api.post('/attachment/upload', formData, {
// headers: {
// 'Content-Type': 'multipart/form-data',
// },
// });
// console.log('Risposta caricamento:', response.data); // console.log('Risposta caricamento:', response.data);
// Alert.alert('Successo', 'Documento caricato con successo!'); // Alert.alert('Successo', 'Documento caricato con successo!');
// setShowUploadModal(false); // setShowUploadModal(false);
@ -108,6 +95,16 @@ export default function DocumentsScreen() {
} }
} }
// Gestione Download e Condivisione Documento
const handleDownloadAndShare = async (mimetype: string, fileName: string, fileUrl: string) => {
try {
await downloadAndShareDocument(mimetype, fileName, fileUrl);
} catch (error) {
console.error('Errore nel download/condivisione del documento:', error);
Alert.alert('Errore', 'Impossibile scaricare/condividere il documento. Riprova più tardi.');
}
};
if (isLoading && !refreshing) { if (isLoading && !refreshing) {
return ( return (
<LoadingScreen /> <LoadingScreen />
@ -117,11 +114,15 @@ export default function DocumentsScreen() {
return ( return (
<View className="flex-1 bg-gray-50"> <View className="flex-1 bg-gray-50">
{/* Header */} {/* Header */}
{/* TODO: Aggiungi torna indietro */} <View className="flex-row items-center gap-4 bg-white p-6 pt-16 shadow-sm border-b border-gray-100">
<View className="bg-white p-6 pt-16 shadow-sm border-b border-gray-100"> <TouchableOpacity onPress={() => router.back()} className="p-2 -ml-2 rounded-full active:bg-gray-100">
<Text className="text-3xl font-bold text-gray-800 mb-1">Documenti</Text> <ArrowLeft size={24} color="#374151" />
</TouchableOpacity>
<View className="flex-1">
<Text className="text-3xl font-bold text-gray-800">Documenti</Text>
<Text className="text-base text-gray-500">Gestisci i tuoi documenti</Text> <Text className="text-base text-gray-500">Gestisci i tuoi documenti</Text>
</View> </View>
</View>
<View className="p-5 gap-6 flex-1"> <View className="p-5 gap-6 flex-1">
{/* Search + Date Row */} {/* Search + Date Row */}
@ -179,7 +180,9 @@ export default function DocumentsScreen() {
</View> </View>
</View> </View>
</View> </View>
<TouchableOpacity className="p-4 bg-gray-50 rounded-2xl active:bg-gray-100"> <TouchableOpacity
onPress={() => downloadDocumentLegacy(doc.mimetype, doc.title, doc.url)} // downloadDocumentByUrl(doc.url, doc.title) handleDownloadAndShare(doc.mimetype, doc.title, doc.url)
className="p-4 bg-gray-50 rounded-2xl active:bg-gray-100">
<Download size={24} color="#6b7280" /> <Download size={24} color="#6b7280" />
</TouchableOpacity> </TouchableOpacity>
</View> </View>

View File

@ -8,6 +8,8 @@ import LoadingScreen from '@/components/LoadingScreen';
import api from '@/utils/api'; import api from '@/utils/api';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { formatTimestamp, parseTimestamp } from '@/utils/dateTime'; import { formatTimestamp, parseTimestamp } from '@/utils/dateTime';
import { downloadAndShareDocument, uploadDocument } from '@/utils/documentUtils';
import AddDocumentModal from '@/components/AddDocumentModal';
export default function SiteDocumentsScreen() { export default function SiteDocumentsScreen() {
const router = useRouter(); const router = useRouter();
@ -17,6 +19,9 @@ export default function SiteDocumentsScreen() {
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false); const [refreshing, setRefreshing] = useState(false);
const [showUploadModal, setShowUploadModal] = useState(false);
const [isUploading, setIsUploading] = useState(false);
// Fetch dei documenti del cantiere // Fetch dei documenti del cantiere
const fetchSiteDocuments = useCallback(async (siteId: number, isRefreshing = false) => { const fetchSiteDocuments = useCallback(async (siteId: number, isRefreshing = false) => {
try { try {
@ -88,6 +93,33 @@ export default function SiteDocumentsScreen() {
return true; return true;
}); });
// Gestione Download e Condivisione Documento
const handleDownloadAndShare = async (mimetype: string, fileName: string, fileUrl: string) => {
try {
await downloadAndShareDocument(mimetype, fileName, fileUrl);
} catch (error) {
console.error('Errore nel download/condivisione del documento:', error);
Alert.alert('Errore', 'Impossibile scaricare il documento. Riprova più tardi.');
}
};
// Gestione Caricamento Documento
const handleUploadDocument = async (file: any, customTitle?: string) => {
setIsUploading(true);
try {
const response = await uploadDocument(file, Number(params.id), customTitle);
// console.log('Risposta caricamento:', response.data);
// Alert.alert('Successo', 'Documento caricato con successo!');
// setShowUploadModal(false);
// fetchSiteDocuments(Number(params.id)); // Ricarica la lista dei documenti
} catch (error) {
console.error('Errore nel caricamento del documento:', error);
Alert.alert('Errore', 'Impossibile caricare il documento. Riprova più tardi.');
} finally {
setIsUploading(false);
}
}
if (!site || (isLoading && !refreshing)) { if (!site || (isLoading && !refreshing)) {
return ( return (
<LoadingScreen /> <LoadingScreen />
@ -175,7 +207,9 @@ export default function SiteDocumentsScreen() {
<Text className="text-sm text-gray-400 font-medium">{formatTimestamp(doc.updated_at)}</Text> <Text className="text-sm text-gray-400 font-medium">{formatTimestamp(doc.updated_at)}</Text>
</View> </View>
</View> </View>
<TouchableOpacity className="p-4 bg-gray-50 rounded-2xl active:bg-gray-100"> <TouchableOpacity
onPress={() => handleDownloadAndShare(doc.mimetype, doc.title, doc.url)}
className="p-4 bg-gray-50 rounded-2xl active:bg-gray-100">
<Download size={24} color="#6b7280" /> <Download size={24} color="#6b7280" />
</TouchableOpacity> </TouchableOpacity>
</View> </View>
@ -189,11 +223,19 @@ export default function SiteDocumentsScreen() {
{/* FAB (Spostato qui) */} {/* FAB (Spostato qui) */}
<TouchableOpacity <TouchableOpacity
onPress={() => alert(`Aggiungi doc a ${site.name}`)} onPress={() => setShowUploadModal(true)}
className="absolute bottom-8 right-6 w-16 h-16 bg-[#099499] rounded-full shadow-lg items-center justify-center active:scale-90" className="absolute bottom-8 right-6 w-16 h-16 bg-[#099499] rounded-full shadow-lg items-center justify-center active:scale-90"
> >
<Plus size={32} color="white" /> <Plus size={32} color="white" />
</TouchableOpacity> </TouchableOpacity>
{/* Modale Caricamento Documento */}
<AddDocumentModal
visible={showUploadModal}
onClose={() => setShowUploadModal(false)}
onUpload={handleUploadDocument}
isUploading={isUploading}
/>
</View> </View>
); );
} }

View File

@ -22,6 +22,7 @@ export default function AddDocumentModal({ visible, onClose, onUpload, isUploadi
} }
}, [visible]); }, [visible]);
// TODO: Considerare selezione multipla?
const pickDocument = async () => { const pickDocument = async () => {
try { try {
const result = await DocumentPicker.getDocumentAsync({ const result = await DocumentPicker.getDocumentAsync({
@ -34,7 +35,7 @@ export default function AddDocumentModal({ visible, onClose, onUpload, isUploadi
const asset = result.assets[0]; const asset = result.assets[0];
setSelectedFile(asset); setSelectedFile(asset);
// Pre-compila il titolo con il nome del file (senza estensione se vuoi essere fancy, qui lo lascio intero) // Pre-compila il titolo con il nome del file
setCustomTitle(asset.name); setCustomTitle(asset.name);
} catch (err) { } catch (err) {
@ -54,6 +55,7 @@ export default function AddDocumentModal({ visible, onClose, onUpload, isUploadi
}; };
// Formatta dimensione file // Formatta dimensione file
// TODO: Spostare in utils?
const formatSize = (size?: number) => { const formatSize = (size?: number) => {
if (!size) return '0 B'; if (!size) return '0 B';
const k = 1024; const k = 1024;
@ -102,10 +104,10 @@ export default function AddDocumentModal({ visible, onClose, onUpload, isUploadi
<FileText size={24} color="#099499" /> <FileText size={24} color="#099499" />
</View> </View>
<View className="flex-1"> <View className="flex-1">
<Text className="text-gray-800 font-bold text-sm" numberOfLines={1}> <Text className="text-gray-800 font-bold text-base" numberOfLines={1}>
{selectedFile.name} {selectedFile.name}
</Text> </Text>
<Text className="text-gray-500 text-xs"> <Text className="text-gray-500 text-sm">
{formatSize(selectedFile.size)} {formatSize(selectedFile.size)}
</Text> </Text>
</View> </View>

72
package-lock.json generated
View File

@ -17,12 +17,14 @@
"expo": "~54.0.25", "expo": "~54.0.25",
"expo-constants": "~18.0.10", "expo-constants": "~18.0.10",
"expo-document-picker": "~14.0.8", "expo-document-picker": "~14.0.8",
"expo-file-system": "~19.0.21",
"expo-font": "~14.0.9", "expo-font": "~14.0.9",
"expo-haptics": "~15.0.7", "expo-haptics": "~15.0.7",
"expo-image": "~3.0.10", "expo-image": "~3.0.10",
"expo-linking": "~8.0.9", "expo-linking": "~8.0.11",
"expo-router": "~6.0.15", "expo-router": "~6.0.15",
"expo-secure-store": "~15.0.8", "expo-secure-store": "~15.0.8",
"expo-sharing": "~14.0.8",
"expo-splash-screen": "~31.0.11", "expo-splash-screen": "~31.0.11",
"expo-status-bar": "~3.0.8", "expo-status-bar": "~3.0.8",
"expo-symbols": "~1.0.7", "expo-symbols": "~1.0.7",
@ -1756,15 +1758,15 @@
} }
}, },
"node_modules/@expo/config": { "node_modules/@expo/config": {
"version": "12.0.11", "version": "12.0.13",
"resolved": "https://registry.npmjs.org/@expo/config/-/config-12.0.11.tgz", "resolved": "https://registry.npmjs.org/@expo/config/-/config-12.0.13.tgz",
"integrity": "sha512-bGKNCbHirwgFlcOJHXpsAStQvM0nU3cmiobK0o07UkTfcUxl9q9lOQQh2eoMGqpm6Vs1IcwBpYye6thC3Nri/w==", "integrity": "sha512-Cu52arBa4vSaupIWsF0h7F/Cg//N374nYb7HAxV0I4KceKA7x2UXpYaHOL7EEYYvp7tZdThBjvGpVmr8ScIvaQ==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/code-frame": "~7.10.4", "@babel/code-frame": "~7.10.4",
"@expo/config-plugins": "~54.0.3", "@expo/config-plugins": "~54.0.4",
"@expo/config-types": "^54.0.9", "@expo/config-types": "^54.0.10",
"@expo/json-file": "^10.0.7", "@expo/json-file": "^10.0.8",
"deepmerge": "^4.3.1", "deepmerge": "^4.3.1",
"getenv": "^2.0.0", "getenv": "^2.0.0",
"glob": "^13.0.0", "glob": "^13.0.0",
@ -1777,14 +1779,14 @@
} }
}, },
"node_modules/@expo/config-plugins": { "node_modules/@expo/config-plugins": {
"version": "54.0.3", "version": "54.0.4",
"resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-54.0.3.tgz", "resolved": "https://registry.npmjs.org/@expo/config-plugins/-/config-plugins-54.0.4.tgz",
"integrity": "sha512-tBIUZIxLQfCu5jmqTO+UOeeDUGIB0BbK6xTMkPRObAXRQeTLPPfokZRCo818d2owd+Bcmq1wBaDz0VY3g+glfw==", "integrity": "sha512-g2yXGICdoOw5i3LkQSDxl2Q5AlQCrG7oniu0pCPPO+UxGb7He4AFqSvPSy8HpRUj55io17hT62FTjYRD+d6j3Q==",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@expo/config-types": "^54.0.9", "@expo/config-types": "^54.0.10",
"@expo/json-file": "~10.0.7", "@expo/json-file": "~10.0.8",
"@expo/plist": "^0.4.7", "@expo/plist": "^0.4.8",
"@expo/sdk-runtime-versions": "^1.0.0", "@expo/sdk-runtime-versions": "^1.0.0",
"chalk": "^4.1.2", "chalk": "^4.1.2",
"debug": "^4.3.5", "debug": "^4.3.5",
@ -1811,9 +1813,9 @@
} }
}, },
"node_modules/@expo/config-types": { "node_modules/@expo/config-types": {
"version": "54.0.9", "version": "54.0.10",
"resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-54.0.9.tgz", "resolved": "https://registry.npmjs.org/@expo/config-types/-/config-types-54.0.10.tgz",
"integrity": "sha512-Llf4jwcrAnrxgE5WCdAOxtMf8FGwS4Sk0SSgI0NnIaSyCnmOCAm80GPFvsK778Oj19Ub4tSyzdqufPyeQPksWw==", "integrity": "sha512-/J16SC2an1LdtCZ67xhSkGXpALYUVUNyZws7v+PVsFZxClYehDSoKLqyRaGkpHlYrCc08bS0RF5E0JV6g50psA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/@expo/config/node_modules/@babel/code-frame": { "node_modules/@expo/config/node_modules/@babel/code-frame": {
@ -6453,9 +6455,9 @@
} }
}, },
"node_modules/expo-file-system": { "node_modules/expo-file-system": {
"version": "19.0.20", "version": "19.0.21",
"resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-19.0.20.tgz", "resolved": "https://registry.npmjs.org/expo-file-system/-/expo-file-system-19.0.21.tgz",
"integrity": "sha512-Jr/nNvJmUlptS3cHLKVBNyTyGMHNyxYBKRph1KRe0Nb3RzZza1gZLZXMG5Ky//sO2azTn+OaT0dv/lAyL0vJNA==", "integrity": "sha512-s3DlrDdiscBHtab/6W1osrjGL+C2bvoInPJD7sOwmxfJ5Woynv2oc+Fz1/xVXaE/V7HE/+xrHC/H45tu6lZzzg==",
"license": "MIT", "license": "MIT",
"peerDependencies": { "peerDependencies": {
"expo": "*", "expo": "*",
@ -6514,13 +6516,12 @@
} }
}, },
"node_modules/expo-linking": { "node_modules/expo-linking": {
"version": "8.0.10", "version": "8.0.11",
"resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-8.0.10.tgz", "resolved": "https://registry.npmjs.org/expo-linking/-/expo-linking-8.0.11.tgz",
"integrity": "sha512-0EKtn4Sk6OYmb/5ZqK8riO0k1Ic+wyT3xExbmDvUYhT7p/cKqlVUExMuOIAt3Cx3KUUU1WCgGmdd493W/D5XjA==", "integrity": "sha512-+VSaNL5om3kOp/SSKO5qe6cFgfSIWnnQDSbA7XLs3ECkYzXRquk5unxNS3pg7eK5kNUmQ4kgLI7MhTggAEUBLA==",
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"expo-constants": "~18.0.11", "expo-constants": "~18.0.12",
"invariant": "^2.2.4" "invariant": "^2.2.4"
}, },
"peerDependencies": { "peerDependencies": {
@ -6528,6 +6529,20 @@
"react-native": "*" "react-native": "*"
} }
}, },
"node_modules/expo-linking/node_modules/expo-constants": {
"version": "18.0.13",
"resolved": "https://registry.npmjs.org/expo-constants/-/expo-constants-18.0.13.tgz",
"integrity": "sha512-FnZn12E1dRYKDHlAdIyNFhBurKTS3F9CrfrBDJI5m3D7U17KBHMQ6JEfYlSj7LG7t+Ulr+IKaj58L1k5gBwTcQ==",
"license": "MIT",
"dependencies": {
"@expo/config": "~12.0.13",
"@expo/env": "~2.0.8"
},
"peerDependencies": {
"expo": "*",
"react-native": "*"
}
},
"node_modules/expo-modules-autolinking": { "node_modules/expo-modules-autolinking": {
"version": "3.0.23", "version": "3.0.23",
"resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-3.0.23.tgz", "resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-3.0.23.tgz",
@ -6828,6 +6843,15 @@
"node": ">=20.16.0" "node": ">=20.16.0"
} }
}, },
"node_modules/expo-sharing": {
"version": "14.0.8",
"resolved": "https://registry.npmjs.org/expo-sharing/-/expo-sharing-14.0.8.tgz",
"integrity": "sha512-A1pPr2iBrxypFDCWVAESk532HK+db7MFXbvO2sCV9ienaFXAk7lIBm6bkqgE6vzRd9O3RGdEGzYx80cYlc089Q==",
"license": "MIT",
"peerDependencies": {
"expo": "*"
}
},
"node_modules/expo-splash-screen": { "node_modules/expo-splash-screen": {
"version": "31.0.12", "version": "31.0.12",
"resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-31.0.12.tgz", "resolved": "https://registry.npmjs.org/expo-splash-screen/-/expo-splash-screen-31.0.12.tgz",

View File

@ -20,12 +20,14 @@
"expo": "~54.0.25", "expo": "~54.0.25",
"expo-constants": "~18.0.10", "expo-constants": "~18.0.10",
"expo-document-picker": "~14.0.8", "expo-document-picker": "~14.0.8",
"expo-file-system": "~19.0.21",
"expo-font": "~14.0.9", "expo-font": "~14.0.9",
"expo-haptics": "~15.0.7", "expo-haptics": "~15.0.7",
"expo-image": "~3.0.10", "expo-image": "~3.0.10",
"expo-linking": "~8.0.9", "expo-linking": "~8.0.11",
"expo-router": "~6.0.15", "expo-router": "~6.0.15",
"expo-secure-store": "~15.0.8", "expo-secure-store": "~15.0.8",
"expo-sharing": "~14.0.8",
"expo-splash-screen": "~31.0.11", "expo-splash-screen": "~31.0.11",
"expo-status-bar": "~3.0.8", "expo-status-bar": "~3.0.8",
"expo-symbols": "~1.0.7", "expo-symbols": "~1.0.7",

View File

@ -22,6 +22,7 @@ export interface AttendanceRecord {
export interface DocumentItem { export interface DocumentItem {
id: number; id: number;
mimetype: string;
title: string; title: string;
url: string; url: string;
updated_at: string; updated_at: string;

149
utils/documentUtils.tsx Normal file
View File

@ -0,0 +1,149 @@
import api from '@/utils/api';
import { Directory, File, Paths } from 'expo-file-system';
import * as FileSystem from 'expo-file-system/legacy';
import { StorageAccessFramework } from 'expo-file-system/legacy';
import * as Sharing from 'expo-sharing';
import * as Linking from 'expo-linking';
import { Platform } from 'react-native';
/**
* Gestisce l'upload di un documento verso il server usando Expo FileSystem
* @param file File da caricare (deve avere almeno la proprietà 'uri')
* @param siteId ID del sito a cui associare il documento (null per registro generale)
* @param customTitle Titolo personalizzato per il documento (opzionale)
*/
export const uploadDocument = async (
file: any,
siteId: number | null,
customTitle?: string
): Promise<void> => {
if (!file || !file.uri) {
throw new Error("File non valido per l'upload.");
}
if (siteId === null) {
console.log("Uploading document:", file, "to registry with title:", customTitle);
} else {
console.log("Uploading document:", file, "to site:", siteId, "with title:", customTitle);
}
// TODO: Funzione di upload (manca lato backend)
};
/**
* Scarica un documento e offre di aprirlo/condividerlo (expo-sharing)
* @param attachmentId ID o URL relativo del documento
* @param fileName Nome con cui salvare il file
* @param fileUrl URL completo del file da scaricare
*/
export const downloadAndShareDocument = async (
mimetype: string,
fileName: string,
fileUrl: string
): Promise<void> => {
try {
// TODO: Gestire meglio il download (attualmente si basa su expo-sharing)
if (!fileUrl || !fileName) {
throw new Error("Parametri mancanti per il download del documento.");
}
const destination = new Directory(Paths.cache, 'documents');
destination.exists ? destination.delete() : null;
destination.create({ overwrite: true });
const tmpFile = await File.downloadFileAsync(fileUrl, destination);
console.log("File temporaneo scaricato in:", tmpFile.uri);
const outFile = new File(destination, fileName);
await tmpFile.move(outFile);
console.log("File spostato in:", outFile.uri);
console.log("File type:", mimetype);
if (await Sharing.isAvailableAsync()) {
await Sharing.shareAsync(outFile.uri, {
mimeType: mimetype,
dialogTitle: `Scarica ${fileName}`,
UTI: 'public.item'
});
} else {
throw new Error("Condivisione non supportata su questo dispositivo.");
}
} catch (error) {
console.error("Download Error:", error);
throw error;
}
};
// TODO: Test con versione legacy di FileSystem e SAF
export const downloadDocumentLegacy = async (
mimetype: string,
fileName: string,
fileUrl: string
): Promise<void> => {
try {
if (!fileUrl || !fileName) {
throw new Error("Parametri mancanti per il download del documento.");
}
const path = FileSystem.cacheDirectory + 'documents/';
// Download del file nella directory selezionata
const tmpFile = await FileSystem.downloadAsync(fileUrl, path + fileName);
console.log("File temporaneo scaricato in:", tmpFile.uri);
if (Platform.OS === 'android') {
const permission = await StorageAccessFramework.requestDirectoryPermissionsAsync();
if (permission.granted) {
// Gets SAF URI from response
const safUri = permission.directoryUri;
console.log("Selected Directory URI:", safUri);
const fileContent = await FileSystem.readAsStringAsync(tmpFile.uri, { encoding: FileSystem.EncodingType.Base64 });
console.log("File letto in Base64, dimensione:", fileContent.length);
// Copia il file nella directory SAF selezionata
const destUri = await StorageAccessFramework.createFileAsync(
safUri,
fileName,
mimetype
);
console.log("Destinazione SAF URI:", destUri);
await FileSystem.writeAsStringAsync(destUri, fileContent, { encoding: FileSystem.EncodingType.Base64 });
console.log("File riscritto in SAF.");
}
} else if (Platform.OS === 'ios') {
if (await Sharing.isAvailableAsync()) {
await Sharing.shareAsync(tmpFile.uri, {
mimeType: mimetype,
dialogTitle: `Scarica ${fileName}`,
UTI: 'public.item'
});
} else {
throw new Error("Condivisione non supportata su questo dispositivo.");
}
}
} catch (error) {
console.error("Download Error:", error);
throw error;
}
};
// TODO: Test con Linking standard
export const downloadDocumentByUrl = async (
fileUrl: string,
fileName: string
): Promise<void> => {
try {
if (!fileUrl || !fileName) {
throw new Error("Parametri mancanti per il download del documento.");
}
Linking.openURL(fileUrl);
} catch (error) {
console.error("Download Error:", error);
throw error;
}
};