mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
hoisting notes WIP
This commit is contained in:
parent
8171b68b18
commit
6fbf28b30d
2
db/migrations/0121__add_hoisting_option.sql
Normal file
2
db/migrations/0121__add_hoisting_option.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
INSERT INTO options (name, value, dateCreated, dateModified, isSynced)
|
||||||
|
VALUES ('hoistedNoteId', 'root', '2018-12-11T18:31:00.874Z', '2018-12-11T18:31:00.874Z', 0);
|
@ -4,6 +4,10 @@ function initContextMenu(event, contextMenuItems, selectContextMenuItem) {
|
|||||||
$contextMenuContainer.empty();
|
$contextMenuContainer.empty();
|
||||||
|
|
||||||
for (const item of contextMenuItems) {
|
for (const item of contextMenuItems) {
|
||||||
|
if (item.hidden) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (item.title === '----') {
|
if (item.title === '----') {
|
||||||
$contextMenuContainer.append($("<div>").addClass("dropdown-divider"));
|
$contextMenuContainer.append($("<div>").addClass("dropdown-divider"));
|
||||||
} else {
|
} else {
|
||||||
|
25
src/public/javascripts/services/hoisted_note.js
Normal file
25
src/public/javascripts/services/hoisted_note.js
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import optionsInit from './options_init.js';
|
||||||
|
import server from "./server.js";
|
||||||
|
|
||||||
|
let hoistedNoteId;
|
||||||
|
|
||||||
|
optionsInit.optionsReady.then(options => {
|
||||||
|
hoistedNoteId = options['hoistedNoteId'];
|
||||||
|
});
|
||||||
|
|
||||||
|
async function getHoistedNoteId() {
|
||||||
|
await optionsInit.optionsReady;
|
||||||
|
|
||||||
|
return hoistedNoteId;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setHoistedNoteId(noteId) {
|
||||||
|
hoistedNoteId = noteId;
|
||||||
|
|
||||||
|
await server.put('options/hoistedNoteId/' + noteId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
getHoistedNoteId,
|
||||||
|
setHoistedNoteId
|
||||||
|
}
|
@ -4,13 +4,18 @@ import Branch from "../entities/branch.js";
|
|||||||
import server from "./server.js";
|
import server from "./server.js";
|
||||||
import treeCache from "./tree_cache.js";
|
import treeCache from "./tree_cache.js";
|
||||||
import messagingService from "./messaging.js";
|
import messagingService from "./messaging.js";
|
||||||
|
import hoistedNoteService from "./hoisted_note.js";
|
||||||
|
|
||||||
async function prepareTree(noteRows, branchRows, relations) {
|
async function prepareTree(noteRows, branchRows, relations) {
|
||||||
utils.assertArguments(noteRows, branchRows, relations);
|
utils.assertArguments(noteRows, branchRows, relations);
|
||||||
|
|
||||||
treeCache.load(noteRows, branchRows, relations);
|
treeCache.load(noteRows, branchRows, relations);
|
||||||
|
|
||||||
return [ await prepareNode(await treeCache.getBranch('root')) ];
|
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
|
||||||
|
const hoistedNote = await treeCache.getNote(hoistedNoteId);
|
||||||
|
const hoistedBranch = (await hoistedNote.getBranches())[0];
|
||||||
|
|
||||||
|
return [ await prepareNode(hoistedBranch) ];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function prepareBranch(note) {
|
async function prepareBranch(note) {
|
||||||
|
@ -10,7 +10,7 @@ import exportDialog from '../dialogs/export.js';
|
|||||||
import infoService from "./info.js";
|
import infoService from "./info.js";
|
||||||
import treeCache from "./tree_cache.js";
|
import treeCache from "./tree_cache.js";
|
||||||
import syncService from "./sync.js";
|
import syncService from "./sync.js";
|
||||||
import contextMenuService from "./context_menu.js";
|
import hoistedNoteService from './hoisted_note.js';
|
||||||
|
|
||||||
const $tree = $("#tree");
|
const $tree = $("#tree");
|
||||||
|
|
||||||
@ -83,6 +83,8 @@ const contextMenuItems = [
|
|||||||
{title: "Insert child note <kbd>Ctrl+P</kbd>", cmd: "insertChildNote", uiIcon: "plus"},
|
{title: "Insert child note <kbd>Ctrl+P</kbd>", cmd: "insertChildNote", uiIcon: "plus"},
|
||||||
{title: "Delete", cmd: "delete", uiIcon: "trash"},
|
{title: "Delete", cmd: "delete", uiIcon: "trash"},
|
||||||
{title: "----"},
|
{title: "----"},
|
||||||
|
{title: "Hoist note", cmd: "hoist", uiIcon: "arrow-up"},
|
||||||
|
{title: "Unhoist note", cmd: "unhoist", uiIcon: "arrow-up"},
|
||||||
{title: "Edit branch prefix <kbd>F2</kbd>", cmd: "editBranchPrefix", uiIcon: "pencil"},
|
{title: "Edit branch prefix <kbd>F2</kbd>", cmd: "editBranchPrefix", uiIcon: "pencil"},
|
||||||
{title: "----"},
|
{title: "----"},
|
||||||
{title: "Protect subtree", cmd: "protectSubtree", uiIcon: "shield-check"},
|
{title: "Protect subtree", cmd: "protectSubtree", uiIcon: "shield-check"},
|
||||||
@ -101,6 +103,16 @@ const contextMenuItems = [
|
|||||||
{title: "Sort alphabetically <kbd>Alt+S</kbd>", cmd: "sortAlphabetically", uiIcon: "arrows-v"}
|
{title: "Sort alphabetically <kbd>Alt+S</kbd>", cmd: "sortAlphabetically", uiIcon: "arrows-v"}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function hideItem(cmd, hidden) {
|
||||||
|
const item = contextMenuItems.find(item => item.cmd === cmd);
|
||||||
|
|
||||||
|
if (!item) {
|
||||||
|
throw new Error(`Command ${cmd} has not been found!`);
|
||||||
|
}
|
||||||
|
|
||||||
|
item.hidden = hidden;
|
||||||
|
}
|
||||||
|
|
||||||
function enableItem(cmd, enabled) {
|
function enableItem(cmd, enabled) {
|
||||||
const item = contextMenuItems.find(item => item.cmd === cmd);
|
const item = contextMenuItems.find(item => item.cmd === cmd);
|
||||||
|
|
||||||
@ -130,6 +142,11 @@ async function getContextMenuItems(event) {
|
|||||||
enableItem("export", note.type !== 'search');
|
enableItem("export", note.type !== 'search');
|
||||||
enableItem("editBranchPrefix", isNotRoot && parentNote.type !== 'search');
|
enableItem("editBranchPrefix", isNotRoot && parentNote.type !== 'search');
|
||||||
|
|
||||||
|
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
|
||||||
|
|
||||||
|
hideItem("hoist", note.noteId === hoistedNoteId);
|
||||||
|
hideItem("unhoist", note.noteId !== hoistedNoteId || !isNotRoot);
|
||||||
|
|
||||||
// Activate node on right-click
|
// Activate node on right-click
|
||||||
node.setActive();
|
node.setActive();
|
||||||
|
|
||||||
@ -194,6 +211,12 @@ function selectContextMenuItem(event, cmd) {
|
|||||||
else if (cmd === "sortAlphabetically") {
|
else if (cmd === "sortAlphabetically") {
|
||||||
treeService.sortAlphabetically(node.data.noteId);
|
treeService.sortAlphabetically(node.data.noteId);
|
||||||
}
|
}
|
||||||
|
else if (cmd === "hoist") {
|
||||||
|
hoistedNoteService.setHoistedNoteId(node.data.noteId);
|
||||||
|
}
|
||||||
|
else if (cmd === "unhoist") {
|
||||||
|
hoistedNoteService.setHoistedNoteId('root');
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
messagingService.logError("Unknown command: " + cmd);
|
messagingService.logError("Unknown command: " + cmd);
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ const log = require('../../services/log');
|
|||||||
|
|
||||||
// options allowed to be updated directly in options dialog
|
// options allowed to be updated directly in options dialog
|
||||||
const ALLOWED_OPTIONS = ['protectedSessionTimeout', 'noteRevisionSnapshotTimeInterval',
|
const ALLOWED_OPTIONS = ['protectedSessionTimeout', 'noteRevisionSnapshotTimeInterval',
|
||||||
'zoomFactor', 'theme', 'syncServerHost', 'syncServerTimeout', 'syncProxy', 'leftPaneMinWidth', 'leftPaneWidthPercent'];
|
'zoomFactor', 'theme', 'syncServerHost', 'syncServerTimeout', 'syncProxy', 'leftPaneMinWidth', 'leftPaneWidthPercent', 'hoistedNoteId'];
|
||||||
|
|
||||||
async function getOptions() {
|
async function getOptions() {
|
||||||
return await optionService.getOptionsMap(ALLOWED_OPTIONS);
|
return await optionService.getOptionsMap(ALLOWED_OPTIONS);
|
||||||
|
@ -50,18 +50,20 @@ async function getRelations(noteIds) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function getTree() {
|
async function getTree() {
|
||||||
|
const hoistedNoteId = await optionService.getOption('hoistedNoteId');
|
||||||
|
|
||||||
// we fetch all branches of notes, even if that particular branch isn't visible
|
// we fetch all branches of notes, even if that particular branch isn't visible
|
||||||
// this allows us to e.g. detect and properly display clones
|
// this allows us to e.g. detect and properly display clones
|
||||||
const branches = await sql.getRows(`
|
const branches = await sql.getRows(`
|
||||||
WITH RECURSIVE
|
WITH RECURSIVE
|
||||||
tree(branchId, noteId, isExpanded) AS (
|
tree(branchId, noteId, isExpanded) AS (
|
||||||
SELECT branchId, noteId, isExpanded FROM branches WHERE branchId = 'root'
|
SELECT branchId, noteId, isExpanded FROM branches WHERE noteId = ?
|
||||||
UNION ALL
|
UNION ALL
|
||||||
SELECT branches.branchId, branches.noteId, branches.isExpanded FROM branches
|
SELECT branches.branchId, branches.noteId, branches.isExpanded FROM branches
|
||||||
JOIN tree ON branches.parentNoteId = tree.noteId
|
JOIN tree ON branches.parentNoteId = tree.noteId
|
||||||
WHERE tree.isExpanded = 1 AND branches.isDeleted = 0
|
WHERE tree.isExpanded = 1 AND branches.isDeleted = 0
|
||||||
)
|
)
|
||||||
SELECT branches.* FROM tree JOIN branches USING(noteId) WHERE branches.isDeleted = 0 ORDER BY branches.notePosition`);
|
SELECT branches.* FROM tree JOIN branches USING(noteId) WHERE branches.isDeleted = 0 ORDER BY branches.notePosition`, [hoistedNoteId]);
|
||||||
|
|
||||||
const noteIds = Array.from(new Set(branches.map(b => b.noteId)));
|
const noteIds = Array.from(new Set(branches.map(b => b.noteId)));
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
const build = require('./build');
|
const build = require('./build');
|
||||||
const packageJson = require('../../package');
|
const packageJson = require('../../package');
|
||||||
|
|
||||||
const APP_DB_VERSION = 120;
|
const APP_DB_VERSION = 121;
|
||||||
const SYNC_VERSION = 2;
|
const SYNC_VERSION = 2;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user