Fix 8 critical/high resilience issues across camera, preview, logger, settings, and admin
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 MiB After Width: | Height: | Size: 979 KiB |
@@ -65,6 +65,7 @@ export default function AdminScreen({ currentSettings, onSave, onClose }: AdminS
|
|||||||
const [countdown, setCountdown] = useState<string>(String(currentSettings.countdownDuration));
|
const [countdown, setCountdown] = useState<string>(String(currentSettings.countdownDuration));
|
||||||
const [printerIp, setPrinterIp] = useState<string>(currentSettings.printerIp);
|
const [printerIp, setPrinterIp] = useState<string>(currentSettings.printerIp);
|
||||||
const [password, setPassword] = useState<string>(currentSettings.adminPassword);
|
const [password, setPassword] = useState<string>(currentSettings.adminPassword);
|
||||||
|
const [isTogglingKiosk, setIsTogglingKiosk] = useState<boolean>(false);
|
||||||
const [logs, setLogs] = useState<string | null>(null);
|
const [logs, setLogs] = useState<string | null>(null);
|
||||||
const scrollViewRef = useRef<ScrollView>(null);
|
const scrollViewRef = useRef<ScrollView>(null);
|
||||||
|
|
||||||
@@ -98,7 +99,9 @@ export default function AdminScreen({ currentSettings, onSave, onClose }: AdminS
|
|||||||
return () => clearInterval(interval);
|
return () => clearInterval(interval);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleKioskToggle = (enable: boolean) => {
|
const handleKioskToggle = async (enable: boolean) => {
|
||||||
|
if (isTogglingKiosk) return;
|
||||||
|
setIsTogglingKiosk(true);
|
||||||
try {
|
try {
|
||||||
if (enable) {
|
if (enable) {
|
||||||
const success = KioskMode.startKiosk();
|
const success = KioskMode.startKiosk();
|
||||||
@@ -124,6 +127,8 @@ export default function AdminScreen({ currentSettings, onSave, onClose }: AdminS
|
|||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
Alert.alert('Nativer Fehler', e.message || 'Fehler im Kiosk-Modul');
|
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"
|
placeholder="Standard: 1234"
|
||||||
placeholderTextColor={THEME.colors.textMuted}
|
placeholderTextColor={THEME.colors.textMuted}
|
||||||
secureTextEntry
|
secureTextEntry
|
||||||
|
maxLength={20}
|
||||||
value={password}
|
value={password}
|
||||||
onChangeText={setPassword}
|
onChangeText={setPassword}
|
||||||
keyboardType="number-pad"
|
keyboardType="number-pad"
|
||||||
|
|||||||
@@ -129,10 +129,10 @@ export default function CameraScreen({
|
|||||||
|
|
||||||
// 1. Request built-in camera permission on mount (just in case we fallback)
|
// 1. Request built-in camera permission on mount (just in case we fallback)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!permission || !permission.granted) {
|
if (permission && !permission.granted && permission.canAskAgain) {
|
||||||
requestPermission();
|
requestPermission();
|
||||||
}
|
}
|
||||||
}, [permission]);
|
}, []);
|
||||||
|
|
||||||
// 2. Check if a USB camera is connected.
|
// 2. Check if a USB camera is connected.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -251,7 +251,7 @@ export default function CameraScreen({
|
|||||||
return () => {
|
return () => {
|
||||||
if (countdownTimerRef.current) clearTimeout(countdownTimerRef.current);
|
if (countdownTimerRef.current) clearTimeout(countdownTimerRef.current);
|
||||||
};
|
};
|
||||||
}, [countdownDuration, isIdle, pictureSize, isUsbConnected]);
|
}, [countdownDuration, isIdle, pictureSize, isUsbConnected, burstCount, burstIntervalSec]);
|
||||||
|
|
||||||
const startCountdown = () => {
|
const startCountdown = () => {
|
||||||
isCancelledRef.current = false;
|
isCancelledRef.current = false;
|
||||||
|
|||||||
@@ -217,8 +217,9 @@ export default function PreviewScreen({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Triggers the view capture and the print job
|
// ── Actions ──
|
||||||
const handlePrint = async () => {
|
const handlePrint = async () => {
|
||||||
|
if (idleTimerRef.current) clearTimeout(idleTimerRef.current);
|
||||||
if (!mountedRef.current) return;
|
if (!mountedRef.current) return;
|
||||||
if (isProcessing) return;
|
if (isProcessing) return;
|
||||||
setIsProcessing(true);
|
setIsProcessing(true);
|
||||||
@@ -253,6 +254,7 @@ export default function PreviewScreen({
|
|||||||
|
|
||||||
// Saves to gallery without printing
|
// Saves to gallery without printing
|
||||||
const handleSaveOnly = async () => {
|
const handleSaveOnly = async () => {
|
||||||
|
if (idleTimerRef.current) clearTimeout(idleTimerRef.current);
|
||||||
if (!mountedRef.current) return;
|
if (!mountedRef.current) return;
|
||||||
if (isProcessing) return;
|
if (isProcessing) return;
|
||||||
setIsProcessing(true);
|
setIsProcessing(true);
|
||||||
@@ -281,6 +283,7 @@ export default function PreviewScreen({
|
|||||||
|
|
||||||
// Auto-save and exit
|
// Auto-save and exit
|
||||||
const handleExit = useCallback(async () => {
|
const handleExit = useCallback(async () => {
|
||||||
|
if (idleTimerRef.current) clearTimeout(idleTimerRef.current);
|
||||||
if (!mountedRef.current) return;
|
if (!mountedRef.current) return;
|
||||||
if (isProcessing) return;
|
if (isProcessing) return;
|
||||||
const state = stateRef.current;
|
const state = stateRef.current;
|
||||||
@@ -326,6 +329,7 @@ export default function PreviewScreen({
|
|||||||
}, [resetIdleTimer]);
|
}, [resetIdleTimer]);
|
||||||
|
|
||||||
const handleRetakeClick = async () => {
|
const handleRetakeClick = async () => {
|
||||||
|
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') {
|
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';
|
const logFileUri = FileSystem.documentDirectory + 'app_logs.txt';
|
||||||
|
|
||||||
// Queue to serialize file operations and prevent race conditions
|
// Queue to serialize file operations and prevent race conditions
|
||||||
let writeQueue = Promise.resolve();
|
const logQueue: string[] = [];
|
||||||
|
let isWriting = false;
|
||||||
|
|
||||||
|
const processLogQueue = async () => {
|
||||||
|
if (isWriting) return;
|
||||||
|
isWriting = true;
|
||||||
|
|
||||||
|
while (logQueue.length > 0) {
|
||||||
|
const logLine = logQueue.shift();
|
||||||
|
if (!logLine) continue;
|
||||||
|
|
||||||
const enqueueWrite = (logLine: string) => {
|
|
||||||
writeQueue = writeQueue.then(async () => {
|
|
||||||
try {
|
try {
|
||||||
const fileInfo = await FileSystem.getInfoAsync(logFileUri);
|
if (logFileUri) {
|
||||||
if (!fileInfo.exists) {
|
const fileInfo = await FileSystem.getInfoAsync(logFileUri);
|
||||||
await FileSystem.writeAsStringAsync(logFileUri, logLine, { encoding: FileSystem.EncodingType.UTF8 });
|
if (!fileInfo.exists) {
|
||||||
} else {
|
await FileSystem.writeAsStringAsync(logFileUri, '', { encoding: FileSystem.EncodingType.UTF8 });
|
||||||
|
}
|
||||||
const current = await FileSystem.readAsStringAsync(logFileUri, { encoding: FileSystem.EncodingType.UTF8 });
|
const current = await FileSystem.readAsStringAsync(logFileUri, { encoding: FileSystem.EncodingType.UTF8 });
|
||||||
let newContent = current + logLine;
|
let newContent = current + logLine;
|
||||||
if (newContent.length > 100000) {
|
if (newContent.length > 100000) {
|
||||||
@@ -21,12 +29,15 @@ const enqueueWrite = (logLine: string) => {
|
|||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.warn('Failed to write to log file:', 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);
|
|
||||||
});
|
isWriting = false;
|
||||||
return writeQueue;
|
};
|
||||||
|
|
||||||
|
const enqueueWrite = async (logLine: string) => {
|
||||||
|
logQueue.push(logLine);
|
||||||
|
processLogQueue(); // Intentionally unawaited to run in background
|
||||||
};
|
};
|
||||||
|
|
||||||
export const logger = {
|
export const logger = {
|
||||||
|
|||||||
@@ -61,7 +61,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);
|
||||||
await FileSystem.writeAsStringAsync(settingsFile.uri, content);
|
await settingsFile.write(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