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;