From 2305ad74055aad8e97c09a567d466661d2100b77 Mon Sep 17 00:00:00 2001 From: zadam Date: Sun, 20 Oct 2019 12:29:34 +0200 Subject: [PATCH] reloading notes after script changes --- docs/frontend_api/FrontendScriptApi.html | 4 +- .../services_frontend_script_api.js.html | 6 +- libraries/codemirror/addon/lint/eslint.js | 4 +- src/public/javascripts/desktop.js | 2 +- src/public/javascripts/dialogs/add_link.js | 6 +- src/public/javascripts/dialogs/attributes.js | 4 +- src/public/javascripts/dialogs/link_map.js | 2 +- src/public/javascripts/dialogs/note_info.js | 2 +- .../javascripts/dialogs/note_revisions.js | 4 +- src/public/javascripts/dialogs/note_source.js | 2 +- src/public/javascripts/services/branches.js | 4 +- src/public/javascripts/services/cloning.js | 4 +- .../services/frontend_script_api.js | 10 +-- src/public/javascripts/services/import.js | 2 +- .../javascripts/services/note_autocomplete.js | 2 +- .../javascripts/services/note_detail.js | 27 +++++--- .../javascripts/services/protected_session.js | 6 +- src/public/javascripts/services/tree.js | 62 +++++++++++++------ src/public/javascripts/services/tree_cache.js | 33 +++++----- src/public/stylesheets/style.css | 4 +- src/services/ws.js | 7 +++ 21 files changed, 115 insertions(+), 82 deletions(-) diff --git a/docs/frontend_api/FrontendScriptApi.html b/docs/frontend_api/FrontendScriptApi.html index 8df157743..f654b2fb8 100644 --- a/docs/frontend_api/FrontendScriptApi.html +++ b/docs/frontend_api/FrontendScriptApi.html @@ -1581,7 +1581,7 @@ -

getActiveNote() → {NoteFull}

+

getActiveTabNote() → {NoteFull}

@@ -1687,7 +1687,7 @@ -

getActiveNotePath() → {Promise.<(string|null)>}

+

getActiveTabNotePath() → {Promise.<(string|null)>}

