mirror of
https://github.com/TriliumNext/Notes.git
synced 2025-07-29 02:52:27 +08:00
port tab manager to ts
This commit is contained in:
parent
8e0b9d17a4
commit
45a50f3aa1
@ -22,7 +22,6 @@ import type LoadResults from "../services/load_results.js";
|
|||||||
import type { Attribute } from "../services/attribute_parser.js";
|
import type { Attribute } from "../services/attribute_parser.js";
|
||||||
import type NoteTreeWidget from "../widgets/note_tree.js";
|
import type NoteTreeWidget from "../widgets/note_tree.js";
|
||||||
import type { default as NoteContext, GetTextEditorCallback } from "./note_context.js";
|
import type { default as NoteContext, GetTextEditorCallback } from "./note_context.js";
|
||||||
import type { ContextMenuEvent } from "../menus/context_menu.js";
|
|
||||||
import type TypeWidget from "../widgets/type_widgets/type_widget.js";
|
import type TypeWidget from "../widgets/type_widgets/type_widget.js";
|
||||||
|
|
||||||
interface Layout {
|
interface Layout {
|
||||||
@ -56,8 +55,8 @@ export interface ContextMenuCommandData extends CommandData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface NoteCommandData extends CommandData {
|
export interface NoteCommandData extends CommandData {
|
||||||
notePath?: string;
|
notePath?: string | null;
|
||||||
hoistedNoteId?: string;
|
hoistedNoteId?: string | null;
|
||||||
viewScope?: ViewScope;
|
viewScope?: ViewScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -324,7 +323,7 @@ type EventMappings = {
|
|||||||
ntxId: string | null;
|
ntxId: string | null;
|
||||||
};
|
};
|
||||||
contextsReopenedEvent: {
|
contextsReopenedEvent: {
|
||||||
mainNtxId: string;
|
mainNtxId: string | null;
|
||||||
tabPosition: number;
|
tabPosition: number;
|
||||||
};
|
};
|
||||||
noteDetailRefreshed: {
|
noteDetailRefreshed: {
|
||||||
@ -338,7 +337,7 @@ type EventMappings = {
|
|||||||
newNoteContextCreated: {
|
newNoteContextCreated: {
|
||||||
noteContext: NoteContext;
|
noteContext: NoteContext;
|
||||||
};
|
};
|
||||||
noteContextRemovedEvent: {
|
noteContextRemoved: {
|
||||||
ntxIds: string[];
|
ntxIds: string[];
|
||||||
};
|
};
|
||||||
exportSvg: {
|
exportSvg: {
|
||||||
@ -359,6 +358,7 @@ type EventMappings = {
|
|||||||
relationMapResetPanZoom: { ntxId: string | null | undefined };
|
relationMapResetPanZoom: { ntxId: string | null | undefined };
|
||||||
relationMapResetZoomIn: { ntxId: string | null | undefined };
|
relationMapResetZoomIn: { ntxId: string | null | undefined };
|
||||||
relationMapResetZoomOut: { ntxId: string | null | undefined };
|
relationMapResetZoomOut: { ntxId: string | null | undefined };
|
||||||
|
activeNoteChangedEvent: {};
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EventListener<T extends EventNames> = {
|
export type EventListener<T extends EventNames> = {
|
||||||
|
@ -66,12 +66,13 @@ export default class Entrypoints extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async toggleNoteHoistingCommand({ noteId = appContext.tabManager.getActiveContextNoteId() }) {
|
async toggleNoteHoistingCommand({ noteId = appContext.tabManager.getActiveContextNoteId() }) {
|
||||||
if (!noteId) {
|
const activeNoteContext = appContext.tabManager.getActiveContext();
|
||||||
|
|
||||||
|
if (!activeNoteContext || !noteId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const noteToHoist = await froca.getNote(noteId);
|
const noteToHoist = await froca.getNote(noteId);
|
||||||
const activeNoteContext = appContext.tabManager.getActiveContext();
|
|
||||||
|
|
||||||
if (noteToHoist?.noteId === activeNoteContext.hoistedNoteId) {
|
if (noteToHoist?.noteId === activeNoteContext.hoistedNoteId) {
|
||||||
await activeNoteContext.unhoist();
|
await activeNoteContext.unhoist();
|
||||||
@ -83,6 +84,11 @@ export default class Entrypoints extends Component {
|
|||||||
async hoistNoteCommand({ noteId }: { noteId: string }) {
|
async hoistNoteCommand({ noteId }: { noteId: string }) {
|
||||||
const noteContext = appContext.tabManager.getActiveContext();
|
const noteContext = appContext.tabManager.getActiveContext();
|
||||||
|
|
||||||
|
if (!noteContext) {
|
||||||
|
logError("hoistNoteCommand: noteContext is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (noteContext.hoistedNoteId !== noteId) {
|
if (noteContext.hoistedNoteId !== noteId) {
|
||||||
await noteContext.setHoistedNoteId(noteId);
|
await noteContext.setHoistedNoteId(noteId);
|
||||||
}
|
}
|
||||||
@ -174,7 +180,11 @@ export default class Entrypoints extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async runActiveNoteCommand() {
|
async runActiveNoteCommand() {
|
||||||
const { ntxId, note } = appContext.tabManager.getActiveContext();
|
const noteContext = appContext.tabManager.getActiveContext();
|
||||||
|
if (!noteContext) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { ntxId, note } = noteContext;
|
||||||
|
|
||||||
// ctrl+enter is also used elsewhere, so make sure we're running only when appropriate
|
// ctrl+enter is also used elsewhere, so make sure we're running only when appropriate
|
||||||
if (!note || note.type !== "code") {
|
if (!note || note.type !== "code") {
|
||||||
|
@ -4,23 +4,40 @@ import server from "../services/server.js";
|
|||||||
import options from "../services/options.js";
|
import options from "../services/options.js";
|
||||||
import froca from "../services/froca.js";
|
import froca from "../services/froca.js";
|
||||||
import treeService from "../services/tree.js";
|
import treeService from "../services/tree.js";
|
||||||
import utils from "../services/utils.js";
|
|
||||||
import NoteContext from "./note_context.js";
|
import NoteContext from "./note_context.js";
|
||||||
import appContext from "./app_context.js";
|
import appContext from "./app_context.js";
|
||||||
import Mutex from "../utils/mutex.js";
|
import Mutex from "../utils/mutex.js";
|
||||||
import linkService from "../services/link.js";
|
import linkService from "../services/link.js";
|
||||||
|
import type { EventData } from "./app_context.js";
|
||||||
|
import type FNote from "../entities/fnote.js";
|
||||||
|
|
||||||
|
interface TabState {
|
||||||
|
contexts: NoteContext[];
|
||||||
|
position: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface NoteContextState {
|
||||||
|
ntxId: string;
|
||||||
|
mainNtxId: string | null;
|
||||||
|
notePath: string | null;
|
||||||
|
hoistedNoteId: string;
|
||||||
|
active: boolean;
|
||||||
|
viewScope: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
export default class TabManager extends Component {
|
export default class TabManager extends Component {
|
||||||
|
public children: NoteContext[];
|
||||||
|
public mutex: Mutex;
|
||||||
|
public activeNtxId: string | null;
|
||||||
|
public recentlyClosedTabs: TabState[];
|
||||||
|
public tabsUpdate: SpacedUpdate;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
/** @property {NoteContext[]} */
|
|
||||||
this.children = [];
|
this.children = [];
|
||||||
this.mutex = new Mutex();
|
this.mutex = new Mutex();
|
||||||
|
|
||||||
this.activeNtxId = null;
|
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.recentlyClosedTabs = [];
|
||||||
|
|
||||||
this.tabsUpdate = new SpacedUpdate(async () => {
|
this.tabsUpdate = new SpacedUpdate(async () => {
|
||||||
@ -28,7 +45,9 @@ export default class TabManager extends Component {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const openNoteContexts = this.noteContexts.map((nc) => nc.getPojoState()).filter((t) => !!t);
|
const openNoteContexts = this.noteContexts
|
||||||
|
.map((nc) => nc.getPojoState())
|
||||||
|
.filter((t) => !!t);
|
||||||
|
|
||||||
await server.put("options", {
|
await server.put("options", {
|
||||||
openNoteContexts: JSON.stringify(openNoteContexts)
|
openNoteContexts: JSON.stringify(openNoteContexts)
|
||||||
@ -38,13 +57,11 @@ export default class TabManager extends Component {
|
|||||||
appContext.addBeforeUnloadListener(this);
|
appContext.addBeforeUnloadListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {NoteContext[]} */
|
get noteContexts(): NoteContext[] {
|
||||||
get noteContexts() {
|
|
||||||
return this.children;
|
return this.children;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @type {NoteContext[]} */
|
get mainNoteContexts(): NoteContext[] {
|
||||||
get mainNoteContexts() {
|
|
||||||
return this.noteContexts.filter((nc) => !nc.mainNtxId);
|
return this.noteContexts.filter((nc) => !nc.mainNtxId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,11 +70,12 @@ export default class TabManager extends Component {
|
|||||||
const noteContextsToOpen = (appContext.isMainWindow && options.getJson("openNoteContexts")) || [];
|
const noteContextsToOpen = (appContext.isMainWindow && options.getJson("openNoteContexts")) || [];
|
||||||
|
|
||||||
// preload all notes at once
|
// preload all notes at once
|
||||||
await froca.getNotes([...noteContextsToOpen.flatMap((tab) => [treeService.getNoteIdFromUrl(tab.notePath), tab.hoistedNoteId])], true);
|
await froca.getNotes([...noteContextsToOpen.flatMap((tab: NoteContextState) =>
|
||||||
|
[treeService.getNoteIdFromUrl(tab.notePath), tab.hoistedNoteId])], true);
|
||||||
|
|
||||||
const filteredNoteContexts = noteContextsToOpen.filter((openTab) => {
|
const filteredNoteContexts = noteContextsToOpen.filter((openTab: NoteContextState) => {
|
||||||
const noteId = treeService.getNoteIdFromUrl(openTab.notePath);
|
const noteId = treeService.getNoteIdFromUrl(openTab.notePath);
|
||||||
if (!(noteId in froca.notes)) {
|
if (noteId && !(noteId in froca.notes)) {
|
||||||
// note doesn't exist so don't try to open tab for it
|
// note doesn't exist so don't try to open tab for it
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -80,9 +98,10 @@ export default class TabManager extends Component {
|
|||||||
ntxId: parsedFromUrl.ntxId,
|
ntxId: parsedFromUrl.ntxId,
|
||||||
active: true,
|
active: true,
|
||||||
hoistedNoteId: parsedFromUrl.hoistedNoteId || "root",
|
hoistedNoteId: parsedFromUrl.hoistedNoteId || "root",
|
||||||
viewScope: parsedFromUrl.viewScope || {}
|
viewScope: parsedFromUrl.viewScope || {},
|
||||||
|
mainNtxId: null
|
||||||
});
|
});
|
||||||
} else if (!filteredNoteContexts.find((tab) => tab.active)) {
|
} else if (!filteredNoteContexts.find((tab: NoteContextState) => tab.active)) {
|
||||||
filteredNoteContexts[0].active = true;
|
filteredNoteContexts[0].active = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,21 +120,30 @@ export default class TabManager extends Component {
|
|||||||
// if there's a notePath in the URL, make sure it's open and active
|
// 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)
|
// (useful, for e.g., opening clipped notes from clipper or opening link in an extra window)
|
||||||
if (parsedFromUrl.notePath) {
|
if (parsedFromUrl.notePath) {
|
||||||
await appContext.tabManager.switchToNoteContext(parsedFromUrl.ntxId, parsedFromUrl.notePath, parsedFromUrl.viewScope, parsedFromUrl.hoistedNoteId);
|
await appContext.tabManager.switchToNoteContext(
|
||||||
|
parsedFromUrl.ntxId,
|
||||||
|
parsedFromUrl.notePath,
|
||||||
|
parsedFromUrl.viewScope,
|
||||||
|
parsedFromUrl.hoistedNoteId
|
||||||
|
);
|
||||||
} else if (parsedFromUrl.searchString) {
|
} else if (parsedFromUrl.searchString) {
|
||||||
await appContext.triggerCommand("searchNotes", {
|
await appContext.triggerCommand("searchNotes", {
|
||||||
searchString: parsedFromUrl.searchString
|
searchString: parsedFromUrl.searchString
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e: unknown) {
|
||||||
|
if (e instanceof Error) {
|
||||||
logError(`Loading note contexts '${options.get("openNoteContexts")}' failed: ${e.message} ${e.stack}`);
|
logError(`Loading note contexts '${options.get("openNoteContexts")}' failed: ${e.message} ${e.stack}`);
|
||||||
|
} else {
|
||||||
|
logError(`Loading note contexts '${options.get("openNoteContexts")}' failed: ${String(e)}`);
|
||||||
|
}
|
||||||
|
|
||||||
// try to recover
|
// try to recover
|
||||||
await this.openEmptyTab();
|
await this.openEmptyTab();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
noteSwitchedEvent({ noteContext }) {
|
noteSwitchedEvent({ noteContext }: EventData<"noteSwitched">) {
|
||||||
if (noteContext.isActive()) {
|
if (noteContext.isActive()) {
|
||||||
this.setCurrentNavigationStateToHash();
|
this.setCurrentNavigationStateToHash();
|
||||||
}
|
}
|
||||||
@ -135,10 +163,10 @@ export default class TabManager extends Component {
|
|||||||
const activeNoteContext = this.getActiveContext();
|
const activeNoteContext = this.getActiveContext();
|
||||||
this.updateDocumentTitle(activeNoteContext);
|
this.updateDocumentTitle(activeNoteContext);
|
||||||
|
|
||||||
this.triggerEvent("activeNoteChanged"); // trigger this even in on popstate event
|
this.triggerEvent("activeNoteChangedEvent", {}); // trigger this even in on popstate event
|
||||||
}
|
}
|
||||||
|
|
||||||
calculateHash() {
|
calculateHash(): string {
|
||||||
const activeNoteContext = this.getActiveContext();
|
const activeNoteContext = this.getActiveContext();
|
||||||
if (!activeNoteContext) {
|
if (!activeNoteContext) {
|
||||||
return "";
|
return "";
|
||||||
@ -152,21 +180,15 @@ export default class TabManager extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {NoteContext[]} */
|
getNoteContexts(): NoteContext[] {
|
||||||
getNoteContexts() {
|
|
||||||
return this.noteContexts;
|
return this.noteContexts;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
getMainNoteContexts(): NoteContext[] {
|
||||||
* Main context is essentially a tab (children are splits), so this returns tabs.
|
|
||||||
* @returns {NoteContext[]}
|
|
||||||
*/
|
|
||||||
getMainNoteContexts() {
|
|
||||||
return this.noteContexts.filter((nc) => nc.isMainContext());
|
return this.noteContexts.filter((nc) => nc.isMainContext());
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {NoteContext} */
|
getNoteContextById(ntxId: string | null): NoteContext {
|
||||||
getNoteContextById(ntxId) {
|
|
||||||
const noteContext = this.noteContexts.find((nc) => nc.ntxId === ntxId);
|
const noteContext = this.noteContexts.find((nc) => nc.ntxId === ntxId);
|
||||||
|
|
||||||
if (!noteContext) {
|
if (!noteContext) {
|
||||||
@ -176,58 +198,47 @@ export default class TabManager extends Component {
|
|||||||
return noteContext;
|
return noteContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
getActiveContext(): NoteContext | null {
|
||||||
* Get active context which represents the visible split with focus. Active context can, but doesn't have to be "main".
|
|
||||||
*
|
|
||||||
* @returns {NoteContext}
|
|
||||||
*/
|
|
||||||
getActiveContext() {
|
|
||||||
return this.activeNtxId ? this.getNoteContextById(this.activeNtxId) : null;
|
return this.activeNtxId ? this.getNoteContextById(this.activeNtxId) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
getActiveMainContext(): NoteContext | null {
|
||||||
* Get active main context which corresponds to the active tab.
|
|
||||||
*
|
|
||||||
* @returns {NoteContext}
|
|
||||||
*/
|
|
||||||
getActiveMainContext() {
|
|
||||||
return this.activeNtxId ? this.getNoteContextById(this.activeNtxId).getMainContext() : null;
|
return this.activeNtxId ? this.getNoteContextById(this.activeNtxId).getMainContext() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {string|null} */
|
getActiveContextNotePath(): string | null {
|
||||||
getActiveContextNotePath() {
|
|
||||||
const activeContext = this.getActiveContext();
|
const activeContext = this.getActiveContext();
|
||||||
return activeContext ? activeContext.notePath : null;
|
return activeContext?.notePath ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {FNote} */
|
getActiveContextNote(): FNote | null {
|
||||||
getActiveContextNote() {
|
|
||||||
const activeContext = this.getActiveContext();
|
const activeContext = this.getActiveContext();
|
||||||
return activeContext ? activeContext.note : null;
|
return activeContext ? activeContext.note : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {string|null} */
|
getActiveContextNoteId(): string | null {
|
||||||
getActiveContextNoteId() {
|
|
||||||
const activeNote = this.getActiveContextNote();
|
const activeNote = this.getActiveContextNote();
|
||||||
|
|
||||||
return activeNote ? activeNote.noteId : null;
|
return activeNote ? activeNote.noteId : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @returns {string|null} */
|
getActiveContextNoteType(): string | null {
|
||||||
getActiveContextNoteType() {
|
|
||||||
const activeNote = this.getActiveContextNote();
|
const activeNote = this.getActiveContextNote();
|
||||||
|
|
||||||
return activeNote ? activeNote.type : null;
|
return activeNote ? activeNote.type : null;
|
||||||
}
|
}
|
||||||
/** @returns {string|null} */
|
|
||||||
getActiveContextNoteMime() {
|
|
||||||
const activeNote = this.getActiveContextNote();
|
|
||||||
|
|
||||||
|
getActiveContextNoteMime(): string | null {
|
||||||
|
const activeNote = this.getActiveContextNote();
|
||||||
return activeNote ? activeNote.mime : null;
|
return activeNote ? activeNote.mime : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async switchToNoteContext(ntxId, notePath, viewScope = {}, hoistedNoteId = null) {
|
async switchToNoteContext(
|
||||||
const noteContext = this.noteContexts.find((nc) => nc.ntxId === ntxId) || (await this.openEmptyTab());
|
ntxId: string | null,
|
||||||
|
notePath: string,
|
||||||
|
viewScope: Record<string, any> = {},
|
||||||
|
hoistedNoteId: string | null = null
|
||||||
|
) {
|
||||||
|
const noteContext = this.noteContexts.find((nc) => nc.ntxId === ntxId) ||
|
||||||
|
await this.openEmptyTab();
|
||||||
|
|
||||||
await this.activateNoteContext(noteContext.ntxId);
|
await this.activateNoteContext(noteContext.ntxId);
|
||||||
|
|
||||||
@ -242,20 +253,21 @@ export default class TabManager extends Component {
|
|||||||
|
|
||||||
async openAndActivateEmptyTab() {
|
async openAndActivateEmptyTab() {
|
||||||
const noteContext = await this.openEmptyTab();
|
const noteContext = await this.openEmptyTab();
|
||||||
|
|
||||||
await this.activateNoteContext(noteContext.ntxId);
|
await this.activateNoteContext(noteContext.ntxId);
|
||||||
|
noteContext.setEmpty();
|
||||||
await noteContext.setEmpty();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async openEmptyTab(ntxId = null, hoistedNoteId = "root", mainNtxId = null) {
|
async openEmptyTab(
|
||||||
|
ntxId: string | null = null,
|
||||||
|
hoistedNoteId: string = "root",
|
||||||
|
mainNtxId: string | null = null
|
||||||
|
): Promise<NoteContext> {
|
||||||
const noteContext = new NoteContext(ntxId, hoistedNoteId, mainNtxId);
|
const noteContext = new NoteContext(ntxId, hoistedNoteId, mainNtxId);
|
||||||
|
|
||||||
const existingNoteContext = this.children.find((nc) => nc.ntxId === noteContext.ntxId);
|
const existingNoteContext = this.children.find((nc) => nc.ntxId === noteContext.ntxId);
|
||||||
|
|
||||||
if (existingNoteContext) {
|
if (existingNoteContext) {
|
||||||
await existingNoteContext.setHoistedNoteId(hoistedNoteId);
|
await existingNoteContext.setHoistedNoteId(hoistedNoteId);
|
||||||
|
|
||||||
return existingNoteContext;
|
return existingNoteContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -266,29 +278,40 @@ export default class TabManager extends Component {
|
|||||||
return noteContext;
|
return noteContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
async openInNewTab(targetNoteId, hoistedNoteId = null) {
|
async openInNewTab(targetNoteId: string, hoistedNoteId: string | null = null) {
|
||||||
const noteContext = await this.openEmptyTab(null, hoistedNoteId || this.getActiveContext().hoistedNoteId);
|
const noteContext = await this.openEmptyTab(
|
||||||
|
null,
|
||||||
|
hoistedNoteId || this.getActiveContext()?.hoistedNoteId
|
||||||
|
);
|
||||||
|
|
||||||
await noteContext.setNote(targetNoteId);
|
await noteContext.setNote(targetNoteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async openInSameTab(targetNoteId, hoistedNoteId = null) {
|
async openInSameTab(targetNoteId: string, hoistedNoteId: string | null = null) {
|
||||||
const activeContext = this.getActiveContext();
|
const activeContext = this.getActiveContext();
|
||||||
|
if (!activeContext) return;
|
||||||
|
|
||||||
await activeContext.setHoistedNoteId(hoistedNoteId || activeContext.hoistedNoteId);
|
await activeContext.setHoistedNoteId(hoistedNoteId || activeContext.hoistedNoteId);
|
||||||
await activeContext.setNote(targetNoteId);
|
await activeContext.setNote(targetNoteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async openTabWithNoteWithHoisting(
|
||||||
* If the requested notePath is within current note hoisting scope then keep the note hoisting also for the new tab.
|
notePath: string,
|
||||||
*/
|
opts: {
|
||||||
async openTabWithNoteWithHoisting(notePath, opts = {}) {
|
activate?: boolean | null;
|
||||||
|
ntxId?: string | null;
|
||||||
|
mainNtxId?: string | null;
|
||||||
|
hoistedNoteId?: string | null;
|
||||||
|
viewScope?: Record<string, any> | null;
|
||||||
|
} = {}
|
||||||
|
): Promise<NoteContext> {
|
||||||
const noteContext = this.getActiveContext();
|
const noteContext = this.getActiveContext();
|
||||||
let hoistedNoteId = "root";
|
let hoistedNoteId = "root";
|
||||||
|
|
||||||
if (noteContext) {
|
if (noteContext) {
|
||||||
const resolvedNotePath = await treeService.resolveNotePath(notePath, noteContext.hoistedNoteId);
|
const resolvedNotePath = await treeService.resolveNotePath(notePath, noteContext.hoistedNoteId);
|
||||||
|
|
||||||
if (resolvedNotePath.includes(noteContext.hoistedNoteId) || resolvedNotePath.includes("_hidden")) {
|
if (resolvedNotePath?.includes(noteContext.hoistedNoteId) || resolvedNotePath?.includes("_hidden")) {
|
||||||
hoistedNoteId = noteContext.hoistedNoteId;
|
hoistedNoteId = noteContext.hoistedNoteId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -298,7 +321,16 @@ export default class TabManager extends Component {
|
|||||||
return this.openContextWithNote(notePath, opts);
|
return this.openContextWithNote(notePath, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
async openContextWithNote(notePath, opts = {}) {
|
async openContextWithNote(
|
||||||
|
notePath: string | null,
|
||||||
|
opts: {
|
||||||
|
activate?: boolean | null;
|
||||||
|
ntxId?: string | null;
|
||||||
|
mainNtxId?: string | null;
|
||||||
|
hoistedNoteId?: string | null;
|
||||||
|
viewScope?: Record<string, any> | null;
|
||||||
|
} = {}
|
||||||
|
): Promise<NoteContext> {
|
||||||
const activate = !!opts.activate;
|
const activate = !!opts.activate;
|
||||||
const ntxId = opts.ntxId || null;
|
const ntxId = opts.ntxId || null;
|
||||||
const mainNtxId = opts.mainNtxId || null;
|
const mainNtxId = opts.mainNtxId || null;
|
||||||
@ -315,10 +347,10 @@ export default class TabManager extends Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activate) {
|
if (activate && noteContext.notePath) {
|
||||||
this.activateNoteContext(noteContext.ntxId, false);
|
this.activateNoteContext(noteContext.ntxId, false);
|
||||||
|
|
||||||
await this.triggerEvent("noteSwitchedAndActivated", {
|
await this.triggerEvent("noteSwitchedAndActivatedEvent", {
|
||||||
noteContext,
|
noteContext,
|
||||||
notePath: noteContext.notePath // resolved note path
|
notePath: noteContext.notePath // resolved note path
|
||||||
});
|
});
|
||||||
@ -327,21 +359,24 @@ export default class TabManager extends Component {
|
|||||||
return noteContext;
|
return noteContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
async activateOrOpenNote(noteId) {
|
async activateOrOpenNote(noteId: string) {
|
||||||
for (const noteContext of this.getNoteContexts()) {
|
for (const noteContext of this.getNoteContexts()) {
|
||||||
if (noteContext.note && noteContext.note.noteId === noteId) {
|
if (noteContext.note && noteContext.note.noteId === noteId) {
|
||||||
this.activateNoteContext(noteContext.ntxId);
|
this.activateNoteContext(noteContext.ntxId);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if no tab with this note has been found we'll create new tab
|
// if no tab with this note has been found we'll create new tab
|
||||||
|
|
||||||
await this.openContextWithNote(noteId, { activate: true });
|
await this.openContextWithNote(noteId, { activate: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
async activateNoteContext(ntxId, triggerEvent = true) {
|
async activateNoteContext(ntxId: string | null, triggerEvent: boolean = true) {
|
||||||
|
if (!ntxId) {
|
||||||
|
logError("activateNoteContext: ntxId is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (ntxId === this.activeNtxId) {
|
if (ntxId === this.activeNtxId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -359,11 +394,7 @@ export default class TabManager extends Component {
|
|||||||
this.setCurrentNavigationStateToHash();
|
this.setCurrentNavigationStateToHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async removeNoteContext(ntxId: string | null) {
|
||||||
* @param ntxId
|
|
||||||
* @returns {Promise<boolean>} true if note context has been removed, false otherwise
|
|
||||||
*/
|
|
||||||
async removeNoteContext(ntxId) {
|
|
||||||
// removing note context is an async process which can take some time, if users presses CTRL-W quickly, two
|
// 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.
|
// close events could interleave which would then lead to attempting to activate already removed context.
|
||||||
return await this.mutex.runExclusively(async () => {
|
return await this.mutex.runExclusively(async () => {
|
||||||
@ -373,7 +404,7 @@ export default class TabManager extends Component {
|
|||||||
noteContextToRemove = this.getNoteContextById(ntxId);
|
noteContextToRemove = this.getNoteContextById(ntxId);
|
||||||
} catch {
|
} catch {
|
||||||
// note context not found
|
// note context not found
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (noteContextToRemove.isMainContext()) {
|
if (noteContextToRemove.isMainContext()) {
|
||||||
@ -383,7 +414,7 @@ export default class TabManager extends Component {
|
|||||||
if (noteContextToRemove.isEmpty()) {
|
if (noteContextToRemove.isEmpty()) {
|
||||||
// this is already the empty note context, no point in closing it and replacing with another
|
// this is already the empty note context, no point in closing it and replacing with another
|
||||||
// empty tab
|
// empty tab
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.openEmptyTab();
|
await this.openEmptyTab();
|
||||||
@ -399,7 +430,7 @@ export default class TabManager extends Component {
|
|||||||
const noteContextsToRemove = noteContextToRemove.getSubContexts();
|
const noteContextsToRemove = noteContextToRemove.getSubContexts();
|
||||||
const ntxIdsToRemove = noteContextsToRemove.map((nc) => nc.ntxId);
|
const ntxIdsToRemove = noteContextsToRemove.map((nc) => nc.ntxId);
|
||||||
|
|
||||||
await this.triggerEvent("beforeNoteContextRemove", { ntxIds: ntxIdsToRemove });
|
await this.triggerEvent("beforeNoteContextRemove", { ntxIds: ntxIdsToRemove.filter((id) => id !== null) });
|
||||||
|
|
||||||
if (!noteContextToRemove.isMainContext()) {
|
if (!noteContextToRemove.isMainContext()) {
|
||||||
const siblings = noteContextToRemove.getMainContext().getSubContexts();
|
const siblings = noteContextToRemove.getMainContext().getSubContexts();
|
||||||
@ -421,12 +452,10 @@ export default class TabManager extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.removeNoteContexts(noteContextsToRemove);
|
this.removeNoteContexts(noteContextsToRemove);
|
||||||
|
|
||||||
return true;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
removeNoteContexts(noteContextsToRemove) {
|
removeNoteContexts(noteContextsToRemove: NoteContext[]) {
|
||||||
const ntxIdsToRemove = noteContextsToRemove.map((nc) => nc.ntxId);
|
const ntxIdsToRemove = noteContextsToRemove.map((nc) => nc.ntxId);
|
||||||
|
|
||||||
const position = this.noteContexts.findIndex((nc) => ntxIdsToRemove.includes(nc.ntxId));
|
const position = this.noteContexts.findIndex((nc) => ntxIdsToRemove.includes(nc.ntxId));
|
||||||
@ -435,12 +464,12 @@ export default class TabManager extends Component {
|
|||||||
|
|
||||||
this.addToRecentlyClosedTabs(noteContextsToRemove, position);
|
this.addToRecentlyClosedTabs(noteContextsToRemove, position);
|
||||||
|
|
||||||
this.triggerEvent("noteContextRemoved", { ntxIds: ntxIdsToRemove });
|
this.triggerEvent("noteContextRemoved", { ntxIds: ntxIdsToRemove.filter((id) => id !== null) });
|
||||||
|
|
||||||
this.tabsUpdate.scheduleUpdate();
|
this.tabsUpdate.scheduleUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
addToRecentlyClosedTabs(noteContexts, position) {
|
addToRecentlyClosedTabs(noteContexts: NoteContext[], position: number) {
|
||||||
if (noteContexts.length === 1 && noteContexts[0].isEmpty()) {
|
if (noteContexts.length === 1 && noteContexts[0].isEmpty()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -448,26 +477,42 @@ export default class TabManager extends Component {
|
|||||||
this.recentlyClosedTabs.push({ contexts: noteContexts, position: position });
|
this.recentlyClosedTabs.push({ contexts: noteContexts, position: position });
|
||||||
}
|
}
|
||||||
|
|
||||||
tabReorderEvent({ ntxIdsInOrder }) {
|
tabReorderEvent({ ntxIdsInOrder }: { ntxIdsInOrder: string[] }) {
|
||||||
const order = {};
|
const order: Record<string, number> = {};
|
||||||
|
|
||||||
let i = 0;
|
let i = 0;
|
||||||
|
|
||||||
for (const ntxId of ntxIdsInOrder) {
|
for (const ntxId of ntxIdsInOrder) {
|
||||||
for (const noteContext of this.getNoteContextById(ntxId).getSubContexts()) {
|
for (const noteContext of this.getNoteContextById(ntxId).getSubContexts()) {
|
||||||
|
if (noteContext.ntxId) {
|
||||||
order[noteContext.ntxId] = i++;
|
order[noteContext.ntxId] = i++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.children.sort((a, b) => (order[a.ntxId] < order[b.ntxId] ? -1 : 1));
|
this.children.sort((a, b) => {
|
||||||
|
if (!a.ntxId || !b.ntxId) return 0;
|
||||||
|
return (order[a.ntxId] ?? 0) < (order[b.ntxId] ?? 0) ? -1 : 1;
|
||||||
|
});
|
||||||
|
|
||||||
this.tabsUpdate.scheduleUpdate();
|
this.tabsUpdate.scheduleUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
noteContextReorderEvent({ ntxIdsInOrder, oldMainNtxId, newMainNtxId }) {
|
noteContextReorderEvent({
|
||||||
|
ntxIdsInOrder,
|
||||||
|
oldMainNtxId,
|
||||||
|
newMainNtxId
|
||||||
|
}: {
|
||||||
|
ntxIdsInOrder: string[];
|
||||||
|
oldMainNtxId?: string;
|
||||||
|
newMainNtxId?: string;
|
||||||
|
}) {
|
||||||
const order = Object.fromEntries(ntxIdsInOrder.map((v, i) => [v, i]));
|
const order = Object.fromEntries(ntxIdsInOrder.map((v, i) => [v, i]));
|
||||||
|
|
||||||
this.children.sort((a, b) => (order[a.ntxId] < order[b.ntxId] ? -1 : 1));
|
this.children.sort((a, b) => {
|
||||||
|
if (!a.ntxId || !b.ntxId) return 0;
|
||||||
|
return (order[a.ntxId] ?? 0) < (order[b.ntxId] ?? 0) ? -1 : 1;
|
||||||
|
});
|
||||||
|
|
||||||
if (oldMainNtxId && newMainNtxId) {
|
if (oldMainNtxId && newMainNtxId) {
|
||||||
this.children.forEach((c) => {
|
this.children.forEach((c) => {
|
||||||
@ -485,7 +530,8 @@ export default class TabManager extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async activateNextTabCommand() {
|
async activateNextTabCommand() {
|
||||||
const activeMainNtxId = this.getActiveMainContext().ntxId;
|
const activeMainNtxId = this.getActiveMainContext()?.ntxId;
|
||||||
|
if (!activeMainNtxId) return;
|
||||||
|
|
||||||
const oldIdx = this.mainNoteContexts.findIndex((nc) => nc.ntxId === activeMainNtxId);
|
const oldIdx = this.mainNoteContexts.findIndex((nc) => nc.ntxId === activeMainNtxId);
|
||||||
const newActiveNtxId = this.mainNoteContexts[oldIdx === this.mainNoteContexts.length - 1 ? 0 : oldIdx + 1].ntxId;
|
const newActiveNtxId = this.mainNoteContexts[oldIdx === this.mainNoteContexts.length - 1 ? 0 : oldIdx + 1].ntxId;
|
||||||
@ -494,7 +540,8 @@ export default class TabManager extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async activatePreviousTabCommand() {
|
async activatePreviousTabCommand() {
|
||||||
const activeMainNtxId = this.getActiveMainContext().ntxId;
|
const activeMainNtxId = this.getActiveMainContext()?.ntxId;
|
||||||
|
if (!activeMainNtxId) return;
|
||||||
|
|
||||||
const oldIdx = this.mainNoteContexts.findIndex((nc) => nc.ntxId === activeMainNtxId);
|
const oldIdx = this.mainNoteContexts.findIndex((nc) => nc.ntxId === activeMainNtxId);
|
||||||
const newActiveNtxId = this.mainNoteContexts[oldIdx === 0 ? this.mainNoteContexts.length - 1 : oldIdx - 1].ntxId;
|
const newActiveNtxId = this.mainNoteContexts[oldIdx === 0 ? this.mainNoteContexts.length - 1 : oldIdx - 1].ntxId;
|
||||||
@ -503,12 +550,13 @@ export default class TabManager extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async closeActiveTabCommand() {
|
async closeActiveTabCommand() {
|
||||||
|
if (this.activeNtxId) {
|
||||||
await this.removeNoteContext(this.activeNtxId);
|
await this.removeNoteContext(this.activeNtxId);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
beforeUnloadEvent() {
|
beforeUnloadEvent(): boolean {
|
||||||
this.tabsUpdate.updateNowIfNecessary();
|
this.tabsUpdate.updateNowIfNecessary();
|
||||||
|
|
||||||
return true; // don't block closing the tab, this metadata is not that important
|
return true; // don't block closing the tab, this metadata is not that important
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -518,35 +566,39 @@ export default class TabManager extends Component {
|
|||||||
|
|
||||||
async closeAllTabsCommand() {
|
async closeAllTabsCommand() {
|
||||||
for (const ntxIdToRemove of this.mainNoteContexts.map((nc) => nc.ntxId)) {
|
for (const ntxIdToRemove of this.mainNoteContexts.map((nc) => nc.ntxId)) {
|
||||||
|
if (ntxIdToRemove) {
|
||||||
await this.removeNoteContext(ntxIdToRemove);
|
await this.removeNoteContext(ntxIdToRemove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async closeOtherTabsCommand({ ntxId }) {
|
async closeOtherTabsCommand({ ntxId }: { ntxId: string }) {
|
||||||
for (const ntxIdToRemove of this.mainNoteContexts.map((nc) => nc.ntxId)) {
|
for (const ntxIdToRemove of this.mainNoteContexts.map((nc) => nc.ntxId)) {
|
||||||
if (ntxIdToRemove !== ntxId) {
|
if (ntxIdToRemove && ntxIdToRemove !== ntxId) {
|
||||||
await this.removeNoteContext(ntxIdToRemove);
|
await this.removeNoteContext(ntxIdToRemove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async closeRightTabsCommand({ ntxId }) {
|
async closeRightTabsCommand({ ntxId }: { ntxId: string }) {
|
||||||
const ntxIds = this.mainNoteContexts.map((nc) => nc.ntxId);
|
const ntxIds = this.mainNoteContexts.map((nc) => nc.ntxId);
|
||||||
const index = ntxIds.indexOf(ntxId);
|
const index = ntxIds.indexOf(ntxId);
|
||||||
|
|
||||||
if (index !== -1) {
|
if (index !== -1) {
|
||||||
const idsToRemove = ntxIds.slice(index + 1);
|
const idsToRemove = ntxIds.slice(index + 1);
|
||||||
for (const ntxIdToRemove of idsToRemove) {
|
for (const ntxIdToRemove of idsToRemove) {
|
||||||
|
if (ntxIdToRemove) {
|
||||||
await this.removeNoteContext(ntxIdToRemove);
|
await this.removeNoteContext(ntxIdToRemove);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async closeTabCommand({ ntxId }) {
|
async closeTabCommand({ ntxId }: { ntxId: string }) {
|
||||||
await this.removeNoteContext(ntxId);
|
await this.removeNoteContext(ntxId);
|
||||||
}
|
}
|
||||||
|
|
||||||
async moveTabToNewWindowCommand({ ntxId }) {
|
async moveTabToNewWindowCommand({ ntxId }: { ntxId: string }) {
|
||||||
const { notePath, hoistedNoteId } = this.getNoteContextById(ntxId);
|
const { notePath, hoistedNoteId } = this.getNoteContextById(ntxId);
|
||||||
|
|
||||||
const removed = await this.removeNoteContext(ntxId);
|
const removed = await this.removeNoteContext(ntxId);
|
||||||
@ -556,17 +608,16 @@ export default class TabManager extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async copyTabToNewWindowCommand({ ntxId }) {
|
async copyTabToNewWindowCommand({ ntxId }: { ntxId: string }) {
|
||||||
const { notePath, hoistedNoteId } = this.getNoteContextById(ntxId);
|
const { notePath, hoistedNoteId } = this.getNoteContextById(ntxId);
|
||||||
this.triggerCommand("openInWindow", { notePath, hoistedNoteId });
|
this.triggerCommand("openInWindow", { notePath, hoistedNoteId });
|
||||||
}
|
}
|
||||||
|
|
||||||
async reopenLastTabCommand() {
|
async reopenLastTabCommand() {
|
||||||
let closeLastEmptyTab = null;
|
const closeLastEmptyTab: NoteContext | undefined = await this.mutex.runExclusively(async () => {
|
||||||
|
let closeLastEmptyTab
|
||||||
await this.mutex.runExclusively(async () => {
|
|
||||||
if (this.recentlyClosedTabs.length === 0) {
|
if (this.recentlyClosedTabs.length === 0) {
|
||||||
return;
|
return closeLastEmptyTab;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.noteContexts.length === 1 && this.noteContexts[0].isEmpty()) {
|
if (this.noteContexts.length === 1 && this.noteContexts[0].isEmpty()) {
|
||||||
@ -575,6 +626,8 @@ export default class TabManager extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const lastClosedTab = this.recentlyClosedTabs.pop();
|
const lastClosedTab = this.recentlyClosedTabs.pop();
|
||||||
|
if (!lastClosedTab) return closeLastEmptyTab;
|
||||||
|
|
||||||
const noteContexts = lastClosedTab.contexts;
|
const noteContexts = lastClosedTab.contexts;
|
||||||
|
|
||||||
for (const noteContext of noteContexts) {
|
for (const noteContext of noteContexts) {
|
||||||
@ -589,25 +642,26 @@ export default class TabManager extends Component {
|
|||||||
...this.noteContexts.slice(-noteContexts.length),
|
...this.noteContexts.slice(-noteContexts.length),
|
||||||
...this.noteContexts.slice(lastClosedTab.position, -noteContexts.length)
|
...this.noteContexts.slice(lastClosedTab.position, -noteContexts.length)
|
||||||
];
|
];
|
||||||
await this.noteContextReorderEvent({ ntxIdsInOrder: ntxsInOrder.map((nc) => nc.ntxId) });
|
this.noteContextReorderEvent({ ntxIdsInOrder: ntxsInOrder.map((nc) => nc.ntxId).filter((id) => id !== null) });
|
||||||
|
|
||||||
let mainNtx = noteContexts.find((nc) => nc.isMainContext());
|
let mainNtx = noteContexts.find((nc) => nc.isMainContext());
|
||||||
if (mainNtx) {
|
if (mainNtx) {
|
||||||
// reopened a tab, need to reorder new tab widget in tab row
|
// reopened a tab, need to reorder new tab widget in tab row
|
||||||
await this.triggerEvent("contextsReopened", {
|
await this.triggerEvent("contextsReopenedEvent", {
|
||||||
mainNtxId: mainNtx.ntxId,
|
mainNtxId: mainNtx.ntxId,
|
||||||
tabPosition: ntxsInOrder.filter((nc) => nc.isMainContext()).findIndex((nc) => nc.ntxId === mainNtx.ntxId)
|
tabPosition: ntxsInOrder.filter((nc) => nc.isMainContext()).findIndex((nc) => nc.ntxId === mainNtx.ntxId)
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
// reopened a single split, need to reorder the pane widget in split note container
|
// reopened a single split, need to reorder the pane widget in split note container
|
||||||
await this.triggerEvent("contextsReopened", {
|
await this.triggerEvent("contextsReopenedEvent", {
|
||||||
ntxId: ntxsInOrder[lastClosedTab.position].ntxId,
|
mainNtxId: ntxsInOrder[lastClosedTab.position].ntxId,
|
||||||
// this is safe since lastClosedTab.position can never be 0 in this case
|
// this is safe since lastClosedTab.position can never be 0 in this case
|
||||||
afterNtxId: ntxsInOrder[lastClosedTab.position - 1].ntxId
|
tabPosition: lastClosedTab.position - 1
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const noteContextToActivate = noteContexts.length === 1 ? noteContexts[0] : noteContexts.find((nc) => nc.isMainContext());
|
const noteContextToActivate = noteContexts.length === 1 ? noteContexts[0] : noteContexts.find((nc) => nc.isMainContext());
|
||||||
|
if (!noteContextToActivate) return closeLastEmptyTab;
|
||||||
|
|
||||||
await this.activateNoteContext(noteContextToActivate.ntxId);
|
await this.activateNoteContext(noteContextToActivate.ntxId);
|
||||||
|
|
||||||
@ -615,6 +669,7 @@ export default class TabManager extends Component {
|
|||||||
noteContext: noteContextToActivate,
|
noteContext: noteContextToActivate,
|
||||||
notePath: noteContextToActivate.notePath
|
notePath: noteContextToActivate.notePath
|
||||||
});
|
});
|
||||||
|
return closeLastEmptyTab;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (closeLastEmptyTab) {
|
if (closeLastEmptyTab) {
|
||||||
@ -626,7 +681,9 @@ export default class TabManager extends Component {
|
|||||||
this.tabsUpdate.scheduleUpdate();
|
this.tabsUpdate.scheduleUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
async updateDocumentTitle(activeNoteContext) {
|
async updateDocumentTitle(activeNoteContext: NoteContext | null) {
|
||||||
|
if (!activeNoteContext) return;
|
||||||
|
|
||||||
const titleFragments = [
|
const titleFragments = [
|
||||||
// it helps to navigate in history if note title is included in the title
|
// it helps to navigate in history if note title is included in the title
|
||||||
await activeNoteContext.getNavigationTitle(),
|
await activeNoteContext.getNavigationTitle(),
|
||||||
@ -636,7 +693,7 @@ export default class TabManager extends Component {
|
|||||||
document.title = titleFragments.join(" - ");
|
document.title = titleFragments.join(" - ");
|
||||||
}
|
}
|
||||||
|
|
||||||
async entitiesReloadedEvent({ loadResults }) {
|
async entitiesReloadedEvent({ loadResults }: EventData<"entitiesReloaded">) {
|
||||||
const activeContext = this.getActiveContext();
|
const activeContext = this.getActiveContext();
|
||||||
|
|
||||||
if (activeContext && loadResults.isNoteReloaded(activeContext.noteId)) {
|
if (activeContext && loadResults.isNoteReloaded(activeContext.noteId)) {
|
||||||
@ -646,7 +703,6 @@ export default class TabManager extends Component {
|
|||||||
|
|
||||||
async frocaReloadedEvent() {
|
async frocaReloadedEvent() {
|
||||||
const activeContext = this.getActiveContext();
|
const activeContext = this.getActiveContext();
|
||||||
|
|
||||||
if (activeContext) {
|
if (activeContext) {
|
||||||
await this.updateDocumentTitle(activeContext);
|
await this.updateDocumentTitle(activeContext);
|
||||||
}
|
}
|
@ -22,13 +22,19 @@ function getItems(): MenuItem<CommandNames>[] {
|
|||||||
|
|
||||||
function handleLinkContextMenuItem(command: string | undefined, notePath: string, viewScope = {}, hoistedNoteId: string | null = null) {
|
function handleLinkContextMenuItem(command: string | undefined, notePath: string, viewScope = {}, hoistedNoteId: string | null = null) {
|
||||||
if (!hoistedNoteId) {
|
if (!hoistedNoteId) {
|
||||||
hoistedNoteId = appContext.tabManager.getActiveContext().hoistedNoteId;
|
hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId ?? null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (command === "openNoteInNewTab") {
|
if (command === "openNoteInNewTab") {
|
||||||
appContext.tabManager.openContextWithNote(notePath, { hoistedNoteId, viewScope });
|
appContext.tabManager.openContextWithNote(notePath, { hoistedNoteId, viewScope });
|
||||||
} else if (command === "openNoteInNewSplit") {
|
} else if (command === "openNoteInNewSplit") {
|
||||||
const subContexts = appContext.tabManager.getActiveContext().getSubContexts();
|
const subContexts = appContext.tabManager.getActiveContext()?.getSubContexts();
|
||||||
|
|
||||||
|
if (!subContexts) {
|
||||||
|
logError("subContexts is null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const { ntxId } = subContexts[subContexts.length - 1];
|
const { ntxId } = subContexts[subContexts.length - 1];
|
||||||
|
|
||||||
appContext.triggerCommand("openNewNoteSplit", { ntxId, notePath, hoistedNoteId, viewScope });
|
appContext.triggerCommand("openNewNoteSplit", { ntxId, notePath, hoistedNoteId, viewScope });
|
||||||
|
@ -288,11 +288,15 @@ function goToLinkExt(evt: MouseEvent | JQuery.ClickEvent | JQuery.MouseDownEvent
|
|||||||
|
|
||||||
const noteContext = ntxId ? appContext.tabManager.getNoteContextById(ntxId) : appContext.tabManager.getActiveContext();
|
const noteContext = ntxId ? appContext.tabManager.getNoteContextById(ntxId) : appContext.tabManager.getActiveContext();
|
||||||
|
|
||||||
|
if (noteContext) {
|
||||||
noteContext.setNote(notePath, { viewScope }).then(() => {
|
noteContext.setNote(notePath, { viewScope }).then(() => {
|
||||||
if (noteContext !== appContext.tabManager.getActiveContext()) {
|
if (noteContext !== appContext.tabManager.getActiveContext()) {
|
||||||
appContext.tabManager.activateNoteContext(noteContext.ntxId);
|
appContext.tabManager.activateNoteContext(noteContext.ntxId);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
appContext.tabManager.openContextWithNote(notePath, { viewScope, activate: true });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (hrefLink) {
|
} else if (hrefLink) {
|
||||||
const withinEditLink = $link?.hasClass("ck-link-actions__preview");
|
const withinEditLink = $link?.hasClass("ck-link-actions__preview");
|
||||||
|
@ -138,7 +138,7 @@ function getParentProtectedStatus(node: Fancytree.FancytreeNode) {
|
|||||||
return hoistedNoteService.isHoistedNode(node) ? false : node.getParent().data.isProtected;
|
return hoistedNoteService.isHoistedNode(node) ? false : node.getParent().data.isProtected;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNoteIdFromUrl(urlOrNotePath: string | undefined) {
|
function getNoteIdFromUrl(urlOrNotePath: string | null | undefined) {
|
||||||
if (!urlOrNotePath) {
|
if (!urlOrNotePath) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -16,7 +16,7 @@ export default class Mutex {
|
|||||||
return newPromise;
|
return newPromise;
|
||||||
}
|
}
|
||||||
|
|
||||||
async runExclusively(cb: () => Promise<void>) {
|
async runExclusively(cb: () => Promise<any>) {
|
||||||
const unlock = await this.lock();
|
const unlock = await this.lock();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -63,7 +63,7 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
|
|||||||
hoistedNoteId?: string;
|
hoistedNoteId?: string;
|
||||||
viewScope?: any;
|
viewScope?: any;
|
||||||
}) {
|
}) {
|
||||||
const mainNtxId = appContext.tabManager.getActiveMainContext().ntxId;
|
const mainNtxId = appContext.tabManager.getActiveMainContext()?.ntxId;
|
||||||
|
|
||||||
if (!mainNtxId) {
|
if (!mainNtxId) {
|
||||||
logError("empty mainNtxId!");
|
logError("empty mainNtxId!");
|
||||||
@ -76,7 +76,7 @@ export default class SplitNoteContainer extends FlexContainer<SplitNoteWidget> {
|
|||||||
ntxId = mainNtxId;
|
ntxId = mainNtxId;
|
||||||
}
|
}
|
||||||
|
|
||||||
hoistedNoteId = hoistedNoteId || appContext.tabManager.getActiveContext().hoistedNoteId;
|
hoistedNoteId = hoistedNoteId || appContext.tabManager.getActiveContext()?.hoistedNoteId;
|
||||||
|
|
||||||
const noteContext = await appContext.tabManager.openEmptyTab(null, hoistedNoteId, mainNtxId);
|
const noteContext = await appContext.tabManager.openEmptyTab(null, hoistedNoteId, mainNtxId);
|
||||||
|
|
||||||
|
@ -1,10 +1,10 @@
|
|||||||
import Draggabilly, { type DraggabillyCallback, type MoveVector } from "draggabilly";
|
import Draggabilly, { type MoveVector } from "draggabilly";
|
||||||
import { t } from "../services/i18n.js";
|
import { t } from "../services/i18n.js";
|
||||||
import BasicWidget from "./basic_widget.js";
|
import BasicWidget from "./basic_widget.js";
|
||||||
import contextMenu from "../menus/context_menu.js";
|
import contextMenu from "../menus/context_menu.js";
|
||||||
import utils from "../services/utils.js";
|
import utils from "../services/utils.js";
|
||||||
import keyboardActionService from "../services/keyboard_actions.js";
|
import keyboardActionService from "../services/keyboard_actions.js";
|
||||||
import appContext, { type CommandData, type CommandListenerData, type EventData } from "../components/app_context.js";
|
import appContext, { type CommandListenerData, type EventData } from "../components/app_context.js";
|
||||||
import froca from "../services/froca.js";
|
import froca from "../services/froca.js";
|
||||||
import attributeService from "../services/attributes.js";
|
import attributeService from "../services/attributes.js";
|
||||||
import type NoteContext from "../components/note_context.js";
|
import type NoteContext from "../components/note_context.js";
|
||||||
@ -419,13 +419,13 @@ export default class TabRowWidget extends BasicWidget {
|
|||||||
closeActiveTabCommand({ $el }: CommandListenerData<"closeActiveTab">) {
|
closeActiveTabCommand({ $el }: CommandListenerData<"closeActiveTab">) {
|
||||||
const ntxId = $el.closest(".note-tab").attr("data-ntx-id");
|
const ntxId = $el.closest(".note-tab").attr("data-ntx-id");
|
||||||
|
|
||||||
appContext.tabManager.removeNoteContext(ntxId);
|
appContext.tabManager.removeNoteContext(ntxId ?? null);
|
||||||
}
|
}
|
||||||
|
|
||||||
setTabCloseEvent($tab: JQuery<HTMLElement>) {
|
setTabCloseEvent($tab: JQuery<HTMLElement>) {
|
||||||
$tab.on("mousedown", (e) => {
|
$tab.on("mousedown", (e) => {
|
||||||
if (e.which === 2) {
|
if (e.which === 2) {
|
||||||
appContext.tabManager.removeNoteContext($tab.attr("data-ntx-id"));
|
appContext.tabManager.removeNoteContext($tab.attr("data-ntx-id") ?? null);
|
||||||
|
|
||||||
return true; // event has been handled
|
return true; // event has been handled
|
||||||
}
|
}
|
||||||
@ -494,7 +494,8 @@ export default class TabRowWidget extends BasicWidget {
|
|||||||
return $tab.attr("data-ntx-id");
|
return $tab.attr("data-ntx-id");
|
||||||
}
|
}
|
||||||
|
|
||||||
noteContextRemovedEvent({ ntxIds }: EventData<"noteContextRemovedEvent">) {
|
noteContextRemovedEvent({ ntxIds }: EventData<"noteContextRemoved">) {
|
||||||
|
console.log(ntxIds);
|
||||||
for (const ntxId of ntxIds) {
|
for (const ntxId of ntxIds) {
|
||||||
this.removeTab(ntxId);
|
this.removeTab(ntxId);
|
||||||
}
|
}
|
||||||
@ -650,7 +651,7 @@ export default class TabRowWidget extends BasicWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
contextsReopenedEvent({ mainNtxId, tabPosition }: EventData<"contextsReopenedEvent">) {
|
contextsReopenedEvent({ mainNtxId, tabPosition }: EventData<"contextsReopenedEvent">) {
|
||||||
if (mainNtxId === undefined || tabPosition === undefined) {
|
if (!mainNtxId || !tabPosition) {
|
||||||
// no tab reopened
|
// no tab reopened
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -748,7 +749,7 @@ export default class TabRowWidget extends BasicWidget {
|
|||||||
hoistedNoteChangedEvent({ ntxId }: EventData<"hoistedNoteChanged">) {
|
hoistedNoteChangedEvent({ ntxId }: EventData<"hoistedNoteChanged">) {
|
||||||
const $tab = this.getTabById(ntxId);
|
const $tab = this.getTabById(ntxId);
|
||||||
|
|
||||||
if ($tab) {
|
if ($tab && ntxId) {
|
||||||
const noteContext = appContext.tabManager.getNoteContextById(ntxId);
|
const noteContext = appContext.tabManager.getNoteContextById(ntxId);
|
||||||
|
|
||||||
this.updateTab($tab, noteContext);
|
this.updateTab($tab, noteContext);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user