diff --git a/packages/core/__e2e__/monographs.test.js b/packages/core/__e2e__/monographs.test.js
index 0509e6b1d..d59373526 100644
--- a/packages/core/__e2e__/monographs.test.js
+++ b/packages/core/__e2e__/monographs.test.js
@@ -26,7 +26,7 @@ const TEST_TIMEOUT = 30 * 1000;
afterAll(async () => {
const db = await databaseTest();
await login(db);
- await db.monographs.init();
+ await db.monographs.refresh();
for (const id of db.monographs.monographs) {
await db.monographs.unpublish(id);
@@ -39,7 +39,7 @@ afterAll(async () => {
// databaseTest().then(async (db) => {
// await db.user.login(user.email, user.password, user.hashedPassword);
-// await db.monographs.init();
+// await db.monographs.refresh();
// expect(db.monographs.all).toBeGreaterThanOrEqual(0);
// }));
@@ -49,7 +49,7 @@ test(
() =>
noteTest().then(async ({ db, id }) => {
await login(db);
- await db.monographs.init();
+ await db.monographs.refresh();
const monographId = await db.monographs.publish(id);
@@ -70,7 +70,7 @@ test(
() =>
noteTest().then(async ({ db, id }) => {
await login(db);
- await db.monographs.init();
+ await db.monographs.refresh();
const monographId = await db.monographs.publish(id);
let monograph = await db.monographs.get(monographId);
@@ -93,7 +93,7 @@ test(
() =>
noteTest().then(async ({ db, id }) => {
await login(db);
- await db.monographs.init();
+ await db.monographs.refresh();
await db.monographs.publish(id);
expect(db.monographs.all.find((m) => m.id === id)).toBeDefined();
diff --git a/packages/core/src/api/index.ts b/packages/core/src/api/index.ts
index c86610b57..c41e4da85 100644
--- a/packages/core/src/api/index.ts
+++ b/packages/core/src/api/index.ts
@@ -36,7 +36,7 @@ import { LegacySettings } from "../collections/legacy-settings";
import Migrations from "./migrations";
import UserManager from "./user-manager";
import http from "../utils/http";
-import Monographs from "./monographs";
+import { Monographs } from "./monographs";
import { Offers } from "./offers";
import { Attachments } from "../collections/attachments";
import { Debug } from "./debug";
@@ -174,7 +174,7 @@ class Database {
await this.fs().cancel(attachment.metadata.hash, "download");
});
EV.subscribe(EVENTS.userLoggedOut, async () => {
- await this.monographs.deinit();
+ await this.monographs.clear();
await this.fs().clear();
this.disconnectSSE();
});
@@ -208,7 +208,9 @@ class Database {
await this.trash.init();
- this.monographs.init().catch(console.error);
+ // we must not wait on network requests that's why
+ // no await
+ this.monographs.refresh();
}
disconnectSSE() {
diff --git a/packages/core/src/api/monographs.js b/packages/core/src/api/monographs.js
deleted file mode 100644
index 47fba028f..000000000
--- a/packages/core/src/api/monographs.js
+++ /dev/null
@@ -1,165 +0,0 @@
-/*
-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 http from "../utils/http";
-import Constants from "../utils/constants";
-
-class Monographs {
- /**
- *
- * @param {import("./index").default} db
- */
- constructor(db) {
- this._db = db;
- this.monographs = undefined;
- }
-
- async deinit() {
- this.monographs = undefined;
- await this._db.storage().write("monographs", this.monographs);
- }
-
- async init() {
- try {
- const user = await this._db.user.getUser();
- const token = await this._db.tokenManager.getAccessToken();
- if (!user || !token || !user.isEmailConfirmed) return;
- let monographs = await this._db.storage().read("monographs", true);
- monographs = await http.get(`${Constants.API_HOST}/monographs`, token);
- await this._db.storage().write("monographs", monographs);
-
- if (monographs) this.monographs = monographs;
- } catch (e) {
- console.error(e);
- }
- }
-
- /**
- * Check if note is published.
- * @param {string} noteId id of the note
- * @returns {boolean} Whether note is published or not.
- */
- isPublished(noteId) {
- return this.monographs && this.monographs.indexOf(noteId) > -1;
- }
-
- /**
- * Get note published monograph id
- * @param {string} noteId id of the note
- * @returns Monograph Id
- */
- monograph(noteId) {
- if (!this.monographs) return;
-
- return this.monographs[this.monographs.indexOf(noteId)];
- }
-
- /**
- * Publish a note as a monograph
- * @param {string} noteId id of the note to publish
- * @param {{password: string, selfDestruct: boolean}} opts Publish options
- * @returns
- */
- async publish(noteId, opts = { password: undefined, selfDestruct: false }) {
- if (!this.monographs) await this.init();
-
- let update = !!this.isPublished(noteId);
-
- const user = await this._db.user.getUser();
- const token = await this._db.tokenManager.getAccessToken();
- if (!user || !token) throw new Error("Please login to publish a note.");
-
- const note = this._db.notes.note(noteId);
- if (!note) throw new Error("No such note found.");
-
- const content = await this._db.content.downloadMedia(
- `monograph-${noteId}`,
- await this._db.content.raw(note.data.contentId),
- false
- );
- if (!content) throw new Error("This note has no content.");
-
- const monograph = {
- id: noteId,
- title: note.title,
- userId: user.id,
- selfDestruct: opts.selfDestruct
- };
-
- if (opts.password) {
- monograph.encryptedContent = await this._db
- .storage()
- .encrypt(
- { password: opts.password },
- JSON.stringify({ type: content.type, data: content.data })
- );
- } else {
- monograph.content = JSON.stringify({
- type: content.type,
- data: content.data
- });
- }
-
- const method = update ? http.patch.json : http.post.json;
-
- const { id } = await method(
- `${Constants.API_HOST}/monographs`,
- monograph,
- token
- );
-
- this.monographs.push(id);
- return id;
- }
-
- /**
- * Unpublish a note
- * @param {string} noteId id of the note to unpublish
- */
- async unpublish(noteId) {
- if (!this.monographs) await this.init();
-
- const user = await this._db.user.getUser();
- const token = await this._db.tokenManager.getAccessToken();
- if (!user || !token) throw new Error("Please login to publish a note.");
-
- // const note = this._db.notes.note(noteId);
- // if (!note) throw new Error("No such note found.");
-
- if (!this.isPublished(noteId))
- throw new Error("This note is not published.");
-
- await http.delete(`${Constants.API_HOST}/monographs/${noteId}`, token);
-
- this.monographs.splice(this.monographs.indexOf(noteId), 1);
- }
-
- get all() {
- if (!this.monographs) return [];
-
- return this._db.notes.all.filter(
- (note) => this.monographs.indexOf(note.id) > -1
- );
- }
-
- async get(monographId) {
- return await http.get(`${Constants.API_HOST}/monographs/${monographId}`);
- }
-}
-export default Monographs;
diff --git a/packages/core/src/api/monographs.ts b/packages/core/src/api/monographs.ts
new file mode 100644
index 000000000..d7727beb3
--- /dev/null
+++ b/packages/core/src/api/monographs.ts
@@ -0,0 +1,177 @@
+/*
+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 http from "../utils/http";
+import Constants from "../utils/constants";
+import Database from ".";
+import { Note, isDeleted } from "../types";
+import { isUnencryptedContent } from "../collections/content";
+import { Cipher } from "@notesnook/crypto";
+
+type BaseMonograph = {
+ id: string;
+ title: string;
+ userId: string;
+ selfDestruct: boolean;
+};
+type UnencryptedMonograph = BaseMonograph & {
+ content: string;
+};
+type EncryptedMonograph = BaseMonograph & {
+ encryptedContent: Cipher<"base64">;
+};
+type Monograph = UnencryptedMonograph | EncryptedMonograph;
+
+export class Monographs {
+ monographs: string[] = [];
+ constructor(private readonly db: Database) {}
+
+ async clear() {
+ this.monographs = [];
+ await this.db.storage().write("monographs", this.monographs);
+ }
+
+ async refresh() {
+ try {
+ const user = await this.db.user.getUser();
+ const token = await this.db.tokenManager.getAccessToken();
+ if (!user || !token || !user.isEmailConfirmed) return;
+
+ const monographs = await http.get(
+ `${Constants.API_HOST}/monographs`,
+ token
+ );
+ await this.db.storage().write("monographs", monographs);
+ if (monographs) this.monographs = monographs;
+ } catch (e) {
+ console.error(e);
+ }
+ }
+
+ /**
+ * Check if note is published.
+ */
+ isPublished(noteId: string) {
+ return this.monographs && this.monographs.indexOf(noteId) > -1;
+ }
+
+ /**
+ * Get note published monograph id
+ */
+ monograph(noteId: string) {
+ return this.monographs[this.monographs.indexOf(noteId)];
+ }
+
+ /**
+ * Publish a note as a monograph
+ */
+ async publish(
+ noteId: string,
+ opts: { password?: string; selfDestruct?: boolean } = {}
+ ) {
+ if (!this.monographs.length) await this.refresh();
+
+ const update = !!this.isPublished(noteId);
+
+ const user = await this.db.user.getUser();
+ const token = await this.db.tokenManager.getAccessToken();
+ if (!user || !token) throw new Error("Please login to publish a note.");
+
+ const note = this.db.notes.note(noteId);
+ if (!note) throw new Error("No such note found.");
+ if (!note.data.contentId) throw new Error("Cannot publish an empty note.");
+
+ const contentItem = await this.db.content.raw(note.data.contentId);
+
+ if (!contentItem || isDeleted(contentItem))
+ throw new Error("Could not find content for this note.");
+
+ if (!isUnencryptedContent(contentItem))
+ throw new Error("Cannot published locked notes.");
+
+ const content = await this.db.content.downloadMedia(
+ `monograph-${noteId}`,
+ contentItem,
+ false
+ );
+
+ const monograph: Monograph = {
+ id: noteId,
+ title: note.title,
+ userId: user.id,
+ selfDestruct: opts.selfDestruct || false,
+ ...(opts.password
+ ? {
+ encryptedContent: await this.db
+ .storage()
+ .encrypt(
+ { password: opts.password },
+ JSON.stringify({ type: content.type, data: content.data })
+ )
+ }
+ : {
+ content: JSON.stringify({
+ type: content.type,
+ data: content.data
+ })
+ })
+ };
+
+ const method = update ? http.patch.json : http.post.json;
+
+ const { id } = await method(
+ `${Constants.API_HOST}/monographs`,
+ monograph,
+ token
+ );
+
+ this.monographs.push(id);
+ return id;
+ }
+
+ /**
+ * Unpublish a note
+ */
+ async unpublish(noteId: string) {
+ if (!this.monographs.length) await this.refresh();
+
+ const user = await this.db.user.getUser();
+ const token = await this.db.tokenManager.getAccessToken();
+ if (!user || !token) throw new Error("Please login to publish a note.");
+
+ if (!this.isPublished(noteId))
+ throw new Error("This note is not published.");
+
+ await http.delete(`${Constants.API_HOST}/monographs/${noteId}`, token);
+
+ this.monographs.splice(this.monographs.indexOf(noteId), 1);
+ }
+
+ get all() {
+ if (!this.monographs.length) return [];
+
+ return this.monographs
+ .map((noteId) => this.db.notes.note(noteId)?.data)
+ .filter(Boolean) as Note[];
+ }
+
+ get(monographId: string) {
+ return http.get(`${Constants.API_HOST}/monographs/${monographId}`);
+ }
+}
diff --git a/packages/core/src/api/sync/index.ts b/packages/core/src/api/sync/index.ts
index 0a1c4d159..30b84e196 100644
--- a/packages/core/src/api/sync/index.ts
+++ b/packages/core/src/api/sync/index.ts
@@ -329,7 +329,7 @@ class Sync {
async stop(lastSynced: number) {
// refresh monographs on sync completed
- await this.db.monographs.init();
+ await this.db.monographs.refresh();
this.logger.info("Stopping sync", { lastSynced });
const storedLastSynced = await this.db.lastSynced();
@@ -370,9 +370,6 @@ class Sync {
false,
lastSynced
);
-
- // refresh monographs on sync completed
- await this.db.monographs.init();
}
async processChunk(