| 
									
										
										
										
											2024-07-18 21:35:17 +03:00
										 |  |  | import noteService from "./notes.js"; | 
					
						
							|  |  |  | import attributeService from "./attributes.js"; | 
					
						
							|  |  |  | import dateUtils from "./date_utils.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"; | 
					
						
							| 
									
										
										
										
											2025-01-13 23:18:10 +02:00
										 |  |  | import type BNote from "../becca/entities/bnote.js"; | 
					
						
							| 
									
										
										
										
											2025-04-01 17:25:03 +02:00
										 |  |  | import optionService from "./options.js"; | 
					
						
							| 
									
										
										
										
											2024-10-23 19:34:09 +03:00
										 |  |  | import { t } from "i18next"; | 
					
						
							| 
									
										
										
										
											2025-04-01 17:57:16 +02:00
										 |  |  | import dayjs from "dayjs"; | 
					
						
							|  |  |  | import type { Dayjs } from "dayjs"; | 
					
						
							|  |  |  | import isSameOrAfter from "dayjs/plugin/isSameOrAfter.js"; | 
					
						
							| 
									
										
										
										
											2025-04-01 18:09:08 +02:00
										 |  |  | import cloningService from "./cloning.js"; | 
					
						
							| 
									
										
										
										
											2025-04-01 17:57:16 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | dayjs.extend(isSameOrAfter); | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  | const CALENDAR_ROOT_LABEL = "calendarRoot"; | 
					
						
							|  |  |  | const YEAR_LABEL = "yearNote"; | 
					
						
							|  |  |  | const MONTH_LABEL = "monthNote"; | 
					
						
							| 
									
										
										
										
											2025-04-01 14:46:35 +02:00
										 |  |  | const WEEK_LABEL = "weekNote"; | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  | const DATE_LABEL = "dateNote"; | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  | const WEEKDAY_TRANSLATION_IDS = ["weekdays.sunday", "weekdays.monday", "weekdays.tuesday", "weekdays.wednesday", "weekdays.thursday", "weekdays.friday", "weekdays.saturday", "weekdays.sunday"]; | 
					
						
							| 
									
										
										
										
											2024-10-23 19:34:09 +03:00
										 |  |  | 
 | 
					
						
							|  |  |  | const MONTH_TRANSLATION_IDS = [ | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |     "months.january", | 
					
						
							|  |  |  |     "months.february", | 
					
						
							|  |  |  |     "months.march", | 
					
						
							|  |  |  |     "months.april", | 
					
						
							|  |  |  |     "months.may", | 
					
						
							|  |  |  |     "months.june", | 
					
						
							|  |  |  |     "months.july", | 
					
						
							|  |  |  |     "months.august", | 
					
						
							|  |  |  |     "months.september", | 
					
						
							|  |  |  |     "months.october", | 
					
						
							|  |  |  |     "months.november", | 
					
						
							|  |  |  |     "months.december" | 
					
						
							| 
									
										
										
										
											2024-10-23 19:34:09 +03:00
										 |  |  | ]; | 
					
						
							| 
									
										
										
										
											2018-02-10 13:53:35 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-18 13:42:05 +02:00
										 |  |  | function createNote(parentNote: BNote, noteTitle: string) { | 
					
						
							| 
									
										
										
										
											2020-12-21 23:00:39 +01:00
										 |  |  |     return noteService.createNewNote({ | 
					
						
							|  |  |  |         parentNoteId: parentNote.noteId, | 
					
						
							| 
									
										
										
										
											2018-01-28 19:30:14 -05:00
										 |  |  |         title: noteTitle, | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |         content: "", | 
					
						
							| 
									
										
										
										
											2020-12-21 23:00:39 +01:00
										 |  |  |         isProtected: parentNote.isProtected && protectedSessionService.isProtectedSessionAvailable(), | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |         type: "text" | 
					
						
							| 
									
										
										
										
											2020-12-21 23:00:39 +01:00
										 |  |  |     }).note; | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-18 13:42:05 +02:00
										 |  |  | function getRootCalendarNote(): BNote { | 
					
						
							| 
									
										
										
										
											2022-07-31 09:33:14 +02:00
										 |  |  |     let rootNote; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-23 15:07:48 +01:00
										 |  |  |     const workspaceNote = hoistedNoteService.getWorkspaceNote(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-18 13:42:05 +02:00
										 |  |  |     if (!workspaceNote || !workspaceNote.isRoot()) { | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |         rootNote = searchService.findFirstNoteWithQuery("#workspaceCalendarRoot", new SearchContext({ ignoreHoistedNote: false })); | 
					
						
							| 
									
										
										
										
											2022-07-31 09:33:14 +02:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-08-02 18:25:05 +02:00
										 |  |  |     if (!rootNote) { | 
					
						
							| 
									
										
										
										
											2022-07-31 09:33:14 +02:00
										 |  |  |         rootNote = attributeService.getNoteWithLabel(CALENDAR_ROOT_LABEL); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-04-07 13:03:16 -04:00
										 |  |  |     if (!rootNote) { | 
					
						
							| 
									
										
										
										
											2020-08-18 21:32:45 +02:00
										 |  |  |         sql.transactional(() => { | 
					
						
							|  |  |  |             rootNote = noteService.createNewNote({ | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |                 parentNoteId: "root", | 
					
						
							|  |  |  |                 title: "Calendar", | 
					
						
							|  |  |  |                 target: "into", | 
					
						
							| 
									
										
										
										
											2020-08-18 21:32:45 +02:00
										 |  |  |                 isProtected: false, | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |                 type: "text", | 
					
						
							|  |  |  |                 content: "" | 
					
						
							| 
									
										
										
										
											2020-08-18 21:32:45 +02:00
										 |  |  |             }).note; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             attributeService.createLabel(rootNote.noteId, CALENDAR_ROOT_LABEL); | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |             attributeService.createLabel(rootNote.noteId, "sorted"); | 
					
						
							| 
									
										
										
										
											2020-08-18 21:32:45 +02:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-18 13:42:05 +02:00
										 |  |  |     return rootNote as BNote; | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-18 13:42:05 +02:00
										 |  |  | function getYearNote(dateStr: string, _rootNote: BNote | null = null): BNote { | 
					
						
							|  |  |  |     const rootNote = _rootNote || getRootCalendarNote(); | 
					
						
							| 
									
										
										
										
											2019-02-19 22:49:57 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-01 00:08:48 +02:00
										 |  |  |     const yearStr = dateStr.trim().substring(0, 4); | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |     let yearNote = searchService.findFirstNoteWithQuery(`#${YEAR_LABEL}="${yearStr}"`, new SearchContext({ ancestorNoteId: rootNote.noteId })); | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-24 11:28:47 +02:00
										 |  |  |     if (yearNote) { | 
					
						
							|  |  |  |         return yearNote; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-24 11:28:47 +02:00
										 |  |  |     sql.transactional(() => { | 
					
						
							|  |  |  |         yearNote = createNote(rootNote, yearStr); | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-24 11:28:47 +02:00
										 |  |  |         attributeService.createLabel(yearNote.noteId, YEAR_LABEL, yearStr); | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |         attributeService.createLabel(yearNote.noteId, "sorted"); | 
					
						
							| 
									
										
										
										
											2019-09-07 21:40:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |         const yearTemplateAttr = rootNote.getOwnedAttribute("relation", "yearTemplate"); | 
					
						
							| 
									
										
										
										
											2019-09-07 21:40:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-24 11:28:47 +02:00
										 |  |  |         if (yearTemplateAttr) { | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |             attributeService.createRelation(yearNote.noteId, "template", yearTemplateAttr.value); | 
					
						
							| 
									
										
										
										
											2019-09-07 21:40:18 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-07-24 11:28:47 +02:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-18 13:42:05 +02:00
										 |  |  |     return yearNote as unknown as BNote; | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-18 13:42:05 +02:00
										 |  |  | function getMonthNoteTitle(rootNote: BNote, monthNumber: string, dateObj: Date) { | 
					
						
							| 
									
										
										
										
											2020-06-20 12:31:38 +02:00
										 |  |  |     const pattern = rootNote.getOwnedLabelValue("monthPattern") || "{monthNumberPadded} - {month}"; | 
					
						
							| 
									
										
										
										
											2024-10-23 19:34:09 +03:00
										 |  |  |     const monthName = t(MONTH_TRANSLATION_IDS[dateObj.getMonth()]); | 
					
						
							| 
									
										
										
										
											2019-05-19 11:30:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return pattern | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |         .replace(/{shortMonth3}/g, monthName.slice(0, 3)) | 
					
						
							|  |  |  |         .replace(/{shortMonth4}/g, monthName.slice(0, 4)) | 
					
						
							| 
									
										
										
										
											2025-03-20 22:59:20 +00:00
										 |  |  |         .replace(/{isoMonth}/g, dateUtils.utcDateStr(dateObj).slice(0, 7)) | 
					
						
							| 
									
										
										
										
											2019-05-19 11:30:17 +02:00
										 |  |  |         .replace(/{monthNumberPadded}/g, monthNumber) | 
					
						
							|  |  |  |         .replace(/{month}/g, monthName); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-18 13:42:05 +02:00
										 |  |  | function getMonthNote(dateStr: string, _rootNote: BNote | null = null): BNote { | 
					
						
							|  |  |  |     const rootNote = _rootNote || getRootCalendarNote(); | 
					
						
							| 
									
										
										
										
											2019-09-07 21:40:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-01 00:08:48 +02:00
										 |  |  |     const monthStr = dateStr.substring(0, 7); | 
					
						
							| 
									
										
										
										
											2025-04-01 15:33:10 +02:00
										 |  |  |     const monthNumber = dateStr.substring(5, 7); | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |     let monthNote = searchService.findFirstNoteWithQuery(`#${MONTH_LABEL}="${monthStr}"`, new SearchContext({ ancestorNoteId: rootNote.noteId })); | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-24 11:28:47 +02:00
										 |  |  |     if (monthNote) { | 
					
						
							|  |  |  |         return monthNote; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const dateObj = dateUtils.parseLocalDate(dateStr); | 
					
						
							| 
									
										
										
										
											2018-02-10 13:53:35 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-24 11:28:47 +02:00
										 |  |  |     const noteTitle = getMonthNoteTitle(rootNote, monthNumber, dateObj); | 
					
						
							| 
									
										
										
										
											2018-02-10 13:53:35 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-11 14:15:38 +01:00
										 |  |  |     const yearNote = getYearNote(dateStr, rootNote); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-24 11:28:47 +02:00
										 |  |  |     sql.transactional(() => { | 
					
						
							|  |  |  |         monthNote = createNote(yearNote, noteTitle); | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-24 11:28:47 +02:00
										 |  |  |         attributeService.createLabel(monthNote.noteId, MONTH_LABEL, monthStr); | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |         attributeService.createLabel(monthNote.noteId, "sorted"); | 
					
						
							| 
									
										
										
										
											2019-09-07 21:40:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |         const monthTemplateAttr = rootNote.getOwnedAttribute("relation", "monthTemplate"); | 
					
						
							| 
									
										
										
										
											2019-09-07 21:40:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-24 11:28:47 +02:00
										 |  |  |         if (monthTemplateAttr) { | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |             attributeService.createRelation(monthNote.noteId, "template", monthTemplateAttr.value); | 
					
						
							| 
									
										
										
										
											2019-09-07 21:40:18 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-07-24 11:28:47 +02:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-18 13:42:05 +02:00
										 |  |  |     return monthNote as unknown as BNote; | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-18 13:42:05 +02:00
										 |  |  | function getDayNoteTitle(rootNote: BNote, dayNumber: string, dateObj: Date) { | 
					
						
							| 
									
										
										
										
											2020-06-20 12:31:38 +02:00
										 |  |  |     const pattern = rootNote.getOwnedLabelValue("datePattern") || "{dayInMonthPadded} - {weekDay}"; | 
					
						
							| 
									
										
										
										
											2024-10-23 19:34:09 +03:00
										 |  |  |     const weekDay = t(WEEKDAY_TRANSLATION_IDS[dateObj.getDay()]); | 
					
						
							| 
									
										
										
										
											2019-05-19 11:30:17 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     return pattern | 
					
						
							| 
									
										
										
										
											2023-09-12 07:30:30 +10:00
										 |  |  |         .replace(/{ordinal}/g, ordinal(parseInt(dayNumber))) | 
					
						
							| 
									
										
										
										
											2019-05-19 11:30:17 +02:00
										 |  |  |         .replace(/{dayInMonthPadded}/g, dayNumber) | 
					
						
							| 
									
										
										
										
											2021-11-04 19:56:45 +01:00
										 |  |  |         .replace(/{isoDate}/g, dateUtils.utcDateStr(dateObj)) | 
					
						
							| 
									
										
										
										
											2019-05-19 11:30:17 +02:00
										 |  |  |         .replace(/{weekDay}/g, weekDay) | 
					
						
							| 
									
										
										
										
											2025-04-01 00:08:48 +02:00
										 |  |  |         .replace(/{weekDay3}/g, weekDay.substring(0, 3)) | 
					
						
							|  |  |  |         .replace(/{weekDay2}/g, weekDay.substring(0, 2)); | 
					
						
							| 
									
										
										
										
											2019-05-19 11:30:17 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-03 13:49:18 +01:00
										 |  |  | /** produces 1st, 2nd, 3rd, 4th, 21st, 31st for 1, 2, 3, 4, 21, 31 */ | 
					
						
							| 
									
										
										
										
											2024-02-18 13:42:05 +02:00
										 |  |  | function ordinal(dayNumber: number) { | 
					
						
							| 
									
										
										
										
											2023-11-03 13:49:18 +01:00
										 |  |  |     const suffixes = ["th", "st", "nd", "rd"]; | 
					
						
							|  |  |  |     const suffix = suffixes[(dayNumber - 20) % 10] || suffixes[dayNumber] || suffixes[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return `${dayNumber}${suffix}`; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-18 13:42:05 +02:00
										 |  |  | function getDayNote(dateStr: string, _rootNote: BNote | null = null): BNote { | 
					
						
							|  |  |  |     const rootNote = _rootNote || getRootCalendarNote(); | 
					
						
							| 
									
										
										
										
											2022-10-26 14:23:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-01 00:08:48 +02:00
										 |  |  |     dateStr = dateStr.trim().substring(0, 10); | 
					
						
							| 
									
										
										
										
											2021-12-27 20:48:14 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |     let dateNote = searchService.findFirstNoteWithQuery(`#${DATE_LABEL}="${dateStr}"`, new SearchContext({ ancestorNoteId: rootNote.noteId })); | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-24 11:28:47 +02:00
										 |  |  |     if (dateNote) { | 
					
						
							|  |  |  |         return dateNote; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-01 17:57:16 +02:00
										 |  |  |     let dateParentNote; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (checkWeekNoteEnabled(rootNote)) { | 
					
						
							|  |  |  |         dateParentNote = getWeekNote(getWeekNumberStr(dayjs(dateStr)), rootNote); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         dateParentNote = getMonthNote(dateStr, rootNote); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-01 15:33:10 +02:00
										 |  |  |     const dayNumber = dateStr.substring(8, 10); | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-24 11:28:47 +02:00
										 |  |  |     const dateObj = dateUtils.parseLocalDate(dateStr); | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-10 17:09:20 +01:00
										 |  |  |     const noteTitle = getDayNoteTitle(rootNote, dayNumber, dateObj); | 
					
						
							| 
									
										
										
										
											2019-09-07 21:40:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-24 11:28:47 +02:00
										 |  |  |     sql.transactional(() => { | 
					
						
							| 
									
										
										
										
											2025-04-01 17:57:16 +02:00
										 |  |  |         dateNote = createNote(dateParentNote as BNote, noteTitle); | 
					
						
							| 
									
										
										
										
											2019-09-07 21:40:18 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-01 00:08:48 +02:00
										 |  |  |         attributeService.createLabel(dateNote.noteId, DATE_LABEL, dateStr.substring(0, 10)); | 
					
						
							| 
									
										
										
										
											2021-07-24 11:28:47 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |         const dateTemplateAttr = rootNote.getOwnedAttribute("relation", "dateTemplate"); | 
					
						
							| 
									
										
										
										
											2021-07-24 11:28:47 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (dateTemplateAttr) { | 
					
						
							| 
									
										
										
										
											2025-01-09 18:07:02 +02:00
										 |  |  |             attributeService.createRelation(dateNote.noteId, "template", dateTemplateAttr.value); | 
					
						
							| 
									
										
										
										
											2019-09-07 21:40:18 +02:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-07-24 11:28:47 +02:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-02-18 13:42:05 +02:00
										 |  |  |     return dateNote as unknown as BNote; | 
					
						
							| 
									
										
										
										
											2018-01-24 23:08:14 -05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-04-04 22:00:20 +03:00
										 |  |  | function getTodayNote(rootNote: BNote | null = null) { | 
					
						
							| 
									
										
										
										
											2022-10-26 14:23:56 +02:00
										 |  |  |     return getDayNote(dateUtils.localNowDate(), rootNote); | 
					
						
							| 
									
										
										
										
											2019-11-27 23:07:10 +01:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-01 17:25:03 +02:00
										 |  |  | function getWeekStartDate(date: Date, startOfWeek: string): Date { | 
					
						
							| 
									
										
										
										
											2025-04-01 16:57:52 +02:00
										 |  |  |     const day = date.getDay(); | 
					
						
							|  |  |  |     let diff; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (startOfWeek === "monday") { | 
					
						
							|  |  |  |         diff = date.getDate() - day + (day === 0 ? -6 : 1); // adjust when day is sunday
 | 
					
						
							|  |  |  |     } else if (startOfWeek === "sunday") { | 
					
						
							|  |  |  |         diff = date.getDate() - day; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |         throw new Error(`Unrecognized start of the week ${startOfWeek}`); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const startDate = new Date(date); | 
					
						
							|  |  |  |     startDate.setDate(diff); | 
					
						
							|  |  |  |     return startDate; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-01 17:57:16 +02:00
										 |  |  | // TODO: Duplicated with getWeekNumber in src/public/app/widgets/buttons/calendar.ts
 | 
					
						
							|  |  |  | // Maybe can be merged later in monorepo setup
 | 
					
						
							|  |  |  | function getWeekNumberStr(date: Dayjs): string { | 
					
						
							|  |  |  |     const year = date.year(); | 
					
						
							|  |  |  |     const dayOfWeek = (day: number) => (day - parseInt(optionService.getOption("firstDayOfWeek")) + 7) % 7; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Get first day of the year and adjust to first week start
 | 
					
						
							|  |  |  |     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'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // 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
 | 
					
						
							|  |  |  |             if (thursday.year() < year) { | 
					
						
							|  |  |  |                 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'); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         // default case 0: week containing Jan 1 → already handled
 | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     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')); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Handle case when date belongs to first week of next year
 | 
					
						
							|  |  |  |     const nextYear = year + 1; | 
					
						
							|  |  |  |     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'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     switch (parseInt(optionService.getOption("firstWeekOfYear"))) { | 
					
						
							|  |  |  |         case 1: { | 
					
						
							|  |  |  |             const thursday = nextYearWeekStart.clone().add(3, 'day'); | 
					
						
							|  |  |  |             if (thursday.year() < nextYear) { | 
					
						
							|  |  |  |                 nextYearWeekStart = nextYearWeekStart.add(7, 'day'); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         case 2: { | 
					
						
							|  |  |  |             const daysInFirstWeek = 7 - offsetNext; | 
					
						
							|  |  |  |             if (daysInFirstWeek < parseInt(optionService.getOption("minDaysInFirstWeek"))) { | 
					
						
							|  |  |  |                 nextYearWeekStart = nextYearWeekStart.add(7, 'day'); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (date.isSameOrAfter(nextYearWeekStart)) { | 
					
						
							|  |  |  |         return `${nextYear}-W01`; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return `${year}-W${weekNumber.toString().padStart(2, '0')}`; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function getWeekFirstDayNote(dateStr: string, rootNote: BNote | null = null) { | 
					
						
							|  |  |  |     const startOfWeek = optionService.getOption("firstDayOfWeek") === '0' ? 'sunday' : 'monday'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const dateObj = getWeekStartDate(dateUtils.parseLocalDate(dateStr), startOfWeek); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     dateStr = dateUtils.utcDateTimeStr(dateObj); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return getDayNote(dateStr, rootNote); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function checkWeekNoteEnabled(rootNote: BNote) { | 
					
						
							|  |  |  |     if (!rootNote.hasLabel('enableWeekNote')) { | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-01 16:57:52 +02:00
										 |  |  | function getWeekNoteTitle(rootNote: BNote, weekNumber: number) { | 
					
						
							|  |  |  |     const pattern = rootNote.getOwnedLabelValue("weekPattern") || "Week {weekNumber}"; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return pattern | 
					
						
							|  |  |  |         .replace(/{weekNumber}/g, weekNumber.toString()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-01 17:25:03 +02:00
										 |  |  | function getWeekNote(weekStr: string, _rootNote: BNote | null = null): BNote | null { | 
					
						
							| 
									
										
										
										
											2025-04-01 16:57:52 +02:00
										 |  |  |     const rootNote = _rootNote || getRootCalendarNote(); | 
					
						
							| 
									
										
										
										
											2025-04-01 17:25:03 +02:00
										 |  |  |     if (!checkWeekNoteEnabled(rootNote)) { | 
					
						
							| 
									
										
										
										
											2025-04-01 16:57:52 +02:00
										 |  |  |         return null; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     weekStr = weekStr.trim().substring(0, 8); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     let weekNote = searchService.findFirstNoteWithQuery(`#${WEEK_LABEL}="${weekStr}"`, new SearchContext({ ancestorNoteId: rootNote.noteId })); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (weekNote) { | 
					
						
							|  |  |  |         return weekNote; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const [yearStr, weekNumStr] = weekStr.trim().split('-W'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const year = parseInt(yearStr); | 
					
						
							|  |  |  |     const weekNumber = parseInt(weekNumStr); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const firstDayOfYear = new Date(year, 0, 1); | 
					
						
							|  |  |  |     const weekStartDate = new Date(firstDayOfYear); | 
					
						
							|  |  |  |     weekStartDate.setDate(firstDayOfYear.getDate() + (weekNumber - 1) * 7); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-01 17:25:03 +02:00
										 |  |  |     const startDate = getWeekStartDate(weekStartDate, optionService.getOption("firstDayOfWeek") === '0' ? 'sunday' : 'monday'); | 
					
						
							| 
									
										
										
										
											2025-04-01 18:09:08 +02:00
										 |  |  |     const endDate = new Date(startDate); | 
					
						
							|  |  |  |     endDate.setDate(startDate.getDate() + 6); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const startMonth = startDate.getMonth(); | 
					
						
							|  |  |  |     const endMonth = endDate.getMonth(); | 
					
						
							| 
									
										
										
										
											2025-04-01 16:57:52 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-04-01 18:09:08 +02:00
										 |  |  |     const monthNote = getMonthNote(dateUtils.utcDateStr(startDate), rootNote); | 
					
						
							| 
									
										
										
										
											2025-04-01 16:57:52 +02:00
										 |  |  |     const noteTitle = getWeekNoteTitle(rootNote, weekNumber); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     sql.transactional(() => { | 
					
						
							|  |  |  |         weekNote = createNote(monthNote, noteTitle); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         attributeService.createLabel(weekNote.noteId, WEEK_LABEL, weekStr); | 
					
						
							|  |  |  |         attributeService.createLabel(weekNote.noteId, "sorted"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const weekTemplateAttr = rootNote.getOwnedAttribute("relation", "weekTemplate"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (weekTemplateAttr) { | 
					
						
							|  |  |  |             attributeService.createRelation(weekNote.noteId, "template", weekTemplateAttr.value); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-04-01 18:09:08 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // If the week spans different months, clone the week note in the other month as well
 | 
					
						
							|  |  |  |         if (startMonth !== endMonth) { | 
					
						
							|  |  |  |             const secondMonthNote = getMonthNote(dateUtils.utcDateStr(endDate), rootNote); | 
					
						
							|  |  |  |             cloningService.cloneNoteToParentNote(weekNote.noteId, secondMonthNote.noteId); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2025-04-01 16:57:52 +02:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return weekNote as unknown as BNote; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-07-18 21:47:30 +03:00
										 |  |  | export default { | 
					
						
							| 
									
										
										
										
											2018-04-07 13:03:16 -04:00
										 |  |  |     getRootCalendarNote, | 
					
						
							|  |  |  |     getYearNote, | 
					
						
							|  |  |  |     getMonthNote, | 
					
						
							| 
									
										
										
										
											2025-04-01 16:57:52 +02:00
										 |  |  |     getWeekNote, | 
					
						
							| 
									
										
										
										
											2025-04-01 14:46:35 +02:00
										 |  |  |     getWeekFirstDayNote, | 
					
						
							| 
									
										
										
										
											2022-01-10 17:09:20 +01:00
										 |  |  |     getDayNote, | 
					
						
							| 
									
										
										
										
											2019-11-27 23:07:10 +01:00
										 |  |  |     getTodayNote | 
					
						
							| 
									
										
										
										
											2020-06-20 12:31:38 +02:00
										 |  |  | }; |