mirror of
				https://github.com/zadam/trilium.git
				synced 2025-10-31 18:36:30 +01:00 
			
		
		
		
	WIP per-tab hoisting
This commit is contained in:
		
							
								
								
									
										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) { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user