Localize app to German, implement auto-save to gallery, always-on camera stream, fix expo-media-library and expo-file-system deprecation/compile errors

This commit is contained in:
2026-05-25 21:39:15 +02:00
parent 99634bb480
commit d8e397eac1
9 changed files with 474 additions and 135 deletions
+51 -32
View File
@@ -41,36 +41,36 @@ export default function PreviewScreen({
// Triggers the view capture and the print job
const handlePrint = async () => {
setIsProcessing(true);
setStatusMessage('Preparing your photo...');
setStatusMessage('Dein Foto wird vorbereitet...');
try {
let printUri = currentPhoto;
// If we are printing a collage layout, capture the ViewShot container
if (layout !== 'single') {
setStatusMessage('Generating collage...');
setStatusMessage('Collage wird generiert...');
const capturedUri = await viewShotRef.current.capture();
printUri = capturedUri;
}
setStatusMessage('Sending print job to printer...');
setStatusMessage('Druckauftrag wird an Drucker gesendet...');
// 1. Silent Print via IPP
await printImageLocal(printUri, {
ipAddress: printerIp,
jobName: 'Schnappix Photo Booth',
jobName: 'Schnappix Fotobox',
});
setStatusMessage('Saving to tablet library...');
setStatusMessage('Wird in Galerie gespeichert...');
// 2. Save permanently to DCIM/Schnappix
await saveToGallery(printUri);
setStatusMessage('Print successful! Enjoy!');
setStatusMessage('Druck erfolgreich! Viel Spaß! 🎉');
setTimeout(() => {
setIsProcessing(false);
onReset(); // Go back to Home Screen
}, 2000);
} catch (error: any) {
console.error('Printing failed:', error);
alert('Error: ' + error.message);
alert('Fehler: ' + error.message);
setIsProcessing(false);
}
};
@@ -78,7 +78,7 @@ export default function PreviewScreen({
// Saves to gallery without printing
const handleSaveOnly = async () => {
setIsProcessing(true);
setStatusMessage('Generating image...');
setStatusMessage('Bild wird generiert...');
try {
let saveUri = currentPhoto;
@@ -87,21 +87,46 @@ export default function PreviewScreen({
saveUri = capturedUri;
}
setStatusMessage('Saving to tablet library...');
setStatusMessage('Wird in Galerie gespeichert...');
await saveToGallery(saveUri);
setStatusMessage('Saved successfully!');
setStatusMessage('Erfolgreich gespeichert!');
setTimeout(() => {
setIsProcessing(false);
onReset();
}, 1500);
} catch (error: any) {
console.error('Saving failed:', error);
alert('Error: ' + error.message);
alert('Fehler: ' + error.message);
setIsProcessing(false);
}
};
// Auto-save collage and exit
const handleExit = async () => {
// If layout is single, it has already been auto-saved to gallery right when captured
if (layout === 'single') {
onReset();
return;
}
setIsProcessing(true);
setStatusMessage('Collage wird gespeichert...');
try {
const capturedUri = await viewShotRef.current.capture();
await saveToGallery(capturedUri);
setStatusMessage('Erfolgreich gespeichert!');
setTimeout(() => {
setIsProcessing(false);
onReset();
}, 1000);
} catch (error) {
console.error('Auto-saving collage on exit failed:', error);
setIsProcessing(false);
onReset();
}
};
// Render the selected collage inside the capture container
const renderCollageView = () => {
switch (layout) {
@@ -114,8 +139,8 @@ export default function PreviewScreen({
))}
</View>
<View style={styles.collageFooter}>
<Text style={styles.collageFooterText}>SCHNAPPIX PHOTO BOOTH</Text>
<Text style={styles.collageFooterDate}>{new Date().toLocaleDateString()}</Text>
<Text style={styles.collageFooterText}>SCHNAPPIX FOTOBOX</Text>
<Text style={styles.collageFooterDate}>{new Date().toLocaleDateString('de-DE')}</Text>
</View>
</View>
);
@@ -128,7 +153,7 @@ export default function PreviewScreen({
))}
</View>
<View style={styles.collageFooter}>
<Text style={styles.collageFooterText}>SCHNAPPIX PHOTO BOOTH</Text>
<Text style={styles.collageFooterText}>SCHNAPPIX FOTOBOX</Text>
</View>
</View>
);
@@ -141,7 +166,7 @@ export default function PreviewScreen({
))}
</View>
<View style={styles.collageFooter}>
<Text style={styles.collageFooterText}>SCHNAPPIX PHOTO BOOTH</Text>
<Text style={styles.collageFooterText}>SCHNAPPIX FOTOBOX</Text>
</View>
</View>
);
@@ -174,7 +199,7 @@ export default function PreviewScreen({
{/* Right panel: Controls */}
<View style={styles.controlPanel}>
<Text style={styles.header}>CHOOSE YOUR STYLE</Text>
<Text style={styles.header}>WÄHLE DEINEN STIL</Text>
{/* Layout Selection */}
<View style={styles.layoutSelector}>
@@ -182,7 +207,7 @@ export default function PreviewScreen({
style={[styles.layoutBtn, layout === 'single' && styles.layoutBtnActive]}
onPress={() => setLayout('single')}
>
<Text style={styles.layoutBtnText}>Single</Text>
<Text style={styles.layoutBtnText}>Einzelbild</Text>
</TouchableOpacity>
<TouchableOpacity
@@ -206,7 +231,7 @@ export default function PreviewScreen({
disabled={photoUris.length < 3}
onPress={() => setLayout('strip')}
>
<Text style={styles.layoutBtnText}>Strip (3)</Text>
<Text style={styles.layoutBtnText}>Streifen (3)</Text>
</TouchableOpacity>
<TouchableOpacity
@@ -218,37 +243,37 @@ export default function PreviewScreen({
disabled={photoUris.length < 4}
onPress={() => setLayout('grid')}
>
<Text style={styles.layoutBtnText}>Grid (4)</Text>
<Text style={styles.layoutBtnText}>Raster (4)</Text>
</TouchableOpacity>
</View>
<Text style={styles.photosCountText}>
Photos Captured: {photoUris.length} / 4
Aufgenommene Fotos: {photoUris.length} / 4
</Text>
{/* Action Buttons */}
<View style={styles.actions}>
<TouchableOpacity style={[styles.actionBtn, styles.printBtn]} onPress={handlePrint}>
<Text style={styles.actionBtnText}>PRINT NOW 🖨</Text>
<Text style={styles.actionBtnText}>JETZT DRUCKEN 🖨</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.actionBtn, styles.saveBtn]} onPress={handleSaveOnly}>
<Text style={styles.actionBtnText}>Save to Tablet Only 💾</Text>
<Text style={styles.actionBtnText}>Nur auf Tablet speichern 💾</Text>
</TouchableOpacity>
{photoUris.length < 4 && (
<TouchableOpacity style={[styles.actionBtn, styles.addBtn]} onPress={onAddAnother}>
<Text style={styles.actionBtnText}>+ Add Another Photo</Text>
<Text style={styles.actionBtnText}>+ Weiteres Foto aufnehmen</Text>
</TouchableOpacity>
)}
<View style={styles.rowActions}>
<TouchableOpacity style={[styles.smallBtn, styles.retakeBtn]} onPress={onRetakeLast}>
<Text style={styles.smallBtnText}>Retake Last</Text>
<Text style={styles.smallBtnText}>Letztes wiederholen</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.smallBtn, styles.resetBtn]} onPress={onReset}>
<Text style={styles.smallBtnText}>Exit</Text>
<TouchableOpacity style={[styles.smallBtn, styles.resetBtn]} onPress={handleExit}>
<Text style={styles.smallBtnText}>Beenden</Text>
</TouchableOpacity>
</View>
</View>
@@ -281,7 +306,6 @@ const styles = StyleSheet.create({
backgroundColor: '#050507',
},
viewShotContainer: {
// 3:2 ratio aspect layout matching Canon Selphy postcard paper
width: 320,
height: 480,
justifyContent: 'center',
@@ -389,7 +413,6 @@ const styles = StyleSheet.create({
fontSize: 14,
fontWeight: '600',
},
// Collage Canvas Layouts (3:2 ratio aspect - width 320, height 480)
singleCanvas: {
width: 320,
height: 480,
@@ -433,7 +456,6 @@ const styles = StyleSheet.create({
fontSize: 8,
color: '#666666',
},
// Strip (3 images vertical)
stripCanvas: {},
stripContent: {
flex: 1,
@@ -446,7 +468,6 @@ const styles = StyleSheet.create({
resizeMode: 'cover',
borderRadius: 2,
},
// Grid (4 images 2x2)
gridCanvas: {},
gridContainer: {
flex: 1,
@@ -462,7 +483,6 @@ const styles = StyleSheet.create({
resizeMode: 'cover',
borderRadius: 2,
},
// Duo (2 images side by side or stacked)
duoCanvas: {},
duoContainer: {
flex: 1,
@@ -475,7 +495,6 @@ const styles = StyleSheet.create({
resizeMode: 'cover',
borderRadius: 2,
},
// Loading overlay
loadingOverlay: {
position: 'absolute',
top: 0,