2020-04-15 23:25:53 +05:00
|
|
|
import Collection from "./collection";
|
2020-02-05 20:57:43 +05:00
|
|
|
import Note from "../models/note";
|
2020-03-19 11:30:05 +05:00
|
|
|
import getId from "../utils/id";
|
2021-01-23 10:48:21 +05:00
|
|
|
import { EV, EVENTS } from "../common";
|
2020-11-04 10:17:37 +05:00
|
|
|
import { getContentFromData } from "../content-types";
|
2020-12-07 11:19:36 +05:00
|
|
|
import qclone from "qclone/src/qclone";
|
2021-02-27 11:55:13 +05:00
|
|
|
import sort from "fast-sort";
|
2021-07-06 12:12:55 +05:00
|
|
|
import { deleteItem } from "../utils/array";
|
2020-02-03 12:03:07 +05:00
|
|
|
|
2020-04-15 23:25:53 +05:00
|
|
|
export default class Notes extends Collection {
|
2020-02-03 12:03:07 +05:00
|
|
|
async add(noteArg) {
|
|
|
|
|
if (!noteArg) return;
|
2021-02-16 16:56:06 +05:00
|
|
|
if (noteArg.deleted) {
|
|
|
|
|
await this._collection.addItem(noteArg);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-02-03 12:03:07 +05:00
|
|
|
|
2020-03-18 14:06:20 +05:00
|
|
|
let id = noteArg.id || getId();
|
2020-02-22 21:53:56 +05:00
|
|
|
let oldNote = this._collection.getItem(id);
|
2020-03-19 11:30:05 +05:00
|
|
|
|
2020-12-29 12:31:26 +05:00
|
|
|
if (noteArg.remote || noteArg.migrated) {
|
2021-10-10 20:19:33 +05:00
|
|
|
const { color, tags } = noteArg;
|
2021-07-06 12:12:55 +05:00
|
|
|
|
2020-12-29 12:31:26 +05:00
|
|
|
if (oldNote) {
|
2021-07-06 12:12:55 +05:00
|
|
|
if (!!oldNote.color && oldNote.color !== color) {
|
2021-10-10 20:19:33 +05:00
|
|
|
await this._db.colors.untag(oldNote.color, id);
|
2020-12-29 12:31:26 +05:00
|
|
|
}
|
2021-02-20 10:04:40 +05:00
|
|
|
if (!!oldNote.tags) {
|
2020-12-29 12:31:26 +05:00
|
|
|
for (let tag of oldNote.tags) {
|
2021-07-12 10:32:35 +05:00
|
|
|
await this._db.tags.untag(tag, id);
|
2020-12-29 12:31:26 +05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-07-06 12:12:55 +05:00
|
|
|
if (color) {
|
|
|
|
|
await this._db.colors.add(color, id);
|
2020-12-29 12:31:26 +05:00
|
|
|
}
|
|
|
|
|
|
2021-07-06 12:12:55 +05:00
|
|
|
if (tags && tags.length) {
|
|
|
|
|
for (let tag of tags) {
|
2020-12-29 12:31:26 +05:00
|
|
|
await this._db.tags.add(tag, id);
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-07-06 12:12:55 +05:00
|
|
|
|
2020-12-29 12:31:26 +05:00
|
|
|
return await this._collection.addItem(noteArg);
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-03 12:03:07 +05:00
|
|
|
let note = {
|
|
|
|
|
...oldNote,
|
2020-04-04 13:29:33 +05:00
|
|
|
...noteArg,
|
2020-02-03 12:03:07 +05:00
|
|
|
};
|
|
|
|
|
|
2021-06-01 09:47:42 +05:00
|
|
|
if (oldNote) note.contentId = oldNote.contentId;
|
|
|
|
|
|
2020-12-29 14:08:48 +05:00
|
|
|
if (!oldNote && !noteArg.content && !noteArg.contentId) return;
|
2020-02-03 12:03:07 +05:00
|
|
|
|
2020-11-04 10:17:37 +05:00
|
|
|
if (noteArg.content && noteArg.content.data && noteArg.content.type) {
|
2021-10-27 10:53:36 +05:00
|
|
|
const {
|
|
|
|
|
type,
|
|
|
|
|
data,
|
|
|
|
|
conflicted,
|
|
|
|
|
dateEdited,
|
|
|
|
|
persistDateEdited,
|
|
|
|
|
dateResolved,
|
|
|
|
|
} = noteArg.content;
|
2020-03-21 11:15:24 +05:00
|
|
|
|
2020-11-04 10:17:37 +05:00
|
|
|
let content = getContentFromData(type, data);
|
|
|
|
|
if (!content) throw new Error("Invalid content type.");
|
|
|
|
|
note.title = getNoteTitle(note, content);
|
|
|
|
|
note.headline = getNoteHeadline(note, content);
|
2020-03-21 11:15:24 +05:00
|
|
|
|
2020-11-04 10:17:37 +05:00
|
|
|
if (isNoteEmpty(note, content)) {
|
|
|
|
|
if (oldNote) {
|
2021-01-23 10:48:21 +05:00
|
|
|
EV.publish(EVENTS.noteRemoved, id);
|
2020-11-04 10:17:37 +05:00
|
|
|
await this.remove(id);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
2020-03-19 11:30:05 +05:00
|
|
|
|
2020-11-04 10:17:37 +05:00
|
|
|
note.contentId = await this._db.content.add({
|
2020-03-19 12:38:33 +05:00
|
|
|
noteId: id,
|
2020-11-04 10:17:37 +05:00
|
|
|
id: note.contentId,
|
|
|
|
|
type,
|
|
|
|
|
data,
|
2021-07-03 12:15:02 +05:00
|
|
|
localOnly: !!note.localOnly,
|
2021-07-25 11:31:44 +05:00
|
|
|
dateEdited,
|
2021-08-10 11:59:56 +05:00
|
|
|
dateResolved,
|
2021-07-25 11:31:44 +05:00
|
|
|
conflicted,
|
2021-10-27 10:53:36 +05:00
|
|
|
persistDateEdited,
|
2020-03-19 11:30:05 +05:00
|
|
|
});
|
2020-02-20 11:28:16 +05:00
|
|
|
}
|
2020-02-03 23:53:58 +05:00
|
|
|
|
2020-02-03 12:03:07 +05:00
|
|
|
note = {
|
|
|
|
|
id,
|
2020-11-04 10:17:37 +05:00
|
|
|
contentId: note.contentId,
|
2020-02-03 12:03:07 +05:00
|
|
|
type: "note",
|
2020-03-19 11:30:05 +05:00
|
|
|
title: note.title,
|
2020-11-04 10:17:37 +05:00
|
|
|
headline: note.headline,
|
2020-02-03 12:03:07 +05:00
|
|
|
pinned: !!note.pinned,
|
|
|
|
|
locked: !!note.locked,
|
2020-12-05 12:38:15 +05:00
|
|
|
notebooks: note.notebooks || undefined,
|
2020-12-06 22:14:33 +05:00
|
|
|
color: note.color,
|
2020-02-03 12:03:07 +05:00
|
|
|
tags: note.tags || [],
|
|
|
|
|
favorite: !!note.favorite,
|
2021-07-28 13:58:12 +05:00
|
|
|
dateCreated: note.dateCreated,
|
|
|
|
|
dateEdited: note.dateEdited,
|
2021-04-06 09:13:06 +05:00
|
|
|
localOnly: !!note.localOnly,
|
2021-07-25 11:31:44 +05:00
|
|
|
conflicted: !!note.conflicted,
|
2020-02-03 12:03:07 +05:00
|
|
|
};
|
|
|
|
|
|
2020-11-21 11:54:41 +05:00
|
|
|
if (!oldNote || oldNote.deleted) {
|
2020-12-06 22:14:33 +05:00
|
|
|
if (note.color) await this._db.colors.add(note.color, id);
|
2020-02-06 22:35:53 +05:00
|
|
|
|
2020-02-03 12:03:07 +05:00
|
|
|
for (let tag of note.tags) {
|
2021-07-24 11:32:40 +05:00
|
|
|
if (!tag || !tag.trim()) continue;
|
2020-04-15 23:25:53 +05:00
|
|
|
await this._db.tags.add(tag, id);
|
2020-02-03 12:03:07 +05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-22 21:53:56 +05:00
|
|
|
await this._collection.addItem(note);
|
2020-02-03 23:53:58 +05:00
|
|
|
return note.id;
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-06 22:46:57 +05:00
|
|
|
/**
|
|
|
|
|
*
|
|
|
|
|
* @param {string} id The id of note
|
|
|
|
|
* @returns {Note} The note of the given id
|
|
|
|
|
*/
|
2020-02-05 20:57:43 +05:00
|
|
|
note(id) {
|
2020-02-23 12:45:00 +05:00
|
|
|
if (!id) return;
|
2020-02-23 11:43:04 +05:00
|
|
|
let note = id.type ? id : this._collection.getItem(id);
|
2020-04-07 15:50:39 +05:00
|
|
|
if (!note || note.deleted) return;
|
2020-04-16 02:14:53 +05:00
|
|
|
return new Note(note, this._db);
|
2020-02-03 12:03:07 +05:00
|
|
|
}
|
|
|
|
|
|
2020-03-23 15:06:12 +05:00
|
|
|
get raw() {
|
|
|
|
|
return this._collection.getRaw();
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-03 12:03:07 +05:00
|
|
|
get all() {
|
2020-11-04 10:42:19 +05:00
|
|
|
return this._collection.getItems();
|
2020-02-03 12:03:07 +05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get pinned() {
|
2021-02-16 21:38:32 +05:00
|
|
|
return this.all.filter((item) => item.pinned === true);
|
2020-02-03 12:03:07 +05:00
|
|
|
}
|
|
|
|
|
|
2020-04-04 13:29:33 +05:00
|
|
|
get conflicted() {
|
2021-02-16 21:38:32 +05:00
|
|
|
return this.all.filter((item) => item.conflicted === true);
|
2020-04-04 13:29:33 +05:00
|
|
|
}
|
|
|
|
|
|
2020-02-03 12:03:07 +05:00
|
|
|
get favorites() {
|
2021-02-16 21:38:32 +05:00
|
|
|
return this.all.filter((item) => item.favorite === true);
|
2020-02-03 12:03:07 +05:00
|
|
|
}
|
|
|
|
|
|
2021-02-16 16:56:06 +05:00
|
|
|
get deleted() {
|
2021-02-16 21:38:32 +05:00
|
|
|
return this.raw.filter((item) => item.dateDeleted > 0);
|
2021-02-16 16:56:06 +05:00
|
|
|
}
|
|
|
|
|
|
2021-07-07 10:15:28 +05:00
|
|
|
get locked() {
|
|
|
|
|
return this.all.filter((item) => item.locked === true);
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-26 11:44:33 +05:00
|
|
|
tagged(tagId) {
|
2021-02-20 11:31:44 +05:00
|
|
|
return this._getTagItems(tagId, "tags");
|
2020-02-03 12:03:07 +05:00
|
|
|
}
|
|
|
|
|
|
2020-12-01 10:57:31 +05:00
|
|
|
colored(colorId) {
|
2021-02-20 11:31:44 +05:00
|
|
|
return this._getTagItems(colorId, "colors");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @private
|
|
|
|
|
*/
|
|
|
|
|
_getTagItems(tagId, collection) {
|
|
|
|
|
const tag = this._db[collection].tag(tagId);
|
|
|
|
|
if (!tag || tag.noteIds.length <= 0) return [];
|
2021-02-27 11:55:13 +05:00
|
|
|
const array = tag.noteIds.reduce((arr, id) => {
|
2021-02-20 11:31:44 +05:00
|
|
|
const item = this._collection.getItem(id);
|
|
|
|
|
if (item) arr.push(item);
|
|
|
|
|
return arr;
|
|
|
|
|
}, []);
|
2021-02-27 11:55:13 +05:00
|
|
|
return sort(array).desc((note) => note.dateCreated);
|
2020-02-03 12:03:07 +05:00
|
|
|
}
|
|
|
|
|
|
2020-09-09 11:14:35 +05:00
|
|
|
delete(...ids) {
|
|
|
|
|
return this._delete(true, ...ids);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
remove(...ids) {
|
|
|
|
|
return this._delete(false, ...ids);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @private
|
|
|
|
|
*/
|
|
|
|
|
async _delete(moveToTrash = true, ...ids) {
|
2020-02-03 12:03:07 +05:00
|
|
|
for (let id of ids) {
|
2020-02-05 20:57:43 +05:00
|
|
|
let item = this.note(id);
|
|
|
|
|
if (!item) continue;
|
2021-01-01 15:03:51 +05:00
|
|
|
const itemData = qclone(item.data);
|
2021-08-17 09:35:28 +05:00
|
|
|
|
2021-09-13 11:51:03 +05:00
|
|
|
if (itemData.notebooks) {
|
|
|
|
|
for (let notebook of itemData.notebooks) {
|
2021-08-17 09:35:28 +05:00
|
|
|
const notebookRef = this._db.notebooks.notebook(notebook.id);
|
|
|
|
|
if (!notebookRef) continue;
|
|
|
|
|
|
|
|
|
|
for (let topicId of notebook.topics) {
|
|
|
|
|
const topic = notebookRef.topics.topic(topicId);
|
|
|
|
|
if (!topic) continue;
|
|
|
|
|
|
|
|
|
|
await topic.delete(id);
|
2020-12-07 11:19:36 +05:00
|
|
|
}
|
|
|
|
|
}
|
2020-02-03 12:03:07 +05:00
|
|
|
}
|
2021-08-17 09:35:28 +05:00
|
|
|
|
2021-09-13 11:51:03 +05:00
|
|
|
for (let tag of itemData.tags) {
|
2021-07-12 10:32:35 +05:00
|
|
|
await this._db.tags.untag(tag, id);
|
2020-02-03 12:03:07 +05:00
|
|
|
}
|
2021-09-13 11:51:03 +05:00
|
|
|
|
|
|
|
|
if (itemData.color) {
|
|
|
|
|
await this._db.colors.untag(itemData.color, id);
|
2020-03-11 11:17:52 +05:00
|
|
|
}
|
2021-02-16 16:56:06 +05:00
|
|
|
// await this._collection.removeItem(id);
|
2020-12-07 11:19:36 +05:00
|
|
|
if (moveToTrash) await this._db.trash.add(itemData);
|
2021-07-07 10:15:28 +05:00
|
|
|
else {
|
|
|
|
|
await this._collection.removeItem(id);
|
|
|
|
|
await this._db.content.remove(itemData.contentId);
|
|
|
|
|
}
|
2020-02-03 12:03:07 +05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-05 00:17:12 +05:00
|
|
|
async move(to, ...noteIds) {
|
|
|
|
|
if (!to) throw new Error("The destination notebook cannot be undefined.");
|
|
|
|
|
if (!to.id || !to.topic)
|
|
|
|
|
throw new Error(
|
|
|
|
|
"The destination notebook must contain notebookId and topic."
|
|
|
|
|
);
|
2020-04-15 23:25:53 +05:00
|
|
|
let topic = this._db.notebooks.notebook(to.id).topics.topic(to.topic);
|
2020-02-05 00:17:12 +05:00
|
|
|
if (!topic) throw new Error("No such topic exists.");
|
2020-03-02 10:29:29 +05:00
|
|
|
await topic.add(...noteIds);
|
2020-02-05 00:17:12 +05:00
|
|
|
}
|
2020-02-03 12:03:07 +05:00
|
|
|
}
|
|
|
|
|
|
2020-11-04 10:17:37 +05:00
|
|
|
function isNoteEmpty(note, content) {
|
|
|
|
|
const { title, locked } = note;
|
2020-03-09 09:45:54 +05:00
|
|
|
const isTitleEmpty = !title || !title.trim().length;
|
2020-11-04 10:17:37 +05:00
|
|
|
return !locked && isTitleEmpty && content.isEmpty();
|
2020-09-09 11:09:03 +05:00
|
|
|
}
|
|
|
|
|
|
2020-11-04 10:17:37 +05:00
|
|
|
function getNoteHeadline(note, content) {
|
2020-02-03 12:03:07 +05:00
|
|
|
if (note.locked) return "";
|
2021-04-27 09:05:25 +05:00
|
|
|
return content.toHeadline();
|
2020-04-16 14:04:57 +05:00
|
|
|
}
|
|
|
|
|
|
2020-11-04 10:17:37 +05:00
|
|
|
function getNoteTitle(note, content) {
|
2020-11-20 22:20:51 +05:00
|
|
|
if (note.title && note.title.trim().length > 0)
|
|
|
|
|
return note.title.replace(/\r?\n/g, " ");
|
2020-11-04 10:17:37 +05:00
|
|
|
return content.toTitle();
|
2020-02-03 12:03:07 +05:00
|
|
|
}
|