diff --git a/.idea/dataSources/a2c75661-f9e2-478f-a69f-6a9409e69997.xml b/.idea/dataSources/a2c75661-f9e2-478f-a69f-6a9409e69997.xml index 0e12ce82d..953e5646b 100644 --- a/.idea/dataSources/a2c75661-f9e2-478f-a69f-6a9409e69997.xml +++ b/.idea/dataSources/a2c75661-f9e2-478f-a69f-6a9409e69997.xml @@ -10,10 +10,10 @@ -
-
-
-
+
+
+
+
@@ -59,7 +59,7 @@ 1sqlite_autoindex_api_tokens_1 - + 1 TEXT|0s 1 @@ -69,198 +69,198 @@ TEXT|0s 1 - + 3 TEXT|0s 1 - - 4 - TEXT|0s - 1 - '' - - - 5 - INT|0s - 1 - 0 - - - 6 - TEXT|0s - 1 - - - 7 - TEXT|0s - 1 - - - 8 - INT|0s - 1 - - - 1 - attributeId - - 1 - - - noteId - - - - name -value - - - - - attributeId - 1 - sqlite_autoindex_attributes_1 - - - 1 - TEXT|0s - 1 - - - 2 - TEXT|0s - 1 - - - 3 - TEXT|0s - 1 - - + 4 INTEGER|0s 1 - + 5 TEXT|0s - + 6 BOOLEAN|0s - + 7 INTEGER|0s 1 0 - + 8 TEXT|0s 1 - + 1 branchId 1 - + noteId parentNoteId - + noteId - + branchId 1 sqlite_autoindex_branches_1 - + 1 INTEGER|0s 1 1 - + 2 TEXT|0s - + 3 TEXT|0s - + 4 TEXT|0s 1 - + id 1 - + noteId notes noteId - + 1 TEXT|0s 1 - + 2 TEXT|0s 1 - + 3 TEXT|0s 1 - + 4 TEXT|0s 1 - + 5 BLOB|0s - + 6 INT|0s 1 0 - + 7 TEXT|0s 1 - + 8 TEXT|0s 1 - + 1 imageId 1 - + imageId 1 sqlite_autoindex_images_1 + + 1 + TEXT|0s + 1 + + + 2 + TEXT|0s + 1 + + + 3 + TEXT|0s + 1 + + + 4 + TEXT|0s + 1 + '' + + + 5 + INT|0s + 1 + 0 + + + 6 + TEXT|0s + 1 + + + 7 + TEXT|0s + 1 + + + 8 + INT|0s + 1 + + + 1 + labelId + + 1 + + + noteId + + + + name +value + + + + + labelId + 1 + sqlite_autoindex_labels_1 + 1 TEXT|0s diff --git a/src/entities/note.js b/src/entities/note.js index 270df80c7..7d3263991 100644 --- a/src/entities/note.js +++ b/src/entities/note.js @@ -20,7 +20,7 @@ class Note extends Entity { } isJson() { - return this.type === "code" && this.mime === "application/json"; + return this.mime === "application/json"; } isJavaScript() { diff --git a/src/public/javascripts/dialogs/note_source.js b/src/public/javascripts/dialogs/note_source.js index fc55a8ff2..e09bfd64a 100644 --- a/src/public/javascripts/dialogs/note_source.js +++ b/src/public/javascripts/dialogs/note_source.js @@ -12,7 +12,7 @@ function showDialog() { height: 500 }); - const noteText = noteDetailService.getCurrentNote().detail.content; + const noteText = noteDetailService.getCurrentNote().content; $noteSource.text(formatHtml(noteText)); } diff --git a/src/public/javascripts/entities/note_full.js b/src/public/javascripts/entities/note_full.js new file mode 100644 index 000000000..0f500b36e --- /dev/null +++ b/src/public/javascripts/entities/note_full.js @@ -0,0 +1,15 @@ +import NoteShort from './note_short.js'; + +class NoteFull extends NoteShort { + constructor(treeCache, row) { + super(treeCache, row); + + this.content = row.content; + + if (this.isJson()) { + this.jsonContent = JSON.parse(this.content); + } + } +} + +export default NoteFull; \ No newline at end of file diff --git a/src/public/javascripts/entities/note_short.js b/src/public/javascripts/entities/note_short.js index fc1457e3a..9ffdb01c5 100644 --- a/src/public/javascripts/entities/note_short.js +++ b/src/public/javascripts/entities/note_short.js @@ -9,6 +9,10 @@ class NoteShort { this.hideInAutocomplete = row.hideInAutocomplete; } + isJson() { + return this.mime === "application/json"; + } + async getBranches() { const branches = []; diff --git a/src/public/javascripts/services/init.js b/src/public/javascripts/services/init.js index 54e9dd5fd..6398dc27f 100644 --- a/src/public/javascripts/services/init.js +++ b/src/public/javascripts/services/init.js @@ -164,7 +164,7 @@ $(document).tooltip({ if (notePath !== null) { const noteId = treeUtils.getNoteIdFromNotePath(notePath); - noteDetailService.loadNote(noteId).then(note => callback(note.detail.content)); + noteDetailService.loadNote(noteId).then(note => callback(note.content)); } }, close: function(event, ui) diff --git a/src/public/javascripts/services/note_detail.js b/src/public/javascripts/services/note_detail.js index 51d3fbeda..b7a821768 100644 --- a/src/public/javascripts/services/note_detail.js +++ b/src/public/javascripts/services/note_detail.js @@ -7,6 +7,8 @@ import server from './server.js'; import messagingService from "./messaging.js"; import bundleService from "./bundle.js"; import infoService from "./info.js"; +import treeCache from "./tree_cache.js"; +import NoteFull from "../entities/note_full.js"; const $noteTitle = $("#note-title"); @@ -45,7 +47,7 @@ function getCurrentNote() { } function getCurrentNoteId() { - return currentNote ? currentNote.detail.noteId : null; + return currentNote ? currentNote.noteId : null; } function noteChanged() { @@ -81,13 +83,13 @@ async function saveNoteIfChanged() { await saveNoteToServer(note); - if (note.detail.isProtected) { + if (note.isProtected) { protectedSessionHolder.touchProtectedSession(); } } function updateNoteFromInputs(note) { - if (note.detail.type === 'text') { + if (note.type === 'text') { let content = editor.getData(); // if content is only tags/whitespace (typically

 

), then just make it empty @@ -96,32 +98,32 @@ function updateNoteFromInputs(note) { content = ''; } - note.detail.content = content; + note.content = content; } - else if (note.detail.type === 'code') { - note.detail.content = codeEditor.getValue(); + else if (note.type === 'code') { + note.content = codeEditor.getValue(); } - else if (note.detail.type === 'search') { - note.detail.content = JSON.stringify({ + else if (note.type === 'search') { + note.content = JSON.stringify({ searchString: $searchString.val() }); } - else if (note.detail.type === 'render' || note.detail.type === 'file') { + else if (note.type === 'render' || note.type === 'file') { // nothing } else { - infoService.throwError("Unrecognized type: " + note.detail.type); + infoService.throwError("Unrecognized type: " + note.type); } const title = $noteTitle.val(); - note.detail.title = title; + note.title = title; - treeService.setNoteTitle(note.detail.noteId, title); + treeService.setNoteTitle(note.noteId, title); } async function saveNoteToServer(note) { - await server.put('notes/' + note.detail.noteId, note); + await server.put('notes/' + note.noteId, note); isNoteChanged = false; @@ -129,7 +131,7 @@ async function saveNoteToServer(note) { } function setNoteBackgroundIfProtected(note) { - const isProtected = !!note.detail.isProtected; + const isProtected = !!note.isProtected; $noteDetailWrapper.toggleClass("protected", isProtected); $protectButton.toggle(!isProtected); @@ -143,7 +145,7 @@ function newNoteCreated() { } async function setContent(content) { - if (currentNote.detail.type === 'text') { + if (currentNote.type === 'text') { if (!editor) { await utils.requireLibrary(utils.CKEDITOR); @@ -157,7 +159,7 @@ async function setContent(content) { $noteDetail.show(); } - else if (currentNote.detail.type === 'code') { + else if (currentNote.type === 'code') { if (!codeEditor) { await utils.requireLibrary(utils.CODE_MIRROR); @@ -186,7 +188,7 @@ async function setContent(content) { // this needs to happen after the element is shown, otherwise the editor won't be refresheds codeEditor.setValue(content); - const info = CodeMirror.findModeByMIME(currentNote.detail.mime); + const info = CodeMirror.findModeByMIME(currentNote.mime); if (info) { codeEditor.setOption("mode", info.mime); @@ -195,7 +197,7 @@ async function setContent(content) { codeEditor.refresh(); } - else if (currentNote.detail.type === 'search') { + else if (currentNote.type === 'search') { $noteDetailSearch.show(); try { @@ -223,9 +225,9 @@ async function loadNoteToEditor(noteId) { $noteIdDisplay.html(noteId); - await protectedSessionService.ensureProtectedSession(currentNote.detail.isProtected, false); + await protectedSessionService.ensureProtectedSession(currentNote.isProtected, false); - if (currentNote.detail.isProtected) { + if (currentNote.isProtected) { protectedSessionHolder.touchProtectedSession(); } @@ -237,10 +239,10 @@ async function loadNoteToEditor(noteId) { noteChangeDisabled = true; - $noteTitle.val(currentNote.detail.title); + $noteTitle.val(currentNote.title); - noteTypeService.setNoteType(currentNote.detail.type); - noteTypeService.setNoteMime(currentNote.detail.mime); + noteTypeService.setNoteType(currentNote.type); + noteTypeService.setNoteMime(currentNote.mime); $noteDetail.hide(); $noteDetailSearch.hide(); @@ -248,7 +250,7 @@ async function loadNoteToEditor(noteId) { $noteDetailRender.html('').hide(); $noteDetailAttachment.hide(); - if (currentNote.detail.type === 'render') { + if (currentNote.type === 'render') { $noteDetailRender.show(); const bundle = await server.get('script/bundle/' + getCurrentNoteId()); @@ -257,15 +259,18 @@ async function loadNoteToEditor(noteId) { bundleService.executeBundle(bundle); } - else if (currentNote.detail.type === 'file') { + else if (currentNote.type === 'file') { + const labels = await server.get('notes/' + currentNote.noteId + '/labels'); + const labelMap = utils.toObject(labels, l => [l.name, l.value]); + $noteDetailAttachment.show(); - $attachmentFileName.text(currentNote.labels.original_file_name); - $attachmentFileSize.text(currentNote.labels.file_size + " bytes"); - $attachmentFileType.text(currentNote.detail.mime); + $attachmentFileName.text(labelMap.original_file_name); + $attachmentFileSize.text(labelMap.file_size + " bytes"); + $attachmentFileType.text(currentNote.mime); } else { - await setContent(currentNote.detail.content); + await setContent(currentNote.content); } noteChangeDisabled = false; @@ -299,7 +304,9 @@ async function loadLabelList() { } async function loadNote(noteId) { - return await server.get('notes/' + noteId); + const row = await server.get('notes/' + noteId); + + return new NoteFull(treeCache, row); } function getEditor() { @@ -309,24 +316,24 @@ function getEditor() { function focus() { const note = getCurrentNote(); - if (note.detail.type === 'text') { + if (note.type === 'text') { $noteDetail.focus(); } - else if (note.detail.type === 'code') { + else if (note.type === 'code') { codeEditor.focus(); } - else if (note.detail.type === 'render' || note.detail.type === 'file' || note.detail.type === 'search') { + else if (note.type === 'render' || note.type === 'file' || note.type === 'search') { // do nothing } else { - infoService.throwError('Unrecognized type: ' + note.detail.type); + infoService.throwError('Unrecognized type: ' + note.type); } } function getCurrentNoteType() { const currentNote = getCurrentNote(); - return currentNote ? currentNote.detail.type : null; + return currentNote ? currentNote.type : null; } async function executeCurrentNote() { @@ -334,13 +341,13 @@ async function executeCurrentNote() { // make sure note is saved so we load latest changes await saveNoteIfChanged(); - if (currentNote.detail.mime.endsWith("env=frontend")) { + if (currentNote.mime.endsWith("env=frontend")) { const bundle = await server.get('script/bundle/' + getCurrentNoteId()); bundleService.executeBundle(bundle); } - if (currentNote.detail.mime.endsWith("env=backend")) { + if (currentNote.mime.endsWith("env=backend")) { await server.post('script/run/' + getCurrentNoteId()); } diff --git a/src/public/javascripts/services/note_type.js b/src/public/javascripts/services/note_type.js index 127de49c3..8ae1b2f47 100644 --- a/src/public/javascripts/services/note_type.js +++ b/src/public/javascripts/services/note_type.js @@ -1,6 +1,6 @@ import treeService from './tree.js'; import noteDetail from './note_detail.js'; -import utils from './utils.js'; +import server from './server.js'; import infoService from "./info.js"; const $executeScriptButton = $("#execute-script-button"); @@ -86,7 +86,7 @@ function NoteTypeModel() { async function save() { const note = noteDetail.getCurrentNote(); - await server.put('notes/' + note.detail.noteId + await server.put('notes/' + note.noteId + '/type/' + encodeURIComponent(self.type()) + '/mime/' + encodeURIComponent(self.mime())); diff --git a/src/public/javascripts/services/protected_session.js b/src/public/javascripts/services/protected_session.js index cddbe619d..9a1a9e007 100644 --- a/src/public/javascripts/services/protected_session.js +++ b/src/public/javascripts/services/protected_session.js @@ -94,11 +94,11 @@ async function protectNoteAndSendToServer() { noteDetail.updateNoteFromInputs(note); - note.detail.isProtected = true; + note.isProtected = true; await noteDetail.saveNoteToServer(note); - treeService.setProtected(note.detail.noteId, note.detail.isProtected); + treeService.setProtected(note.noteId, note.isProtected); noteDetail.setNoteBackgroundIfProtected(note); } @@ -110,11 +110,11 @@ async function unprotectNoteAndSendToServer() { noteDetail.updateNoteFromInputs(note); - note.detail.isProtected = false; + note.isProtected = false; await noteDetail.saveNoteToServer(note); - treeService.setProtected(note.detail.noteId, note.detail.isProtected); + treeService.setProtected(note.noteId, note.isProtected); noteDetail.setNoteBackgroundIfProtected(note); } diff --git a/src/public/javascripts/services/search_tree.js b/src/public/javascripts/services/search_tree.js index a141cc96e..8769279d5 100644 --- a/src/public/javascripts/services/search_tree.js +++ b/src/public/javascripts/services/search_tree.js @@ -1,4 +1,5 @@ import treeService from './tree.js'; +import server from './server.js'; const $tree = $("#tree"); const $searchInput = $("input[name='search-text']"); @@ -45,6 +46,8 @@ async function doSearch() { async function saveSearch() { const {noteId} = await server.post('search/' + encodeURIComponent($searchInput.val())); + resetSearch(); + await treeService.reload(); await treeService.activateNode(noteId); diff --git a/src/public/javascripts/services/tree.js b/src/public/javascripts/services/tree.js index 5892f2456..43c1d5c67 100644 --- a/src/public/javascripts/services/tree.js +++ b/src/public/javascripts/services/tree.js @@ -12,6 +12,7 @@ import recentNotesDialog from '../dialogs/recent_notes.js'; import editTreePrefixDialog from '../dialogs/edit_tree_prefix.js'; import treeCache from './tree_cache.js'; import infoService from "./info.js"; +import Branch from '../entities/branch.js'; const $tree = $("#tree"); const $parentList = $("#parent-list"); @@ -615,25 +616,22 @@ function initFancyTree(branch) { } async function loadSearchNote(searchNoteId) { - const note = await server.get('notes/' + searchNoteId); - - const json = JSON.parse(note.detail.content); - - const noteIds = await server.get('search/' + encodeURIComponent(json.searchString)); + const searchNote = await noteDetailService.loadNote(searchNoteId); + const noteIds = await server.get('search/' + encodeURIComponent(searchNote.jsonContent.searchString)); for (const noteId of noteIds) { - const branchId = "virt" + utils.randomString(10); - - treeCache.addBranch({ - branchId: branchId, + const branch = new Branch(treeCache, { + branchId: "virt" + utils.randomString(10), noteId: noteId, parentNoteId: searchNoteId, prefix: '', virtual: true }); + + treeCache.addBranch(branch); } - return await prepareBranchInner(await treeCache.getNote(searchNoteId)); + return await prepareBranchInner(searchNote); } function getTree() { diff --git a/src/public/javascripts/services/utils.js b/src/public/javascripts/services/utils.js index 32e6a5a1d..fd1f33aa8 100644 --- a/src/public/javascripts/services/utils.js +++ b/src/public/javascripts/services/utils.js @@ -167,9 +167,9 @@ function toObject(array, fn) { const obj = {}; for (const item of array) { - const ret = fn(item); + const [key, value] = fn(item); - obj[ret[0]] = ret[1]; + obj[key] = value; } return obj; diff --git a/src/public/libraries/codemirror/addon/lint/eslint.js b/src/public/libraries/codemirror/addon/lint/eslint.js index ba4b221ef..28ca71b97 100644 --- a/src/public/libraries/codemirror/addon/lint/eslint.js +++ b/src/public/libraries/codemirror/addon/lint/eslint.js @@ -28,7 +28,7 @@ } async function validatorJavaScript(text, options) { - if (glob.getCurrentNote().detail.mime === 'application/json') { + if (glob.getCurrentNote().mime === 'application/json') { // eslint doesn't seem to validate pure JSON well return []; } diff --git a/src/routes/api/notes.js b/src/routes/api/notes.js index 138fd828a..f49e476a9 100644 --- a/src/routes/api/notes.js +++ b/src/routes/api/notes.js @@ -5,7 +5,6 @@ const router = express.Router(); const auth = require('../../services/auth'); const sql = require('../../services/sql'); const notes = require('../../services/notes'); -const labels = require('../../services/labels'); const log = require('../../services/log'); const utils = require('../../services/utils'); const protected_session = require('../../services/protected_session'); @@ -26,20 +25,12 @@ router.get('/:noteId', auth.checkApiAuth, wrap(async (req, res, next) => { protected_session.decryptNote(req, detail); - let labelMap = null; - if (detail.type === 'file') { - // no need to transfer attachment payload for this request + // no need to transfer (potentially large) attachment payload for this request detail.content = null; - - // labels contain important attachment metadata - filename and size - labelMap = await labels.getNoteLabelMap(noteId); } - res.send({ - detail: detail, - labels: labelMap - }); + res.send(detail); })); router.post('/:parentNoteId/children', auth.checkApiAuth, wrap(async (req, res, next) => { diff --git a/src/routes/api/search.js b/src/routes/api/search.js index 61ce68d02..7d280260c 100644 --- a/src/routes/api/search.js +++ b/src/routes/api/search.js @@ -26,7 +26,8 @@ router.post('/:searchString', auth.checkApiAuth, wrap(async (req, res, next) => const noteId = await notes.createNote('root', 'Search note', noteContent, { json: true, - type: 'search' + type: 'search', + mime: "application/json" }); res.send({ noteId });