mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
redesign of createNote APIs, WIP
This commit is contained in:
parent
e143becb7a
commit
de02e9e889
@ -79,6 +79,10 @@ class Attribute extends Entity {
|
|||||||
|
|
||||||
async beforeSaving() {
|
async beforeSaving() {
|
||||||
if (!this.value) {
|
if (!this.value) {
|
||||||
|
if (this.type === 'relation') {
|
||||||
|
throw new Error(`Cannot save relation ${this.name} since it does not target any note.`);
|
||||||
|
}
|
||||||
|
|
||||||
// null value isn't allowed
|
// null value isn't allowed
|
||||||
this.value = "";
|
this.value = "";
|
||||||
}
|
}
|
||||||
|
@ -472,6 +472,32 @@ class Note extends Entity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {Promise<Attribute>}
|
||||||
|
*/
|
||||||
|
async createAttribute(type, name, value = "") {
|
||||||
|
const attr = new Attribute({
|
||||||
|
noteId: this.noteId,
|
||||||
|
type: type,
|
||||||
|
name: name,
|
||||||
|
value: value
|
||||||
|
});
|
||||||
|
|
||||||
|
await attr.save();
|
||||||
|
|
||||||
|
this.invalidateAttributeCache();
|
||||||
|
|
||||||
|
return attr;
|
||||||
|
}
|
||||||
|
|
||||||
|
async createLabel(name, value = "") {
|
||||||
|
return await this.createAttribute(LABEL, name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
async createRelation(name, targetNoteId) {
|
||||||
|
return await this.createAttribute(RELATION, name, targetNoteId);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} name - label name
|
* @param {string} name - label name
|
||||||
* @returns {Promise<boolean>} true if label exists (including inherited)
|
* @returns {Promise<boolean>} true if label exists (including inherited)
|
||||||
|
@ -16,29 +16,14 @@ const protectedSessionService = require('../services/protected_session');
|
|||||||
const log = require('../services/log');
|
const log = require('../services/log');
|
||||||
const noteRevisionService = require('../services/note_revisions');
|
const noteRevisionService = require('../services/note_revisions');
|
||||||
|
|
||||||
async function getNewNotePosition(parentNoteId, noteData) {
|
async function getNewNotePosition(parentNoteId) {
|
||||||
let newNotePos = 0;
|
const maxNotePos = await sql.getValue(`
|
||||||
|
SELECT MAX(notePosition)
|
||||||
|
FROM branches
|
||||||
|
WHERE parentNoteId = ?
|
||||||
|
AND isDeleted = 0`, [parentNoteId]);
|
||||||
|
|
||||||
if (noteData.target === 'into') {
|
return maxNotePos === null ? 0 : maxNotePos + 10;
|
||||||
const maxNotePos = await sql.getValue('SELECT MAX(notePosition) FROM branches WHERE parentNoteId = ? AND isDeleted = 0', [parentNoteId]);
|
|
||||||
|
|
||||||
newNotePos = maxNotePos === null ? 0 : maxNotePos + 10;
|
|
||||||
}
|
|
||||||
else if (noteData.target === 'after') {
|
|
||||||
const afterNote = await sql.getRow('SELECT notePosition FROM branches WHERE branchId = ?', [noteData.target_branchId]);
|
|
||||||
|
|
||||||
newNotePos = afterNote.notePosition + 10;
|
|
||||||
|
|
||||||
// not updating utcDateModified to avoig having to sync whole rows
|
|
||||||
await sql.execute('UPDATE branches SET notePosition = notePosition + 10 WHERE parentNoteId = ? AND notePosition > ? AND isDeleted = 0',
|
|
||||||
[parentNoteId, afterNote.notePosition]);
|
|
||||||
|
|
||||||
await syncTableService.addNoteReorderingSync(parentNoteId);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new Error('Unknown target: ' + noteData.target);
|
|
||||||
}
|
|
||||||
return newNotePos;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function triggerChildNoteCreated(childNote, parentNote) {
|
async function triggerChildNoteCreated(childNote, parentNote) {
|
||||||
@ -49,31 +34,12 @@ async function triggerNoteTitleChanged(note) {
|
|||||||
await eventService.emit(eventService.NOTE_TITLE_CHANGED, note);
|
await eventService.emit(eventService.NOTE_TITLE_CHANGED, note);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
function deriveTypeAndMime(noteData, parentNote) {
|
||||||
* FIXME: noteData has mandatory property "target", it might be better to add it as parameter to reflect this
|
|
||||||
*/
|
|
||||||
async function createNewNote(parentNoteId, noteData) {
|
|
||||||
let newNotePos;
|
|
||||||
|
|
||||||
if (noteData.notePosition !== undefined) {
|
|
||||||
newNotePos = noteData.notePosition;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
newNotePos = await getNewNotePosition(parentNoteId, noteData);
|
|
||||||
}
|
|
||||||
|
|
||||||
const parentNote = await repository.getNote(parentNoteId);
|
|
||||||
|
|
||||||
if (!parentNote) {
|
|
||||||
throw new Error(`Parent note ${parentNoteId} not found.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!noteData.type) {
|
if (!noteData.type) {
|
||||||
if (parentNote.type === 'text' || parentNote.type === 'code') {
|
if (parentNote.type === 'text' || parentNote.type === 'code') {
|
||||||
noteData.type = parentNote.type;
|
noteData.type = parentNote.type;
|
||||||
noteData.mime = parentNote.mime;
|
noteData.mime = parentNote.mime;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// inheriting note type makes sense only for text and code
|
// inheriting note type makes sense only for text and code
|
||||||
noteData.type = 'text';
|
noteData.type = 'text';
|
||||||
noteData.mime = 'text/html';
|
noteData.mime = 'text/html';
|
||||||
@ -83,54 +49,83 @@ async function createNewNote(parentNoteId, noteData) {
|
|||||||
if (!noteData.mime) {
|
if (!noteData.mime) {
|
||||||
if (noteData.type === 'text') {
|
if (noteData.type === 'text') {
|
||||||
noteData.mime = 'text/html';
|
noteData.mime = 'text/html';
|
||||||
}
|
} else if (noteData.type === 'code') {
|
||||||
else if (noteData.type === 'code') {
|
|
||||||
noteData.mime = 'text/plain';
|
noteData.mime = 'text/plain';
|
||||||
}
|
} else if (noteData.type === 'relation-map' || noteData.type === 'search') {
|
||||||
else if (noteData.type === 'relation-map' || noteData.type === 'search') {
|
|
||||||
noteData.mime = 'application/json';
|
noteData.mime = 'application/json';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
noteData.type = noteData.type || parentNote.type;
|
noteData.type = noteData.type || parentNote.type;
|
||||||
noteData.mime = noteData.mime || parentNote.mime;
|
noteData.mime = noteData.mime || parentNote.mime;
|
||||||
|
}
|
||||||
|
|
||||||
const note = await new Note({
|
async function copyChildAttributes(parentNote, childNote) {
|
||||||
noteId: noteData.noteId, // optionally can force specific noteId
|
|
||||||
title: noteData.title,
|
|
||||||
isProtected: noteData.isProtected,
|
|
||||||
type: noteData.type || 'text',
|
|
||||||
mime: noteData.mime || 'text/html'
|
|
||||||
}).save();
|
|
||||||
|
|
||||||
if (note.isStringNote() || this.type === 'render') { // render to just make sure it's not null
|
|
||||||
noteData.content = noteData.content || "";
|
|
||||||
}
|
|
||||||
|
|
||||||
await note.setContent(noteData.content);
|
|
||||||
|
|
||||||
const branch = await new Branch({
|
|
||||||
noteId: note.noteId,
|
|
||||||
parentNoteId: parentNoteId,
|
|
||||||
notePosition: newNotePos,
|
|
||||||
prefix: noteData.prefix,
|
|
||||||
isExpanded: !!noteData.isExpanded
|
|
||||||
}).save();
|
|
||||||
|
|
||||||
for (const attr of await parentNote.getAttributes()) {
|
for (const attr of await parentNote.getAttributes()) {
|
||||||
if (attr.name.startsWith("child:")) {
|
if (attr.name.startsWith("child:")) {
|
||||||
await new Attribute({
|
await new Attribute({
|
||||||
noteId: note.noteId,
|
noteId: childNote.noteId,
|
||||||
type: attr.type,
|
type: attr.type,
|
||||||
name: attr.name.substr(6),
|
name: attr.name.substr(6),
|
||||||
value: attr.value,
|
value: attr.value,
|
||||||
position: attr.position,
|
position: attr.position,
|
||||||
isInheritable: attr.isInheritable
|
isInheritable: attr.isInheritable
|
||||||
}).save();
|
}).save();
|
||||||
|
|
||||||
note.invalidateAttributeCache();
|
childNote.invalidateAttributeCache();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Following object properties are mandatory:
|
||||||
|
* - {string} parentNoteId
|
||||||
|
* - {string} title
|
||||||
|
* - {*} content
|
||||||
|
* - {string} type
|
||||||
|
*
|
||||||
|
* Following are optional (have defaults)
|
||||||
|
* - {string} mime - value is derived from default mimes for type
|
||||||
|
* - {boolean} isProtected - default is false
|
||||||
|
* - {boolean} isExpanded - default is false
|
||||||
|
* - {string} prefix - default is empty string
|
||||||
|
* - {integer} notePosition - default is last existing notePosition in a parent + 10
|
||||||
|
*
|
||||||
|
* @param params
|
||||||
|
* @return {Promise<{note: Entity, branch: Entity}>}
|
||||||
|
*/
|
||||||
|
async function createNewNote(params) {
|
||||||
|
const parentNote = await repository.getNote(params.parentNoteId);
|
||||||
|
|
||||||
|
if (!parentNote) {
|
||||||
|
throw new Error(`Parent note ${params.parentNoteId} not found.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!params.title || params.title.trim().length === 0) {
|
||||||
|
throw new Error(`Note title must not be empty`);
|
||||||
|
}
|
||||||
|
|
||||||
|
deriveTypeAndMime(params, parentNote);
|
||||||
|
|
||||||
|
const note = await new Note({
|
||||||
|
noteId: params.noteId, // optionally can force specific noteId
|
||||||
|
title: params.title,
|
||||||
|
isProtected: !!params.isProtected,
|
||||||
|
type: params.type,
|
||||||
|
mime: params.mime
|
||||||
|
}).save();
|
||||||
|
|
||||||
|
await note.setContent(params.content);
|
||||||
|
|
||||||
|
const branch = await new Branch({
|
||||||
|
noteId: note.noteId,
|
||||||
|
parentNoteId: params.parentNoteId,
|
||||||
|
notePosition: params.notePosition !== undefined ? params.notePosition : await getNewNotePosition(params.parentNoteId),
|
||||||
|
prefix: params.prefix,
|
||||||
|
isExpanded: !!params.isExpanded
|
||||||
|
}).save();
|
||||||
|
|
||||||
|
await copyChildAttributes(parentNote, note);
|
||||||
|
|
||||||
await triggerNoteTitleChanged(note);
|
await triggerNoteTitleChanged(note);
|
||||||
await triggerChildNoteCreated(note, parentNote);
|
await triggerChildNoteCreated(note, parentNote);
|
||||||
@ -141,13 +136,41 @@ async function createNewNote(parentNoteId, noteData) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// methods below should be probably just backend API methods
|
||||||
|
async function createJsonNote(parentNoteId, title, content = {}, params = {}) {
|
||||||
|
params.parentNoteId = parentNoteId;
|
||||||
|
params.title = title;
|
||||||
|
|
||||||
|
params.type = "code";
|
||||||
|
params.mime = "application/json";
|
||||||
|
|
||||||
|
params.content = JSON.stringify(content, null, '\t');
|
||||||
|
|
||||||
|
return await createNewNote(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createTextNote(parentNoteId, title, content = "", params = {}) {
|
||||||
|
params.parentNoteId = parentNoteId;
|
||||||
|
params.title = title;
|
||||||
|
|
||||||
|
params.type = "text";
|
||||||
|
params.mime = "text/html";
|
||||||
|
|
||||||
|
params.content = content;
|
||||||
|
|
||||||
|
return await createNewNote(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
async function createNote(parentNoteId, title, content = "", extraOptions = {}) {
|
async function createNote(parentNoteId, title, content = "", extraOptions = {}) {
|
||||||
if (!parentNoteId) throw new Error("Empty parentNoteId");
|
if (!parentNoteId) throw new Error("Empty parentNoteId");
|
||||||
if (!title) throw new Error("Empty title");
|
if (!title) throw new Error("Empty title");
|
||||||
|
|
||||||
const noteData = {
|
const noteData = {
|
||||||
title: title,
|
title: title,
|
||||||
content: extraOptions.json ? JSON.stringify(content, null, '\t') : content,
|
content: content,
|
||||||
target: 'into',
|
target: 'into',
|
||||||
noteId: extraOptions.noteId,
|
noteId: extraOptions.noteId,
|
||||||
isProtected: !!extraOptions.isProtected,
|
isProtected: !!extraOptions.isProtected,
|
||||||
@ -158,12 +181,7 @@ async function createNote(parentNoteId, title, content = "", extraOptions = {})
|
|||||||
notePosition: extraOptions.notePosition
|
notePosition: extraOptions.notePosition
|
||||||
};
|
};
|
||||||
|
|
||||||
if (extraOptions.json && !noteData.type) {
|
const {note, branch} = await createNewNote(parentNoteId, title, noteData);
|
||||||
noteData.type = "code";
|
|
||||||
noteData.mime = "application/json";
|
|
||||||
}
|
|
||||||
|
|
||||||
const {note, branch} = await createNewNote(parentNoteId, noteData);
|
|
||||||
|
|
||||||
for (const attr of extraOptions.attributes || []) {
|
for (const attr of extraOptions.attributes || []) {
|
||||||
await attributeService.createAttribute({
|
await attributeService.createAttribute({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user