Fix bugs from code review (state flow, idle timer, logger, kiosk, temp files)
This commit is contained in:
@@ -1,11 +1,12 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { StyleSheet, View, SafeAreaView, StatusBar } from 'react-native';
|
import { StyleSheet, View, SafeAreaView, StatusBar, ActivityIndicator } from 'react-native';
|
||||||
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
import { GestureHandlerRootView } from 'react-native-gesture-handler';
|
||||||
import CameraScreen from './src/screens/CameraScreen';
|
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 { logger } from './src/services/logger';
|
import { logger } from './src/services/logger';
|
||||||
|
import * as FileSystem from 'expo-file-system/legacy';
|
||||||
|
|
||||||
// Set up global error handler
|
// Set up global error handler
|
||||||
const defaultErrorHandler = (global as any).ErrorUtils.getGlobalHandler();
|
const defaultErrorHandler = (global as any).ErrorUtils.getGlobalHandler();
|
||||||
@@ -24,6 +25,7 @@ type ScreenState = 'home' | 'camera' | 'preview' | 'admin';
|
|||||||
export default function App() {
|
export default function App() {
|
||||||
const [currentScreen, setCurrentScreen] = useState<ScreenState>('home');
|
const [currentScreen, setCurrentScreen] = useState<ScreenState>('home');
|
||||||
const [photoUris, setPhotoUris] = useState<string[]>([]);
|
const [photoUris, setPhotoUris] = useState<string[]>([]);
|
||||||
|
const [captureHistory, setCaptureHistory] = useState<number[]>([]);
|
||||||
const [settings, setSettings] = useState<AppSettings | null>(null);
|
const [settings, setSettings] = useState<AppSettings | null>(null);
|
||||||
|
|
||||||
// 1. Load configuration settings on app start
|
// 1. Load configuration settings on app start
|
||||||
@@ -45,19 +47,29 @@ export default function App() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!settings) {
|
if (!settings) {
|
||||||
return <View style={styles.loadingContainer} />;
|
return (
|
||||||
|
<View style={styles.loadingContainer}>
|
||||||
|
<StatusBar hidden={true} />
|
||||||
|
<ActivityIndicator size="large" color="#ff2bd6" />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Navigation Actions
|
// 2. Navigation Actions
|
||||||
const handleStartBooth = () => {
|
const handleStartBooth = () => {
|
||||||
setPhotoUris([]);
|
setPhotoUris([]);
|
||||||
|
setCaptureHistory([]);
|
||||||
setCurrentScreen('camera');
|
setCurrentScreen('camera');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePhotoCaptured = async (uri: string) => {
|
const handlePhotoCaptured = async (uri: string) => {
|
||||||
await logger.log(`Photo captured via CameraScreen: ${uri}`);
|
await logger.log(`Photo captured via CameraScreen: ${uri}`);
|
||||||
try {
|
try {
|
||||||
setPhotoUris((prev) => [...prev, uri]);
|
setPhotoUris((prev) => {
|
||||||
|
const next = [...prev, uri];
|
||||||
|
return next.length > 4 ? next.slice(-4) : next;
|
||||||
|
});
|
||||||
|
setCaptureHistory((prev) => [...prev, 1]);
|
||||||
setCurrentScreen('preview');
|
setCurrentScreen('preview');
|
||||||
await logger.log('State updated to preview screen successfully.');
|
await logger.log('State updated to preview screen successfully.');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -67,15 +79,30 @@ export default function App() {
|
|||||||
|
|
||||||
// Called by CameraScreen when burst mode completes all photos
|
// Called by CameraScreen when burst mode completes all photos
|
||||||
const handleBurstComplete = (uris: string[]) => {
|
const handleBurstComplete = (uris: string[]) => {
|
||||||
setPhotoUris((prev) => [...prev, ...uris]);
|
setPhotoUris((prev) => {
|
||||||
|
const next = [...prev, ...uris];
|
||||||
|
return next.length > 4 ? next.slice(-4) : next;
|
||||||
|
});
|
||||||
|
setCaptureHistory((prev) => [...prev, uris.length]);
|
||||||
setCurrentScreen('preview');
|
setCurrentScreen('preview');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRetakeLast = () => {
|
const handleRetakeLast = async () => {
|
||||||
// Remove last captured photo
|
// Remove photos from the last capture event
|
||||||
|
const lastCaptureCount = captureHistory.length > 0 ? captureHistory[captureHistory.length - 1] : 1;
|
||||||
const updatedUris = [...photoUris];
|
const updatedUris = [...photoUris];
|
||||||
updatedUris.pop();
|
const removedUris = updatedUris.splice(-lastCaptureCount, lastCaptureCount);
|
||||||
setPhotoUris(updatedUris);
|
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
|
// Always let user retake
|
||||||
setCurrentScreen('camera');
|
setCurrentScreen('camera');
|
||||||
@@ -85,8 +112,17 @@ export default function App() {
|
|||||||
setCurrentScreen('camera');
|
setCurrentScreen('camera');
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleReset = () => {
|
const handleReset = async () => {
|
||||||
|
// Clean up temp files
|
||||||
|
for (const uri of photoUris) {
|
||||||
|
try {
|
||||||
|
await FileSystem.deleteAsync(uri, { idempotent: true });
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to delete temp file:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
setPhotoUris([]);
|
setPhotoUris([]);
|
||||||
|
setCaptureHistory([]);
|
||||||
setCurrentScreen('home'); // Go to home when cancelled
|
setCurrentScreen('home'); // Go to home when cancelled
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -217,6 +217,22 @@ export default function AdminScreen({ currentSettings, onSave, onClose }: AdminS
|
|||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
// If user exits without saving, revert Kiosk Mode to match saved settings
|
||||||
|
if (kioskActive !== currentSettings.kioskModeEnabled) {
|
||||||
|
try {
|
||||||
|
if (currentSettings.kioskModeEnabled) {
|
||||||
|
KioskMode.startKiosk();
|
||||||
|
} else {
|
||||||
|
KioskMode.stopKiosk();
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to revert Kiosk mode:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
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}>
|
||||||
<View style={styles.headerRow}>
|
<View style={styles.headerRow}>
|
||||||
@@ -224,7 +240,7 @@ export default function AdminScreen({ currentSettings, onSave, onClose }: AdminS
|
|||||||
<FontAwesomeIcon icon={faUserGear} size={22} color={THEME.colors.accent} />
|
<FontAwesomeIcon icon={faUserGear} size={22} color={THEME.colors.accent} />
|
||||||
<Text style={styles.title}>EINSTELLUNGEN</Text>
|
<Text style={styles.title}>EINSTELLUNGEN</Text>
|
||||||
</View>
|
</View>
|
||||||
<TouchableOpacity style={styles.closeBtn} onPress={onClose}>
|
<TouchableOpacity style={styles.closeBtn} onPress={handleClose}>
|
||||||
<FontAwesomeIcon icon={faArrowLeft} size={14} color={THEME.colors.text} />
|
<FontAwesomeIcon icon={faArrowLeft} size={14} color={THEME.colors.text} />
|
||||||
<Text style={styles.closeBtnText}>Zurück zur Fotobox</Text>
|
<Text style={styles.closeBtnText}>Zurück zur Fotobox</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
@@ -523,7 +539,7 @@ export default function AdminScreen({ currentSettings, onSave, onClose }: AdminS
|
|||||||
<Text style={{ color: '#fff', fontWeight: 'bold' }}>Schließen</Text>
|
<Text style={{ color: '#fff', fontWeight: 'bold' }}>Schließen</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={{ backgroundColor: THEME.colors.danger, padding: 15, borderRadius: 8, flex: 0.48, alignItems: 'center' }}
|
style={{ backgroundColor: THEME.colors.error, padding: 15, borderRadius: 8, flex: 0.48, alignItems: 'center' }}
|
||||||
onPress={async () => { await logger.clearLogs(); setLogs(await logger.readLogs()); }}
|
onPress={async () => { await logger.clearLogs(); setLogs(await logger.readLogs()); }}
|
||||||
>
|
>
|
||||||
<Text style={{ color: '#fff', fontWeight: 'bold' }}>Leeren</Text>
|
<Text style={{ color: '#fff', fontWeight: 'bold' }}>Leeren</Text>
|
||||||
|
|||||||
@@ -159,8 +159,18 @@ export default function CameraScreen({
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
// Cancel capture process
|
// Cancel capture process
|
||||||
const handleLocalCancel = () => {
|
const handleLocalCancel = async () => {
|
||||||
if (burstTimerRef.current) clearTimeout(burstTimerRef.current);
|
if (burstTimerRef.current) clearTimeout(burstTimerRef.current);
|
||||||
|
|
||||||
|
// Clean up any temp files from a partially completed burst
|
||||||
|
for (const uri of burstUris) {
|
||||||
|
try {
|
||||||
|
await FileSystem.deleteAsync(uri, { idempotent: true });
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to delete temp file on cancel:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setCountdown(countdownDuration);
|
setCountdown(countdownDuration);
|
||||||
setIsCapturing(false);
|
setIsCapturing(false);
|
||||||
setBurstIndex(0);
|
setBurstIndex(0);
|
||||||
|
|||||||
@@ -183,16 +183,22 @@ export default function PreviewScreen({
|
|||||||
setSelectedStickerId(null);
|
setSelectedStickerId(null);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
// Keep latest state in a ref to avoid stale closures and rapid re-renders
|
||||||
|
const stateRef = useRef({ stickers, layout, activeFrame, dateOverlay, onReset, currentPhoto });
|
||||||
|
React.useEffect(() => {
|
||||||
|
stateRef.current = { stickers, layout, activeFrame, dateOverlay, onReset, currentPhoto };
|
||||||
|
}, [stickers, layout, activeFrame, dateOverlay, onReset, currentPhoto]);
|
||||||
|
|
||||||
// ── Capture the final image ──
|
// ── Capture the final image ──
|
||||||
const captureComposite = async () => {
|
const captureComposite = async (fallbackPhoto: string = currentPhoto) => {
|
||||||
try {
|
try {
|
||||||
if (viewShotRef.current) {
|
if (viewShotRef.current) {
|
||||||
return await viewShotRef.current.capture();
|
return await viewShotRef.current.capture();
|
||||||
}
|
}
|
||||||
return currentPhoto;
|
return fallbackPhoto;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('Failed to capture composite:', e);
|
console.error('Failed to capture composite:', e);
|
||||||
return currentPhoto;
|
return fallbackPhoto;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -202,7 +208,7 @@ export default function PreviewScreen({
|
|||||||
setStatusMessage('Dein Foto wird vorbereitet...');
|
setStatusMessage('Dein Foto wird vorbereitet...');
|
||||||
try {
|
try {
|
||||||
setStatusMessage('Bild wird generiert...');
|
setStatusMessage('Bild wird generiert...');
|
||||||
const printUri = await captureComposite();
|
const printUri = await captureComposite(currentPhoto);
|
||||||
|
|
||||||
setStatusMessage('Druckauftrag wird an Drucker gesendet...');
|
setStatusMessage('Druckauftrag wird an Drucker gesendet...');
|
||||||
await printImageLocal(printUri, {
|
await printImageLocal(printUri, {
|
||||||
@@ -230,7 +236,7 @@ export default function PreviewScreen({
|
|||||||
setIsProcessing(true);
|
setIsProcessing(true);
|
||||||
setStatusMessage('Bild wird generiert...');
|
setStatusMessage('Bild wird generiert...');
|
||||||
try {
|
try {
|
||||||
const saveUri = await captureComposite();
|
const saveUri = await captureComposite(currentPhoto);
|
||||||
|
|
||||||
setStatusMessage('Wird in Galerie gespeichert...');
|
setStatusMessage('Wird in Galerie gespeichert...');
|
||||||
await saveToGallery(saveUri);
|
await saveToGallery(saveUri);
|
||||||
@@ -249,27 +255,28 @@ export default function PreviewScreen({
|
|||||||
|
|
||||||
// Auto-save and exit
|
// Auto-save and exit
|
||||||
const handleExit = useCallback(async () => {
|
const handleExit = useCallback(async () => {
|
||||||
if (stickers.length === 0 && layout === 'single' && !activeFrame && dateOverlay === 'off') {
|
const state = stateRef.current;
|
||||||
onReset();
|
if (state.stickers.length === 0 && state.layout === 'single' && !state.activeFrame && state.dateOverlay === 'off') {
|
||||||
|
state.onReset();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setIsProcessing(true);
|
setIsProcessing(true);
|
||||||
setStatusMessage('Wird gespeichert...');
|
setStatusMessage('Wird gespeichert...');
|
||||||
try {
|
try {
|
||||||
const capturedUri = await captureComposite();
|
const capturedUri = await captureComposite(state.currentPhoto);
|
||||||
await saveToGallery(capturedUri);
|
await saveToGallery(capturedUri);
|
||||||
setStatusMessage('Erfolgreich gespeichert!');
|
setStatusMessage('Erfolgreich gespeichert!');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
onReset();
|
state.onReset();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Auto-saving on exit failed:', error);
|
console.error('Auto-saving on exit failed:', error);
|
||||||
setIsProcessing(false);
|
setIsProcessing(false);
|
||||||
onReset();
|
state.onReset();
|
||||||
}
|
}
|
||||||
}, [stickers, layout, activeFrame, dateOverlay, onReset]);
|
}, []); // Stable reference
|
||||||
|
|
||||||
// Idle timer logic
|
// Idle timer logic
|
||||||
const resetIdleTimer = useCallback(() => {
|
const resetIdleTimer = useCallback(() => {
|
||||||
@@ -291,7 +298,7 @@ export default function PreviewScreen({
|
|||||||
setIsProcessing(true);
|
setIsProcessing(true);
|
||||||
setStatusMessage('Wird vor dem Wiederholen gespeichert...');
|
setStatusMessage('Wird vor dem Wiederholen gespeichert...');
|
||||||
try {
|
try {
|
||||||
const capturedUri = await captureComposite();
|
const capturedUri = await captureComposite(currentPhoto);
|
||||||
await saveToGallery(capturedUri);
|
await saveToGallery(capturedUri);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Auto-saving before retake failed:', error);
|
console.error('Auto-saving before retake failed:', error);
|
||||||
|
|||||||
+41
-43
@@ -2,22 +2,17 @@ import * as FileSystem from 'expo-file-system/legacy';
|
|||||||
|
|
||||||
const logFileUri = FileSystem.documentDirectory + 'app_logs.txt';
|
const logFileUri = FileSystem.documentDirectory + 'app_logs.txt';
|
||||||
|
|
||||||
export const logger = {
|
// Queue to serialize file operations and prevent race conditions
|
||||||
log: async (message: string) => {
|
let writeQueue = Promise.resolve();
|
||||||
try {
|
|
||||||
const timestamp = new Date().toISOString();
|
|
||||||
const logLine = `[INFO] ${timestamp}: ${message}\n`;
|
|
||||||
console.log(logLine.trim());
|
|
||||||
|
|
||||||
|
const enqueueWrite = (logLine: string) => {
|
||||||
|
writeQueue = writeQueue.then(async () => {
|
||||||
|
try {
|
||||||
const fileInfo = await FileSystem.getInfoAsync(logFileUri);
|
const fileInfo = await FileSystem.getInfoAsync(logFileUri);
|
||||||
if (!fileInfo.exists) {
|
if (!fileInfo.exists) {
|
||||||
await FileSystem.writeAsStringAsync(logFileUri, logLine, { encoding: FileSystem.EncodingType.UTF8 });
|
await FileSystem.writeAsStringAsync(logFileUri, logLine, { encoding: FileSystem.EncodingType.UTF8 });
|
||||||
} else {
|
} else {
|
||||||
// Read existing and append (simplistic approach for small logs)
|
|
||||||
// expo-file-system doesn't have an append method out of the box in older versions,
|
|
||||||
// but wait, we can just read, then write. To avoid OOM we should truncate if too big.
|
|
||||||
const current = await FileSystem.readAsStringAsync(logFileUri, { encoding: FileSystem.EncodingType.UTF8 });
|
const current = await FileSystem.readAsStringAsync(logFileUri, { encoding: FileSystem.EncodingType.UTF8 });
|
||||||
// Keep last 100000 chars
|
|
||||||
let newContent = current + logLine;
|
let newContent = current + logLine;
|
||||||
if (newContent.length > 100000) {
|
if (newContent.length > 100000) {
|
||||||
newContent = newContent.slice(-100000);
|
newContent = newContent.slice(-100000);
|
||||||
@@ -25,45 +20,45 @@ export const logger = {
|
|||||||
await FileSystem.writeAsStringAsync(logFileUri, newContent, { encoding: FileSystem.EncodingType.UTF8 });
|
await FileSystem.writeAsStringAsync(logFileUri, newContent, { encoding: FileSystem.EncodingType.UTF8 });
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('Failed to write log:', 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const logger = {
|
||||||
|
log: async (message: string) => {
|
||||||
|
const timestamp = new Date().toISOString();
|
||||||
|
const logLine = `[INFO] ${timestamp}: ${message}\n`;
|
||||||
|
console.log(logLine.trim());
|
||||||
|
return enqueueWrite(logLine);
|
||||||
},
|
},
|
||||||
|
|
||||||
error: async (message: string, error?: any) => {
|
error: async (message: string, error?: any) => {
|
||||||
try {
|
const timestamp = new Date().toISOString();
|
||||||
const timestamp = new Date().toISOString();
|
let errorString = '';
|
||||||
let errorString = '';
|
if (error) {
|
||||||
if (error) {
|
if (error instanceof Error) {
|
||||||
if (error instanceof Error) {
|
errorString = `\n Name: ${error.name}\n Message: ${error.message}\n Stack: ${error.stack}`;
|
||||||
errorString = `\n Name: ${error.name}\n Message: ${error.message}\n Stack: ${error.stack}`;
|
} else if (typeof error === 'object') {
|
||||||
} else if (typeof error === 'object') {
|
errorString = `\n Object: ${JSON.stringify(error)}`;
|
||||||
errorString = `\n Object: ${JSON.stringify(error)}`;
|
|
||||||
} else {
|
|
||||||
errorString = `\n Value: ${String(error)}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const logLine = `[ERROR] ${timestamp}: ${message}${errorString}\n`;
|
|
||||||
console.error(logLine.trim());
|
|
||||||
|
|
||||||
const fileInfo = await FileSystem.getInfoAsync(logFileUri);
|
|
||||||
if (!fileInfo.exists) {
|
|
||||||
await FileSystem.writeAsStringAsync(logFileUri, logLine, { encoding: FileSystem.EncodingType.UTF8 });
|
|
||||||
} else {
|
} else {
|
||||||
const current = await FileSystem.readAsStringAsync(logFileUri, { encoding: FileSystem.EncodingType.UTF8 });
|
errorString = `\n Value: ${String(error)}`;
|
||||||
let newContent = current + logLine;
|
|
||||||
if (newContent.length > 100000) {
|
|
||||||
newContent = newContent.slice(-100000);
|
|
||||||
}
|
|
||||||
await FileSystem.writeAsStringAsync(logFileUri, newContent, { encoding: FileSystem.EncodingType.UTF8 });
|
|
||||||
}
|
}
|
||||||
} catch (e) {
|
|
||||||
console.log('Failed to write error log:', e);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const logLine = `[ERROR] ${timestamp}: ${message}${errorString}\n`;
|
||||||
|
console.error(logLine.trim());
|
||||||
|
return enqueueWrite(logLine);
|
||||||
},
|
},
|
||||||
|
|
||||||
readLogs: async (): Promise<string> => {
|
readLogs: async (): Promise<string> => {
|
||||||
try {
|
try {
|
||||||
|
// Ensure we wait for pending writes before reading
|
||||||
|
await writeQueue;
|
||||||
const fileInfo = await FileSystem.getInfoAsync(logFileUri);
|
const fileInfo = await FileSystem.getInfoAsync(logFileUri);
|
||||||
if (!fileInfo.exists) return 'Keine Logs vorhanden.';
|
if (!fileInfo.exists) return 'Keine Logs vorhanden.';
|
||||||
return await FileSystem.readAsStringAsync(logFileUri, { encoding: FileSystem.EncodingType.UTF8 });
|
return await FileSystem.readAsStringAsync(logFileUri, { encoding: FileSystem.EncodingType.UTF8 });
|
||||||
@@ -73,10 +68,13 @@ export const logger = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
clearLogs: async () => {
|
clearLogs: async () => {
|
||||||
try {
|
writeQueue = writeQueue.then(async () => {
|
||||||
await FileSystem.deleteAsync(logFileUri, { idempotent: true });
|
try {
|
||||||
} catch (e) {
|
await FileSystem.deleteAsync(logFileUri, { idempotent: true });
|
||||||
console.log('Failed to clear logs:', e);
|
} catch (e) {
|
||||||
}
|
console.warn('Failed to clear logs:', e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return writeQueue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user