mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +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