From bff145d27fbb8d6958f1f82b7928db7f3f8ce88c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Pluta?= Date: Mon, 9 Jun 2025 13:44:52 +0200 Subject: [PATCH] Add support for multiple reminders --- grammar/task.pegjs | 4 +++- src/backend/index.ts | 6 +++--- src/database/serializer.ts | 37 +++++++++++++++++++++++++++++-------- src/model/task.ts | 8 ++++---- src/types/grammar.ts | 2 +- src/types/task.ts | 2 +- 6 files changed, 41 insertions(+), 18 deletions(-) diff --git a/grammar/task.pegjs b/grammar/task.pegjs index 251444c..75c7dea 100644 --- a/grammar/task.pegjs +++ b/grammar/task.pegjs @@ -74,13 +74,15 @@ delete = type:deleteIcon _ action:[a-zA-Z]+ { /**************************************************************************************************************************************/ -reminder = reminderIcon _ time:(longTime / shortTime)? { +reminder = reminderIcon _ time:(defaultTime / longTime / shortTime)|..,(_ "," _)| { return { feature: "reminder", time } } +defaultTime = "_" + longTime = hour:[0-9]|1..2| ":" minute:[0-9]|1..2| { return `${hour.join("").padStart(2, '0')}:${minute.join("").padStart(2, '0')}`; } diff --git a/src/backend/index.ts b/src/backend/index.ts index 10d6d72..90a0c73 100644 --- a/src/backend/index.ts +++ b/src/backend/index.ts @@ -16,14 +16,14 @@ const backends = [ export async function remind(config: Config, profileConfig: ProfileConfig, db: TaskDatabase) { const now = dayjs().format("HH:mm"); - await run(config, profileConfig, db, db[now]); + await run(config, profileConfig, db[now]); if(profileConfig?.defaultTime && profileConfig?.defaultTime === now) { - run(config, profileConfig, db, db.default); + run(config, profileConfig, db._); } } -async function run(config: Config, profileConfig: ProfileConfig, db: TaskDatabase, tasks?: Task[]) { +async function run(config: Config, profileConfig: ProfileConfig, tasks?: Task[]) { if(!tasks) { return; } diff --git a/src/database/serializer.ts b/src/database/serializer.ts index 5419066..adc793f 100644 --- a/src/database/serializer.ts +++ b/src/database/serializer.ts @@ -1,6 +1,10 @@ import fs from "fs"; import { Task, TaskDatabase } from "../types/task"; +type FlatReminder = { + task: Task; + time: string; +} /** * Applies the mapper for each task from list, groups them by time and dumps the data into JSON formatted file. @@ -21,12 +25,29 @@ export function serializeDatabase(tasks: Task[]): string { * Applies the mapper for each task from list and groups them by time. */ export function createDatabase(tasks: Task[]): TaskDatabase { - - return tasks.filter(t => t.reminder).reduce((acc, t) => { - if (t.reminder) { - (acc[t.reminder] = (acc[t.reminder] || [])).push(t); - }; - - return acc; - }, {} as TaskDatabase); + return tasks + .flatMap(flatTimes) + .reduce(pushTaskToDatabase, {} as TaskDatabase); } + +/** + * Returns list pairs of input task and consecutive reminder times. + * If reminder is missing, returns empty array. + * If reminder is empty array, returns ['_'], where '_' designates the default time. + * @param task input task + * @returns list pairs of input task and consecutive reminder times + */ +function flatTimes(task: Task): FlatReminder[] { + if (task.reminder === undefined) { + return []; + } + + return task.reminder.length === 0 + ? [{ task, time: '_' }] + : task.reminder.map(time => ({ task, time })); +} + +function pushTaskToDatabase(db: TaskDatabase, { task, time }: FlatReminder): TaskDatabase { + (db[time] = db[time] ?? []).push(task); + return db; +} \ No newline at end of file diff --git a/src/model/task.ts b/src/model/task.ts index d21bf6d..4600b8e 100644 --- a/src/model/task.ts +++ b/src/model/task.ts @@ -128,11 +128,11 @@ export class DynamicTask implements Task { return this.#features("dependency").find(x => x.type === '⛔')?.deps || []; } - get reminder(): string|undefined { + get reminder(): string[]|undefined { const feature = this.parsed.meta.find(x => x.feature === 'reminder'); if (feature) { - return feature.time || "default"; + return feature.time; } return undefined; @@ -209,7 +209,7 @@ export class DynamicTask implements Task { o("delete", this.onDelete), o("id", this.id), o("deps", this.dependsOn?.join(",")), - o("reminder", this.reminder), + o("reminder", this.reminder?.join(",")), o("tags", this.tags.join(",")) ]; @@ -255,7 +255,7 @@ export class DynamicTask implements Task { break; case 'reminder': - segments.push(['⏰', meta.time ?? ""]); + segments.push(['⏰', meta.time.join(",")]); break; case 'recurrence': diff --git a/src/types/grammar.ts b/src/types/grammar.ts index fb02e6f..2308963 100644 --- a/src/types/grammar.ts +++ b/src/types/grammar.ts @@ -54,5 +54,5 @@ export type ParsedTaskDependency = { export type ParsedTaskReminder = { feature: 'reminder'; - time: string; + time: string[]; } \ No newline at end of file diff --git a/src/types/task.ts b/src/types/task.ts index a056c62..553616b 100644 --- a/src/types/task.ts +++ b/src/types/task.ts @@ -17,7 +17,7 @@ export type Task = { onDelete?: string; id?: string; dependsOn?: string[]; - reminder?: string; + reminder?: string[]; sourceFile: string; sourceLine: number; source: string;