From a8828ae521b8d2f9e89b898064a9b9e2a768ce1a Mon Sep 17 00:00:00 2001 From: Anton Tranelis Date: Tue, 5 Aug 2025 09:33:06 +0200 Subject: [PATCH] fix: add localStorage error handling to theme functionality - Create safeLocalStorage utility for graceful error handling - Update useTheme hook to use safeLocalStorage - Update ThemeControl component to use safeLocalStorage - Prevents SecurityError when localStorage is unavailable in theme features This extends the localStorage fix from issue #212 to cover all localStorage usage in the codebase. --- .../Components/AppShell/hooks/useTheme.tsx | 6 ++-- lib/src/Components/Templates/ThemeControl.tsx | 10 ++++-- lib/src/Utils/localStorage.ts | 32 +++++++++++++++++++ 3 files changed, 43 insertions(+), 5 deletions(-) create mode 100644 lib/src/Utils/localStorage.ts diff --git a/lib/src/Components/AppShell/hooks/useTheme.tsx b/lib/src/Components/AppShell/hooks/useTheme.tsx index 367d8fd3..254996b9 100644 --- a/lib/src/Components/AppShell/hooks/useTheme.tsx +++ b/lib/src/Components/AppShell/hooks/useTheme.tsx @@ -1,12 +1,14 @@ import { useEffect } from 'react' +import { safeLocalStorage } from '#utils/localStorage' + export const useTheme = (defaultTheme = 'default') => { useEffect(() => { - const savedTheme = localStorage.getItem('theme') + const savedTheme = safeLocalStorage.getItem('theme') const initialTheme = savedTheme ? (JSON.parse(savedTheme) as string) : defaultTheme if (initialTheme !== 'default') { document.documentElement.setAttribute('data-theme', defaultTheme) - localStorage.setItem('theme', JSON.stringify(initialTheme)) + safeLocalStorage.setItem('theme', JSON.stringify(initialTheme)) } }, [defaultTheme]) } diff --git a/lib/src/Components/Templates/ThemeControl.tsx b/lib/src/Components/Templates/ThemeControl.tsx index 6ed593fc..28cc2c7a 100644 --- a/lib/src/Components/Templates/ThemeControl.tsx +++ b/lib/src/Components/Templates/ThemeControl.tsx @@ -1,5 +1,7 @@ import { useState, useEffect } from 'react' +import { safeLocalStorage } from '#utils/localStorage' + const themes = [ 'default', 'light', @@ -15,14 +17,16 @@ const themes = [ export const ThemeControl = () => { const [theme, setTheme] = useState(() => { - const savedTheme = localStorage.getItem('theme') + const savedTheme = safeLocalStorage.getItem('theme') return savedTheme ? (JSON.parse(savedTheme) as string) : 'default' }) useEffect(() => { if (theme !== 'default') { - localStorage.setItem('theme', JSON.stringify(theme)) - } else localStorage.removeItem('theme') + safeLocalStorage.setItem('theme', JSON.stringify(theme)) + } else { + safeLocalStorage.removeItem('theme') + } document.documentElement.setAttribute('data-theme', theme) }, [theme]) diff --git a/lib/src/Utils/localStorage.ts b/lib/src/Utils/localStorage.ts new file mode 100644 index 00000000..47dbc2d2 --- /dev/null +++ b/lib/src/Utils/localStorage.ts @@ -0,0 +1,32 @@ +/** + * Safe localStorage utility that handles SecurityError gracefully + * when localStorage is not available (e.g., private browsing mode) + */ +export const safeLocalStorage = { + getItem: (key: string): string | null => { + try { + return localStorage.getItem(key) + } catch (error) { + console.warn(`localStorage.getItem failed for key "${key}":`, error) + return null + } + }, + + setItem: (key: string, value: string): void => { + try { + localStorage.setItem(key, value) + } catch (error) { + console.warn(`localStorage.setItem failed for key "${key}":`, error) + // Silently fail - functionality will work in memory-only mode + } + }, + + removeItem: (key: string): void => { + try { + localStorage.removeItem(key) + } catch (error) { + console.warn(`localStorage.removeItem failed for key "${key}":`, error) + // Silently fail + } + }, +} \ No newline at end of file