From 25442948110871e66836c990436274868bd35f40 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 16 Mar 2025 21:20:28 +0200 Subject: [PATCH] feat(calendar): save and restore selected view --- .../app/widgets/view_widgets/calendar_view.ts | 44 +++++++++++++++++-- 1 file changed, 40 insertions(+), 4 deletions(-) diff --git a/src/public/app/widgets/view_widgets/calendar_view.ts b/src/public/app/widgets/view_widgets/calendar_view.ts index 964e8cb22..5f0d24449 100644 --- a/src/public/app/widgets/view_widgets/calendar_view.ts +++ b/src/public/app/widgets/view_widgets/calendar_view.ts @@ -1,4 +1,4 @@ -import type { Calendar, DateSelectArg, EventChangeArg, EventDropArg, EventInput, EventSourceFunc, EventSourceFuncArg, EventSourceInput, PluginDef } from "@fullcalendar/core"; +import type { Calendar, DateSelectArg, DatesSetArg, EventChangeArg, EventDropArg, EventInput, EventSourceFunc, EventSourceFuncArg, EventSourceInput, PluginDef } from "@fullcalendar/core"; import froca from "../../services/froca.js"; import ViewMode, { type ViewModeArgs } from "./view_mode.js"; import type FNote from "../../entities/fnote.js"; @@ -12,6 +12,7 @@ import utils from "../../services/utils.js"; import date_notes from "../../services/date_notes.js"; import appContext from "../../components/app_context.js"; import type { EventImpl } from "@fullcalendar/core/internal"; +import debounce, { type DebouncedFunction } from "debounce"; const TPL = `
@@ -79,6 +80,13 @@ interface CreateChildResponse { }; } +const CALENDAR_VIEWS = [ + "timeGridWeek", + "dayGridMonth", + "multiMonthYear", + "listMonth" +] + export default class CalendarView extends ViewMode { private $root: JQuery; @@ -87,6 +95,8 @@ export default class CalendarView extends ViewMode { private parentNote: FNote; private calendar?: Calendar; private isCalendarRoot: boolean; + private lastView?: string; + private debouncedSaveView?: DebouncedFunction<() => void>; constructor(args: ViewModeArgs) { super(args); @@ -124,9 +134,16 @@ export default class CalendarView extends ViewMode { eventBuilder = async (e: EventSourceFuncArg) => await this.#buildEventsForCalendar(e); } + // Parse user's initial view, if valid. + let initialView = "dayGridMonth"; + const userInitialView = this.parentNote.getLabelValue("calendar:view"); + if (userInitialView && CALENDAR_VIEWS.includes(userInitialView)) { + initialView = userInitialView; + } + const calendar = new Calendar(this.$calendarContainer[0], { plugins, - initialView: "dayGridMonth", + initialView, events: eventBuilder, editable: isEditable, selectable: isEditable, @@ -170,9 +187,10 @@ export default class CalendarView extends ViewMode { appContext.tabManager.getActiveContext()?.setNote(note.noteId); } }, + datesSet: (e) => this.#onDatesSet(e), headerToolbar: { start: "title", - end: "timeGridWeek,dayGridMonth,multiMonthYear,listMonth today prev,next" + end: `${CALENDAR_VIEWS.join(",")} today prev,next` } }); calendar.render(); @@ -204,6 +222,24 @@ export default class CalendarView extends ViewMode { } } + #onDatesSet(e: DatesSetArg) { + const currentView = e.view.type; + if (currentView === this.lastView) { + return; + } + + if (!this.debouncedSaveView) { + this.debouncedSaveView = debounce(() => { + if (this.lastView) { + attributes.setLabel(this.parentNote.noteId, "calendar:view", this.lastView); + } + }, 1_000); + } + + this.debouncedSaveView(); + this.lastView = currentView; + } + async #onCalendarSelection(e: DateSelectArg) { // Handle start and end date const { startDate, endDate } = this.#parseStartEndDateFromEvent(e); @@ -310,7 +346,7 @@ export default class CalendarView extends ViewMode { } // Refresh calendar on attribute change. - if (loadResults.getAttributeRows().some((attribute) => attribute.noteId === this.parentNote.noteId && attribute.name?.startsWith("calendar:"))) { + if (loadResults.getAttributeRows().some((attribute) => attribute.noteId === this.parentNote.noteId && attribute.name?.startsWith("calendar:") && attribute.name !== "calendar:view")) { return true; }