Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 22 additions & 9 deletions app/src/api/directus.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-non-null-assertion */
/* eslint-disable @typescript-eslint/require-await */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable no-catch-all/no-catch-all */
/* eslint-disable no-console */
import { createDirectus, rest, authentication } from '@directus/sdk'

import type { AuthenticationData, AuthenticationStorage } from '@directus/sdk'
Expand Down Expand Up @@ -74,24 +75,36 @@ export const authLocalStorage = (mainKey = 'directus_storage') =>
({
// implementation of get, here return json parsed data from localStorage at mainKey (or null if not found)
get: async () => {
const data = window.localStorage.getItem(mainKey)
if (data) {
return JSON.parse(data)
try {
const data = window.localStorage.getItem(mainKey)
if (data) {
return JSON.parse(data)
}
return null
} catch (error) {
// Handle SecurityError when localStorage is not available (e.g., in private browsing mode)
console.warn('localStorage not available:', error)
return null
}
return null
},
// implementation of set, here set the value at mainKey in localStorage, or remove it if value is null
set: async (value: AuthenticationData | null) => {
if (!value) {
return window.localStorage.removeItem(mainKey)
try {
if (!value) {
return window.localStorage.removeItem(mainKey)
}
return window.localStorage.setItem(mainKey, JSON.stringify(value))
} catch (error) {
// Handle SecurityError when localStorage is not available (e.g., in private browsing mode)
console.warn('localStorage not available:', error)
// Silently fail - authentication will fall back to memory-only storage
}
return window.localStorage.setItem(mainKey, JSON.stringify(value))
},
}) as AuthenticationStorage

export async function getRefreshToken() {
const auth = await authLocalStorage().get()
return auth!.refresh_token
return auth?.refresh_token ?? null
}

export const directusClient = createDirectus<MyCollections>('https://api.utopia-lab.org/')
Expand Down
7 changes: 4 additions & 3 deletions app/src/api/userApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
/* eslint-disable no-console */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unsafe-return */
/* eslint-disable no-catch-all/no-catch-all */
import { createUser, passwordRequest, passwordReset, readMe, updateMe } from '@directus/sdk'

import { directusClient } from './directus'
Expand Down Expand Up @@ -78,9 +79,9 @@ export class UserApi {
const token = await directusClient.getToken()
return token
} catch (error: any) {
console.log(error)
if (error.errors[0].message) throw error.errors[0].message
else throw error
console.warn('Failed to get token:', error)
// Don't throw error - return null instead to allow graceful fallback
return null
}
}

Expand Down
6 changes: 4 additions & 2 deletions lib/src/Components/AppShell/hooks/useTheme.tsx
Original file line number Diff line number Diff line change
@@ -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])
}
3 changes: 3 additions & 0 deletions lib/src/Components/Auth/useAuth.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,10 +64,13 @@ export const AuthProvider = ({ userApi, children }: AuthProviderProps) => {
setLoading(false)
return me
} else {
setLoading(false)
return undefined
}
// eslint-disable-next-line no-catch-all/no-catch-all
} catch (error) {
// eslint-disable-next-line no-console
console.warn('Failed to load user token:', error)
setLoading(false)
return undefined
} finally {
Expand Down
10 changes: 7 additions & 3 deletions lib/src/Components/Templates/ThemeControl.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { useState, useEffect } from 'react'

import { safeLocalStorage } from '#utils/localStorage'

const themes = [
'default',
'light',
Expand All @@ -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])

Expand Down
35 changes: 35 additions & 0 deletions lib/src/Utils/localStorage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* eslint-disable no-catch-all/no-catch-all */
/* eslint-disable no-console */

/**
* 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
}
},
}