diff --git a/libraries/codemirror/addon/lint/eslint.js b/libraries/codemirror/addon/lint/eslint.js
index 5c310fa63..b1ab412e3 100644
--- a/libraries/codemirror/addon/lint/eslint.js
+++ b/libraries/codemirror/addon/lint/eslint.js
@@ -46,7 +46,7 @@
const errors = new eslint().verify(text, {
root: true,
parserOptions: {
- ecmaVersion: 2019
+ ecmaVersion: 2022
},
extends: ['eslint:recommended', 'airbnb-base'],
env: {
diff --git a/nodemon.json b/nodemon.json
new file mode 100644
index 000000000..df14c4a84
--- /dev/null
+++ b/nodemon.json
@@ -0,0 +1,13 @@
+{
+ "restartable": "rs",
+ "ignore": [".git", "node_modules/**/node_modules", "src/public/"],
+ "verbose": false,
+ "execMap": {
+ "js": "node --harmony"
+ },
+ "watch": ["src/"],
+ "env": {
+ "NODE_ENV": "development"
+ },
+ "ext": "js,json"
+}
diff --git a/package.json b/package.json
index 8728f961a..cce7feb50 100644
--- a/package.json
+++ b/package.json
@@ -13,16 +13,16 @@
"url": "https://github.com/zadam/trilium.git"
},
"scripts": {
- "start-server": "cross-env TRILIUM_DATA_DIR=./data TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 node ./src/www",
- "start-server-no-dir": "cross-env TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 node ./src/www",
+ "start-server": "cross-env TRILIUM_DATA_DIR=./data TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 nodemon ./src/www",
+ "start-server-no-dir": "cross-env TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 nodemon ./src/www",
"start-electron": "cross-env TRILIUM_DATA_DIR=./data TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 TRILIUM_ENV=dev electron --inspect=5858 .",
"start-electron-no-dir": "cross-env TRILIUM_ENV=dev TRILIUM_SYNC_SERVER_HOST=http://tsyncserver:4000 electron --inspect=5858 .",
"switch-server": "rm -rf ./node_modules/better-sqlite3 && npm install",
- "switch-electron": "rm -rf ./node_modules/better-sqlite3 && npm install && ./node_modules/.bin/electron-rebuild",
+ "switch-electron": "./node_modules/.bin/electron-rebuild",
"build-backend-docs": "rm -rf ./docs/backend_api && ./node_modules/.bin/jsdoc -c jsdoc-conf.json -d ./docs/backend_api src/becca/entities/*.js src/services/backend_script_api.js src/services/sql.js",
"build-frontend-docs": "rm -rf ./docs/frontend_api && ./node_modules/.bin/jsdoc -c jsdoc-conf.json -d ./docs/frontend_api src/public/app/entities/*.js src/public/app/services/frontend_script_api.js src/public/app/widgets/right_panel_widget.js",
"build-docs": "npm run build-backend-docs && npm run build-frontend-docs",
- "webpack": "npx webpack -c webpack-desktop.config.js && npx webpack -c webpack-mobile.config.js && npx webpack -c webpack-setup.config.js",
+ "webpack": "webpack -c webpack.config.js",
"test-jasmine": "jasmine",
"test-es6": "node -r esm spec-es6/attribute_parser.spec.js ",
"test": "npm run test-jasmine && npm run test-es6",
@@ -115,6 +115,7 @@
"jsdoc": "4.0.2",
"lorem-ipsum": "2.0.8",
"prettier": "2.8.7",
+ "nodemon": "^2.0.22",
"rcedit": "3.0.1",
"webpack": "5.78.0",
"webpack-cli": "5.0.1"
diff --git a/src/becca/becca_service.js b/src/becca/becca_service.js
index 80942c564..aad6fff5b 100644
--- a/src/becca/becca_service.js
+++ b/src/becca/becca_service.js
@@ -24,49 +24,12 @@ function isNotePathArchived(notePath) {
return false;
}
-/**
- * This assumes that note is available. "archived" note means that there isn't a single non-archived note-path
- * leading to this note.
- *
- * @param noteId
- */
-function isArchived(noteId) {
- const notePath = getSomePath(noteId);
-
- return isNotePathArchived(notePath);
-}
-
-/**
- * @param {string} noteId
- * @param {string} ancestorNoteId
- * @returns {boolean} - true if given noteId has ancestorNoteId in any of its paths (even archived)
- */
-function isInAncestor(noteId, ancestorNoteId) {
- if (ancestorNoteId === 'root' || ancestorNoteId === noteId) {
- return true;
- }
-
- const note = becca.notes[noteId];
-
- if (!note) {
- return false;
- }
-
- for (const parentNote of note.parents) {
- if (isInAncestor(parentNote.noteId, ancestorNoteId)) {
- return true;
- }
- }
-
- return false;
-}
-
function getNoteTitle(childNoteId, parentNoteId) {
const childNote = becca.notes[childNoteId];
const parentNote = becca.notes[parentNoteId];
if (!childNote) {
- log.info(`Cannot find note in cache for noteId '${childNoteId}'`);
+ log.info(`Cannot find note '${childNoteId}'`);
return "[error fetching title]";
}
@@ -119,107 +82,8 @@ function getNoteTitleForPath(notePathArray) {
return titles.join(' / ');
}
-/**
- * Returns notePath for noteId from cache. Note hoisting is respected.
- * Archived (and hidden) notes are also returned, but non-archived paths are preferred if available
- * - this means that archived paths is returned only if there's no non-archived path
- * - you can check whether returned path is archived using isArchived
- *
- * @param {BNote} note
- * @param {string[]} path
- */
-function getSomePath(note, path = []) {
- // first try to find note within hoisted note, otherwise take any existing note path
- return getSomePathInner(note, path, true)
- || getSomePathInner(note, path, false);
-}
-
-/**
- * @param {BNote} note
- * @param {string[]} path
- * @param {boolean}respectHoisting
- * @returns {string[]|false}
- */
-function getSomePathInner(note, path, respectHoisting) {
- if (note.isRoot()) {
- const foundPath = [...path, note.noteId];
- foundPath.reverse();
-
- if (respectHoisting && !foundPath.includes(cls.getHoistedNoteId())) {
- return false;
- }
-
- return foundPath;
- }
-
- const parents = note.parents;
- if (parents.length === 0) {
- console.log(`Note '${note.noteId}' - '${note.title}' has no parents.`);
-
- return false;
- }
-
- for (const parentNote of parents) {
- const retPath = getSomePathInner(parentNote, [...path, note.noteId], respectHoisting);
-
- if (retPath) {
- return retPath;
- }
- }
-
- return false;
-}
-
-function getNotePath(noteId) {
- const note = becca.notes[noteId];
-
- if (!note) {
- console.trace(`Cannot find note '${noteId}' in cache.`);
- return;
- }
-
- const retPath = getSomePath(note);
-
- if (retPath) {
- const noteTitle = getNoteTitleForPath(retPath);
-
- let branchId;
-
- if (note.isRoot()) {
- branchId = 'none_root';
- }
- else {
- const parentNote = note.parents[0];
- branchId = becca.getBranchFromChildAndParent(noteId, parentNote.noteId).branchId;
- }
-
- return {
- noteId: noteId,
- branchId: branchId,
- title: noteTitle,
- notePath: retPath,
- path: retPath.join('/')
- };
- }
-}
-
-/**
- * @param noteId
- * @returns {boolean} - true if note exists (is not deleted) and is available in current note hoisting
- */
-function isAvailable(noteId) {
- const notePath = getNotePath(noteId);
-
- return !!notePath;
-}
-
module.exports = {
- getSomePath,
- getNotePath,
getNoteTitle,
getNoteTitleForPath,
- isAvailable,
- isArchived,
- isInAncestor,
isNotePathArchived
};
diff --git a/src/becca/entities/bnote.js b/src/becca/entities/bnote.js
index 2e9febadb..b8e58c740 100644
--- a/src/becca/entities/bnote.js
+++ b/src/becca/entities/bnote.js
@@ -747,6 +747,21 @@ class BNote extends AbstractBeccaEntity {
return this.hasAttribute('label', 'archived');
}
+ areAllNotePathsArchived() {
+ // there's a slight difference between note being itself archived and all its note paths being archived
+ // - note is archived when it itself has an archived label or inherits it
+ // - note does not have or inherit archived label, but each note paths contains a note with (non-inheritable)
+ // archived label
+
+ const bestNotePathRecord = this.getSortedNotePathRecords()[0];
+
+ if (!bestNotePathRecord) {
+ throw new Error(`No note path available for note '${this.noteId}'`);
+ }
+
+ return bestNotePathRecord.isArchived;
+ }
+
hasInheritableArchivedLabel() {
for (const attr of this.getAttributes()) {
if (attr.name === 'archived' && attr.type === LABEL && attr.isInheritable) {
@@ -1150,6 +1165,8 @@ class BNote extends AbstractBeccaEntity {
}
/**
+ * Gives all possible note paths leading to this note. Paths containing search note are ignored (could form cycles)
+ *
* @returns {string[][]} - array of notePaths (each represented by array of noteIds constituting the particular note path)
*/
getAllNotePaths() {
@@ -1157,18 +1174,73 @@ class BNote extends AbstractBeccaEntity {
return [['root']];
}
- const notePaths = [];
+ const parentNotes = this.getParentNotes();
+ let notePaths = [];
- for (const parentNote of this.getParentNotes()) {
- for (const parentPath of parentNote.getAllNotePaths()) {
- parentPath.push(this.noteId);
- notePaths.push(parentPath);
- }
+ if (parentNotes.length === 1) { // optimization for most common case
+ notePaths = parentNotes[0].getAllNotePaths();
+ } else {
+ notePaths = parentNotes.flatMap(parentNote => parentNote.getAllNotePaths());
+ }
+
+ for (const notePath of notePaths) {
+ notePath.push(this.noteId);
}
return notePaths;
}
+ /**
+ * @param {string} [hoistedNoteId='root']
+ * @return {{isArchived: boolean, isInHoistedSubTree: boolean, notePath: string[], isHidden: boolean}[]}
+ */
+ getSortedNotePathRecords(hoistedNoteId = 'root') {
+ const isHoistedRoot = hoistedNoteId === 'root';
+
+ const notePaths = this.getAllNotePaths().map(path => ({
+ notePath: path,
+ isInHoistedSubTree: isHoistedRoot || path.includes(hoistedNoteId),
+ isArchived: path.some(noteId => this.becca.notes[noteId].isArchived),
+ isHidden: path.includes('_hidden')
+ }));
+
+ notePaths.sort((a, b) => {
+ if (a.isInHoistedSubTree !== b.isInHoistedSubTree) {
+ return a.isInHoistedSubTree ? -1 : 1;
+ } else if (a.isArchived !== b.isArchived) {
+ return a.isArchived ? 1 : -1;
+ } else if (a.isHidden !== b.isHidden) {
+ return a.isHidden ? 1 : -1;
+ } else {
+ return a.notePath.length - b.notePath.length;
+ }
+ });
+
+ return notePaths;
+ }
+
+ /**
+ * Returns note path considered to be the "best"
+ *
+ * @param {string} [hoistedNoteId='root']
+ * @return {string[]} array of noteIds constituting the particular note path
+ */
+ getBestNotePath(hoistedNoteId = 'root') {
+ return this.getSortedNotePathRecords(hoistedNoteId)[0]?.notePath;
+ }
+
+ /**
+ * Returns note path considered to be the "best"
+ *
+ * @param {string} [hoistedNoteId='root']
+ * @return {string} serialized note path (e.g. 'root/a1h315/js725h')
+ */
+ getBestNotePathString(hoistedNoteId = 'root') {
+ const notePath = this.getBestNotePath(hoistedNoteId);
+
+ return notePath?.join("/");
+ }
+
/**
* @return boolean - true if there's no non-hidden path, note is not cloned to the visible tree
*/
diff --git a/src/becca/similarity.js b/src/becca/similarity.js
index 2e7750100..8900bb87d 100644
--- a/src/becca/similarity.js
+++ b/src/becca/similarity.js
@@ -404,7 +404,7 @@ async function findSimilarNotes(noteId) {
let score = computeScore(candidateNote);
if (score >= 1.5) {
- const notePath = beccaService.getSomePath(candidateNote);
+ const notePath = candidateNote.getBestNotePath();
// this takes care of note hoisting
if (!notePath) {
diff --git a/src/public/app/components/tab_manager.js b/src/public/app/components/tab_manager.js
index 92e94b218..e5ca32c45 100644
--- a/src/public/app/components/tab_manager.js
+++ b/src/public/app/components/tab_manager.js
@@ -388,7 +388,12 @@ export default class TabManager extends Component {
await this.triggerEvent('beforeNoteContextRemove', { ntxIds: ntxIdsToRemove });
if (!noteContextToRemove.isMainContext()) {
- await this.activateNoteContext(noteContextToRemove.getMainContext().ntxId);
+ const siblings = noteContextToRemove.getMainContext().getSubContexts();
+ const idx = siblings.findIndex(nc => nc.ntxId === noteContextToRemove.ntxId);
+ const contextToActivateIdx = idx === siblings.length - 1 ? idx - 1 : idx + 1;
+ const contextToActivate = siblings[contextToActivateIdx];
+
+ await this.activateNoteContext(contextToActivate.ntxId);
}
else if (this.mainNoteContexts.length <= 1) {
await this.openAndActivateEmptyTab();
diff --git a/src/public/app/entities/fnote.js b/src/public/app/entities/fnote.js
index 1066a9341..ad442e999 100644
--- a/src/public/app/entities/fnote.js
+++ b/src/public/app/entities/fnote.js
@@ -247,6 +247,11 @@ class FNote {
return this.__filterAttrs(this.__getCachedAttributes([]), type, name);
}
+ /**
+ * @param {string[]} path
+ * @return {FAttribute[]}
+ * @private
+ */
__getCachedAttributes(path) {
// notes/clones cannot form tree cycles, it is possible to create attribute inheritance cycle via templates
// when template instance is a parent of template itself
@@ -299,63 +304,49 @@ class FNote {
return this.noteId === 'root';
}
- getAllNotePaths(encounteredNoteIds = null) {
+ /**
+ * Gives all possible note paths leading to this note. Paths containing search note are ignored (could form cycles)
+ *
+ * @returns {string[][]} - array of notePaths (each represented by array of noteIds constituting the particular note path)
+ */
+ getAllNotePaths() {
if (this.noteId === 'root') {
return [['root']];
}
- if (!encounteredNoteIds) {
- encounteredNoteIds = new Set();
- }
-
- encounteredNoteIds.add(this.noteId);
-
const parentNotes = this.getParentNotes();
- let paths;
+ let notePaths = [];
- if (parentNotes.length === 1) { // optimization for the most common case
- if (encounteredNoteIds.has(parentNotes[0].noteId)) {
- return [];
- }
- else {
- paths = parentNotes[0].getAllNotePaths(encounteredNoteIds);
- }
- }
- else {
- paths = [];
-
- for (const parentNote of parentNotes) {
- if (encounteredNoteIds.has(parentNote.noteId)) {
- continue;
- }
-
- const newSet = new Set(encounteredNoteIds);
-
- paths.push(...parentNote.getAllNotePaths(newSet));
- }
+ if (parentNotes.length === 1) { // optimization for most common case
+ notePaths = parentNotes[0].getAllNotePaths();
+ } else {
+ notePaths = parentNotes.flatMap(parentNote => parentNote.getAllNotePaths());
}
- for (const path of paths) {
- path.push(this.noteId);
+ for (const notePath of notePaths) {
+ notePath.push(this.noteId);
}
- return paths;
+ return notePaths;
}
- getSortedNotePaths(hoistedNotePath = 'root') {
+ /**
+ * @param {string} [hoistedNoteId='root']
+ * @return {{isArchived: boolean, isInHoistedSubTree: boolean, notePath: string[], isHidden: boolean}[]}
+ */
+ getSortedNotePathRecords(hoistedNoteId = 'root') {
+ const isHoistedRoot = hoistedNoteId === 'root';
+
const notePaths = this.getAllNotePaths().map(path => ({
notePath: path,
- isInHoistedSubTree: path.includes(hoistedNotePath),
- isArchived: path.find(noteId => froca.notes[noteId].isArchived),
- isSearch: path.find(noteId => froca.notes[noteId].type === 'search'),
+ isInHoistedSubTree: isHoistedRoot || path.includes(hoistedNoteId),
+ isArchived: path.some(noteId => froca.notes[noteId].isArchived),
isHidden: path.includes('_hidden')
}));
notePaths.sort((a, b) => {
if (a.isInHoistedSubTree !== b.isInHoistedSubTree) {
return a.isInHoistedSubTree ? -1 : 1;
- } else if (a.isSearch !== b.isSearch) {
- return a.isSearch ? 1 : -1;
} else if (a.isArchived !== b.isArchived) {
return a.isArchived ? 1 : -1;
} else if (a.isHidden !== b.isHidden) {
@@ -368,6 +359,28 @@ class FNote {
return notePaths;
}
+ /**
+ * Returns note path considered to be the "best"
+ *
+ * @param {string} [hoistedNoteId='root']
+ * @return {string[]} array of noteIds constituting the particular note path
+ */
+ getBestNotePath(hoistedNoteId = 'root') {
+ return this.getSortedNotePathRecords(hoistedNoteId)[0]?.notePath;
+ }
+
+ /**
+ * Returns note path considered to be the "best"
+ *
+ * @param {string} [hoistedNoteId='root']
+ * @return {string} serialized note path (e.g. 'root/a1h315/js725h')
+ */
+ getBestNotePathString(hoistedNoteId = 'root') {
+ const notePath = this.getBestNotePath(hoistedNoteId);
+
+ return notePath?.join("/");
+ }
+
/**
* @return boolean - true if there's no non-hidden path, note is not cloned to the visible tree
*/
@@ -391,6 +404,13 @@ class FNote {
return true;
}
+ /**
+ * @param {FAttribute[]} attributes
+ * @param {string} type
+ * @param {string} name
+ * @return {FAttribute[]}
+ * @private
+ */
__filterAttrs(attributes, type, name) {
this.__validateTypeName(type, name);
@@ -527,7 +547,9 @@ class FNote {
* @returns {boolean} true if note has an attribute with given type and name (including inherited)
*/
hasAttribute(type, name) {
- return !!this.getAttribute(type, name);
+ const attributes = this.getAttributes();
+
+ return attributes.some(attr => attr.name === name && attr.type === type);
}
/**
diff --git a/src/public/app/services/branches.js b/src/public/app/services/branches.js
index c414cf257..b0cf129a2 100644
--- a/src/public/app/services/branches.js
+++ b/src/public/app/services/branches.js
@@ -227,7 +227,7 @@ async function cloneNoteToBranch(childNoteId, parentBranchId, prefix) {
}
}
-async function cloneNoteToNote(childNoteId, parentNoteId, prefix) {
+async function cloneNoteToParentNote(childNoteId, parentNoteId, prefix) {
const resp = await server.put(`notes/${childNoteId}/clone-to-note/${parentNoteId}`, {
prefix: prefix
});
@@ -254,5 +254,5 @@ export default {
moveNodeUpInHierarchy,
cloneNoteAfter,
cloneNoteToBranch,
- cloneNoteToNote,
+ cloneNoteToParentNote,
};
diff --git a/src/public/app/services/note_autocomplete.js b/src/public/app/services/note_autocomplete.js
index 07c75d447..c548d6f0f 100644
--- a/src/public/app/services/note_autocomplete.js
+++ b/src/public/app/services/note_autocomplete.js
@@ -2,7 +2,6 @@ import server from "./server.js";
import appContext from "../components/app_context.js";
import utils from './utils.js';
import noteCreateService from './note_create.js';
-import treeService from './tree.js';
import froca from "./froca.js";
// this key needs to have this value, so it's hit by the tooltip
@@ -188,7 +187,8 @@ function initNoteAutocomplete($el, options) {
templateNoteId: templateNoteId
});
- suggestion.notePath = treeService.getSomeNotePath(note);
+ const hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId;
+ suggestion.notePath = note.getBestNotePathString(hoistedNoteId);
}
$el.setSelectedNotePath(suggestion.notePath);
diff --git a/src/public/app/services/note_tooltip.js b/src/public/app/services/note_tooltip.js
index b83296cd8..ceff12b49 100644
--- a/src/public/app/services/note_tooltip.js
+++ b/src/public/app/services/note_tooltip.js
@@ -4,6 +4,7 @@ import froca from "./froca.js";
import utils from "./utils.js";
import attributeRenderer from "./attribute_renderer.js";
import noteContentRenderer from "./note_content_renderer.js";
+import appContext from "../components/app_context.js";
function setupGlobalTooltip() {
$(document).on("mouseenter", "a", mouseEnterHandler);
@@ -83,13 +84,14 @@ async function renderTooltip(note) {
return '
Note has been deleted.
';
}
- const someNotePath = treeService.getSomeNotePath(note);
+ const hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId;
+ const bestNotePath = note.getBestNotePathString(hoistedNoteId);
- if (!someNotePath) {
+ if (!bestNotePath) {
return;
}
- let content = ``;
+ let content = ``;
const {$renderedAttributes} = await attributeRenderer.renderNormalAttributes(note);
diff --git a/src/public/app/services/tree.js b/src/public/app/services/tree.js
index 3c4925f3e..88f13cdde 100644
--- a/src/public/app/services/tree.js
+++ b/src/public/app/services/tree.js
@@ -79,14 +79,10 @@ async function resolveNotePathToSegments(notePath, hoistedNoteId = 'root', logEr
You can ignore this message as it is mostly harmless.`);
}
- const someNotePath = getSomeNotePath(child, hoistedNoteId);
+ const bestNotePath = child.getBestNotePath(hoistedNoteId);
- if (someNotePath) { // in case it's root the path may be empty
- const pathToRoot = someNotePath.split("/").reverse().slice(1);
-
- if (!pathToRoot.includes("root")) {
- pathToRoot.push('root');
- }
+ if (bestNotePath) {
+ const pathToRoot = bestNotePath.reverse().slice(1);
for (const noteId of pathToRoot) {
effectivePathSegments.push(noteId);
@@ -109,31 +105,17 @@ async function resolveNotePathToSegments(notePath, hoistedNoteId = 'root', logEr
else {
const note = await froca.getNote(getNoteIdFromNotePath(notePath));
- const someNotePathSegments = getSomeNotePathSegments(note, hoistedNoteId);
+ const bestNotePath = note.getBestNotePath(hoistedNoteId);
- if (!someNotePathSegments) {
- throw new Error(`Did not find any path segments for ${note.toString()}, hoisted note ${hoistedNoteId}`);
+ if (!bestNotePath) {
+ throw new Error(`Did not find any path segments for '${note.toString()}', hoisted note '${hoistedNoteId}'`);
}
// if there isn't actually any note path with hoisted note then return the original resolved note path
- return someNotePathSegments.includes(hoistedNoteId) ? someNotePathSegments : effectivePathSegments;
+ return bestNotePath.includes(hoistedNoteId) ? bestNotePath : effectivePathSegments;
}
}
-function getSomeNotePathSegments(note, hoistedNotePath = 'root') {
- utils.assertArguments(note);
-
- const notePaths = note.getSortedNotePaths(hoistedNotePath);
-
- return notePaths.length > 0 ? notePaths[0].notePath : null;
-}
-
-function getSomeNotePath(note, hoistedNotePath = 'root') {
- const notePath = getSomeNotePathSegments(note, hoistedNotePath);
-
- return notePath === null ? null : notePath.join('/');
-}
-
ws.subscribeToMessages(message => {
if (message.type === 'openNote') {
appContext.tabManager.activateOrOpenNote(message.noteId);
@@ -311,16 +293,6 @@ function isNotePathInAddress() {
|| (notePath === '' && !!ntxId);
}
-function parseNotePath(notePath) {
- let noteIds = notePath.split('/');
-
- if (noteIds[0] !== 'root') {
- noteIds = ['root'].concat(noteIds);
- }
-
- return noteIds;
-}
-
function isNotePathInHiddenSubtree(notePath) {
return notePath?.includes("root/_hidden");
}
@@ -328,8 +300,6 @@ function isNotePathInHiddenSubtree(notePath) {
export default {
resolveNotePath,
resolveNotePathToSegments,
- getSomeNotePath,
- getSomeNotePathSegments,
getParentProtectedStatus,
getNotePath,
getNoteIdFromNotePath,
@@ -340,6 +310,5 @@ export default {
getNoteTitleWithPathAsSuffix,
getHashValueFromAddress,
isNotePathInAddress,
- parseNotePath,
isNotePathInHiddenSubtree
};
diff --git a/src/public/app/widgets/attribute_widgets/attribute_detail.js b/src/public/app/widgets/attribute_widgets/attribute_detail.js
index 80efc31ba..dba64da70 100644
--- a/src/public/app/widgets/attribute_widgets/attribute_detail.js
+++ b/src/public/app/widgets/attribute_widgets/attribute_detail.js
@@ -1,6 +1,5 @@
import server from "../../services/server.js";
import froca from "../../services/froca.js";
-import treeService from "../../services/tree.js";
import linkService from "../../services/link.js";
import attributeAutocompleteService from "../../services/attribute_autocomplete.js";
import noteAutocompleteService from "../../services/note_autocomplete.js";
@@ -9,6 +8,7 @@ import NoteContextAwareWidget from "../note_context_aware_widget.js";
import SpacedUpdate from "../../services/spaced_update.js";
import utils from "../../services/utils.js";
import shortcutService from "../../services/shortcuts.js";
+import appContext from "../../components/app_context.js";
const TPL = `
@@ -598,9 +598,10 @@ export default class AttributeDetailWidget extends NoteContextAwareWidget {
const displayedResults = results.length <= DISPLAYED_NOTES ? results : results.slice(0, DISPLAYED_NOTES);
const displayedNotes = await froca.getNotes(displayedResults.map(res => res.noteId));
+ const hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId;
for (const note of displayedNotes) {
- const notePath = treeService.getSomeNotePath(note);
+ const notePath = note.getBestNotePathString(hoistedNoteId);
const $noteLink = await linkService.createNoteLink(notePath, {showNotePath: true});
this.$relatedNotesList.append(
diff --git a/src/public/app/widgets/attribute_widgets/attribute_editor.js b/src/public/app/widgets/attribute_widgets/attribute_editor.js
index 217717f05..88d3e837a 100644
--- a/src/public/app/widgets/attribute_widgets/attribute_editor.js
+++ b/src/public/app/widgets/attribute_widgets/attribute_editor.js
@@ -7,7 +7,6 @@ import libraryLoader from "../../services/library_loader.js";
import froca from "../../services/froca.js";
import attributeRenderer from "../../services/attribute_renderer.js";
import noteCreateService from "../../services/note_create.js";
-import treeService from "../../services/tree.js";
import attributeService from "../../services/attributes.js";
const HELP_TEXT = `
@@ -503,7 +502,7 @@ export default class AttributeEditorWidget extends NoteContextAwareWidget {
title: title
});
- return treeService.getSomeNotePath(note);
+ return note.getBestNotePathString();
}
async updateAttributeList(attributes) {
diff --git a/src/public/app/widgets/dialogs/recent_changes.js b/src/public/app/widgets/dialogs/recent_changes.js
index 75b477276..21d38d016 100644
--- a/src/public/app/widgets/dialogs/recent_changes.js
+++ b/src/public/app/widgets/dialogs/recent_changes.js
@@ -1,7 +1,6 @@
import linkService from '../../services/link.js';
import utils from '../../services/utils.js';
import server from '../../services/server.js';
-import treeService from "../../services/tree.js";
import froca from "../../services/froca.js";
import appContext from "../../components/app_context.js";
import hoistedNoteService from "../../services/hoisted_note.js";
@@ -108,7 +107,7 @@ export default class RecentChangesDialog extends BasicWidget {
}
} else {
const note = await froca.getNote(change.noteId);
- const notePath = treeService.getSomeNotePath(note);
+ const notePath = note.getBestNotePathString();
if (notePath) {
$noteLink = await linkService.createNoteLink(notePath, {
diff --git a/src/public/app/widgets/note_tree.js b/src/public/app/widgets/note_tree.js
index 7837672d2..d438bba4b 100644
--- a/src/public/app/widgets/note_tree.js
+++ b/src/public/app/widgets/note_tree.js
@@ -223,7 +223,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
return false;
});
- this.$treeSettingsPopup.on("click", e => { e.stopPropagation(); });
+ this.$treeSettingsPopup.on("click", e => {e.stopPropagation();});
$(document).on('click', () => this.$treeSettingsPopup.hide());
@@ -251,12 +251,12 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
// code inspired by https://gist.github.com/jtsternberg/c272d7de5b967cec2d3d
const isEnclosing = ($container, $sub) => {
- const conOffset = $container.offset();
- const conDistanceFromTop = conOffset.top + $container.outerHeight(true);
+ const conOffset = $container.offset();
+ const conDistanceFromTop = conOffset.top + $container.outerHeight(true);
const conDistanceFromLeft = conOffset.left + $container.outerWidth(true);
- const subOffset = $sub.offset();
- const subDistanceFromTop = subOffset.top + $sub.outerHeight(true);
+ const subOffset = $sub.offset();
+ const subDistanceFromTop = subOffset.top + $sub.outerHeight(true);
const subDistanceFromLeft = subOffset.left + $sub.outerWidth(true);
return conDistanceFromTop > subDistanceFromTop
@@ -673,7 +673,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
return noteList;
}
- updateNode(node) {
+ async updateNode(node) {
const note = froca.getNoteFromCache(node.data.noteId);
const branch = froca.getBranch(node.data.branchId);
@@ -697,7 +697,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
node.title = utils.escapeHtml(title);
if (node.isExpanded() !== branch.isExpanded) {
- node.setExpanded(branch.isExpanded, {noEvents: true, noAnimation: true});
+ await node.setExpanded(branch.isExpanded, {noEvents: true, noAnimation: true});
}
node.renderTitle();
@@ -849,7 +849,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
await this.setExpandedStatusForSubtree(node, false);
}
- collapseTreeEvent() { this.collapseTree(); }
+ collapseTreeEvent() {this.collapseTree();}
/**
* @returns {FancytreeNode|null}
@@ -920,7 +920,9 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
}
if (expand) {
- await parentNode.setExpanded(true, {noAnimation: true});
+ if (!parentNode.isExpanded()) {
+ await parentNode.setExpanded(true, {noAnimation: true});
+ }
// although previous line should set the expanded status, it seems to happen asynchronously,
// so we need to make sure it is set properly before calling updateNode which uses this flag
@@ -928,7 +930,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
branch.isExpanded = true;
}
- this.updateNode(parentNode);
+ await this.updateNode(parentNode);
let foundChildNode = this.findChildNode(parentNode, childNoteId);
@@ -1096,10 +1098,10 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
const activeNode = this.getActiveNode();
const activeNodeFocused = activeNode && activeNode.hasFocus();
const nextNode = activeNode ? (activeNode.getNextSibling() || activeNode.getPrevSibling() || activeNode.getParent()) : null;
- const activeNotePath = activeNode ? treeService.getNotePath(activeNode) : null;
+ let activeNotePath = activeNode ? treeService.getNotePath(activeNode) : null;
const nextNotePath = nextNode ? treeService.getNotePath(nextNode) : null;
- const activeNoteId = activeNode ? activeNode.data.noteId : null;
+ let activeNoteId = activeNode ? activeNode.data.noteId : null;
const noteIdsToUpdate = new Set();
const noteIdsToReload = new Set();
@@ -1142,7 +1144,14 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
}
}
- for (const ecBranch of loadResults.getBranches()) {
+ // activeNode is supposed to be moved when we find out activeNode is deleted but not all branches are deleted. save it for fixing activeNodePath after all nodes loaded.
+ let movedActiveNode = null;
+ let parentsOfAddedNodes = [];
+
+ const allBranches = loadResults.getBranches();
+ const allBranchesDeleted = allBranches.every(branch => !!branch.isDeleted);
+
+ for (const ecBranch of allBranches) {
if (ecBranch.parentNoteId === '_share') {
// all shared notes have a sign in the tree, even the descendants of shared notes
noteIdsToReload.add(ecBranch.noteId);
@@ -1155,12 +1164,16 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
for (const node of this.getNodesByBranch(ecBranch)) {
if (ecBranch.isDeleted) {
if (node.isActive()) {
- const newActiveNode = node.getNextSibling()
- || node.getPrevSibling()
- || node.getParent();
+ if (allBranchesDeleted) {
+ const newActiveNode = node.getNextSibling()
+ || node.getPrevSibling()
+ || node.getParent();
- if (newActiveNode) {
- newActiveNode.setActive(true, {noEvents: true, noFocus: true});
+ if (newActiveNode) {
+ newActiveNode.setActive(true, {noEvents: true, noFocus: true});
+ }
+ } else {
+ movedActiveNode = node;
}
}
@@ -1174,12 +1187,13 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
if (!ecBranch.isDeleted) {
for (const parentNode of this.getNodesByNoteId(ecBranch.parentNoteId)) {
+ parentsOfAddedNodes.push(parentNode)
+
if (parentNode.isFolder() && !parentNode.isLoaded()) {
continue;
}
const found = (parentNode.getChildren() || []).find(child => child.data.noteId === ecBranch.noteId);
-
if (!found) {
// make sure it's loaded
await froca.getNote(ecBranch.noteId);
@@ -1222,7 +1236,18 @@ export default class NoteTreeWidget extends NoteContextAwareWidget {
// for some reason node update cannot be in the batchUpdate() block (node is not re-rendered)
for (const noteId of noteIdsToUpdate) {
for (const node of this.getNodesByNoteId(noteId)) {
- this.updateNode(node);
+ await this.updateNode(node);
+ }
+ }
+
+ if (movedActiveNode) {
+ for (const parentNode of parentsOfAddedNodes) {
+ const found = (parentNode.getChildren() || []).find(child => child.data.noteId === movedActiveNode.data.noteId);
+ if (found) {
+ activeNotePath = treeService.getNotePath(found);
+ activeNoteId = found.data.noteId;
+ break
+ }
}
}
diff --git a/src/public/app/widgets/ribbon_widgets/note_paths.js b/src/public/app/widgets/ribbon_widgets/note_paths.js
index 3270e0eb4..7a6977b17 100644
--- a/src/public/app/widgets/ribbon_widgets/note_paths.js
+++ b/src/public/app/widgets/ribbon_widgets/note_paths.js
@@ -72,7 +72,7 @@ export default class NotePathsWidget extends NoteContextAwareWidget {
return;
}
- const sortedNotePaths = this.note.getSortedNotePaths(this.hoistedNoteId)
+ const sortedNotePaths = this.note.getSortedNotePathRecords(this.hoistedNoteId)
.filter(notePath => !notePath.isHidden);
if (sortedNotePaths.length > 0) {
diff --git a/src/public/app/widgets/shared_switch.js b/src/public/app/widgets/shared_switch.js
index 61dd140db..a7aaa1e52 100644
--- a/src/public/app/widgets/shared_switch.js
+++ b/src/public/app/widgets/shared_switch.js
@@ -25,7 +25,7 @@ export default class SharedSwitchWidget extends SwitchWidget {
}
async switchOn() {
- await branchService.cloneNoteToNote(this.noteId, '_share');
+ await branchService.cloneNoteToParentNote(this.noteId, '_share');
syncService.syncNow(true);
}
diff --git a/src/public/app/widgets/type_widgets/editable_code.js b/src/public/app/widgets/type_widgets/editable_code.js
index abb55b36b..a183601ce 100644
--- a/src/public/app/widgets/type_widgets/editable_code.js
+++ b/src/public/app/widgets/type_widgets/editable_code.js
@@ -53,7 +53,7 @@ export default class EditableCodeTypeWidget extends TypeWidget {
matchBrackets: true,
keyMap: options.is('vimKeymapEnabled') ? "vim": "default",
matchTags: {bothTags: true},
- highlightSelectionMatches: {showToken: /\w/, annotateScrollbar: false},
+ highlightSelectionMatches: {showToken: false, annotateScrollbar: false},
lint: true,
gutters: ["CodeMirror-lint-markers"],
lineNumbers: true,
@@ -62,7 +62,7 @@ export default class EditableCodeTypeWidget extends TypeWidget {
// all the way to the bottom of the note. With line wrap there's no horizontal scrollbar so no problem
lineWrapping: options.is('codeLineWrapEnabled'),
dragDrop: false, // with true the editor inlines dropped files which is not what we expect
- placeholder: "Type the content of your code note here..."
+ placeholder: "Type the content of your code note here...",
});
this.codeEditor.on('change', () => this.spacedUpdate.scheduleUpdate());
diff --git a/src/public/app/widgets/type_widgets/editable_text.js b/src/public/app/widgets/type_widgets/editable_text.js
index aa1015242..2c6cf55f2 100644
--- a/src/public/app/widgets/type_widgets/editable_text.js
+++ b/src/public/app/widgets/type_widgets/editable_text.js
@@ -4,7 +4,6 @@ import mimeTypesService from '../../services/mime_types.js';
import utils from "../../services/utils.js";
import keyboardActionService from "../../services/keyboard_actions.js";
import froca from "../../services/froca.js";
-import treeService from "../../services/tree.js";
import noteCreateService from "../../services/note_create.js";
import AbstractTextTypeWidget from "./abstract_text_type_widget.js";
import link from "../../services/link.js";
@@ -378,7 +377,7 @@ export default class EditableTextTypeWidget extends AbstractTextTypeWidget {
return;
}
- return treeService.getSomeNotePath(resp.note);
+ return resp.note.getBestNotePathString();
}
async refreshIncludedNoteEvent({noteId}) {
diff --git a/src/routes/api/cloning.js b/src/routes/api/cloning.js
index da557715c..75a42e675 100644
--- a/src/routes/api/cloning.js
+++ b/src/routes/api/cloning.js
@@ -9,11 +9,11 @@ function cloneNoteToBranch(req) {
return cloningService.cloneNoteToBranch(noteId, parentBranchId, prefix);
}
-function cloneNoteToNote(req) {
+function cloneNoteToParentNote(req) {
const {noteId, parentNoteId} = req.params;
const {prefix} = req.body;
- return cloningService.cloneNoteToNote(noteId, parentNoteId, prefix);
+ return cloningService.cloneNoteToParentNote(noteId, parentNoteId, prefix);
}
function cloneNoteAfter(req) {
@@ -30,7 +30,7 @@ function toggleNoteInParent(req) {
module.exports = {
cloneNoteToBranch,
- cloneNoteToNote,
+ cloneNoteToParentNote,
cloneNoteAfter,
toggleNoteInParent
};
diff --git a/src/routes/api/note_revisions.js b/src/routes/api/note_revisions.js
index 7448eb7da..f28239f2e 100644
--- a/src/routes/api/note_revisions.js
+++ b/src/routes/api/note_revisions.js
@@ -135,7 +135,7 @@ function getEditedNotesOnDate(req) {
notes = notes.map(note => note.getPojo());
for (const note of notes) {
- const notePath = note.isDeleted ? null : beccaService.getNotePath(note.noteId);
+ const notePath = note.isDeleted ? null : getNotePathData(note);
note.notePath = notePath ? notePath.notePath : null;
}
@@ -143,6 +143,32 @@ function getEditedNotesOnDate(req) {
return notes;
}
+function getNotePathData(note) {
+ const retPath = note.getBestNotePath();
+
+ if (retPath) {
+ const noteTitle = beccaService.getNoteTitleForPath(retPath);
+
+ let branchId;
+
+ if (note.isRoot()) {
+ branchId = 'none_root';
+ }
+ else {
+ const parentNote = note.parents[0];
+ branchId = becca.getBranchFromChildAndParent(note.noteId, parentNote.noteId).branchId;
+ }
+
+ return {
+ noteId: note.noteId,
+ branchId: branchId,
+ title: noteTitle,
+ notePath: retPath,
+ path: retPath.join('/')
+ };
+ }
+}
+
module.exports = {
getNoteRevisions,
getNoteRevision,
diff --git a/src/routes/api/recent_changes.js b/src/routes/api/recent_changes.js
index 2d54af93b..646898e9c 100644
--- a/src/routes/api/recent_changes.js
+++ b/src/routes/api/recent_changes.js
@@ -3,14 +3,14 @@
const sql = require('../../services/sql');
const protectedSessionService = require('../../services/protected_session');
const noteService = require('../../services/notes');
-const beccaService = require('../../becca/becca_service');
+const becca = require("../../becca/becca");
function getRecentChanges(req) {
const {ancestorNoteId} = req.params;
let recentChanges = [];
- const noteRevisions = sql.getRows(`
+ const noteRevisionRows = sql.getRows(`
SELECT
notes.noteId,
notes.isDeleted AS current_isDeleted,
@@ -24,16 +24,18 @@ function getRecentChanges(req) {
note_revisions
JOIN notes USING(noteId)`);
- for (const noteRevision of noteRevisions) {
- if (beccaService.isInAncestor(noteRevision.noteId, ancestorNoteId)) {
- recentChanges.push(noteRevision);
+ for (const noteRevisionRow of noteRevisionRows) {
+ const note = becca.getNote(noteRevisionRow.noteId);
+
+ if (note?.hasAncestor(ancestorNoteId)) {
+ recentChanges.push(noteRevisionRow);
}
}
// now we need to also collect date points not represented in note revisions:
// 1. creation for all notes (dateCreated)
// 2. deletion for deleted notes (dateModified)
- const notes = sql.getRows(`
+ const noteRows = sql.getRows(`
SELECT
notes.noteId,
notes.isDeleted AS current_isDeleted,
@@ -57,9 +59,11 @@ function getRecentChanges(req) {
FROM notes
WHERE notes.isDeleted = 1`);
- for (const note of notes) {
- if (beccaService.isInAncestor(note.noteId, ancestorNoteId)) {
- recentChanges.push(note);
+ for (const noteRow of noteRows) {
+ const note = becca.getNote(noteRow.noteId);
+
+ if (note?.hasAncestor(ancestorNoteId)) {
+ recentChanges.push(noteRow);
}
}
diff --git a/src/routes/routes.js b/src/routes/routes.js
index 9cc87bb37..a52a067f5 100644
--- a/src/routes/routes.js
+++ b/src/routes/routes.js
@@ -143,7 +143,7 @@ function register(app) {
apiRoute(PUT, '/api/notes/:noteId/clone-to-branch/:parentBranchId', cloningApiRoute.cloneNoteToBranch);
apiRoute(PUT, '/api/notes/:noteId/toggle-in-parent/:parentNoteId/:present', cloningApiRoute.toggleNoteInParent);
- apiRoute(PUT, '/api/notes/:noteId/clone-to-note/:parentNoteId', cloningApiRoute.cloneNoteToNote);
+ apiRoute(PUT, '/api/notes/:noteId/clone-to-note/:parentNoteId', cloningApiRoute.cloneNoteToParentNote);
apiRoute(PUT, '/api/notes/:noteId/clone-after/:afterBranchId', cloningApiRoute.cloneNoteAfter);
route(GET, '/api/notes/:branchId/export/:type/:format/:version/:taskId', [auth.checkApiAuthOrElectron], exportRoute.exportBranch);
diff --git a/src/services/bulk_actions.js b/src/services/bulk_actions.js
index eaa57a1fb..c51ba1d5e 100644
--- a/src/services/bulk_actions.js
+++ b/src/services/bulk_actions.js
@@ -83,7 +83,7 @@ const ACTION_HANDLERS = {
let res;
if (note.getParentBranches().length > 1) {
- res = cloningService.cloneNoteToNote(note.noteId, action.targetParentNoteId);
+ res = cloningService.cloneNoteToParentNote(note.noteId, action.targetParentNoteId);
}
else {
res = branchService.moveBranchToNote(note.getParentBranches()[0], action.targetParentNoteId);
diff --git a/src/services/cloning.js b/src/services/cloning.js
index dad3d3906..f62b8724b 100644
--- a/src/services/cloning.js
+++ b/src/services/cloning.js
@@ -8,7 +8,7 @@ const becca = require("../becca/becca");
const beccaService = require("../becca/becca_service");
const log = require("./log");
-function cloneNoteToNote(noteId, parentNoteId, prefix) {
+function cloneNoteToParentNote(noteId, parentNoteId, prefix) {
const parentNote = becca.getNote(parentNoteId);
if (parentNote.type === 'search') {
@@ -19,7 +19,7 @@ function cloneNoteToNote(noteId, parentNoteId, prefix) {
}
if (isNoteDeleted(noteId) || isNoteDeleted(parentNoteId)) {
- return { success: false, message: 'Note is deleted.' };
+ return { success: false, message: 'Note cannot be cloned because either the cloned note or the intended parent is deleted.' };
}
const validationResult = treeService.validateParentChild(parentNoteId, noteId);
@@ -35,12 +35,12 @@ function cloneNoteToNote(noteId, parentNoteId, prefix) {
isExpanded: 0
}).save();
- log.info(`Cloned note '${noteId}' to new parent note '${parentNoteId}' with prefix '${prefix}'`);
+ log.info(`Cloned note '${noteId}' to a new parent note '${parentNoteId}' with prefix '${prefix}'`);
return {
success: true,
branchId: branch.branchId,
- notePath: `${beccaService.getNotePath(parentNoteId).path}/${noteId}`
+ notePath: `${parentNote.getBestNotePathString()}/${noteId}`
};
}
@@ -51,7 +51,7 @@ function cloneNoteToBranch(noteId, parentBranchId, prefix) {
return { success: false, message: `Parent branch ${parentBranchId} does not exist.` };
}
- const ret = cloneNoteToNote(noteId, parentBranch.noteId, prefix);
+ const ret = cloneNoteToParentNote(noteId, parentBranch.noteId, prefix);
parentBranch.isExpanded = true; // the new target should be expanded, so it immediately shows up to the user
parentBranch.save();
@@ -182,7 +182,7 @@ function isNoteDeleted(noteId) {
module.exports = {
cloneNoteToBranch,
- cloneNoteToNote,
+ cloneNoteToParentNote,
ensureNoteIsPresentInParent,
ensureNoteIsAbsentFromParent,
toggleNoteInParent,
diff --git a/src/services/search/expressions/note_flat_text.js b/src/services/search/expressions/note_flat_text.js
index 863573a13..62985f590 100644
--- a/src/services/search/expressions/note_flat_text.js
+++ b/src/services/search/expressions/note_flat_text.js
@@ -22,9 +22,9 @@ class NoteFlatTextExp extends Expression {
* @param {string[]} tokens
* @param {string[]} path
*/
- function searchDownThePath(note, tokens, path) {
+ const searchDownThePath = (note, tokens, path) => {
if (tokens.length === 0) {
- const retPath = beccaService.getSomePath(note, path);
+ const retPath = this.getNotePath(note, path);
if (retPath) {
const noteId = retPath[retPath.length - 1];
@@ -131,6 +131,17 @@ class NoteFlatTextExp extends Expression {
return resultNoteSet;
}
+ getNotePath(note, path) {
+ if (path.length === 0) {
+ return note.getBestNotePath();
+ } else {
+ const closestNoteId = path[0];
+ const closestNoteBestNotePath = becca.getNote(closestNoteId).getBestNotePath();
+
+ return [...closestNoteBestNotePath, ...path.slice(1)];
+ }
+ }
+
/**
* Returns noteIds which have at least one matching tokens
*
diff --git a/src/services/search/services/search.js b/src/services/search/services/search.js
index f1adc72d7..3e36040ab 100644
--- a/src/services/search/services/search.js
+++ b/src/services/search/services/search.js
@@ -157,7 +157,7 @@ function findResultsWithExpression(expression, searchContext) {
const searchResults = noteSet.notes
.filter(note => !note.isDeleted)
.map(note => {
- const notePathArray = executionContext.noteIdToNotePath[note.noteId] || beccaService.getSomePath(note);
+ const notePathArray = executionContext.noteIdToNotePath[note.noteId] || note.getBestNotePath();
if (!notePathArray) {
throw new Error(`Can't find note path for note ${JSON.stringify(note.getPojo())}`);
diff --git a/src/services/ws.js b/src/services/ws.js
index ac81c367d..f019691c3 100644
--- a/src/services/ws.js
+++ b/src/services/ws.js
@@ -9,6 +9,18 @@ const protectedSessionService = require('./protected_session');
const becca = require("../becca/becca");
const AbstractBeccaEntity = require("../becca/entities/abstract_becca_entity");
+const env = require('./env');
+if (env.isDev()) {
+ const chokidar = require('chokidar');
+ const debounce = require('debounce');
+ const debouncedReloadFrontend = debounce(reloadFrontend, 200);
+ chokidar
+ .watch('src/public')
+ .on('add', debouncedReloadFrontend)
+ .on('change', debouncedReloadFrontend)
+ .on('unlink', debouncedReloadFrontend);
+}
+
let webSocketServer;
let lastSyncedPush = null;
diff --git a/webpack-desktop.config.js b/webpack-desktop.config.js
deleted file mode 100644
index 8fbf0e152..000000000
--- a/webpack-desktop.config.js
+++ /dev/null
@@ -1,16 +0,0 @@
-const path = require('path');
-const assetPath = require('./src/services/asset_path');
-
-module.exports = {
- mode: 'production',
- entry: {
- mobile: './src/public/app/desktop.js',
- },
- output: {
- publicPath: `${assetPath}/app-dist/`,
- path: path.resolve(__dirname, 'src/public/app-dist'),
- filename: 'desktop.js'
- },
- devtool: 'source-map',
- target: 'electron-renderer'
-};
diff --git a/webpack-setup.config.js b/webpack-setup.config.js
deleted file mode 100644
index dee04f090..000000000
--- a/webpack-setup.config.js
+++ /dev/null
@@ -1,16 +0,0 @@
-const path = require('path');
-const assetPath = require('./src/services/asset_path');
-
-module.exports = {
- mode: 'production',
- entry: {
- mobile: './src/public/app/setup.js',
- },
- output: {
- publicPath: `${assetPath}/app-dist/`,
- path: path.resolve(__dirname, 'src/public/app-dist'),
- filename: 'setup.js'
- },
- devtool: 'source-map',
- target: 'electron-renderer'
-};
diff --git a/webpack-mobile.config.js b/webpack.config.js
similarity index 69%
rename from webpack-mobile.config.js
rename to webpack.config.js
index 4fc72b8be..41077c00e 100644
--- a/webpack-mobile.config.js
+++ b/webpack.config.js
@@ -4,13 +4,15 @@ const assetPath = require('./src/services/asset_path');
module.exports = {
mode: 'production',
entry: {
+ setup: './src/public/app/setup.js',
mobile: './src/public/app/mobile.js',
+ desktop: './src/public/app/desktop.js',
},
output: {
publicPath: `${assetPath}/app-dist/`,
path: path.resolve(__dirname, 'src/public/app-dist'),
- filename: 'mobile.js'
+ filename: '[name].js',
},
devtool: 'source-map',
- target: 'electron-renderer'
+ target: 'electron-renderer',
};