feat: persist view style and start-on preference per device

viewStyle, showTimeGrid, and startDayOffset now also write to a device
cookie so each device remembers its own view independently. Cookie
wins over DB on load, giving phone/desktop their own default view
without losing the cross-device DB fallback.

- DEVICE_ALSO_COOKIE_KEYS: viewStyle, showTimeGrid, startDayOffset
- saveSetting writes cookie for these in addition to DB
- viewStyle and showTimeGrid lazy-init from cookie on first render
- Both fetchProfile and fetchUserInfo apply cookie overrides last

v1.94.1
This commit is contained in:
mARTin 2026-04-07 23:29:35 +02:00
parent 0b40974b88
commit 075ccdba04
2 changed files with 37 additions and 3 deletions

View File

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

View File

@ -117,6 +117,9 @@ const DEVICE_SETTINGS_KEYS = ["viewDays", "cellDuration", "startHour", "endHour"
const DEVICE_VIEW_SETTINGS_KEYS = ["showSomeday", "showAllDayEvents", "showTaskCheckboxes", "showProjectIcons", "weatherEnabled"] as const; const DEVICE_VIEW_SETTINGS_KEYS = ["showSomeday", "showAllDayEvents", "showTaskCheckboxes", "showProjectIcons", "weatherEnabled"] as const;
type DeviceViewSettingKey = typeof DEVICE_VIEW_SETTINGS_KEYS[number]; type DeviceViewSettingKey = typeof DEVICE_VIEW_SETTINGS_KEYS[number];
// Settings that save to DB (cross-device default) AND to cookie (device override wins on load)
const DEVICE_ALSO_COOKIE_KEYS = ["viewStyle", "showTimeGrid", "startDayOffset"];
function getCookie(name: string): string | null { function getCookie(name: string): string | null {
if (typeof document === "undefined") return null; if (typeof document === "undefined") return null;
const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`)); const match = document.cookie.match(new RegExp(`(?:^|; )${name}=([^;]*)`));
@ -999,7 +1002,10 @@ export default function WeeklyView() {
}; };
const [cellDuration, setCellDuration] = useState<CellDuration>(30); const [cellDuration, setCellDuration] = useState<CellDuration>(30);
const [draggedTask, setDraggedTask] = useState<Task | null>(null); const [draggedTask, setDraggedTask] = useState<Task | null>(null);
const [showTimeGrid, setShowTimeGrid] = useState(true); const [showTimeGrid, setShowTimeGrid] = useState<boolean>(() => {
const c = getCookie("setting_showTimeGrid");
return c !== null ? c === "true" : true;
});
const [slideDirection, setSlideDirection] = useState<"next" | "prev" | null>( const [slideDirection, setSlideDirection] = useState<"next" | "prev" | null>(
null, null,
); );
@ -1018,7 +1024,10 @@ export default function WeeklyView() {
listId?: string; listId?: string;
slotIdx?: number; slotIdx?: number;
} | null>(null); } | null>(null);
const [viewStyle, setViewStyle] = useState<ViewStyle>("simple"); const [viewStyle, setViewStyle] = useState<ViewStyle>(() => {
const c = getCookie("setting_viewStyle");
return (c as ViewStyle) || "simple";
});
// Per-view settings: overrides that apply only to a specific view // Per-view settings: overrides that apply only to a specific view
type PerViewOverrides = { type PerViewOverrides = {
@ -1407,6 +1416,12 @@ export default function WeeklyView() {
if (_cStart) setStartHour(Number(_cStart)); if (_cStart) setStartHour(Number(_cStart));
const _cEnd = getCookie("setting_endHour"); const _cEnd = getCookie("setting_endHour");
if (_cEnd) setEndHour(Number(_cEnd)); if (_cEnd) setEndHour(Number(_cEnd));
const _cVS = getCookie("setting_viewStyle");
if (_cVS) { setViewStyle(_cVS as ViewStyle); setProfile((p: any) => ({ ...p, viewStyle: _cVS })); }
const _cTG = getCookie("setting_showTimeGrid");
if (_cTG !== null) { const v = _cTG === "true"; setShowTimeGrid(v); setProfile((p: any) => ({ ...p, showTimeGrid: v })); }
const _cSDO = getCookie("setting_startDayOffset");
if (_cSDO !== null) setProfile((p: any) => ({ ...p, startDayOffset: Number(_cSDO) }));
// Show onboarding wizard for new users // Show onboarding wizard for new users
if (profileData.hasCompletedOnboarding === false) { if (profileData.hasCompletedOnboarding === false) {
@ -2271,6 +2286,10 @@ export default function WeeklyView() {
setCookie(`setting_${key}`, String(value)); setCookie(`setting_${key}`, String(value));
return; // Don't write to DB — that would overwrite other devices return; // Don't write to DB — that would overwrite other devices
} }
// Also persist to device cookie so this device remembers its own preference on reload
if (DEVICE_ALSO_COOKIE_KEYS.includes(key)) {
setCookie(`setting_${key}`, String(value));
}
// Keep profile object in sync so the debounced auto-save never sends stale values // Keep profile object in sync so the debounced auto-save never sends stale values
setProfile((p: any) => ({ ...p, [key]: value })); setProfile((p: any) => ({ ...p, [key]: value }));
try { try {
@ -2499,6 +2518,21 @@ export default function WeeklyView() {
setCellDuration(data.user.cellDuration as CellDuration); setCellDuration(data.user.cellDuration as CellDuration);
if (data.user.viewStyle) if (data.user.viewStyle)
setViewStyle(data.user.viewStyle as ViewStyle); setViewStyle(data.user.viewStyle as ViewStyle);
// Cookie overrides for settings that are also device-local
const _cVS2 = getCookie("setting_viewStyle");
if (_cVS2) { setViewStyle(_cVS2 as ViewStyle); setProfile((p: any) => ({ ...p, viewStyle: _cVS2, showTimeGrid: _cVS2 === "simple" || _cVS2 === "calendar" })); }
const _cTG2 = getCookie("setting_showTimeGrid");
if (_cTG2 !== null) setShowTimeGrid(_cTG2 === "true");
const _cSDO2 = getCookie("setting_startDayOffset");
if (_cSDO2 !== null) {
const offset = Number(_cSDO2);
setProfile((p: any) => ({ ...p, startDayOffset: offset }));
if (offset !== 0) {
const d = new Date(); d.setHours(0, 0, 0, 0); d.setDate(d.getDate() + offset);
setCurrentWeekStart(d);
}
}
} }
} }
} catch (e) { } catch (e) {