WIP per-tab hoisting

This commit is contained in:
zadam 2020-11-23 22:52:48 +01:00
parent 167b6974fe
commit 52b8162d01
16 changed files with 64 additions and 74 deletions

View file

@ -0,0 +1,15 @@
DELETE FROM options WHERE name IN (
'noteInfoWidget',
'attributesWidget',
'linkMapWidget',
'noteRevisionsWidget',
'whatLinksHereWidget',
'codeNotesMimeTypes',
'similarNotesWidget',
'editedNotesWidget',
'calendarWidget',
'sidebarMinWidth',
'sidebarWidthPercent',
'showSidebarInNewTab',
'hoistedNoteId'
);

View file

@ -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();

View file

@ -91,7 +91,11 @@ export default class Entrypoints extends Component {
}
async unhoistCommand() {
hoistedNoteService.unhoist();
const activeTabContext = appContext.tabManager.getActiveTabContext();
if (activeTabContext) {
activeTabContext.unhoist();
}
}
copyWithoutFormattingCommand() {

View file

@ -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

View file

@ -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,

View file

@ -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 = {};

View file

@ -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

View file

@ -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
};

View file

@ -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);

View file

@ -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";

View file

@ -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,

View file

@ -1,6 +0,0 @@
let hoistedNoteId = 'root';
module.exports = {
getHoistedNoteId: () => hoistedNoteId,
setHoistedNoteId(noteId) { hoistedNoteId = noteId; }
};

View file

@ -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'));
});

View file

@ -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;
}

View file

@ -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");
}

View file

@ -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) {