chore: update app configurations and screens
This commit is contained in:
@@ -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 = () => {
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
@@ -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',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in New Issue
Block a user