diff --git a/src/App.tsx b/src/App.tsx index a0e2889..03fd839 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -16,6 +16,8 @@ import ProfileSelectionPage from "./pages/ProfileSelectionPage"; import ErrorPage from "./components/shared/ErrorPage"; const STARTUP_TIMEOUT_MS = 10_000; +const MAX_RETRIES = 3; +const RETRY_DELAY_MS = 1_000; export default function App() { const { t } = useTranslation(); @@ -23,29 +25,42 @@ export default function App() { const [dbReady, setDbReady] = useState(false); const [startupError, setStartupError] = useState(null); const timeoutRef = useRef | null>(null); + const cancelledRef = useRef(false); useEffect(() => { if (activeProfile && !isLoading) { setDbReady(false); setStartupError(null); + cancelledRef.current = false; timeoutRef.current = setTimeout(() => { setStartupError(t("error.startupTimeout")); }, STARTUP_TIMEOUT_MS); - connectActiveProfile() - .then(() => { + const attemptConnect = async (attempt: number): Promise => { + try { + await connectActiveProfile(); + if (cancelledRef.current) return; if (timeoutRef.current) clearTimeout(timeoutRef.current); setDbReady(true); - }) - .catch((err) => { - if (timeoutRef.current) clearTimeout(timeoutRef.current); - console.error("Failed to connect profile:", err); - setStartupError(err instanceof Error ? err.message : String(err)); - }); + } catch (err) { + if (cancelledRef.current) return; + console.error(`Failed to connect profile (attempt ${attempt}/${MAX_RETRIES}):`, err); + if (attempt < MAX_RETRIES) { + await new Promise((r) => setTimeout(r, RETRY_DELAY_MS)); + if (!cancelledRef.current) return attemptConnect(attempt + 1); + } else { + if (timeoutRef.current) clearTimeout(timeoutRef.current); + setStartupError(err instanceof Error ? err.message : String(err)); + } + } + }; + + attemptConnect(1); } return () => { + cancelledRef.current = true; if (timeoutRef.current) clearTimeout(timeoutRef.current); }; }, [activeProfile, isLoading, connectActiveProfile, t]); diff --git a/src/services/logService.ts b/src/services/logService.ts index 96068f4..4053d08 100644 --- a/src/services/logService.ts +++ b/src/services/logService.ts @@ -9,11 +9,32 @@ export interface LogEntry { type LogListener = () => void; const MAX_ENTRIES = 500; +const STORAGE_KEY = "simpl-resultat-logs"; const logs: LogEntry[] = []; const listeners = new Set(); let initialized = false; +function loadFromStorage() { + try { + const stored = sessionStorage.getItem(STORAGE_KEY); + if (stored) { + const parsed: LogEntry[] = JSON.parse(stored); + logs.push(...parsed); + } + } catch { + // ignore corrupted storage + } +} + +function saveToStorage() { + try { + sessionStorage.setItem(STORAGE_KEY, JSON.stringify(logs)); + } catch { + // ignore quota errors + } +} + function addEntry(level: LogLevel, args: unknown[]) { const message = args .map((a) => { @@ -31,6 +52,7 @@ function addEntry(level: LogLevel, args: unknown[]) { logs.splice(0, logs.length - MAX_ENTRIES); } + saveToStorage(); listeners.forEach((fn) => fn()); } @@ -38,6 +60,8 @@ export function initLogCapture() { if (initialized) return; initialized = true; + loadFromStorage(); + const origLog = console.log.bind(console); const origWarn = console.warn.bind(console); const origError = console.error.bind(console); @@ -64,6 +88,7 @@ export function getLogs(): readonly LogEntry[] { export function clearLogs() { logs.length = 0; + saveToStorage(); listeners.forEach((fn) => fn()); }