mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 09:58:32 +02:00
work on hash & history
This commit is contained in:
parent
2178f82324
commit
dd1fc23fe8
@ -1,4 +1,4 @@
|
|||||||
INSERT INTO options (name, value, utcDateCreated, utcDateModified, isSynced)
|
INSERT INTO options (name, value, utcDateCreated, utcDateModified, isSynced)
|
||||||
SELECT 'openTabs', '[{"notePath":"' || value || '","active": true}]', '2019-05-01T18:31:00.874Z', '2019-05-01T18:31:00.874Z', 0 FROM options WHERE name = 'startNotePath';
|
SELECT 'openTabs', '[{"notePath":"' || value || '","active": true,"tabId":"1111"}]', '2019-05-01T18:31:00.874Z', '2019-05-01T18:31:00.874Z', 0 FROM options WHERE name = 'startNotePath';
|
||||||
|
|
||||||
DELETE FROM options WHERE name = 'startNotePath';
|
DELETE FROM options WHERE name = 'startNotePath';
|
@ -47,8 +47,6 @@ async function showTree() {
|
|||||||
|
|
||||||
treeService.clearSelectedNodes();
|
treeService.clearSelectedNodes();
|
||||||
|
|
||||||
treeService.setCurrentNotePathToHash(node);
|
|
||||||
|
|
||||||
showDetailPane();
|
showDetailPane();
|
||||||
|
|
||||||
const notePath = await treeUtils.getNotePath(node);
|
const notePath = await treeUtils.getNotePath(node);
|
||||||
|
@ -103,8 +103,6 @@ async function deleteNodes(nodes) {
|
|||||||
if (next) {
|
if (next) {
|
||||||
// activate next element after this one is deleted so we don't lose focus
|
// activate next element after this one is deleted so we don't lose focus
|
||||||
next.setActive();
|
next.setActive();
|
||||||
|
|
||||||
treeService.setCurrentNotePathToHash(next);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await treeService.loadTreeCache();
|
await treeService.loadTreeCache();
|
||||||
@ -163,8 +161,6 @@ async function changeNode(func, node, beforeNoteId = null, afterNoteId = null) {
|
|||||||
|
|
||||||
await treeCache.moveNote(childNoteId, thisOldParentNode.data.noteId, thisNewParentNode.data.noteId, beforeNoteId, afterNoteId);
|
await treeCache.moveNote(childNoteId, thisOldParentNode.data.noteId, thisNewParentNode.data.noteId, beforeNoteId, afterNoteId);
|
||||||
|
|
||||||
treeService.setCurrentNotePathToHash(node);
|
|
||||||
|
|
||||||
await treeService.checkFolderStatus(thisOldParentNode);
|
await treeService.checkFolderStatus(thisOldParentNode);
|
||||||
await treeService.checkFolderStatus(thisNewParentNode);
|
await treeService.checkFolderStatus(thisNewParentNode);
|
||||||
|
|
||||||
|
@ -268,9 +268,13 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @method
|
* @method
|
||||||
* @returns {Promise<string>} returns note path of active note
|
* @returns {Promise<string|null>} returns note path of active note or null if there isn't active note
|
||||||
*/
|
*/
|
||||||
this.getActiveNotePath = treeService.getActiveNotePath;
|
this.getActiveNotePath = () => {
|
||||||
|
const activeTabContext = noteDetailService.getActiveTabContext();
|
||||||
|
|
||||||
|
return activeTabContext ? activeTabContext.notePath : null;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method checks whether user navigated away from the note from which the scripts has been started.
|
* This method checks whether user navigated away from the note from which the scripts has been started.
|
||||||
|
@ -102,9 +102,27 @@ function getActiveTabContext() {
|
|||||||
return tabContexts.find(tc => tc.tabId === tabId);
|
return tabContexts.find(tc => tc.tabId === tabId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isActive(tabContext) {
|
||||||
|
return tabContext.$tab[0] === tabRow.activateTab;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function activateTabContext(tabContext) {
|
||||||
|
await tabRow.activateTab(tabContext.$tab[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @returns {TabContext} */
|
||||||
|
function getTabContext(tabId) {
|
||||||
|
return tabContexts.find(tc => tc.tabId === tabId);
|
||||||
|
}
|
||||||
|
|
||||||
async function showTab(tabId) {
|
async function showTab(tabId) {
|
||||||
for (const ctx of tabContexts) {
|
for (const ctx of tabContexts) {
|
||||||
ctx.$tabContent.toggle(ctx.tabId === tabId);
|
if (ctx.tabId === tabId) {
|
||||||
|
ctx.show();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ctx.hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const oldActiveNode = treeService.getActiveNode();
|
const oldActiveNode = treeService.getActiveNode();
|
||||||
@ -207,9 +225,9 @@ async function loadNoteDetail(origNotePath, options = {}) {
|
|||||||
const loadedNote = await loadNote(noteId);
|
const loadedNote = await loadNote(noteId);
|
||||||
let ctx;
|
let ctx;
|
||||||
|
|
||||||
if (tabContexts.length === 0 || newTab) {
|
if (!getActiveTabContext() || newTab) {
|
||||||
// if it's a new tab explicitly by user then it's in background
|
// if it's a new tab explicitly by user then it's in background
|
||||||
ctx = new TabContext(tabRow);
|
ctx = new TabContext(tabRow, options.tabId);
|
||||||
tabContexts.push(ctx);
|
tabContexts.push(ctx);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@ -229,7 +247,7 @@ async function loadNoteDetail(origNotePath, options = {}) {
|
|||||||
|
|
||||||
if (activate) {
|
if (activate) {
|
||||||
// will also trigger showTab via event
|
// will also trigger showTab via event
|
||||||
tabRow.setCurrentTab(ctx.$tab[0]);
|
await tabRow.activateTab(ctx.$tab[0]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -316,7 +334,7 @@ async function openEmptyTab() {
|
|||||||
|
|
||||||
await renderComponent(ctx);
|
await renderComponent(ctx);
|
||||||
|
|
||||||
await tabRow.setCurrentTab(ctx.tab);
|
await tabRow.activateTab(ctx.$tab[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
tabRow.addListener('newTab', openEmptyTab);
|
tabRow.addListener('newTab', openEmptyTab);
|
||||||
@ -380,7 +398,7 @@ if (utils.isElectron()) {
|
|||||||
const nextTab = tabRow.nextTabEl;
|
const nextTab = tabRow.nextTabEl;
|
||||||
|
|
||||||
if (nextTab) {
|
if (nextTab) {
|
||||||
tabRow.setCurrentTab(nextTab);
|
tabRow.activateTab(nextTab);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -388,7 +406,7 @@ if (utils.isElectron()) {
|
|||||||
const prevTab = tabRow.previousTabEl;
|
const prevTab = tabRow.previousTabEl;
|
||||||
|
|
||||||
if (prevTab) {
|
if (prevTab) {
|
||||||
tabRow.setCurrentTab(prevTab);
|
tabRow.activateTab(prevTab);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -424,6 +442,7 @@ async function saveOpenTabs() {
|
|||||||
|
|
||||||
if (tabContext && tabContext.notePath) {
|
if (tabContext && tabContext.notePath) {
|
||||||
openTabs.push({
|
openTabs.push({
|
||||||
|
tabId: tabContext.tabId,
|
||||||
notePath: tabContext.notePath,
|
notePath: tabContext.notePath,
|
||||||
active: activeTabEl === tabEl
|
active: activeTabEl === tabEl
|
||||||
});
|
});
|
||||||
@ -457,8 +476,11 @@ export default {
|
|||||||
saveNotesIfChanged,
|
saveNotesIfChanged,
|
||||||
onNoteChange,
|
onNoteChange,
|
||||||
addDetailLoadedListener,
|
addDetailLoadedListener,
|
||||||
|
getTabContext,
|
||||||
getTabContexts,
|
getTabContexts,
|
||||||
getActiveTabContext,
|
getActiveTabContext,
|
||||||
|
isActive,
|
||||||
|
activateTabContext,
|
||||||
getActiveComponent,
|
getActiveComponent,
|
||||||
clearOpenTabsTask,
|
clearOpenTabsTask,
|
||||||
filterTabs
|
filterTabs
|
||||||
|
@ -141,7 +141,7 @@ async function refreshSearch() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function init() {
|
function init() {
|
||||||
const hashValue = treeService.getHashValueFromAddress();
|
const hashValue = document.location.hash ? document.location.hash.substr(1) : ""; // strip initial #
|
||||||
|
|
||||||
if (hashValue.startsWith("search=")) {
|
if (hashValue.startsWith("search=")) {
|
||||||
showSearch();
|
showSearch();
|
||||||
|
@ -36,10 +36,10 @@ class TabContext {
|
|||||||
/**
|
/**
|
||||||
* @param {TabRow} tabRow
|
* @param {TabRow} tabRow
|
||||||
*/
|
*/
|
||||||
constructor(tabRow) {
|
constructor(tabRow, tabId = null) {
|
||||||
this.tabRow = tabRow;
|
this.tabRow = tabRow;
|
||||||
this.$tab = $(this.tabRow.addTab());
|
this.tabId = tabId || utils.randomString(4);
|
||||||
this.tabId = this.$tab.attr('data-tab-id');
|
this.$tab = $(this.tabRow.addTab(this.tabId));
|
||||||
|
|
||||||
this.$tabContent = $(".note-tab-content-template").clone();
|
this.$tabContent = $(".note-tab-content-template").clone();
|
||||||
this.$tabContent.removeClass('note-tab-content-template');
|
this.$tabContent.removeClass('note-tab-content-template');
|
||||||
@ -96,9 +96,40 @@ class TabContext {
|
|||||||
|
|
||||||
this.setupClasses();
|
this.setupClasses();
|
||||||
|
|
||||||
|
this.setCurrentNotePathToHash();
|
||||||
|
|
||||||
|
setTimeout(async () => {
|
||||||
|
// we include the note into recent list only if the user stayed on the note at least 5 seconds
|
||||||
|
if (notePath && notePath === await this.notePath) {
|
||||||
|
await server.post('recent-notes', { notePath });
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
console.log(`Switched tab ${this.tabId} to ${this.noteId}`);
|
console.log(`Switched tab ${this.tabId} to ${this.noteId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
show() {
|
||||||
|
this.$tabContent.show();
|
||||||
|
this.setCurrentNotePathToHash();
|
||||||
|
|
||||||
|
document.title = "Trilium Notes";
|
||||||
|
|
||||||
|
if (this.note) {
|
||||||
|
// it helps navigating in history if note title is included in the title
|
||||||
|
document.title += " - " + this.note.title;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hide() {
|
||||||
|
this.$tabContent.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
setCurrentNotePathToHash() {
|
||||||
|
if (this.$tab[0] === this.tabRow.activeTabEl) {
|
||||||
|
document.location.hash = (this.notePath || "") + "-" + this.tabId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setupClasses() {
|
setupClasses() {
|
||||||
for (const clazz of Array.from(this.$tab[0].classList)) { // create copy to safely iterate over while removing classes
|
for (const clazz of Array.from(this.$tab[0].classList)) { // create copy to safely iterate over while removing classes
|
||||||
if (clazz !== 'note-tab') {
|
if (clazz !== 'note-tab') {
|
||||||
@ -205,13 +236,11 @@ class TabContext {
|
|||||||
|
|
||||||
this.$childrenOverview.empty();
|
this.$childrenOverview.empty();
|
||||||
|
|
||||||
const notePath = await treeService.getActiveNotePath();
|
|
||||||
|
|
||||||
for (const childBranch of await this.note.getChildBranches()) {
|
for (const childBranch of await this.note.getChildBranches()) {
|
||||||
const link = $('<a>', {
|
const link = $('<a>', {
|
||||||
href: 'javascript:',
|
href: 'javascript:',
|
||||||
text: await treeUtils.getNoteTitle(childBranch.noteId, childBranch.parentNoteId)
|
text: await treeUtils.getNoteTitle(childBranch.noteId, childBranch.parentNoteId)
|
||||||
}).attr('data-action', 'note').attr('data-note-path', notePath + '/' + childBranch.noteId);
|
}).attr('data-action', 'note').attr('data-note-path', this.notePath + '/' + childBranch.noteId);
|
||||||
|
|
||||||
const childEl = $('<div class="child-overview-item">').html(link);
|
const childEl = $('<div class="child-overview-item">').html(link);
|
||||||
this.$childrenOverview.append(childEl);
|
this.$childrenOverview.append(childEl);
|
||||||
|
File diff suppressed because one or more lines are too long
@ -38,10 +38,6 @@ function getActiveNode() {
|
|||||||
return $tree.fancytree("getActiveNode");
|
return $tree.fancytree("getActiveNode");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getActiveNotePath() {
|
|
||||||
return getHashValueFromAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getNodesByBranchId(branchId) {
|
async function getNodesByBranchId(branchId) {
|
||||||
utils.assertArguments(branchId);
|
utils.assertArguments(branchId);
|
||||||
|
|
||||||
@ -135,6 +131,12 @@ async function activateNote(notePath, noteLoadedListener) {
|
|||||||
// notePath argument can contain only noteId which is not good when hoisted since
|
// notePath argument can contain only noteId which is not good when hoisted since
|
||||||
// then we need to check the whole note path
|
// then we need to check the whole note path
|
||||||
const runNotePath = await getRunPath(notePath);
|
const runNotePath = await getRunPath(notePath);
|
||||||
|
|
||||||
|
if (!runNotePath) {
|
||||||
|
console.log("Cannot activate " + notePath);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
|
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
|
||||||
|
|
||||||
if (hoistedNoteId !== 'root' && !runNotePath.includes(hoistedNoteId)) {
|
if (hoistedNoteId !== 'root' && !runNotePath.includes(hoistedNoteId)) {
|
||||||
@ -172,7 +174,7 @@ async function activateNote(notePath, noteLoadedListener) {
|
|||||||
async function resolveNotePath(notePath) {
|
async function resolveNotePath(notePath) {
|
||||||
const runPath = await getRunPath(notePath);
|
const runPath = await getRunPath(notePath);
|
||||||
|
|
||||||
return runPath.join("/");
|
return runPath ? runPath.join("/") : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -205,7 +207,8 @@ async function getRunPath(notePath) {
|
|||||||
const child = await treeCache.getNote(childNoteId);
|
const child = await treeCache.getNote(childNoteId);
|
||||||
|
|
||||||
if (!child) {
|
if (!child) {
|
||||||
console.log("Can't find " + childNoteId);
|
console.log("Can't find note " + childNoteId);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const parents = await child.getParentNotes();
|
const parents = await child.getParentNotes();
|
||||||
@ -331,26 +334,6 @@ async function setExpandedToServer(branchId, isExpanded) {
|
|||||||
await server.put('branches/' + branchId + '/expanded/' + expandedNum);
|
await server.put('branches/' + branchId + '/expanded/' + expandedNum);
|
||||||
}
|
}
|
||||||
|
|
||||||
function addRecentNote(branchId, notePath) {
|
|
||||||
setTimeout(async () => {
|
|
||||||
// we include the note into recent list only if the user stayed on the note at least 5 seconds
|
|
||||||
if (notePath && notePath === await getActiveNotePath()) {
|
|
||||||
await server.post('recent-notes', { branchId, notePath });
|
|
||||||
}
|
|
||||||
}, 1500);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function setCurrentNotePathToHash(node) {
|
|
||||||
utils.assertArguments(node);
|
|
||||||
|
|
||||||
const activeNotePath = await treeUtils.getNotePath(node);
|
|
||||||
const currentBranchId = node.data.branchId;
|
|
||||||
|
|
||||||
document.location.hash = activeNotePath;
|
|
||||||
|
|
||||||
addRecentNote(currentBranchId, activeNotePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSelectedNodes(stopOnParents = false) {
|
function getSelectedNodes(stopOnParents = false) {
|
||||||
return getTree().getSelectedNodes(stopOnParents);
|
return getTree().getSelectedNodes(stopOnParents);
|
||||||
}
|
}
|
||||||
@ -402,8 +385,13 @@ async function treeInitialized() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!filteredTabs.find(tab => tab.active)) {
|
||||||
|
filteredTabs[0].active = true;
|
||||||
|
}
|
||||||
|
|
||||||
for (const tab of filteredTabs) {
|
for (const tab of filteredTabs) {
|
||||||
await noteDetailService.loadNoteDetail(tab.notePath, {
|
await noteDetailService.loadNoteDetail(tab.notePath, {
|
||||||
|
tabId: tab.tabId,
|
||||||
newTab: true,
|
newTab: true,
|
||||||
activate: tab.active
|
activate: tab.active
|
||||||
});
|
});
|
||||||
@ -465,8 +453,6 @@ function initFancyTree(tree) {
|
|||||||
// click event won't propagate so let's close context menu manually
|
// click event won't propagate so let's close context menu manually
|
||||||
contextMenuWidget.hideContextMenu();
|
contextMenuWidget.hideContextMenu();
|
||||||
|
|
||||||
await setCurrentNotePathToHash(node);
|
|
||||||
|
|
||||||
const notePath = await treeUtils.getNotePath(node);
|
const notePath = await treeUtils.getNotePath(node);
|
||||||
|
|
||||||
noteDetailService.switchToNote(notePath);
|
noteDetailService.switchToNote(notePath);
|
||||||
@ -554,11 +540,17 @@ async function reload() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function isNotePathInAddress() {
|
function isNotePathInAddress() {
|
||||||
return getHashValueFromAddress().startsWith("root");
|
const [notePath, tabId] = getHashValueFromAddress();
|
||||||
|
|
||||||
|
return notePath.startsWith("root")
|
||||||
|
// empty string is for empty/uninitialized tab
|
||||||
|
|| (notePath === '' && !!tabId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHashValueFromAddress() {
|
function getHashValueFromAddress() {
|
||||||
return document.location.hash ? document.location.hash.substr(1) : ""; // strip initial #
|
const str = document.location.hash ? document.location.hash.substr(1) : ""; // strip initial #
|
||||||
|
|
||||||
|
return str.split("-");
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadTreeCache() {
|
async function loadTreeCache() {
|
||||||
@ -835,13 +827,27 @@ utils.bindShortcut('ctrl+.', scrollToActiveNote);
|
|||||||
|
|
||||||
$(window).bind('hashchange', async function() {
|
$(window).bind('hashchange', async function() {
|
||||||
if (isNotePathInAddress()) {
|
if (isNotePathInAddress()) {
|
||||||
const notePath = getHashValueFromAddress();
|
const [notePath, tabId] = getHashValueFromAddress();
|
||||||
const noteId = notePath.split("/").pop();
|
|
||||||
|
|
||||||
if (noteId !== '-' && noteId !== getActiveNode().data.noteId) {
|
console.debug(`Switching to ${notePath} on tab ${tabId} because of hash change`);
|
||||||
console.debug("Switching to " + notePath + " because of hash change");
|
|
||||||
|
|
||||||
activateNote(notePath);
|
let tabContext = noteDetailService.getTabContext(tabId);
|
||||||
|
|
||||||
|
if (!tabContext) {
|
||||||
|
noteDetailService.loadNoteDetail(notePath, {
|
||||||
|
newTab: true,
|
||||||
|
tabId: tabId,
|
||||||
|
activate: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!noteDetailService.isActive(tabContext)) {
|
||||||
|
noteDetailService.activateTabContext(tabContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notePath && tabContext.notePath !== notePath) {
|
||||||
|
noteDetailService.loadNoteDetail(notePath);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -860,8 +866,6 @@ export default {
|
|||||||
activateNote,
|
activateNote,
|
||||||
getFocusedNode,
|
getFocusedNode,
|
||||||
getActiveNode,
|
getActiveNode,
|
||||||
getActiveNotePath,
|
|
||||||
setCurrentNotePathToHash,
|
|
||||||
setNoteTitle,
|
setNoteTitle,
|
||||||
setPrefix,
|
setPrefix,
|
||||||
createNote,
|
createNote,
|
||||||
|
@ -134,11 +134,6 @@ li.dropdown-submenu:hover > ul.dropdown-menu {
|
|||||||
border-style: solid;
|
border-style: solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-tab-content {
|
|
||||||
font-family: var(--detail-font-family);
|
|
||||||
font-size: var(--detail-font-size);
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
::-webkit-scrollbar {
|
||||||
width: 12px;
|
width: 12px;
|
||||||
}
|
}
|
||||||
|
@ -118,6 +118,8 @@ ul.fancytree-container {
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
min-height: 200px;
|
min-height: 200px;
|
||||||
word-break: break-all; /* otherwise CKEditor fails miserably on super long lines */
|
word-break: break-all; /* otherwise CKEditor fails miserably on super long lines */
|
||||||
|
font-family: var(--detail-font-family);
|
||||||
|
font-size: var(--detail-font-size);
|
||||||
}
|
}
|
||||||
|
|
||||||
.note-detail-component {
|
.note-detail-component {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const optionService = require('../../services/options');
|
|
||||||
const RecentNote = require('../../entities/recent_note');
|
const RecentNote = require('../../entities/recent_note');
|
||||||
|
|
||||||
async function addRecentNote(req) {
|
async function addRecentNote(req) {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user