mirror of
https://github.com/zadam/trilium.git
synced 2025-11-09 23:05:51 +01:00
chore(react/collections/calendar): render calendar events
This commit is contained in:
@@ -47,27 +47,6 @@ function parseDate(str: string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Source: https://stackoverflow.com/a/30465299/4898894
|
||||
function getMonthsInDateRange(startDate: string, endDate: string) {
|
||||
const start = startDate.split("-");
|
||||
const end = endDate.split("-");
|
||||
const startYear = parseInt(start[0]);
|
||||
const endYear = parseInt(end[0]);
|
||||
const dates: string[] = [];
|
||||
|
||||
for (let i = startYear; i <= endYear; i++) {
|
||||
const endMonth = i != endYear ? 11 : parseInt(end[1]) - 1;
|
||||
const startMon = i === startYear ? parseInt(start[1]) - 1 : 0;
|
||||
|
||||
for (let j = startMon; j <= endMonth; j = j > 12 ? j % 12 || 11 : j + 1) {
|
||||
const month = j + 1;
|
||||
const displayMonth = month < 10 ? "0" + month : month;
|
||||
dates.push([i, displayMonth].join("-"));
|
||||
}
|
||||
}
|
||||
return dates;
|
||||
}
|
||||
|
||||
function padNum(num: number) {
|
||||
return `${num <= 9 ? "0" : ""}${num}`;
|
||||
}
|
||||
@@ -835,7 +814,6 @@ export default {
|
||||
restartDesktopApp,
|
||||
reloadTray,
|
||||
parseDate,
|
||||
getMonthsInDateRange,
|
||||
formatDateISO,
|
||||
formatDateTime,
|
||||
formatTimeInterval,
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { EventInput, EventSourceInput } from "@fullcalendar/core/index.js";
|
||||
import { EventInput, EventSourceFuncArg, EventSourceInput } from "@fullcalendar/core/index.js";
|
||||
import froca from "../../../services/froca";
|
||||
import { formatDateToLocalISO, getCustomisableLabel, offsetDate } from "./utils";
|
||||
import { formatDateToLocalISO, getCustomisableLabel, getMonthsInDateRange, offsetDate } from "./utils";
|
||||
import FNote from "../../../entities/fnote";
|
||||
import server from "../../../services/server";
|
||||
|
||||
interface Event {
|
||||
startDate: string,
|
||||
@@ -30,6 +31,50 @@ export async function buildEvents(noteIds: string[]) {
|
||||
return events.flat();
|
||||
}
|
||||
|
||||
export async function buildEventsForCalendar(note: FNote, e: EventSourceFuncArg) {
|
||||
const events: EventInput[] = [];
|
||||
|
||||
// Gather all the required date note IDs.
|
||||
const dateRange = getMonthsInDateRange(e.startStr, e.endStr);
|
||||
let allDateNoteIds: string[] = [];
|
||||
for (const month of dateRange) {
|
||||
// TODO: Deduplicate get type.
|
||||
const dateNotesForMonth = await server.get<Record<string, string>>(`special-notes/notes-for-month/${month}?calendarRoot=${note.noteId}`);
|
||||
const dateNoteIds = Object.values(dateNotesForMonth);
|
||||
allDateNoteIds = [...allDateNoteIds, ...dateNoteIds];
|
||||
}
|
||||
|
||||
// Request all the date notes.
|
||||
const dateNotes = await froca.getNotes(allDateNoteIds);
|
||||
const childNoteToDateMapping: Record<string, string> = {};
|
||||
for (const dateNote of dateNotes) {
|
||||
const startDate = dateNote.getLabelValue("dateNote");
|
||||
if (!startDate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
events.push(await buildEvent(dateNote, { startDate }));
|
||||
|
||||
if (dateNote.hasChildren()) {
|
||||
const childNoteIds = await dateNote.getSubtreeNoteIds();
|
||||
for (const childNoteId of childNoteIds) {
|
||||
childNoteToDateMapping[childNoteId] = startDate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Request all child notes of date notes in a single run.
|
||||
const childNoteIds = Object.keys(childNoteToDateMapping);
|
||||
const childNotes = await froca.getNotes(childNoteIds);
|
||||
for (const childNote of childNotes) {
|
||||
const startDate = childNoteToDateMapping[childNote.noteId];
|
||||
const event = await buildEvent(childNote, { startDate });
|
||||
events.push(event);
|
||||
}
|
||||
|
||||
return events.flat();
|
||||
}
|
||||
|
||||
async function buildEvent(note: FNote, { startDate, endDate, startTime, endTime }: Event) {
|
||||
const customTitleAttributeName = note.getLabelValue("calendar:title");
|
||||
const titles = await parseCustomTitle(customTitleAttributeName, note);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { DateSelectArg, LocaleInput, PluginDef } from "@fullcalendar/core/index.js";
|
||||
import { DateSelectArg, EventSourceFuncArg, LocaleInput, PluginDef } from "@fullcalendar/core/index.js";
|
||||
import { ViewModeProps } from "../interface";
|
||||
import Calendar from "./calendar";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "preact/hooks";
|
||||
@@ -12,7 +12,7 @@ import server from "../../../services/server";
|
||||
import { parseStartEndDateFromEvent, parseStartEndTimeFromEvent } from "./utils";
|
||||
import dialog from "../../../services/dialog";
|
||||
import { t } from "../../../services/i18n";
|
||||
import { buildEvents } from "./event_builder";
|
||||
import { buildEvents, buildEventsForCalendar } from "./event_builder";
|
||||
|
||||
interface CalendarViewData {
|
||||
|
||||
@@ -58,6 +58,8 @@ export default function CalendarView({ note, noteIds }: ViewModeProps<CalendarVi
|
||||
const eventBuilder = useMemo(() => {
|
||||
if (!isCalendarRoot) {
|
||||
return async () => await buildEvents(noteIds);
|
||||
} else {
|
||||
return async (e: EventSourceFuncArg) => await buildEventsForCalendar(note, e);
|
||||
}
|
||||
}, [isCalendarRoot, noteIds]);
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { DateSelectArg } from "@fullcalendar/core/index.js";
|
||||
import { EventImpl } from "@fullcalendar/core/internal";
|
||||
import FNote from "../../../entities/fnote";
|
||||
|
||||
export function parseStartEndDateFromEvent(e: DateSelectArg | EventImpl) {
|
||||
const startDate = formatDateToLocalISO(e.start);
|
||||
@@ -79,3 +80,24 @@ export function getCustomisableLabel(note: FNote, defaultLabelName: string, cust
|
||||
|
||||
return note.getLabelValue(defaultLabelName);
|
||||
}
|
||||
|
||||
// Source: https://stackoverflow.com/a/30465299/4898894
|
||||
export function getMonthsInDateRange(startDate: string, endDate: string) {
|
||||
const start = startDate.split("-");
|
||||
const end = endDate.split("-");
|
||||
const startYear = parseInt(start[0]);
|
||||
const endYear = parseInt(end[0]);
|
||||
const dates: string[] = [];
|
||||
|
||||
for (let i = startYear; i <= endYear; i++) {
|
||||
const endMonth = i != endYear ? 11 : parseInt(end[1]) - 1;
|
||||
const startMon = i === startYear ? parseInt(start[1]) - 1 : 0;
|
||||
|
||||
for (let j = startMon; j <= endMonth; j = j > 12 ? j % 12 || 11 : j + 1) {
|
||||
const month = j + 1;
|
||||
const displayMonth = month < 10 ? "0" + month : month;
|
||||
dates.push([i, displayMonth].join("-"));
|
||||
}
|
||||
}
|
||||
return dates;
|
||||
}
|
||||
|
||||
@@ -30,22 +30,11 @@ export default class CalendarView extends ViewMode<{}> {
|
||||
|
||||
this.$root = $(TPL);
|
||||
this.$calendarContainer = this.$root.find(".calendar-container");
|
||||
this.isCalendarRoot = false;
|
||||
args.$parent.append(this.$root);
|
||||
}
|
||||
|
||||
async renderList(): Promise<JQuery<HTMLElement> | undefined> {
|
||||
const { Calendar } = await import("@fullcalendar/core");
|
||||
|
||||
let eventBuilder: EventSourceFunc;
|
||||
if (!this.isCalendarRoot) {
|
||||
eventBuilder =
|
||||
} else {
|
||||
eventBuilder = async (e: EventSourceFuncArg) => await this.#buildEventsForCalendar(e);
|
||||
}
|
||||
|
||||
const calendar = new Calendar(this.$calendarContainer[0], {
|
||||
events: eventBuilder,
|
||||
select: (e) => this.#onCalendarSelection(e),
|
||||
eventChange: (e) => this.#onEventMoved(e),
|
||||
height: "100%",
|
||||
@@ -183,50 +172,6 @@ export default class CalendarView extends ViewMode<{}> {
|
||||
}
|
||||
}
|
||||
|
||||
async #buildEventsForCalendar(e: EventSourceFuncArg) {
|
||||
const events: EventInput[] = [];
|
||||
|
||||
// Gather all the required date note IDs.
|
||||
const dateRange = utils.getMonthsInDateRange(e.startStr, e.endStr);
|
||||
let allDateNoteIds: string[] = [];
|
||||
for (const month of dateRange) {
|
||||
// TODO: Deduplicate get type.
|
||||
const dateNotesForMonth = await server.get<Record<string, string>>(`special-notes/notes-for-month/${month}?calendarRoot=${this.parentNote.noteId}`);
|
||||
const dateNoteIds = Object.values(dateNotesForMonth);
|
||||
allDateNoteIds = [...allDateNoteIds, ...dateNoteIds];
|
||||
}
|
||||
|
||||
// Request all the date notes.
|
||||
const dateNotes = await froca.getNotes(allDateNoteIds);
|
||||
const childNoteToDateMapping: Record<string, string> = {};
|
||||
for (const dateNote of dateNotes) {
|
||||
const startDate = dateNote.getLabelValue("dateNote");
|
||||
if (!startDate) {
|
||||
continue;
|
||||
}
|
||||
|
||||
events.push(await CalendarView.buildEvent(dateNote, { startDate }));
|
||||
|
||||
if (dateNote.hasChildren()) {
|
||||
const childNoteIds = await dateNote.getSubtreeNoteIds();
|
||||
for (const childNoteId of childNoteIds) {
|
||||
childNoteToDateMapping[childNoteId] = startDate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Request all child notes of date notes in a single run.
|
||||
const childNoteIds = Object.keys(childNoteToDateMapping);
|
||||
const childNotes = await froca.getNotes(childNoteIds);
|
||||
for (const childNote of childNotes) {
|
||||
const startDate = childNoteToDateMapping[childNote.noteId];
|
||||
const event = await CalendarView.buildEvent(childNote, { startDate });
|
||||
events.push(event);
|
||||
}
|
||||
|
||||
return events.flat();
|
||||
}
|
||||
|
||||
buildTouchBarCommand({ TouchBar, buildIcon }: CommandListenerData<"buildTouchBar">) {
|
||||
if (!this.calendar) {
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user