Fix 8 critical/high resilience issues across camera, preview, logger, settings, and admin
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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 = {
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user