All files / src/hooks useStoredProfile.ts

78% Statements 39/50
59.09% Branches 13/22
84.61% Functions 11/13
79.54% Lines 35/44

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106      2x     2x 2x       1x 1x       6x 6x 1x 1x                                               22x 6x   22x 6x   22x     22x 6x                           22x 7x 1x   6x         22x 6x   6x 6x 6x 6x 6x 1x 1x 1x 1x 1x                 6x     6x     22x    
import { useState, useEffect, type Dispatch, type SetStateAction } from 'react'
import { api, type Profile } from '../api/client'
 
const PROFILE_STORAGE_KEY = 'cookie_selected_profile_id'
 
export function saveProfileId(id: number) {
  localStorage.setItem(PROFILE_STORAGE_KEY, String(id))
  document.cookie = `selected_profile_id=${id};path=/;SameSite=Lax`
}
 
export function clearProfileId() {
  localStorage.removeItem(PROFILE_STORAGE_KEY)
  document.cookie = 'selected_profile_id=;path=/;expires=Thu, 01 Jan 1970 00:00:00 GMT'
}
 
function getStoredProfileId(): number | null {
  const stored = localStorage.getItem(PROFILE_STORAGE_KEY)
  if (!stored) return null
  const id = parseInt(stored, 10)
  return isNaN(id) ? null : id
}
 
interface AuthProfile {
  id: number
  name: string
  avatar_color: string
  theme: string
  unit_preference: string
}
 
interface UseStoredProfileReturn {
  profile: Profile | null
  setProfile: Dispatch<SetStateAction<Profile | null>>
  theme: 'light' | 'dark'
  setTheme: Dispatch<SetStateAction<'light' | 'dark'>>
  loading: boolean
}
 
/**
 * Manages profile restoration from localStorage (home mode) or auth (passkey mode).
 * Applies the theme class to the document element.
 */
export function useStoredProfile(authProfile?: AuthProfile | null): UseStoredProfileReturn {
  const [profile, setProfile] = useState<Profile | null>(() =>
    authProfile ? (authProfile as Profile) : null
  )
  const [theme, setTheme] = useState<'light' | 'dark'>(() =>
    authProfile ? (authProfile.theme as 'light' | 'dark') : 'light'
  )
  const [loading, setLoading] = useState(authProfile === undefined)
 
  // Sync auth profile changes to state (passkey mode)
  useEffect(() => {
    Eif (authProfile === undefined) return
    const id = requestAnimationFrame(() => {
      if (authProfile) {
        setProfile(authProfile as Profile)
        setTheme(authProfile.theme as 'light' | 'dark')
      } else {
        setProfile(null)
      }
      setLoading(false)
    })
    return () => cancelAnimationFrame(id)
  }, [authProfile])
 
  // Apply theme class to document
  useEffect(() => {
    if (theme === 'dark') {
      document.documentElement.classList.add('dark')
    } else {
      document.documentElement.classList.remove('dark')
    }
  }, [theme])
 
  // Restore profile from localStorage (home mode only)
  useEffect(() => {
    Iif (authProfile !== undefined) return
 
    let cancelled = false
    ;(async () => {
      try {
        const storedId = getStoredProfileId()
        if (storedId) {
          try {
            const restored = await api.profiles.select(storedId)
            Eif (!cancelled) {
              setProfile(restored)
              setTheme(restored.theme as 'light' | 'dark')
            }
          } catch {
            clearProfileId()
          }
        }
      } catch (error) {
        console.error('Failed to restore session:', error)
      } finally {
        Eif (!cancelled) setLoading(false)
      }
    })()
    return () => { cancelled = true }
  }, [authProfile])
 
  return { profile, setProfile, theme, setTheme, loading }
}
 
← Back to Dashboard