diff --git a/packages/core/__benches__/reminders.bench.ts b/packages/core/__benches__/reminders.bench.ts
new file mode 100644
index 000000000..4e03cc6e7
--- /dev/null
+++ b/packages/core/__benches__/reminders.bench.ts
@@ -0,0 +1,186 @@
+/*
+This file is part of the Notesnook project (https://notesnook.com/)
+
+Copyright (C) 2023 Streetwriters (Private) Limited
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+*/
+
+import { bench, describe } from "vitest";
+import { databaseTest } from "../__tests__/utils";
+import Database from "../src/api";
+import MockDate from "mockdate";
+import dayjs from "dayjs";
+import { Reminder } from "../src/types";
+import {
+ createUpcomingReminderTimeQuery,
+ getUpcomingReminderTime
+} from "../src/collections/reminders";
+
+async function addReminders(db: Database) {
+ const reminders: Partial[] = [
+ {
+ recurringMode: "day",
+ date: new Date(0).setHours(14),
+ mode: "repeat"
+ },
+ {
+ recurringMode: "day",
+ date: new Date(0).setHours(3),
+ mode: "repeat",
+ title: "Random reminder"
+ },
+ {
+ recurringMode: "week",
+ date: new Date(0).setHours(8),
+ selectedDays: [3, 5],
+ mode: "repeat"
+ },
+ {
+ recurringMode: "week",
+ date: dayjs().hour(3).valueOf(),
+ selectedDays: [0],
+ mode: "repeat"
+ },
+ {
+ recurringMode: "week",
+ date: new Date(0).setHours(8),
+ selectedDays: [0, 5, 6],
+ mode: "repeat"
+ },
+ {
+ recurringMode: "week",
+ date: new Date(0).setHours(21),
+ selectedDays: [0, 1],
+ mode: "repeat"
+ },
+ {
+ recurringMode: "week",
+ date: new Date(5).setHours(21),
+ selectedDays: [1, 2, 5, 6],
+ mode: "repeat"
+ },
+ {
+ recurringMode: "month",
+ date: new Date(0).setHours(8),
+ selectedDays: [12, 18],
+ mode: "repeat"
+ },
+ {
+ recurringMode: "month",
+ date: new Date(0).setHours(3),
+ selectedDays: [1, 2, 3],
+ mode: "repeat"
+ },
+ {
+ recurringMode: "month",
+ date: new Date(0).setHours(21),
+ selectedDays: [6],
+ mode: "repeat"
+ },
+ {
+ recurringMode: "month",
+ date: new Date(0).setHours(21),
+ selectedDays: [6, 7, 8],
+ mode: "repeat"
+ },
+ {
+ date: new Date(2022, 5, 6, 8, 5).getTime(),
+ mode: "once"
+ },
+ {
+ date: new Date(2022, 5, 7, 8, 5).getTime(),
+ mode: "once"
+ },
+ {
+ date: new Date(2022, 5, 5, 8, 5).getTime(),
+ mode: "once"
+ },
+ {
+ date: new Date(2022, 5, 6, 5, 5).getTime(),
+ mode: "once"
+ },
+ {
+ date: new Date(2022, 5, 6, 3, 5).getTime(),
+ mode: "once"
+ },
+ {
+ date: new Date(2022, 5, 6, 5, 5).getTime(),
+ mode: "repeat",
+ recurringMode: "day"
+ },
+ {
+ recurringMode: "week",
+ date: new Date(0).setHours(3),
+ selectedDays: [1],
+ mode: "repeat"
+ },
+ {
+ recurringMode: "year",
+ date: dayjs().month(7).date(20).hour(5).minute(5).valueOf(),
+ selectedDays: [],
+ mode: "repeat"
+ },
+ {
+ recurringMode: "year",
+ date: dayjs().month(2).date(20).hour(5).minute(5).valueOf(),
+ selectedDays: [],
+ mode: "repeat"
+ }
+ ];
+ for (let i = 0; i < 10000; ++i) {
+ const random = reminders[getRandom(0, reminders.length)];
+ if (!random) continue;
+ await db.reminders.add({
+ title: "Random reminder",
+ ...random
+ });
+ if (i % 100 === 0) console.log(i);
+ }
+ console.log("DONE");
+}
+
+describe("reminders", async () => {
+ MockDate.set(new Date(2022, 5, 6, 5, 5, 0, 0));
+
+ const db = await databaseTest();
+ await addReminders(db);
+
+ bench("derive due date in sqlite", async () => {
+ await db
+ .sql()
+ .selectFrom("reminders")
+ .select([
+ createUpcomingReminderTimeQuery(dayjs().format("YYYY-MM-DDTHH:mm")).as(
+ "dueDate"
+ )
+ ])
+ .execute();
+ });
+
+ bench("derive due date in js", async () => {
+ const reminders = (await db
+ .sql()
+ .selectFrom("reminders")
+ .select(["type", "date", "mode", "recurringMode", "date", "selectedDays"])
+ .execute()) as Reminder[];
+ reminders.map((r) => getUpcomingReminderTime(r));
+ });
+
+ MockDate.reset();
+});
+
+function getRandom(min: number, max: number) {
+ return Math.round(Math.random() * (max - min) + min);
+}
diff --git a/packages/core/__tests__/reminders.test.js b/packages/core/__tests__/reminders.test.js
index 1ed4a9f57..5c1c50792 100644
--- a/packages/core/__tests__/reminders.test.js
+++ b/packages/core/__tests__/reminders.test.js
@@ -17,9 +17,18 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { formatReminderTime } from "../src/collections/reminders";
+import {
+ createIsReminderActiveQuery,
+ createUpcomingReminderTimeQuery,
+ formatReminderTime,
+ getUpcomingReminderTime,
+ isReminderActive
+} from "../src/collections/reminders";
import MockDate from "mockdate";
import { describe, afterAll, beforeAll, test, expect } from "vitest";
+import { databaseTest } from "./utils";
+import dayjs from "dayjs";
+import assert from "assert";
describe("format reminder time", () => {
afterAll(() => {
@@ -27,185 +36,297 @@ describe("format reminder time", () => {
});
beforeAll(() => {
- MockDate.set(new Date(2022, 5, 6, 5, 5));
+ MockDate.set(new Date(2022, 5, 6, 5, 5, 0, 0));
});
- test("daily reminder [today]", () => {
+ test("daily reminder [today]", async () => {
const reminder = {
recurringMode: "day",
date: new Date(0).setHours(14),
- mode: "repeat"
+ mode: "repeat",
+ title: "Random reminder"
};
+
+ expect(await compareReminder(reminder)).toBe(true);
expect(formatReminderTime(reminder)).toBe("Upcoming: Today, 02:00 PM");
});
- test("daily reminder [tomorrow]", () => {
+ test("daily reminder [tomorrow]", async () => {
const reminder = {
recurringMode: "day",
date: new Date(0).setHours(3),
- mode: "repeat"
+ mode: "repeat",
+ title: "Random reminder"
};
+
+ expect(await compareReminder(reminder)).toBe(true);
expect(formatReminderTime(reminder)).toBe("Upcoming: Tomorrow, 03:00 AM");
});
- test("weekly reminder [current week]", () => {
+ test("weekly reminder [current week]", async () => {
const reminder = {
recurringMode: "week",
date: new Date(0).setHours(8),
selectedDays: [3, 5],
- mode: "repeat"
+ mode: "repeat",
+ title: "Random reminder"
};
+
+ expect(await compareReminder(reminder)).toBe(true);
expect(formatReminderTime(reminder)).toBe(
"Upcoming: Wed, 08-06-2022 08:00 AM"
);
});
- test("weekly reminder [next week]", () => {
+ test("weekly reminder [next week]", async () => {
const reminder = {
recurringMode: "week",
- date: new Date(0).setHours(3),
+ date: dayjs().hour(3).valueOf(),
selectedDays: [0],
- mode: "repeat"
+ mode: "repeat",
+ title: "Random reminder"
};
+
+ expect(await compareReminder(reminder)).toBe(true);
expect(formatReminderTime(reminder)).toBe(
- "Upcoming: Sun, 12-06-2022 03:00 AM"
+ "Upcoming: Sun, 12-06-2022 03:05 AM"
);
});
- test("weekly reminder [current week, multiple days]", () => {
+ test("weekly reminder [current week, multiple days]", async () => {
const reminder = {
recurringMode: "week",
date: new Date(0).setHours(8),
selectedDays: [0, 5, 6],
- mode: "repeat"
+ mode: "repeat",
+ title: "Random reminder"
};
+
+ expect(await compareReminder(reminder)).toBe(true);
expect(formatReminderTime(reminder)).toBe(
"Upcoming: Fri, 10-06-2022 08:00 AM"
);
});
- test("weekly reminder [current week, today]", () => {
+ test("weekly reminder [current week, today]", async () => {
const reminder = {
recurringMode: "week",
date: new Date(0).setHours(21),
selectedDays: [0, 1],
- mode: "repeat"
+ mode: "repeat",
+ title: "Random reminder"
};
+
+ expect(await compareReminder(reminder)).toBe(true);
expect(formatReminderTime(reminder)).toBe("Upcoming: Today, 09:00 PM");
});
- test("weekly reminder [current week, today with multiple days]", () => {
+ test("weekly reminder [current week, today with multiple days]", async () => {
const reminder = {
recurringMode: "week",
date: new Date(5).setHours(21),
selectedDays: [1, 2, 5, 6],
- mode: "repeat"
+ mode: "repeat",
+ title: "Random reminder"
};
+
+ expect(await compareReminder(reminder)).toBe(true);
expect(formatReminderTime(reminder)).toBe("Upcoming: Today, 09:00 PM");
});
- test("monthly reminder [current month]", () => {
+ test("monthly reminder [current month]", async () => {
const reminder = {
recurringMode: "month",
date: new Date(0).setHours(8),
selectedDays: [12, 18],
- mode: "repeat"
+ mode: "repeat",
+ title: "Random reminder"
};
+
+ expect(await compareReminder(reminder)).toBe(true);
expect(formatReminderTime(reminder)).toBe(
"Upcoming: Sun, 12-06-2022 08:00 AM"
);
});
- test("monthly reminder [next month]", () => {
+ test("monthly reminder [next month]", async () => {
const reminder = {
recurringMode: "month",
date: new Date(0).setHours(3),
selectedDays: [1, 2, 3],
- mode: "repeat"
+ mode: "repeat",
+ title: "Random reminder"
};
+
+ expect(await compareReminder(reminder)).toBe(true);
expect(formatReminderTime(reminder)).toBe(
"Upcoming: Fri, 01-07-2022 03:00 AM"
);
});
- test("monthly reminder [current month, today]", () => {
+ test("monthly reminder [current month, today]", async () => {
const reminder = {
recurringMode: "month",
date: new Date(0).setHours(21),
selectedDays: [6],
- mode: "repeat"
+ mode: "repeat",
+ title: "Random reminder"
};
+
+ expect(await compareReminder(reminder)).toBe(true);
expect(formatReminderTime(reminder)).toBe("Upcoming: Today, 09:00 PM");
});
- test("monthly reminder [current month, today with multiple days]", () => {
+ test("monthly reminder [current month, today with multiple days]", async () => {
const reminder = {
recurringMode: "month",
date: new Date(0).setHours(21),
selectedDays: [6, 7, 8],
- mode: "repeat"
+ mode: "repeat",
+ title: "Random reminder"
};
+
+ expect(await compareReminder(reminder)).toBe(true);
expect(formatReminderTime(reminder)).toBe("Upcoming: Today, 09:00 PM");
});
- test("today", () => {
+ test("today", async () => {
const reminder = {
date: new Date(2022, 5, 6, 8, 5).getTime(),
- mode: "once"
+ mode: "once",
+ title: "Random reminder"
};
+
+ expect(await compareReminder(reminder)).toBe(true);
expect(formatReminderTime(reminder)).toBe("Upcoming: Today, 08:05 AM");
});
- test("tomorrow", () => {
+ test("tomorrow", async () => {
const reminder = {
date: new Date(2022, 5, 7, 8, 5).getTime(),
- mode: "once"
+ mode: "once",
+ title: "Random reminder"
};
+
+ expect(await compareReminder(reminder)).toBe(true);
expect(formatReminderTime(reminder)).toBe("Upcoming: Tomorrow, 08:05 AM");
});
- test("yesterday", () => {
+ test("yesterday", async () => {
const reminder = {
date: new Date(2022, 5, 5, 8, 5).getTime(),
- mode: "once"
+ mode: "once",
+ title: "Random reminder"
};
+
+ expect(await compareReminder(reminder)).toBe(true);
expect(formatReminderTime(reminder)).toBe("Last: Yesterday, 08:05 AM");
});
- test("exactly on time", () => {
+ test("exactly on time", async () => {
const reminder = {
date: new Date(2022, 5, 6, 5, 5).getTime(),
- mode: "once"
+ mode: "once",
+ title: "Random reminder"
};
+
+ expect(await compareReminder(reminder)).toBe(true);
expect(formatReminderTime(reminder)).toBe("Last: Today, 05:05 AM");
});
- test("past but still on the same day", () => {
+ test("past but still on the same day", async () => {
const reminder = {
date: new Date(2022, 5, 6, 3, 5).getTime(),
- mode: "once"
+ mode: "once",
+ title: "Random reminder"
};
+
+ expect(await compareReminder(reminder)).toBe(true);
expect(formatReminderTime(reminder)).toBe("Last: Today, 03:05 AM");
});
- test("the exact current time tomorrow", () => {
+ test("the exact current time tomorrow", async () => {
const reminder = {
date: new Date(2022, 5, 6, 5, 5).getTime(),
mode: "repeat",
- recurringMode: "day"
+ recurringMode: "day",
+ title: "Random reminder"
};
+
+ expect(await compareReminder(reminder)).toBe(true);
expect(formatReminderTime(reminder)).toBe("Upcoming: Tomorrow, 05:05 AM");
});
- test("same day next week because time has passed today", () => {
+ test("same day next week because time has passed today", async () => {
const reminder = {
recurringMode: "week",
date: new Date(0).setHours(3),
selectedDays: [1],
- mode: "repeat"
+ mode: "repeat",
+ title: "Random reminder"
};
+
+ expect(await compareReminder(reminder)).toBe(true);
expect(formatReminderTime(reminder)).toBe(
"Upcoming: Mon, 13-06-2022 03:00 AM"
);
});
+
+ test("yearly reminder [this year]", async () => {
+ const reminder = {
+ recurringMode: "year",
+ date: dayjs().month(7).date(20).hour(5).minute(5).valueOf(),
+ selectedDays: [],
+ mode: "repeat",
+ title: "Random reminder"
+ };
+
+ expect(await compareReminder(reminder)).toBe(true);
+ expect(formatReminderTime(reminder)).toBe(
+ "Upcoming: Sat, 20-08-2022 05:05 AM"
+ );
+ });
+
+ test("yearly reminder [next year]", async () => {
+ const reminder = {
+ recurringMode: "year",
+ date: dayjs().month(2).date(20).hour(5).minute(5).valueOf(),
+ selectedDays: [],
+ mode: "repeat",
+ title: "Random reminder"
+ };
+
+ expect(await compareReminder(reminder)).toBe(true);
+ expect(formatReminderTime(reminder)).toBe(
+ "Upcoming: Mon, 20-03-2023 05:05 AM"
+ );
+ });
});
+
+async function compareReminder(reminder) {
+ const db = await databaseTest();
+ const id = await db.reminders.add(reminder);
+ const result = await db
+ .sql()
+ .selectFrom("reminders")
+ .select([
+ createUpcomingReminderTimeQuery(dayjs().format("YYYY-MM-DDTHH:mm")).as(
+ "dueDate"
+ ),
+ createIsReminderActiveQuery(dayjs().format("YYYY-MM-DDTHH:mm")).as(
+ "isActive"
+ ),
+ "id"
+ ])
+ .where("id", "=", id)
+ .executeTakeFirst();
+
+ assert(
+ result.isActive === Number(isReminderActive(reminder)),
+ "is active value is not equal"
+ );
+ return (
+ result.dueDate ===
+ dayjs(getUpcomingReminderTime(reminder)).second(0).millisecond(0).valueOf()
+ );
+}
diff --git a/packages/core/src/collections/reminders.ts b/packages/core/src/collections/reminders.ts
index e547112f8..9db421345 100644
--- a/packages/core/src/collections/reminders.ts
+++ b/packages/core/src/collections/reminders.ts
@@ -29,6 +29,7 @@ import { Reminder } from "../types";
import Database from "../api";
import { SQLCollection } from "../database/sql-collection";
import { isFalse } from "../database";
+import { sql } from "kysely";
dayjs.extend(isTomorrow);
dayjs.extend(isSameOrBefore);
@@ -66,7 +67,7 @@ export class Reminders implements ICollection {
};
if (!reminder.date || !reminder.title)
- throw new Error("date and title are required in a reminder.");
+ throw new Error(`date and title are required in a reminder.`);
await this.collection.upsert({
id,
@@ -247,15 +248,57 @@ export function getUpcomingReminder(reminders: Reminder[]) {
}
export function isReminderActive(reminder: Reminder) {
- const time =
- reminder.mode === "once"
- ? reminder.date
- : getUpcomingReminderTime(reminder);
-
return (
!reminder.disabled &&
(reminder.mode !== "once" ||
- time > Date.now() ||
- (reminder.snoozeUntil && reminder.snoozeUntil > Date.now()))
+ reminder.date > Date.now() ||
+ (!!reminder.snoozeUntil && reminder.snoozeUntil > Date.now()))
);
}
+
+export function createUpcomingReminderTimeQuery(now = "now") {
+ return sql`CASE
+ WHEN mode = 'once' THEN date / 1000
+ WHEN recurringMode = 'year' THEN
+ strftime('%s',
+ strftime('%Y-', date(${now})) || strftime('%m-%d%H:%M', date / 1000, 'unixepoch', 'localtime'),
+ IIF(datetime(strftime('%Y-', date(${now})) || strftime('%m-%d%H:%M', date / 1000, 'unixepoch', 'localtime')) <= datetime(${now}), '+1 year', '+0 year'),
+ 'utc'
+ )
+ WHEN recurringMode = 'day' THEN
+ strftime('%s',
+ date(${now}) || time(date / 1000, 'unixepoch', 'localtime'),
+ IIF(datetime(date(${now}) || time(date / 1000, 'unixepoch', 'localtime')) <= datetime(${now}), '+1 day', '+0 day'),
+ 'utc'
+ )
+ WHEN recurringMode = 'week' AND selectedDays IS NOT NULL AND json_array_length(selectedDays) > 0 THEN
+ CASE
+ WHEN CAST(strftime('%w', date(${now})) AS INTEGER) > (SELECT MAX(value) FROM json_each(selectedDays))
+ OR datetime(date(${now}) || time(date / 1000, 'unixepoch', 'localtime')) <= datetime(${now})
+ THEN
+ strftime('%s', datetime(date(${now}), time(date / 1000, 'unixepoch', 'localtime'), '+1 day', 'weekday ' || json_extract(selectedDays, '$[0]'), 'utc'))
+ ELSE
+ strftime('%s', datetime(date(${now}), time(date / 1000, 'unixepoch', 'localtime'), 'weekday ' || (SELECT value FROM json_each(selectedDays) WHERE CAST(strftime('%w', date(${now})) AS INTEGER) <= value), 'utc'))
+ END
+ WHEN recurringMode = 'month' AND selectedDays IS NOT NULL AND json_array_length(selectedDays) > 0 THEN
+ CASE
+ WHEN CAST(strftime('%d', date(${now})) AS INTEGER) > (SELECT MAX(value) FROM json_each(selectedDays))
+ OR datetime(date(${now}) || time(date / 1000, 'unixepoch', 'localtime')) <= datetime(${now})
+ THEN
+ strftime('%s', strftime('%Y-%m-', date(${now})) || printf('%02d', json_extract(selectedDays, '$[0]')) || time(date / 1000, 'unixepoch', 'localtime'), '+1 month', 'utc')
+ ELSE strftime('%s', strftime('%Y-%m-', date(${now})) || (SELECT printf('%02d', value) FROM json_each(selectedDays) WHERE value <= strftime('%d', date(${now}))) || time(date / 1000, 'unixepoch', 'localtime'), 'utc')
+ END
+ ELSE strftime('%s', date(${now}) || time(date / 1000, 'unixepoch', 'localtime'), 'utc')
+ END * 1000
+`.$castTo();
+}
+
+export function createIsReminderActiveQuery(now = "now") {
+ return sql`IIF(
+ (disabled IS NULL OR disabled = 0)
+ AND (mode != 'once'
+ OR datetime(date / 1000, 'unixepoch', 'localtime') > datetime(${now})
+ OR (snoozeUntil IS NOT NULL
+ AND datetime(snoozeUntil / 1000, 'unixepoch', 'localtime') > datetime(${now}))
+ ), 1, 0)`.$castTo();
+}
diff --git a/packages/core/src/collections/settings.ts b/packages/core/src/collections/settings.ts
index 72faf6039..136e83407 100644
--- a/packages/core/src/collections/settings.ts
+++ b/packages/core/src/collections/settings.ts
@@ -40,7 +40,7 @@ const DEFAULT_GROUP_OPTIONS = (key: GroupingKey) =>
sortBy:
key === "trash"
? "dateDeleted"
- : key === "tags" || key === "reminders"
+ : key === "tags"
? "dateCreated"
: key === "reminders"
? "dueDate"
diff --git a/packages/core/src/database/index.ts b/packages/core/src/database/index.ts
index ce9672814..6dbff40dc 100644
--- a/packages/core/src/database/index.ts
+++ b/packages/core/src/database/index.ts
@@ -40,7 +40,6 @@ import {
Attachment,
Color,
ContentItem,
- GroupOptions,
HistorySession,
ItemType,
MaybeDeletedItem,
@@ -125,7 +124,6 @@ export interface DatabaseCollection {
get(id: string): AsyncOrSyncResult;
put(items: (T | undefined)[]): Promise[]>;
update(ids: string[], partial: Partial): Promise;
- ids(options: GroupOptions): AsyncOrSyncResult;
records(
ids: string[]
): AsyncOrSyncResult<
diff --git a/packages/core/src/database/sql-cached-collection.ts b/packages/core/src/database/sql-cached-collection.ts
index 042dd9a07..0a898cf8c 100644
--- a/packages/core/src/database/sql-cached-collection.ts
+++ b/packages/core/src/database/sql-cached-collection.ts
@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
-import { GroupOptions, MaybeDeletedItem, isDeleted } from "../types";
+import { MaybeDeletedItem, isDeleted } from "../types";
import EventManager from "../utils/event-manager";
import { DatabaseAccessor, DatabaseCollection, DatabaseSchema } from ".";
import { SQLCollection } from "./sql-collection";
@@ -126,10 +126,6 @@ export class SQLCachedCollection<
}
}
- ids(_options: GroupOptions): string[] {
- return Array.from(this.cache.keys());
- }
-
records(ids: string[]): Record | undefined> {
const items: Record | undefined> = {};
for (const id of ids) {
diff --git a/packages/core/src/database/sql-collection.ts b/packages/core/src/database/sql-collection.ts
index 4fe7971af..833084d40 100644
--- a/packages/core/src/database/sql-collection.ts
+++ b/packages/core/src/database/sql-collection.ts
@@ -34,6 +34,7 @@ import {
isFalse
} from ".";
import {
+ AliasedRawBuilder,
AnyColumn,
AnyColumnWithTable,
ExpressionOrFactory,
@@ -46,6 +47,10 @@ import { VirtualizedGrouping } from "../utils/virtualized-grouping";
import { groupArray } from "../utils/grouping";
import { toChunks } from "../utils/array";
import { Sanitizer } from "./sanitizer";
+import {
+ createIsReminderActiveQuery,
+ createUpcomingReminderTimeQuery
+} from "../collections/reminders";
const formats = {
month: "%Y-%m",
@@ -207,19 +212,6 @@ export class SQLCollection<
}
}
- async ids(sortOptions: GroupOptions): Promise {
- const ids = await this.db()
- .selectFrom(this.type)
- .select("id")
- .where(isFalse("deleted"))
- .$if(this.type === "notes" || this.type === "notebooks", (eb) =>
- eb.where(isFalse("dateDeleted"))
- )
- .orderBy(sortOptions.sortBy, sortOptions.sortDirection)
- .execute();
- return ids.map((id) => id.id);
- }
-
async records(
ids: string[]
): Promise | undefined>> {
@@ -432,26 +424,26 @@ export class FilteredSelector {
const fields: Array<
| AnyColumnWithTable
| AnyColumn
- > = ["id", "type", options.sortBy];
+ | AliasedRawBuilder
+ > = ["id", "type"];
if (this.type === "notes") fields.push("notes.pinned", "notes.conflicted");
else if (this.type === "notebooks") fields.push("notebooks.pinned");
else if (this.type === "attachments" && options.groupBy === "abc")
fields.push("attachments.filename");
- else if (this.type === "reminders") {
+ else if (this.type === "reminders" || options.sortBy === "dueDate") {
fields.push(
"reminders.mode",
- "reminders.date",
- "reminders.recurringMode",
- "reminders.selectedDays",
+ "reminders.snoozeUntil",
"reminders.disabled",
- "reminders.snoozeUntil"
+ "reminders.date",
+ createUpcomingReminderTimeQuery().as("dueDate")
);
- }
+ } else fields.push(options.sortBy);
return Array.from(
groupArray(
await this.filter
- .$call(this.buildSortExpression(options))
.select(fields)
+ .$call(this.buildSortExpression(options, true))
.execute(),
options
).values()
@@ -508,7 +500,7 @@ export class FilteredSelector {
private buildSortExpression(
options: GroupOptions | SortOptions,
- persistent?: boolean
+ hasDueDate?: boolean
) {
const sortBy: Set = new Set();
if (isGroupOptions(options)) {
@@ -524,6 +516,11 @@ export class FilteredSelector {
qb = qb.orderBy(sql`IFNULL(conflicted, 0) desc`);
if (this.type === "notes" || this.type === "notebooks")
qb = qb.orderBy(sql`IFNULL(pinned, 0) desc`);
+ if (this.type === "reminders")
+ qb = qb.orderBy(
+ (qb) => qb.parens(createIsReminderActiveQuery()),
+ "desc"
+ );
for (const item of sortBy) {
if (item === "title") {
@@ -538,36 +535,30 @@ export class FilteredSelector {
? formats[options.groupBy]
: null;
if (!timeFormat || isSortByDate(options)) {
- qb = qb.orderBy(item, options.sortDirection);
+ if (item === "dueDate") {
+ if (hasDueDate)
+ qb = qb.orderBy(item as any, options.sortDirection);
+ else
+ qb = qb.orderBy(
+ (qb) => qb.parens(createUpcomingReminderTimeQuery()),
+ options.sortDirection
+ );
+ } else qb = qb.orderBy(item, options.sortDirection);
continue;
}
qb = qb.orderBy(
sql`strftime('${sql.raw(timeFormat)}', ${sql.raw(
item
- )} / 1000, 'unixepoch')`,
+ )} / 1000, 'unixepoch', 'localtime')`,
options.sortDirection
);
}
}
- if (persistent) qb = qb.orderBy("id asc");
return qb;
};
}
-
- private sortFields(options: SortOptions, persistent?: boolean) {
- const fields: Array<
- | AnyColumnWithTable
- | AnyColumn
- > = [];
- if (this.type === "notes") fields.push("conflicted");
- if (this.type === "notes" || this.type === "notebooks")
- fields.push("pinned");
- fields.push(options.sortBy);
- if (persistent) fields.push("id");
- return fields;
- }
}
function isGroupOptions(
@@ -582,6 +573,7 @@ function isSortByDate(options: SortOptions | GroupOptions) {
options.sortBy === "dateEdited" ||
options.sortBy === "dateDeleted" ||
options.sortBy === "dateModified" ||
- options.sortBy === "dateUploaded"
+ options.sortBy === "dateUploaded" ||
+ options.sortBy === "dueDate"
);
}
diff --git a/packages/core/src/types.ts b/packages/core/src/types.ts
index 76bb7e0e3..926e4113f 100644
--- a/packages/core/src/types.ts
+++ b/packages/core/src/types.ts
@@ -45,7 +45,6 @@ export type GroupingKey =
| "notes"
| "notebooks"
| "tags"
- //| "topics"
| "trash"
| "favorites"
| "reminders";