mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-17 05:59:39 +02:00
refactor(calendar): simplify settings from 41 to 18 persisted preferences
Remove unused header/dateStrip settings (hardcode defaults), move runtime UI state (sidebar, tags, immersive mode) to non-persisted $state() variables, and add localStorage migration for existing users. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
98d1d1cc90
commit
93e1c7de4a
9 changed files with 278 additions and 453 deletions
|
|
@ -1,67 +1,26 @@
|
|||
<script lang="ts">
|
||||
import { viewStore } from '$lib/stores/view.svelte';
|
||||
import { settingsStore } from '$lib/stores/settings.svelte';
|
||||
import { format } from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
// Get weekday format string based on setting
|
||||
function getWeekdayFormat(): string {
|
||||
switch (settingsStore.headerWeekdayFormat) {
|
||||
case 'full':
|
||||
return 'EEEE';
|
||||
case 'short':
|
||||
return 'EEE';
|
||||
case 'hidden':
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
// Get date format string based on settings
|
||||
function getDateFormat(includeYear: boolean = true): string {
|
||||
const parts: string[] = [];
|
||||
|
||||
// Weekday
|
||||
const weekdayFormat = getWeekdayFormat();
|
||||
if (weekdayFormat) {
|
||||
parts.push(weekdayFormat);
|
||||
}
|
||||
|
||||
// Date with optional month
|
||||
if (settingsStore.headerShowDate) {
|
||||
if (settingsStore.headerAlwaysShowMonth) {
|
||||
parts.push(includeYear ? 'd.M. MMMM yyyy' : 'd.M.');
|
||||
} else {
|
||||
parts.push(includeYear ? 'd. MMMM yyyy' : 'd.');
|
||||
}
|
||||
} else if (includeYear) {
|
||||
// Only month and year if date is hidden
|
||||
parts.push('MMMM yyyy');
|
||||
}
|
||||
|
||||
return parts.join(', ');
|
||||
}
|
||||
|
||||
// Format title based on view type and settings
|
||||
// Format title based on view type
|
||||
let title = $derived.by(() => {
|
||||
const date = viewStore.currentDate;
|
||||
const rangeStart = viewStore.viewRange.start;
|
||||
const rangeEnd = viewStore.viewRange.end;
|
||||
|
||||
// Helper to format date range
|
||||
const formatRange = () => {
|
||||
const showMonth = settingsStore.headerAlwaysShowMonth;
|
||||
const startFormat = showMonth ? 'd.M.' : 'd.';
|
||||
|
||||
if (rangeStart.getMonth() === rangeEnd.getMonth()) {
|
||||
return (
|
||||
format(rangeStart, startFormat, { locale: de }) +
|
||||
format(rangeStart, 'd.', { locale: de }) +
|
||||
' - ' +
|
||||
format(rangeEnd, showMonth ? 'd.M. MMMM yyyy' : 'd. MMMM yyyy', { locale: de })
|
||||
format(rangeEnd, 'd. MMMM yyyy', { locale: de })
|
||||
);
|
||||
}
|
||||
return (
|
||||
format(rangeStart, showMonth ? 'd.M. MMM' : 'd. MMM', { locale: de }) +
|
||||
format(rangeStart, 'd. MMM', { locale: de }) +
|
||||
' - ' +
|
||||
format(rangeEnd, showMonth ? 'd.M. MMM yyyy' : 'd. MMM yyyy', { locale: de })
|
||||
format(rangeEnd, 'd. MMM yyyy', { locale: de })
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -78,7 +37,7 @@
|
|||
});
|
||||
</script>
|
||||
|
||||
<header class="calendar-header" class:compact={settingsStore.headerCompact} role="banner">
|
||||
<header class="calendar-header" role="banner">
|
||||
<h1 class="header-title" aria-live="polite">{title}</h1>
|
||||
</header>
|
||||
|
||||
|
|
@ -101,19 +60,4 @@
|
|||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compact variant */
|
||||
.calendar-header.compact {
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.calendar-header.compact .header-title {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.calendar-header.compact .header-title {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@
|
|||
subDays,
|
||||
startOfDay,
|
||||
isWithinInterval,
|
||||
getWeek,
|
||||
startOfWeek,
|
||||
} from 'date-fns';
|
||||
import { de } from 'date-fns/locale';
|
||||
import { onMount, tick } from 'svelte';
|
||||
|
|
@ -64,17 +62,6 @@
|
|||
return { significant: false, emoji: '' };
|
||||
}
|
||||
|
||||
// Check if a date is the first day of the week (respects weekStartsOn setting)
|
||||
function isFirstDayOfWeek(date: Date): boolean {
|
||||
const weekStart = startOfWeek(date, { weekStartsOn: settingsStore.weekStartsOn });
|
||||
return isSameDay(date, weekStart);
|
||||
}
|
||||
|
||||
// Get week number for a date
|
||||
function getWeekNumber(date: Date): number {
|
||||
return getWeek(date, { weekStartsOn: settingsStore.weekStartsOn });
|
||||
}
|
||||
|
||||
// Reactive view range - needed to trigger re-renders
|
||||
let viewRange = $derived(viewStore.viewRange);
|
||||
let currentDate = $derived(viewStore.currentDate);
|
||||
|
|
@ -264,12 +251,8 @@
|
|||
{@const isFirstOfMonth = day.getDate() === 1}
|
||||
{@const moonPhase = isSignificantMoonPhase(day)}
|
||||
{@const eventCount = getEventCount(day)}
|
||||
{@const showWeekNumber = settingsStore.dateStripShowWeekNumbers && isFirstDayOfWeek(day)}
|
||||
{#if isFirstOfMonth}
|
||||
<div
|
||||
class="month-divider"
|
||||
class:show-line={settingsStore.dateStripShowMonthDividers}
|
||||
></div>
|
||||
<div class="month-divider show-line"></div>
|
||||
{/if}
|
||||
<button
|
||||
class="day-item"
|
||||
|
|
@ -283,15 +266,10 @@
|
|||
onclick={() => handleDayClick(day)}
|
||||
class:is-today={dayIsToday}
|
||||
>
|
||||
{#if showWeekNumber}
|
||||
<span class="week-number-label">KW {getWeekNumber(day)}</span>
|
||||
{/if}
|
||||
{#if moonPhase.significant && settingsStore.dateStripShowMoonPhases}
|
||||
<span class="moon-indicator">{moonPhase.emoji}</span>
|
||||
{/if}
|
||||
{#if settingsStore.dateStripShowWeekday}
|
||||
<span class="day-weekday">{format(day, 'EE', { locale: de })}</span>
|
||||
{/if}
|
||||
<span class="day-weekday">{format(day, 'EE', { locale: de })}</span>
|
||||
<span class="day-number">{format(day, 'd')}</span>
|
||||
{#if eventCount > 0 && settingsStore.dateStripShowEventIndicators}
|
||||
<div class="event-dots">
|
||||
|
|
@ -463,20 +441,6 @@
|
|||
line-height: 1;
|
||||
}
|
||||
|
||||
.week-number-label {
|
||||
position: absolute;
|
||||
top: -14px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 0.5625rem;
|
||||
font-weight: 600;
|
||||
color: hsl(var(--color-muted-foreground));
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
|
||||
.event-dots {
|
||||
display: flex;
|
||||
gap: 2px;
|
||||
|
|
@ -611,11 +575,6 @@
|
|||
top: -14px;
|
||||
}
|
||||
|
||||
.week-number-label {
|
||||
top: -12px;
|
||||
font-size: 0.5rem;
|
||||
}
|
||||
|
||||
.day-number {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
|
@ -662,11 +621,6 @@
|
|||
top: -12px;
|
||||
}
|
||||
|
||||
.date-strip-wrapper.compact .week-number-label {
|
||||
top: -10px;
|
||||
font-size: 0.5rem;
|
||||
}
|
||||
|
||||
.date-strip-wrapper.compact .month-divider {
|
||||
height: 28px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
let { isToolbarExpanded = false, isMobile = false }: Props = $props();
|
||||
|
||||
function handleClick() {
|
||||
settingsStore.set('dateStripCollapsed', false);
|
||||
settingsStore.setDateStripCollapsed(false);
|
||||
}
|
||||
|
||||
// Format current date for FAB display: "Dez 14"
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@
|
|||
{/if}
|
||||
|
||||
<!-- Layer 1: DateStrip -->
|
||||
{#if unifiedBarStore.showDateStrip && !unifiedBarStore.legacyDateStripCollapsed}
|
||||
{#if unifiedBarStore.showDateStrip}
|
||||
<div
|
||||
class="unified-bar-layer date-strip-layer"
|
||||
style="z-index: {layerZIndices.date}; bottom: 70px;"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { viewStore } from '$lib/stores/view.svelte';
|
||||
import { settingsStore } from '$lib/stores/settings.svelte';
|
||||
import type { CalendarViewType } from '@calendar/shared';
|
||||
interface Props {
|
||||
isToolbarExpanded?: boolean;
|
||||
|
|
@ -13,13 +12,6 @@
|
|||
viewStore.setViewType(view);
|
||||
}
|
||||
|
||||
// View labels (short versions for pill)
|
||||
const viewLabels: Record<CalendarViewType, string> = {
|
||||
week: '7',
|
||||
month: 'M',
|
||||
agenda: 'A',
|
||||
};
|
||||
|
||||
// View titles for tooltip
|
||||
const viewTitles: Record<CalendarViewType, string> = {
|
||||
week: 'Wochenansicht',
|
||||
|
|
@ -27,8 +19,7 @@
|
|||
agenda: 'Agenda',
|
||||
};
|
||||
|
||||
// Get enabled views from settings
|
||||
let enabledViews = $derived(settingsStore.quickViewPillViews);
|
||||
const enabledViews: CalendarViewType[] = ['week', 'month', 'agenda'];
|
||||
</script>
|
||||
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
<script lang="ts">
|
||||
import { viewStore } from '$lib/stores/view.svelte';
|
||||
import { settingsStore } from '$lib/stores/settings.svelte';
|
||||
import type { CalendarViewType } from '@calendar/shared';
|
||||
interface Props {
|
||||
/** Bottom offset from viewport bottom (default: '70px') */
|
||||
|
|
@ -27,15 +26,12 @@
|
|||
agenda: 'Agenda',
|
||||
};
|
||||
|
||||
// Get enabled views from settings
|
||||
let enabledViews = $derived(settingsStore.quickViewPillViews);
|
||||
const enabledViews: CalendarViewType[] = ['week', 'month', 'agenda'];
|
||||
|
||||
// Get label for a view
|
||||
function getViewLabel(view: CalendarViewType): string {
|
||||
return viewLabels[view] || '';
|
||||
}
|
||||
|
||||
// Get title for a view
|
||||
function getViewTitle(view: CalendarViewType): string {
|
||||
return viewTitles[view] || '';
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import { userSettings } from './user-settings.svelte';
|
|||
export type WeekStartDay = 0 | 1;
|
||||
export type TimeFormat = '24h' | '12h';
|
||||
export type AllDayDisplayMode = 'header' | 'block';
|
||||
export type WeekdayFormat = 'full' | 'short' | 'hidden';
|
||||
export type SttLanguage = 'de' | 'auto';
|
||||
|
||||
export interface CalendarAppSettings extends Record<string, unknown> {
|
||||
|
|
@ -27,42 +26,13 @@ export interface CalendarAppSettings extends Record<string, unknown> {
|
|||
dayEndHour: number;
|
||||
allDayDisplayMode: AllDayDisplayMode;
|
||||
|
||||
// Header settings
|
||||
headerCompact: boolean;
|
||||
headerWeekdayFormat: WeekdayFormat;
|
||||
headerShowDate: boolean;
|
||||
headerAlwaysShowMonth: boolean;
|
||||
|
||||
// DateStrip settings
|
||||
dateStripShowMoonPhases: boolean;
|
||||
dateStripShowEventIndicators: boolean;
|
||||
dateStripShowWeekday: boolean;
|
||||
dateStripHighlightWeekends: boolean;
|
||||
dateStripShowMonthDividers: boolean;
|
||||
dateStripCompact: boolean;
|
||||
dateStripShowWeekNumbers: boolean;
|
||||
dateStripCollapsed: boolean;
|
||||
|
||||
// TagStrip settings
|
||||
tagStripCollapsed: boolean;
|
||||
selectedTagIds: string[];
|
||||
|
||||
// Immersive Mode settings
|
||||
immersiveModeEnabled: boolean;
|
||||
|
||||
// Birthday settings
|
||||
// Display settings
|
||||
showBirthdays: boolean;
|
||||
showBirthdayAge: boolean;
|
||||
|
||||
// Task settings
|
||||
showTasksInCalendar: boolean;
|
||||
|
||||
// UI settings
|
||||
sidebarCollapsed: boolean;
|
||||
|
||||
// Quick View Pill settings
|
||||
quickViewPillViews: CalendarViewType[];
|
||||
customDayCount: number;
|
||||
dateStripShowMoonPhases: boolean;
|
||||
dateStripShowEventIndicators: boolean;
|
||||
dateStripHighlightWeekends: boolean;
|
||||
dateStripCompact: boolean;
|
||||
|
||||
// Event defaults
|
||||
defaultEventDuration: number;
|
||||
|
|
@ -72,6 +42,14 @@ export interface CalendarAppSettings extends Record<string, unknown> {
|
|||
sttLanguage: SttLanguage;
|
||||
}
|
||||
|
||||
// --- UI state (not persisted, resets on page load) ---
|
||||
let _dateStripCollapsed = $state(false);
|
||||
let _tagStripCollapsed = $state(true);
|
||||
let _selectedTagIds = $state<string[]>([]);
|
||||
let _immersiveModeEnabled = $state(false);
|
||||
let _showTasksInCalendar = $state(false);
|
||||
let _sidebarCollapsed = $state(true);
|
||||
|
||||
const DEFAULT_SETTINGS: CalendarAppSettings = {
|
||||
defaultView: 'week',
|
||||
weekStartsOn: 1,
|
||||
|
|
@ -82,27 +60,12 @@ const DEFAULT_SETTINGS: CalendarAppSettings = {
|
|||
dayStartHour: 6,
|
||||
dayEndHour: 20,
|
||||
allDayDisplayMode: 'header',
|
||||
headerCompact: false,
|
||||
headerWeekdayFormat: 'full',
|
||||
headerShowDate: true,
|
||||
headerAlwaysShowMonth: false,
|
||||
dateStripShowMoonPhases: true,
|
||||
dateStripShowEventIndicators: true,
|
||||
dateStripShowWeekday: true,
|
||||
dateStripHighlightWeekends: true,
|
||||
dateStripShowMonthDividers: true,
|
||||
dateStripCompact: false,
|
||||
dateStripShowWeekNumbers: false,
|
||||
dateStripCollapsed: false,
|
||||
tagStripCollapsed: true,
|
||||
selectedTagIds: [],
|
||||
immersiveModeEnabled: false,
|
||||
showBirthdays: true,
|
||||
showBirthdayAge: true,
|
||||
showTasksInCalendar: false,
|
||||
sidebarCollapsed: true,
|
||||
quickViewPillViews: ['week', 'month', 'agenda'],
|
||||
customDayCount: 30,
|
||||
defaultEventDuration: 60,
|
||||
defaultReminder: 15,
|
||||
sttLanguage: 'de',
|
||||
|
|
@ -131,11 +94,43 @@ const baseStore = createAppSettingsStore<CalendarAppSettings>(
|
|||
}
|
||||
);
|
||||
|
||||
// Always start with tasks/sidebar hidden when the app loads
|
||||
// (don't persist this from previous sessions)
|
||||
// Migrate: strip removed keys from localStorage to keep it clean
|
||||
if (browser) {
|
||||
baseStore.set('showTasksInCalendar', false);
|
||||
baseStore.set('sidebarCollapsed', true);
|
||||
try {
|
||||
const raw = localStorage.getItem('calendar-settings');
|
||||
if (raw) {
|
||||
const stored = JSON.parse(raw);
|
||||
const removedKeys = [
|
||||
'headerCompact',
|
||||
'headerWeekdayFormat',
|
||||
'headerShowDate',
|
||||
'headerAlwaysShowMonth',
|
||||
'dateStripShowWeekday',
|
||||
'dateStripShowMonthDividers',
|
||||
'dateStripShowWeekNumbers',
|
||||
'dateStripCollapsed',
|
||||
'tagStripCollapsed',
|
||||
'selectedTagIds',
|
||||
'immersiveModeEnabled',
|
||||
'showTasksInCalendar',
|
||||
'sidebarCollapsed',
|
||||
'quickViewPillViews',
|
||||
'customDayCount',
|
||||
];
|
||||
let changed = false;
|
||||
for (const key of removedKeys) {
|
||||
if (key in stored) {
|
||||
delete stored[key];
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
if (changed) {
|
||||
localStorage.setItem('calendar-settings', JSON.stringify(stored));
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// ignore migration errors
|
||||
}
|
||||
}
|
||||
|
||||
// Load settings from cloud
|
||||
|
|
@ -158,9 +153,8 @@ export const settingsStore = {
|
|||
update: baseStore.update,
|
||||
reset: baseStore.reset,
|
||||
getDefaults: baseStore.getDefaults,
|
||||
toggleImmersiveMode: baseStore.toggleImmersiveMode,
|
||||
|
||||
// Convenience getters
|
||||
// Persisted preference getters
|
||||
get defaultView() {
|
||||
return baseStore.settings.defaultView;
|
||||
},
|
||||
|
|
@ -188,78 +182,30 @@ export const settingsStore = {
|
|||
get allDayDisplayMode() {
|
||||
return baseStore.settings.allDayDisplayMode;
|
||||
},
|
||||
get headerCompact() {
|
||||
return baseStore.settings.headerCompact;
|
||||
},
|
||||
get headerWeekdayFormat() {
|
||||
return baseStore.settings.headerWeekdayFormat;
|
||||
},
|
||||
get headerShowDate() {
|
||||
return baseStore.settings.headerShowDate;
|
||||
},
|
||||
get headerAlwaysShowMonth() {
|
||||
return baseStore.settings.headerAlwaysShowMonth;
|
||||
},
|
||||
get dateStripShowMoonPhases() {
|
||||
return baseStore.settings.dateStripShowMoonPhases;
|
||||
},
|
||||
get dateStripShowEventIndicators() {
|
||||
return baseStore.settings.dateStripShowEventIndicators;
|
||||
},
|
||||
get dateStripShowWeekday() {
|
||||
return baseStore.settings.dateStripShowWeekday;
|
||||
},
|
||||
get dateStripHighlightWeekends() {
|
||||
return baseStore.settings.dateStripHighlightWeekends;
|
||||
},
|
||||
get dateStripShowMonthDividers() {
|
||||
return baseStore.settings.dateStripShowMonthDividers;
|
||||
},
|
||||
get dateStripCompact() {
|
||||
return baseStore.settings.dateStripCompact;
|
||||
},
|
||||
get dateStripShowWeekNumbers() {
|
||||
return baseStore.settings.dateStripShowWeekNumbers;
|
||||
},
|
||||
get dateStripCollapsed() {
|
||||
return baseStore.settings.dateStripCollapsed;
|
||||
},
|
||||
get tagStripCollapsed() {
|
||||
return baseStore.settings.tagStripCollapsed;
|
||||
},
|
||||
get selectedTagIds() {
|
||||
return baseStore.settings.selectedTagIds;
|
||||
},
|
||||
get hasSelectedTags() {
|
||||
return baseStore.settings.selectedTagIds.length > 0;
|
||||
},
|
||||
get immersiveModeEnabled() {
|
||||
return baseStore.settings.immersiveModeEnabled;
|
||||
},
|
||||
get showBirthdays() {
|
||||
return baseStore.settings.showBirthdays;
|
||||
},
|
||||
get showBirthdayAge() {
|
||||
return baseStore.settings.showBirthdayAge;
|
||||
},
|
||||
get showTasksInCalendar() {
|
||||
return baseStore.settings.showTasksInCalendar;
|
||||
},
|
||||
get defaultEventDuration() {
|
||||
return baseStore.settings.defaultEventDuration;
|
||||
},
|
||||
get defaultReminder() {
|
||||
return baseStore.settings.defaultReminder;
|
||||
},
|
||||
get sidebarCollapsed() {
|
||||
return baseStore.settings.sidebarCollapsed;
|
||||
},
|
||||
get quickViewPillViews() {
|
||||
return baseStore.settings.quickViewPillViews;
|
||||
},
|
||||
get customDayCount() {
|
||||
return baseStore.settings.customDayCount;
|
||||
},
|
||||
get sttLanguage() {
|
||||
return baseStore.settings.sttLanguage;
|
||||
},
|
||||
|
|
@ -267,20 +213,36 @@ export const settingsStore = {
|
|||
return cloudSyncEnabled;
|
||||
},
|
||||
|
||||
// --- UI state getters (non-persisted) ---
|
||||
get dateStripCollapsed() {
|
||||
return _dateStripCollapsed;
|
||||
},
|
||||
get tagStripCollapsed() {
|
||||
return _tagStripCollapsed;
|
||||
},
|
||||
get selectedTagIds() {
|
||||
return _selectedTagIds;
|
||||
},
|
||||
get hasSelectedTags() {
|
||||
return _selectedTagIds.length > 0;
|
||||
},
|
||||
get immersiveModeEnabled() {
|
||||
return _immersiveModeEnabled;
|
||||
},
|
||||
get showTasksInCalendar() {
|
||||
return _showTasksInCalendar;
|
||||
},
|
||||
get sidebarCollapsed() {
|
||||
return _sidebarCollapsed;
|
||||
},
|
||||
|
||||
// Cloud sync methods
|
||||
enableCloudSync() {
|
||||
cloudSyncEnabled = true;
|
||||
if (!initialSyncDone) {
|
||||
const cloudSettings = loadFromCloud();
|
||||
if (cloudSettings && Object.keys(cloudSettings).length > 0) {
|
||||
// Exclude showTasksInCalendar and sidebarCollapsed from cloud sync
|
||||
// - always start with tasks hidden and sidebar collapsed
|
||||
const {
|
||||
showTasksInCalendar: _,
|
||||
sidebarCollapsed: __,
|
||||
...settingsWithoutTasks
|
||||
} = cloudSettings;
|
||||
baseStore.update(settingsWithoutTasks);
|
||||
baseStore.update(cloudSettings);
|
||||
} else {
|
||||
syncToCloud(baseStore.settings);
|
||||
}
|
||||
|
|
@ -292,32 +254,40 @@ export const settingsStore = {
|
|||
cloudSyncEnabled = false;
|
||||
},
|
||||
|
||||
// Calendar-specific toggle methods
|
||||
// UI state toggle methods (non-persisted)
|
||||
toggleSidebar() {
|
||||
baseStore.set('sidebarCollapsed', !baseStore.settings.sidebarCollapsed);
|
||||
_sidebarCollapsed = !_sidebarCollapsed;
|
||||
},
|
||||
|
||||
toggleTagStrip() {
|
||||
baseStore.set('tagStripCollapsed', !baseStore.settings.tagStripCollapsed);
|
||||
_tagStripCollapsed = !_tagStripCollapsed;
|
||||
},
|
||||
|
||||
toggleTagSelection(tagId: string) {
|
||||
const currentIds = baseStore.settings.selectedTagIds;
|
||||
const isSelected = currentIds.includes(tagId);
|
||||
const newIds = isSelected ? currentIds.filter((id) => id !== tagId) : [...currentIds, tagId];
|
||||
baseStore.set('selectedTagIds', newIds);
|
||||
const isSelected = _selectedTagIds.includes(tagId);
|
||||
_selectedTagIds = isSelected
|
||||
? _selectedTagIds.filter((id) => id !== tagId)
|
||||
: [..._selectedTagIds, tagId];
|
||||
},
|
||||
|
||||
isTagSelected(tagId: string): boolean {
|
||||
return baseStore.settings.selectedTagIds.includes(tagId);
|
||||
return _selectedTagIds.includes(tagId);
|
||||
},
|
||||
|
||||
clearTagSelection() {
|
||||
baseStore.set('selectedTagIds', []);
|
||||
_selectedTagIds = [];
|
||||
},
|
||||
|
||||
toggleTasksInCalendar() {
|
||||
baseStore.set('showTasksInCalendar', !baseStore.settings.showTasksInCalendar);
|
||||
_showTasksInCalendar = !_showTasksInCalendar;
|
||||
},
|
||||
|
||||
toggleImmersiveMode() {
|
||||
_immersiveModeEnabled = !_immersiveModeEnabled;
|
||||
},
|
||||
|
||||
setDateStripCollapsed(value: boolean) {
|
||||
_dateStripCollapsed = value;
|
||||
},
|
||||
|
||||
// Time formatting helpers
|
||||
|
|
|
|||
|
|
@ -13,55 +13,55 @@ export type UnifiedBarLayer = 'input' | 'date' | 'tag' | 'toolbar' | 'settings';
|
|||
|
||||
// Store interface
|
||||
export interface UnifiedBarSettings extends Record<string, unknown> {
|
||||
// UnifiedBar mode control
|
||||
unifiedBarMode: UnifiedBarMode;
|
||||
unifiedBarActiveLayer: UnifiedBarLayer;
|
||||
// UnifiedBar mode control
|
||||
unifiedBarMode: UnifiedBarMode;
|
||||
unifiedBarActiveLayer: UnifiedBarLayer;
|
||||
|
||||
// Legacy compatibility (keep existing settings working)
|
||||
dateStripCollapsed: boolean;
|
||||
tagStripCollapsed: boolean;
|
||||
calendarToolbarCollapsed: boolean;
|
||||
// Legacy compatibility (keep existing settings working)
|
||||
dateStripCollapsed: boolean;
|
||||
tagStripCollapsed: boolean;
|
||||
calendarToolbarCollapsed: boolean;
|
||||
|
||||
// New UnifiedBar-specific settings
|
||||
showQuickInput: boolean;
|
||||
showDateStrip: boolean;
|
||||
showTagStrip: boolean;
|
||||
showCalendarToolbar: boolean;
|
||||
overlayMenuOpen: boolean;
|
||||
// New UnifiedBar-specific settings
|
||||
showQuickInput: boolean;
|
||||
showDateStrip: boolean;
|
||||
showTagStrip: boolean;
|
||||
showCalendarToolbar: boolean;
|
||||
overlayMenuOpen: boolean;
|
||||
|
||||
// Animation and interaction preferences
|
||||
barAnimationDuration: number;
|
||||
enableHapticFeedback: boolean;
|
||||
autoCollapseBars: boolean;
|
||||
// Animation and interaction preferences
|
||||
barAnimationDuration: number;
|
||||
enableHapticFeedback: boolean;
|
||||
autoCollapseBars: boolean;
|
||||
|
||||
// Quick access toggles
|
||||
quickAccessActions: string[];
|
||||
// Quick access toggles
|
||||
quickAccessActions: string[];
|
||||
}
|
||||
|
||||
const DEFAULT_SETTINGS: UnifiedBarSettings = {
|
||||
// Default to collapsed mode with only input bar visible
|
||||
unifiedBarMode: 'collapsed',
|
||||
unifiedBarActiveLayer: 'input',
|
||||
// Default to collapsed mode with only input bar visible
|
||||
unifiedBarMode: 'collapsed',
|
||||
unifiedBarActiveLayer: 'input',
|
||||
|
||||
// Legacy compatibility
|
||||
dateStripCollapsed: false,
|
||||
tagStripCollapsed: true,
|
||||
calendarToolbarCollapsed: true,
|
||||
// Legacy compatibility
|
||||
dateStripCollapsed: false,
|
||||
tagStripCollapsed: true,
|
||||
calendarToolbarCollapsed: true,
|
||||
|
||||
// New settings
|
||||
showQuickInput: true,
|
||||
showDateStrip: true,
|
||||
showTagStrip: false,
|
||||
showCalendarToolbar: false,
|
||||
overlayMenuOpen: false,
|
||||
// New settings
|
||||
showQuickInput: true,
|
||||
showDateStrip: true,
|
||||
showTagStrip: false,
|
||||
showCalendarToolbar: false,
|
||||
overlayMenuOpen: false,
|
||||
|
||||
// Interaction preferences
|
||||
barAnimationDuration: 300,
|
||||
enableHapticFeedback: true,
|
||||
autoCollapseBars: false,
|
||||
// Interaction preferences
|
||||
barAnimationDuration: 300,
|
||||
enableHapticFeedback: true,
|
||||
autoCollapseBars: false,
|
||||
|
||||
// Quick actions
|
||||
quickAccessActions: ['new-event', 'search', 'today', 'calendar-toggle'],
|
||||
// Quick actions
|
||||
quickAccessActions: ['new-event', 'search', 'today', 'calendar-toggle'],
|
||||
};
|
||||
|
||||
// Cloud sync state
|
||||
|
|
@ -70,197 +70,169 @@ let initialSyncDone = $state(false);
|
|||
|
||||
// Sync to cloud callback
|
||||
async function syncToCloud(settings: UnifiedBarSettings) {
|
||||
if (!cloudSyncEnabled || typeof window === 'undefined') return;
|
||||
try {
|
||||
await userSettings.updateDeviceAppSettings(settings as unknown as Record<string, unknown>);
|
||||
} catch (e) {
|
||||
console.error('Failed to sync unified bar settings to cloud:', e);
|
||||
}
|
||||
if (!cloudSyncEnabled || typeof window === 'undefined') return;
|
||||
try {
|
||||
await userSettings.updateDeviceAppSettings(settings as unknown as Record<string, unknown>);
|
||||
} catch (e) {
|
||||
console.error('Failed to sync unified bar settings to cloud:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// Create base store
|
||||
const baseStore = createAppSettingsStore<UnifiedBarSettings>(
|
||||
'unified-bar-settings',
|
||||
DEFAULT_SETTINGS,
|
||||
{
|
||||
onSettingsChange: syncToCloud,
|
||||
}
|
||||
'unified-bar-settings',
|
||||
DEFAULT_SETTINGS,
|
||||
{
|
||||
onSettingsChange: syncToCloud,
|
||||
}
|
||||
);
|
||||
|
||||
// Load settings from cloud
|
||||
function loadFromCloud(): Partial<UnifiedBarSettings> | null {
|
||||
if (!userSettings.loaded) return null;
|
||||
const cloudSettings = userSettings.currentDeviceAppSettings;
|
||||
if (cloudSettings && Object.keys(cloudSettings).length > 0) {
|
||||
return cloudSettings as unknown as Partial<UnifiedBarSettings>;
|
||||
}
|
||||
return null;
|
||||
if (!userSettings.loaded) return null;
|
||||
const cloudSettings = userSettings.currentDeviceAppSettings;
|
||||
if (cloudSettings && Object.keys(cloudSettings).length > 0) {
|
||||
return cloudSettings as unknown as Partial<UnifiedBarSettings>;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export const unifiedBarStore = {
|
||||
// Base store methods
|
||||
get settings() {
|
||||
return baseStore.settings;
|
||||
},
|
||||
initialize: baseStore.initialize,
|
||||
set: baseStore.set,
|
||||
update: baseStore.update,
|
||||
reset: baseStore.reset,
|
||||
getDefaults: baseStore.getDefaults,
|
||||
// Base store methods
|
||||
get settings() {
|
||||
return baseStore.settings;
|
||||
},
|
||||
initialize: baseStore.initialize,
|
||||
set: baseStore.set,
|
||||
update: baseStore.update,
|
||||
reset: baseStore.reset,
|
||||
getDefaults: baseStore.getDefaults,
|
||||
|
||||
// Mode management
|
||||
get mode() {
|
||||
return baseStore.settings.unifiedBarMode;
|
||||
},
|
||||
// Mode management
|
||||
get mode() {
|
||||
return baseStore.settings.unifiedBarMode;
|
||||
},
|
||||
|
||||
get activeLayer() {
|
||||
return baseStore.settings.unifiedBarActiveLayer;
|
||||
},
|
||||
get activeLayer() {
|
||||
return baseStore.settings.unifiedBarActiveLayer;
|
||||
},
|
||||
|
||||
get isOverlayOpen() {
|
||||
return baseStore.settings.overlayMenuOpen;
|
||||
},
|
||||
get isOverlayOpen() {
|
||||
return baseStore.settings.overlayMenuOpen;
|
||||
},
|
||||
|
||||
// Layer visibility helpers
|
||||
get showQuickInput() {
|
||||
return baseStore.settings.showQuickInput;
|
||||
},
|
||||
// Layer visibility helpers
|
||||
get showQuickInput() {
|
||||
return baseStore.settings.showQuickInput;
|
||||
},
|
||||
|
||||
get showDateStrip() {
|
||||
return baseStore.settings.showDateStrip && !baseStore.settings.dateStripCollapsed;
|
||||
},
|
||||
get showDateStrip() {
|
||||
return baseStore.settings.showDateStrip && !baseStore.settings.dateStripCollapsed;
|
||||
},
|
||||
|
||||
get showTagStrip() {
|
||||
return baseStore.settings.showTagStrip && !baseStore.settings.tagStripCollapsed;
|
||||
},
|
||||
get showTagStrip() {
|
||||
return baseStore.settings.showTagStrip && !baseStore.settings.tagStripCollapsed;
|
||||
},
|
||||
|
||||
get showCalendarToolbar() {
|
||||
return baseStore.settings.showCalendarToolbar && !baseStore.settings.calendarToolbarCollapsed;
|
||||
},
|
||||
get showCalendarToolbar() {
|
||||
return baseStore.settings.showCalendarToolbar && !baseStore.settings.calendarToolbarCollapsed;
|
||||
},
|
||||
|
||||
// Mode switching
|
||||
setMode(mode: UnifiedBarMode) {
|
||||
baseStore.set('unifiedBarMode', mode);
|
||||
},
|
||||
// Mode switching
|
||||
setMode(mode: UnifiedBarMode) {
|
||||
baseStore.set('unifiedBarMode', mode);
|
||||
},
|
||||
|
||||
toggleOverlay() {
|
||||
const isOpen = baseStore.settings.overlayMenuOpen;
|
||||
baseStore.set('overlayMenuOpen', !isOpen);
|
||||
if (!isOpen) {
|
||||
baseStore.set('unifiedBarMode', 'overlay');
|
||||
} else {
|
||||
baseStore.set('unifiedBarMode', 'collapsed');
|
||||
}
|
||||
},
|
||||
toggleOverlay() {
|
||||
const isOpen = baseStore.settings.overlayMenuOpen;
|
||||
baseStore.set('overlayMenuOpen', !isOpen);
|
||||
if (!isOpen) {
|
||||
baseStore.set('unifiedBarMode', 'overlay');
|
||||
} else {
|
||||
baseStore.set('unifiedBarMode', 'collapsed');
|
||||
}
|
||||
},
|
||||
|
||||
setActiveLayer(layer: UnifiedBarLayer) {
|
||||
baseStore.set('unifiedBarActiveLayer', layer);
|
||||
},
|
||||
setActiveLayer(layer: UnifiedBarLayer) {
|
||||
baseStore.set('unifiedBarActiveLayer', layer);
|
||||
},
|
||||
|
||||
// Layer toggles
|
||||
toggleQuickInput() {
|
||||
baseStore.set('showQuickInput', !baseStore.settings.showQuickInput);
|
||||
},
|
||||
// Layer toggles
|
||||
toggleQuickInput() {
|
||||
baseStore.set('showQuickInput', !baseStore.settings.showQuickInput);
|
||||
},
|
||||
|
||||
toggleDateStrip() {
|
||||
const newValue = !baseStore.settings.showDateStrip;
|
||||
baseStore.set('showDateStrip', newValue);
|
||||
baseStore.set('dateStripCollapsed', !newValue);
|
||||
},
|
||||
toggleDateStrip() {
|
||||
const newValue = !baseStore.settings.showDateStrip;
|
||||
baseStore.set('showDateStrip', newValue);
|
||||
baseStore.set('dateStripCollapsed', !newValue);
|
||||
},
|
||||
|
||||
toggleTagStrip() {
|
||||
const newValue = !baseStore.settings.showTagStrip;
|
||||
baseStore.set('showTagStrip', newValue);
|
||||
baseStore.set('tagStripCollapsed', !newValue);
|
||||
},
|
||||
toggleTagStrip() {
|
||||
const newValue = !baseStore.settings.showTagStrip;
|
||||
baseStore.set('showTagStrip', newValue);
|
||||
baseStore.set('tagStripCollapsed', !newValue);
|
||||
},
|
||||
|
||||
toggleCalendarToolbar() {
|
||||
const newValue = !baseStore.settings.showCalendarToolbar;
|
||||
baseStore.set('showCalendarToolbar', newValue);
|
||||
baseStore.set('calendarToolbarCollapsed', !newValue);
|
||||
},
|
||||
toggleCalendarToolbar() {
|
||||
const newValue = !baseStore.settings.showCalendarToolbar;
|
||||
baseStore.set('showCalendarToolbar', newValue);
|
||||
baseStore.set('calendarToolbarCollapsed', !newValue);
|
||||
},
|
||||
|
||||
// Quick actions
|
||||
expandToLayer(layer: UnifiedBarLayer) {
|
||||
baseStore.set('unifiedBarMode', 'expanded');
|
||||
baseStore.set('unifiedBarActiveLayer', layer);
|
||||
// Quick actions
|
||||
expandToLayer(layer: UnifiedBarLayer) {
|
||||
baseStore.set('unifiedBarMode', 'expanded');
|
||||
baseStore.set('unifiedBarActiveLayer', layer);
|
||||
|
||||
// Auto-show the layer if hidden
|
||||
switch (layer) {
|
||||
case 'date':
|
||||
if (!baseStore.settings.showDateStrip) {
|
||||
baseStore.set('showDateStrip', true);
|
||||
baseStore.set('dateStripCollapsed', false);
|
||||
}
|
||||
break;
|
||||
case 'tag':
|
||||
if (!baseStore.settings.showTagStrip) {
|
||||
baseStore.set('showTagStrip', true);
|
||||
baseStore.set('tagStripCollapsed', false);
|
||||
}
|
||||
break;
|
||||
case 'toolbar':
|
||||
if (!baseStore.settings.showCalendarToolbar) {
|
||||
baseStore.set('showCalendarToolbar', true);
|
||||
baseStore.set('calendarToolbarCollapsed', false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
// Auto-show the layer if hidden
|
||||
switch (layer) {
|
||||
case 'date':
|
||||
if (!baseStore.settings.showDateStrip) {
|
||||
baseStore.set('showDateStrip', true);
|
||||
baseStore.set('dateStripCollapsed', false);
|
||||
}
|
||||
break;
|
||||
case 'tag':
|
||||
if (!baseStore.settings.showTagStrip) {
|
||||
baseStore.set('showTagStrip', true);
|
||||
baseStore.set('tagStripCollapsed', false);
|
||||
}
|
||||
break;
|
||||
case 'toolbar':
|
||||
if (!baseStore.settings.showCalendarToolbar) {
|
||||
baseStore.set('showCalendarToolbar', true);
|
||||
baseStore.set('calendarToolbarCollapsed', false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
collapseAll() {
|
||||
baseStore.set('unifiedBarMode', 'collapsed');
|
||||
baseStore.set('unifiedBarActiveLayer', 'input');
|
||||
if (baseStore.settings.autoCollapseBars) {
|
||||
baseStore.set('showDateStrip', false);
|
||||
baseStore.set('showTagStrip', false);
|
||||
baseStore.set('showCalendarToolbar', false);
|
||||
}
|
||||
},
|
||||
collapseAll() {
|
||||
baseStore.set('unifiedBarMode', 'collapsed');
|
||||
baseStore.set('unifiedBarActiveLayer', 'input');
|
||||
if (baseStore.settings.autoCollapseBars) {
|
||||
baseStore.set('showDateStrip', false);
|
||||
baseStore.set('showTagStrip', false);
|
||||
baseStore.set('showCalendarToolbar', false);
|
||||
}
|
||||
},
|
||||
|
||||
// Cloud sync methods
|
||||
enableCloudSync() {
|
||||
cloudSyncEnabled = true;
|
||||
if (!initialSyncDone) {
|
||||
const cloudSettings = loadFromCloud();
|
||||
if (cloudSettings && Object.keys(cloudSettings).length > 0) {
|
||||
baseStore.update(cloudSettings);
|
||||
} else {
|
||||
syncToCloud(baseStore.settings);
|
||||
}
|
||||
initialSyncDone = true;
|
||||
}
|
||||
},
|
||||
// Cloud sync methods
|
||||
enableCloudSync() {
|
||||
cloudSyncEnabled = true;
|
||||
if (!initialSyncDone) {
|
||||
const cloudSettings = loadFromCloud();
|
||||
if (cloudSettings && Object.keys(cloudSettings).length > 0) {
|
||||
baseStore.update(cloudSettings);
|
||||
} else {
|
||||
syncToCloud(baseStore.settings);
|
||||
}
|
||||
initialSyncDone = true;
|
||||
}
|
||||
},
|
||||
|
||||
disableCloudSync() {
|
||||
cloudSyncEnabled = false;
|
||||
},
|
||||
|
||||
// Legacy compatibility helpers (for gradual migration)
|
||||
get legacyDateStripCollapsed() {
|
||||
return baseStore.settings.dateStripCollapsed;
|
||||
},
|
||||
|
||||
get legacyTagStripCollapsed() {
|
||||
return baseStore.settings.tagStripCollapsed;
|
||||
},
|
||||
|
||||
// Sync from legacy settings (migration helper)
|
||||
syncFromLegacySettings(legacy: { dateStripCollapsed?: boolean; tagStripCollapsed?: boolean }) {
|
||||
const updates: Partial<UnifiedBarSettings> = {};
|
||||
|
||||
if (legacy.dateStripCollapsed !== undefined) {
|
||||
updates.dateStripCollapsed = legacy.dateStripCollapsed;
|
||||
updates.showDateStrip = !legacy.dateStripCollapsed;
|
||||
}
|
||||
|
||||
if (legacy.tagStripCollapsed !== undefined) {
|
||||
updates.tagStripCollapsed = legacy.tagStripCollapsed;
|
||||
updates.showTagStrip = !legacy.tagStripCollapsed;
|
||||
}
|
||||
|
||||
if (Object.keys(updates).length > 0) {
|
||||
baseStore.update(updates);
|
||||
}
|
||||
},
|
||||
disableCloudSync() {
|
||||
cloudSyncEnabled = false;
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ vi.mock('$app/environment', () => ({
|
|||
vi.mock('./settings.svelte', () => ({
|
||||
settingsStore: {
|
||||
weekStartsOn: 1 as 0 | 1,
|
||||
customDayCount: 30,
|
||||
defaultView: 'week',
|
||||
initialize: vi.fn(),
|
||||
},
|
||||
|
|
@ -19,13 +18,12 @@ vi.mock('./settings.svelte', () => ({
|
|||
// Mock @manacore/shared-stores
|
||||
vi.mock('@manacore/shared-stores', () => ({
|
||||
createAppSettingsStore: vi.fn(() => ({
|
||||
settings: { weekStartsOn: 1, customDayCount: 30, defaultView: 'week' },
|
||||
settings: { weekStartsOn: 1, defaultView: 'week' },
|
||||
initialize: vi.fn(),
|
||||
set: vi.fn(),
|
||||
update: vi.fn(),
|
||||
reset: vi.fn(),
|
||||
getDefaults: vi.fn(),
|
||||
toggleImmersiveMode: vi.fn(),
|
||||
})),
|
||||
}));
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue