diff --git a/src/services/date_notes.ts b/src/services/date_notes.ts index 8659651b5..942d691c5 100644 --- a/src/services/date_notes.ts +++ b/src/services/date_notes.ts @@ -1,20 +1,21 @@ -import noteService from "./notes.js"; -import attributeService from "./attributes.js"; -import sql from "./sql.js"; -import protectedSessionService from "./protected_session.js"; -import searchService from "../services/search/services/search.js"; -import SearchContext from "../services/search/search_context.js"; -import hoistedNoteService from "./hoisted_note.js"; import type BNote from "../becca/entities/bnote.js"; -import optionService from "./options.js"; -import { t } from "i18next"; -import i18next from "i18next"; -import dayjs from "dayjs"; import type { Dayjs } from "dayjs"; -import isSameOrAfter from "dayjs/plugin/isSameOrAfter.js"; -import quarterOfYear from "dayjs/plugin/quarterOfYear.js"; + import advancedFormat from "dayjs/plugin/advancedFormat.js"; +import attributeService from "./attributes.js"; import cloningService from "./cloning.js"; +import dayjs from "dayjs"; +import hoistedNoteService from "./hoisted_note.js"; +import i18next from "i18next"; +import isSameOrAfter from "dayjs/plugin/isSameOrAfter.js"; +import noteService from "./notes.js"; +import optionService from "./options.js"; +import protectedSessionService from "./protected_session.js"; +import quarterOfYear from "dayjs/plugin/quarterOfYear.js"; +import searchContext from "../services/search/search_context.js"; +import searchService from "../services/search/services/search.js"; +import sql from "./sql.js"; +import { t } from "i18next"; dayjs.extend(isSameOrAfter); dayjs.extend(quarterOfYear); @@ -27,7 +28,7 @@ const MONTH_LABEL = "monthNote"; const WEEK_LABEL = "weekNote"; const DATE_LABEL = "dateNote"; -const WEEKDAY_TRANSLATION_IDS = ["weekdays.sunday", "weekdays.monday", "weekdays.tuesday", "weekdays.wednesday", "weekdays.thursday", "weekdays.friday", "weekdays.saturday", "weekdays.sunday"]; +const WEEKDAY_TRANSLATION_IDS = [ "weekdays.sunday", "weekdays.monday", "weekdays.tuesday", "weekdays.wednesday", "weekdays.thursday", "weekdays.friday", "weekdays.saturday", "weekdays.sunday" ]; const MONTH_TRANSLATION_IDS = [ "months.january", @@ -44,26 +45,26 @@ const MONTH_TRANSLATION_IDS = [ "months.december" ]; -type TimeUnit = 'year' | 'quarter' | 'month' | 'week' | 'day'; +type TimeUnit = "year" | "quarter" | "month" | "week" | "day"; const baseReplacements = { - year: ['year'], - quarter: ['quarterNumber', 'shortQuarter'], - month: ['isoMonth', 'monthNumber', 'monthNumberPadded', 'month', 'shortMonth3', 'shortMonth4'], - week: ['weekNumber', 'weekNumberPadded', 'shortWeek', 'shortWeek3'], - day: ['isoDate', 'dateNumber', 'dateNumberPadded', 'ordinal', 'weekDay', 'weekDay3', 'weekDay2'] + year: [ "year" ], + quarter: [ "quarterNumber", "shortQuarter" ], + month: [ "isoMonth", "monthNumber", "monthNumberPadded", "month", "shortMonth3", "shortMonth4" ], + week: [ "weekNumber", "weekNumberPadded", "shortWeek", "shortWeek3" ], + day: [ "isoDate", "dateNumber", "dateNumberPadded", "ordinal", "weekDay", "weekDay3", "weekDay2" ] }; function getTimeUnitReplacements(timeUnit: TimeUnit): string[] { - const units: TimeUnit[] = ['year', 'quarter', 'month', 'week', 'day']; + const units: TimeUnit[] = [ "year", "quarter", "month", "week", "day" ]; const index = units.indexOf(timeUnit); return units.slice(0, index + 1).flatMap(unit => baseReplacements[unit]); } async function ordinal(date: Dayjs, lng: string) { const localeMap: Record = { - 'cn': 'zh-cn', - 'tw': 'zh-tw' + cn: "zh-cn", + tw: "zh-tw" }; const dayjsLocale = localeMap[lng] || lng; @@ -74,15 +75,15 @@ async function ordinal(date: Dayjs, lng: string) { console.warn(`Could not load locale ${dayjsLocale}`, err); } - return dayjs(date).locale(dayjsLocale).format('Do'); + return dayjs(date).locale(dayjsLocale).format("Do"); } async function getJournalNoteTitle(rootNote: BNote, timeUnit: TimeUnit, dateObj: Dayjs, number: number) { const patterns = { year: rootNote.getOwnedLabelValue("yearPattern") || "{year}", - quarter: rootNote.getOwnedLabelValue("quarterPattern") || t('quarterNumber'), + quarter: rootNote.getOwnedLabelValue("quarterPattern") || t("quarterNumber"), month: rootNote.getOwnedLabelValue("monthPattern") || "{monthNumberPadded} - {month}", - week: rootNote.getOwnedLabelValue("weekPattern") || t('weekdayNumber'), + week: rootNote.getOwnedLabelValue("weekPattern") || t("weekdayNumber"), day: rootNote.getOwnedLabelValue("datePattern") || "{dateNumberPadded} - {weekDay}" }; @@ -94,37 +95,37 @@ async function getJournalNoteTitle(rootNote: BNote, timeUnit: TimeUnit, dateObj: const allReplacements: Record = { // Common date formats - '{year}': dateObj.format('YYYY'), + "{year}": dateObj.format("YYYY"), // Month related - '{isoMonth}': dateObj.format('YYYY-MM'), - '{monthNumber}': numberStr, - '{monthNumberPadded}': numberStr.padStart(2, '0'), - '{month}': monthName, - '{shortMonth3}': monthName.slice(0, 3), - '{shortMonth4}': monthName.slice(0, 4), + "{isoMonth}": dateObj.format("YYYY-MM"), + "{monthNumber}": numberStr, + "{monthNumberPadded}": numberStr.padStart(2, "0"), + "{month}": monthName, + "{shortMonth3}": monthName.slice(0, 3), + "{shortMonth4}": monthName.slice(0, 4), // Quarter related - '{quarterNumber}': numberStr, - '{shortQuarter}': `Q${numberStr}`, + "{quarterNumber}": numberStr, + "{shortQuarter}": `Q${numberStr}`, // Week related - '{weekNumber}': numberStr, - '{weekNumberPadded}': numberStr.padStart(2, '0'), - '{shortWeek}': `W${numberStr}`, - '{shortWeek3}': `W${numberStr.padStart(2, '0')}`, + "{weekNumber}": numberStr, + "{weekNumberPadded}": numberStr.padStart(2, "0"), + "{shortWeek}": `W${numberStr}`, + "{shortWeek3}": `W${numberStr.padStart(2, "0")}`, // Day related - '{isoDate}': dateObj.format('YYYY-MM-DD'), - '{dateNumber}': numberStr, - '{dateNumberPadded}': numberStr.padStart(2, '0'), - '{ordinal}': ordinalStr, - '{weekDay}': weekDay, - '{weekDay3}': weekDay.substring(0, 3), - '{weekDay2}': weekDay.substring(0, 2) + "{isoDate}": dateObj.format("YYYY-MM-DD"), + "{dateNumber}": numberStr, + "{dateNumberPadded}": numberStr.padStart(2, "0"), + "{ordinal}": ordinalStr, + "{weekDay}": weekDay, + "{weekDay3}": weekDay.substring(0, 3), + "{weekDay2}": weekDay.substring(0, 2) }; - const allowedReplacements = Object.entries(allReplacements).reduce((acc, [key, value]) => { + const allowedReplacements = Object.entries(allReplacements).reduce((acc, [ key, value ]) => { const replacementKey = key.slice(1, -1); if (getTimeUnitReplacements(timeUnit).includes(replacementKey)) { acc[key] = value; @@ -133,7 +134,7 @@ async function getJournalNoteTitle(rootNote: BNote, timeUnit: TimeUnit, dateObj: }, {} as Record); return Object.entries(allowedReplacements).reduce( - (title, [key, value]) => title.replace(new RegExp(key, 'g'), value), + (title, [ key, value ]) => title.replace(new RegExp(key, "g"), value), pattern ); } @@ -154,7 +155,7 @@ function getRootCalendarNote(): BNote { const workspaceNote = hoistedNoteService.getWorkspaceNote(); if (!workspaceNote || !workspaceNote.isRoot()) { - rootNote = searchService.findFirstNoteWithQuery("#workspaceCalendarRoot", new SearchContext({ ignoreHoistedNote: false })); + rootNote = searchService.findFirstNoteWithQuery("#workspaceCalendarRoot", new searchContext({ ignoreHoistedNote: false })); } if (!rootNote) { @@ -185,7 +186,7 @@ function getYearNote(dateStr: string, _rootNote: BNote | null = null): BNote { const yearStr = dateStr.trim().substring(0, 4); - let yearNote = searchService.findFirstNoteWithQuery(`#${YEAR_LABEL}="${yearStr}"`, new SearchContext({ ancestorNoteId: rootNote.noteId })); + let yearNote = searchService.findFirstNoteWithQuery(`#${YEAR_LABEL}="${yearStr}"`, new searchContext({ ancestorNoteId: rootNote.noteId })); if (yearNote) { return yearNote; @@ -216,19 +217,19 @@ async function getQuarterNote(quarterStr: string, _rootNote: BNote | null = null quarterStr = quarterStr.trim().substring(0, 7); - let quarterNote = searchService.findFirstNoteWithQuery(`#${QUARTER_LABEL}="${quarterStr}"`, new SearchContext({ ancestorNoteId: rootNote.noteId })); + let quarterNote = searchService.findFirstNoteWithQuery(`#${QUARTER_LABEL}="${quarterStr}"`, new searchContext({ ancestorNoteId: rootNote.noteId })); if (quarterNote) { return quarterNote; } - const [yearStr, quarterNumberStr] = quarterStr.trim().split('-Q'); + const [ yearStr, quarterNumberStr ] = quarterStr.trim().split("-Q"); const quarterNumber = parseInt(quarterNumberStr); const firstMonth = (quarterNumber - 1) * 3; const quarterStartDate = dayjs().year(parseInt(yearStr)).month(firstMonth).date(1); const yearNote = getYearNote(yearStr, rootNote); - const noteTitle = await getJournalNoteTitle(rootNote, 'quarter', quarterStartDate, quarterNumber); + const noteTitle = await getJournalNoteTitle(rootNote, "quarter", quarterStartDate, quarterNumber); sql.transactional(() => { quarterNote = createNote(yearNote, noteTitle); @@ -252,7 +253,7 @@ async function getMonthNote(dateStr: string, _rootNote: BNote | null = null): Pr const monthStr = dateStr.substring(0, 7); const monthNumber = dateStr.substring(5, 7); - let monthNote = searchService.findFirstNoteWithQuery(`#${MONTH_LABEL}="${monthStr}"`, new SearchContext({ ancestorNoteId: rootNote.noteId })); + let monthNote = searchService.findFirstNoteWithQuery(`#${MONTH_LABEL}="${monthStr}"`, new searchContext({ ancestorNoteId: rootNote.noteId })); if (monthNote) { return monthNote; @@ -260,13 +261,13 @@ async function getMonthNote(dateStr: string, _rootNote: BNote | null = null): Pr let monthParentNote; - if (rootNote.hasLabel('enableQuarterNote')) { + if (rootNote.hasLabel("enableQuarterNote")) { monthParentNote = await getQuarterNote(getQuarterNumberStr(dayjs(dateStr)), rootNote); } else { monthParentNote = getYearNote(dateStr, rootNote); } - const noteTitle = await getJournalNoteTitle(rootNote, 'month', dayjs(dateStr), parseInt(monthNumber)); + const noteTitle = await getJournalNoteTitle(rootNote, "month", dayjs(dateStr), parseInt(monthNumber)); sql.transactional(() => { monthNote = createNote(monthParentNote, noteTitle); @@ -308,33 +309,33 @@ function getWeekNumberStr(date: Dayjs): string { const jan1 = date.clone().year(year).month(0).date(1); const jan1Weekday = jan1.day(); const dayOffset = dayOfWeek(jan1Weekday); - let firstWeekStart = jan1.clone().subtract(dayOffset, 'day'); + let firstWeekStart = jan1.clone().subtract(dayOffset, "day"); // Adjust based on week rule switch (parseInt(optionService.getOption("firstWeekOfYear"))) { case 1: { // ISO 8601: first week contains Thursday - const thursday = firstWeekStart.clone().add(3, 'day'); // Monday + 3 = Thursday + const thursday = firstWeekStart.clone().add(3, "day"); // Monday + 3 = Thursday if (thursday.year() < year) { - firstWeekStart = firstWeekStart.add(7, 'day'); + firstWeekStart = firstWeekStart.add(7, "day"); } break; } case 2: { // minDaysInFirstWeek rule const daysInFirstWeek = 7 - dayOffset; if (daysInFirstWeek < parseInt(optionService.getOption("minDaysInFirstWeek"))) { - firstWeekStart = firstWeekStart.add(7, 'day'); + firstWeekStart = firstWeekStart.add(7, "day"); } break; } // default case 0: week containing Jan 1 → already handled } - const diffDays = date.startOf('day').diff(firstWeekStart.startOf('day'), 'day'); + const diffDays = date.startOf("day").diff(firstWeekStart.startOf("day"), "day"); const weekNumber = Math.floor(diffDays / 7) + 1; // Handle case when date is before first week start → belongs to last week of previous year if (weekNumber <= 0) { - return getWeekNumberStr(date.subtract(1, 'day')); + return getWeekNumberStr(date.subtract(1, "day")); } // Handle case when date belongs to first week of next year @@ -342,20 +343,20 @@ function getWeekNumberStr(date: Dayjs): string { const jan1Next = date.clone().year(nextYear).month(0).date(1); const jan1WeekdayNext = jan1Next.day(); const offsetNext = dayOfWeek(jan1WeekdayNext); - let nextYearWeekStart = jan1Next.clone().subtract(offsetNext, 'day'); + let nextYearWeekStart = jan1Next.clone().subtract(offsetNext, "day"); switch (parseInt(optionService.getOption("firstWeekOfYear"))) { case 1: { - const thursday = nextYearWeekStart.clone().add(3, 'day'); + const thursday = nextYearWeekStart.clone().add(3, "day"); if (thursday.year() < nextYear) { - nextYearWeekStart = nextYearWeekStart.add(7, 'day'); + nextYearWeekStart = nextYearWeekStart.add(7, "day"); } break; } case 2: { const daysInFirstWeek = 7 - offsetNext; if (daysInFirstWeek < parseInt(optionService.getOption("minDaysInFirstWeek"))) { - nextYearWeekStart = nextYearWeekStart.add(7, 'day'); + nextYearWeekStart = nextYearWeekStart.add(7, "day"); } break; } @@ -365,41 +366,41 @@ function getWeekNumberStr(date: Dayjs): string { return `${nextYear}-W01`; } - return `${year}-W${weekNumber.toString().padStart(2, '0')}`; + return `${year}-W${weekNumber.toString().padStart(2, "0")}`; } function getWeekFirstDayNote(dateStr: string, rootNote: BNote | null = null) { const weekStartDate = getWeekStartDate(dayjs(dateStr)); - return getDayNote(weekStartDate.format('YYYY-MM-DD'), rootNote); + return getDayNote(weekStartDate.format("YYYY-MM-DD"), rootNote); } async function getWeekNote(weekStr: string, _rootNote: BNote | null = null): Promise { const rootNote = _rootNote || getRootCalendarNote(); - if (!rootNote.hasLabel('enableWeekNote')) { + if (!rootNote.hasLabel("enableWeekNote")) { return null; } weekStr = weekStr.trim().substring(0, 8); - let weekNote = searchService.findFirstNoteWithQuery(`#${WEEK_LABEL}="${weekStr}"`, new SearchContext({ ancestorNoteId: rootNote.noteId })); + let weekNote = searchService.findFirstNoteWithQuery(`#${WEEK_LABEL}="${weekStr}"`, new searchContext({ ancestorNoteId: rootNote.noteId })); if (weekNote) { return weekNote; } - const [yearStr, weekNumStr] = weekStr.trim().split('-W'); + const [ yearStr, weekNumStr ] = weekStr.trim().split("-W"); const weekNumber = parseInt(weekNumStr); const firstDayOfYear = dayjs().year(parseInt(yearStr)).month(0).date(1); - const weekStartDate = firstDayOfYear.add(weekNumber - 1, 'week'); + const weekStartDate = firstDayOfYear.add(weekNumber - 1, "week"); const startDate = getWeekStartDate(weekStartDate); - const endDate = dayjs(startDate).add(6, 'day'); + const endDate = dayjs(startDate).add(6, "day"); const startMonth = startDate.month(); const endMonth = endDate.month(); - const monthNote = await getMonthNote(startDate.format('YYYY-MM-DD'), rootNote); - const noteTitle = await getJournalNoteTitle(rootNote, 'week', startDate, weekNumber); + const monthNote = await getMonthNote(startDate.format("YYYY-MM-DD"), rootNote); + const noteTitle = await getJournalNoteTitle(rootNote, "week", startDate, weekNumber); sql.transactional(async () => { weekNote = createNote(monthNote, noteTitle); @@ -415,7 +416,7 @@ async function getWeekNote(weekStr: string, _rootNote: BNote | null = null): Pro // If the week spans different months, clone the week note in the other month as well if (startMonth !== endMonth) { - const secondMonthNote = await getMonthNote(endDate.format('YYYY-MM-DD'), rootNote); + const secondMonthNote = await getMonthNote(endDate.format("YYYY-MM-DD"), rootNote); cloningService.cloneNoteToParentNote(weekNote.noteId, secondMonthNote.noteId); } }); @@ -428,7 +429,7 @@ async function getDayNote(dateStr: string, _rootNote: BNote | null = null): Prom dateStr = dateStr.trim().substring(0, 10); - let dateNote = searchService.findFirstNoteWithQuery(`#${DATE_LABEL}="${dateStr}"`, new SearchContext({ ancestorNoteId: rootNote.noteId })); + let dateNote = searchService.findFirstNoteWithQuery(`#${DATE_LABEL}="${dateStr}"`, new searchContext({ ancestorNoteId: rootNote.noteId })); if (dateNote) { return dateNote; @@ -436,14 +437,14 @@ async function getDayNote(dateStr: string, _rootNote: BNote | null = null): Prom let dateParentNote; - if (rootNote.hasLabel('enableWeekNote')) { + if (rootNote.hasLabel("enableWeekNote")) { dateParentNote = await getWeekNote(getWeekNumberStr(dayjs(dateStr)), rootNote); } else { dateParentNote = await getMonthNote(dateStr, rootNote); } const dayNumber = dateStr.substring(8, 10); - const noteTitle = await getJournalNoteTitle(rootNote, 'day', dayjs(dateStr), parseInt(dayNumber)); + const noteTitle = await getJournalNoteTitle(rootNote, "day", dayjs(dateStr), parseInt(dayNumber)); sql.transactional(() => { dateNote = createNote(dateParentNote as BNote, noteTitle); @@ -461,7 +462,7 @@ async function getDayNote(dateStr: string, _rootNote: BNote | null = null): Prom } function getTodayNote(rootNote: BNote | null = null) { - return getDayNote(dayjs().format('YYYY-MM-DD'), rootNote); + return getDayNote(dayjs().format("YYYY-MM-DD"), rootNote); } export default {