further tab management moved to app context

This commit is contained in:
zadam 2020-01-12 12:48:17 +01:00
parent 4d16018f6c
commit f25d735b9d
5 changed files with 111 additions and 115 deletions

View File

@ -6,12 +6,16 @@ import tabRow from "./tab_row.js";
import treeService from "./tree.js";
import noteDetailService from "./note_detail.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 {
constructor() {
this.widgets = [];
/** @type {TabContext[]} */
this.tabContexts = [];
this.tabsChangedTaskId = null;
}
trigger(name, data) {
@ -175,6 +179,56 @@ class AppContext {
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();
@ -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;

View File

@ -21,7 +21,7 @@ async function getHoistedNoteId() {
async function setHoistedNoteId(noteId) {
if (noteId !== 'root') {
await noteDetailService.filterTabs(noteId);
await appContext.filterTabs(noteId);
}
hoistedNoteId = noteId;

View File

@ -36,7 +36,7 @@ async function switchToNote(notePath) {
await loadNoteDetail(notePath);
openTabsChanged();
appContext.openTabsChanged();
}
function onNoteChange(func) {
@ -87,7 +87,7 @@ async function activateOrOpenNote(noteId) {
async function loadNoteDetailToContext(ctx, note, notePath) {
await ctx.setNote(note, notePath);
openTabsChanged();
appContext.openTabsChanged();
fireDetailLoaded();
}
@ -141,23 +141,6 @@ async function loadNote(noteId) {
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) {
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)
@ -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() {
const activeTabContext = appContext.getActiveTabContext();
@ -367,10 +260,7 @@ export default {
addDetailLoadedListener,
getActiveEditor,
activateOrOpenNote,
clearOpenTabsTask,
filterTabs,
noteDeleted,
noteChanged,
openTabsChanged,
reloadNote
};

View File

@ -479,7 +479,7 @@ class TabContext {
}
stateChanged() {
noteDetailService.openTabsChanged();
appContext.openTabsChanged();
}
}

View File

@ -287,7 +287,7 @@ async function treeInitialized() {
// previous opening triggered task to save tab changes but these are bogus changes (this is init)
// so we'll cancel it
noteDetailService.clearOpenTabsTask();
appContext.clearOpenTabsTask();
setFrontendAsLoaded();
}