2020-03-21 11:15:24 +05:00
|
|
|
import { groupBy, isHex } from "../utils";
|
2020-04-15 23:25:53 +05:00
|
|
|
import Collection from "./collection";
|
2020-02-03 23:53:58 +05:00
|
|
|
import sort from "fast-sort";
|
2020-02-03 12:03:07 +05:00
|
|
|
import {
|
|
|
|
|
getWeekGroupFromTimestamp,
|
|
|
|
|
months,
|
2020-03-11 12:11:59 +05:00
|
|
|
getLastWeekTimestamp,
|
2020-04-04 13:29:33 +05:00
|
|
|
get7DayTimestamp,
|
2020-02-03 12:03:07 +05:00
|
|
|
} from "../utils/date";
|
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";
|
2020-02-03 12:03:07 +05:00
|
|
|
var tfun = require("transfun/transfun.js").tfun;
|
|
|
|
|
if (!tfun) {
|
|
|
|
|
tfun = global.tfun;
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
2020-03-21 11:15:24 +05:00
|
|
|
if (noteArg.remote) {
|
|
|
|
|
return await this._collection.addItem(noteArg);
|
2020-03-19 15:40:15 +05:00
|
|
|
}
|
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
|
|
|
let deltaId = 0;
|
|
|
|
|
let textId = 0;
|
|
|
|
|
|
|
|
|
|
if (oldNote && oldNote.content) {
|
|
|
|
|
deltaId = oldNote.content.delta;
|
|
|
|
|
textId = oldNote.content.text;
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (isNoteEmpty(note)) {
|
2020-09-09 11:33:47 +05:00
|
|
|
if (oldNote) {
|
|
|
|
|
this._db.ev.publish("notes:removeEmptyNote", id);
|
|
|
|
|
await this.remove(id);
|
|
|
|
|
}
|
2020-02-03 12:03:07 +05:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-21 11:15:24 +05:00
|
|
|
const { text, delta } = note.content;
|
|
|
|
|
|
|
|
|
|
if (!textId && isHex(text)) textId = text;
|
|
|
|
|
if (!deltaId && isHex(delta)) deltaId = delta;
|
|
|
|
|
|
2020-03-25 12:18:04 +05:00
|
|
|
if (delta && typeof delta === "object") {
|
2020-04-15 23:25:53 +05:00
|
|
|
deltaId = await this._db.delta.add({
|
2020-03-19 12:38:33 +05:00
|
|
|
noteId: id,
|
2020-03-19 11:30:05 +05:00
|
|
|
id: deltaId,
|
2020-04-04 14:59:51 +05:00
|
|
|
data: delta.data || delta,
|
|
|
|
|
conflicted: delta.conflicted,
|
|
|
|
|
resolved: delta.resolved,
|
2020-03-19 11:30:05 +05:00
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-21 11:15:24 +05:00
|
|
|
if (text !== textId) {
|
2020-04-15 23:25:53 +05:00
|
|
|
textId = await this._db.text.add({
|
2020-03-19 12:38:33 +05:00
|
|
|
noteId: id,
|
2020-03-19 11:30:05 +05:00
|
|
|
id: textId,
|
2020-04-04 13:29:33 +05:00
|
|
|
data: text,
|
2020-03-19 11:30:05 +05:00
|
|
|
});
|
|
|
|
|
note.title = getNoteTitle(note);
|
|
|
|
|
note.headline = getNoteHeadline(note);
|
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,
|
|
|
|
|
type: "note",
|
2020-03-19 11:30:05 +05:00
|
|
|
title: note.title,
|
|
|
|
|
content: { text: textId, delta: deltaId },
|
2020-02-03 12:03:07 +05:00
|
|
|
pinned: !!note.pinned,
|
|
|
|
|
locked: !!note.locked,
|
|
|
|
|
notebook: note.notebook || {},
|
|
|
|
|
colors: note.colors || [],
|
|
|
|
|
tags: note.tags || [],
|
|
|
|
|
favorite: !!note.favorite,
|
2020-03-19 11:30:05 +05:00
|
|
|
headline: note.headline,
|
2020-03-29 19:20:12 +05:00
|
|
|
dateCreated: note.dateCreated,
|
2020-04-04 13:29:33 +05:00
|
|
|
conflicted: !!note.conflicted,
|
2020-02-03 12:03:07 +05:00
|
|
|
};
|
|
|
|
|
|
2020-02-06 22:35:53 +05:00
|
|
|
if (!oldNote) {
|
|
|
|
|
for (let color of note.colors) {
|
2020-04-15 23:25:53 +05:00
|
|
|
await this._db.colors.add(color, id);
|
2020-02-06 22:35:53 +05:00
|
|
|
}
|
|
|
|
|
|
2020-02-03 12:03:07 +05:00
|
|
|
for (let tag of note.tags) {
|
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-02-22 21:53:56 +05:00
|
|
|
return this._collection.getAllItems();
|
2020-02-03 12:03:07 +05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
get pinned() {
|
|
|
|
|
return tfun.filter(".pinned === true")(this.all);
|
|
|
|
|
}
|
|
|
|
|
|
2020-04-04 13:29:33 +05:00
|
|
|
get conflicted() {
|
|
|
|
|
return tfun.filter(".conflicted === true")(this.all);
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-03 12:03:07 +05:00
|
|
|
get favorites() {
|
|
|
|
|
return tfun.filter(".favorite === true")(this.all);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
tagged(tag) {
|
2020-04-15 23:25:53 +05:00
|
|
|
return this._db.tags.notes(tag).map((id) => this._collection.getItem(id));
|
2020-02-03 12:03:07 +05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
colored(color) {
|
2020-04-15 23:25:53 +05:00
|
|
|
return this._db.colors
|
2020-03-23 15:06:12 +05:00
|
|
|
.notes(color)
|
2020-04-04 13:29:33 +05:00
|
|
|
.map((id) => this._collection.getItem(id));
|
2020-02-03 12:03:07 +05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
group(by, special = false) {
|
|
|
|
|
let notes = !special
|
|
|
|
|
? tfun.filter(".pinned === false")(this.all)
|
|
|
|
|
: this.all;
|
2020-04-04 13:29:33 +05:00
|
|
|
notes = sort(notes).desc((t) => t.dateCreated);
|
2020-02-03 12:03:07 +05:00
|
|
|
switch (by) {
|
|
|
|
|
case "abc":
|
2020-04-04 13:29:33 +05:00
|
|
|
return groupBy(notes, (note) => note.title[0].toUpperCase(), special);
|
2020-02-03 12:03:07 +05:00
|
|
|
case "month":
|
|
|
|
|
return groupBy(
|
|
|
|
|
notes,
|
2020-04-04 13:29:33 +05:00
|
|
|
(note) => months[new Date(note.dateCreated).getMonth()],
|
2020-02-03 12:03:07 +05:00
|
|
|
special
|
|
|
|
|
);
|
|
|
|
|
case "week":
|
|
|
|
|
return groupBy(
|
|
|
|
|
notes,
|
2020-04-04 13:29:33 +05:00
|
|
|
(note) => getWeekGroupFromTimestamp(note.dateCreated),
|
2020-02-03 12:03:07 +05:00
|
|
|
special
|
|
|
|
|
);
|
|
|
|
|
case "year":
|
|
|
|
|
return groupBy(
|
|
|
|
|
notes,
|
2020-04-04 13:29:33 +05:00
|
|
|
(note) => new Date(note.dateCreated).getFullYear().toString(),
|
2020-02-03 12:03:07 +05:00
|
|
|
special
|
|
|
|
|
);
|
|
|
|
|
default:
|
|
|
|
|
let timestamps = {
|
|
|
|
|
recent: getLastWeekTimestamp(7),
|
2020-04-04 13:29:33 +05:00
|
|
|
lastWeek: getLastWeekTimestamp(7) - get7DayTimestamp(), //seven day timestamp value
|
2020-02-03 12:03:07 +05:00
|
|
|
};
|
|
|
|
|
return groupBy(
|
|
|
|
|
notes,
|
2020-04-04 13:29:33 +05:00
|
|
|
(note) =>
|
2020-02-03 12:03:07 +05:00
|
|
|
note.dateCreated >= timestamps.recent
|
|
|
|
|
? "Recent"
|
|
|
|
|
: note.dateCreated >= timestamps.lastWeek
|
|
|
|
|
? "Last week"
|
|
|
|
|
: "Older",
|
|
|
|
|
special
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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;
|
2020-02-05 01:12:36 +05:00
|
|
|
if (item.notebook && item.notebook.id && item.notebook.topic) {
|
2020-02-22 21:53:56 +05:00
|
|
|
await this._collection.transaction(() =>
|
2020-04-15 23:25:53 +05:00
|
|
|
this._db.notebooks
|
2020-02-05 20:57:43 +05:00
|
|
|
.notebook(item.notebook.id)
|
|
|
|
|
.topics.topic(item.notebook.topic)
|
2020-02-05 01:12:36 +05:00
|
|
|
.delete(id)
|
|
|
|
|
);
|
2020-02-03 12:03:07 +05:00
|
|
|
}
|
|
|
|
|
for (let tag of item.tags) {
|
2020-04-15 23:25:53 +05:00
|
|
|
await this._db.tags.remove(tag, id);
|
2020-02-03 12:03:07 +05:00
|
|
|
}
|
2020-03-11 11:17:52 +05:00
|
|
|
for (let color of item.colors) {
|
2020-04-15 23:25:53 +05:00
|
|
|
await this._db.colors.remove(color, id);
|
2020-03-11 11:17:52 +05:00
|
|
|
}
|
2020-03-23 13:22:28 +05:00
|
|
|
await this._collection.removeItem(id);
|
2020-09-09 11:14:35 +05:00
|
|
|
if (moveToTrash) await this._db.trash.add(item.data);
|
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
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function isNoteEmpty(note) {
|
2020-03-09 09:45:54 +05:00
|
|
|
if (!note.content) return true;
|
|
|
|
|
const {
|
|
|
|
|
title,
|
2020-04-16 14:04:57 +05:00
|
|
|
content: { delta },
|
2020-04-04 13:29:33 +05:00
|
|
|
locked,
|
2020-03-09 09:45:54 +05:00
|
|
|
} = note;
|
2020-04-16 14:04:57 +05:00
|
|
|
const text = getText(note);
|
|
|
|
|
|
2020-03-09 09:45:54 +05:00
|
|
|
const isTitleEmpty = !title || !title.trim().length;
|
2020-04-16 14:04:57 +05:00
|
|
|
const isTextEmpty = !isHex(text) && (!text || !text.trim().length);
|
2020-09-09 11:09:03 +05:00
|
|
|
const isDeltaEmpty = !isHex(delta) && _isDeltaEmpty(delta);
|
2020-03-09 09:45:54 +05:00
|
|
|
return !locked && isTitleEmpty && isTextEmpty && isDeltaEmpty;
|
2020-02-03 12:03:07 +05:00
|
|
|
}
|
|
|
|
|
|
2020-09-09 11:09:03 +05:00
|
|
|
function _isDeltaEmpty(delta) {
|
|
|
|
|
let obj = delta;
|
|
|
|
|
if (delta.constructor === Object) obj = delta.ops;
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
!obj ||
|
|
|
|
|
obj
|
|
|
|
|
.map((o) => o.insert)
|
|
|
|
|
.join("")
|
|
|
|
|
.trim().length <= 0
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2020-02-03 12:03:07 +05:00
|
|
|
function getNoteHeadline(note) {
|
|
|
|
|
if (note.locked) return "";
|
2020-04-16 14:04:57 +05:00
|
|
|
const text = getText(note);
|
|
|
|
|
return text.substring(0, 150) + (text.length > 150 ? "..." : "");
|
2020-02-03 12:03:07 +05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getNoteTitle(note) {
|
|
|
|
|
if (note.title && note.title.length > 0) return note.title.trim();
|
2020-04-16 14:04:57 +05:00
|
|
|
const text = getText(note);
|
|
|
|
|
return text.split(" ").slice(0, 3).join(" ").trim();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getText(note) {
|
|
|
|
|
if (!note.content || !note.content.text) return "";
|
|
|
|
|
const { text } = note.content;
|
|
|
|
|
if (text.data != null) return text.data;
|
|
|
|
|
else if (text != null) return text;
|
|
|
|
|
else return "";
|
2020-02-03 12:03:07 +05:00
|
|
|
}
|