262 lines
6.2 KiB
TypeScript
262 lines
6.2 KiB
TypeScript
/**
|
|
* FileUpload - Datei-Auswahl und -Versand
|
|
*
|
|
* Oeffnet den Dateimanager des Geraets, zeigt eine Vorschau
|
|
* und konvertiert die Datei zu Base64 fuer die Uebertragung.
|
|
*/
|
|
|
|
import React, { useState } from 'react';
|
|
import {
|
|
View,
|
|
Text,
|
|
TouchableOpacity,
|
|
Image,
|
|
StyleSheet,
|
|
ActivityIndicator,
|
|
} from 'react-native';
|
|
import DocumentPicker, {
|
|
DocumentPickerResponse,
|
|
} from 'react-native-document-picker';
|
|
import RNFS from 'react-native-fs';
|
|
|
|
// --- Typen ---
|
|
|
|
export interface FileData {
|
|
name: string;
|
|
type: string;
|
|
size: number;
|
|
base64: string;
|
|
uri: string;
|
|
}
|
|
|
|
interface FileUploadProps {
|
|
onFileSelected: (file: FileData) => void;
|
|
onCancel: () => void;
|
|
}
|
|
|
|
// Unterstuetzte Dateitypen
|
|
const SUPPORTED_TYPES = [
|
|
DocumentPicker.types.images,
|
|
DocumentPicker.types.pdf,
|
|
DocumentPicker.types.docx,
|
|
DocumentPicker.types.plainText,
|
|
];
|
|
|
|
// --- Komponente ---
|
|
|
|
const FileUpload: React.FC<FileUploadProps> = ({ onFileSelected, onCancel }) => {
|
|
const [selectedFile, setSelectedFile] = useState<DocumentPickerResponse | null>(null);
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
|
|
const pickFile = async () => {
|
|
setError(null);
|
|
try {
|
|
const result = await DocumentPicker.pick({
|
|
type: SUPPORTED_TYPES,
|
|
copyTo: 'cachesDirectory',
|
|
});
|
|
|
|
if (result.length > 0) {
|
|
setSelectedFile(result[0]);
|
|
}
|
|
} catch (err) {
|
|
if (DocumentPicker.isCancel(err)) {
|
|
onCancel();
|
|
} else {
|
|
setError('Fehler beim Auswaehlen der Datei');
|
|
console.error('[FileUpload] Fehler:', err);
|
|
}
|
|
}
|
|
};
|
|
|
|
const sendFile = async () => {
|
|
if (!selectedFile) return;
|
|
|
|
setLoading(true);
|
|
try {
|
|
// Datei lesen und zu Base64 konvertieren
|
|
const filePath = selectedFile.fileCopyUri || selectedFile.uri;
|
|
// URI-Schema entfernen fuer RNFS (file:// → absoluter Pfad)
|
|
const cleanPath = filePath.replace('file://', '');
|
|
const base64 = await RNFS.readFile(cleanPath, 'base64');
|
|
|
|
const fileData: FileData = {
|
|
name: selectedFile.name || 'unbenannt',
|
|
type: selectedFile.type || 'application/octet-stream',
|
|
size: selectedFile.size || 0,
|
|
base64,
|
|
uri: selectedFile.uri,
|
|
};
|
|
|
|
onFileSelected(fileData);
|
|
} catch (err) {
|
|
setError('Fehler beim Verarbeiten der Datei');
|
|
console.error('[FileUpload] Verarbeitungsfehler:', err);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const isImage = selectedFile?.type?.startsWith('image/');
|
|
const fileSizeFormatted = selectedFile?.size
|
|
? selectedFile.size > 1024 * 1024
|
|
? `${(selectedFile.size / (1024 * 1024)).toFixed(1)} MB`
|
|
: `${(selectedFile.size / 1024).toFixed(0)} KB`
|
|
: '';
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
{!selectedFile ? (
|
|
// Datei auswaehlen
|
|
<TouchableOpacity style={styles.pickButton} onPress={pickFile} activeOpacity={0.7}>
|
|
<Text style={styles.pickIcon}>{'\uD83D\uDCC1'}</Text>
|
|
<Text style={styles.pickText}>Datei ausw\u00E4hlen</Text>
|
|
<Text style={styles.pickHint}>JPG, PNG, PDF, DOCX, TXT</Text>
|
|
</TouchableOpacity>
|
|
) : (
|
|
// Vorschau und Senden
|
|
<View style={styles.previewContainer}>
|
|
{isImage ? (
|
|
<Image source={{ uri: selectedFile.uri }} style={styles.imagePreview} />
|
|
) : (
|
|
<View style={styles.filePreview}>
|
|
<Text style={styles.fileIcon}>{'\uD83D\uDCC4'}</Text>
|
|
</View>
|
|
)}
|
|
|
|
<Text style={styles.fileName} numberOfLines={1}>
|
|
{selectedFile.name}
|
|
</Text>
|
|
<Text style={styles.fileSize}>{fileSizeFormatted}</Text>
|
|
|
|
{error && <Text style={styles.errorText}>{error}</Text>}
|
|
|
|
<View style={styles.buttonRow}>
|
|
<TouchableOpacity
|
|
style={styles.cancelButton}
|
|
onPress={() => setSelectedFile(null)}
|
|
>
|
|
<Text style={styles.cancelButtonText}>Andere Datei</Text>
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity
|
|
style={styles.sendButton}
|
|
onPress={sendFile}
|
|
disabled={loading}
|
|
>
|
|
{loading ? (
|
|
<ActivityIndicator color="#FFFFFF" size="small" />
|
|
) : (
|
|
<Text style={styles.sendButtonText}>Senden</Text>
|
|
)}
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
)}
|
|
</View>
|
|
);
|
|
};
|
|
|
|
// --- Styles ---
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
backgroundColor: '#1A1A2E',
|
|
borderRadius: 16,
|
|
padding: 20,
|
|
margin: 12,
|
|
},
|
|
pickButton: {
|
|
alignItems: 'center',
|
|
padding: 30,
|
|
borderWidth: 2,
|
|
borderColor: '#2A2A3E',
|
|
borderStyle: 'dashed',
|
|
borderRadius: 12,
|
|
},
|
|
pickIcon: {
|
|
fontSize: 40,
|
|
marginBottom: 10,
|
|
},
|
|
pickText: {
|
|
color: '#FFFFFF',
|
|
fontSize: 16,
|
|
fontWeight: '600',
|
|
},
|
|
pickHint: {
|
|
color: '#666680',
|
|
fontSize: 12,
|
|
marginTop: 4,
|
|
},
|
|
previewContainer: {
|
|
alignItems: 'center',
|
|
},
|
|
imagePreview: {
|
|
width: 200,
|
|
height: 200,
|
|
borderRadius: 12,
|
|
marginBottom: 12,
|
|
resizeMode: 'cover',
|
|
},
|
|
filePreview: {
|
|
width: 80,
|
|
height: 80,
|
|
borderRadius: 12,
|
|
backgroundColor: '#2A2A3E',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
marginBottom: 12,
|
|
},
|
|
fileIcon: {
|
|
fontSize: 36,
|
|
},
|
|
fileName: {
|
|
color: '#FFFFFF',
|
|
fontSize: 14,
|
|
fontWeight: '500',
|
|
maxWidth: 250,
|
|
},
|
|
fileSize: {
|
|
color: '#666680',
|
|
fontSize: 12,
|
|
marginTop: 2,
|
|
},
|
|
errorText: {
|
|
color: '#FF3B30',
|
|
fontSize: 12,
|
|
marginTop: 8,
|
|
},
|
|
buttonRow: {
|
|
flexDirection: 'row',
|
|
marginTop: 16,
|
|
gap: 12,
|
|
},
|
|
cancelButton: {
|
|
paddingHorizontal: 20,
|
|
paddingVertical: 10,
|
|
borderRadius: 8,
|
|
backgroundColor: '#2A2A3E',
|
|
},
|
|
cancelButtonText: {
|
|
color: '#8888AA',
|
|
fontSize: 14,
|
|
fontWeight: '600',
|
|
},
|
|
sendButton: {
|
|
paddingHorizontal: 28,
|
|
paddingVertical: 10,
|
|
borderRadius: 8,
|
|
backgroundColor: '#0096FF',
|
|
minWidth: 90,
|
|
alignItems: 'center',
|
|
},
|
|
sendButtonText: {
|
|
color: '#FFFFFF',
|
|
fontSize: 14,
|
|
fontWeight: '700',
|
|
},
|
|
});
|
|
|
|
export default FileUpload;
|