feat: implement trash

This commit is contained in:
thecodrr
2020-02-06 16:46:23 +05:00
parent 90124ca43d
commit 1b53f967d8
8 changed files with 242 additions and 21 deletions

View File

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

View 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);
}));

View File

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

View File

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

View File

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

View File

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

View 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);
}
}

View File

@@ -50,6 +50,10 @@ export default class CachedCollection {
}
}
exists(id) {
return this.map.has(id);
}
getItem(id) {
return this.map.get(id);
}