mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
navigation state is more nicely and completely serialized into URL
This commit is contained in:
parent
9e71c44c76
commit
17128c5874
@ -155,14 +155,14 @@ $(window).on('beforeunload', () => {
|
|||||||
|
|
||||||
$(window).on('hashchange', function() {
|
$(window).on('hashchange', function() {
|
||||||
if (treeService.isNotePathInAddress()) {
|
if (treeService.isNotePathInAddress()) {
|
||||||
const [notePath, ntxId] = treeService.getHashValueFromAddress();
|
const {notePath, ntxId, viewScope} = treeService.parseNavigationStateFromAddress();
|
||||||
|
|
||||||
if (!notePath && !ntxId) {
|
if (!notePath && !ntxId) {
|
||||||
console.log(`Invalid hash value "${document.location.hash}", ignoring.`);
|
console.log(`Invalid hash value "${document.location.hash}", ignoring.`);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
appContext.tabManager.switchToNoteContext(ntxId, notePath);
|
appContext.tabManager.switchToNoteContext(ntxId, notePath, viewScope);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ import toastService from "../services/toast.js";
|
|||||||
import ws from "../services/ws.js";
|
import ws from "../services/ws.js";
|
||||||
import bundleService from "../services/bundle.js";
|
import bundleService from "../services/bundle.js";
|
||||||
import froca from "../services/froca.js";
|
import froca from "../services/froca.js";
|
||||||
|
import linkService from "../services/link.js";
|
||||||
|
|
||||||
export default class Entrypoints extends Component {
|
export default class Entrypoints extends Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -136,17 +137,15 @@ export default class Entrypoints extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async openInWindowCommand({notePath, hoistedNoteId, viewScope}) {
|
async openInWindowCommand({notePath, hoistedNoteId, viewScope}) {
|
||||||
if (!hoistedNoteId) {
|
const extraWindowHash = linkService.calculateHash({notePath, hoistedNoteId, viewScope});
|
||||||
hoistedNoteId = 'root';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (utils.isElectron()) {
|
if (utils.isElectron()) {
|
||||||
const {ipcRenderer} = utils.dynamicRequire('electron');
|
const {ipcRenderer} = utils.dynamicRequire('electron');
|
||||||
|
|
||||||
ipcRenderer.send('create-extra-window', {notePath, hoistedNoteId, viewScope});
|
ipcRenderer.send('create-extra-window', { extraWindowHash });
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const url = `${window.location.protocol}//${window.location.host}${window.location.pathname}?extraWindow=1&extraHoistedNoteId=${hoistedNoteId}&extraViewScope=${JSON.stringify(viewScope)}#${notePath}`;
|
const url = `${window.location.protocol}//${window.location.host}${window.location.pathname}?extraWindow=1${extraWindowHash}`;
|
||||||
|
|
||||||
window.open(url, '', 'width=1000,height=800');
|
window.open(url, '', 'width=1000,height=800');
|
||||||
}
|
}
|
||||||
|
@ -12,13 +12,17 @@ class NoteContext extends Component {
|
|||||||
constructor(ntxId = null, hoistedNoteId = 'root', mainNtxId = null) {
|
constructor(ntxId = null, hoistedNoteId = 'root', mainNtxId = null) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.ntxId = ntxId || utils.randomString(4);
|
this.ntxId = ntxId || this.constructor.generateNtxId();
|
||||||
this.hoistedNoteId = hoistedNoteId;
|
this.hoistedNoteId = hoistedNoteId;
|
||||||
this.mainNtxId = mainNtxId;
|
this.mainNtxId = mainNtxId;
|
||||||
|
|
||||||
this.resetViewScope();
|
this.resetViewScope();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static generateNtxId() {
|
||||||
|
return utils.randomString(6);
|
||||||
|
}
|
||||||
|
|
||||||
setEmpty() {
|
setEmpty() {
|
||||||
this.notePath = null;
|
this.notePath = null;
|
||||||
this.noteId = null;
|
this.noteId = null;
|
||||||
@ -57,9 +61,8 @@ class NoteContext extends Component {
|
|||||||
utils.closeActiveDialog();
|
utils.closeActiveDialog();
|
||||||
|
|
||||||
this.notePath = resolvedNotePath;
|
this.notePath = resolvedNotePath;
|
||||||
({noteId: this.noteId, parentNoteId: this.parentNoteId} = treeService.getNoteIdAndParentIdFromNotePath(resolvedNotePath));
|
|
||||||
|
|
||||||
this.viewScope = opts.viewScope;
|
this.viewScope = opts.viewScope;
|
||||||
|
({noteId: this.noteId, parentNoteId: this.parentNoteId} = treeService.getNoteIdAndParentIdFromNotePath(resolvedNotePath));
|
||||||
|
|
||||||
this.saveToRecentNotes(resolvedNotePath);
|
this.saveToRecentNotes(resolvedNotePath);
|
||||||
|
|
||||||
@ -298,6 +301,29 @@ class NoteContext extends Component {
|
|||||||
// this is reset after navigating to a different note
|
// this is reset after navigating to a different note
|
||||||
this.viewScope = {};
|
this.viewScope = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async getNavigationTitle() {
|
||||||
|
if (!this.note) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { note, viewScope } = this;
|
||||||
|
|
||||||
|
let title = viewScope.viewMode === 'default'
|
||||||
|
? note.title
|
||||||
|
: `${note.title}: ${viewScope.viewMode}`;
|
||||||
|
|
||||||
|
if (viewScope.attachmentId) {
|
||||||
|
// assuming the attachment has been already loaded
|
||||||
|
const attachment = await note.getAttachmentById(viewScope.attachmentId);
|
||||||
|
|
||||||
|
if (attachment) {
|
||||||
|
title += `: ${attachment.title}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return title;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NoteContext;
|
export default NoteContext;
|
||||||
|
@ -8,6 +8,7 @@ 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";
|
||||||
|
|
||||||
export default class TabManager extends Component {
|
export default class TabManager extends Component {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -53,45 +54,44 @@ export default class TabManager extends Component {
|
|||||||
? (options.getJson('openTabs') || [])
|
? (options.getJson('openTabs') || [])
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
let filteredTabs = [];
|
|
||||||
|
|
||||||
// preload all notes at once
|
// preload all notes at once
|
||||||
await froca.getNotes([
|
await froca.getNotes([
|
||||||
...tabsToOpen.map(tab => treeService.getNoteIdFromNotePath(tab.notePath)),
|
...tabsToOpen.map(tab => treeService.getNoteIdFromNotePath(tab.notePath)),
|
||||||
...tabsToOpen.map(tab => tab.hoistedNoteId),
|
...tabsToOpen.map(tab => tab.hoistedNoteId),
|
||||||
], true);
|
], true);
|
||||||
|
|
||||||
for (const openTab of tabsToOpen) {
|
const filteredTabs = tabsToOpen.filter(openTab => {
|
||||||
if (openTab.notePath && !(treeService.getNoteIdFromNotePath(openTab.notePath) in froca.notes)) {
|
if (utils.isMobile()) { // mobile frontend doesn't have tabs so show only the active tab
|
||||||
|
return !!openTab.active;
|
||||||
|
}
|
||||||
|
|
||||||
|
const noteId = treeService.getNoteIdFromNotePath(openTab.notePath);
|
||||||
|
if (!(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
|
||||||
continue;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(openTab.hoistedNoteId in froca.notes)) {
|
if (!(openTab.hoistedNoteId in froca.notes)) {
|
||||||
openTab.hoistedNoteId = 'root';
|
openTab.hoistedNoteId = 'root';
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredTabs.push(openTab);
|
return true;
|
||||||
}
|
});
|
||||||
|
|
||||||
if (utils.isMobile()) {
|
|
||||||
// mobile frontend doesn't have tabs so show only the active tab
|
|
||||||
filteredTabs = filteredTabs.filter(tab => tab.active);
|
|
||||||
}
|
|
||||||
|
|
||||||
// resolve before opened tabs can change this
|
// resolve before opened tabs can change this
|
||||||
const [notePathInUrl, ntxIdInUrl] = treeService.getHashValueFromAddress();
|
const parsedFromUrl = treeService.parseNavigationStateFromAddress();
|
||||||
|
|
||||||
if (filteredTabs.length === 0) {
|
if (filteredTabs.length === 0) {
|
||||||
filteredTabs.push({
|
parsedFromUrl.ntxId = parsedFromUrl.ntxId || NoteContext.generateNtxId(); // generate already here, so that we later know which one to activate
|
||||||
notePath: notePathInUrl || 'root',
|
|
||||||
active: true,
|
|
||||||
hoistedNoteId: glob.extraHoistedNoteId || 'root',
|
|
||||||
viewScope: glob.extraViewScope || {}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!filteredTabs.find(tab => tab.active)) {
|
filteredTabs.push({
|
||||||
|
notePath: parsedFromUrl.notePath || 'root',
|
||||||
|
ntxId: parsedFromUrl.ntxId,
|
||||||
|
active: true,
|
||||||
|
hoistedNoteId: parsedFromUrl.hoistedNoteId || 'root',
|
||||||
|
viewScope: parsedFromUrl.viewScope || {}
|
||||||
|
});
|
||||||
|
} else if (!filteredTabs.find(tab => tab.active)) {
|
||||||
filteredTabs[0].active = true;
|
filteredTabs[0].active = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,8 +109,13 @@ export default class TabManager extends Component {
|
|||||||
|
|
||||||
// if there's notePath in the URL, make sure it's open and active
|
// if there's 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 (notePathInUrl) {
|
if (parsedFromUrl.notePath) {
|
||||||
await appContext.tabManager.switchToNoteContext(ntxIdInUrl, notePathInUrl);
|
await appContext.tabManager.switchToNoteContext(
|
||||||
|
parsedFromUrl.ntxId,
|
||||||
|
parsedFromUrl.notePath,
|
||||||
|
parsedFromUrl.viewScope,
|
||||||
|
parsedFromUrl.hoistedNoteId
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
@ -123,28 +128,41 @@ export default class TabManager extends Component {
|
|||||||
|
|
||||||
noteSwitchedEvent({noteContext}) {
|
noteSwitchedEvent({noteContext}) {
|
||||||
if (noteContext.isActive()) {
|
if (noteContext.isActive()) {
|
||||||
this.setCurrentNotePathToHash();
|
this.setCurrentNavigationStateToHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.tabsUpdate.scheduleUpdate();
|
this.tabsUpdate.scheduleUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
setCurrentNotePathToHash() {
|
setCurrentNavigationStateToHash() {
|
||||||
const activeNoteContext = this.getActiveContext();
|
const calculatedHash = this.calculateHash();
|
||||||
|
|
||||||
if (window.history.length === 0 // first history entry
|
|
||||||
|| (activeNoteContext && activeNoteContext.notePath !== treeService.getHashValueFromAddress()[0])) {
|
|
||||||
const url = `#${activeNoteContext.notePath || ""}-${activeNoteContext.ntxId}`;
|
|
||||||
|
|
||||||
|
// update if it's the first history entry or there has been a change
|
||||||
|
if (window.history.length === 0 || calculatedHash !== window.location?.hash) {
|
||||||
// using pushState instead of directly modifying document.location because it does not trigger hashchange
|
// using pushState instead of directly modifying document.location because it does not trigger hashchange
|
||||||
window.history.pushState(null, "", url);
|
window.history.pushState(null, "", calculatedHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const activeNoteContext = this.getActiveContext();
|
||||||
this.updateDocumentTitle(activeNoteContext);
|
this.updateDocumentTitle(activeNoteContext);
|
||||||
|
|
||||||
this.triggerEvent('activeNoteChanged'); // trigger this even in on popstate event
|
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[]} */
|
/** @returns {NoteContext[]} */
|
||||||
getNoteContexts() {
|
getNoteContexts() {
|
||||||
return this.noteContexts;
|
return this.noteContexts;
|
||||||
@ -212,14 +230,18 @@ export default class TabManager extends Component {
|
|||||||
return activeNote ? activeNote.mime : null;
|
return activeNote ? activeNote.mime : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
async switchToNoteContext(ntxId, notePath) {
|
async switchToNoteContext(ntxId, notePath, viewScope = {}, hoistedNoteId = null) {
|
||||||
const noteContext = this.noteContexts.find(nc => nc.ntxId === ntxId)
|
const noteContext = this.noteContexts.find(nc => nc.ntxId === ntxId)
|
||||||
|| await this.openEmptyTab();
|
|| await this.openEmptyTab();
|
||||||
|
|
||||||
await this.activateNoteContext(noteContext.ntxId);
|
await this.activateNoteContext(noteContext.ntxId);
|
||||||
|
|
||||||
|
if (hoistedNoteId) {
|
||||||
|
await noteContext.setHoistedNoteId(hoistedNoteId);
|
||||||
|
}
|
||||||
|
|
||||||
if (notePath) {
|
if (notePath) {
|
||||||
await noteContext.setNote(notePath);
|
await noteContext.setNote(notePath, { viewScope });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -347,7 +369,7 @@ export default class TabManager extends Component {
|
|||||||
|
|
||||||
this.tabsUpdate.scheduleUpdate();
|
this.tabsUpdate.scheduleUpdate();
|
||||||
|
|
||||||
this.setCurrentNotePathToHash();
|
this.setCurrentNavigationStateToHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -564,21 +586,21 @@ export default class TabManager extends Component {
|
|||||||
this.tabsUpdate.scheduleUpdate();
|
this.tabsUpdate.scheduleUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
updateDocumentTitle(activeNoteContext) {
|
async updateDocumentTitle(activeNoteContext) {
|
||||||
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
|
||||||
activeNoteContext.note?.title,
|
await activeNoteContext.getNavigationTitle(),
|
||||||
"Trilium Notes"
|
"Trilium Notes"
|
||||||
].filter(Boolean);
|
].filter(Boolean);
|
||||||
|
|
||||||
document.title = titleFragments.join(" - ");
|
document.title = titleFragments.join(" - ");
|
||||||
}
|
}
|
||||||
|
|
||||||
entitiesReloadedEvent({loadResults}) {
|
async entitiesReloadedEvent({loadResults}) {
|
||||||
const activeContext = this.getActiveContext();
|
const activeContext = this.getActiveContext();
|
||||||
|
|
||||||
if (activeContext && loadResults.isNoteReloaded(activeContext.noteId)) {
|
if (activeContext && loadResults.isNoteReloaded(activeContext.noteId)) {
|
||||||
this.updateDocumentTitle(activeContext);
|
await this.updateDocumentTitle(activeContext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,37 @@ function parseNotePathAndScope($link) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function calculateHash({notePath, ntxId, hoistedNoteId, viewScope = {}}) {
|
||||||
|
notePath = notePath || "";
|
||||||
|
const params = [
|
||||||
|
ntxId ? { ntxId: ntxId } : null,
|
||||||
|
(hoistedNoteId && hoistedNoteId !== 'root') ? { hoistedNoteId: hoistedNoteId } : null,
|
||||||
|
viewScope.viewMode !== 'default' ? { viewMode: viewScope.viewMode } : null,
|
||||||
|
viewScope.attachmentId ? { attachmentId: viewScope.attachmentId } : null
|
||||||
|
].filter(p => !!p);
|
||||||
|
|
||||||
|
const paramStr = params.map(pair => {
|
||||||
|
const name = Object.keys(pair)[0];
|
||||||
|
const value = pair[name];
|
||||||
|
|
||||||
|
return `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
|
||||||
|
}).join("&");
|
||||||
|
|
||||||
|
if (!notePath && !paramStr) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
let hash = `#${notePath}`;
|
||||||
|
|
||||||
|
if (paramStr) {
|
||||||
|
hash += `?${paramStr}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(hash);
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
function goToLink(evt) {
|
function goToLink(evt) {
|
||||||
const $link = $(evt.target).closest("a,.block-link");
|
const $link = $(evt.target).closest("a,.block-link");
|
||||||
const hrefLink = $link.attr('href');
|
const hrefLink = $link.attr('href');
|
||||||
@ -223,5 +254,6 @@ export default {
|
|||||||
createNoteLink,
|
createNoteLink,
|
||||||
goToLink,
|
goToLink,
|
||||||
loadReferenceLinkTitle,
|
loadReferenceLinkTitle,
|
||||||
parseNotePathAndScope
|
parseNotePathAndScope,
|
||||||
|
calculateHash
|
||||||
};
|
};
|
||||||
|
@ -23,8 +23,8 @@ async function resolveNotePath(notePath, hoistedNoteId = 'root') {
|
|||||||
async function resolveNotePathToSegments(notePath, hoistedNoteId = 'root', logErrors = true) {
|
async function resolveNotePathToSegments(notePath, hoistedNoteId = 'root', logErrors = true) {
|
||||||
utils.assertArguments(notePath);
|
utils.assertArguments(notePath);
|
||||||
|
|
||||||
// we might get notePath with the ntxId suffix, remove it if present
|
// we might get notePath with the params suffix, remove it if present
|
||||||
notePath = notePath.split("-")[0].trim();
|
notePath = notePath.split("?")[0].trim();
|
||||||
|
|
||||||
if (notePath.length === 0) {
|
if (notePath.length === 0) {
|
||||||
return;
|
return;
|
||||||
@ -159,8 +159,8 @@ function getNoteIdFromNotePath(notePath) {
|
|||||||
|
|
||||||
const lastSegment = path[path.length - 1];
|
const lastSegment = path[path.length - 1];
|
||||||
|
|
||||||
// path could have also ntxId suffix
|
// path could have also params suffix
|
||||||
return lastSegment.split("-")[0];
|
return lastSegment.split("?")[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getBranchIdFromNotePath(notePath) {
|
async function getBranchIdFromNotePath(notePath) {
|
||||||
@ -185,8 +185,8 @@ function getNoteIdAndParentIdFromNotePath(notePath) {
|
|||||||
|
|
||||||
const lastSegment = path[path.length - 1];
|
const lastSegment = path[path.length - 1];
|
||||||
|
|
||||||
// path could have also ntxId suffix
|
// path could have also params suffix
|
||||||
noteId = lastSegment.split("-")[0];
|
noteId = lastSegment.split("?")[0];
|
||||||
|
|
||||||
if (path.length > 1) {
|
if (path.length > 1) {
|
||||||
parentNoteId = path[path.length - 2];
|
parentNoteId = path[path.length - 2];
|
||||||
@ -297,14 +297,44 @@ async function getNoteTitleWithPathAsSuffix(notePath) {
|
|||||||
return $titleWithPath;
|
return $titleWithPath;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHashValueFromAddress() {
|
function parseNavigationStateFromAddress() {
|
||||||
const str = document.location.hash ? document.location.hash.substr(1) : ""; // strip initial #
|
const str = document.location.hash?.substr(1) || ""; // strip initial #
|
||||||
|
|
||||||
return str.split("-");
|
const [notePath, paramString] = str.split("?");
|
||||||
|
const viewScope = {
|
||||||
|
viewMode: 'default'
|
||||||
|
};
|
||||||
|
let ntxId = null;
|
||||||
|
let hoistedNoteId = null;
|
||||||
|
|
||||||
|
if (paramString) {
|
||||||
|
for (const pair of paramString.split("&")) {
|
||||||
|
let [name, value] = pair.split("=");
|
||||||
|
name = decodeURIComponent(name);
|
||||||
|
value = decodeURIComponent(value);
|
||||||
|
|
||||||
|
if (name === 'ntxId') {
|
||||||
|
ntxId = value;
|
||||||
|
} else if (name === 'hoistedNoteId') {
|
||||||
|
hoistedNoteId = value;
|
||||||
|
} else if (['viewMode', 'attachmentId'].includes(name)) {
|
||||||
|
viewScope[name] = value;
|
||||||
|
} else {
|
||||||
|
console.warn(`Unrecognized hash parameter '${name}'.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
notePath,
|
||||||
|
ntxId,
|
||||||
|
hoistedNoteId,
|
||||||
|
viewScope
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function isNotePathInAddress() {
|
function isNotePathInAddress() {
|
||||||
const [notePath, ntxId] = getHashValueFromAddress();
|
const {notePath, ntxId} = parseNavigationStateFromAddress();
|
||||||
|
|
||||||
return notePath.startsWith("root")
|
return notePath.startsWith("root")
|
||||||
// empty string is for empty/uninitialized tab
|
// empty string is for empty/uninitialized tab
|
||||||
@ -338,7 +368,7 @@ export default {
|
|||||||
getNoteTitle,
|
getNoteTitle,
|
||||||
getNotePathTitle,
|
getNotePathTitle,
|
||||||
getNoteTitleWithPathAsSuffix,
|
getNoteTitleWithPathAsSuffix,
|
||||||
getHashValueFromAddress,
|
parseNavigationStateFromAddress,
|
||||||
isNotePathInAddress,
|
isNotePathInAddress,
|
||||||
parseNotePath,
|
parseNotePath,
|
||||||
isNotePathInHiddenSubtree
|
isNotePathInHiddenSubtree
|
||||||
|
@ -55,6 +55,7 @@ export default class HistoryNavigationButton extends ButtonFromNoteWidget {
|
|||||||
for (const idx in this.webContents.history) {
|
for (const idx in this.webContents.history) {
|
||||||
const url = this.webContents.history[idx];
|
const url = this.webContents.history[idx];
|
||||||
const [_, notePathWithTab] = url.split('#');
|
const [_, notePathWithTab] = url.split('#');
|
||||||
|
// broken: use treeService.parseNavigationStateFromAddress();
|
||||||
const [notePath, ntxId] = notePathWithTab.split('-');
|
const [notePath, ntxId] = notePathWithTab.split('-');
|
||||||
|
|
||||||
const title = await treeService.getNotePathTitle(notePath);
|
const title = await treeService.getNotePathTitle(notePath);
|
||||||
|
@ -70,37 +70,20 @@ export default class NoteTitleWidget extends NoteContextAwareWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async refreshWithNote(note) {
|
async refreshWithNote(note) {
|
||||||
this.$noteTitle.val(await this.getTitleText(note));
|
const isReadOnly = (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable())
|
||||||
|
|
||||||
this.$noteTitle.prop("readonly",
|
|
||||||
(note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable())
|
|
||||||
|| ['_lbRoot', '_lbAvailableLaunchers', '_lbVisibleLaunchers'].includes(note.noteId)
|
|| ['_lbRoot', '_lbAvailableLaunchers', '_lbVisibleLaunchers'].includes(note.noteId)
|
||||||
|| this.noteContext.viewScope.viewMode !== 'default'
|
|| this.noteContext.viewScope.viewMode !== 'default';
|
||||||
|
|
||||||
|
this.$noteTitle.val(
|
||||||
|
isReadOnly
|
||||||
|
? await this.noteContext.getNavigationTitle()
|
||||||
|
: note.title
|
||||||
);
|
);
|
||||||
|
this.$noteTitle.prop("readonly", isReadOnly);
|
||||||
|
|
||||||
this.setProtectedStatus(note);
|
this.setProtectedStatus(note);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @param {FNote} note */
|
|
||||||
async getTitleText(note) {
|
|
||||||
const viewScope = this.noteContext.viewScope;
|
|
||||||
|
|
||||||
let title = viewScope.viewMode === 'default'
|
|
||||||
? note.title
|
|
||||||
: `${note.title}: ${viewScope.viewMode}`;
|
|
||||||
|
|
||||||
if (viewScope.attachmentId) {
|
|
||||||
// assuming the attachment has been already loaded
|
|
||||||
const attachment = await note.getAttachmentById(viewScope.attachmentId);
|
|
||||||
|
|
||||||
if (attachment) {
|
|
||||||
title += `: ${attachment.title}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return title;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @param {FNote} note */
|
/** @param {FNote} note */
|
||||||
setProtectedStatus(note) {
|
setProtectedStatus(note) {
|
||||||
this.$noteTitle.toggleClass("protected", !!note.isProtected);
|
this.$noteTitle.toggleClass("protected", !!note.isProtected);
|
||||||
|
@ -618,7 +618,7 @@ export default class TabRowWidget extends BasicWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @param {NoteContext} noteContext */
|
/** @param {NoteContext} noteContext */
|
||||||
updateTab($tab, noteContext) {
|
async updateTab($tab, noteContext) {
|
||||||
if (!$tab.length) {
|
if (!$tab.length) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -652,11 +652,7 @@ export default class TabRowWidget extends BasicWidget {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const viewMode = noteContext.viewScope?.viewMode;
|
const title = await noteContext.getNavigationTitle();
|
||||||
const title = (viewMode && viewMode !== 'default')
|
|
||||||
? `${viewMode}: ${note.title}`
|
|
||||||
: note.title;
|
|
||||||
|
|
||||||
this.updateTitle($tab, title);
|
this.updateTitle($tab, title);
|
||||||
|
|
||||||
$tab.addClass(note.getCssClass());
|
$tab.addClass(note.getCssClass());
|
||||||
|
@ -35,9 +35,6 @@ function index(req, res) {
|
|||||||
appCssNoteIds: getAppCssNoteIds(),
|
appCssNoteIds: getAppCssNoteIds(),
|
||||||
isDev: env.isDev(),
|
isDev: env.isDev(),
|
||||||
isMainWindow: !req.query.extraWindow,
|
isMainWindow: !req.query.extraWindow,
|
||||||
extraHoistedNoteId: req.query.extraHoistedNoteId,
|
|
||||||
// make sure only valid JSON gets rendered
|
|
||||||
extraViewScope: JSON.stringify(req.query.extraViewScope ? JSON.parse(req.query.extraViewScope) : {}),
|
|
||||||
isProtectedSessionAvailable: protectedSessionService.isProtectedSessionAvailable(),
|
isProtectedSessionAvailable: protectedSessionService.isProtectedSessionAvailable(),
|
||||||
maxContentWidth: parseInt(options.maxContentWidth),
|
maxContentWidth: parseInt(options.maxContentWidth),
|
||||||
triliumVersion: packageJson.version,
|
triliumVersion: packageJson.version,
|
||||||
|
@ -15,7 +15,7 @@ let mainWindow;
|
|||||||
/** @type {Electron.BrowserWindow} */
|
/** @type {Electron.BrowserWindow} */
|
||||||
let setupWindow;
|
let setupWindow;
|
||||||
|
|
||||||
async function createExtraWindow(notePath, hoistedNoteId = 'root', viewScope = {}) {
|
async function createExtraWindow(extraWindowHash) {
|
||||||
const spellcheckEnabled = optionService.getOptionBool('spellCheckEnabled');
|
const spellcheckEnabled = optionService.getOptionBool('spellCheckEnabled');
|
||||||
|
|
||||||
const {BrowserWindow} = require('electron');
|
const {BrowserWindow} = require('electron');
|
||||||
@ -35,13 +35,13 @@ async function createExtraWindow(notePath, hoistedNoteId = 'root', viewScope = {
|
|||||||
});
|
});
|
||||||
|
|
||||||
win.setMenuBarVisibility(false);
|
win.setMenuBarVisibility(false);
|
||||||
win.loadURL(`http://127.0.0.1:${port}/?extraWindow=1&extraHoistedNoteId=${hoistedNoteId}&extraViewScope=${JSON.stringify(viewScope)}#${notePath}`);
|
win.loadURL(`http://127.0.0.1:${port}/?extraWindow=1${extraWindowHash}`);
|
||||||
|
|
||||||
configureWebContents(win.webContents, spellcheckEnabled);
|
configureWebContents(win.webContents, spellcheckEnabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcMain.on('create-extra-window', (event, arg) => {
|
ipcMain.on('create-extra-window', (event, arg) => {
|
||||||
createExtraWindow(arg.notePath, arg.hoistedNoteId, arg.viewScope);
|
createExtraWindow(arg.extraWindowHash);
|
||||||
});
|
});
|
||||||
|
|
||||||
async function createMainWindow(app) {
|
async function createMainWindow(app) {
|
||||||
|
@ -32,8 +32,6 @@
|
|||||||
isDev: <%= isDev %>,
|
isDev: <%= isDev %>,
|
||||||
appCssNoteIds: <%- JSON.stringify(appCssNoteIds) %>,
|
appCssNoteIds: <%- JSON.stringify(appCssNoteIds) %>,
|
||||||
isMainWindow: <%= isMainWindow %>,
|
isMainWindow: <%= isMainWindow %>,
|
||||||
extraHoistedNoteId: '<%= extraHoistedNoteId %>',
|
|
||||||
extraViewScope: <%- extraViewScope %>,
|
|
||||||
isProtectedSessionAvailable: <%= isProtectedSessionAvailable %>,
|
isProtectedSessionAvailable: <%= isProtectedSessionAvailable %>,
|
||||||
triliumVersion: "<%= triliumVersion %>",
|
triliumVersion: "<%= triliumVersion %>",
|
||||||
assetPath: "<%= assetPath %>",
|
assetPath: "<%= assetPath %>",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user