chore: update app configurations and screens

This commit is contained in:
2026-05-31 13:48:02 +02:00
parent 9296a9bc8b
commit d3c0ef2420
5 changed files with 90 additions and 98 deletions
+2 -6
View File
@@ -59,12 +59,8 @@ export default function App() {
updatedUris.pop(); updatedUris.pop();
setPhotoUris(updatedUris); setPhotoUris(updatedUris);
// If we have other photos, let user retake from current collage length // Always let user retake
if (updatedUris.length > 0) { setCurrentScreen('camera');
setCurrentScreen('camera');
} else {
setCurrentScreen('home');
}
}; };
const handleAddAnother = () => { const handleAddAnother = () => {
-4
View File
@@ -12,10 +12,6 @@
}, },
"android": { "android": {
"package": "com.schnappix", "package": "com.schnappix",
"adaptiveIcon": {
"backgroundColor": "#050510",
"foregroundImage": "./Icon.png"
},
"permissions": [ "permissions": [
"android.permission.CAMERA", "android.permission.CAMERA",
"android.permission.RECORD_AUDIO", "android.permission.RECORD_AUDIO",
+40 -80
View File
@@ -272,6 +272,15 @@ export default function CameraScreen({
} }
}; };
const handleCancel = useCallback(() => {
if (burstTimerRef.current) {
clearTimeout(burstTimerRef.current);
}
setHasStarted(false);
setIsCapturing(false);
onCancel();
}, [onCancel]);
const handleSettingsTap = () => { const handleSettingsTap = () => {
setEnteredPassword(''); setEnteredPassword('');
setErrorText(''); setErrorText('');
@@ -327,24 +336,13 @@ export default function CameraScreen({
<FontAwesomeIcon icon={faUserGear} size={22} color={THEME.colors.accent} /> <FontAwesomeIcon icon={faUserGear} size={22} color={THEME.colors.accent} />
</TouchableOpacity> </TouchableOpacity>
<View style={styles.welcomeBox}> {/* Header Event Text */}
<Text style={styles.logo}>SCHNAPPIX</Text> <Text style={styles.headerTitle}>{welcomeText}</Text>
<Text style={styles.welcomeTitle}>{welcomeText}</Text>
<View style={styles.divider} /> {/* Footer Instructions */}
<Text style={styles.footerInstructions}>
<LinearGradient ZUM STARTEN TIPPEN Fotos machen Collage Drucken
colors={THEME.gradient.primary} </Text>
start={{ x: 0, y: 0.5 }}
end={{ x: 1, y: 0.5 }}
style={styles.startButton}
>
<FontAwesomeIcon icon={faCameraRetro} size={20} color={THEME.colors.text} style={{ marginRight: 10 }} />
<Text style={styles.startButtonText}>ZUM STARTEN ÜBERALL TIPPEN</Text>
</LinearGradient>
<Text style={styles.welcomeFooter}>Fotos machen Collage erstellen Sofort drucken</Text>
</View>
{/* Password Prompt Modal */} {/* Password Prompt Modal */}
<Modal <Modal
@@ -459,7 +457,7 @@ export default function CameraScreen({
{/* Cancel Button */} {/* Cancel Button */}
{!isCapturing && !isBurstWaiting && ( {!isCapturing && !isBurstWaiting && (
<TouchableOpacity style={styles.backButton} onPress={onCancel}> <TouchableOpacity style={styles.backButton} onPress={handleCancel}>
<FontAwesomeIcon icon={faXmark} size={16} color={THEME.colors.text} style={{ marginRight: 6 }} /> <FontAwesomeIcon icon={faXmark} size={16} color={THEME.colors.text} style={{ marginRight: 6 }} />
<Text style={styles.backButtonText}>ABBRECHEN</Text> <Text style={styles.backButtonText}>ABBRECHEN</Text>
</TouchableOpacity> </TouchableOpacity>
@@ -530,10 +528,12 @@ const styles = StyleSheet.create({
zIndex: 9999, zIndex: 9999,
}, },
idleOverlayContainer: { idleOverlayContainer: {
flex: 1, position: 'absolute',
justifyContent: 'center', top: 0,
alignItems: 'center', left: 0,
backgroundColor: 'rgba(5, 5, 16, 0.25)', right: 0,
bottom: 0,
backgroundColor: 'transparent',
}, },
captureOverlayContainer: { captureOverlayContainer: {
flex: 1, flex: 1,
@@ -554,73 +554,31 @@ const styles = StyleSheet.create({
alignItems: 'center', alignItems: 'center',
zIndex: 999, zIndex: 999,
}, },
welcomeBox: { headerTitle: {
alignItems: 'center', position: 'absolute',
padding: THEME.spacing.xl, top: 40,
borderRadius: THEME.borderRadius.lg, width: '100%',
backgroundColor: 'rgba(5, 5, 16, 0.85)', textAlign: 'center',
borderWidth: 1, fontSize: 48,
borderColor: THEME.colors.gradientBorder,
width: '60%',
shadowColor: THEME.colors.gradientGlow,
shadowOffset: { width: 0, height: 0 },
shadowOpacity: 0.4,
shadowRadius: 30,
elevation: 10,
},
logo: {
fontSize: 54,
fontWeight: '900', fontWeight: '900',
color: THEME.colors.accent, color: THEME.colors.accent,
letterSpacing: 8, letterSpacing: 6,
marginBottom: THEME.spacing.sm,
textShadowColor: THEME.colors.cyanGlow, textShadowColor: THEME.colors.cyanGlow,
textShadowOffset: { width: 0, height: 0 }, textShadowOffset: { width: 0, height: 0 },
textShadowRadius: 25, textShadowRadius: 25,
}, },
welcomeTitle: { footerInstructions: {
fontSize: 22, position: 'absolute',
color: THEME.colors.text, bottom: 40,
width: '100%',
textAlign: 'center', textAlign: 'center',
marginBottom: THEME.spacing.lg, fontSize: 20,
letterSpacing: 1,
},
divider: {
width: 60,
height: 3,
backgroundColor: THEME.colors.accent,
marginBottom: THEME.spacing.xl,
borderRadius: THEME.borderRadius.sm,
shadowColor: THEME.colors.accent,
shadowOffset: { width: 0, height: 0 },
shadowOpacity: 0.8,
shadowRadius: 8,
},
startButton: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingVertical: THEME.spacing.md,
paddingHorizontal: THEME.spacing.xl,
borderRadius: THEME.borderRadius.round,
marginBottom: THEME.spacing.xl,
overflow: 'hidden',
shadowColor: THEME.colors.gradientGlow,
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.6,
shadowRadius: 14,
elevation: 8,
},
startButtonText: {
color: THEME.colors.text,
fontSize: 18,
fontWeight: 'bold', fontWeight: 'bold',
color: THEME.colors.text,
letterSpacing: 2, letterSpacing: 2,
}, textShadowColor: 'rgba(0,0,0,0.9)',
welcomeFooter: { textShadowOffset: { width: 0, height: 2 },
fontSize: 14, textShadowRadius: 15,
color: THEME.colors.textMuted,
letterSpacing: 1,
}, },
hudContainer: { hudContainer: {
position: 'absolute', position: 'absolute',
@@ -838,6 +796,8 @@ const styles = StyleSheet.create({
}, },
confirmGradient: { confirmGradient: {
flex: 1, flex: 1,
width: '100%',
height: '100%',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
}, },
+42 -4
View File
@@ -70,6 +70,9 @@ export default function PreviewScreen({
const [selectedStickerId, setSelectedStickerId] = useState<string | null>(null); const [selectedStickerId, setSelectedStickerId] = useState<string | null>(null);
const [showStickerTray, setShowStickerTray] = useState<boolean>(false); const [showStickerTray, setShowStickerTray] = useState<boolean>(false);
// Idle Timer state
const idleTimerRef = useRef<any>(null);
// Frame state // Frame state
const [selectedFrameId, setSelectedFrameId] = useState<string | null>( const [selectedFrameId, setSelectedFrameId] = useState<string | null>(
frameMode === 'always' ? initialFrameId : null frameMode === 'always' ? initialFrameId : null
@@ -227,7 +230,7 @@ export default function PreviewScreen({
}; };
// Auto-save and exit // Auto-save and exit
const handleExit = async () => { const handleExit = useCallback(async () => {
if (stickers.length === 0 && layout === 'single' && !activeFrame && dateOverlay === 'off') { if (stickers.length === 0 && layout === 'single' && !activeFrame && dateOverlay === 'off') {
onReset(); onReset();
return; return;
@@ -248,6 +251,36 @@ export default function PreviewScreen({
setIsProcessing(false); setIsProcessing(false);
onReset(); onReset();
} }
}, [stickers, layout, activeFrame, dateOverlay, onReset]);
// Idle timer logic
const resetIdleTimer = useCallback(() => {
if (idleTimerRef.current) clearTimeout(idleTimerRef.current);
idleTimerRef.current = setTimeout(() => {
handleExit();
}, 60000);
}, [handleExit]);
useEffect(() => {
resetIdleTimer();
return () => {
if (idleTimerRef.current) clearTimeout(idleTimerRef.current);
};
}, [resetIdleTimer]);
const handleRetakeClick = async () => {
if (stickers.length > 0 || layout !== 'single' || activeFrame || dateOverlay !== 'off') {
setIsProcessing(true);
setStatusMessage('Wird vor dem Wiederholen gespeichert...');
try {
const capturedUri = await captureComposite();
await saveToGallery(capturedUri);
} catch (error) {
console.error('Auto-saving before retake failed:', error);
}
setIsProcessing(false);
}
onRetakeLast();
}; };
// ── Render the collage ── // ── Render the collage ──
@@ -303,7 +336,12 @@ export default function PreviewScreen({
}; };
return ( return (
<Animated.View entering={FadeIn.duration(400)} exiting={FadeOut.duration(300)} style={styles.container}> <Animated.View
entering={FadeIn.duration(400)}
exiting={FadeOut.duration(300)}
style={styles.container}
onTouchStart={resetIdleTimer}
>
{/* Left panel: Preview Canvas */} {/* Left panel: Preview Canvas */}
<TouchableOpacity <TouchableOpacity
style={styles.previewPanel} style={styles.previewPanel}
@@ -482,7 +520,7 @@ export default function PreviewScreen({
)} )}
<View style={styles.rowActions}> <View style={styles.rowActions}>
<TouchableOpacity style={[styles.smallBtn, styles.retakeBtn]} onPress={onRetakeLast}> <TouchableOpacity style={[styles.smallBtn, styles.retakeBtn]} onPress={handleRetakeClick}>
<FontAwesomeIcon icon={faRotateLeft} size={14} color={THEME.colors.error} style={{ marginRight: 6 }} /> <FontAwesomeIcon icon={faRotateLeft} size={14} color={THEME.colors.error} style={{ marginRight: 6 }} />
<Text style={[styles.smallBtnText, { color: THEME.colors.error }]}>Wiederholen</Text> <Text style={[styles.smallBtnText, { color: THEME.colors.error }]}>Wiederholen</Text>
</TouchableOpacity> </TouchableOpacity>
@@ -686,7 +724,7 @@ const styles = StyleSheet.create({
width: 480, width: 480,
height: 320, height: 320,
backgroundColor: '#fff', backgroundColor: '#fff',
padding: 10, padding: 0,
shadowColor: THEME.colors.gradientGlow, shadowColor: THEME.colors.gradientGlow,
shadowOffset: { width: 0, height: 0 }, shadowOffset: { width: 0, height: 0 },
shadowOpacity: 0.35, shadowOpacity: 0.35,
+5 -3
View File
@@ -1,4 +1,6 @@
import { File, Paths } from 'expo-file-system'; import { File, Paths } from 'expo-file-system';
import * as FileSystem from 'expo-file-system';
import { FRAMES } from '../data/frames';
export interface AppSettings { export interface AppSettings {
countdownDuration: number; // in seconds countdownDuration: number; // in seconds
@@ -28,9 +30,9 @@ const DEFAULT_SETTINGS: AppSettings = {
printerIp: '192.168.1.100', printerIp: '192.168.1.100',
adminPassword: '1234', adminPassword: '1234',
kioskModeEnabled: false, kioskModeEnabled: false,
frameMode: 'off', frameMode: 'available',
selectedFrameId: null, selectedFrameId: null,
availableFrameIds: [], availableFrameIds: FRAMES.map(f => f.id),
dateOverlay: 'off', dateOverlay: 'off',
dateOverlayPosition: 'bottom-right', dateOverlayPosition: 'bottom-right',
eventText: '', eventText: '',
@@ -61,7 +63,7 @@ export async function loadSettings(): Promise<AppSettings> {
export async function saveSettings(settings: AppSettings): Promise<void> { export async function saveSettings(settings: AppSettings): Promise<void> {
try { try {
const content = JSON.stringify(settings, null, 2); const content = JSON.stringify(settings, null, 2);
settingsFile.write(content); await FileSystem.writeAsStringAsync(settingsFile.uri, content);
console.log('Settings saved successfully:', content); console.log('Settings saved successfully:', content);
} catch (e) { } catch (e) {
console.error('Failed to save settings:', e); console.error('Failed to save settings:', e);