core: sort & group reminders in sqlite

This commit is contained in:
Abdullah Atta
2024-03-13 08:59:23 +05:00
parent 00ecb149df
commit 67fa1d6bb4
8 changed files with 431 additions and 96 deletions

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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<Reminder>[] = [
{
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);
}

View File

@@ -17,9 +17,18 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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()
);
}

View File

@@ -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<number>();
}
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<boolean>();
}

View File

@@ -40,7 +40,7 @@ const DEFAULT_GROUP_OPTIONS = (key: GroupingKey) =>
sortBy:
key === "trash"
? "dateDeleted"
: key === "tags" || key === "reminders"
: key === "tags"
? "dateCreated"
: key === "reminders"
? "dueDate"

View File

@@ -40,7 +40,6 @@ import {
Attachment,
Color,
ContentItem,
GroupOptions,
HistorySession,
ItemType,
MaybeDeletedItem,
@@ -125,7 +124,6 @@ export interface DatabaseCollection<T, IsAsync extends boolean> {
get(id: string): AsyncOrSyncResult<IsAsync, T | undefined>;
put(items: (T | undefined)[]): Promise<SQLiteItem<T>[]>;
update(ids: string[], partial: Partial<T>): Promise<void>;
ids(options: GroupOptions): AsyncOrSyncResult<IsAsync, string[]>;
records(
ids: string[]
): AsyncOrSyncResult<

View File

@@ -17,7 +17,7 @@ You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
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<string, MaybeDeletedItem<T> | undefined> {
const items: Record<string, MaybeDeletedItem<T> | undefined> = {};
for (const id of ids) {

View File

@@ -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<string[]> {
const ids = await this.db()
.selectFrom<keyof DatabaseSchema>(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<Record<string, MaybeDeletedItem<T> | undefined>> {
@@ -432,26 +424,26 @@ export class FilteredSelector<T extends Item> {
const fields: Array<
| AnyColumnWithTable<DatabaseSchema, keyof DatabaseSchema>
| AnyColumn<DatabaseSchema, keyof DatabaseSchema>
> = ["id", "type", options.sortBy];
| AliasedRawBuilder<number, "dueDate">
> = ["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<T extends Item> {
private buildSortExpression(
options: GroupOptions | SortOptions,
persistent?: boolean
hasDueDate?: boolean
) {
const sortBy: Set<SortOptions["sortBy"]> = new Set();
if (isGroupOptions(options)) {
@@ -524,6 +516,11 @@ export class FilteredSelector<T extends Item> {
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<T extends Item> {
? 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<DatabaseSchema, keyof DatabaseSchema>
| AnyColumn<DatabaseSchema, keyof DatabaseSchema>
> = [];
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"
);
}

View File

@@ -45,7 +45,6 @@ export type GroupingKey =
| "notes"
| "notebooks"
| "tags"
//| "topics"
| "trash"
| "favorites"
| "reminders";