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.
This commit is contained in:
Anton Tranelis 2025-08-05 09:33:06 +02:00
parent c4131ced0f
commit a8828ae521
3 changed files with 43 additions and 5 deletions

View File

@ -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])
}

View File

@ -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<string>(() => {
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])

View File

@ -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
}
},
}