mirror of
				https://github.com/zadam/trilium.git
				synced 2025-11-04 05:28:59 +01:00 
			
		
		
		
	further tab management moved to app context
This commit is contained in:
		
							parent
							
								
									4d16018f6c
								
							
						
					
					
						commit
						f25d735b9d
					
				@ -6,12 +6,16 @@ import tabRow from "./tab_row.js";
 | 
				
			|||||||
import treeService from "./tree.js";
 | 
					import treeService from "./tree.js";
 | 
				
			||||||
import noteDetailService from "./note_detail.js";
 | 
					import noteDetailService from "./note_detail.js";
 | 
				
			||||||
import TabContext from "./tab_context.js";
 | 
					import TabContext from "./tab_context.js";
 | 
				
			||||||
 | 
					import server from "./server.js";
 | 
				
			||||||
 | 
					import keyboardActionService from "./keyboard_actions.js";
 | 
				
			||||||
 | 
					import contextMenuService from "./context_menu.js";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class AppContext {
 | 
					class AppContext {
 | 
				
			||||||
    constructor() {
 | 
					    constructor() {
 | 
				
			||||||
        this.widgets = [];
 | 
					        this.widgets = [];
 | 
				
			||||||
        /** @type {TabContext[]} */
 | 
					        /** @type {TabContext[]} */
 | 
				
			||||||
        this.tabContexts = [];
 | 
					        this.tabContexts = [];
 | 
				
			||||||
 | 
					        this.tabsChangedTaskId = null;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    trigger(name, data) {
 | 
					    trigger(name, data) {
 | 
				
			||||||
@ -175,6 +179,56 @@ class AppContext {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        await tabRow.activateTab(ctx.$tab[0]);
 | 
					        await tabRow.activateTab(ctx.$tab[0]);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async filterTabs(noteId) {
 | 
				
			||||||
 | 
					        for (const tc of this.tabContexts) {
 | 
				
			||||||
 | 
					            if (tc.notePath && !tc.notePath.split("/").includes(noteId)) {
 | 
				
			||||||
 | 
					                await tabRow.removeTab(tc.$tab[0]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.tabContexts.length === 0) {
 | 
				
			||||||
 | 
					            this.openEmptyTab()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await this.saveOpenTabs();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async saveOpenTabs() {
 | 
				
			||||||
 | 
					        const openTabs = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (const tabEl of tabRow.tabEls) {
 | 
				
			||||||
 | 
					            const tabId = tabEl.getAttribute('data-tab-id');
 | 
				
			||||||
 | 
					            const tabContext = appContext.getTabContexts().find(tc => tc.tabId === tabId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (tabContext) {
 | 
				
			||||||
 | 
					                const tabState = tabContext.getTabState();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (tabState) {
 | 
				
			||||||
 | 
					                    openTabs.push(tabState);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await server.put('options', {
 | 
				
			||||||
 | 
					            openTabs: JSON.stringify(openTabs)
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    clearOpenTabsTask() {
 | 
				
			||||||
 | 
					        if (this.tabsChangedTaskId) {
 | 
				
			||||||
 | 
					            clearTimeout(this.tabsChangedTaskId);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    openTabsChanged() {
 | 
				
			||||||
 | 
					        // 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
 | 
				
			||||||
 | 
					        this.clearOpenTabsTask();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        this.tabsChangedTaskId = setTimeout(() => this.saveOpenTabs(), 1000);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const appContext = new AppContext();
 | 
					const appContext = new AppContext();
 | 
				
			||||||
@ -200,4 +254,56 @@ tabRow.addListener('tabRemove', async ({ detail }) => {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					tabRow.addListener('activeTabChange', () => appContext.openTabsChanged());
 | 
				
			||||||
 | 
					tabRow.addListener('tabRemove', () => appContext.openTabsChanged());
 | 
				
			||||||
 | 
					tabRow.addListener('tabReorder', () => appContext.openTabsChanged());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					keyboardActionService.setGlobalActionHandler('OpenNewTab', () => {
 | 
				
			||||||
 | 
					    appContext.openEmptyTab();
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					keyboardActionService.setGlobalActionHandler('CloseActiveTab', () => {
 | 
				
			||||||
 | 
					    if (tabRow.activeTabEl) {
 | 
				
			||||||
 | 
					        tabRow.removeTab(tabRow.activeTabEl);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					keyboardActionService.setGlobalActionHandler('ActivateNextTab', () => {
 | 
				
			||||||
 | 
					    const nextTab = tabRow.nextTabEl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (nextTab) {
 | 
				
			||||||
 | 
					        tabRow.activateTab(nextTab);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					keyboardActionService.setGlobalActionHandler('ActivatePreviousTab', () => {
 | 
				
			||||||
 | 
					    const prevTab = tabRow.previousTabEl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (prevTab) {
 | 
				
			||||||
 | 
					        tabRow.activateTab(prevTab);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					$(tabRow.el).on('contextmenu', '.note-tab', e => {
 | 
				
			||||||
 | 
					    e.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const tab = $(e.target).closest(".note-tab");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    contextMenuService.initContextMenu(e, {
 | 
				
			||||||
 | 
					        getContextMenuItems: () => {
 | 
				
			||||||
 | 
					            return [
 | 
				
			||||||
 | 
					                {title: "Close all tabs", cmd: "removeAllTabs", uiIcon: "empty"},
 | 
				
			||||||
 | 
					                {title: "Close all tabs except for this", cmd: "removeAllTabsExceptForThis", uiIcon: "empty"}
 | 
				
			||||||
 | 
					            ];
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        selectContextMenuItem: (e, cmd) => {
 | 
				
			||||||
 | 
					            if (cmd === 'removeAllTabs') {
 | 
				
			||||||
 | 
					                tabRow.removeAllTabs();
 | 
				
			||||||
 | 
					            } else if (cmd === 'removeAllTabsExceptForThis') {
 | 
				
			||||||
 | 
					                tabRow.removeAllTabsExceptForThis(tab[0]);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default appContext;
 | 
					export default appContext;
 | 
				
			||||||
@ -21,7 +21,7 @@ async function getHoistedNoteId() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
async function setHoistedNoteId(noteId) {
 | 
					async function setHoistedNoteId(noteId) {
 | 
				
			||||||
    if (noteId !== 'root') {
 | 
					    if (noteId !== 'root') {
 | 
				
			||||||
        await noteDetailService.filterTabs(noteId);
 | 
					        await appContext.filterTabs(noteId);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    hoistedNoteId = noteId;
 | 
					    hoistedNoteId = noteId;
 | 
				
			||||||
 | 
				
			|||||||
@ -36,7 +36,7 @@ async function switchToNote(notePath) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    await loadNoteDetail(notePath);
 | 
					    await loadNoteDetail(notePath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    openTabsChanged();
 | 
					    appContext.openTabsChanged();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function onNoteChange(func) {
 | 
					function onNoteChange(func) {
 | 
				
			||||||
@ -87,7 +87,7 @@ async function activateOrOpenNote(noteId) {
 | 
				
			|||||||
async function loadNoteDetailToContext(ctx, note, notePath) {
 | 
					async function loadNoteDetailToContext(ctx, note, notePath) {
 | 
				
			||||||
    await ctx.setNote(note, notePath);
 | 
					    await ctx.setNote(note, notePath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    openTabsChanged();
 | 
					    appContext.openTabsChanged();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fireDetailLoaded();
 | 
					    fireDetailLoaded();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@ -141,23 +141,6 @@ async function loadNote(noteId) {
 | 
				
			|||||||
    return new NoteFull(treeCache, row, noteShort);
 | 
					    return new NoteFull(treeCache, row, noteShort);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
async function filterTabs(noteId) {
 | 
					 | 
				
			||||||
    for (const tc of appContext.getTabContexts()) {
 | 
					 | 
				
			||||||
        if (tc.notePath && !tc.notePath.split("/").includes(noteId)) {
 | 
					 | 
				
			||||||
            await tabRow.removeTab(tc.$tab[0]);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (appContext.getTabContexts().length === 0) {
 | 
					 | 
				
			||||||
        await loadNoteDetail(noteId, {
 | 
					 | 
				
			||||||
            newTab: true,
 | 
					 | 
				
			||||||
            activate: true
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    await saveOpenTabs();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function noteDeleted(noteId) {
 | 
					async function noteDeleted(noteId) {
 | 
				
			||||||
    for (const tc of appContext.getTabContexts()) {
 | 
					    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)
 | 
					        // not removing active even if it contains deleted note since that one will move to another note (handled by deletion logic)
 | 
				
			||||||
@ -250,96 +233,6 @@ $tabContentsContainer.on("drop", async e => {
 | 
				
			|||||||
    });
 | 
					    });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
$(tabRow.el).on('contextmenu', '.note-tab', e => {
 | 
					 | 
				
			||||||
    e.preventDefault();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    const tab = $(e.target).closest(".note-tab");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    contextMenuService.initContextMenu(e, {
 | 
					 | 
				
			||||||
        getContextMenuItems: () => {
 | 
					 | 
				
			||||||
            return [
 | 
					 | 
				
			||||||
                {title: "Close all tabs", cmd: "removeAllTabs", uiIcon: "empty"},
 | 
					 | 
				
			||||||
                {title: "Close all tabs except for this", cmd: "removeAllTabsExceptForThis", uiIcon: "empty"}
 | 
					 | 
				
			||||||
            ];
 | 
					 | 
				
			||||||
        },
 | 
					 | 
				
			||||||
        selectContextMenuItem: (e, cmd) => {
 | 
					 | 
				
			||||||
            if (cmd === 'removeAllTabs') {
 | 
					 | 
				
			||||||
                tabRow.removeAllTabs();
 | 
					 | 
				
			||||||
            } else if (cmd === 'removeAllTabsExceptForThis') {
 | 
					 | 
				
			||||||
                tabRow.removeAllTabsExceptForThis(tab[0]);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
keyboardActionService.setGlobalActionHandler('OpenNewTab', () => {
 | 
					 | 
				
			||||||
    openEmptyTab();
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
keyboardActionService.setGlobalActionHandler('CloseActiveTab', () => {
 | 
					 | 
				
			||||||
    if (tabRow.activeTabEl) {
 | 
					 | 
				
			||||||
        tabRow.removeTab(tabRow.activeTabEl);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
keyboardActionService.setGlobalActionHandler('ActivateNextTab', () => {
 | 
					 | 
				
			||||||
    const nextTab = tabRow.nextTabEl;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (nextTab) {
 | 
					 | 
				
			||||||
        tabRow.activateTab(nextTab);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
keyboardActionService.setGlobalActionHandler('ActivatePreviousTab', () => {
 | 
					 | 
				
			||||||
    const prevTab = tabRow.previousTabEl;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (prevTab) {
 | 
					 | 
				
			||||||
        tabRow.activateTab(prevTab);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
});
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
tabRow.addListener('activeTabChange', openTabsChanged);
 | 
					 | 
				
			||||||
tabRow.addListener('tabRemove', openTabsChanged);
 | 
					 | 
				
			||||||
tabRow.addListener('tabReorder', openTabsChanged);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
let tabsChangedTaskId = null;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function clearOpenTabsTask() {
 | 
					 | 
				
			||||||
    if (tabsChangedTaskId) {
 | 
					 | 
				
			||||||
        clearTimeout(tabsChangedTaskId);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function openTabsChanged() {
 | 
					 | 
				
			||||||
    // 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
 | 
					 | 
				
			||||||
    clearOpenTabsTask();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    tabsChangedTaskId = setTimeout(saveOpenTabs, 1000);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
async function saveOpenTabs() {
 | 
					 | 
				
			||||||
    const openTabs = [];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (const tabEl of tabRow.tabEls) {
 | 
					 | 
				
			||||||
        const tabId = tabEl.getAttribute('data-tab-id');
 | 
					 | 
				
			||||||
        const tabContext = appContext.getTabContexts().find(tc => tc.tabId === tabId);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if (tabContext) {
 | 
					 | 
				
			||||||
            const tabState = tabContext.getTabState();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            if (tabState) {
 | 
					 | 
				
			||||||
                openTabs.push(tabState);
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    await server.put('options', {
 | 
					 | 
				
			||||||
        openTabs: JSON.stringify(openTabs)
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function noteChanged() {
 | 
					function noteChanged() {
 | 
				
			||||||
    const activeTabContext = appContext.getActiveTabContext();
 | 
					    const activeTabContext = appContext.getActiveTabContext();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -367,10 +260,7 @@ export default {
 | 
				
			|||||||
    addDetailLoadedListener,
 | 
					    addDetailLoadedListener,
 | 
				
			||||||
    getActiveEditor,
 | 
					    getActiveEditor,
 | 
				
			||||||
    activateOrOpenNote,
 | 
					    activateOrOpenNote,
 | 
				
			||||||
    clearOpenTabsTask,
 | 
					 | 
				
			||||||
    filterTabs,
 | 
					 | 
				
			||||||
    noteDeleted,
 | 
					    noteDeleted,
 | 
				
			||||||
    noteChanged,
 | 
					    noteChanged,
 | 
				
			||||||
    openTabsChanged,
 | 
					 | 
				
			||||||
    reloadNote
 | 
					    reloadNote
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@ -479,7 +479,7 @@ class TabContext {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    stateChanged() {
 | 
					    stateChanged() {
 | 
				
			||||||
        noteDetailService.openTabsChanged();
 | 
					        appContext.openTabsChanged();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -287,7 +287,7 @@ async function treeInitialized() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    // previous opening triggered task to save tab changes but these are bogus changes (this is init)
 | 
					    // previous opening triggered task to save tab changes but these are bogus changes (this is init)
 | 
				
			||||||
    // so we'll cancel it
 | 
					    // so we'll cancel it
 | 
				
			||||||
    noteDetailService.clearOpenTabsTask();
 | 
					    appContext.clearOpenTabsTask();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    setFrontendAsLoaded();
 | 
					    setFrontendAsLoaded();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user