Notes/src/public/app/components/tab_manager.js

655 lines
23 KiB
JavaScript
Raw Normal View History

2022-12-01 13:07:23 +01:00
import Component from "./component.js";
import SpacedUpdate from "../services/spaced_update.js";
import server from "../services/server.js";
import options from "../services/options.js";
import froca from "../services/froca.js";
import treeService from "../services/tree.js";
import utils from "../services/utils.js";
2021-05-22 12:35:41 +02:00
import NoteContext from "./note_context.js";
2020-04-25 23:52:13 +02:00
import appContext from "./app_context.js";
import Mutex from "../utils/mutex.js";
import linkService from "../services/link.js";
export default class TabManager extends Component {
2020-02-27 10:03:14 +01:00
constructor() {
super();
/** @property {NoteContext[]} */
this.children = [];
this.mutex = new Mutex();
2021-05-22 13:04:08 +02:00
this.activeNtxId = null;
// elements are arrays of {contexts, position}, storing note contexts for each tab (one main context + subcontexts [splits]), and the original position of the tab
this.recentlyClosedTabs = [];
this.tabsUpdate = new SpacedUpdate(async () => {
2020-04-25 23:52:13 +02:00
if (!appContext.isMainWindow) {
return;
}
const openNoteContexts = this.noteContexts.map((nc) => nc.getPojoState()).filter((t) => !!t);
2025-01-09 18:07:02 +02:00
await server.put("options", {
2023-04-11 22:00:04 +02:00
openNoteContexts: JSON.stringify(openNoteContexts)
});
});
appContext.addBeforeUnloadListener(this);
}
/** @returns {NoteContext[]} */
get noteContexts() {
2020-02-09 21:13:05 +01:00
return this.children;
}
/** @type {NoteContext[]} */
get mainNoteContexts() {
2025-01-09 18:07:02 +02:00
return this.noteContexts.filter((nc) => !nc.mainNtxId);
2021-05-20 23:13:34 +02:00
}
2020-04-25 23:52:13 +02:00
async loadTabs() {
try {
2025-01-09 18:07:02 +02:00
const noteContextsToOpen = (appContext.isMainWindow && options.getJson("openNoteContexts")) || [];
// preload all notes at once
await froca.getNotes([...noteContextsToOpen.flatMap((tab) => [treeService.getNoteIdFromUrl(tab.notePath), tab.hoistedNoteId])], true);
const filteredNoteContexts = noteContextsToOpen.filter((openTab) => {
const noteId = treeService.getNoteIdFromUrl(openTab.notePath);
if (!(noteId in froca.notes)) {
// note doesn't exist so don't try to open tab for it
return false;
}
if (!(openTab.hoistedNoteId in froca.notes)) {
2025-01-09 18:07:02 +02:00
openTab.hoistedNoteId = "root";
}
return true;
});
// resolve before opened tabs can change this
const parsedFromUrl = linkService.parseNavigationStateFromUrl(window.location.href);
2022-12-22 23:38:57 +01:00
2023-04-11 22:00:04 +02:00
if (filteredNoteContexts.length === 0) {
parsedFromUrl.ntxId = parsedFromUrl.ntxId || NoteContext.generateNtxId(); // generate already here, so that we later know which one to activate
2023-04-11 22:00:04 +02:00
filteredNoteContexts.push({
2025-01-09 18:07:02 +02:00
notePath: parsedFromUrl.notePath || "root",
ntxId: parsedFromUrl.ntxId,
active: true,
2025-01-09 18:07:02 +02:00
hoistedNoteId: parsedFromUrl.hoistedNoteId || "root",
viewScope: parsedFromUrl.viewScope || {}
});
} else if (!filteredNoteContexts.find((tab) => tab.active)) {
2023-04-11 22:00:04 +02:00
filteredNoteContexts[0].active = true;
}
await this.tabsUpdate.allowUpdateWithoutChange(async () => {
2023-04-11 22:00:04 +02:00
for (const tab of filteredNoteContexts) {
await this.openContextWithNote(tab.notePath, {
activate: tab.active,
ntxId: tab.ntxId,
mainNtxId: tab.mainNtxId,
hoistedNoteId: tab.hoistedNoteId,
2023-04-03 23:47:24 +02:00
viewScope: tab.viewScope
});
}
});
// if there's a notePath in the URL, make sure it's open and active
// (useful, for e.g., opening clipped notes from clipper or opening link in an extra window)
if (parsedFromUrl.notePath) {
await appContext.tabManager.switchToNoteContext(parsedFromUrl.ntxId, parsedFromUrl.notePath, parsedFromUrl.viewScope, parsedFromUrl.hoistedNoteId);
} else if (parsedFromUrl.searchString) {
2025-01-09 18:07:02 +02:00
await appContext.triggerCommand("searchNotes", {
searchString: parsedFromUrl.searchString
});
}
} catch (e) {
logError(`Loading note contexts '${options.get("openNoteContexts")}' failed: ${e.message} ${e.stack}`);
2022-12-13 16:57:46 +01:00
// try to recover
await this.openEmptyTab();
2022-12-13 16:57:46 +01:00
}
}
noteSwitchedEvent({ noteContext }) {
2021-05-22 12:26:45 +02:00
if (noteContext.isActive()) {
this.setCurrentNavigationStateToHash();
}
2020-02-08 21:23:42 +01:00
this.tabsUpdate.scheduleUpdate();
}
setCurrentNavigationStateToHash() {
const calculatedHash = this.calculateHash();
2020-02-09 21:13:05 +01:00
// update if it's the first history entry or there has been a change
if (window.history.length === 0 || calculatedHash !== window.location?.hash) {
2020-02-09 21:13:05 +01:00
// using pushState instead of directly modifying document.location because it does not trigger hashchange
window.history.pushState(null, "", calculatedHash);
}
2020-02-09 21:13:05 +01:00
const activeNoteContext = this.getActiveContext();
this.updateDocumentTitle(activeNoteContext);
2020-03-08 17:17:18 +01:00
this.triggerEvent("activeNoteChanged"); // trigger this even in on popstate event
}
calculateHash() {
const activeNoteContext = this.getActiveContext();
if (!activeNoteContext) {
return "";
}
return linkService.calculateHash({
notePath: activeNoteContext.notePath,
ntxId: activeNoteContext.ntxId,
hoistedNoteId: activeNoteContext.hoistedNoteId,
viewScope: activeNoteContext.viewScope
});
}
/** @returns {NoteContext[]} */
getNoteContexts() {
2021-05-22 12:26:45 +02:00
return this.noteContexts;
}
/**
* Main context is essentially a tab (children are splits), so this returns tabs.
* @returns {NoteContext[]}
*/
getMainNoteContexts() {
2025-01-09 18:07:02 +02:00
return this.noteContexts.filter((nc) => nc.isMainContext());
}
/** @returns {NoteContext} */
getNoteContextById(ntxId) {
2025-01-09 18:07:02 +02:00
const noteContext = this.noteContexts.find((nc) => nc.ntxId === ntxId);
2021-05-20 23:13:34 +02:00
2021-05-22 12:26:45 +02:00
if (!noteContext) {
throw new Error(`Cannot find noteContext id='${ntxId}'`);
2021-05-20 23:13:34 +02:00
}
2021-05-22 12:26:45 +02:00
return noteContext;
}
/**
* Get active context which represents the visible split with focus. Active context can, but doesn't have to be "main".
*
* @returns {NoteContext}
*/
getActiveContext() {
2025-01-09 18:07:02 +02:00
return this.activeNtxId ? this.getNoteContextById(this.activeNtxId) : null;
2021-05-22 13:04:08 +02:00
}
/**
* Get active main context which corresponds to the active tab.
*
* @returns {NoteContext}
*/
getActiveMainContext() {
2025-01-09 18:07:02 +02:00
return this.activeNtxId ? this.getNoteContextById(this.activeNtxId).getMainContext() : null;
}
/** @returns {string|null} */
getActiveContextNotePath() {
2021-05-22 12:35:41 +02:00
const activeContext = this.getActiveContext();
return activeContext ? activeContext.notePath : null;
}
/** @returns {FNote} */
getActiveContextNote() {
2021-05-22 12:35:41 +02:00
const activeContext = this.getActiveContext();
return activeContext ? activeContext.note : null;
}
/** @returns {string|null} */
getActiveContextNoteId() {
2021-05-22 12:35:41 +02:00
const activeNote = this.getActiveContextNote();
return activeNote ? activeNote.noteId : null;
}
/** @returns {string|null} */
getActiveContextNoteType() {
2021-05-22 12:35:41 +02:00
const activeNote = this.getActiveContextNote();
return activeNote ? activeNote.type : null;
}
/** @returns {string|null} */
getActiveContextNoteMime() {
2025-03-03 01:04:42 +01:00
const activeNote = this.getActiveContextNote();
return activeNote ? activeNote.mime : null;
}
async switchToNoteContext(ntxId, notePath, viewScope = {}, hoistedNoteId = null) {
const noteContext = this.noteContexts.find((nc) => nc.ntxId === ntxId) || (await this.openEmptyTab());
2022-12-13 16:57:46 +01:00
await this.activateNoteContext(noteContext.ntxId);
if (hoistedNoteId) {
await noteContext.setHoistedNoteId(hoistedNoteId);
}
2022-12-13 16:57:46 +01:00
if (notePath) {
await noteContext.setNote(notePath, { viewScope });
2022-12-13 16:57:46 +01:00
}
}
async openAndActivateEmptyTab() {
2021-05-22 12:26:45 +02:00
const noteContext = await this.openEmptyTab();
2021-05-22 12:35:41 +02:00
await this.activateNoteContext(noteContext.ntxId);
await noteContext.setEmpty();
}
async openEmptyTab(ntxId = null, hoistedNoteId = "root", mainNtxId = null) {
2021-05-22 12:26:45 +02:00
const noteContext = new NoteContext(ntxId, hoistedNoteId, mainNtxId);
2021-05-21 22:34:40 +02:00
2025-01-09 18:07:02 +02:00
const existingNoteContext = this.children.find((nc) => nc.ntxId === noteContext.ntxId);
2021-05-21 22:34:40 +02:00
2021-05-22 12:26:45 +02:00
if (existingNoteContext) {
await existingNoteContext.setHoistedNoteId(hoistedNoteId);
2021-05-22 12:26:45 +02:00
return existingNoteContext;
2021-05-21 22:34:40 +02:00
}
2021-05-22 12:26:45 +02:00
this.child(noteContext);
2020-02-27 10:03:14 +01:00
2025-01-09 18:07:02 +02:00
await this.triggerEvent("newNoteContextCreated", { noteContext });
2020-02-27 10:03:14 +01:00
2021-05-22 12:26:45 +02:00
return noteContext;
}
async openInNewTab(targetNoteId, hoistedNoteId = null) {
const noteContext = await this.openEmptyTab(null, hoistedNoteId || this.getActiveContext().hoistedNoteId);
await noteContext.setNote(targetNoteId);
}
async openInSameTab(targetNoteId, hoistedNoteId = null) {
const activeContext = this.getActiveContext();
await activeContext.setHoistedNoteId(hoistedNoteId || activeContext.hoistedNoteId);
await activeContext.setNote(targetNoteId);
}
/**
* If the requested notePath is within current note hoisting scope then keep the note hoisting also for the new tab.
*/
async openTabWithNoteWithHoisting(notePath, opts = {}) {
2021-05-22 12:35:41 +02:00
const noteContext = this.getActiveContext();
2025-01-09 18:07:02 +02:00
let hoistedNoteId = "root";
2020-11-24 23:24:05 +01:00
2021-05-22 12:26:45 +02:00
if (noteContext) {
const resolvedNotePath = await treeService.resolveNotePath(notePath, noteContext.hoistedNoteId);
2020-11-24 23:24:05 +01:00
if (resolvedNotePath.includes(noteContext.hoistedNoteId) || resolvedNotePath.includes("_hidden")) {
2021-05-22 12:26:45 +02:00
hoistedNoteId = noteContext.hoistedNoteId;
2020-11-24 23:24:05 +01:00
}
}
2023-04-03 23:47:24 +02:00
opts.hoistedNoteId = hoistedNoteId;
return this.openContextWithNote(notePath, opts);
2020-11-24 23:24:05 +01:00
}
async openContextWithNote(notePath, opts = {}) {
const activate = !!opts.activate;
const ntxId = opts.ntxId || null;
const mainNtxId = opts.mainNtxId || null;
2025-01-09 18:07:02 +02:00
const hoistedNoteId = opts.hoistedNoteId || "root";
2023-04-03 23:47:24 +02:00
const viewScope = opts.viewScope || { viewMode: "default" };
2021-05-22 12:26:45 +02:00
const noteContext = await this.openEmptyTab(ntxId, hoistedNoteId, mainNtxId);
2020-02-27 12:26:42 +01:00
if (notePath) {
await noteContext.setNote(notePath, {
2023-06-30 11:18:34 +02:00
// if activate is false, then send normal noteSwitched event
triggerSwitchEvent: !activate,
2023-04-03 23:47:24 +02:00
viewScope: viewScope
});
}
2020-02-27 12:26:42 +01:00
if (activate) {
2021-05-22 12:35:41 +02:00
this.activateNoteContext(noteContext.ntxId, false);
2020-02-27 12:26:42 +01:00
await this.triggerEvent("noteSwitchedAndActivated", {
2021-05-22 12:26:45 +02:00
noteContext,
notePath: noteContext.notePath // resolved note path
2020-02-28 00:31:12 +01:00
});
2020-02-27 12:26:42 +01:00
}
2021-05-22 12:26:45 +02:00
return noteContext;
2020-02-27 12:26:42 +01:00
}
async activateOrOpenNote(noteId) {
2021-05-22 12:26:45 +02:00
for (const noteContext of this.getNoteContexts()) {
if (noteContext.note && noteContext.note.noteId === noteId) {
2021-05-22 12:35:41 +02:00
this.activateNoteContext(noteContext.ntxId);
return;
}
}
// if no tab with this note has been found we'll create new tab
await this.openContextWithNote(noteId, { activate: true });
}
async activateNoteContext(ntxId, triggerEvent = true) {
2021-05-22 13:04:08 +02:00
if (ntxId === this.activeNtxId) {
return;
}
2021-05-22 13:04:08 +02:00
this.activeNtxId = ntxId;
2020-02-27 12:26:42 +01:00
if (triggerEvent) {
2025-01-09 18:07:02 +02:00
await this.triggerEvent("activeContextChanged", {
2021-05-22 12:26:45 +02:00
noteContext: this.getNoteContextById(ntxId)
2020-03-07 13:40:46 +01:00
});
2020-02-27 12:26:42 +01:00
}
2020-02-08 21:23:42 +01:00
this.tabsUpdate.scheduleUpdate();
this.setCurrentNavigationStateToHash();
}
/**
* @param ntxId
* @returns {Promise<boolean>} true if note context has been removed, false otherwise
*/
async removeNoteContext(ntxId) {
2023-06-30 11:18:34 +02:00
// removing note context is an async process which can take some time, if users presses CTRL-W quickly, two
// close events could interleave which would then lead to attempting to activate already removed context.
return await this.mutex.runExclusively(async () => {
let noteContextToRemove;
try {
noteContextToRemove = this.getNoteContextById(ntxId);
2025-01-09 18:07:02 +02:00
} catch {
// note context not found
return false;
}
if (noteContextToRemove.isMainContext()) {
2025-01-09 18:07:02 +02:00
const mainNoteContexts = this.getNoteContexts().filter((nc) => nc.isMainContext());
if (mainNoteContexts.length === 1) {
if (noteContextToRemove.isEmpty()) {
// this is already the empty note context, no point in closing it and replacing with another
// empty tab
return false;
}
await this.openEmptyTab();
}
}
// close dangling autocompletes after closing the tab
const $autocompleteEl = $(".aa-input");
if ("autocomplete" in $autocompleteEl) {
$autocompleteEl.autocomplete("close");
}
2021-05-20 23:13:34 +02:00
const noteContextsToRemove = noteContextToRemove.getSubContexts();
2025-01-09 18:07:02 +02:00
const ntxIdsToRemove = noteContextsToRemove.map((nc) => nc.ntxId);
await this.triggerEvent("beforeNoteContextRemove", { ntxIds: ntxIdsToRemove });
if (!noteContextToRemove.isMainContext()) {
const siblings = noteContextToRemove.getMainContext().getSubContexts();
2025-01-09 18:07:02 +02:00
const idx = siblings.findIndex((nc) => nc.ntxId === noteContextToRemove.ntxId);
const contextToActivateIdx = idx === siblings.length - 1 ? idx - 1 : idx + 1;
const contextToActivate = siblings[contextToActivateIdx];
await this.activateNoteContext(contextToActivate.ntxId);
2025-01-09 18:07:02 +02:00
} else if (this.mainNoteContexts.length <= 1) {
await this.openAndActivateEmptyTab();
2025-01-09 18:07:02 +02:00
} else if (ntxIdsToRemove.includes(this.activeNtxId)) {
const idx = this.mainNoteContexts.findIndex((nc) => nc.ntxId === noteContextToRemove.ntxId);
if (idx === this.mainNoteContexts.length - 1) {
await this.activatePreviousTabCommand();
2025-01-09 18:07:02 +02:00
} else {
await this.activateNextTabCommand();
}
}
this.removeNoteContexts(noteContextsToRemove);
return true;
});
}
removeNoteContexts(noteContextsToRemove) {
2025-01-09 18:07:02 +02:00
const ntxIdsToRemove = noteContextsToRemove.map((nc) => nc.ntxId);
2025-01-09 18:07:02 +02:00
const position = this.noteContexts.findIndex((nc) => ntxIdsToRemove.includes(nc.ntxId));
2025-01-09 18:07:02 +02:00
this.children = this.children.filter((nc) => !ntxIdsToRemove.includes(nc.ntxId));
this.addToRecentlyClosedTabs(noteContextsToRemove, position);
this.triggerEvent("noteContextRemoved", { ntxIds: ntxIdsToRemove });
this.tabsUpdate.scheduleUpdate();
}
addToRecentlyClosedTabs(noteContexts, position) {
if (noteContexts.length === 1 && noteContexts[0].isEmpty()) {
return;
}
2025-01-09 18:07:02 +02:00
this.recentlyClosedTabs.push({ contexts: noteContexts, position: position });
}
tabReorderEvent({ ntxIdsInOrder }) {
const order = {};
2021-05-24 21:05:44 +02:00
let i = 0;
2021-05-24 21:43:24 +02:00
for (const ntxId of ntxIdsInOrder) {
for (const noteContext of this.getNoteContextById(ntxId).getSubContexts()) {
order[noteContext.ntxId] = i++;
2021-05-24 21:05:44 +02:00
}
}
this.children.sort((a, b) => (order[a.ntxId] < order[b.ntxId] ? -1 : 1));
2021-05-24 21:05:44 +02:00
this.tabsUpdate.scheduleUpdate();
}
noteContextReorderEvent({ ntxIdsInOrder, oldMainNtxId, newMainNtxId }) {
2023-05-31 01:53:55 +08:00
const order = Object.fromEntries(ntxIdsInOrder.map((v, i) => [v, i]));
this.children.sort((a, b) => (order[a.ntxId] < order[b.ntxId] ? -1 : 1));
2023-06-01 00:48:37 +08:00
if (oldMainNtxId && newMainNtxId) {
2025-01-09 18:07:02 +02:00
this.children.forEach((c) => {
2023-06-01 00:48:37 +08:00
if (c.ntxId === newMainNtxId) {
// new main context has null mainNtxId
c.mainNtxId = null;
} else if (c.ntxId === oldMainNtxId || c.mainNtxId === oldMainNtxId) {
// old main context or subcontexts all have the new mainNtxId
c.mainNtxId = newMainNtxId;
}
});
2023-05-31 01:53:55 +08:00
}
2020-02-08 21:23:42 +01:00
this.tabsUpdate.scheduleUpdate();
}
async activateNextTabCommand() {
const activeMainNtxId = this.getActiveMainContext().ntxId;
2025-01-09 18:07:02 +02:00
const oldIdx = this.mainNoteContexts.findIndex((nc) => nc.ntxId === activeMainNtxId);
const newActiveNtxId = this.mainNoteContexts[oldIdx === this.mainNoteContexts.length - 1 ? 0 : oldIdx + 1].ntxId;
2021-07-20 13:29:11 +02:00
await this.activateNoteContext(newActiveNtxId);
}
async activatePreviousTabCommand() {
const activeMainNtxId = this.getActiveMainContext().ntxId;
2021-07-20 13:29:11 +02:00
2025-01-09 18:07:02 +02:00
const oldIdx = this.mainNoteContexts.findIndex((nc) => nc.ntxId === activeMainNtxId);
const newActiveNtxId = this.mainNoteContexts[oldIdx === 0 ? this.mainNoteContexts.length - 1 : oldIdx - 1].ntxId;
2021-07-20 13:29:11 +02:00
await this.activateNoteContext(newActiveNtxId);
}
async closeActiveTabCommand() {
await this.removeNoteContext(this.activeNtxId);
}
beforeUnloadEvent() {
2020-02-08 20:53:07 +01:00
this.tabsUpdate.updateNowIfNecessary();
return true; // don't block closing the tab, this metadata is not that important
2020-02-08 20:53:07 +01:00
}
openNewTabCommand() {
this.openAndActivateEmptyTab();
}
2023-01-08 13:58:51 +01:00
async closeAllTabsCommand() {
2025-01-09 18:07:02 +02:00
for (const ntxIdToRemove of this.mainNoteContexts.map((nc) => nc.ntxId)) {
await this.removeNoteContext(ntxIdToRemove);
2020-02-09 21:13:05 +01:00
}
}
async closeOtherTabsCommand({ ntxId }) {
2025-01-09 18:07:02 +02:00
for (const ntxIdToRemove of this.mainNoteContexts.map((nc) => nc.ntxId)) {
if (ntxIdToRemove !== ntxId) {
2021-05-22 12:35:41 +02:00
await this.removeNoteContext(ntxIdToRemove);
2020-02-09 21:13:05 +01:00
}
}
}
async closeRightTabsCommand({ ntxId }) {
2025-01-09 18:07:02 +02:00
const ntxIds = this.mainNoteContexts.map((nc) => nc.ntxId);
2024-11-01 20:02:22 +08:00
const index = ntxIds.indexOf(ntxId);
if (index !== -1) {
const idsToRemove = ntxIds.slice(index + 1);
for (const ntxIdToRemove of idsToRemove) {
await this.removeNoteContext(ntxIdToRemove);
2024-11-01 20:02:22 +08:00
}
}
}
async closeTabCommand({ ntxId }) {
2023-01-08 13:58:51 +01:00
await this.removeNoteContext(ntxId);
}
async moveTabToNewWindowCommand({ ntxId }) {
2025-01-09 18:07:02 +02:00
const { notePath, hoistedNoteId } = this.getNoteContextById(ntxId);
2020-04-26 23:11:52 +02:00
const removed = await this.removeNoteContext(ntxId);
2020-04-26 23:11:52 +02:00
if (removed) {
2025-01-09 18:07:02 +02:00
this.triggerCommand("openInWindow", { notePath, hoistedNoteId });
}
2020-04-26 23:11:52 +02:00
}
2020-11-24 22:32:22 +01:00
async copyTabToNewWindowCommand({ ntxId }) {
2025-01-09 18:07:02 +02:00
const { notePath, hoistedNoteId } = this.getNoteContextById(ntxId);
this.triggerCommand("openInWindow", { notePath, hoistedNoteId });
}
async reopenLastTabCommand() {
let closeLastEmptyTab = null;
await this.mutex.runExclusively(async () => {
if (this.recentlyClosedTabs.length === 0) {
return;
}
if (this.noteContexts.length === 1 && this.noteContexts[0].isEmpty()) {
// new empty tab is created after closing the last tab, this reverses the empty tab creation
closeLastEmptyTab = this.noteContexts[0];
}
const lastClosedTab = this.recentlyClosedTabs.pop();
const noteContexts = lastClosedTab.contexts;
2021-10-09 22:03:24 +02:00
for (const noteContext of noteContexts) {
this.child(noteContext);
2025-01-09 18:07:02 +02:00
await this.triggerEvent("newNoteContextCreated", { noteContext });
2021-10-09 22:03:24 +02:00
}
// restore last position of contexts stored in tab manager
const ntxsInOrder = [
...this.noteContexts.slice(0, lastClosedTab.position),
...this.noteContexts.slice(-noteContexts.length),
2025-01-09 18:07:02 +02:00
...this.noteContexts.slice(lastClosedTab.position, -noteContexts.length)
];
await this.noteContextReorderEvent({ ntxIdsInOrder: ntxsInOrder.map((nc) => nc.ntxId) });
2025-01-09 18:07:02 +02:00
let mainNtx = noteContexts.find((nc) => nc.isMainContext());
if (mainNtx) {
// reopened a tab, need to reorder new tab widget in tab row
await this.triggerEvent("contextsReopened", {
mainNtxId: mainNtx.ntxId,
2025-01-09 18:07:02 +02:00
tabPosition: ntxsInOrder.filter((nc) => nc.isMainContext()).findIndex((nc) => nc.ntxId === mainNtx.ntxId)
});
} else {
// reopened a single split, need to reorder the pane widget in split note container
await this.triggerEvent("contextsReopened", {
ntxId: ntxsInOrder[lastClosedTab.position].ntxId,
// this is safe since lastClosedTab.position can never be 0 in this case
afterNtxId: ntxsInOrder[lastClosedTab.position - 1].ntxId
});
}
2025-01-09 18:07:02 +02:00
const noteContextToActivate = noteContexts.length === 1 ? noteContexts[0] : noteContexts.find((nc) => nc.isMainContext());
2021-10-09 22:03:24 +02:00
await this.activateNoteContext(noteContextToActivate.ntxId);
2025-01-09 18:07:02 +02:00
await this.triggerEvent("noteSwitched", {
2021-10-09 22:03:24 +02:00
noteContext: noteContextToActivate,
notePath: noteContextToActivate.notePath
});
});
if (closeLastEmptyTab) {
await this.removeNoteContext(closeLastEmptyTab.ntxId);
}
}
2020-11-24 22:32:22 +01:00
hoistedNoteChangedEvent() {
this.tabsUpdate.scheduleUpdate();
}
async updateDocumentTitle(activeNoteContext) {
const titleFragments = [
2022-07-05 22:40:41 +02:00
// it helps to navigate in history if note title is included in the title
await activeNoteContext.getNavigationTitle(),
"TriliumNext Notes"
].filter(Boolean);
document.title = titleFragments.join(" - ");
}
async entitiesReloadedEvent({ loadResults }) {
const activeContext = this.getActiveContext();
if (activeContext && loadResults.isNoteReloaded(activeContext.noteId)) {
await this.updateDocumentTitle(activeContext);
}
}
async frocaReloadedEvent() {
const activeContext = this.getActiveContext();
if (activeContext) {
await this.updateDocumentTitle(activeContext);
}
}
}