From 98a0b53635ee24992c128cda5d37181fff7de326 Mon Sep 17 00:00:00 2001 From: Jonathan Clark Date: Fri, 6 Sep 2024 11:44:00 +0100 Subject: [PATCH] EventHelpers v0.22 --- helpers/NPdateTime.js | 4 +- helpers/dateTime.js | 2 +- jgclark.EventHelpers/CHANGELOG.md | 4 + jgclark.EventHelpers/README.md | 73 +++++++------ .../__tests__/eventsToNotes.test.js | 12 ++- jgclark.EventHelpers/plugin.json | 4 +- .../src/{config.js => eventsHelpers.js} | 16 +-- jgclark.EventHelpers/src/eventsToNotes.js | 100 ++++++++++++------ jgclark.EventHelpers/src/offsets.js | 17 +-- jgclark.EventHelpers/src/timeblocks.js | 2 +- 10 files changed, 139 insertions(+), 95 deletions(-) rename jgclark.EventHelpers/src/{config.js => eventsHelpers.js} (89%) diff --git a/helpers/NPdateTime.js b/helpers/NPdateTime.js index f2523dcf8..9168386b3 100644 --- a/helpers/NPdateTime.js +++ b/helpers/NPdateTime.js @@ -734,7 +734,7 @@ export function getNPWeekData(dateIn: string | Date = new Date(), offsetIncremen // This might be run from React side, where Calendar.* is not available. // If this happens, then instead offer the ISO week number. - if (typeof Calendar !== 'function') { + if (!Calendar || typeof Calendar !== 'object') { logInfo('NPdateTime::getNPWeekData', `NP's Calendar API functions are not available, so I will use moment instead. This doesn't know what your chosen first day of week is.`) const weekNumber = newMom.week() // uses moment locale const startDate = newMom.startOf('week').toDate() @@ -952,7 +952,7 @@ export function getRelativeDates(): Array { const relativeDates = [] const todayMom = moment() - if (typeof DataStore !== 'function') { + if (!DataStore || typeof DataStore !== 'object') { logDebug('NPdateTime::getRelativeDates', `NP DataStore functions are not available, so returning an empty set.`) return [{}] } diff --git a/helpers/dateTime.js b/helpers/dateTime.js index 1fc80c224..06bca5f32 100644 --- a/helpers/dateTime.js +++ b/helpers/dateTime.js @@ -155,7 +155,7 @@ export const isYearlyNote = (note: CoreNoteFields): boolean => new RegExp(RE_YEA * Note: also see getNoteType in note.js to get the type of a note in a more conversational way (e.g. -> 'Monthly') * @author @jgclark * @param {TNote} note - the note to look at - * @returns false | 'Daily' | 'Weekly' | 'Monthly' | 'Quarterly' | 'Yearly' | 'Project' + * @returns false | 'day' | 'week' | 'month' | 'quarter' | 'year' */ export function getCalendarNoteTimeframe(note: TNote): false | 'day' | 'week' | 'month' | 'quarter' | 'year' { if (note.type === 'Calendar') { diff --git a/jgclark.EventHelpers/CHANGELOG.md b/jgclark.EventHelpers/CHANGELOG.md index e05d7901c..97ddd29a9 100644 --- a/jgclark.EventHelpers/CHANGELOG.md +++ b/jgclark.EventHelpers/CHANGELOG.md @@ -2,6 +2,10 @@ See [website README for more details](https://github.com/NotePlan/plugins/tree/main/jgclark.EventHelpers), and how to configure. +## [0.22.0] - 2024-09-06 @jgclark +- can now use `events()` and `matchingEvents()` calls from Templates running on Weekly notes and other non-daily Calendar notes (for @gdrn). +- refactored documentation. + ## [0.21.3] - 2024-06-04 @jgclark - fix bug when adding time blocks to calendar - better handle template `<%- events() %>`, which has no formatter string diff --git a/jgclark.EventHelpers/README.md b/jgclark.EventHelpers/README.md index 6d26c05c0..53b90945f 100644 --- a/jgclark.EventHelpers/README.md +++ b/jgclark.EventHelpers/README.md @@ -1,7 +1,7 @@ # 🕓 Event Helpers plugin This plugin provides commands to help you do useful things with Events and Calendars that can't (yet) be done by NotePlan itself: -- **insert day's events as list**: insert a list of this day's calendar events into the current note +- **insert day's events as list**: insert a list of this day's calendar events into the current note -- including doing so automatically in **day/week note templates** - **insert matching events**: insert a list of this day's calendar events that match certain patterns into the current note - **time blocks to calendar**: takes [NotePlan-defined time blocks](https://help.noteplan.co/article/52-part-2-tasks-events-and-reminders#timeblocking) and converts to them to full Calendar events in your current default calendar, as set by iCal. (See also [Display of Time Blocks](#display-of-time-blocks) below.) - **process date offsets**: finds date offset patterns and turns them into due dates, based on date at start of section. (See [Date Offsets](#process-date-offsets) below for full details.) @@ -9,26 +9,42 @@ This plugin provides commands to help you do useful things with Events and Calen Most of these commands require configuration, described in the sections below. On macOS, click the gear button on the 'Event Helpers' line in the Plugin Preferences panel to access the settings. Or on on iOS/iPadOS devices, use the **Events: update plugin settings** command instead. +[Buy Me A Coffee](https://www.buymeacoffee.com/revjgc) +_This plugin is a labour of love designed to strengthen the NotePlan product and grow its community. To support my late-night coding you might like to buy me a coffee!_ + ## "insert day's events as list" and "insert matching events" commands Settings: - **Events heading**: in `/insert today's events as list` command (or `events()` template call), the heading to put before the list of the day's events. Optional. -- **Events List display format**: the format string to use to customise how events are displayed (when run as a /command, not through a Template). The available placeholders are 'CAL', 'TITLE', 'LOCATION', 'EVENTLINK', 'DATE', 'START', 'END', 'NOTES', 'ATTENDEES', 'NOTES', 'URL', 'MEETINGNOTE'. (Default is `- (*|CAL, |**|START|*) *|TITLE|**|\nEVENTLINK|**|\nwith ATTENDEES|**|\nNOTES|*`.) -- **Events List display format for all-day events**: the format string to use to customise how all-day events are displayed (when run as a /command, not through a Template). The available placeholders are 'CAL', 'TITLE', 'LOCATION', 'EVENTLINK', 'DATE', 'NOTES', 'ATTENDEES', 'NOTES', 'URL', 'MEETINGNOTE'. (Default is `- (*|CAL, |**|START|*) *|TITLE|**|\nEVENTLINK|**|\nwith ATTENDEES|**|\nNOTES|*`.) +- **Events List display format**: the format string to use to customise how events are displayed (when run as a /command, not through a Template). The available placeholders are `CAL`, `TITLE`, `LOCATION`, `EVENTLINK`, `DATE`, `START`, `END`, `NOTES`, `ATTENDEES`, `NOTES`, `URL`, `MEETINGNOTE`. Most of these are self-explanatory for events in most types of calendars, other than: + - `ATTENDEES` gives the full details of event attendees provided by the operating system, and can be a mix of names, email addresses, and other details; + - `ATTENDEENAMES` just gives the name of event attendees, or if that's missing, just the email address; + - `DATE` is formatted using the locale settings from your operating system, unless you override that with the 'Shared Settings > Locale' setting; + - `EVENTLINK` is specific to NotePlan: it will make a nicely-formatted link to the actual calendar event, and clicking on it will show a pop with all the event's details. + - `MEETINGNOTE` will insert a link that when clicked, will help you create a meeting note for this particular event. For this there's a setting 'Meeting Note Template title' which you can use to set which template to pick if you have several; if it isn't set then a list will be presented. + - `START` and `END` are the start and end times of the event. (The format of these can be controlled -- see 'Time options' below.) + + **Note**: each placeholder has to be surrounded by `*|...|*` to distinguish them as placeholders and not other markdown text to include. + + Default: `- (*|CAL, |**|START|*) *|TITLE|**|\nEVENTLINK|**|\nwith ATTENDEES|**|\nNOTES|*` +- **Events List display format for all-day events**: the format string to use to customise how all-day events are displayed (when run as a /command, not through a Template). The available placeholders are as above, with the exception of `START` and `END`. + + Default: `- (*|CAL, |**|START|*) *|TITLE|**|\nEVENTLINK|**|\nwith ATTENDEES|**|\nNOTES|*` - **Sort order of events list**: by 'time' (= increasing start time of events) or by 'calendar' first, which then secondarily sorts by start time. - **Calendars to include**: optional ["array","of calendar","names"] to filter by when showing list of events. If empty or missing, no filtering will be done. - **Calendar name mappings**: optional - add mappings for your calendar names to appear as in the output - e.g. from "Jonathan (iCloud)" to "Me" (and from "Us (iCloud)" to "Us") with `Jonathan (iCloud);Me, Us (iCloud);Us`. Note: separate mapping from main name by `;` a character, and separate mapping pairs with the `,` character. - **Meeting Note Template title**: use to set which template to pick if you have several; if it isn't set then a list of meeting note templates will be offered. - **Matching Events heading**: in `/insert matching events` command (or `listMatchingEvents()` template call), the heading to put before the list of matching events -- **Events match list**: for `/add matching events` is an array of pairs of strings. The first string is what is matched for in an event's title. If it does match, the second string is used as the format for how to insert the event details at the cursor. This uses the `*|TITLE|*`, `*|START|*` (time), `*|END|*` (time), `*|NOTES|*`, `*|ATTENDEES|*`, `*|EVENTLINK|*`, `*|LOCATION|*`, `*|MEETINGNOTE|*` and `*|URL|*` format items below ... -For example: +- **Events match list**: for `/add matching events` is an array of pairs of strings. The first string is what is matched for in an event's title. If it does match, the second string is used as the format for how to insert the event details at the cursor. This uses the same placeholders as above. For example: ```jsonc { "#meeting" : "### *|TITLE|* (*|START|*)\nWith *|ATTENDEES|**|\n NOTES|**|\nEVENTLINK|*", "holiday" : "*|TITLE|*\nHoliday:: *|NOTES|*" } ``` - You can also add `*|STOPMATCHING|*` placeholder which will mean only the first match in this list is applied for a given event. -- **Stop after first match in the list above?**: If true, only the first match in the list above is used for a given event. (Note: this doesn't stop matching the rest of the events in the Calendars.) This is the equivalent of setting 'STOPMATCHING' on every item in the above list. _Unfortunately, the NP plugin settings can change the order of the items in this list without warning. So to use this succesfully, you may need to manually edit the settings file, which is found at_ `/Plugins/jgclark.EventHelpers/settings.json`. The default is false. + You can also add the special `*|STOPMATCHING|*` placeholder which will mean only the first match in this list is applied for a given event. +- **Stop after first match in the list above?**: If true, only the first match in the list above is used for a given event. (Note: this doesn't stop matching the rest of the events in the Calendars.) This is the equivalent of setting 'STOPMATCHING' on every item in the above list. + + _Unfortunately, the NP plugin settings can change the order of the items in this list without warning. So to use this successfully, you may need to manually edit the settings file, which is found at_ `/Plugins/jgclark.EventHelpers/settings.json`. The default is `false`. - **Include time blocks from completed tasks?**: whether to include time blocks from lines with completed tasks. - **Name of Calendar to write to**: the name of the calendar for `/time blocks to calendar` to write events to. Must be a writable calendar. If empty, then the default system calendar will be used. (Note: you have to specifically set a default calendar in the settings of the macOS Calendar app or in iOS Settings app > Calendar > Default Calendar.) - **Default event duration**: Event duration (in minutes) to use when making an event from a time block, if no end time is given. @@ -37,41 +53,31 @@ For example: - **Add event link?**: whether to add a nicely-formatted event link when creating an event from a time block. (This can return rather long strings (e.g. `⏰event:287B39C1-4D0A-46DC-BD72-84D79167EFDF`) and so you might want to use a theme option to shorten them until needed (details [below](#theme-customisation)).) - **Processed tag name**: if this is set, then this tag will get added on the end of the line with the time block, to show that it has been processed. Otherwise, next time this command is run, it will create another event. This can be used with or without addEventID. - **Locale**: optional Locale to use for times in events. If not given, will default to what the OS reports, or failing that, 'en-US'. -- **Time options**: Optional Time format settings. Default is `{\n\t\"hour\": \"2-digit\", \n\t\"minute\": \"2-digit\", \n\t\"hour12\": false\n}`. +- **Time options**: Optional Time format settings. + + Default: `{\n\t\"hour\": \"2-digit\", \n\t\"minute\": \"2-digit\", \n\t\"hour12\": false\n}` ### Using Event Lists from a Template You can use these commands from Templates, when they are applied to a note, or are used to create one: -- **`<%- events(...) %>`** produces a list of all events for the period wherever you wish it to appear in the Template. By default it gives a simple markdown list of event title and start time for today. -- **`<%- matchingEvents(...) %>`** produces a list of all matching events. This is more powerful, with each configured match able to have a different format of output. The matches and format strings are entered in a JSON-formatted array, which is specified in the Plugin's settings. - -These work particularly well in **Daily Note templates**. - -To **customise the list display**, you can add a `'format:"..."'` parameter to the `<%- events() %>` command that sets how to present the list, and a separate parameter for items with no start/end times (`'allday_format:"..."`). - -**Formats**: The `*|CAL|*`, `*|TITLE|*`, `*|LOCATION|*`, `*|START|*`, `*|END|*`, `*|DATE|*`, `*|NOTES|*`, `*|URL|*`, `*|ATTENDEES|*`, `*|ATTENDEENAMES|*`, `*|LOCATION|*`, `*|MEETINGNOTE|*`, and `*|EVENTLINK|*` can be mixed with whatever markdown characters or other text you like, and they will get replaced accordingly with the fields from each matching event found. (Note the difference between the } and ) bracket types, and use of double quotes around the parameter's setting. I didn't design this syntax ...) +- **`<%- events(...) %>`** produces a list of all events for the period wherever you wish it to appear in the Template. By default it gives a simple markdown list of event title and start time for today, though this can all be customised. +- **`<%- matchingEvents(...) %>`** produces a list of all matching events. This is more powerful, with each configured match able to have a different format of output. The matches and format strings are entered in a JSON-formatted array, which can only be specified in the Plugin's settings. -Most of these are self-explanatory for events in most types of calendars, other than: -- `*|ATTENDEES|*` gives the full details of event attendees provided by the operating system, and can be a mix of names, email addresses, and other details; -- `*|ATTENDEENAMES|*` just gives the name of event attendees, or if that's missing, just the email address; -- `*|DATE|*` is formatted using the locale settings from your operating system, unless you override that with the 'Shared Settings > Locale' setting; -- `*|EVENTLINK|*` is specific to NotePlan: it will make a nicely-formatted link to the actual calendar event, and clicking on it will show a pop with all the event's details. -- `*|MEETINGNOTE|*` will insert a link that when clicked, will help you create a meeting note for this particular event. For this there's a setting 'Meeting Note Template title' which you can use to set which template to pick if you have several; if it isn't set then a list will be presented. **Note:** MEETINGNOTE links requires at least v1.1.2 of the separate "Meeting Notes" plugin. +These work particularly well in **Daily Note templates** (or in **Weekly Note templates** with `daysToCover: 7` -- see below). -v0.15.0 added more flexibility in the formatting of event lists. So now instead of including (for example) `*|ATTENDEENAMES|*` you can now include other text (including line breaks indicated by `\n`) within the placeholder. For example in `*|\nwith ATTENDEENAMES|*` if the ATTENDEENAMES is not empty, then it will output the list after a newline and the text 'with '. +You can customise the output by adding parameters to the commands. **Note**: these need to be comma separated, the values enclosed in double quotes, and all surrounded by curly quotes. (Sorry: I didn't design this syntax ...) For example: +```js +<%- events( {format:"### *|CAL|*: *|TITLE|* (*|START|*-*|END|*)*|\nEVENTLINK|**|with ATTENDEES|**|\nLOCATION|**|\nNOTES|*", allday_format:"### *|TITLE|**|\nEVENTLINK|**|\nNOTES|*", includeHeadings:false, calendars:"home,children"} ) %> +``` -Other options: +The following **Parameters** are available: - If you want to disable the adding of the heading, add the following parameter `includeHeadings:false` (no double quotes around `false` as its being treated as JSON) - If you want to exclude all-day events, add the following parameter `includeAllDayEvents:false` - If you want to include only certain calendars, then add the following parameter `calendarSet:"list,of,calendar,names"` -- If you want to include calendar name mapping (see disusssion above), then add something like the following parameter `calendarNameMappings:"Jonathan (iCloud);Me, Us (iCloud);Us"` +- If you want to include calendar name mapping (see discussion above), then add something like the following parameter `calendarNameMappings:"Jonathan (iCloud);Me, Us (iCloud);Us"` +- If you wish to see multiple day's output, not just the day for the active calendar note, add the `daysToCover` parameter. For example: include `daysToCover: 3` to the parameter string to see events for the selected day, plus the following 2. (Note: if you use this, then H3 date headings will be inserted between dates for clarity, even if the `includingHeadings` parameter is false.) +- If you wish to **format the list display** (only in `events()` command), you can add a `'format:"..."'` and/or an `'allday_format:"..."`. This uses the same placeholders (surrounded by `*|...|*`) as above, and can be mixed with whatever markdown characters or other text you like, and they will get replaced accordingly with the fields from each matching event found. -For example: - -```js -<%- events( {format:"### *|CAL|*: *|TITLE|* (*|START|*-*|END|*)*|\nEVENTLINK|**|with ATTENDEES|**|\nLOCATION|**|\nNOTES|*", allday_format:"### *|TITLE|**|\nEVENTLINK|**|\nNOTES|*", includeHeadings:false, calendars:"home,children"} ) %> -``` - -If you wish to see multiple day's output, not just the day for the active calendar note, add the `daysToCover` paramter. For example: include `daysToCover: 3` to the parameter string to see events for the selected day, plus the following 2. (Note: if you use this, then H3 date headings will be inserted between dates for clarity, even if the `includingHeadings` parameter is false.) +You can include other text (including line breaks indicated by `\n`) within the placeholder. For example in `*|\nwith ATTENDEENAMES|*`, if the ATTENDEENAMES is not empty, then it will output the attendees list on a newline and after the text 'with '. NB: the `Sort order` setting above also controls how the output of this list is sorted. @@ -147,8 +153,5 @@ If you're adding event IDs through the `/time blocks to calendar` command, then ## Support If you find an issue with this plugin, or would like to suggest new features for it, please raise a [Bug or Feature 'Issue'](https://github.com/NotePlan/plugins/issues). -[Buy Me A Coffee](https://www.buymeacoffee.com/revjgc) - -Thanks! ## History See [CHANGELOG](CHANGELOG.md) for the plugin's history. diff --git a/jgclark.EventHelpers/__tests__/eventsToNotes.test.js b/jgclark.EventHelpers/__tests__/eventsToNotes.test.js index cd51ce37c..06c0fd2fd 100644 --- a/jgclark.EventHelpers/__tests__/eventsToNotes.test.js +++ b/jgclark.EventHelpers/__tests__/eventsToNotes.test.js @@ -65,8 +65,8 @@ describe('eventsToNotes.js tests', () => { }) describe('smartStringReplace()', () => { - // This function's tests use $Shape<...> to use a subset of large objects without causing errors - const config: $Shape = { + // This function's tests use Partial<...> to use a subset of large objects without causing errors + const config: Partial = { calendarNameMappings: [], locale: 'se-SE', timeOptions: '', @@ -80,7 +80,8 @@ describe('eventsToNotes.js tests', () => { const endDT = new Date(2021, 0, 23, 22, 0, 0) const attendeesArray: Array = ['✓ Jonathan Clark', '? James Bond', 'x Martha', '? bob@example.com'] const attendeeNamesArray: Array = ['Jonathan Clark', 'Martha Clark', 'bob@example.com'] - const event1: $Shape = { + // $FlowFixMe[prop-missing] the Type has functions + const event1: Partial = { calendar: 'Jonathan', title: 'title of event1', url: 'https://example.com/easy', @@ -90,7 +91,8 @@ describe('eventsToNotes.js tests', () => { attendees: attendeesArray, attendeeNames: attendeeNamesArray, } // simple case - const event2: $Shape = { + // $FlowFixMe[prop-missing] the Type has functions + const event2: Partial = { calendar: 'Us', title: 'title of event2 with & more', url: 'https://example.com/bothersomeURL/example', @@ -100,7 +102,9 @@ describe('eventsToNotes.js tests', () => { attendees: attendeesArray, attendeeNames: attendeeNamesArray, } // case with inclusion + // $FlowIgnore[incompatible-call] only sending through what we need const replacements1 = e.getReplacements(event1, config) + // $FlowIgnore[incompatible-call] only sending through what we need const replacements2 = e.getReplacements(event2, config) test('event 1 format 1 easy', () => { diff --git a/jgclark.EventHelpers/plugin.json b/jgclark.EventHelpers/plugin.json index ca3243fa4..4bf58ae02 100644 --- a/jgclark.EventHelpers/plugin.json +++ b/jgclark.EventHelpers/plugin.json @@ -8,8 +8,8 @@ "plugin.author": "jgclark", "plugin.url": "https://github.com/NotePlan/plugins/tree/main/jgclark.EventHelpers", "plugin.changelog": "https://github.com/NotePlan/plugins/tree/main/jgclark.EventHelpers/CHANGELOG.md", - "plugin.version": "0.21.3", - "plugin.lastUpdateInfo": "0.21.3: bug fix adding time blocks to calendar. 0.21.2: /shiftDates now covers more cases.\n0.21.1: add 'Yes to all' to option to create time blocks. Extend '/shift dates' to work on week dates.\n0.21.0: improvements to '/shift dates' and '/process date offsets'.\n0.20.3: new 'STOPMATCHING' placeholder for '/insert matching events'.\n0.20.2: upper-case letters allowed in offsets.\n0.20.1: adds support for time blocks in Checklists (available from NP 3.8). 0.20.0: can now insert a 'MEETINGNOTE' link next to each item. Please read important note in the README file on how to enable it.\n0.19.4: can now include 'calendarSet' and 'calendarNameMappings' parameters when using event lists from Templates.\n0.19.3: de-dupes lists of attendees in event lists.", + "plugin.version": "0.22.0", + "plugin.lastUpdateInfo": "0.22.0: can now use events() template calls in Weekly notes.\n0.21.3: bug fix adding time blocks to calendar.\n0.21.2: /shiftDates now covers more cases.\n0.21.1: add 'Yes to all' to option to create time blocks. Extend '/shift dates' to work on week dates.\n0.21.0: improvements to '/shift dates' and '/process date offsets'.", "plugin.dependencies": [], "plugin.script": "script.js", "plugin.isRemote": "false", diff --git a/jgclark.EventHelpers/src/config.js b/jgclark.EventHelpers/src/eventsHelpers.js similarity index 89% rename from jgclark.EventHelpers/src/config.js rename to jgclark.EventHelpers/src/eventsHelpers.js index be9f6bec0..2220c9442 100644 --- a/jgclark.EventHelpers/src/config.js +++ b/jgclark.EventHelpers/src/eventsHelpers.js @@ -1,20 +1,12 @@ // @flow // ---------------------------------------------------------------------------- // Sort configuration for commands in the Event Helpers plugin. -// Last updated 29.9.2023 for v0.21.0, by @jgclark +// Last updated 2024-09-05 for v0.22.0, by @jgclark // @jgclark // ---------------------------------------------------------------------------- import pluginJson from "../plugin.json" -import { - castBooleanFromMixed, - castHeadingLevelFromMixed, - castNumberFromMixed, - castStringArrayFromMixed, - castStringFromMixed, - trimAnyQuotes, -} from '@helpers/dataManipulation' -import { type HourMinObj } from '@helpers/dateTime' +import { castStringFromMixed } from '@helpers/dataManipulation' import { clo, log, logDebug, logWarn, logError } from "@helpers/dev" import { type EventsConfig } from '@helpers/NPCalendar' import { showMessage } from '@helpers/userInput' @@ -22,7 +14,7 @@ import { showMessage } from '@helpers/userInput' //------------------------------------------------------------------------------ // Get settings -const configKey = 'events' +// const configKey = 'events' /** * Get config settings using Config V2 system. (Have now removed support for Config V1.) @@ -90,7 +82,7 @@ function getLocale(tempConfig: Object): string { // or if not available default function getTimeOptions(tempConfig: Object): Object { const env1224 = NotePlan?.environment ? NotePlan?.environment?.is12hFormat : false - let tempTimeOptions = tempConfig?.timeOptions ?? { hour: '2-digit', minute: '2-digit', hour12: env1224 } + const tempTimeOptions = tempConfig?.timeOptions ?? { hour: '2-digit', minute: '2-digit', hour12: env1224 } clo(tempTimeOptions, `tempTimeOptions: `) return tempTimeOptions } diff --git a/jgclark.EventHelpers/src/eventsToNotes.js b/jgclark.EventHelpers/src/eventsToNotes.js index ab505be27..f35c61124 100644 --- a/jgclark.EventHelpers/src/eventsToNotes.js +++ b/jgclark.EventHelpers/src/eventsToNotes.js @@ -1,21 +1,23 @@ // @flow // ---------------------------------------------------------------------------- // Command to bring calendar events into notes -// Last updated 4.6.2024 for v0.21.3 by @jgclark +// Last updated 2024-09-06 for v0.22.0, by @jgclark // @jgclark, with additions by @dwertheimer, @weyert, @m1well, @akrabat // ---------------------------------------------------------------------------- import pluginJson from '../plugin.json' -import { getEventsSettings } from './config' +import { getEventsSettings } from './eventsHelpers' import { getEventsForDay, type EventsConfig } from '@helpers/NPCalendar' import { calcOffsetDateStr, - getDateStringFromCalendarFilename, + getCalendarNoteTimeframe, + // getDateStringFromCalendarFilename, getDateFromUnhyphenatedDateString, - getISODateStringFromYYYYMMDD, + getDateStrForStartofPeriodFromCalendarFilename, + // getISODateStringFromYYYYMMDD, toLocaleDateString, toLocaleTime, - unhyphenateString, + // unhyphenateString, } from '@helpers/dateTime' import { clo, logDebug, logError, logWarn } from '@helpers/dev' import { getTagParamsFromString } from '@helpers/general' @@ -23,7 +25,7 @@ import { toNPLocaleDateString } from '@helpers/NPdateTime' import { showMessage } from '@helpers/userInput' /** - * Return MD list of the current open Calendar note's events + * Return markdown list of the current open Calendar note's events (and potentially the days after it) * @author @jgclark * * @param {string} paramString - checked for options @@ -35,6 +37,7 @@ export async function listDaysEvents(paramStringIn: string = ''): Promise 1) { - // $FlowIgnore[incompatible-call] - const localisedDateStr = toNPLocaleDateString(getDateFromUnhyphenatedDateString(dateStr)) - outputArray.push(config.eventsHeading !== '' ? `${config.eventsHeading} for ${localisedDateStr}` : `### for ${localisedDateStr}`) + const npDateStr = getDateFromUnhyphenatedDateString(dateStr) + if (!npDateStr) { + throw new Error(`Could not get valid NP date string from ${dateStr}`) + } + const localisedDateStr = toNPLocaleDateString(npDateStr) + // figure out H level to set: calc from config.eventsHeading or default to 2 + const hLevel = config.eventsHeading !== '' ? config.eventsHeading.split(' ')[0].length : 2 + outputArray.push(config.eventsHeading !== '' ? `${config.eventsHeading} for ${localisedDateStr}` : `${'#'.repeat(hLevel)} for ${localisedDateStr}`) } else { if (config.eventsHeading !== '' && includeHeadings) { outputArray.push(config.eventsHeading) @@ -93,14 +105,14 @@ export async function listDaysEvents(paramStringIn: string = ''): Promise = await getEventsForDay(dateStr, calendarSet) ?? [] - logDebug('listDaysEvents', `- ${eArr.length} events found on ${dateStr} from ${calendarSet.length} calendars ${String(calendarSet)}`) + // logDebug('listDaysEvents', `- ${eArr.length} events found on ${dateStr} from ${calendarSet.length} calendars ${String(calendarSet)}`) const mapForSorting: { cal: string, start: Date, text: string }[] = [] // Process each event for (const e of eArr) { - logDebug('listDaysEvents', `- Processing event '${e.title}' (${typeof e})`) + // logDebug('listDaysEvents', `- Processing event '${e.title}' (${typeof e})`) if (!includeAllDayEvents && e.isAllDay) { - logDebug('listDaysEvents', ` - skipping as event is all day and includeAllDayEvents is false`) + // logDebug('listDaysEvents', ` - skipping as event is all day and includeAllDayEvents is false`) continue } @@ -163,26 +175,44 @@ export async function insertDaysEvents(paramString: ?string): Promise { /** * Return string of matching events in the current day's note, from list in keys of config.addMatchingEvents, having applied placeholder formatting. + * Note: Parameters can be passed in as a JSON string, except for the complex 'format' which only comes from 'config.addMatchingEvents'. * @author @jgclark - * @param {?string} paramString Paramaters to use (for future expansion) + * @param {?string} paramStringIn Paramaters to use * @return {string} List of matching events, as a multi-line string */ export async function listMatchingDaysEvents( - paramString: string = '', // NB: the parameter isn't currently used, but is provided for future expansion. + paramStringIn: string = '', // NB: the parameter isn't currently used, but is provided for future expansion. ): Promise { try { - // $FlowIgnore[incompatible-call] - called by a function that checks Editor is valid. - const baseDateStr = getDateStringFromCalendarFilename(Editor.filename) - logDebug(pluginJson, `listMatchingDaysEvents: starting for date ${baseDateStr} with paramString=${paramString}`) + if (Editor.note == null || Editor.filename == null || Editor.type !== 'Calendar') { + await showMessage(`Please run again with a calendar note open.`, 'OK', 'List Events') + return '' + } + const openNote = Editor.note + + // handle getting no parameters passed at all + let paramString = '' + if (paramStringIn == null) { + logWarn('listMatchingDaysEvents', `No parameters passed (from template), so will use defaults.`) + } else { + paramString = paramStringIn + } - // Get config settings const config = await getEventsSettings() + // const baseDateStr = getDateStringFromCalendarFilename(Editor.filename) + // logDebug(pluginJson, `listMatchingDaysEvents: starting for date ${baseDateStr} with paramString=${paramString}`) + const noteTimeFrame = getCalendarNoteTimeframe(openNote) + if (!noteTimeFrame) throw new Error(`No noteTimeFrame found for note ${openNote.filename}. Stopping.`) + const startDayDateString = getDateStrForStartofPeriodFromCalendarFilename(Editor.filename) + logDebug(pluginJson, `listMatchingDaysEvents: starting for noteTimeFrame=${noteTimeFrame} / date ${startDayDateString} with paramString='${paramString}'`) + if (config.addMatchingEvents == null) { await showMessage(`Error: Empty 'addMatchingEvents' setting in Config. Stopping`, 'OK', 'List Matching Events') return `**Error: found no 'Add matching events' in plugin settings.**` } - const textToMatchArr = Object.keys(config.addMatchingEvents) + const formatArr = Object.values(config.addMatchingEvents) + const textToMatchArr = Object.keys(config.addMatchingEvents) logDebug('listMatchingDaysEvents', `- from settings found ${textToMatchArr.length} match strings to look for`) // Get a couple of other supplied parameters, or use defaults @@ -199,13 +229,20 @@ export async function listMatchingDaysEvents( // For each day to cover for (let i = 0; i < daysToCover; i++) { // Set dateStr to the day in question (YYYYMMDD) - const dateStr = unhyphenateString(calcOffsetDateStr(getISODateStringFromYYYYMMDD(baseDateStr), `+${i}d`)) + // const dateStr = unhyphenateString(calcOffsetDateStr(getISODateStringFromYYYYMMDD(baseDateStr), `+${i}d`)) + const dateStr = calcOffsetDateStr(startDayDateString, `+${i}d`) + logDebug('listDaysEvents', `${i}: startDayDateString=${startDayDateString}, dateStr=${dateStr}`) // Add heading if wanted, or if doing more than 1 day if (daysToCover > 1) { - // $FlowIgnore[incompatible-call] - const localisedDateStr = toNPLocaleDateString(getDateFromUnhyphenatedDateString(dateStr)) - outputArray.push(config.matchingEventsHeading !== '' ? `${config.matchingEventsHeading} for ${localisedDateStr}` : `### for ${localisedDateStr}`) + const npDateStr = getDateFromUnhyphenatedDateString(dateStr) + if (!npDateStr) { + throw new Error(`Could not get valid NP date string from ${dateStr}`) + } + const localisedDateStr = toNPLocaleDateString(npDateStr) + // figure out H level to set: calc from config.eventsHeading or default to 2 + const hLevel = config.matchingEventsHeading !== '' ? config.matchingEventsHeading.split(' ')[0].length : 2 + outputArray.push(config.matchingEventsHeading !== '' ? `${config.matchingEventsHeading} for ${localisedDateStr}` : `${'#'.repeat(hLevel)} for ${localisedDateStr}`) } else { if (config.matchingEventsHeading !== '' && includeHeadings) { outputArray.push(config.matchingEventsHeading) @@ -216,12 +253,15 @@ export async function listMatchingDaysEvents( const eArr: Array = await getEventsForDay(dateStr, calendarSet) ?? [] const mapForSorting: { cal: string, start: Date, text: string }[] = [] - // for each event, check each of the strings we want to match + // Process each event for (const e of eArr) { + logDebug('listMatchingDaysEvents', `- Processing event '${e.title}' (${typeof e})`) if (!includeAllDayEvents && e.isAllDay) { + logDebug('listMatchingDaysEvents', ` - skipping as event is all day and includeAllDayEvents is false`) continue } + // for each event, check each of the strings we want to match for (let j = 0; j < textToMatchArr.length; j++) { const thisFormat: string = String(formatArr[j]) const withCalendarName = thisFormat.includes('CAL') @@ -249,7 +289,7 @@ export async function listMatchingDaysEvents( } } - // If there are matching events + // Sort the events (if there are matching events) if (mapForSorting.length > 0) { // Sort the matched events if (config.sortOrder === 'calendar') { diff --git a/jgclark.EventHelpers/src/offsets.js b/jgclark.EventHelpers/src/offsets.js index 5e278aab4..e6773a97e 100644 --- a/jgclark.EventHelpers/src/offsets.js +++ b/jgclark.EventHelpers/src/offsets.js @@ -2,7 +2,7 @@ // ---------------------------------------------------------------------------- // Command to Process Date Offsets and Shifts // @jgclark -// Last updated 13.2.2024 for v0.21.2, by @jgclark +// Last updated 13.2.2024 for v0.21.2+, by @jgclark // ---------------------------------------------------------------------------- // TODO: // * [Allow other date styles in /process date offsets](https://github.com/NotePlan/plugins/issues/221) from Feb 2021 -- but much harder than it looks. @@ -10,14 +10,14 @@ import pluginJson from '../plugin.json' -import { getEventsSettings } from './config' +import { getEventsSettings } from './eventsHelpers' import { timeBlocksToCalendar } from './timeblocks' import { calcOffsetDateStr, RE_BARE_DATE_CAPTURE, RE_BARE_DATE, - RE_BARE_WEEKLY_DATE, - RE_BARE_WEEKLY_DATE_CAPTURE, + // RE_BARE_WEEKLY_DATE, + // RE_BARE_WEEKLY_DATE_CAPTURE, RE_DATE_INTERVAL, RE_DONE_DATE_OPT_TIME, RE_ISO_DATE, @@ -25,10 +25,10 @@ import { RE_OFFSET_DATE, RE_OFFSET_DATE_CAPTURE, splitIntervalToParts, - toISODateString, - toLocaleDateString, + // toISODateString, + // toLocaleDateString, } from '@helpers/dateTime' -import { getNPWeekData } from '@helpers/NPdateTime' +// import { getNPWeekData } from '@helpers/NPdateTime' import { clo, log, logDebug, logError, logInfo, logWarn } from '@helpers/dev' import { displayTitle } from '@helpers/general' import { findEndOfActivePartOfNote } from '@helpers/paragraph' @@ -155,8 +155,9 @@ export async function shiftDates(): Promise { originalDateStr = thisDate // v1: but doesn't handle different start-of-week settings // shiftedDateStr = calcOffsetDateStr(originalDateStr, interval) + // v2: using NPdateTime::getNPWeekData instead - const thisWeekInfo = getNPWeekData(originalDateStr, intervalParts.number, intervalParts.type) + // const thisWeekInfo = getNPWeekData(originalDateStr, intervalParts.number, intervalParts.type) // Replace date part with the new shiftedDateStr updatedContent = updatedContent.replace(originalDateStr, shiftedDateStr) logDebug(pluginJson, `- ${originalDateStr}: match found -> ${shiftedDateStr} from interval ${interval}`) diff --git a/jgclark.EventHelpers/src/timeblocks.js b/jgclark.EventHelpers/src/timeblocks.js index b9f877454..d6990aac9 100644 --- a/jgclark.EventHelpers/src/timeblocks.js +++ b/jgclark.EventHelpers/src/timeblocks.js @@ -6,7 +6,7 @@ // Last updated 5.2.2022 for v0.11.4, by @jgclark // ------------------------------------------------------------------------------------ -import { getEventsSettings } from './config' +import { getEventsSettings } from './eventsHelpers' import { writeTimeBlocksToCalendar } from '@helpers/NPCalendar' /**