core: migrate monographs to typescript

This commit is contained in:
Abdullah Atta
2023-09-18 12:37:30 +05:00
parent 110ba450c0
commit 09a19e61d5
5 changed files with 188 additions and 177 deletions

View File

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

View File

@@ -37,7 +37,7 @@ import Migrations from "./migrations";
import Outbox from "./outbox";
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";
@@ -176,7 +176,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();
});
@@ -211,7 +211,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() {

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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;

View File

@@ -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 <http://www.gnu.org/licenses/>.
*/
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}`);
}
}

View File

@@ -320,7 +320,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();
@@ -361,9 +361,6 @@ class Sync {
false,
lastSynced
);
// refresh monographs on sync completed
await this.db.monographs.init();
}
async processChunk(