feat: improve preview screen workflow, persist edits, remove retake button

This commit is contained in:
2026-07-10 11:30:10 +02:00
parent e8925b2b62
commit 0f7d0def17
2 changed files with 47 additions and 52 deletions
+19 -20
View File
@@ -5,6 +5,7 @@ import CameraScreen from './src/screens/CameraScreen';
import PreviewScreen from './src/screens/PreviewScreen'; import PreviewScreen from './src/screens/PreviewScreen';
import AdminScreen from './src/screens/AdminScreen'; import AdminScreen from './src/screens/AdminScreen';
import { loadSettings, saveSettings, AppSettings } from './src/services/settings'; import { loadSettings, saveSettings, AppSettings } from './src/services/settings';
import { StickerData } from './src/components/DraggableSticker';
import { logger, setLogsEnabled } from './src/services/logger'; import { logger, setLogsEnabled } from './src/services/logger';
import * as FileSystem from 'expo-file-system/legacy'; import * as FileSystem from 'expo-file-system/legacy';
@@ -30,6 +31,10 @@ export default function App() {
const [showEventModal, setShowEventModal] = useState(false); const [showEventModal, setShowEventModal] = useState(false);
const [tempEventName, setTempEventName] = useState(''); const [tempEventName, setTempEventName] = useState('');
// Persist edits across camera switches for collages
const [stickers, setStickers] = useState<StickerData[]>([]);
const [selectedFrameId, setSelectedFrameId] = useState<string | null>(null);
// 1. Load configuration settings on app start // 1. Load configuration settings on app start
useEffect(() => { useEffect(() => {
async function initApp() { async function initApp() {
@@ -45,6 +50,10 @@ export default function App() {
console.warn('Failed to auto-start Kiosk mode:', e); console.warn('Failed to auto-start Kiosk mode:', e);
} }
} }
if (savedSettings.frameMode === 'always') {
setSelectedFrameId(savedSettings.selectedFrameId);
}
} }
initApp(); initApp();
}, []); }, []);
@@ -67,6 +76,8 @@ export default function App() {
} }
setPhotoUris([]); setPhotoUris([]);
setCaptureHistory([]); setCaptureHistory([]);
setStickers([]);
setSelectedFrameId(settings?.frameMode === 'always' ? settings.selectedFrameId : null);
setCurrentScreen('camera'); setCurrentScreen('camera');
}; };
@@ -81,6 +92,8 @@ export default function App() {
// Resume start // Resume start
setPhotoUris([]); setPhotoUris([]);
setCaptureHistory([]); setCaptureHistory([]);
setStickers([]);
setSelectedFrameId(updated.frameMode === 'always' ? updated.selectedFrameId : null);
setCurrentScreen('camera'); setCurrentScreen('camera');
}; };
@@ -110,26 +123,7 @@ export default function App() {
setCurrentScreen('preview'); 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 = () => { const handleAddAnother = () => {
setCurrentScreen('camera'); setCurrentScreen('camera');
@@ -146,6 +140,8 @@ export default function App() {
} }
setPhotoUris([]); setPhotoUris([]);
setCaptureHistory([]); setCaptureHistory([]);
setStickers([]);
setSelectedFrameId(settings?.frameMode === 'always' ? settings.selectedFrameId : null);
setCurrentScreen('home'); // Go to home when cancelled setCurrentScreen('home'); // Go to home when cancelled
}; };
@@ -199,7 +195,6 @@ export default function App() {
<PreviewScreen <PreviewScreen
photoUris={photoUris} photoUris={photoUris}
printerIp={settings.printerIp} printerIp={settings.printerIp}
onRetakeLast={handleRetakeLast}
onAddAnother={handleAddAnother} onAddAnother={handleAddAnother}
onReset={handleReset} onReset={handleReset}
frameMode={settings.frameMode} frameMode={settings.frameMode}
@@ -208,6 +203,10 @@ export default function App() {
dateOverlay={settings.dateOverlay} dateOverlay={settings.dateOverlay}
dateOverlayTransform={settings.dateOverlayTransform} dateOverlayTransform={settings.dateOverlayTransform}
eventText={settings.eventText} eventText={settings.eventText}
stickers={stickers}
setStickers={setStickers}
selectedFrameId={selectedFrameId}
setSelectedFrameId={setSelectedFrameId}
/> />
</View> </View>
)} )}
+20 -24
View File
@@ -27,7 +27,6 @@ import { FRAMES } from '../data/frames';
interface PreviewScreenProps { interface PreviewScreenProps {
photoUris: string[]; photoUris: string[];
printerIp: string; printerIp: string;
onRetakeLast: () => void;
onAddAnother: () => void; onAddAnother: () => void;
onReset: () => void; onReset: () => void;
// Frame settings // Frame settings
@@ -39,6 +38,11 @@ interface PreviewScreenProps {
dateOverlayTransform: { x: number; y: number; rotation: number; scale: number }; dateOverlayTransform: { x: number; y: number; rotation: number; scale: number };
// Event text // Event text
eventText: string; eventText: string;
// Hoisted state
stickers: StickerData[];
setStickers: React.Dispatch<React.SetStateAction<StickerData[]>>;
selectedFrameId: string | null;
setSelectedFrameId: React.Dispatch<React.SetStateAction<string | null>>;
} }
type CollageLayout = 'single' | 'strip' | 'grid' | 'duo'; type CollageLayout = 'single' | 'strip' | 'grid' | 'duo';
@@ -46,7 +50,6 @@ type CollageLayout = 'single' | 'strip' | 'grid' | 'duo';
export default function PreviewScreen({ export default function PreviewScreen({
photoUris, photoUris,
printerIp, printerIp,
onRetakeLast,
onAddAnother, onAddAnother,
onReset, onReset,
frameMode, frameMode,
@@ -55,6 +58,10 @@ export default function PreviewScreen({
dateOverlay, dateOverlay,
dateOverlayTransform, dateOverlayTransform,
eventText, eventText,
stickers,
setStickers,
selectedFrameId,
setSelectedFrameId,
}: PreviewScreenProps) { }: PreviewScreenProps) {
const [layout, setLayout] = useState<CollageLayout>('single'); const [layout, setLayout] = useState<CollageLayout>('single');
const [isProcessing, setIsProcessing] = useState<boolean>(false); const [isProcessing, setIsProcessing] = useState<boolean>(false);
@@ -107,8 +114,6 @@ export default function PreviewScreen({
// Sticker state
const [stickers, setStickers] = useState<StickerData[]>([]);
const [selectedStickerId, setSelectedStickerId] = useState<string | null>(null); const [selectedStickerId, setSelectedStickerId] = useState<string | null>(null);
const mountedRef = useRef(true); const mountedRef = useRef(true);
@@ -147,10 +152,6 @@ export default function PreviewScreen({
}; };
}, [onReset]); }, [onReset]);
// Frame state
const [selectedFrameId, setSelectedFrameId] = useState<string | null>(
frameMode === 'always' ? initialFrameId : null
);
const viewShotRef = useRef<any>(null); const viewShotRef = useRef<any>(null);
@@ -372,26 +373,26 @@ export default function PreviewScreen({
}; };
}, [resetIdleTimer]); }, [resetIdleTimer]);
const handleRetakeClick = async () => { const handleAddAnotherClick = async () => {
logger.log('Action: Clicked Retake button'); logger.log('Action: Clicked Add Another button');
if (idleTimerRef.current) clearTimeout(idleTimerRef.current); if (idleTimerRef.current) clearTimeout(idleTimerRef.current);
if (!mountedRef.current) return; if (!mountedRef.current) return;
if (isProcessing) return; if (isProcessing) return;
if (stickers.length > 0 || layout !== 'single' || activeFrame || dateOverlay !== 'off') {
// Auto-save the intermediate collage before adding another photo
setIsProcessing(true); setIsProcessing(true);
try { try {
const capturedUri = await captureComposite(currentPhoto); const capturedUri = await captureComposite(currentPhoto);
if (!mountedRef.current) return; if (!mountedRef.current) return;
setStatusMessage('Wird vor dem Wiederholen gespeichert...'); setStatusMessage('Wird zwischengespeichert...');
await saveToGallery(capturedUri, 'Collage', eventText); await saveToGallery(capturedUri, 'Collage', eventText);
if (!mountedRef.current) return;
} catch (e) { } catch (e) {
if (!mountedRef.current) return; if (!mountedRef.current) return;
console.error('Failed to auto-save before retake:', e); console.error('Failed to auto-save before adding another:', e);
} }
setIsProcessing(false); setIsProcessing(false);
}
onRetakeLast(); onAddAnother();
}; };
// ── Render the collage ── // ── Render the collage ──
@@ -652,19 +653,14 @@ export default function PreviewScreen({
{photoUris.length < 4 && ( {photoUris.length < 4 && (
<TouchableOpacity style={[styles.actionBtn, styles.addBtn, isProcessing && styles.btnDisabled, isSmallDevice && { paddingVertical: THEME.spacing.sm, marginBottom: THEME.spacing.sm }]} onPress={onAddAnother} disabled={isProcessing}> <TouchableOpacity style={[styles.actionBtn, styles.addBtn, isProcessing && styles.btnDisabled, isSmallDevice && { paddingVertical: THEME.spacing.sm, marginBottom: THEME.spacing.sm }]} onPress={handleAddAnotherClick} disabled={isProcessing}>
<FontAwesomeIcon icon={faPlus} size={16} color={THEME.colors.text} style={{ marginRight: 8 }} /> <FontAwesomeIcon icon={faPlus} size={16} color={THEME.colors.text} style={{ marginRight: 8 }} />
<Text style={styles.actionBtnText}>Weiteres Foto aufnehmen</Text> <Text style={styles.actionBtnText}>Foto zur Collage hinzufügen</Text>
</TouchableOpacity> </TouchableOpacity>
)} )}
<View style={styles.rowActions}> <View style={styles.rowActions}>
<TouchableOpacity style={[styles.smallBtn, styles.retakeBtn, isProcessing && styles.btnDisabled]} onPress={handleRetakeClick} disabled={isProcessing}> <TouchableOpacity style={[styles.smallBtn, styles.resetBtn, isProcessing && styles.btnDisabled, { width: '100%' }]} onPress={handleExit} disabled={isProcessing}>
<FontAwesomeIcon icon={faRotateLeft} size={14} color={THEME.colors.error} style={{ marginRight: 6 }} />
<Text style={[styles.smallBtnText, { color: THEME.colors.error }]}>Wiederholen</Text>
</TouchableOpacity>
<TouchableOpacity style={[styles.smallBtn, styles.resetBtn, isProcessing && styles.btnDisabled]} onPress={handleExit} disabled={isProcessing}>
<FontAwesomeIcon icon={faRightFromBracket} size={14} color={THEME.colors.textMuted} style={{ marginRight: 6 }} /> <FontAwesomeIcon icon={faRightFromBracket} size={14} color={THEME.colors.textMuted} style={{ marginRight: 6 }} />
<Text style={styles.smallBtnText}>Beenden</Text> <Text style={styles.smallBtnText}>Beenden</Text>
</TouchableOpacity> </TouchableOpacity>