Fix 8 critical/high resilience issues across camera, preview, logger, settings, and admin

This commit is contained in:
2026-06-01 10:14:29 +02:00
parent e069fef170
commit 2ddd74468e
6 changed files with 39 additions and 18 deletions
+7 -1
View File
@@ -65,6 +65,7 @@ export default function AdminScreen({ currentSettings, onSave, onClose }: AdminS
const [countdown, setCountdown] = useState<string>(String(currentSettings.countdownDuration));
const [printerIp, setPrinterIp] = useState<string>(currentSettings.printerIp);
const [password, setPassword] = useState<string>(currentSettings.adminPassword);
const [isTogglingKiosk, setIsTogglingKiosk] = useState<boolean>(false);
const [logs, setLogs] = useState<string | null>(null);
const scrollViewRef = useRef<ScrollView>(null);
@@ -98,7 +99,9 @@ export default function AdminScreen({ currentSettings, onSave, onClose }: AdminS
return () => clearInterval(interval);
}, []);
const handleKioskToggle = (enable: boolean) => {
const handleKioskToggle = async (enable: boolean) => {
if (isTogglingKiosk) return;
setIsTogglingKiosk(true);
try {
if (enable) {
const success = KioskMode.startKiosk();
@@ -124,6 +127,8 @@ export default function AdminScreen({ currentSettings, onSave, onClose }: AdminS
}
} catch (e: any) {
Alert.alert('Nativer Fehler', e.message || 'Fehler im Kiosk-Modul');
} finally {
setIsTogglingKiosk(false);
}
};
@@ -298,6 +303,7 @@ export default function AdminScreen({ currentSettings, onSave, onClose }: AdminS
placeholder="Standard: 1234"
placeholderTextColor={THEME.colors.textMuted}
secureTextEntry
maxLength={20}
value={password}
onChangeText={setPassword}
keyboardType="number-pad"
+3 -3
View File
@@ -129,10 +129,10 @@ export default function CameraScreen({
// 1. Request built-in camera permission on mount (just in case we fallback)
useEffect(() => {
if (!permission || !permission.granted) {
if (permission && !permission.granted && permission.canAskAgain) {
requestPermission();
}
}, [permission]);
}, []);
// 2. Check if a USB camera is connected.
useEffect(() => {
@@ -251,7 +251,7 @@ export default function CameraScreen({
return () => {
if (countdownTimerRef.current) clearTimeout(countdownTimerRef.current);
};
}, [countdownDuration, isIdle, pictureSize, isUsbConnected]);
}, [countdownDuration, isIdle, pictureSize, isUsbConnected, burstCount, burstIntervalSec]);
const startCountdown = () => {
isCancelledRef.current = false;
+5 -1
View File
@@ -217,8 +217,9 @@ export default function PreviewScreen({
}
};
// Triggers the view capture and the print job
// ── Actions ──
const handlePrint = async () => {
if (idleTimerRef.current) clearTimeout(idleTimerRef.current);
if (!mountedRef.current) return;
if (isProcessing) return;
setIsProcessing(true);
@@ -253,6 +254,7 @@ export default function PreviewScreen({
// Saves to gallery without printing
const handleSaveOnly = async () => {
if (idleTimerRef.current) clearTimeout(idleTimerRef.current);
if (!mountedRef.current) return;
if (isProcessing) return;
setIsProcessing(true);
@@ -281,6 +283,7 @@ export default function PreviewScreen({
// Auto-save and exit
const handleExit = useCallback(async () => {
if (idleTimerRef.current) clearTimeout(idleTimerRef.current);
if (!mountedRef.current) return;
if (isProcessing) return;
const state = stateRef.current;
@@ -326,6 +329,7 @@ export default function PreviewScreen({
}, [resetIdleTimer]);
const handleRetakeClick = async () => {
if (idleTimerRef.current) clearTimeout(idleTimerRef.current);
if (!mountedRef.current) return;
if (isProcessing) return;
if (stickers.length > 0 || layout !== 'single' || activeFrame || dateOverlay !== 'off') {
+23 -12
View File
@@ -3,15 +3,23 @@ import * as FileSystem from 'expo-file-system/legacy';
const logFileUri = FileSystem.documentDirectory + 'app_logs.txt';
// Queue to serialize file operations and prevent race conditions
let writeQueue = Promise.resolve();
const logQueue: string[] = [];
let isWriting = false;
const enqueueWrite = (logLine: string) => {
writeQueue = writeQueue.then(async () => {
const processLogQueue = async () => {
if (isWriting) return;
isWriting = true;
while (logQueue.length > 0) {
const logLine = logQueue.shift();
if (!logLine) continue;
try {
const fileInfo = await FileSystem.getInfoAsync(logFileUri);
if (!fileInfo.exists) {
await FileSystem.writeAsStringAsync(logFileUri, logLine, { encoding: FileSystem.EncodingType.UTF8 });
} else {
if (logFileUri) {
const fileInfo = await FileSystem.getInfoAsync(logFileUri);
if (!fileInfo.exists) {
await FileSystem.writeAsStringAsync(logFileUri, '', { encoding: FileSystem.EncodingType.UTF8 });
}
const current = await FileSystem.readAsStringAsync(logFileUri, { encoding: FileSystem.EncodingType.UTF8 });
let newContent = current + logLine;
if (newContent.length > 100000) {
@@ -21,12 +29,15 @@ const enqueueWrite = (logLine: string) => {
}
} catch (e) {
console.warn('Failed to write to log file:', e);
// We catch here so the queue is not broken for subsequent logs
}
}).catch((e) => {
console.error('Queue error:', e);
});
return writeQueue;
}
isWriting = false;
};
const enqueueWrite = async (logLine: string) => {
logQueue.push(logLine);
processLogQueue(); // Intentionally unawaited to run in background
};
export const logger = {
+1 -1
View File
@@ -61,7 +61,7 @@ export async function loadSettings(): Promise<AppSettings> {
export async function saveSettings(settings: AppSettings): Promise<void> {
try {
const content = JSON.stringify(settings, null, 2);
await FileSystem.writeAsStringAsync(settingsFile.uri, content);
await settingsFile.write(content);
console.log('Settings saved successfully:', content);
} catch (e) {
console.error('Failed to save settings:', e);