mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	refactoring of note detail API
This commit is contained in:
		| @@ -174,8 +174,7 @@ function AttributesModel() { | ||||
|  | ||||
|         appContext.getActiveTabContext().attributes.refreshAttributes(); | ||||
|  | ||||
|         // reload | ||||
|         noteDetailService.reload(); | ||||
|         // FIXME detail should be also reloaded | ||||
|         appContext.trigger('reloadTree'); | ||||
|     }; | ||||
|  | ||||
|   | ||||
| @@ -62,7 +62,6 @@ async function showTree() { | ||||
|  | ||||
|             const notePath = await treeUtils.getNotePath(node); | ||||
|  | ||||
|             noteDetailService.switchToNote(notePath); | ||||
|         }, | ||||
|         expand: (event, data) => treeService.setExpandedToServer(data.node.data.branchId, true), | ||||
|         collapse: (event, data) => treeService.setExpandedToServer(data.node.data.branchId, false), | ||||
|   | ||||
| @@ -216,21 +216,12 @@ class AppContext { | ||||
|     } | ||||
|  | ||||
|     async switchToTab(tabId, notePath) { | ||||
|         const tabContext = this.tabContexts.find(tc => tc.tabId === tabId); | ||||
|         const tabContext = this.tabContexts.find(tc => tc.tabId === tabId) | ||||
|                          || this.openEmptyTab(); | ||||
|  | ||||
|         if (!tabContext) { | ||||
|             await noteDetailService.loadNoteDetail(notePath, { | ||||
|                 newTab: true, | ||||
|                 activate: true | ||||
|             }); | ||||
|         } else { | ||||
|             await this.activateTab(tabContext.tabId); | ||||
|  | ||||
|             if (notePath && tabContext.notePath !== notePath) { | ||||
|         this.activateTab(tabContext.tabId); | ||||
|         await tabContext.setNote(notePath); | ||||
|     } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * @return {NoteTreeWidget} | ||||
| @@ -252,18 +243,6 @@ class AppContext { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async reloadAllTabs() { | ||||
|         for (const tabContext of this.tabContexts) { | ||||
|             await this.reloadTab(tabContext); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async reloadTab(tc) { | ||||
|         if (tc.note) { | ||||
|             noteDetailService.reloadNote(tc); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     async openAndActivateEmptyTab() { | ||||
|         const tabContext = this.openEmptyTab(); | ||||
|  | ||||
| @@ -278,6 +257,20 @@ class AppContext { | ||||
|         return tabContext; | ||||
|     } | ||||
|  | ||||
|     async activateOrOpenNote(noteId) { | ||||
|         for (const tabContext of this.getTabContexts()) { | ||||
|             if (tabContext.note && tabContext.note.noteId === noteId) { | ||||
|                 await tabContext.activate(); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // if no tab with this note has been found we'll create new tab | ||||
|  | ||||
|         const tabContext = this.openEmptyTab(); | ||||
|         await tabContext.setNote(noteId); | ||||
|     } | ||||
|  | ||||
|     async filterTabs(noteId) { | ||||
|         for (const tc of this.tabContexts) { | ||||
|             if (tc.notePath && !tc.notePath.split("/").includes(noteId)) { | ||||
| @@ -318,7 +311,7 @@ class AppContext { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     openTabsChanged() { | ||||
|     openTabsChangedListener() { | ||||
|         // we don't want to send too many requests with tab changes so we always schedule task to do this in 1 seconds, | ||||
|         // but if there's any change in between, we cancel the old one and schedule new one | ||||
|         // so effectively we kind of wait until user stopped e.g. quickly switching tabs | ||||
| @@ -327,7 +320,7 @@ class AppContext { | ||||
|         this.tabsChangedTaskId = setTimeout(() => this.saveOpenTabs(), 1000); | ||||
|     } | ||||
|  | ||||
|     async activateTab(tabId) { | ||||
|     activateTab(tabId) { | ||||
|         this.activeTabId = tabId; | ||||
|  | ||||
|         this.trigger('activeTabChanged', { tabId: this.activeTabId }); | ||||
| @@ -364,11 +357,11 @@ class AppContext { | ||||
|  | ||||
|         this.tabContexts = this.tabContexts.filter(tc => tc.tabId === tabId); | ||||
|  | ||||
|         this.openTabsChanged(); | ||||
|         this.openTabsChangedListener(); | ||||
|     } | ||||
|  | ||||
|     tabReorderListener() { | ||||
|         this.openTabsChanged(); | ||||
|         this.openTabsChangedListener(); | ||||
|     } | ||||
|  | ||||
|     noteChangesSavedListener() { | ||||
|   | ||||
| @@ -78,7 +78,9 @@ export default class Entrypoints extends Component { | ||||
|  | ||||
|         await treeService.expandToNote(note.noteId); | ||||
|  | ||||
|         await noteDetailService.openInTab(note.noteId, true); | ||||
|         const tabContext = appContext.openEmptyTab(); | ||||
|         appContext.activateTab(tabContext.tabId); | ||||
|         await tabContext.setNote(note.noteId); | ||||
|  | ||||
|         noteDetailService.focusAndSelectTitle(); | ||||
|     } | ||||
|   | ||||
| @@ -272,22 +272,6 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte | ||||
|      */ | ||||
|     this.refreshTree = treeService.reload; | ||||
|  | ||||
|     /** | ||||
|      * Refresh active tab | ||||
|      * | ||||
|      * @method | ||||
|      * @returns {Promise<void>} | ||||
|      */ | ||||
|     this.refreshActiveTab = noteDetailService.reload; | ||||
|  | ||||
|     /** | ||||
|      * Refresh current tab | ||||
|      * | ||||
|      * @method | ||||
|      * @returns {Promise<void>} | ||||
|      */ | ||||
|     this.refreshAllTabs = appContext.reloadAllTabs; | ||||
|  | ||||
|     /** | ||||
|      * Create note link (jQuery object) for given note. | ||||
|      * | ||||
|   | ||||
| @@ -79,10 +79,13 @@ function goToLink(e) { | ||||
|  | ||||
|     if (notePath) { | ||||
|         if ((e.which === 1 && e.ctrlKey) || e.which === 2) { | ||||
|             noteDetailService.openInTab(notePath, false); | ||||
|             const tabContext = appContext.openEmptyTab(); | ||||
|             appContext.activateTab(tabContext.tabId); | ||||
|             tabContext.setNote(notePath); | ||||
|         } | ||||
|         else if (e.which === 1) { | ||||
|             treeService.activateNote(notePath); | ||||
|             const activeTabContext = appContext.getActiveTabContext(); | ||||
|             activeTabContext.setNote(notePath) | ||||
|         } | ||||
|         else { | ||||
|             return false; | ||||
| @@ -118,7 +121,9 @@ function newTabContextMenu(e) { | ||||
|         }, | ||||
|         selectContextMenuItem: (e, cmd) => { | ||||
|             if (cmd === 'openNoteInNewTab') { | ||||
|                 noteDetailService.loadNoteDetail(notePath.split("/").pop(), { newTab: true }); | ||||
|                 const tabContext = appContext.openEmptyTab(); | ||||
|                 tabContext.setNote(notePath); | ||||
|                 appContext.activateTab(tabContext.tabId); | ||||
|             } | ||||
|         } | ||||
|     }); | ||||
| @@ -138,7 +143,9 @@ $(document).on('mousedown', '.note-detail-text a', function (e) { | ||||
|         e.preventDefault(); | ||||
|  | ||||
|         if (notePath) { | ||||
|             noteDetailService.loadNoteDetail(notePath, {newTab: true}); | ||||
|             const tabContext = appContext.openEmptyTab(); | ||||
|             tabContext.setNote(notePath); | ||||
|             appContext.activateTab(tabContext.tabId); | ||||
|         } | ||||
|         else { | ||||
|             const address = $link.attr('href'); | ||||
|   | ||||
| @@ -1,35 +1,9 @@ | ||||
| import treeService from './tree.js'; | ||||
| import TabContext from './tab_context.js'; | ||||
| import server from './server.js'; | ||||
| import ws from "./ws.js"; | ||||
| import treeCache from "./tree_cache.js"; | ||||
| import NoteFull from "../entities/note_full.js"; | ||||
| import treeUtils from "./tree_utils.js"; | ||||
| import tabRow from "../widgets/tab_row.js"; | ||||
| import appContext from "./app_context.js"; | ||||
|  | ||||
| let detailLoadedListeners = []; | ||||
|  | ||||
| async function reload() { | ||||
|     // no saving here | ||||
|  | ||||
|     await loadNoteDetail(appContext.getActiveTabNotePath()); | ||||
| } | ||||
|  | ||||
| async function reloadNote(tabContext) { | ||||
|     await loadNoteDetailToContext(tabContext, tabContext.notePath); | ||||
| } | ||||
|  | ||||
| async function openInTab(notePath, activate) { | ||||
|     await loadNoteDetail(notePath, { newTab: true, activate }); | ||||
| } | ||||
|  | ||||
| async function switchToNote(notePath) { | ||||
|     await loadNoteDetail(notePath); | ||||
|  | ||||
|     appContext.openTabsChanged(); | ||||
| } | ||||
|  | ||||
| function getActiveEditor() { | ||||
|     const activeTabContext = appContext.getActiveTabContext(); | ||||
|  | ||||
| @@ -41,73 +15,6 @@ function getActiveEditor() { | ||||
|     } | ||||
| } | ||||
|  | ||||
| async function activateOrOpenNote(noteId) { | ||||
|     for (const tabContext of appContext.getTabContexts()) { | ||||
|         if (tabContext.note && tabContext.note.noteId === noteId) { | ||||
|             await tabContext.activate(); | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // if no tab with this note has been found we'll create new tab | ||||
|  | ||||
|     await loadNoteDetail(noteId, { | ||||
|         newTab: true, | ||||
|         activate: true | ||||
|     }); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * @param {TabContext} ctx | ||||
|  * @param {string} notePath | ||||
|  */ | ||||
| async function loadNoteDetailToContext(ctx, notePath) { | ||||
|     await ctx.setNote(notePath); | ||||
|  | ||||
|     appContext.openTabsChanged(); | ||||
|  | ||||
|     fireDetailLoaded(); | ||||
| } | ||||
|  | ||||
| async function loadNoteDetail(origNotePath, options = {}) { | ||||
|     const newTab = !!options.newTab; | ||||
|     const activate = !!options.activate; | ||||
|  | ||||
|     let notePath = await treeService.resolveNotePath(origNotePath); | ||||
|  | ||||
|     if (!notePath) { | ||||
|         console.error(`Cannot resolve note path ${origNotePath}`); | ||||
|  | ||||
|         // fallback to display something | ||||
|         notePath = 'root'; | ||||
|     } | ||||
|  | ||||
|     const noteId = treeUtils.getNoteIdFromNotePath(notePath); | ||||
|     const ctx = appContext.getTab(newTab, options.state); | ||||
|  | ||||
|     // we will try to render the new note only if it's still the active one in the tree | ||||
|     // this is useful when user quickly switches notes (by e.g. holding down arrow) so that we don't | ||||
|     // try to render all those loaded notes one after each other. This only guarantees that correct note | ||||
|     // will be displayed independent of timing | ||||
|     const currentTreeNode = appContext.getMainNoteTree().getActiveNode(); | ||||
|     if (!newTab && currentTreeNode && currentTreeNode.data.noteId !== noteId) { | ||||
|         return; | ||||
|     } | ||||
|      | ||||
|     const loadPromise = loadNoteDetailToContext(ctx, notePath).then(() => { | ||||
|         if (activate) { | ||||
|             return appContext.activateTab(ctx.tabId); | ||||
|         } | ||||
|         else { | ||||
|             return Promise.resolve(); | ||||
|         } | ||||
|     }); | ||||
|  | ||||
|     if (!options.async) { | ||||
|         await loadPromise; | ||||
|     } | ||||
| } | ||||
|  | ||||
| async function loadNote(noteId) { | ||||
|     const row = await server.get('notes/' + noteId); | ||||
|  | ||||
| @@ -116,49 +23,12 @@ async function loadNote(noteId) { | ||||
|     return new NoteFull(treeCache, row, noteShort); | ||||
| } | ||||
|  | ||||
| async function noteDeleted(noteId) { | ||||
|     for (const tc of appContext.getTabContexts()) { | ||||
|         // not removing active even if it contains deleted note since that one will move to another note (handled by deletion logic) | ||||
|         // and we would lose tab context state (e.g. sidebar visibility) | ||||
|         if (!tc.isActive() && tc.notePath && tc.notePath.split("/").includes(noteId)) { | ||||
|             tabRow.removeTab(tc.tabId); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| function focusOnTitle() { | ||||
|     appContext.getActiveTabContext().$noteTitle.trigger('focus'); | ||||
|     appContext.trigger('focusOnTitle'); | ||||
| } | ||||
|  | ||||
| function focusAndSelectTitle() { | ||||
|     appContext.getActiveTabContext() | ||||
|         .$noteTitle | ||||
|             .trigger('focus') | ||||
|             .trigger('select'); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Since detail loading may take some time and user might just browse through the notes using UP-DOWN keys, | ||||
|  * we intentionally decouple activation of the note in the tree and full load of the note so just avaiting on | ||||
|  * fancytree's activate() won't wait for the full load. | ||||
|  * | ||||
|  * This causes an issue where in some cases you want to do some action after detail is loaded. For this reason | ||||
|  * we provide the listeners here which will be triggered after the detail is loaded and if the loaded note | ||||
|  * is the one registered in the listener. | ||||
|  */ | ||||
| function addDetailLoadedListener(noteId, callback) { | ||||
|     detailLoadedListeners.push({ noteId, callback }); | ||||
| } | ||||
|  | ||||
| function fireDetailLoaded() { | ||||
|     for (const {noteId, callback} of detailLoadedListeners) { | ||||
|         if (noteId === appContext.getActiveTabNoteId()) { | ||||
|             callback(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // all the listeners are one time only | ||||
|     detailLoadedListeners = []; | ||||
|     appContext.trigger('focusAndSelectTitle'); | ||||
| } | ||||
|  | ||||
| ws.subscribeToOutsideSyncMessages(syncData => { | ||||
| @@ -199,17 +69,9 @@ $(window).on('beforeunload', () => { | ||||
|  }); | ||||
|  | ||||
| export default { | ||||
|     reload, | ||||
|     openInTab, | ||||
|     switchToNote, | ||||
|     loadNote, | ||||
|     loadNoteDetail, | ||||
|     focusOnTitle, | ||||
|     focusAndSelectTitle, | ||||
|     addDetailLoadedListener, | ||||
|     getActiveEditor, | ||||
|     activateOrOpenNote, | ||||
|     noteDeleted, | ||||
|     noteChanged, | ||||
|     reloadNote | ||||
|     noteChanged | ||||
| }; | ||||
| @@ -109,17 +109,12 @@ async function unprotectNoteAndSendToServer() { | ||||
|     await appContext.getActiveTabContext().saveNote(); | ||||
|  | ||||
|     treeService.setProtected(activeNote.noteId, activeNote.isProtected); | ||||
|  | ||||
|     await noteDetailService.reload(); | ||||
| } | ||||
|  | ||||
| async function protectSubtree(noteId, protect) { | ||||
|     await enterProtectedSession(); | ||||
|  | ||||
|     await server.put('notes/' + noteId + "/protect/" + (protect ? 1 : 0)); | ||||
|  | ||||
|     treeService.reload(); | ||||
|     noteDetailService.reload(); | ||||
| } | ||||
|  | ||||
| function makeToast(message, protectingLabel, text) { | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import appContext from "./app_context.js"; | ||||
| import treeUtils from "./tree_utils.js"; | ||||
| import noteDetailService from "./note_detail.js"; | ||||
| import Component from "../widgets/component.js"; | ||||
| import treeService from "./tree.js"; | ||||
|  | ||||
| let showSidebarInNewTab = true; | ||||
|  | ||||
| @@ -35,7 +36,19 @@ class TabContext extends Component { | ||||
|         this.trigger('tabOpened', {tabId: this.tabId}); | ||||
|     } | ||||
|  | ||||
|     async setNote(notePath) { | ||||
|     async setNote(inputNotePath) { | ||||
|         const notePath = await treeService.resolveNotePath(inputNotePath); | ||||
|  | ||||
|         if (!notePath) { | ||||
|             console.error(`Cannot resolve note path ${inputNotePath}`); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (notePath === this.notePath) { | ||||
|             console.log(`Setting existing notePath ${notePath} so ignoring ...`); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         await this.trigger('beforeNoteSwitch', {tabId: this.tabId}, true); | ||||
|  | ||||
|         this.notePath = notePath; | ||||
| @@ -64,6 +77,7 @@ class TabContext extends Component { | ||||
|         } | ||||
|  | ||||
|         this.trigger('tabNoteSwitched', {tabId: this.tabId}); | ||||
|         this.trigger('openTabsChanged'); | ||||
|     } | ||||
|  | ||||
|     async remove() { | ||||
| @@ -113,7 +127,16 @@ class TabContext extends Component { | ||||
|     } | ||||
|  | ||||
|     stateChanged() { | ||||
|         appContext.openTabsChanged(); | ||||
|         appContext.openTabsChangedListener(); | ||||
|     } | ||||
|  | ||||
|     noteDeletedListener({noteId}) { | ||||
|         if (this.note && noteId === this.note.noteId) { | ||||
|             this.note = null; | ||||
|             this.notePath = null; | ||||
|  | ||||
|             this.trigger('tabNoteSwitched', {tabId: this.tabId}); | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -263,12 +263,12 @@ async function treeInitialized() { | ||||
|     } | ||||
|  | ||||
|     for (const tab of filteredTabs) { | ||||
|         await noteDetailService.loadNoteDetail(tab.notePath, { | ||||
|             state: tab, | ||||
|             newTab: true, | ||||
|             activate: tab.active, | ||||
|             async: true // faster initial load | ||||
|         }); | ||||
|         const tabContext = appContext.openEmptyTab(); | ||||
|         tabContext.setNote(tab.notePath); | ||||
|  | ||||
|         if (tab.active) { | ||||
|             appContext.activateTab(tabContext.tabId); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     // previous opening triggered task to save tab changes but these are bogus changes (this is init) | ||||
| @@ -446,7 +446,7 @@ ws.subscribeToMessages(message => { | ||||
|        reload(); | ||||
|    } | ||||
|    else if (message.type === 'open-note') { | ||||
|        noteDetailService.activateOrOpenNote(message.noteId); | ||||
|        appContext.activateOrOpenNote(message.noteId); | ||||
|  | ||||
|        if (utils.isElectron()) { | ||||
|            const currentWindow = require("electron").remote.getCurrentWindow(); | ||||
|   | ||||
| @@ -104,7 +104,9 @@ class TreeContextMenu { | ||||
|         const notePath = await treeUtils.getNotePath(this.node); | ||||
|  | ||||
|         if (cmd === 'openInTab') { | ||||
|             noteDetailService.openInTab(notePath, false); | ||||
|             const tabContext = appContext.openEmptyTab(); | ||||
|             appContext.activateTab(tabContext.tabId); | ||||
|             tabContext.setNote(notePath); | ||||
|         } | ||||
|         else if (cmd.startsWith("insertNoteAfter")) { | ||||
|             const parentNoteId = this.node.data.parentNoteId; | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import protectedSessionHolder from "../services/protected_session_holder.js"; | ||||
| import treeCache from "../services/tree_cache.js"; | ||||
| import server from "../services/server.js"; | ||||
| import SpacedUpdate from "../services/spaced_update.js"; | ||||
| import appContext from "../services/app_context.js"; | ||||
|  | ||||
| const TPL = ` | ||||
| <div class="note-title-container"> | ||||
| @@ -107,4 +108,18 @@ export default class NoteTitleWidget extends TabAwareWidget { | ||||
|             await this.spacedUpdate.updateNowIfNecessary(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     focusOnTitleListener() { | ||||
|         if (this.tabContext && this.tabContext.isActive()) { | ||||
|             this.$noteTitle.trigger('focus'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     focusAndSelectTitleListener() { | ||||
|         if (this.tabContext && this.tabContext.isActive()) { | ||||
|             this.$noteTitle | ||||
|                 .trigger('focus') | ||||
|                 .trigger('select'); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -53,7 +53,8 @@ export default class NoteTreeWidget extends TabAwareWidget { | ||||
|  | ||||
|                 treeUtils.getNotePath(node).then(notePath => { | ||||
|                     if (notePath) { | ||||
|                         noteDetailService.openInTab(notePath, false); | ||||
|                         const tabContext = appContext.openEmptyTab(); | ||||
|                         tabContext.setNote(notePath); | ||||
|                     } | ||||
|                 }); | ||||
|  | ||||
| @@ -87,7 +88,9 @@ export default class NoteTreeWidget extends TabAwareWidget { | ||||
|                         node.setFocus(true); | ||||
|                     } | ||||
|                     else if (event.ctrlKey) { | ||||
|                         noteDetailService.loadNoteDetail(node.data.noteId, { newTab: true }); | ||||
|                         const tabContext = appContext.openEmptyTab(); | ||||
|                         treeUtils.getNotePath(node).then(notePath => tabContext.setNote(notePath)); | ||||
|                         appContext.activateTab(tabContext.tabId); | ||||
|                     } | ||||
|                     else { | ||||
|                         node.setActive(); | ||||
|   | ||||
| @@ -130,9 +130,6 @@ export default class NoteTypeWidget extends TabAwareWidget { | ||||
|  | ||||
|         await noteDetailService.reload(); | ||||
|  | ||||
|         // for the note icon to be updated in the tree | ||||
|         await treeService.reload(); | ||||
|  | ||||
|         this.update(); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -85,7 +85,7 @@ export default class SearchBoxWidget extends BasicWidget { | ||||
|  | ||||
|         this.$saveSearchButton.on('click', () => this.saveSearch()); | ||||
|  | ||||
|         this.$closeSearchButton.on('click', () => this.hideSearchListener()); | ||||
|         this.$closeSearchButton.on('click', () => this.trigger('hideSearch')); | ||||
|  | ||||
|         return this.$widget; | ||||
|     } | ||||
| @@ -159,6 +159,8 @@ export default class SearchBoxWidget extends BasicWidget { | ||||
|         this.resetSearchListener(); | ||||
|  | ||||
|         this.$searchBox.slideUp(); | ||||
|  | ||||
|         this.trigger('hideSearchResults'); | ||||
|     } | ||||
|  | ||||
|     toggleSearchListener() { | ||||
| @@ -167,7 +169,6 @@ export default class SearchBoxWidget extends BasicWidget { | ||||
|         } | ||||
|         else { | ||||
|             this.hideSearchListener(); | ||||
|             this.trigger('hideSearchResults'); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -34,6 +34,8 @@ export default class SearchResultsWidget extends BasicWidget { | ||||
|         this.$searchResults = this.$widget; | ||||
|         this.$searchResultsInner = this.$widget.find(".search-results-inner"); | ||||
|  | ||||
|         this.toggle(false); | ||||
|  | ||||
|         return this.$widget; | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -20,6 +20,10 @@ export default class TabCachingWidget extends TabAwareWidget { | ||||
|             widget.toggle(false); | ||||
|         } | ||||
|  | ||||
|         if (!this.tabContext) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let widget = this.widgets[this.tabContext.tabId]; | ||||
|  | ||||
|         if (!widget) { | ||||
| @@ -31,8 +35,6 @@ export default class TabCachingWidget extends TabAwareWidget { | ||||
|         } | ||||
|  | ||||
|         widget.toggle(true); | ||||
|  | ||||
|         return false; // stop propagation to children | ||||
|     } | ||||
|  | ||||
|     tabRemovedListener({tabId}) { | ||||
|   | ||||
| @@ -7,6 +7,7 @@ import contextMenuWidget from "../../services/context_menu.js"; | ||||
| import toastService from "../../services/toast.js"; | ||||
| import attributeAutocompleteService from "../../services/attribute_autocomplete.js"; | ||||
| import TypeWidget from "./type_widget.js"; | ||||
| import appContext from "../../services/app_context.js"; | ||||
|  | ||||
| const uniDirectionalOverlays = [ | ||||
|     [ "Arrow", { | ||||
| @@ -196,7 +197,8 @@ export default class RelationMapTypeWidget extends TypeWidget { | ||||
|         const noteId = this.idToNoteId($noteBox.prop("id")); | ||||
|  | ||||
|         if (cmd === "open-in-new-tab") { | ||||
|             noteDetailService.openInTab(noteId, false); | ||||
|             const tabContext = appContext.openEmptyTab(); | ||||
|             tabContext.setNote(noteId); | ||||
|         } | ||||
|         else if (cmd === "remove") { | ||||
|             const confirmDialog = await import('../../dialogs/confirm.js'); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user