diff --git a/assets/frames/frame_summer2.png b/assets/frames/frame_summer2.png index 573d8bc6..02e35f75 100644 Binary files a/assets/frames/frame_summer2.png and b/assets/frames/frame_summer2.png differ diff --git a/src/screens/AdminScreen.tsx b/src/screens/AdminScreen.tsx index 7f97efef..d31ce78c 100644 --- a/src/screens/AdminScreen.tsx +++ b/src/screens/AdminScreen.tsx @@ -65,6 +65,7 @@ export default function AdminScreen({ currentSettings, onSave, onClose }: AdminS const [countdown, setCountdown] = useState(String(currentSettings.countdownDuration)); const [printerIp, setPrinterIp] = useState(currentSettings.printerIp); const [password, setPassword] = useState(currentSettings.adminPassword); + const [isTogglingKiosk, setIsTogglingKiosk] = useState(false); const [logs, setLogs] = useState(null); const scrollViewRef = useRef(null); @@ -98,7 +99,9 @@ export default function AdminScreen({ currentSettings, onSave, onClose }: AdminS return () => clearInterval(interval); }, []); - const handleKioskToggle = (enable: boolean) => { + const handleKioskToggle = async (enable: boolean) => { + if (isTogglingKiosk) return; + setIsTogglingKiosk(true); try { if (enable) { const success = KioskMode.startKiosk(); @@ -124,6 +127,8 @@ export default function AdminScreen({ currentSettings, onSave, onClose }: AdminS } } catch (e: any) { Alert.alert('Nativer Fehler', e.message || 'Fehler im Kiosk-Modul'); + } finally { + setIsTogglingKiosk(false); } }; @@ -298,6 +303,7 @@ export default function AdminScreen({ currentSettings, onSave, onClose }: AdminS placeholder="Standard: 1234" placeholderTextColor={THEME.colors.textMuted} secureTextEntry + maxLength={20} value={password} onChangeText={setPassword} keyboardType="number-pad" diff --git a/src/screens/CameraScreen.tsx b/src/screens/CameraScreen.tsx index 778c40f4..37b575ce 100644 --- a/src/screens/CameraScreen.tsx +++ b/src/screens/CameraScreen.tsx @@ -129,10 +129,10 @@ export default function CameraScreen({ // 1. Request built-in camera permission on mount (just in case we fallback) useEffect(() => { - if (!permission || !permission.granted) { + if (permission && !permission.granted && permission.canAskAgain) { requestPermission(); } - }, [permission]); + }, []); // 2. Check if a USB camera is connected. useEffect(() => { @@ -251,7 +251,7 @@ export default function CameraScreen({ return () => { if (countdownTimerRef.current) clearTimeout(countdownTimerRef.current); }; - }, [countdownDuration, isIdle, pictureSize, isUsbConnected]); + }, [countdownDuration, isIdle, pictureSize, isUsbConnected, burstCount, burstIntervalSec]); const startCountdown = () => { isCancelledRef.current = false; diff --git a/src/screens/PreviewScreen.tsx b/src/screens/PreviewScreen.tsx index 5d826a7b..496f7272 100644 --- a/src/screens/PreviewScreen.tsx +++ b/src/screens/PreviewScreen.tsx @@ -217,8 +217,9 @@ export default function PreviewScreen({ } }; - // Triggers the view capture and the print job + // ── Actions ── const handlePrint = async () => { + if (idleTimerRef.current) clearTimeout(idleTimerRef.current); if (!mountedRef.current) return; if (isProcessing) return; setIsProcessing(true); @@ -253,6 +254,7 @@ export default function PreviewScreen({ // Saves to gallery without printing const handleSaveOnly = async () => { + if (idleTimerRef.current) clearTimeout(idleTimerRef.current); if (!mountedRef.current) return; if (isProcessing) return; setIsProcessing(true); @@ -281,6 +283,7 @@ export default function PreviewScreen({ // Auto-save and exit const handleExit = useCallback(async () => { + if (idleTimerRef.current) clearTimeout(idleTimerRef.current); if (!mountedRef.current) return; if (isProcessing) return; const state = stateRef.current; @@ -326,6 +329,7 @@ export default function PreviewScreen({ }, [resetIdleTimer]); const handleRetakeClick = async () => { + if (idleTimerRef.current) clearTimeout(idleTimerRef.current); if (!mountedRef.current) return; if (isProcessing) return; if (stickers.length > 0 || layout !== 'single' || activeFrame || dateOverlay !== 'off') { diff --git a/src/services/logger.ts b/src/services/logger.ts index 1a4a73ad..20ea34d2 100644 --- a/src/services/logger.ts +++ b/src/services/logger.ts @@ -3,15 +3,23 @@ import * as FileSystem from 'expo-file-system/legacy'; const logFileUri = FileSystem.documentDirectory + 'app_logs.txt'; // Queue to serialize file operations and prevent race conditions -let writeQueue = Promise.resolve(); +const logQueue: string[] = []; +let isWriting = false; -const enqueueWrite = (logLine: string) => { - writeQueue = writeQueue.then(async () => { +const processLogQueue = async () => { + if (isWriting) return; + isWriting = true; + + while (logQueue.length > 0) { + const logLine = logQueue.shift(); + if (!logLine) continue; + try { - const fileInfo = await FileSystem.getInfoAsync(logFileUri); - if (!fileInfo.exists) { - await FileSystem.writeAsStringAsync(logFileUri, logLine, { encoding: FileSystem.EncodingType.UTF8 }); - } else { + if (logFileUri) { + const fileInfo = await FileSystem.getInfoAsync(logFileUri); + if (!fileInfo.exists) { + await FileSystem.writeAsStringAsync(logFileUri, '', { encoding: FileSystem.EncodingType.UTF8 }); + } const current = await FileSystem.readAsStringAsync(logFileUri, { encoding: FileSystem.EncodingType.UTF8 }); let newContent = current + logLine; if (newContent.length > 100000) { @@ -21,12 +29,15 @@ const enqueueWrite = (logLine: string) => { } } catch (e) { console.warn('Failed to write to log file:', e); - // We catch here so the queue is not broken for subsequent logs } - }).catch((e) => { - console.error('Queue error:', e); - }); - return writeQueue; + } + + isWriting = false; +}; + +const enqueueWrite = async (logLine: string) => { + logQueue.push(logLine); + processLogQueue(); // Intentionally unawaited to run in background }; export const logger = { diff --git a/src/services/settings.ts b/src/services/settings.ts index f1951e34..ec264c30 100644 --- a/src/services/settings.ts +++ b/src/services/settings.ts @@ -61,7 +61,7 @@ export async function loadSettings(): Promise { export async function saveSettings(settings: AppSettings): Promise { try { const content = JSON.stringify(settings, null, 2); - await FileSystem.writeAsStringAsync(settingsFile.uri, content); + await settingsFile.write(content); console.log('Settings saved successfully:', content); } catch (e) { console.error('Failed to save settings:', e);