From 66204811cf53021c791f17043e483bb70396e7c5 Mon Sep 17 00:00:00 2001 From: zadam Date: Mon, 3 Feb 2020 20:07:34 +0100 Subject: [PATCH] refactored note creation methods into a separate service --- src/public/javascripts/entities/note_short.js | 5 + src/public/javascripts/mobile.js | 9 +- .../javascripts/services/app_context.js | 22 ++++ .../javascripts/services/note_create.js | 89 +++++++++++++++ src/public/javascripts/services/tree.js | 101 ------------------ .../javascripts/services/tree_context_menu.js | 7 +- src/public/javascripts/widgets/note_tree.js | 9 +- src/public/javascripts/widgets/search_box.js | 3 +- src/services/note_cache.js | 3 +- 9 files changed, 138 insertions(+), 110 deletions(-) create mode 100644 src/public/javascripts/services/note_create.js diff --git a/src/public/javascripts/entities/note_short.js b/src/public/javascripts/entities/note_short.js index da3ae2f48..304926a99 100644 --- a/src/public/javascripts/entities/note_short.js +++ b/src/public/javascripts/entities/note_short.js @@ -103,6 +103,11 @@ class NoteShort { } } + /** @returns {string[]} */ + getBranchIds() { + return Object.values(this.parentToBranch); + } + /** @returns {Promise} */ async getBranches() { const branchIds = Object.values(this.parentToBranch); diff --git a/src/public/javascripts/mobile.js b/src/public/javascripts/mobile.js index 636d503a4..273713ab1 100644 --- a/src/public/javascripts/mobile.js +++ b/src/public/javascripts/mobile.js @@ -5,6 +5,7 @@ import contextMenuWidget from "./services/context_menu.js"; import treeChangesService from "./services/branches.js"; import utils from "./services/utils.js"; import appContext from "./services/app_context.js"; +import noteCreateService from "./services/note_create.js"; window.glob.isDesktop = utils.isDesktop; window.glob.isMobile = utils.isMobile; @@ -111,10 +112,14 @@ $detail.on("click", ".note-menu-button", async e => { const parentNoteId = node.data.parentNoteId; const isProtected = await treeService.getParentProtectedStatus(node); - treeService.createNote(node, parentNoteId, 'after', { isProtected: isProtected }); + noteCreateService.createNote(parentNoteId, { + isProtected: isProtected, + target: 'after', + targetBranchId: node.data.branchId + }); } else if (cmd === "insertChildNote") { - treeService.createNote(node, node.data.noteId, 'into'); + noteCreateService.createNote(node.data.noteId); } else if (cmd === "delete") { if (await treeChangesService.deleteNodes([node])) { diff --git a/src/public/javascripts/services/app_context.js b/src/public/javascripts/services/app_context.js index c2015123f..ebec1a93d 100644 --- a/src/public/javascripts/services/app_context.js +++ b/src/public/javascripts/services/app_context.js @@ -495,4 +495,26 @@ $(window).on('beforeunload', () => { appContext.trigger('beforeUnload'); }); +function isNotePathInAddress() { + const [notePath, tabId] = getHashValueFromAddress(); + + return notePath.startsWith("root") + // empty string is for empty/uninitialized tab + || (notePath === '' && !!tabId); +} + +function getHashValueFromAddress() { + const str = document.location.hash ? document.location.hash.substr(1) : ""; // strip initial # + + return str.split("-"); +} + +$(window).on('hashchange', function() { + if (isNotePathInAddress()) { + const [notePath, tabId] = getHashValueFromAddress(); + + appContext.switchToTab(tabId, notePath); + } +}); + export default appContext; \ No newline at end of file diff --git a/src/public/javascripts/services/note_create.js b/src/public/javascripts/services/note_create.js new file mode 100644 index 000000000..7715e0cc3 --- /dev/null +++ b/src/public/javascripts/services/note_create.js @@ -0,0 +1,89 @@ +import hoistedNoteService from "./hoisted_note.js"; +import appContext from "./app_context.js"; +import utils from "./utils.js"; +import protectedSessionHolder from "./protected_session_holder.js"; +import server from "./server.js"; +import ws from "./ws.js"; +import treeCache from "./tree_cache.js"; +import toastService from "./toast.js"; + +async function createNewTopLevelNote() { + const hoistedNoteId = await hoistedNoteService.getHoistedNoteId(); + + await createNote(hoistedNoteId); +} + +async function createNote(parentNoteId, options = {}) { + options = Object.assign({ + activate: true, + target: 'into' + }, options); + + // if isProtected isn't available (user didn't enter password yet), then note is created as unencrypted + // but this is quite weird since user doesn't see WHERE the note is being created so it shouldn't occur often + if (!options.isProtected || !protectedSessionHolder.isProtectedSessionAvailable()) { + options.isProtected = false; + } + + if (appContext.getActiveTabNoteType() !== 'text') { + options.saveSelection = false; + } + + if (options.saveSelection && utils.isCKEditorInitialized()) { + [options.title, options.content] = parseSelectedHtml(window.cutToNote.getSelectedHtml()); + } + + const newNoteName = options.title || "new note"; + + const {note, branch} = await server.post(`notes/${parentNoteId}/children?target=${options.target}&targetBranchId=${options.targetBranchId}`, { + title: newNoteName, + content: options.content || "", + isProtected: options.isProtected, + type: options.type + }); + + if (options.saveSelection && utils.isCKEditorInitialized()) { + // we remove the selection only after it was saved to server to make sure we don't lose anything + window.cutToNote.removeSelection(); + } + + if (options.activate) { + const activeTabContext = appContext.getActiveTabContext(); + activeTabContext.setNote(note.noteId); + } + + return {note, branch}; +} + +/* If first element is heading, parse it out and use it as a new heading. */ +function parseSelectedHtml(selectedHtml) { + const dom = $.parseHTML(selectedHtml); + + if (dom.length > 0 && dom[0].tagName && dom[0].tagName.match(/h[1-6]/i)) { + const title = $(dom[0]).text(); + // remove the title from content (only first occurence) + const content = selectedHtml.replace(dom[0].outerHTML, ""); + + return [title, content]; + } + else { + return [null, selectedHtml]; + } +} + +async function duplicateNote(noteId, parentNoteId) { + const {note} = await server.post(`notes/${noteId}/duplicate/${parentNoteId}`); + + await ws.waitForMaxKnownSyncId(); + + await appContext.activateOrOpenNote(note.noteId); + + const origNote = await treeCache.getNote(noteId); + toastService.showMessage(`Note "${origNote.title}" has been duplicated`); +} + +export default { + createNote, + createNewTopLevelNote, + duplicateNote +}; \ No newline at end of file diff --git a/src/public/javascripts/services/tree.js b/src/public/javascripts/services/tree.js index 3f2b699a2..b5c9a4166 100644 --- a/src/public/javascripts/services/tree.js +++ b/src/public/javascripts/services/tree.js @@ -133,85 +133,6 @@ async function getSomeNotePath(note) { return path.reverse().join('/'); } -function isNotePathInAddress() { - const [notePath, tabId] = getHashValueFromAddress(); - - return notePath.startsWith("root") - // empty string is for empty/uninitialized tab - || (notePath === '' && !!tabId); -} - -function getHashValueFromAddress() { - const str = document.location.hash ? document.location.hash.substr(1) : ""; // strip initial # - - return str.split("-"); -} - -async function createNewTopLevelNote() { - const hoistedNoteId = await hoistedNoteService.getHoistedNoteId(); - - const rootNode = appContext.getMainNoteTree().getNodesByNoteId(hoistedNoteId)[0]; - - await createNote(rootNode, hoistedNoteId, "into"); -} - -async function createNote(node, parentNoteId, target, extraOptions = {}) { - utils.assertArguments(node, parentNoteId, target); - - extraOptions.activate = extraOptions.activate === undefined ? true : !!extraOptions.activate; - - // if isProtected isn't available (user didn't enter password yet), then note is created as unencrypted - // but this is quite weird since user doesn't see WHERE the note is being created so it shouldn't occur often - if (!extraOptions.isProtected || !protectedSessionHolder.isProtectedSessionAvailable()) { - extraOptions.isProtected = false; - } - - if (appContext.getActiveTabNoteType() !== 'text') { - extraOptions.saveSelection = false; - } - - if (extraOptions.saveSelection && utils.isCKEditorInitialized()) { - [extraOptions.title, extraOptions.content] = parseSelectedHtml(window.cutToNote.getSelectedHtml()); - } - - const newNoteName = extraOptions.title || "new note"; - - const {note, branch} = await server.post(`notes/${parentNoteId}/children?target=${target}&targetBranchId=${node.data.branchId}`, { - title: newNoteName, - content: extraOptions.content || "", - isProtected: extraOptions.isProtected, - type: extraOptions.type - }); - - if (extraOptions.saveSelection && utils.isCKEditorInitialized()) { - // we remove the selection only after it was saved to server to make sure we don't lose anything - window.cutToNote.removeSelection(); - } - - if (extraOptions.activate) { - const activeTabContext = appContext.getActiveTabContext(); - activeTabContext.setNote(note.noteId); - } - - return {note, branch}; -} - -/* If first element is heading, parse it out and use it as a new heading. */ -function parseSelectedHtml(selectedHtml) { - const dom = $.parseHTML(selectedHtml); - - if (dom.length > 0 && dom[0].tagName && dom[0].tagName.match(/h[1-6]/i)) { - const title = $(dom[0]).text(); - // remove the title from content (only first occurence) - const content = selectedHtml.replace(dom[0].outerHTML, ""); - - return [title, content]; - } - else { - return [null, selectedHtml]; - } -} - async function sortAlphabetically(noteId) { await server.put('notes/' + noteId + '/sort'); } @@ -228,25 +149,6 @@ ws.subscribeToMessages(message => { } }); -$(window).on('hashchange', function() { - if (isNotePathInAddress()) { - const [notePath, tabId] = getHashValueFromAddress(); - - appContext.switchToTab(tabId, notePath); - } -}); - -async function duplicateNote(noteId, parentNoteId) { - const {note} = await server.post(`notes/${noteId}/duplicate/${parentNoteId}`); - - await ws.waitForMaxKnownSyncId(); - - await appContext.activateOrOpenNote(note.noteId); - - const origNote = await treeCache.getNote(noteId); - toastService.showMessage(`Note "${origNote.title}" has been duplicated`); -} - async function getParentProtectedStatus(node) { return await hoistedNoteService.isRootNode(node) ? 0 : node.getParent().data.isProtected; } @@ -361,12 +263,9 @@ async function getNotePathTitle(notePath) { } export default { - createNote, sortAlphabetically, resolveNotePath, getSomeNotePath, - createNewTopLevelNote, - duplicateNote, getRunPath, getParentProtectedStatus, getNotePath, diff --git a/src/public/javascripts/services/tree_context_menu.js b/src/public/javascripts/services/tree_context_menu.js index 4cb66dd72..f184051c4 100644 --- a/src/public/javascripts/services/tree_context_menu.js +++ b/src/public/javascripts/services/tree_context_menu.js @@ -8,6 +8,7 @@ import hoistedNoteService from './hoisted_note.js'; import clipboard from './clipboard.js'; import protectedSessionHolder from "./protected_session_holder.js"; import appContext from "./app_context.js"; +import noteCreateService from "./note_create.js"; class TreeContextMenu { /** @@ -110,7 +111,9 @@ class TreeContextMenu { const isProtected = await treeService.getParentProtectedStatus(this.node); const type = cmd.split("_")[1]; - treeService.createNote(this.node, parentNoteId, 'after', { + noteCreateService.createNote(parentNoteId, { + target: 'after', + targetBranchId: this.node.data.branchId, type: type, isProtected: isProtected }); @@ -118,7 +121,7 @@ class TreeContextMenu { else if (cmd.startsWith("insertChildNote")) { const type = cmd.split("_")[1]; - treeService.createNote(this.node, this.node.data.noteId, 'into', { + noteCreateService.createNote(noteId, { type: type, isProtected: this.node.data.isProtected }); diff --git a/src/public/javascripts/widgets/note_tree.js b/src/public/javascripts/widgets/note_tree.js index ff8e94a75..275b7952f 100644 --- a/src/public/javascripts/widgets/note_tree.js +++ b/src/public/javascripts/widgets/note_tree.js @@ -12,6 +12,7 @@ import ws from "../services/ws.js"; import appContext from "../services/app_context.js"; import TabAwareWidget from "./tab_aware_widget.js"; import server from "../services/server.js"; +import noteCreateService from "../services/note_create.js"; const TPL = `
@@ -545,7 +546,9 @@ export default class NoteTreeWidget extends TabAwareWidget { return; } - await treeService.createNote(node, parentNoteId, 'after', { + await noteCreateService.createNote(parentNoteId, { + target: 'after', + targetBranchId: node.data.branchId, isProtected: isProtected, saveSelection: true }); @@ -555,7 +558,7 @@ export default class NoteTreeWidget extends TabAwareWidget { const node = this.getActiveNode(); if (node) { - await treeService.createNote(node, node.data.noteId, 'into', { + await noteCreateService.createNote(node.data.noteId, { isProtected: node.data.isProtected, saveSelection: false }); @@ -566,7 +569,7 @@ export default class NoteTreeWidget extends TabAwareWidget { const node = this.getActiveNode(); if (node) { - await treeService.createNote(node, node.data.noteId, 'into', { + await noteCreateService.createNote(node.data.noteId, { isProtected: node.data.isProtected, saveSelection: true }); diff --git a/src/public/javascripts/widgets/search_box.js b/src/public/javascripts/widgets/search_box.js index ed72890bd..55c9ba8a7 100644 --- a/src/public/javascripts/widgets/search_box.js +++ b/src/public/javascripts/widgets/search_box.js @@ -3,6 +3,7 @@ import treeService from "../services/tree.js"; import treeCache from "../services/tree_cache.js"; import toastService from "../services/toast.js"; import appContext from "../services/app_context.js"; +import noteCreateService from "../services/note_create.js"; const helpText = ` Search tips - also see @@ -128,7 +129,7 @@ export default class SearchBoxWidget extends BasicWidget { activeNode = activeNode.getParent(); } - await treeService.createNote(activeNode, activeNode.data.noteId, 'into', { + await noteCreateService.createNote(activeNode.data.noteId, { type: "search", mime: "application/json", title: searchString, diff --git a/src/services/note_cache.js b/src/services/note_cache.js index c19910be8..993b4dd49 100644 --- a/src/services/note_cache.js +++ b/src/services/note_cache.js @@ -485,11 +485,12 @@ eventService.subscribe([eventService.ENTITY_CHANGED, eventService.ENTITY_DELETED delete childParentToBranchId[branch.noteId + '-' + branch.parentNoteId]; } else { - // ... and then we create new records if (branch.prefix) { prefixes[branch.noteId + '-' + branch.parentNoteId] = branch.prefix; } + childToParent[branch.noteId] = childToParent[branch.noteId] || []; + if (!childToParent[branch.noteId].includes(branch.parentNoteId)) { childToParent[branch.noteId].push(branch.parentNoteId); }