263 lines
6.2 KiB
TypeScript
263 lines
6.2 KiB
TypeScript
/**
|
|
* CameraUpload - Kamera-Foto oder Galerie-Auswahl
|
|
*
|
|
* Ermoeglicht das Aufnehmen eines Fotos mit der Geraetekamera
|
|
* oder die Auswahl aus der Galerie, mit Vorschau vor dem Senden.
|
|
*/
|
|
|
|
import React, { useState } from 'react';
|
|
import {
|
|
View,
|
|
Text,
|
|
TouchableOpacity,
|
|
Image,
|
|
StyleSheet,
|
|
ActivityIndicator,
|
|
Platform,
|
|
PermissionsAndroid,
|
|
} from 'react-native';
|
|
import { launchCamera, launchImageLibrary, ImagePickerResponse } from 'react-native-image-picker';
|
|
|
|
// --- Typen ---
|
|
|
|
export interface PhotoData {
|
|
base64: string;
|
|
width: number;
|
|
height: number;
|
|
fileName: string;
|
|
type: string;
|
|
uri: string;
|
|
}
|
|
|
|
interface CameraUploadProps {
|
|
onPhotoSelected: (photo: PhotoData) => void;
|
|
onCancel: () => void;
|
|
}
|
|
|
|
// Komprimierungsoptionen
|
|
const IMAGE_OPTIONS = {
|
|
mediaType: 'photo' as const,
|
|
maxWidth: 1920,
|
|
maxHeight: 1920,
|
|
quality: 0.8 as const,
|
|
includeBase64: true,
|
|
};
|
|
|
|
// --- Komponente ---
|
|
|
|
const CameraUpload: React.FC<CameraUploadProps> = ({ onPhotoSelected, onCancel }) => {
|
|
const [preview, setPreview] = useState<ImagePickerResponse | null>(null);
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
/** Kamera-Berechtigung pruefen (Android) */
|
|
const requestCameraPermission = async (): Promise<boolean> => {
|
|
if (Platform.OS !== 'android') return true;
|
|
|
|
try {
|
|
const granted = await PermissionsAndroid.request(
|
|
PermissionsAndroid.PERMISSIONS.CAMERA,
|
|
{
|
|
title: 'ARIA Cockpit - Kamera',
|
|
message: 'ARIA ben\u00F6tigt Zugriff auf die Kamera.',
|
|
buttonPositive: 'Erlauben',
|
|
buttonNegative: 'Ablehnen',
|
|
},
|
|
);
|
|
return granted === PermissionsAndroid.RESULTS.GRANTED;
|
|
} catch {
|
|
return false;
|
|
}
|
|
};
|
|
|
|
/** Foto mit Kamera aufnehmen */
|
|
const takePhoto = async () => {
|
|
const hasPermission = await requestCameraPermission();
|
|
if (!hasPermission) return;
|
|
|
|
launchCamera(IMAGE_OPTIONS, (response) => {
|
|
if (response.didCancel) {
|
|
// Benutzer hat abgebrochen
|
|
return;
|
|
}
|
|
if (response.errorCode) {
|
|
console.error('[CameraUpload] Kamera-Fehler:', response.errorMessage);
|
|
return;
|
|
}
|
|
setPreview(response);
|
|
});
|
|
};
|
|
|
|
/** Foto aus Galerie auswaehlen */
|
|
const pickFromGallery = async () => {
|
|
launchImageLibrary(IMAGE_OPTIONS, (response) => {
|
|
if (response.didCancel) return;
|
|
if (response.errorCode) {
|
|
console.error('[CameraUpload] Galerie-Fehler:', response.errorMessage);
|
|
return;
|
|
}
|
|
setPreview(response);
|
|
});
|
|
};
|
|
|
|
/** Ausgewaehltes Foto senden */
|
|
const sendPhoto = () => {
|
|
const asset = preview?.assets?.[0];
|
|
if (!asset) return;
|
|
|
|
setLoading(true);
|
|
|
|
const photoData: PhotoData = {
|
|
base64: asset.base64 || '',
|
|
width: asset.width || 0,
|
|
height: asset.height || 0,
|
|
fileName: asset.fileName || `foto_${Date.now()}.jpg`,
|
|
type: asset.type || 'image/jpeg',
|
|
uri: asset.uri || '',
|
|
};
|
|
|
|
onPhotoSelected(photoData);
|
|
setLoading(false);
|
|
};
|
|
|
|
const previewUri = preview?.assets?.[0]?.uri;
|
|
|
|
return (
|
|
<View style={styles.container}>
|
|
{!preview ? (
|
|
// Auswahl: Kamera oder Galerie
|
|
<View style={styles.optionsContainer}>
|
|
<TouchableOpacity
|
|
style={styles.optionButton}
|
|
onPress={takePhoto}
|
|
activeOpacity={0.7}
|
|
>
|
|
<Text style={styles.optionIcon}>{'\uD83D\uDCF7'}</Text>
|
|
<Text style={styles.optionText}>Foto aufnehmen</Text>
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity
|
|
style={styles.optionButton}
|
|
onPress={pickFromGallery}
|
|
activeOpacity={0.7}
|
|
>
|
|
<Text style={styles.optionIcon}>{'\uD83D\uDDBC\uFE0F'}</Text>
|
|
<Text style={styles.optionText}>Aus Galerie</Text>
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity style={styles.cancelLink} onPress={onCancel}>
|
|
<Text style={styles.cancelLinkText}>Abbrechen</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
) : (
|
|
// Vorschau
|
|
<View style={styles.previewContainer}>
|
|
{previewUri && (
|
|
<Image source={{ uri: previewUri }} style={styles.imagePreview} />
|
|
)}
|
|
|
|
<View style={styles.buttonRow}>
|
|
<TouchableOpacity
|
|
style={styles.retakeButton}
|
|
onPress={() => setPreview(null)}
|
|
>
|
|
<Text style={styles.retakeText}>Neu</Text>
|
|
</TouchableOpacity>
|
|
|
|
<TouchableOpacity
|
|
style={styles.sendButton}
|
|
onPress={sendPhoto}
|
|
disabled={loading}
|
|
>
|
|
{loading ? (
|
|
<ActivityIndicator color="#FFFFFF" size="small" />
|
|
) : (
|
|
<Text style={styles.sendText}>Senden</Text>
|
|
)}
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
)}
|
|
</View>
|
|
);
|
|
};
|
|
|
|
// --- Styles ---
|
|
|
|
const styles = StyleSheet.create({
|
|
container: {
|
|
backgroundColor: '#1A1A2E',
|
|
borderRadius: 16,
|
|
padding: 20,
|
|
margin: 12,
|
|
},
|
|
optionsContainer: {
|
|
alignItems: 'center',
|
|
gap: 12,
|
|
},
|
|
optionButton: {
|
|
flexDirection: 'row',
|
|
alignItems: 'center',
|
|
backgroundColor: '#2A2A3E',
|
|
borderRadius: 12,
|
|
padding: 16,
|
|
width: '100%',
|
|
},
|
|
optionIcon: {
|
|
fontSize: 28,
|
|
marginRight: 14,
|
|
},
|
|
optionText: {
|
|
color: '#FFFFFF',
|
|
fontSize: 16,
|
|
fontWeight: '500',
|
|
},
|
|
cancelLink: {
|
|
marginTop: 8,
|
|
padding: 8,
|
|
},
|
|
cancelLinkText: {
|
|
color: '#666680',
|
|
fontSize: 14,
|
|
},
|
|
previewContainer: {
|
|
alignItems: 'center',
|
|
},
|
|
imagePreview: {
|
|
width: '100%',
|
|
height: 280,
|
|
borderRadius: 12,
|
|
resizeMode: 'contain',
|
|
marginBottom: 16,
|
|
},
|
|
buttonRow: {
|
|
flexDirection: 'row',
|
|
gap: 12,
|
|
},
|
|
retakeButton: {
|
|
paddingHorizontal: 24,
|
|
paddingVertical: 12,
|
|
borderRadius: 8,
|
|
backgroundColor: '#2A2A3E',
|
|
},
|
|
retakeText: {
|
|
color: '#8888AA',
|
|
fontSize: 14,
|
|
fontWeight: '600',
|
|
},
|
|
sendButton: {
|
|
paddingHorizontal: 32,
|
|
paddingVertical: 12,
|
|
borderRadius: 8,
|
|
backgroundColor: '#0096FF',
|
|
minWidth: 100,
|
|
alignItems: 'center',
|
|
},
|
|
sendText: {
|
|
color: '#FFFFFF',
|
|
fontSize: 14,
|
|
fontWeight: '700',
|
|
},
|
|
});
|
|
|
|
export default CameraUpload;
|