mirror of
https://github.com/zadam/trilium.git
synced 2025-03-01 14:22:32 +01:00
WIP per-tab hoisting
This commit is contained in:
parent
167b6974fe
commit
52b8162d01
15
db/migrations/0171__cleanup_options.sql
Normal file
15
db/migrations/0171__cleanup_options.sql
Normal file
@ -0,0 +1,15 @@
|
||||
DELETE FROM options WHERE name IN (
|
||||
'noteInfoWidget',
|
||||
'attributesWidget',
|
||||
'linkMapWidget',
|
||||
'noteRevisionsWidget',
|
||||
'whatLinksHereWidget',
|
||||
'codeNotesMimeTypes',
|
||||
'similarNotesWidget',
|
||||
'editedNotesWidget',
|
||||
'calendarWidget',
|
||||
'sidebarMinWidth',
|
||||
'sidebarWidthPercent',
|
||||
'showSidebarInNewTab',
|
||||
'hoistedNoteId'
|
||||
);
|
@ -10,7 +10,6 @@ const FileStore = require('session-file-store')(session);
|
||||
const sessionSecret = require('./services/session_secret');
|
||||
const dataDir = require('./services/data_dir');
|
||||
require('./services/handlers');
|
||||
require('./services/hoisted_note_loader');
|
||||
require('./services/note_cache/note_cache_loader');
|
||||
|
||||
const app = express();
|
||||
|
@ -91,7 +91,11 @@ export default class Entrypoints extends Component {
|
||||
}
|
||||
|
||||
async unhoistCommand() {
|
||||
hoistedNoteService.unhoist();
|
||||
const activeTabContext = appContext.tabManager.getActiveTabContext();
|
||||
|
||||
if (activeTabContext) {
|
||||
activeTabContext.unhoist();
|
||||
}
|
||||
}
|
||||
|
||||
copyWithoutFormattingCommand() {
|
||||
|
@ -379,13 +379,19 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, $contain
|
||||
this.getYearNote = dateNotesService.getYearNote;
|
||||
|
||||
/**
|
||||
* Hoist note. See https://github.com/zadam/trilium/wiki/Note-hoisting
|
||||
* Hoist note in the current tab. See https://github.com/zadam/trilium/wiki/Note-hoisting
|
||||
*
|
||||
* @method
|
||||
* @param {string} noteId - set hoisted note. 'root' will effectively unhoist
|
||||
* @return {Promise}
|
||||
*/
|
||||
this.setHoistedNoteId = hoistedNoteService.setHoistedNoteId;
|
||||
this.setHoistedNoteId = (noteId) => {
|
||||
const activeTabContext = appContext.tabManager.getActiveTabContext();
|
||||
|
||||
if (activeTabContext) {
|
||||
activeTabContext.setHoistedNoteId(noteId);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @method
|
||||
|
@ -1,23 +1,18 @@
|
||||
import options from './options.js';
|
||||
import appContext from "./app_context.js";
|
||||
import treeService from "./tree.js";
|
||||
|
||||
function getHoistedNoteId() {
|
||||
return options.get('hoistedNoteId');
|
||||
}
|
||||
const activeTabContext = appContext.tabManager.getActiveTabContext();
|
||||
|
||||
async function setHoistedNoteId(noteId) {
|
||||
if (getHoistedNoteId() === noteId) {
|
||||
return;
|
||||
}
|
||||
|
||||
await options.save('hoistedNoteId', noteId);
|
||||
|
||||
appContext.triggerEvent('hoistedNoteChanged', {noteId});
|
||||
return activeTabContext ? activeTabContext.hoistedNoteId : 'root';
|
||||
}
|
||||
|
||||
async function unhoist() {
|
||||
await setHoistedNoteId('root');
|
||||
const activeTabContext = appContext.tabManager.getActiveTabContext();
|
||||
|
||||
if (activeTabContext) {
|
||||
await activeTabContext.unhoist();
|
||||
}
|
||||
}
|
||||
|
||||
function isTopLevelNode(node) {
|
||||
@ -58,7 +53,6 @@ async function checkNoteAccess(notePath) {
|
||||
|
||||
export default {
|
||||
getHoistedNoteId,
|
||||
setHoistedNoteId,
|
||||
unhoist,
|
||||
isTopLevelNode,
|
||||
isRootNode,
|
||||
|
@ -2,13 +2,16 @@ import utils from './utils.js';
|
||||
|
||||
const REQUEST_LOGGING_ENABLED = false;
|
||||
|
||||
function getHeaders(headers) {
|
||||
async function getHeaders(headers) {
|
||||
const appContext = (await import('./app_context.js')).default;
|
||||
const activeTabContext = appContext.tabManager ? appContext.tabManager.getActiveTabContext() : null;
|
||||
|
||||
// headers need to be lowercase because node.js automatically converts them to lower case
|
||||
// so hypothetical protectedSessionId becomes protectedsessionid on the backend
|
||||
// also avoiding using underscores instead of dashes since nginx filters them out by default
|
||||
const allHeaders = {
|
||||
'trilium-source-id': glob.sourceId,
|
||||
'trilium-local-now-datetime': utils.localNowDateTime(),
|
||||
'trilium-hoisted-note-id': activeTabContext ? activeTabContext.hoistedNoteId : null,
|
||||
'x-csrf-token': glob.csrfToken
|
||||
};
|
||||
|
||||
@ -52,6 +55,8 @@ async function call(method, url, data, headers = {}) {
|
||||
|
||||
const start = Date.now();
|
||||
|
||||
headers = await getHeaders(headers);
|
||||
|
||||
if (utils.isElectron()) {
|
||||
const ipc = utils.dynamicRequire('electron').ipcRenderer;
|
||||
const requestId = i++;
|
||||
@ -65,7 +70,7 @@ async function call(method, url, data, headers = {}) {
|
||||
|
||||
ipc.send('server-request', {
|
||||
requestId: requestId,
|
||||
headers: getHeaders(headers),
|
||||
headers: headers,
|
||||
method: method,
|
||||
url: "/" + baseApiUrl + url,
|
||||
data: data
|
||||
@ -96,7 +101,7 @@ function ajax(url, method, data, headers) {
|
||||
const options = {
|
||||
url: baseApiUrl + url,
|
||||
type: method,
|
||||
headers: getHeaders(headers),
|
||||
headers: headers,
|
||||
timeout: 60000,
|
||||
success: (body, textStatus, jqXhr) => {
|
||||
const respHeaders = {};
|
||||
|
@ -193,8 +193,8 @@ export default class TabManager extends Component {
|
||||
return tabContext;
|
||||
}
|
||||
|
||||
async openTabWithNote(notePath, activate, tabId = null, hoistedNoteId = 'root') {
|
||||
const tabContext = await this.openEmptyTab(tabId);
|
||||
async openTabWithNote(notePath, activate, tabId, hoistedNoteId) {
|
||||
const tabContext = await this.openEmptyTab(tabId, hoistedNoteId);
|
||||
|
||||
if (notePath) {
|
||||
await tabContext.setNote(notePath, !activate); // if activate is false then send normal noteSwitched event
|
||||
|
@ -500,8 +500,6 @@ export default class NoteTreeWidget extends TabAwareWidget {
|
||||
}
|
||||
|
||||
prepareRootNode() {
|
||||
const hoistedNoteId = hoistedNoteService.getHoistedNoteId();
|
||||
|
||||
return this.prepareNode(treeCache.getBranch('root'));
|
||||
}
|
||||
|
||||
@ -532,16 +530,6 @@ export default class NoteTreeWidget extends TabAwareWidget {
|
||||
return noteList;
|
||||
}
|
||||
|
||||
getIcon(note, isFolder) {
|
||||
const hoistedNoteId = hoistedNoteService.getHoistedNoteId();
|
||||
|
||||
if (note.noteId !== 'root' && note.noteId === hoistedNoteId) {
|
||||
return "bx bxs-arrow-from-bottom";
|
||||
}
|
||||
|
||||
return note.getIcon(isFolder);
|
||||
}
|
||||
|
||||
updateNode(node) {
|
||||
const note = treeCache.getNoteFromCache(node.data.noteId);
|
||||
const branch = treeCache.getBranch(node.data.branchId);
|
||||
@ -552,7 +540,7 @@ export default class NoteTreeWidget extends TabAwareWidget {
|
||||
node.data.isProtected = note.isProtected;
|
||||
node.data.noteType = note.type;
|
||||
node.folder = isFolder;
|
||||
node.icon = this.getIcon(note, isFolder);
|
||||
node.icon = note.getIcon(isFolder);
|
||||
node.extraClasses = this.getExtraClasses(note);
|
||||
node.title = utils.escapeHtml(title);
|
||||
|
||||
@ -574,7 +562,6 @@ export default class NoteTreeWidget extends TabAwareWidget {
|
||||
}
|
||||
|
||||
const title = (branch.prefix ? (branch.prefix + " - ") : "") + note.title;
|
||||
const hoistedNoteId = hoistedNoteService.getHoistedNoteId();
|
||||
|
||||
const isFolder = this.isFolder(note);
|
||||
|
||||
@ -586,11 +573,11 @@ export default class NoteTreeWidget extends TabAwareWidget {
|
||||
noteType: note.type,
|
||||
title: utils.escapeHtml(title),
|
||||
extraClasses: this.getExtraClasses(note),
|
||||
icon: this.getIcon(note, isFolder),
|
||||
icon: note.getIcon(isFolder),
|
||||
refKey: note.noteId,
|
||||
lazy: true,
|
||||
folder: isFolder,
|
||||
expanded: (branch.isExpanded || hoistedNoteId === note.noteId) && note.type !== 'search',
|
||||
expanded: branch.isExpanded && note.type !== 'search',
|
||||
key: utils.randomString(12) // this should prevent some "duplicate key" errors
|
||||
};
|
||||
|
||||
|
@ -88,7 +88,8 @@ function route(method, path, middleware, routeHandler, resultHandler, transactio
|
||||
|
||||
const result = cls.init(() => {
|
||||
cls.set('sourceId', req.headers['trilium-source-id']);
|
||||
cls.set('localNowDateTime', req.headers['`trilium-local-now-datetime`']);
|
||||
cls.set('localNowDateTime', req.headers['trilium-local-now-datetime']);
|
||||
cls.set('hoistedNoteId', req.headers['trilium-hoisted-note-id'] || 'root');
|
||||
protectedSessionService.setProtectedSessionId(req);
|
||||
|
||||
const cb = () => routeHandler(req, res, next);
|
||||
|
@ -4,7 +4,7 @@ const build = require('./build');
|
||||
const packageJson = require('../../package');
|
||||
const {TRILIUM_DATA_DIR} = require('./data_dir');
|
||||
|
||||
const APP_DB_VERSION = 170;
|
||||
const APP_DB_VERSION = 171;
|
||||
const SYNC_VERSION = 16;
|
||||
const CLIPPER_PROTOCOL_VERSION = "1.0";
|
||||
|
||||
|
@ -24,6 +24,10 @@ function set(key, value) {
|
||||
namespace.set(key, value);
|
||||
}
|
||||
|
||||
function getHoistedNoteId() {
|
||||
return namespace.get('hoistedNoteId');
|
||||
}
|
||||
|
||||
function getSourceId() {
|
||||
return namespace.get('sourceId');
|
||||
}
|
||||
@ -74,6 +78,7 @@ module.exports = {
|
||||
get,
|
||||
set,
|
||||
namespace,
|
||||
getHoistedNoteId,
|
||||
getSourceId,
|
||||
getLocalNowDateTime,
|
||||
disableEntityEvents,
|
||||
|
@ -1,6 +0,0 @@
|
||||
let hoistedNoteId = 'root';
|
||||
|
||||
module.exports = {
|
||||
getHoistedNoteId: () => hoistedNoteId,
|
||||
setHoistedNoteId(noteId) { hoistedNoteId = noteId; }
|
||||
};
|
@ -1,14 +0,0 @@
|
||||
const optionService = require('./options');
|
||||
const sqlInit = require('./sql_init');
|
||||
const eventService = require('./events');
|
||||
const hoistedNote = require('./hoisted_note');
|
||||
|
||||
eventService.subscribe(eventService.ENTITY_CHANGED, ({entityName, entity}) => {
|
||||
if (entityName === 'options' && entity.name === 'hoistedNoteId') {
|
||||
hoistedNote.setHoistedNoteId(entity.value);
|
||||
}
|
||||
});
|
||||
|
||||
sqlInit.dbReady.then(() => {
|
||||
hoistedNote.setHoistedNoteId(optionService.getOption('hoistedNoteId'));
|
||||
});
|
@ -1,7 +1,7 @@
|
||||
"use strict";
|
||||
|
||||
const noteCache = require('./note_cache');
|
||||
const hoistedNoteService = require('../hoisted_note');
|
||||
const cls = require('../cls');
|
||||
const protectedSessionService = require('../protected_session');
|
||||
const log = require('../log');
|
||||
|
||||
@ -88,10 +88,6 @@ function getNoteTitle(childNoteId, parentNoteId) {
|
||||
function getNoteTitleArrayForPath(notePathArray) {
|
||||
const titles = [];
|
||||
|
||||
if (notePathArray[0] === hoistedNoteService.getHoistedNoteId() && notePathArray.length === 1) {
|
||||
return [ getNoteTitle(hoistedNoteService.getHoistedNoteId()) ];
|
||||
}
|
||||
|
||||
let parentNoteId = 'root';
|
||||
let hoistedNotePassed = false;
|
||||
|
||||
@ -103,7 +99,7 @@ function getNoteTitleArrayForPath(notePathArray) {
|
||||
titles.push(title);
|
||||
}
|
||||
|
||||
if (noteId === hoistedNoteService.getHoistedNoteId()) {
|
||||
if (noteId === cls.getHoistedNoteId()) {
|
||||
hoistedNotePassed = true;
|
||||
}
|
||||
|
||||
@ -130,7 +126,7 @@ function getSomePath(note, path = []) {
|
||||
path.push(note.noteId);
|
||||
path.reverse();
|
||||
|
||||
if (!path.includes(hoistedNoteService.getHoistedNoteId())) {
|
||||
if (!path.includes(cls.getHoistedNoteId())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -7,10 +7,8 @@ const eventService = require('./events');
|
||||
const repository = require('./repository');
|
||||
const cls = require('../services/cls');
|
||||
const Note = require('../entities/note');
|
||||
const NoteRevision = require('../entities/note_revision');
|
||||
const Branch = require('../entities/branch');
|
||||
const Attribute = require('../entities/attribute');
|
||||
const hoistedNoteService = require('../services/hoisted_note');
|
||||
const protectedSessionService = require('../services/protected_session');
|
||||
const log = require('../services/log');
|
||||
const utils = require('../services/utils');
|
||||
@ -524,7 +522,7 @@ function deleteBranch(branch, deleteId, taskContext) {
|
||||
|
||||
if (branch.branchId === 'root'
|
||||
|| branch.noteId === 'root'
|
||||
|| branch.noteId === hoistedNoteService.getHoistedNoteId()) {
|
||||
|| branch.noteId === cls.getHoistedNoteId()) {
|
||||
|
||||
throw new Error("Can't delete root branch/note");
|
||||
}
|
||||
|
@ -8,15 +8,15 @@ const SearchResult = require("../search_result.js");
|
||||
const SearchContext = require("../search_context.js");
|
||||
const noteCache = require('../../note_cache/note_cache.js');
|
||||
const noteCacheService = require('../../note_cache/note_cache_service.js');
|
||||
const hoistedNoteService = require('../../hoisted_note.js');
|
||||
const utils = require('../../utils.js');
|
||||
const cls = require('../../cls.js');
|
||||
|
||||
/**
|
||||
* @param {Expression} expression
|
||||
* @return {SearchResult[]}
|
||||
*/
|
||||
function findNotesWithExpression(expression) {
|
||||
const hoistedNote = noteCache.notes[hoistedNoteService.getHoistedNoteId()];
|
||||
const hoistedNote = noteCache.notes[cls.getHoistedNoteId()];
|
||||
let allNotes = (hoistedNote && hoistedNote.noteId !== 'root')
|
||||
? hoistedNote.subtreeNotes
|
||||
: Object.values(noteCache.notes);
|
||||
@ -35,7 +35,7 @@ function findNotesWithExpression(expression) {
|
||||
|
||||
const searchResults = noteSet.notes
|
||||
.map(note => searchContext.noteIdToNotePath[note.noteId] || noteCacheService.getSomePath(note))
|
||||
.filter(notePathArray => notePathArray.includes(hoistedNoteService.getHoistedNoteId()))
|
||||
.filter(notePathArray => notePathArray.includes(cls.getHoistedNoteId()))
|
||||
.map(notePathArray => new SearchResult(notePathArray));
|
||||
|
||||
if (!noteSet.sorted) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user