mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	redesign of createNote APIs, WIP
This commit is contained in:
		| @@ -79,6 +79,10 @@ class Attribute extends Entity { | ||||
|  | ||||
|     async beforeSaving() { | ||||
|         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 | ||||
|             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 | ||||
|      * @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 noteRevisionService = require('../services/note_revisions'); | ||||
|  | ||||
| async function getNewNotePosition(parentNoteId, noteData) { | ||||
|     let newNotePos = 0; | ||||
| async function getNewNotePosition(parentNoteId) { | ||||
|     const maxNotePos = await sql.getValue(` | ||||
|             SELECT MAX(notePosition)  | ||||
|             FROM branches  | ||||
|             WHERE parentNoteId = ?  | ||||
|               AND isDeleted = 0`, [parentNoteId]); | ||||
|  | ||||
|     if (noteData.target === 'into') { | ||||
|         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; | ||||
|     return maxNotePos === null ? 0 : maxNotePos + 10; | ||||
| } | ||||
|  | ||||
| async function triggerChildNoteCreated(childNote, parentNote) { | ||||
| @@ -49,31 +34,12 @@ async function triggerNoteTitleChanged(note) { | ||||
|     await eventService.emit(eventService.NOTE_TITLE_CHANGED, note); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 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.`); | ||||
|     } | ||||
|  | ||||
| function deriveTypeAndMime(noteData, parentNote) { | ||||
|     if (!noteData.type) { | ||||
|         if (parentNote.type === 'text' || parentNote.type === 'code') { | ||||
|             noteData.type = parentNote.type; | ||||
|             noteData.mime = parentNote.mime; | ||||
|         } | ||||
|         else { | ||||
|         } else { | ||||
|             // inheriting note type makes sense only for text and code | ||||
|             noteData.type = 'text'; | ||||
|             noteData.mime = 'text/html'; | ||||
| @@ -83,44 +49,22 @@ async function createNewNote(parentNoteId, noteData) { | ||||
|     if (!noteData.mime) { | ||||
|         if (noteData.type === 'text') { | ||||
|             noteData.mime = 'text/html'; | ||||
|         } | ||||
|         else if (noteData.type === 'code') { | ||||
|         } else if (noteData.type === 'code') { | ||||
|             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.type = noteData.type || parentNote.type; | ||||
|     noteData.mime = noteData.mime || parentNote.mime; | ||||
| } | ||||
|  | ||||
|     const note = await new Note({ | ||||
|         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(); | ||||
|  | ||||
| async function copyChildAttributes(parentNote, childNote) { | ||||
|     for (const attr of await parentNote.getAttributes()) { | ||||
|         if (attr.name.startsWith("child:")) { | ||||
|             await new Attribute({ | ||||
|                noteId: note.noteId, | ||||
|                 noteId: childNote.noteId, | ||||
|                 type: attr.type, | ||||
|                 name: attr.name.substr(6), | ||||
|                 value: attr.value, | ||||
| @@ -128,9 +72,60 @@ async function createNewNote(parentNoteId, noteData) { | ||||
|                 isInheritable: attr.isInheritable | ||||
|             }).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 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 = {}) { | ||||
|     if (!parentNoteId) throw new Error("Empty parentNoteId"); | ||||
|     if (!title) throw new Error("Empty title"); | ||||
|  | ||||
|     const noteData = { | ||||
|         title: title, | ||||
|         content: extraOptions.json ? JSON.stringify(content, null, '\t') : content, | ||||
|         content: content, | ||||
|         target: 'into', | ||||
|         noteId: extraOptions.noteId, | ||||
|         isProtected: !!extraOptions.isProtected, | ||||
| @@ -158,12 +181,7 @@ async function createNote(parentNoteId, title, content = "", extraOptions = {}) | ||||
|         notePosition: extraOptions.notePosition | ||||
|     }; | ||||
|  | ||||
|     if (extraOptions.json && !noteData.type) { | ||||
|         noteData.type = "code"; | ||||
|         noteData.mime = "application/json"; | ||||
|     } | ||||
|  | ||||
|     const {note, branch} = await createNewNote(parentNoteId, noteData); | ||||
|     const {note, branch} = await createNewNote(parentNoteId, title, noteData); | ||||
|  | ||||
|     for (const attr of extraOptions.attributes || []) { | ||||
|         await attributeService.createAttribute({ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user