mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 13:21:46 +00:00
a07e76c740
- Fix import path: use i18n barrel instead of TranslationContext directly - Encapsulate localStorage key behind hasStoredLanguage() helper in settingsStore - Fix pt-BR detection: only map pt-BR to br, pt-PT now returns null correctly - Add comment linking server SUPPORTED_LANG_CODES to canonical client source - Extract /api/config inline handler to routes/publicConfig.ts - Add aria-haspopup, aria-expanded, role=listbox/option, aria-selected to dropdown - Add 8 tests for detectBrowserLanguage (FE-COMP-I18N-016–023) - Add 3 tests for setLanguageTransient (FE-STORE-SETTINGS-015–017)
89 lines
3.0 KiB
TypeScript
89 lines
3.0 KiB
TypeScript
import { create } from 'zustand'
|
|
import { settingsApi } from '../api/client'
|
|
import type { Settings } from '../types'
|
|
import { getApiErrorMessage } from '../types'
|
|
import { SUPPORTED_LANGUAGE_CODES } from '../i18n/supportedLanguages'
|
|
|
|
interface SettingsState {
|
|
settings: Settings
|
|
isLoaded: boolean
|
|
|
|
loadSettings: () => Promise<void>
|
|
updateSetting: (key: keyof Settings, value: Settings[keyof Settings]) => Promise<void>
|
|
setLanguageLocal: (lang: string) => void
|
|
setLanguageTransient: (lang: string) => void
|
|
updateSettings: (settingsObj: Partial<Settings>) => Promise<void>
|
|
}
|
|
|
|
// Returns true when the user has explicitly chosen a language (persisted in localStorage).
|
|
// Use this instead of reading localStorage directly so the key stays encapsulated here.
|
|
export const hasStoredLanguage = (): boolean =>
|
|
typeof localStorage !== 'undefined' && !!localStorage.getItem('app_language')
|
|
|
|
export const useSettingsStore = create<SettingsState>((set, get) => ({
|
|
settings: {
|
|
map_tile_url: '',
|
|
default_lat: 48.8566,
|
|
default_lng: 2.3522,
|
|
default_zoom: 10,
|
|
dark_mode: false,
|
|
default_currency: 'USD',
|
|
language: localStorage.getItem('app_language') || 'en',
|
|
temperature_unit: 'fahrenheit',
|
|
time_format: '12h',
|
|
show_place_description: false,
|
|
},
|
|
isLoaded: false,
|
|
|
|
loadSettings: async () => {
|
|
try {
|
|
const data = await settingsApi.get()
|
|
set((state) => ({
|
|
settings: { ...state.settings, ...data.settings },
|
|
isLoaded: true,
|
|
}))
|
|
} catch (err: unknown) {
|
|
set({ isLoaded: true })
|
|
console.error('Failed to load settings:', err)
|
|
}
|
|
},
|
|
|
|
updateSetting: async (key: keyof Settings, value: Settings[keyof Settings]) => {
|
|
set((state) => ({
|
|
settings: { ...state.settings, [key]: value },
|
|
}))
|
|
if (key === 'language') localStorage.setItem('app_language', value as string)
|
|
try {
|
|
await settingsApi.set(key, value)
|
|
} catch (err: unknown) {
|
|
console.error('Failed to save setting:', err)
|
|
throw new Error(getApiErrorMessage(err, 'Error saving setting'))
|
|
}
|
|
},
|
|
|
|
setLanguageLocal: (lang: string) => {
|
|
localStorage.setItem('app_language', lang)
|
|
set((state) => ({ settings: { ...state.settings, language: lang } }))
|
|
},
|
|
|
|
// Applies a language for the current session without persisting to localStorage.
|
|
// Used for automatic detection (browser/server default) — only explicit user
|
|
// choices via the UI should be persisted.
|
|
setLanguageTransient: (lang: string) => {
|
|
if (!SUPPORTED_LANGUAGE_CODES.includes(lang)) return
|
|
set((state) => ({ settings: { ...state.settings, language: lang } }))
|
|
},
|
|
|
|
updateSettings: async (settingsObj: Partial<Settings>) => {
|
|
set((state) => ({
|
|
settings: { ...state.settings, ...settingsObj },
|
|
}))
|
|
try {
|
|
await settingsApi.setBulk(settingsObj)
|
|
} catch (err: unknown) {
|
|
console.error('Failed to save settings:', err)
|
|
throw new Error(getApiErrorMessage(err, 'Error saving settings'))
|
|
}
|
|
},
|
|
}))
|