A React Native app for the ultimate thinking partner.
at sdk-v1-upgrade 140 lines 3.8 kB view raw
1/** 2 * File Upload Utility 3 * 4 * Handles document file picking and upload for file attachments. 5 * Supports web, iOS, and Android platforms. 6 * 7 * This utility is used by MessageInputEnhanced to handle document uploads. 8 */ 9 10import { Platform, Alert } from 'react-native'; 11import * as DocumentPicker from 'expo-document-picker'; 12 13export interface FilePickerResult { 14 name: string; 15 size: number; 16 type: string; 17 file: File; // Web File object 18 uri?: string; // Mobile URI 19} 20 21/** 22 * Opens a file picker for document selection 23 * 24 * @returns Promise<FilePickerResult | null> - Selected file info or null if cancelled 25 */ 26export async function pickFile(): Promise<FilePickerResult | null> { 27 // Web platform - use HTML file input 28 if (Platform.OS === 'web') { 29 return pickFileWeb(); 30 } 31 32 // Mobile platforms (iOS/Android) - use expo-document-picker 33 return pickFileMobile(); 34} 35 36/** 37 * Web file picker implementation 38 */ 39async function pickFileWeb(): Promise<FilePickerResult | null> { 40 return new Promise((resolve) => { 41 try { 42 const input = document.createElement('input'); 43 input.type = 'file'; 44 input.accept = '.pdf,.txt,.md,.json,.csv,.doc,.docx'; 45 46 input.onchange = async (e: any) => { 47 const file = e.target?.files?.[0]; 48 if (!file) { 49 resolve(null); 50 return; 51 } 52 53 console.log('Selected file:', file.name, 'size:', file.size, 'type:', file.type); 54 55 // Check file size (10MB limit) 56 const MAX_SIZE = 10 * 1024 * 1024; 57 if (file.size > MAX_SIZE) { 58 const sizeMB = (file.size / 1024 / 1024).toFixed(2); 59 Alert.alert( 60 'File Too Large', 61 `This file is ${sizeMB}MB. Maximum allowed is 10MB.` 62 ); 63 resolve(null); 64 return; 65 } 66 67 resolve({ 68 name: file.name, 69 size: file.size, 70 type: file.type, 71 file, 72 }); 73 }; 74 75 input.oncancel = () => { 76 resolve(null); 77 }; 78 79 input.click(); 80 } catch (error) { 81 console.error('Error creating file picker:', error); 82 Alert.alert('Error', 'Failed to open file picker'); 83 resolve(null); 84 } 85 }); 86} 87 88/** 89 * Mobile file picker implementation (iOS/Android) 90 */ 91async function pickFileMobile(): Promise<FilePickerResult | null> { 92 try { 93 const result = await DocumentPicker.getDocumentAsync({ 94 type: ['application/pdf', 'text/plain', 'text/markdown', 'application/json', 95 'text/csv', 'application/msword', 96 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'], 97 copyToCacheDirectory: true, 98 }); 99 100 if (result.canceled) { 101 return null; 102 } 103 104 const asset = result.assets[0]; 105 if (!asset) { 106 return null; 107 } 108 109 console.log('Selected file:', asset.name, 'size:', asset.size, 'type:', asset.mimeType); 110 111 // Check file size (10MB limit) 112 const MAX_SIZE = 10 * 1024 * 1024; 113 if (asset.size && asset.size > MAX_SIZE) { 114 const sizeMB = (asset.size / 1024 / 1024).toFixed(2); 115 Alert.alert( 116 'File Too Large', 117 `This file is ${sizeMB}MB. Maximum allowed is 10MB.` 118 ); 119 return null; 120 } 121 122 // For mobile, we need to convert the URI to a File object for upload 123 // We'll fetch the file content and create a blob 124 const response = await fetch(asset.uri); 125 const blob = await response.blob(); 126 const file = new File([blob], asset.name, { type: asset.mimeType || 'application/octet-stream' }); 127 128 return { 129 name: asset.name, 130 size: asset.size || blob.size, 131 type: asset.mimeType || 'application/octet-stream', 132 file, 133 uri: asset.uri, 134 }; 135 } catch (error) { 136 console.error('Error picking file:', error); 137 Alert.alert('Error', 'Failed to pick file'); 138 return null; 139 } 140}