diff --git a/packages/core/api/index.js b/packages/core/api/index.js index b3e81104f..3497a6c14 100644 --- a/packages/core/api/index.js +++ b/packages/core/api/index.js @@ -48,6 +48,7 @@ import Pricing from "./pricing"; import { logger } from "../logger"; import Shortcuts from "../collections/shortcuts"; import Reminders from "../collections/reminders"; +import Relations from "../collections/relations"; /** * @type {EventSource} @@ -146,6 +147,8 @@ class Database { this.shortcuts = await Shortcuts.new(this, "shortcuts"); /**@type {Reminders} */ this.reminders = await Reminders.new(this, "reminders"); + /**@type {Relations} */ + this.relations = await Relations.new(this, "relations"); this.trash = new Trash(this); diff --git a/packages/core/api/migrations.js b/packages/core/api/migrations.js index de6335fab..5c73a9fb0 100644 --- a/packages/core/api/migrations.js +++ b/packages/core/api/migrations.js @@ -86,6 +86,10 @@ class Migrations { index: () => this._db.reminders.raw, dbCollection: this._db.reminders }, + { + index: () => this._db.relations.raw, + dbCollection: this._db.relations + }, { index: () => this._db.noteHistory.sessionContent.all(), dbCollection: this._db.noteHistory diff --git a/packages/core/api/sync/collector.js b/packages/core/api/sync/collector.js index cb322139e..a2d96516a 100644 --- a/packages/core/api/sync/collector.js +++ b/packages/core/api/sync/collector.js @@ -44,6 +44,7 @@ class Collector { content: await this._db.content.all(), attachment: this._db.attachments.syncable, reminder: this._db.reminders.raw, + relation: this._db.relations.raw, settings: [this._db.settings.raw] }; diff --git a/packages/core/api/sync/merger.js b/packages/core/api/sync/merger.js index 32c2e376d..1b978a8d2 100644 --- a/packages/core/api/sync/merger.js +++ b/packages/core/api/sync/merger.js @@ -50,6 +50,10 @@ class Merger { get: (id) => this._db.reminders.reminder(id), set: (item) => this._db.reminders.merge(item) }, + relation: { + get: (id) => this._db.relations.relation(id), + set: (item) => this._db.relations.merge(item) + }, notebook: { threshold: 1000, get: (id) => this._db.notebooks.notebook(id), diff --git a/packages/core/collections/relations.js b/packages/core/collections/relations.js new file mode 100644 index 000000000..ca4d31110 --- /dev/null +++ b/packages/core/collections/relations.js @@ -0,0 +1,148 @@ +/* +This file is part of the Notesnook project (https://notesnook.com/) + +Copyright (C) 2022 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 getId from "../utils/id"; +import Collection from "./collection"; + +/** + * @typedef {{ + * id: string; + * type: string; + * }} ItemReference + * + * @typedef {{ + * id: string; + * type: string; + * from: ItemReference; + * to: ItemReference; + * dateCreated: number; + * dateModified: number; + * }} Relation + */ + +export default class Relations extends Collection { + async merge(relation) { + if (!relation) return; + await this._collection.addItem(relation); + } + + /** + * + * @param {ItemReference} from + * @param {ItemReference} to + */ + async add(from, to) { + if ( + this.all.find( + (a) => + compareItemReference(a.from, from) && compareItemReference(a.to, to) + ) + ) + return; + + const relation = { + id: getId(), + type: "relation", + dateCreated: Date.now(), + dateModified: Date.now(), + from: { id: from.id, type: from.type }, + to: { id: to.id, type: to.type } + }; + + await this._collection.addItem(relation); + } + + /** + * + * @param {ItemReference} reference + * @param {string} type + */ + from(reference, type) { + const relations = this.all.filter( + (a) => compareItemReference(a.from, reference) && a.to.type === type + ); + return this.resolve(relations, "to"); + } + + /** + * + * @param {ItemReference} reference + * @param {string} type + */ + to(reference, type) { + const relations = this.all.filter( + (a) => compareItemReference(a.to, reference) && a.from.type === type + ); + return this.resolve(relations, "from"); + } + + get raw() { + return this._collection.getRaw(); + } + + /** + * @return {Relation[]} + */ + get all() { + return this._collection.getItems(); + } + + /** + * @return {Relation} + */ + relation(id) { + return this._collection.getItem(id); + } + + async remove(...ids) { + for (const id of ids) { + await this._collection.removeItem(id); + } + } + + /** + * @param {Relation[]} relations + * @param {"from" | "to"} resolveType + * @private + */ + async resolve(relations, resolveType) { + const items = []; + for (const relation of relations) { + const reference = resolveType === "from" ? relation.from : relation.to; + + let item = null; + switch (reference.type) { + case "reminder": + item = this._db.reminders.reminder(reference.id); + break; + } + if (!item) await this.remove(relation.id); + } + return items; + } +} + +/** + * + * @param {ItemReference} a + * @param {ItemReference} b + */ +function compareItemReference(a, b) { + return a.id === b.id && a.type === b.type; +} diff --git a/packages/core/database/backup.js b/packages/core/database/backup.js index 01b2b8292..a67401907 100644 --- a/packages/core/database/backup.js +++ b/packages/core/database/backup.js @@ -179,6 +179,10 @@ export default class Backup { index: () => data["reminders"], dbCollection: this._db.reminders }, + { + index: () => data["relations"], + dbCollection: this._db.relations + }, { index: () => data["notehistory"], dbCollection: this._db.noteHistory,