mirror of
https://github.com/streetwriters/notesnook.git
synced 2025-12-22 22:49:45 +01:00
feat: implement trash
This commit is contained in:
@@ -71,6 +71,12 @@ test("delete a notebook", () =>
|
||||
.notebook(id)
|
||||
.topics.topic("General")
|
||||
.add(noteId);
|
||||
expect(
|
||||
db.notebooks
|
||||
.notebook(id)
|
||||
.topics.topic("General")
|
||||
.has(noteId)
|
||||
).toBe(true);
|
||||
let note = db.notes.note(noteId);
|
||||
expect(note.notebook.id).toBe(id);
|
||||
await db.notebooks.delete(id);
|
||||
|
||||
103
packages/core/__tests__/trash.test.js
Normal file
103
packages/core/__tests__/trash.test.js
Normal file
@@ -0,0 +1,103 @@
|
||||
import {
|
||||
StorageInterface,
|
||||
noteTest,
|
||||
notebookTest,
|
||||
TEST_NOTE,
|
||||
TEST_NOTEBOOK
|
||||
} from "./utils";
|
||||
|
||||
beforeEach(() => StorageInterface.clear());
|
||||
|
||||
test("delete a note", () =>
|
||||
noteTest().then(async ({ db, id }) => {
|
||||
let nbId = await db.notebooks.add(TEST_NOTEBOOK);
|
||||
await db.notebooks
|
||||
.notebook(nbId)
|
||||
.topics.topic("General")
|
||||
.add(id);
|
||||
await db.notes.delete(id);
|
||||
expect(db.trash.all.length).toBe(1);
|
||||
expect(await db.trash.deltaStorage.read(id + "_delta")).toBeDefined();
|
||||
await db.trash.delete(db.trash.all[0].id);
|
||||
expect(db.trash.all.length).toBe(0);
|
||||
expect(await db.trash.deltaStorage.read(id + "_delta")).toBeUndefined();
|
||||
}));
|
||||
|
||||
test("restore a deleted note", () =>
|
||||
noteTest().then(async ({ db, id }) => {
|
||||
let nbId = await db.notebooks.add(TEST_NOTEBOOK);
|
||||
await db.notebooks
|
||||
.notebook(nbId)
|
||||
.topics.topic("General")
|
||||
.add(id);
|
||||
await db.notes.delete(id);
|
||||
await db.trash.restore(db.trash.all[0].id);
|
||||
expect(db.trash.all.length).toBe(0);
|
||||
let note = db.notes.note(id);
|
||||
expect(note).toBeDefined();
|
||||
expect(
|
||||
db.notebooks
|
||||
.notebook(nbId)
|
||||
.topics.topic("General")
|
||||
.has(id)
|
||||
).toBe(true);
|
||||
expect(db.notes.note(id).notebook.id).toBe(nbId);
|
||||
expect(db.notes.note(id).notebook.topic).toBe("General");
|
||||
}));
|
||||
|
||||
test("restore a deleted note that's in a deleted notebook", () =>
|
||||
noteTest().then(async ({ db, id }) => {
|
||||
let nbId = await db.notebooks.add(TEST_NOTEBOOK);
|
||||
await db.notebooks
|
||||
.notebook(nbId)
|
||||
.topics.topic("General")
|
||||
.add(id);
|
||||
await db.notes.delete(id);
|
||||
await db.notebooks.delete(nbId);
|
||||
await db.trash.restore(db.trash.all[0].id);
|
||||
let note = db.notes.note(id);
|
||||
expect(note).toBeDefined();
|
||||
expect(db.notes.note(id).notebook).toStrictEqual({});
|
||||
}));
|
||||
|
||||
test("delete a notebook", () =>
|
||||
notebookTest().then(async ({ db, id }) => {
|
||||
let noteId = await db.notes.add(TEST_NOTE);
|
||||
await db.notebooks
|
||||
.notebook(id)
|
||||
.topics.topic("General")
|
||||
.add(noteId);
|
||||
await db.notebooks.delete(id);
|
||||
expect(db.notebooks.notebook(id)).toBeUndefined();
|
||||
expect(db.notes.note(noteId).notebook).toStrictEqual({});
|
||||
}));
|
||||
|
||||
test("restore a deleted notebook", () =>
|
||||
notebookTest().then(async ({ db, id }) => {
|
||||
let noteId = await db.notes.add(TEST_NOTE);
|
||||
await db.notebooks
|
||||
.notebook(id)
|
||||
.topics.topic("General")
|
||||
.add(noteId);
|
||||
await db.notebooks.delete(id);
|
||||
await db.trash.restore(db.trash.all[0].id);
|
||||
let notebook = db.notebooks.notebook(id);
|
||||
expect(notebook).toBeDefined();
|
||||
expect(db.notes.note(noteId).notebook.id).toBe(id);
|
||||
expect(db.notes.note(noteId).notebook.topic).toBe("General");
|
||||
}));
|
||||
|
||||
test("restore a notebook that has deleted notes", () =>
|
||||
notebookTest().then(async ({ db, id }) => {
|
||||
let noteId = await db.notes.add(TEST_NOTE);
|
||||
await db.notebooks
|
||||
.notebook(id)
|
||||
.topics.topic("General")
|
||||
.add(noteId);
|
||||
await db.notebooks.delete(id);
|
||||
await db.notes.delete(noteId);
|
||||
await db.trash.restore(db.trash.all[0].id);
|
||||
let notebook = db.notebooks.notebook(id);
|
||||
expect(notebook).toBeDefined();
|
||||
expect(notebook.topics.topic("General").has(noteId)).toBe(false);
|
||||
}));
|
||||
@@ -1,5 +1,6 @@
|
||||
import Notes from "../collections/notes";
|
||||
import Notebooks from "../collections/notebooks";
|
||||
import Trash from "../collections/trash";
|
||||
|
||||
class DB {
|
||||
constructor(context) {
|
||||
@@ -8,8 +9,10 @@ class DB {
|
||||
async init() {
|
||||
this.notebooks = new Notebooks(this.context);
|
||||
this.notes = new Notes(this.context);
|
||||
await this.notes.init(this.notebooks);
|
||||
await this.notebooks.init(this.notes);
|
||||
this.trash = new Trash(this.context);
|
||||
await this.notes.init(this.notebooks, this.trash);
|
||||
await this.notebooks.init(this.notes, this.trash);
|
||||
await this.trash.init(this.notes, this.notebooks);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import CachedCollection from "../database/cached-collection";
|
||||
import fuzzysearch from "fuzzysearch";
|
||||
import Notebook from "../models/notebook";
|
||||
import Notes from "./notes";
|
||||
import Trash from "./trash";
|
||||
var tfun = require("transfun/transfun.js").tfun;
|
||||
if (!tfun) {
|
||||
tfun = global.tfun;
|
||||
@@ -12,8 +14,14 @@ export default class Notebooks {
|
||||
this.notes = undefined;
|
||||
}
|
||||
|
||||
init(notes) {
|
||||
/**
|
||||
*
|
||||
* @param {Notes} notes
|
||||
* @param {Trash} trash
|
||||
*/
|
||||
init(notes, trash) {
|
||||
this.notes = notes;
|
||||
this.trash = trash;
|
||||
return this.collection.init();
|
||||
}
|
||||
|
||||
@@ -37,20 +45,19 @@ export default class Notebooks {
|
||||
notebook.topics = [...notebook.topics, ...oldNotebook.topics];
|
||||
}
|
||||
|
||||
let topics = notebook.topics || [];
|
||||
let topics =
|
||||
notebook.topics.filter((v, i) => notebook.topics.indexOf(v) === i) || [];
|
||||
if (!oldNotebook) {
|
||||
topics[0] = makeTopic("General", id);
|
||||
}
|
||||
|
||||
for (let i = 0; i < topics.length; i++) {
|
||||
let topic = topics[i];
|
||||
let isDuplicate =
|
||||
topics.findIndex(t => t.title === (topic || topic.title)) > -1; //check for duplicate
|
||||
|
||||
let isEmpty =
|
||||
!topic || (typeof topic === "string" && topic.trim().length <= 0);
|
||||
|
||||
if (isEmpty || isDuplicate) {
|
||||
if (isEmpty) {
|
||||
topics.splice(i, 1);
|
||||
i--;
|
||||
continue;
|
||||
@@ -96,6 +103,7 @@ export default class Notebooks {
|
||||
notebook.topics.delete(...notebook.topics.all)
|
||||
);
|
||||
await this.collection.removeItem(id);
|
||||
await this.trash.add(notebook.data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
import Storage from "../database/storage";
|
||||
import Notebooks from "./notebooks";
|
||||
import Note from "../models/note";
|
||||
import Trash from "./trash";
|
||||
var tfun = require("transfun/transfun.js").tfun;
|
||||
if (!tfun) {
|
||||
tfun = global.tfun;
|
||||
@@ -26,10 +27,12 @@ export default class Notes {
|
||||
/**
|
||||
*
|
||||
* @param {Notebooks} notebooks
|
||||
* @param {Trash} trash
|
||||
*/
|
||||
async init(notebooks) {
|
||||
async init(notebooks, trash) {
|
||||
await this.collection.init();
|
||||
this.notebooks = notebooks;
|
||||
this.trash = trash;
|
||||
await this.tagsCollection.init();
|
||||
}
|
||||
|
||||
@@ -175,7 +178,7 @@ export default class Notes {
|
||||
await this.tagsCollection.remove(tag);
|
||||
}
|
||||
await this.collection.removeItem(id);
|
||||
this.deltaStorage.remove(id + "_delta");
|
||||
await this.trash.add(item.data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,10 @@ export default class Topics {
|
||||
this.notes = notes;
|
||||
}
|
||||
|
||||
exists(topic) {
|
||||
return this.all.findIndex(v => v.title === (topic.title || topic)) > -1;
|
||||
}
|
||||
|
||||
async add(topic) {
|
||||
await this.notebooks.add({
|
||||
id: this.notebookId,
|
||||
@@ -36,23 +40,20 @@ export default class Topics {
|
||||
}
|
||||
|
||||
async delete(...topics) {
|
||||
let notebook = this.notebooks.notebook(this.notebookId);
|
||||
if (!notebook) return;
|
||||
notebook = notebook.data;
|
||||
for (let topic of topics) {
|
||||
let allTopics = JSON.parse(JSON.stringify(this.all)); //FIXME: make a deep copy
|
||||
for (let i = 0; i < allTopics.length; i++) {
|
||||
let topic = allTopics[i];
|
||||
if (!topic) continue;
|
||||
let index = notebook.topics.findIndex(
|
||||
t => t.title === topic.title || topic
|
||||
);
|
||||
if (index <= -1) continue;
|
||||
topic = notebook.topics[index];
|
||||
let index = topics.findIndex(t => (t.title || t) === topic.title);
|
||||
let t = this.topic(topic);
|
||||
await t.transaction(() => t.delete(...topic.notes), false);
|
||||
notebook.topics.splice(index, 1);
|
||||
if (index > -1) {
|
||||
allTopics.splice(i, 1);
|
||||
}
|
||||
}
|
||||
await this.notebooks.add({
|
||||
id: notebook.id,
|
||||
topics: notebook.topics
|
||||
id: this.notebookId,
|
||||
topics: allTopics
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
93
packages/core/collections/trash.js
Normal file
93
packages/core/collections/trash.js
Normal file
@@ -0,0 +1,93 @@
|
||||
import CachedCollection from "../database/cached-collection";
|
||||
import Notes from "./notes";
|
||||
import Notebooks from "./notebooks";
|
||||
import Storage from "../database/storage";
|
||||
export default class Trash {
|
||||
constructor(context) {
|
||||
this.collection = new CachedCollection(context, "trash");
|
||||
this.deltaStorage = new Storage(context);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {Notes} notes
|
||||
* @param {Notebooks} notebooks
|
||||
*/
|
||||
async init(notes, notebooks) {
|
||||
this.notes = notes;
|
||||
this.notebooks = notebooks;
|
||||
await this.collection.init();
|
||||
}
|
||||
|
||||
get all() {
|
||||
return this.collection.getAllItems();
|
||||
}
|
||||
|
||||
async add(item) {
|
||||
if (this.collection.exists(item.id + "_deleted"))
|
||||
throw new Error("This item has already been deleted.");
|
||||
item.dateDeleted = Date.now();
|
||||
item.id = item.id + "_deleted";
|
||||
await this.collection.addItem(item);
|
||||
}
|
||||
|
||||
async delete(...ids) {
|
||||
for (let id of ids) {
|
||||
if (!this.collection.exists(id)) return;
|
||||
if (id.indexOf("note") > -1)
|
||||
this.deltaStorage.remove(id.replace("_deleted", "") + "_delta");
|
||||
await this.collection.removeItem(id);
|
||||
}
|
||||
}
|
||||
|
||||
async restore(...ids) {
|
||||
for (let id of ids) {
|
||||
let item = this.collection.getItem(id);
|
||||
if (!item) continue;
|
||||
delete item.dateDeleted;
|
||||
item.id = item.id.replace("_deleted", "");
|
||||
if (item.type === "note") {
|
||||
let { notebook } = item;
|
||||
item.notebook = {};
|
||||
await this.notes.add(item);
|
||||
|
||||
if (notebook && notebook.id && notebook.topic) {
|
||||
const { id, topic } = notebook;
|
||||
|
||||
// if the notebook has been deleted
|
||||
if (!this.notebooks.collection.exists(id)) {
|
||||
notebook = {};
|
||||
} else {
|
||||
// if the topic has been deleted
|
||||
if (!this.notebooks.notebook(id).topics.exists(topic)) {
|
||||
notebook = {};
|
||||
}
|
||||
}
|
||||
|
||||
// restore the note to the topic it was in before deletion
|
||||
if (notebook.id && notebook.topic) {
|
||||
await this.notebooks
|
||||
.notebook(id)
|
||||
.topics.topic(topic)
|
||||
.add(item.id);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const { topics } = item;
|
||||
item.topics = [];
|
||||
await this.notebooks.add(item);
|
||||
let notebook = this.notebooks.notebook(item.id);
|
||||
for (let topic of topics) {
|
||||
let t = await notebook.topics.add(topic.title);
|
||||
await t.add(...topic.notes);
|
||||
}
|
||||
}
|
||||
await this.collection.removeItem(id);
|
||||
}
|
||||
}
|
||||
|
||||
async clear() {
|
||||
let indices = await this.collection.indexer.getIndices();
|
||||
return this.delete(...indices);
|
||||
}
|
||||
}
|
||||
@@ -50,6 +50,10 @@ export default class CachedCollection {
|
||||
}
|
||||
}
|
||||
|
||||
exists(id) {
|
||||
return this.map.has(id);
|
||||
}
|
||||
|
||||
getItem(id) {
|
||||
return this.map.get(id);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user