import React, { useState, useEffect } from 'react'; import { StyleSheet, View, SafeAreaView, StatusBar, ActivityIndicator } from 'react-native'; import { GestureHandlerRootView } from 'react-native-gesture-handler'; import CameraScreen from './src/screens/CameraScreen'; import PreviewScreen from './src/screens/PreviewScreen'; import AdminScreen from './src/screens/AdminScreen'; import { loadSettings, saveSettings, AppSettings } from './src/services/settings'; import { logger, setLogsEnabled } from './src/services/logger'; import * as FileSystem from 'expo-file-system/legacy'; // Set up global error handler const defaultErrorHandler = (global as any).ErrorUtils.getGlobalHandler(); (global as any).ErrorUtils.setGlobalHandler((error: any, isFatal: boolean) => { logger.error(`Unhandled Exception (Fatal: ${isFatal})`, error) .catch(console.error) .finally(() => { // Delay termination until log is written defaultErrorHandler(error, isFatal); }); }); import KioskMode from './modules/kiosk-mode'; type ScreenState = 'home' | 'camera' | 'preview' | 'admin'; export default function App() { const [currentScreen, setCurrentScreen] = useState('home'); const [photoUris, setPhotoUris] = useState([]); const [captureHistory, setCaptureHistory] = useState([]); const [settings, setSettings] = useState(null); // 1. Load configuration settings on app start useEffect(() => { async function initApp() { const savedSettings = await loadSettings(); setSettings(savedSettings); setLogsEnabled(savedSettings.enableLogs); // Auto-start Kiosk mode if it was configured as enabled if (savedSettings.kioskModeEnabled) { try { KioskMode.startKiosk(); } catch (e) { console.warn('Failed to auto-start Kiosk mode:', e); } } } initApp(); }, []); if (!settings) { return ( ); } // 2. Navigation Actions const handleStartBooth = () => { setPhotoUris([]); setCaptureHistory([]); setCurrentScreen('camera'); }; const handlePhotoCaptured = async (uri: string) => { await logger.log(`Photo captured via CameraScreen: ${uri}`); try { setPhotoUris((prev) => { const next = [...prev, uri]; return next.length > 4 ? next.slice(-4) : next; }); setCaptureHistory((prev) => [...prev, 1]); setCurrentScreen('preview'); await logger.log('State updated to preview screen successfully.'); } catch (e) { await logger.error('Error transitioning to preview screen:', e); } }; // Called by CameraScreen when burst mode completes all photos const handleBurstComplete = (uris: string[]) => { setPhotoUris((prev) => { const next = [...prev, ...uris]; const maxKeep = Math.max(4, uris.length); return next.length > maxKeep ? next.slice(-maxKeep) : next; }); setCaptureHistory((prev) => [...prev, uris.length]); setCurrentScreen('preview'); }; const handleRetakeLast = async () => { // Remove photos from the last capture event const lastCaptureCount = captureHistory.length > 0 ? captureHistory[captureHistory.length - 1] : 1; const updatedUris = [...photoUris]; const removedUris = updatedUris.splice(-lastCaptureCount, lastCaptureCount); setPhotoUris(updatedUris); setCaptureHistory((prev) => prev.slice(0, -1)); // Clean up temp files for (const uri of removedUris) { try { await FileSystem.deleteAsync(uri, { idempotent: true }); } catch (e) { console.warn('Failed to delete temp file:', e); } } // Always let user retake setCurrentScreen('camera'); }; const handleAddAnother = () => { setCurrentScreen('camera'); }; const handleReset = async () => { // Clean up temp files for (const uri of photoUris) { try { await FileSystem.deleteAsync(uri, { idempotent: true }); } catch (e) { console.warn('Failed to delete temp file:', e); } } setPhotoUris([]); setCaptureHistory([]); setCurrentScreen('home'); // Go to home when cancelled }; const handleSaveSettings = (updated: AppSettings) => { setSettings(updated); setLogsEnabled(updated.enableLogs); }; const handleOpenAdmin = () => { setCurrentScreen('admin'); }; // 3. Conditional Screen Rendering return ( ); } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#000', }, loadingContainer: { flex: 1, backgroundColor: '#000', justifyContent: 'center', alignItems: 'center', }, });