fix(web): fix userSettings.nav undefined error in guest mode

- Clock: Replace local user-settings store with shared theme store
  from @manacore/shared-theme to support nav settings properly
- All apps: Add optional chaining and fallback values when accessing
  userSettings.nav.desktopPosition and userSettings.nav.hiddenNavItems
  to prevent TypeError when user is not authenticated

Apps fixed: calendar, chat, clock, contacts, manacore, manadeck,
picture, todo

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Till-JS 2026-01-25 02:58:04 +01:00
parent 753e6fd17f
commit 6713919e09
9 changed files with 40 additions and 117 deletions

View file

@ -1,105 +1,28 @@
/**
* User Settings Store - Manages user preferences using Svelte 5 runes
* User Settings Store for Clock
*
* This store syncs settings with mana-core-auth and provides:
* - Global settings that apply to all apps
* - Per-app overrides for customization
* - localStorage caching for offline support
*/
import { browser } from '$app/environment';
import { createUserSettingsStore } from '@manacore/shared-theme';
import { authStore } from './auth.svelte';
export interface UserSettings {
timeFormat: '12h' | '24h';
firstDayOfWeek: 0 | 1; // 0 = Sunday, 1 = Monday
showSeconds: boolean;
defaultAlarmSound: string;
vibrationEnabled: boolean;
}
const DEFAULT_SETTINGS: UserSettings = {
timeFormat: '24h',
firstDayOfWeek: 1, // Monday (European default)
showSeconds: false,
defaultAlarmSound: 'default',
vibrationEnabled: true,
};
const STORAGE_KEY = 'clock_user_settings';
// Load settings from localStorage
function loadSettings(): UserSettings {
if (!browser) return DEFAULT_SETTINGS;
try {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored) {
return { ...DEFAULT_SETTINGS, ...JSON.parse(stored) };
}
} catch (e) {
console.error('Failed to load user settings:', e);
// Get auth URL dynamically at runtime
function getAuthUrl(): string {
if (browser && typeof window !== 'undefined') {
const injectedUrl = (window as unknown as { __PUBLIC_MANA_CORE_AUTH_URL__?: string })
.__PUBLIC_MANA_CORE_AUTH_URL__;
return injectedUrl || 'http://localhost:3001';
}
return DEFAULT_SETTINGS;
return 'http://localhost:3001';
}
// Save settings to localStorage
function saveSettings(settings: UserSettings) {
if (!browser) return;
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(settings));
} catch (e) {
console.error('Failed to save user settings:', e);
}
}
// State
let settings = $state<UserSettings>(loadSettings());
export const userSettings = {
// Getters
get timeFormat() {
return settings.timeFormat;
},
get firstDayOfWeek() {
return settings.firstDayOfWeek;
},
get showSeconds() {
return settings.showSeconds;
},
get defaultAlarmSound() {
return settings.defaultAlarmSound;
},
get vibrationEnabled() {
return settings.vibrationEnabled;
},
get all() {
return settings;
},
/**
* Update a single setting
*/
update<K extends keyof UserSettings>(key: K, value: UserSettings[K]) {
settings = { ...settings, [key]: value };
saveSettings(settings);
},
/**
* Update multiple settings
*/
updateMany(updates: Partial<UserSettings>) {
settings = { ...settings, ...updates };
saveSettings(settings);
},
/**
* Reset to defaults
*/
reset() {
settings = DEFAULT_SETTINGS;
saveSettings(settings);
},
/**
* Initialize (reload from storage)
*/
initialize() {
settings = loadSettings();
},
};
export const userSettings = createUserSettingsStore({
appId: 'clock',
authUrl: getAuthUrl(),
getAccessToken: () => authStore.getAccessToken(),
});

View file

@ -188,9 +188,9 @@
{ href: '/feedback', label: 'Feedback', icon: 'chat' },
];
// Navigation items filtered by visibility settings
// Navigation items filtered by visibility settings (with fallback for guest mode)
const navItems = $derived(
filterHiddenNavItems('clock', baseNavItems, userSettings.nav.hiddenNavItems)
filterHiddenNavItems('clock', baseNavItems, userSettings.nav?.hiddenNavItems || {})
);
// Navigation shortcuts (Ctrl+1-9) - use base items for consistent shortcuts
@ -309,7 +309,7 @@
currentPath={$page.url.pathname}
appName="Clock"
homeRoute="/"
desktopPosition={userSettings.nav.desktopPosition}
desktopPosition={userSettings.nav?.desktopPosition || 'bottom'}
onToggleTheme={handleToggleTheme}
{isDark}
{isSidebarMode}