diff --git a/docs/frontend_api/services_frontend_script_api.js.html b/docs/frontend_api/services_frontend_script_api.js.html index cecb664e8..4d684ad79 100644 --- a/docs/frontend_api/services_frontend_script_api.js.html +++ b/docs/frontend_api/services_frontend_script_api.js.html @@ -236,7 +236,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte * @param {string} noteId * @method */ - this.reloadChildren = async noteId => await treeCache.reloadChildren(noteId); + this.reloadChildren = async noteId => await treeCache.reloadNotesAndTheirChildren(noteId); /** * @param {string} noteId @@ -303,13 +303,13 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte * @method * @returns {NoteFull} active note (loaded into right pane) */ - this.getActiveNote = noteDetailService.getActiveNote; + this.getActiveTabNote = noteDetailService.getActiveTabNote; /** * @method * @returns {Promise<string|null>} returns note path of active note or null if there isn't active note */ - this.getActiveNotePath = () => { + this.getActiveTabNotePath = () => { const activeTabContext = noteDetailService.getActiveTabContext(); return activeTabContext ? activeTabContext.notePath : null; diff --git a/libraries/codemirror/addon/lint/eslint.js b/libraries/codemirror/addon/lint/eslint.js index 321ddd3ba..b5d987e6b 100644 --- a/libraries/codemirror/addon/lint/eslint.js +++ b/libraries/codemirror/addon/lint/eslint.js @@ -29,8 +29,8 @@ async function validatorJavaScript(text, options) { if (glob.isMobile() - || glob.getActiveNote() == null - || glob.getActiveNote().mime === 'application/json') { + || glob.getActiveTabNote() == null + || glob.getActiveTabNote().mime === 'application/json') { // eslint doesn't seem to validate pure JSON well return []; } diff --git a/src/public/javascripts/desktop.js b/src/public/javascripts/desktop.js index fafc2e5f7..b7ee4728a 100644 --- a/src/public/javascripts/desktop.js +++ b/src/public/javascripts/desktop.js @@ -43,7 +43,7 @@ window.glob.noteChanged = noteDetailService.noteChanged; window.glob.refreshTree = treeService.reload; // required for ESLint plugin -window.glob.getActiveNote = noteDetailService.getActiveNote; +window.glob.getActiveTabNote = noteDetailService.getActiveTabNote; window.glob.requireLibrary = libraryLoader.requireLibrary; window.glob.ESLINT = libraryLoader.ESLINT; diff --git a/src/public/javascripts/dialogs/add_link.js b/src/public/javascripts/dialogs/add_link.js index c55a4794c..8a5ba65cd 100644 --- a/src/public/javascripts/dialogs/add_link.js +++ b/src/public/javascripts/dialogs/add_link.js @@ -33,7 +33,7 @@ export async function showDialog(linkType) { glob.activeDialog = $dialog; - if (noteDetailService.getActiveNoteType() === 'text') { + if (noteDetailService.getActiveTabNoteType() === 'text') { $linkTypeHtml.prop('disabled', false); setLinkType('html'); @@ -110,14 +110,14 @@ $form.submit(() => { else if (linkType === 'selected-to-active') { const prefix = $clonePrefix.val(); - cloningService.cloneNoteTo(noteId, noteDetailService.getActiveNoteId(), prefix); + cloningService.cloneNoteTo(noteId, noteDetailService.getActiveTabNoteId(), prefix); $dialog.modal('hide'); } else if (linkType === 'active-to-selected') { const prefix = $clonePrefix.val(); - cloningService.cloneNoteTo(noteDetailService.getActiveNoteId(), noteId, prefix); + cloningService.cloneNoteTo(noteDetailService.getActiveTabNoteId(), noteId, prefix); $dialog.modal('hide'); } diff --git a/src/public/javascripts/dialogs/attributes.js b/src/public/javascripts/dialogs/attributes.js index 80b8d8723..d373f4903 100644 --- a/src/public/javascripts/dialogs/attributes.js +++ b/src/public/javascripts/dialogs/attributes.js @@ -92,7 +92,7 @@ function AttributesModel() { } this.loadAttributes = async function() { - const noteId = noteDetailService.getActiveNoteId(); + const noteId = noteDetailService.getActiveTabNoteId(); const attributes = await server.get('notes/' + noteId + '/attributes'); @@ -138,7 +138,7 @@ function AttributesModel() { self.updateAttributePositions(); - const noteId = noteDetailService.getActiveNoteId(); + const noteId = noteDetailService.getActiveTabNoteId(); const attributesToSave = self.ownedAttributes() .map(attribute => attribute()) diff --git a/src/public/javascripts/dialogs/link_map.js b/src/public/javascripts/dialogs/link_map.js index 8c78e1b81..c62001fae 100644 --- a/src/public/javascripts/dialogs/link_map.js +++ b/src/public/javascripts/dialogs/link_map.js @@ -23,7 +23,7 @@ export async function showDialog() { // set default settings $maxNotesInput.val(20); - const note = noteDetailService.getActiveNote(); + const note = noteDetailService.getActiveTabNote(); if (!note) { return; diff --git a/src/public/javascripts/dialogs/note_info.js b/src/public/javascripts/dialogs/note_info.js index 9da0d2ca3..4d60c9a92 100644 --- a/src/public/javascripts/dialogs/note_info.js +++ b/src/public/javascripts/dialogs/note_info.js @@ -16,7 +16,7 @@ export function showDialog() { $dialog.modal(); - const activeNote = noteDetailService.getActiveNote(); + const activeNote = noteDetailService.getActiveTabNote(); $noteId.text(activeNote.noteId); $dateCreated.text(activeNote.dateCreated); diff --git a/src/public/javascripts/dialogs/note_revisions.js b/src/public/javascripts/dialogs/note_revisions.js index 783ff6a80..29eb16af6 100644 --- a/src/public/javascripts/dialogs/note_revisions.js +++ b/src/public/javascripts/dialogs/note_revisions.js @@ -11,7 +11,7 @@ let revisionItems = []; let note; export async function showCurrentNoteRevisions() { - await showNoteRevisionsDialog(noteDetailService.getActiveNoteId()); + await showNoteRevisionsDialog(noteDetailService.getActiveTabNoteId()); } export async function showNoteRevisionsDialog(noteId, noteRevisionId) { @@ -24,7 +24,7 @@ export async function showNoteRevisionsDialog(noteId, noteRevisionId) { $list.empty(); $content.empty(); - note = noteDetailService.getActiveNote(); + note = noteDetailService.getActiveTabNote(); revisionItems = await server.get('notes/' + noteId + '/revisions'); for (const item of revisionItems) { diff --git a/src/public/javascripts/dialogs/note_source.js b/src/public/javascripts/dialogs/note_source.js index 22d48fd39..76ead802b 100644 --- a/src/public/javascripts/dialogs/note_source.js +++ b/src/public/javascripts/dialogs/note_source.js @@ -11,7 +11,7 @@ export function showDialog() { $dialog.modal(); - const noteText = noteDetailService.getActiveNote().content; + const noteText = noteDetailService.getActiveTabNote().content; $noteSource.text(formatHtml(noteText)); } diff --git a/src/public/javascripts/services/branches.js b/src/public/javascripts/services/branches.js index c9702bd7d..1007a7a1c 100644 --- a/src/public/javascripts/services/branches.js +++ b/src/public/javascripts/services/branches.js @@ -162,9 +162,7 @@ async function deleteNodes(nodes) { node.remove(); } - for (const parentNoteId of parentNoteIds) { - await treeService.reloadNote(parentNoteId); - } + await treeService.reloadNotes(parentNoteIds); // activate after all the reloading if (activeNotePath) { diff --git a/src/public/javascripts/services/cloning.js b/src/public/javascripts/services/cloning.js index 791013ac5..412b33494 100644 --- a/src/public/javascripts/services/cloning.js +++ b/src/public/javascripts/services/cloning.js @@ -14,7 +14,7 @@ async function cloneNoteTo(childNoteId, parentNoteId, prefix) { treeCache.addBranchRelationship(resp.branchId, childNoteId, parentNoteId); - await treeService.reloadNote(parentNoteId); + await treeService.reloadNotes([parentNoteId]); } // beware that first arg is noteId and second is branchId! @@ -30,7 +30,7 @@ async function cloneNoteAfter(noteId, afterBranchId) { treeCache.addBranchRelationship(resp.branchId, noteId, afterBranch.parentNoteId); - await treeService.reloadNote(afterBranch.parentNoteId); + await treeService.reloadNotes([afterBranch.parentNoteId]); } export default { diff --git a/src/public/javascripts/services/frontend_script_api.js b/src/public/javascripts/services/frontend_script_api.js index dc5ef7f52..09c4910c5 100644 --- a/src/public/javascripts/services/frontend_script_api.js +++ b/src/public/javascripts/services/frontend_script_api.js @@ -210,7 +210,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte * @param {string} noteId * @method */ - this.reloadChildren = async noteId => await treeCache.reloadChildren(noteId); + this.reloadNotesAndTheirChildren = async noteId => await treeCache.reloadNotesAndTheirChildren(noteId); /** * @param {string} noteId @@ -277,17 +277,13 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte * @method * @returns {NoteFull} active note (loaded into right pane) */ - this.getActiveNote = noteDetailService.getActiveNote; + this.getActiveTabNote = noteDetailService.getActiveTabNote; /** * @method * @returns {Promise} returns note path of active note or null if there isn't active note */ - this.getActiveNotePath = () => { - const activeTabContext = noteDetailService.getActiveTabContext(); - - return activeTabContext ? activeTabContext.notePath : null; - }; + this.getActiveTabNotePath = noteDetailService.getActiveTabNotePath; /** * This method checks whether user navigated away from the note from which the scripts has been started. diff --git a/src/public/javascripts/services/import.js b/src/public/javascripts/services/import.js index b2c411022..24e3f83e1 100644 --- a/src/public/javascripts/services/import.js +++ b/src/public/javascripts/services/import.js @@ -63,7 +63,7 @@ ws.subscribeToMessages(async message => { toastService.showPersistent(toast); - await treeService.reloadNote(message.parentNoteId); + await treeService.reloadNotes([message.parentNoteId]); if (message.result.importedNoteId) { const node = await treeService.activateNote(message.result.importedNoteId); diff --git a/src/public/javascripts/services/note_autocomplete.js b/src/public/javascripts/services/note_autocomplete.js index 2d5c9536e..e5c66b7c6 100644 --- a/src/public/javascripts/services/note_autocomplete.js +++ b/src/public/javascripts/services/note_autocomplete.js @@ -8,7 +8,7 @@ const SELECTED_PATH_KEY = "data-note-path"; async function autocompleteSource(term, cb) { const result = await server.get('autocomplete' + '?query=' + encodeURIComponent(term) - + '&activeNoteId=' + noteDetailService.getActiveNoteId()); + + '&activeNoteId=' + noteDetailService.getActiveTabNoteId()); if (result.length === 0) { result.push({ diff --git a/src/public/javascripts/services/note_detail.js b/src/public/javascripts/services/note_detail.js index e332e4ec4..c5af66e72 100644 --- a/src/public/javascripts/services/note_detail.js +++ b/src/public/javascripts/services/note_detail.js @@ -107,20 +107,26 @@ function getActiveTabContext() { return tabContexts.find(tc => tc.tabId === tabId); } +/** @returns {string|null} */ +function getActiveTabNotePath() { + const activeContext = getActiveTabContext(); + return activeContext ? activeContext.notePath : null; +} + /** @return {NoteFull} */ -function getActiveNote() { +function getActiveTabNote() { const activeContext = getActiveTabContext(); return activeContext ? activeContext.note : null; } -function getActiveNoteId() { - const activeNote = getActiveNote(); +function getActiveTabNoteId() { + const activeNote = getActiveTabNote(); return activeNote ? activeNote.noteId : null; } -function getActiveNoteType() { - const activeNote = getActiveNote(); +function getActiveTabNoteType() { + const activeNote = getActiveTabNote(); return activeNote ? activeNote.type : null; } @@ -300,7 +306,7 @@ function addDetailLoadedListener(noteId, callback) { function fireDetailLoaded() { for (const {noteId, callback} of detailLoadedListeners) { - if (noteId === getActiveNoteId()) { + if (noteId === getActiveTabNoteId()) { callback(); } } @@ -337,7 +343,7 @@ $tabContentsContainer.on("dragover", e => e.preventDefault()); $tabContentsContainer.on("dragleave", e => e.preventDefault()); $tabContentsContainer.on("drop", async e => { - const activeNote = getActiveNote(); + const activeNote = getActiveTabNote(); if (!activeNote) { return; @@ -497,9 +503,6 @@ export default { switchToNote, loadNote, loadNoteDetail, - getActiveNote, - getActiveNoteType, - getActiveNoteId, focusOnTitle, focusAndSelectTitle, saveNotesIfChanged, @@ -508,6 +511,10 @@ export default { switchToTab, getTabContexts, getActiveTabContext, + getActiveTabNotePath, + getActiveTabNote, + getActiveTabNoteType, + getActiveTabNoteId, getActiveEditor, activateOrOpenNote, clearOpenTabsTask, diff --git a/src/public/javascripts/services/protected_session.js b/src/public/javascripts/services/protected_session.js index 237bbebdc..1a3245e55 100644 --- a/src/public/javascripts/services/protected_session.js +++ b/src/public/javascripts/services/protected_session.js @@ -70,13 +70,13 @@ async function enterProtectedSessionOnServer(password) { } async function protectNoteAndSendToServer() { - if (!noteDetailService.getActiveNote() || noteDetailService.getActiveNote().isProtected) { + if (!noteDetailService.getActiveTabNote() || noteDetailService.getActiveTabNote().isProtected) { return; } await enterProtectedSession(); - const note = noteDetailService.getActiveNote(); + const note = noteDetailService.getActiveTabNote(); note.isProtected = true; await noteDetailService.getActiveTabContext().saveNote(); @@ -87,7 +87,7 @@ async function protectNoteAndSendToServer() { } async function unprotectNoteAndSendToServer() { - const activeNote = noteDetailService.getActiveNote(); + const activeNote = noteDetailService.getActiveTabNote(); if (!activeNote.isProtected) { toastService.showAndLogError(`Note ${activeNote.noteId} is not protected`); diff --git a/src/public/javascripts/services/tree.js b/src/public/javascripts/services/tree.js index 9386d8e7f..4ff366df8 100644 --- a/src/public/javascripts/services/tree.js +++ b/src/public/javascripts/services/tree.js @@ -580,14 +580,11 @@ async function scrollToActiveNote() { } } -function setBranchBackgroundBasedOnProtectedStatus(noteId) { - getNodesByNoteId(noteId).map(node => node.toggleClass("protected", node.data.isProtected)); -} - function setProtected(noteId, isProtected) { - getNodesByNoteId(noteId).map(node => node.data.isProtected = isProtected); - - setBranchBackgroundBasedOnProtectedStatus(noteId); + getNodesByNoteId(noteId).map(node => { + node.data.isProtected = isProtected; + node.toggleClass("protected", isProtected); + }); } async function setNoteTitle(noteId, title) { @@ -619,7 +616,7 @@ async function createNote(node, parentNoteId, target, extraOptions = {}) { extraOptions.isProtected = false; } - if (noteDetailService.getActiveNoteType() !== 'text') { + if (noteDetailService.getActiveTabNoteType() !== 'text') { extraOptions.saveSelection = false; } else { @@ -762,13 +759,27 @@ ws.subscribeToMessages(message => { }); ws.subscribeToOutsideSyncMessages(syncData => { - if (syncData.some(sync => sync.entityName === 'branches') - || syncData.some(sync => sync.entityName === 'notes')) { + const noteIdsToRefresh = []; - console.log(utils.now(), "Reloading tree because of background changes"); - - reload(); + for (const sync of syncData.filter(sync => sync.entityName === 'branches')) { + if (!noteIdsToRefresh.includes(sync.parentNoteId)) { + noteIdsToRefresh.push(sync.parentNoteId); + } } + + for (const sync of syncData.filter(sync => sync.entityName === 'notes')) { + if (!noteIdsToRefresh.includes(sync.noteId)) { + noteIdsToRefresh.push(sync.noteId); + } + } + + for (const sync of syncData.filter(sync => sync.entityName === 'note_reordering')) { + if (!noteIdsToRefresh.includes(sync.entityId)) { + noteIdsToRefresh.push(sync.entityId); + } + } + + reloadNotes(noteIdsToRefresh); }); utils.bindGlobalShortcut('ctrl+o', async () => { @@ -805,13 +816,25 @@ async function checkFolderStatus(node) { node.renderTitle(); } -async function reloadNote(noteId) { - await treeCache.reloadChildren(noteId); +async function reloadNotes(noteIds) { + await treeCache.reloadNotesAndTheirChildren(noteIds); - for (const node of getNodesByNoteId(noteId)) { - await node.load(true); + const activeNotePath = noteDetailService.getActiveTabNotePath(); - await checkFolderStatus(node); + for (const noteId of noteIds) { + for (const node of getNodesByNoteId(noteId)) { + await node.load(true); + + await checkFolderStatus(node); + } + } + + if (activeNotePath) { + const node = await getNodeFromPath(activeNotePath); + + if (node) { + node.setActive(true, {noEvents: true}); // this node has been already active so no need to fire events again + } } } @@ -868,7 +891,6 @@ frontendLoaded.then(bundle.executeStartupBundles); export default { reload, collapseTree, - setBranchBackgroundBasedOnProtectedStatus, setProtected, activateNote, getFocusedNode, @@ -887,7 +909,7 @@ export default { setExpandedToServer, getNodesByNoteId, checkFolderStatus, - reloadNote, + reloadNotes, loadTreeCache, expandToNote, getNodeFromPath, diff --git a/src/public/javascripts/services/tree_cache.js b/src/public/javascripts/services/tree_cache.js index 9c94a911d..ba2e02a09 100644 --- a/src/public/javascripts/services/tree_cache.js +++ b/src/public/javascripts/services/tree_cache.js @@ -54,24 +54,27 @@ class TreeCache { } /** - * Reload children of given noteId. + * Reload notes and their children. */ - async reloadChildren(noteId) { - const resp = await server.post('tree/load', { noteIds: [noteId] }); + async reloadNotesAndTheirChildren(noteIds) { + // first load the data before clearing the cache + const resp = await server.post('tree/load', { noteIds }); - for (const childNoteId of this.children[noteId] || []) { - this.parents[childNoteId] = this.parents[childNoteId].filter(p => p !== noteId); + for (const noteId of noteIds) { + for (const childNoteId of this.children[noteId] || []) { + this.parents[childNoteId] = this.parents[childNoteId].filter(p => p !== noteId); - const branchId = this.getBranchIdByChildParent(childNoteId, noteId); + const branchId = this.getBranchIdByChildParent(childNoteId, noteId); - delete this.branches[branchId]; - delete this.childParentToBranch[childNoteId + '-' + noteId]; + delete this.branches[branchId]; + delete this.childParentToBranch[childNoteId + '-' + noteId]; + } + + this.children[noteId] = []; + + delete this.notes[noteId]; } - this.children[noteId] = []; - - delete this.notes[noteId]; - this.addResp(resp.notes, resp.branches, resp.relations); } @@ -83,12 +86,10 @@ class TreeCache { // to be able to find parents we need first to make sure it is actually loaded await this.getNote(noteId); - for (const parentNoteId of this.parents[noteId] || []) { - await this.reloadChildren(parentNoteId); - } + await this.reloadNotesAndTheirChildren(this.parents[noteId] || []); // this is done to load the new parents for the noteId - await this.reloadChildren(noteId); + await this.reloadNotesAndTheirChildren([noteId]); } /** @return {Promise} */ diff --git a/src/public/stylesheets/style.css b/src/public/stylesheets/style.css index 871196ffa..1790a845a 100644 --- a/src/public/stylesheets/style.css +++ b/src/public/stylesheets/style.css @@ -863,12 +863,14 @@ a.external:not(.no-arrow):after, a[href^="http://"]:not(.no-arrow):after, a[href position: absolute; width: 100%; top: 20px; - z-index: -100; + pointer-events: none; } .toast { background-color: var(--accented-background-color) !important; color: var(--main-text-color) !important; + z-index: 9999999999 !important; + pointer-events: all; } .toast-header { diff --git a/src/services/ws.js b/src/services/ws.js index 96a827922..7828ef6ab 100644 --- a/src/services/ws.js +++ b/src/services/ws.js @@ -67,12 +67,19 @@ async function sendPing(client, lastSentSyncId) { const syncData = await sql.getRows("SELECT * FROM sync WHERE id > ?", [lastSentSyncId]); for (const sync of syncData) { + // fill in some extra data needed by the frontend if (sync.entityName === 'attributes') { sync.noteId = await sql.getValue(`SELECT noteId FROM attributes WHERE attributeId = ?`, [sync.entityId]); } else if (sync.entityName === 'note_revisions') { sync.noteId = await sql.getValue(`SELECT noteId FROM note_revisions WHERE noteRevisionId = ?`, [sync.entityId]); } + else if (sync.entityName === 'branches') { + const {noteId, parentNoteId} = await sql.getRow(`SELECT noteId, parentNoteId FROM branches WHERE branchId = ?`, [sync.entityId]); + + sync.noteId = noteId; + sync.parentNoteId = parentNoteId; + } } const stats = require('./sync').stats;