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

@ -302,9 +302,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('calendar', baseNavItems, userSettings.nav.hiddenNavItems)
filterHiddenNavItems('calendar', baseNavItems, userSettings.nav?.hiddenNavItems || {})
);
// Active tab based on sidebar state: 'tasks' when sidebar is open, 'calendar' when closed

View file

@ -100,9 +100,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('chat', baseNavItems, userSettings.nav.hiddenNavItems)
filterHiddenNavItems('chat', baseNavItems, userSettings.nav?.hiddenNavItems || {})
);
// User email for user dropdown
@ -244,7 +244,7 @@
onModeChange={handleModeChange}
{isCollapsed}
onCollapsedChange={handleCollapsedChange}
desktopPosition={userSettings.nav.desktopPosition}
desktopPosition={userSettings.nav?.desktopPosition || 'bottom'}
showThemeToggle={true}
showThemeVariants={true}
{themeVariantItems}

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}

View file

@ -145,9 +145,9 @@
{ href: '/help', label: 'Hilfe', icon: 'help-circle' },
];
// Navigation items filtered by visibility settings
// Navigation items filtered by visibility settings (with fallback for guest mode)
const navItems = $derived(
filterHiddenNavItems('contacts', baseNavItems, userSettings.nav.hiddenNavItems)
filterHiddenNavItems('contacts', baseNavItems, userSettings.nav?.hiddenNavItems || {})
);
// Navigation shortcuts (Ctrl+1-5) - use base items for consistent shortcuts

View file

@ -212,7 +212,7 @@
onModeChange={handleModeChange}
{isCollapsed}
onCollapsedChange={handleCollapsedChange}
desktopPosition={userSettings.nav.desktopPosition}
desktopPosition={userSettings.nav?.desktopPosition || 'bottom'}
showThemeToggle={true}
showThemeVariants={true}
{themeVariantItems}

View file

@ -41,9 +41,9 @@
{ href: '/progress', label: 'Progress', icon: 'chart' },
];
// Navigation items filtered by visibility settings
// Navigation items filtered by visibility settings (with fallback for guest mode)
const navItems = $derived(
filterHiddenNavItems('manadeck', baseNavItems, userSettings.nav.hiddenNavItems)
filterHiddenNavItems('manadeck', baseNavItems, userSettings.nav?.hiddenNavItems || {})
);
// Get pinned themes from user settings (extended themes only)
@ -208,7 +208,7 @@
onModeChange={handleModeChange}
{isCollapsed}
onCollapsedChange={handleCollapsedChange}
desktopPosition={userSettings.nav.desktopPosition}
desktopPosition={userSettings.nav?.desktopPosition || 'bottom'}
showThemeToggle={true}
showThemeVariants={true}
{themeVariantItems}

View file

@ -105,9 +105,9 @@
{ href: '/app/archive', label: 'Archiv', icon: 'archive' },
];
// Navigation items filtered by visibility settings
// Navigation items filtered by visibility settings (with fallback for guest mode)
const navItems = $derived(
filterHiddenNavItems('picture', baseNavItems, userSettings.nav.hiddenNavItems)
filterHiddenNavItems('picture', baseNavItems, userSettings.nav?.hiddenNavItems || {})
);
// View mode options for tab group
@ -267,7 +267,7 @@
onModeChange={handleModeChange}
{isCollapsed}
onCollapsedChange={handleCollapsedChange}
desktopPosition={userSettings.nav.desktopPosition}
desktopPosition={userSettings.nav?.desktopPosition || 'bottom'}
showThemeToggle={true}
showThemeVariants={true}
{themeVariantItems}

View file

@ -177,8 +177,8 @@
// Navigation items (base items + dynamic label items in sidebar mode, filtered by visibility settings)
const navItems = $derived.by(() => {
// Start with base items, filter out hidden ones
let items = filterHiddenNavItems('todo', baseNavItems, userSettings.nav.hiddenNavItems);
// Start with base items, filter out hidden ones (with fallback for guest mode)
let items = filterHiddenNavItems('todo', baseNavItems, userSettings.nav?.hiddenNavItems || {});
// In sidebar mode, add tags as sub-items if available
if (isSidebarMode && labelsStore.labels.length > 0) {
@ -408,7 +408,7 @@
onModeChange={handleModeChange}
{isCollapsed}
onCollapsedChange={handleCollapsedChange}
desktopPosition={userSettings.nav.desktopPosition}
desktopPosition={userSettings.nav?.desktopPosition || 'bottom'}
showThemeToggle={true}
showThemeVariants={true}
{themeVariantItems}