Files
Schnappix/App.tsx
T
2026-05-31 14:44:48 +02:00

171 lines
5.3 KiB
TypeScript

import React, { useState, useEffect } from 'react';
import { StyleSheet, View, SafeAreaView, StatusBar } 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 } from './src/services/logger';
// Set up global error handler
const defaultErrorHandler = (global as any).ErrorUtils.getGlobalHandler();
(global as any).ErrorUtils.setGlobalHandler(async (error: any, isFatal: boolean) => {
await logger.error(`Unhandled Exception (Fatal: ${isFatal})`, error);
defaultErrorHandler(error, isFatal);
});
import KioskMode from './modules/kiosk-mode';
type ScreenState = 'home' | 'camera' | 'preview' | 'admin';
export default function App() {
const [currentScreen, setCurrentScreen] = useState<ScreenState>('home');
const [photoUris, setPhotoUris] = useState<string[]>([]);
const [settings, setSettings] = useState<AppSettings | null>(null);
// 1. Load configuration settings on app start
useEffect(() => {
async function initApp() {
const savedSettings = await loadSettings();
setSettings(savedSettings);
// 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 <View style={styles.loadingContainer} />;
}
// 2. Navigation Actions
const handleStartBooth = () => {
setPhotoUris([]);
setCurrentScreen('camera');
};
const handlePhotoCaptured = async (uri: string) => {
await logger.log(`Photo captured via CameraScreen: ${uri}`);
try {
setPhotoUris((prev) => [...prev, uri]);
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) => [...prev, ...uris]);
setCurrentScreen('preview');
};
const handleRetakeLast = () => {
// Remove last captured photo
const updatedUris = [...photoUris];
updatedUris.pop();
setPhotoUris(updatedUris);
// Always let user retake
setCurrentScreen('camera');
};
const handleAddAnother = () => {
setCurrentScreen('camera');
};
const handleReset = () => {
setPhotoUris([]);
setCurrentScreen('home');
};
const handleSaveSettings = (updated: AppSettings) => {
setSettings(updated);
};
const handleOpenAdmin = () => {
setCurrentScreen('admin');
};
// 3. Conditional Screen Rendering
return (
<GestureHandlerRootView style={styles.container}>
<SafeAreaView style={styles.container}>
<StatusBar hidden={true} />
{/*
Keep CameraScreen ALWAYS mounted in the background to prevent
Android expo-camera hardware crashes when unmounting right after capture
*/}
<View style={StyleSheet.absoluteFill}>
<CameraScreen
countdownDuration={settings.countdownDuration}
onPhotoCaptured={handlePhotoCaptured}
onBurstComplete={handleBurstComplete}
onCancel={handleReset}
isIdle={currentScreen === 'home'}
onStartBooth={handleStartBooth}
onNavigateToAdmin={handleOpenAdmin}
adminPassword={settings.adminPassword}
welcomeText={settings.welcomeText}
burstCount={settings.burstCount}
burstIntervalSec={settings.burstIntervalSec}
frameMode={settings.frameMode}
selectedFrameId={settings.selectedFrameId}
/>
</View>
{/* Overlay PreviewScreen if active */}
{currentScreen === 'preview' && (
<View style={StyleSheet.absoluteFill}>
<PreviewScreen
photoUris={photoUris}
printerIp={settings.printerIp}
onRetakeLast={handleRetakeLast}
onAddAnother={handleAddAnother}
onReset={handleReset}
frameMode={settings.frameMode}
initialFrameId={settings.selectedFrameId}
availableFrameIds={settings.availableFrameIds}
dateOverlay={settings.dateOverlay}
dateOverlayPosition={settings.dateOverlayPosition}
eventText={settings.eventText}
/>
</View>
)}
{/* Overlay AdminScreen if active */}
{currentScreen === 'admin' && (
<View style={StyleSheet.absoluteFill}>
<AdminScreen
currentSettings={settings}
onSave={handleSaveSettings}
onClose={() => setCurrentScreen('home')}
/>
</View>
)}
</SafeAreaView>
</GestureHandlerRootView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#050510',
},
loadingContainer: {
flex: 1,
backgroundColor: '#050510',
justifyContent: 'center',
alignItems: 'center',
},
});