feat: persist sidebar toggle settings per-device via cookies

The sidebar eye-toggles (Someday, All-day, Checkboxes, Project Icons,
Weather) and the sub-hour slots toggle now save to a device-local
cookie instead of the DB, so phone and desktop can have independent
display preferences.

- DEVICE_SETTINGS_KEYS extended with showSubHourSlots
- New DEVICE_VIEW_SETTINGS_KEYS for per-view sidebar toggles
  (showSomeday, showAllDayEvents, showTaskCheckboxes, showProjectIcons, weatherEnabled)
- saveViewSetting writes cookie-only for device keys; DB untouched
- getEffective checks device cookie before DB per-view overrides
- Cookie restores applied after DB load in both fetchProfile and
  fetchUserInfo so device preference always wins

v1.94.0
This commit is contained in:
mARTin 2026-04-07 10:10:37 +02:00
parent 36744d0e09
commit 0b40974b88
2 changed files with 58 additions and 5 deletions

View File

@ -1,6 +1,6 @@
{
"name": "my-weekly-todo-list",
"version": "1.93.1",
"version": "1.94.0",
"description": "A web-based weekly task management application that organizes to-dos and calendar events in a single, intuitive weekly view",
"main": "index.js",
"scripts": {

View File

@ -110,7 +110,12 @@ const RichTextEditor = dynamic(() => import("./RichTextEditor"), { ssr: false })
const PriorityView = dynamic(() => import("./PriorityView"), { ssr: false });
// Cookie helpers for per-device settings
const DEVICE_SETTINGS_KEYS = ["viewDays", "cellDuration", "startHour", "endHour", "fontSize"];
// Keys here bypass the DB and save/load from cookie only — each device keeps its own value
const DEVICE_SETTINGS_KEYS = ["viewDays", "cellDuration", "startHour", "endHour", "fontSize", "showSubHourSlots"];
// Per-view toggle keys that are also device-specific (sidebar eye toggles)
const DEVICE_VIEW_SETTINGS_KEYS = ["showSomeday", "showAllDayEvents", "showTaskCheckboxes", "showProjectIcons", "weatherEnabled"] as const;
type DeviceViewSettingKey = typeof DEVICE_VIEW_SETTINGS_KEYS[number];
function getCookie(name: string): string | null {
if (typeof document === "undefined") return null;
@ -124,6 +129,16 @@ function setCookie(name: string, value: string, days: number = 365) {
document.cookie = `${name}=${encodeURIComponent(value)}; expires=${expires}; path=/; SameSite=Lax`;
}
function readDeviceViewCookie(): Record<string, Record<string, any>> {
const raw = getCookie("device_view_settings");
if (!raw) return {};
try { return JSON.parse(raw); } catch { return {}; }
}
function writeDeviceViewCookie(settings: Record<string, Record<string, any>>) {
setCookie("device_view_settings", JSON.stringify(settings));
}
export type ViewStyle = "simple" | "calendar" | "list" | "grid" | "kanban" | "priority";
export interface KanbanStage {
@ -611,7 +626,10 @@ export default function WeeklyView() {
const [timeFormat, setTimeFormat] = useState("24h");
const [dateFormat, setDateFormat] = useState("yyyy-MM-dd");
const [hourLabelFormat, setHourLabelFormat] = useState<"short" | "full">("short");
const [showSubHourSlots, setShowSubHourSlots] = useState(true);
const [showSubHourSlots, setShowSubHourSlots] = useState<boolean>(() => {
const c = getCookie("setting_showSubHourSlots");
return c !== null ? c === "true" : true;
});
const [allDayPosition, setAllDayPosition] = useState<"above" | "below">("below");
const [somedayExpanded, setSomedayExpanded] = useState(true);
@ -1023,7 +1041,17 @@ export default function WeeklyView() {
const viewSettingsRef = useRef<Record<string, PerViewOverrides>>({});
viewSettingsRef.current = viewSettings;
// Device-local per-view overrides (cookie-based, not synced to DB)
const [deviceViewSettings, setDeviceViewSettings] = useState<Record<string, Record<string, any>>>(() => readDeviceViewCookie());
const deviceViewSettingsRef = useRef<Record<string, Record<string, any>>>({});
deviceViewSettingsRef.current = deviceViewSettings;
const getEffective = <K extends keyof PerViewOverrides>(key: K, globalVal: PerViewOverrides[K]): PerViewOverrides[K] => {
// Device-specific settings take priority (cookie-based, not synced across devices)
if ((DEVICE_VIEW_SETTINGS_KEYS as readonly string[]).includes(key)) {
const dvs = deviceViewSettingsRef.current[profile.viewStyle];
if (dvs && dvs[key] !== undefined) return dvs[key] as PerViewOverrides[K];
}
const vs = viewSettingsRef.current[profile.viewStyle];
if (vs && vs[key] !== undefined) return vs[key] as PerViewOverrides[K];
return globalVal;
@ -1033,6 +1061,15 @@ export default function WeeklyView() {
return !!(vs && vs[key] !== undefined);
};
const saveViewSetting = async <K extends keyof PerViewOverrides>(key: K, value: PerViewOverrides[K], perView: boolean) => {
// Device-specific keys: save to cookie only, not DB
if ((DEVICE_VIEW_SETTINGS_KEYS as readonly string[]).includes(key) && perView) {
const dvs = { ...deviceViewSettingsRef.current };
dvs[profile.viewStyle] = { ...(dvs[profile.viewStyle] || {}), [key]: value };
deviceViewSettingsRef.current = dvs;
setDeviceViewSettings(dvs);
writeDeviceViewCookie(dvs);
return;
}
const updated = { ...viewSettingsRef.current };
if (perView) {
updated[profile.viewStyle] = { ...(updated[profile.viewStyle] || {}), [key]: value };
@ -1359,6 +1396,18 @@ export default function WeeklyView() {
}
}
// Re-apply per-device cookie overrides — these always win over DB values
const _cFS = getCookie("setting_fontSize");
if (_cFS) setFontSize(_cFS as "S" | "M" | "L");
const _cSH = getCookie("setting_showSubHourSlots");
if (_cSH !== null) setShowSubHourSlots(_cSH === "true");
const _cCD = getCookie("setting_cellDuration");
if (_cCD) setCellDuration(Number(_cCD) as CellDuration);
const _cStart = getCookie("setting_startHour");
if (_cStart) setStartHour(Number(_cStart));
const _cEnd = getCookie("setting_endHour");
if (_cEnd) setEndHour(Number(_cEnd));
// Show onboarding wizard for new users
if (profileData.hasCompletedOnboarding === false) {
setShowOnboarding(true);
@ -2349,12 +2398,13 @@ export default function WeeklyView() {
if (cookieStartHour) setStartHour(Number(cookieStartHour));
const cookieEndHour = getCookie("setting_endHour");
if (cookieEndHour) setEndHour(Number(cookieEndHour));
const cookieFontSize = getCookie("setting_fontSize");
if (cookieFontSize) setFontSize(cookieFontSize as "S" | "M" | "L");
setShowNextTask(data.user.showNextTask || false);
setCalendarEditMode(data.user.calendarEditMode || false);
if (data.user.fontSize)
setFontSize(data.user.fontSize as "S" | "M" | "L");
// Cookie wins over DB for per-device settings
const cookieFontSize2 = getCookie("setting_fontSize");
if (cookieFontSize2) setFontSize(cookieFontSize2 as "S" | "M" | "L");
if (data.user.showSomeday !== undefined)
setShowSomeday(data.user.showSomeday);
@ -2372,6 +2422,9 @@ export default function WeeklyView() {
setHourLabelFormat(data.user.hourLabelFormat as "short" | "full");
if (data.user.showSubHourSlots !== undefined)
setShowSubHourSlots(data.user.showSubHourSlots);
// Cookie wins over DB for showSubHourSlots (per-device)
const _cSH2 = getCookie("setting_showSubHourSlots");
if (_cSH2 !== null) setShowSubHourSlots(_cSH2 === "true");
if (data.user.allDayPosition)
setAllDayPosition(data.user.allDayPosition as "above" | "below");
if (data.user.viewSettings) setViewSettings(data.user.viewSettings);