mirror of
https://github.com/zadam/trilium.git
synced 2025-06-06 18:08:33 +02:00
widgetizing tree WIP
This commit is contained in:
parent
b12e38c231
commit
61474defff
@ -39,7 +39,7 @@ window.glob.isDesktop = utils.isDesktop;
|
||||
window.glob.isMobile = utils.isMobile;
|
||||
|
||||
// required for CKEditor image upload plugin
|
||||
window.glob.getActiveNode = treeService.getActiveNode;
|
||||
window.glob.getActiveNode = () => appContext.getMainNoteTree().getActiveNode();
|
||||
window.glob.getHeaders = server.getHeaders;
|
||||
window.glob.showAddLinkDialog = () => import('./dialogs/add_link.js').then(d => d.showDialog());
|
||||
window.glob.showIncludeNoteDialog = cb => import('./dialogs/include_note.js').then(d => d.showDialog(cb));
|
||||
@ -134,11 +134,11 @@ $noteTabContainer.on("click", ".export-note-button", function () {
|
||||
return;
|
||||
}
|
||||
|
||||
import('./dialogs/export.js').then(d => d.showDialog(treeService.getActiveNode(), 'single'));
|
||||
import('./dialogs/export.js').then(d => d.showDialog(appContext.getMainNoteTree().getActiveNode(), 'single'));
|
||||
});
|
||||
|
||||
$noteTabContainer.on("click", ".import-files-button",
|
||||
() => import('./dialogs/import.js').then(d => d.showDialog(treeService.getActiveNode())));
|
||||
() => import('./dialogs/import.js').then(d => d.showDialog(appContext.getMainNoteTree().getActiveNode())));
|
||||
|
||||
async function printActiveNote() {
|
||||
if ($(this).hasClass("disabled")) {
|
||||
|
@ -39,7 +39,7 @@ export async function showDialog(nodes) {
|
||||
}
|
||||
|
||||
async function moveNotesTo(notePath) {
|
||||
const targetNode = await treeService.getNodeFromPath(notePath);
|
||||
const targetNode = await appContext.getMainNoteTree().getNodeFromPath(notePath);
|
||||
|
||||
await treeChangesService.moveToNode(movedNodes, targetNode);
|
||||
|
||||
|
@ -6,6 +6,7 @@ import contextMenuWidget from "./services/context_menu.js";
|
||||
import treeChangesService from "./services/branches.js";
|
||||
import utils from "./services/utils.js";
|
||||
import treeUtils from "./services/tree_utils.js";
|
||||
import appContext from "./services/app_context.js";
|
||||
|
||||
window.glob.isDesktop = utils.isDesktop;
|
||||
window.glob.isMobile = utils.isMobile;
|
||||
@ -89,7 +90,7 @@ async function showTree() {
|
||||
}
|
||||
|
||||
$detail.on("click", ".note-menu-button", async e => {
|
||||
const node = treeService.getActiveNode();
|
||||
const node = appContext.getMainNoteTree().getActiveNode();
|
||||
const branch = treeCache.getBranch(node.data.branchId);
|
||||
const note = await treeCache.getNote(node.data.noteId);
|
||||
const parentNote = await treeCache.getNote(branch.parentNoteId);
|
||||
|
@ -17,11 +17,13 @@ class AppContext {
|
||||
showWidgets() {
|
||||
const $leftPane = $("#left-pane");
|
||||
|
||||
this.noteTreeWidget = new NoteTreeWidget(this);
|
||||
|
||||
this.widgets = [
|
||||
new GlobalButtonsWidget(this),
|
||||
new SearchBoxWidget(this),
|
||||
new SearchResultsWidget(this),
|
||||
new NoteTreeWidget(this)
|
||||
this.noteTreeWidget
|
||||
];
|
||||
|
||||
for (const widget of this.widgets) {
|
||||
@ -30,6 +32,13 @@ class AppContext {
|
||||
$leftPane.append($widget);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {NoteTreeWidget}
|
||||
*/
|
||||
getMainNoteTree() {
|
||||
return this.noteTreeWidget;
|
||||
}
|
||||
}
|
||||
|
||||
const appContext = new AppContext();
|
||||
|
@ -10,6 +10,7 @@ import keyboardActionService from "./keyboard_actions.js";
|
||||
import hoistedNoteService from "./hoisted_note.js";
|
||||
import treeCache from "./tree_cache.js";
|
||||
import server from "./server.js";
|
||||
import appContext from "./app_context.js";
|
||||
|
||||
const NOTE_REVISIONS = "../dialogs/note_revisions.js";
|
||||
const OPTIONS = "../dialogs/options.js";
|
||||
@ -224,9 +225,7 @@ function registerEntrypoints() {
|
||||
});
|
||||
|
||||
keyboardActionService.setGlobalActionHandler("CloneNotesTo", () => import(CLONE_TO).then(d => {
|
||||
const activeNode = treeService.getActiveNode();
|
||||
|
||||
const selectedOrActiveNodes = treeService.getSelectedOrActiveNodes(activeNode);
|
||||
const selectedOrActiveNodes = appContext.getMainNoteTree().getSelectedOrActiveNodes();
|
||||
|
||||
const noteIds = selectedOrActiveNodes.map(node => node.data.noteId);
|
||||
|
||||
@ -234,9 +233,7 @@ function registerEntrypoints() {
|
||||
}));
|
||||
|
||||
keyboardActionService.setGlobalActionHandler("MoveNotesTo", () => import(MOVE_TO).then(d => {
|
||||
const activeNode = treeService.getActiveNode();
|
||||
|
||||
const selectedOrActiveNodes = treeService.getSelectedOrActiveNodes(activeNode);
|
||||
const selectedOrActiveNodes = appContext.getMainNoteTree().getSelectedOrActiveNodes();
|
||||
|
||||
d.showDialog(selectedOrActiveNodes);
|
||||
}));
|
||||
@ -259,14 +256,14 @@ function registerEntrypoints() {
|
||||
});
|
||||
|
||||
keyboardActionService.setGlobalActionHandler("EditBranchPrefix", async () => {
|
||||
const node = treeService.getActiveNode();
|
||||
const node = appContext.getMainNoteTree().getActiveNode();
|
||||
|
||||
const editBranchPrefixDialog = await import("../dialogs/branch_prefix.js");
|
||||
editBranchPrefixDialog.showDialog(node);
|
||||
});
|
||||
|
||||
keyboardActionService.setGlobalActionHandler("ToggleNoteHoisting", async () => {
|
||||
const node = treeService.getActiveNode();
|
||||
const node = appContext.getMainNoteTree().getActiveNode();
|
||||
|
||||
hoistedNoteService.getHoistedNoteId().then(async hoistedNoteId => {
|
||||
if (node.data.noteId === hoistedNoteId) {
|
||||
@ -283,7 +280,7 @@ function registerEntrypoints() {
|
||||
});
|
||||
|
||||
keyboardActionService.setGlobalActionHandler("SearchInSubtree", () => {
|
||||
const node = treeService.getActiveNode();
|
||||
const node = appContext.getMainNoteTree().getActiveNode();
|
||||
|
||||
searchNotesService.searchInSubtree(node.data.noteId);
|
||||
});
|
||||
|
@ -11,6 +11,7 @@ import dateNotesService from './date_notes.js';
|
||||
import StandardWidget from '../widgets/standard_widget.js';
|
||||
import ws from "./ws.js";
|
||||
import hoistedNoteService from "./hoisted_note.js";
|
||||
import appContext from "./app_context.js";
|
||||
|
||||
/**
|
||||
* This is the main frontend API interface for scripts. It's published in the local "api" object.
|
||||
@ -49,7 +50,7 @@ function FrontendScriptApi(startNote, currentNote, originEntity = null, tabConte
|
||||
*/
|
||||
this.activateNote = async (notePath, noteLoadedListener) => {
|
||||
await treeService.activateNote(notePath, async () => {
|
||||
await treeService.scrollToActiveNote();
|
||||
await appContext.getMainNoteTree().scrollToActiveNote();
|
||||
|
||||
if (noteLoadedListener) {
|
||||
noteLoadedListener();
|
||||
|
@ -9,6 +9,7 @@ import contextMenuService from "./context_menu.js";
|
||||
import treeUtils from "./tree_utils.js";
|
||||
import tabRow from "./tab_row.js";
|
||||
import keyboardActionService from "./keyboard_actions.js";
|
||||
import appContext from "./app_context.js";
|
||||
|
||||
const $tabContentsContainer = $("#note-tab-container");
|
||||
const $savedIndicator = $(".saved-indicator");
|
||||
@ -161,22 +162,20 @@ async function showTab(tabId) {
|
||||
}
|
||||
}
|
||||
|
||||
const oldActiveNode = treeService.getActiveNode();
|
||||
const oldActiveNode = appContext.getMainNoteTree().getActiveNode();
|
||||
|
||||
if (oldActiveNode) {
|
||||
oldActiveNode.setActive(false);
|
||||
}
|
||||
|
||||
treeService.clearSelectedNodes();
|
||||
|
||||
const newActiveTabContext = getActiveTabContext();
|
||||
|
||||
if (newActiveTabContext && newActiveTabContext.notePath) {
|
||||
const newActiveNode = await treeService.getNodeFromPath(newActiveTabContext.notePath);
|
||||
const newActiveNode = await appContext.getMainNoteTree().getNodeFromPath(newActiveTabContext.notePath);
|
||||
|
||||
if (newActiveNode) {
|
||||
if (!newActiveNode.isVisible()) {
|
||||
await treeService.expandToNote(newActiveTabContext.notePath);
|
||||
await appContext.getMainNoteTree().expandToNote(newActiveTabContext.notePath);
|
||||
}
|
||||
|
||||
newActiveNode.setActive(true, {noEvents: true});
|
||||
@ -227,7 +226,7 @@ async function loadNoteDetail(origNotePath, options = {}) {
|
||||
// this is useful when user quickly switches notes (by e.g. holding down arrow) so that we don't
|
||||
// try to render all those loaded notes one after each other. This only guarantees that correct note
|
||||
// will be displayed independent of timing
|
||||
const currentTreeNode = treeService.getActiveNode();
|
||||
const currentTreeNode = appContext.getMainNoteTree().getActiveNode();
|
||||
if (!newTab && currentTreeNode && currentTreeNode.data.noteId !== loadedNote.noteId) {
|
||||
return;
|
||||
}
|
||||
|
@ -2,9 +2,10 @@ import treeService from './tree.js';
|
||||
import treeCache from "./tree_cache.js";
|
||||
import server from './server.js';
|
||||
import toastService from "./toast.js";
|
||||
import appContext from "./app_context.js";
|
||||
|
||||
async function refreshSearch() {
|
||||
const activeNode = treeService.getActiveNode();
|
||||
const activeNode = appContext.getMainNoteTree().getActiveNode();
|
||||
|
||||
activeNode.load(true);
|
||||
activeNode.setExpanded(true);
|
||||
|
@ -11,50 +11,11 @@ import hoistedNoteService from '../services/hoisted_note.js';
|
||||
import optionsService from "../services/options.js";
|
||||
import bundle from "./bundle.js";
|
||||
import keyboardActionService from "./keyboard_actions.js";
|
||||
|
||||
let tree;
|
||||
|
||||
function setTree(treeInstance) {
|
||||
tree = treeInstance;
|
||||
}
|
||||
import appContext from "./app_context.js";
|
||||
|
||||
let setFrontendAsLoaded;
|
||||
const frontendLoaded = new Promise(resolve => { setFrontendAsLoaded = resolve; });
|
||||
|
||||
/**
|
||||
* focused & not active node can happen during multiselection where the node is selected but not activated
|
||||
* (its content is not displayed in the detail)
|
||||
* @return {FancytreeNode|null}
|
||||
*/
|
||||
function getFocusedNode() {
|
||||
return tree.getFocusNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* note that if you want to access data like noteId or isProtected, you need to go into "data" property
|
||||
* @return {FancytreeNode|null}
|
||||
*/
|
||||
function getActiveNode() {
|
||||
return tree.getActiveNode();
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode[]} */
|
||||
async function getNodesByBranchId(branchId) {
|
||||
utils.assertArguments(branchId);
|
||||
|
||||
const branch = treeCache.getBranch(branchId);
|
||||
|
||||
return getNodesByNoteId(branch.noteId).filter(node => node.data.branchId === branchId);
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode[]} */
|
||||
function getNodesByNoteId(noteId) {
|
||||
utils.assertArguments(noteId);
|
||||
|
||||
const list = tree.getNodesByRef(noteId);
|
||||
return list ? list : []; // if no nodes with this refKey are found, fancy tree returns null
|
||||
}
|
||||
|
||||
async function setPrefix(branchId, prefix) {
|
||||
utils.assertArguments(branchId);
|
||||
|
||||
@ -62,7 +23,7 @@ async function setPrefix(branchId, prefix) {
|
||||
|
||||
branch.prefix = prefix;
|
||||
|
||||
for (const node of await getNodesByBranchId(branchId)) {
|
||||
for (const node of await appContext.getMainNoteTree().getNodesByBranchId(branchId)) {
|
||||
await setNodeTitleWithPrefix(node);
|
||||
}
|
||||
}
|
||||
@ -78,80 +39,6 @@ async function setNodeTitleWithPrefix(node) {
|
||||
node.setTitle(utils.escapeHtml(title));
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode} */
|
||||
async function expandToNote(notePath, expandOpts) {
|
||||
return await getNodeFromPath(notePath, true, expandOpts);
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode} */
|
||||
function findChildNode(parentNode, childNoteId) {
|
||||
let foundChildNode = null;
|
||||
|
||||
for (const childNode of parentNode.getChildren()) {
|
||||
if (childNode.data.noteId === childNoteId) {
|
||||
foundChildNode = childNode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return foundChildNode;
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode} */
|
||||
async function getNodeFromPath(notePath, expand = false, expandOpts = {}) {
|
||||
utils.assertArguments(notePath);
|
||||
|
||||
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
|
||||
/** @var {FancytreeNode} */
|
||||
let parentNode = null;
|
||||
|
||||
const runPath = await getRunPath(notePath);
|
||||
|
||||
if (!runPath) {
|
||||
console.error("Could not find run path for notePath:", notePath);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const childNoteId of runPath) {
|
||||
if (childNoteId === hoistedNoteId) {
|
||||
// there must be exactly one node with given hoistedNoteId
|
||||
parentNode = getNodesByNoteId(childNoteId)[0];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// we expand only after hoisted note since before then nodes are not actually present in the tree
|
||||
if (parentNode) {
|
||||
if (!parentNode.isLoaded()) {
|
||||
await parentNode.load();
|
||||
}
|
||||
|
||||
if (expand) {
|
||||
await parentNode.setExpanded(true, expandOpts);
|
||||
}
|
||||
|
||||
await checkFolderStatus(parentNode);
|
||||
|
||||
let foundChildNode = findChildNode(parentNode, childNoteId);
|
||||
|
||||
if (!foundChildNode) { // note might be recently created so we'll force reload and try again
|
||||
await parentNode.load(true);
|
||||
|
||||
foundChildNode = findChildNode(parentNode, childNoteId);
|
||||
|
||||
if (!foundChildNode) {
|
||||
ws.logError(`Can't find node for child node of noteId=${childNoteId} for parent of noteId=${parentNode.data.noteId} and hoistedNoteId=${hoistedNoteId}, requested path is ${notePath}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
parentNode = foundChildNode;
|
||||
}
|
||||
}
|
||||
|
||||
return parentNode;
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode} */
|
||||
async function activateNote(notePath, noteLoadedListener) {
|
||||
utils.assertArguments(notePath);
|
||||
@ -180,7 +67,7 @@ async function activateNote(notePath, noteLoadedListener) {
|
||||
|
||||
utils.closeActiveDialog();
|
||||
|
||||
const node = await expandToNote(notePath);
|
||||
const node = await appContext.getMainNoteTree().expandToNote(notePath);
|
||||
|
||||
if (noteLoadedListener) {
|
||||
noteDetailService.addDetailLoadedListener(node.data.noteId, noteLoadedListener);
|
||||
@ -188,8 +75,6 @@ async function activateNote(notePath, noteLoadedListener) {
|
||||
|
||||
await node.setActive(true);
|
||||
|
||||
clearSelectedNodes();
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
@ -329,23 +214,6 @@ function getSelectedNodes(stopOnParents = false) {
|
||||
return tree.getSelectedNodes(stopOnParents);
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode[]} */
|
||||
function getSelectedOrActiveNodes(node) {
|
||||
let notes = getSelectedNodes(true);
|
||||
|
||||
if (notes.length === 0) {
|
||||
notes.push(node);
|
||||
}
|
||||
|
||||
return notes;
|
||||
}
|
||||
|
||||
function clearSelectedNodes() {
|
||||
for (const selectedNode of getSelectedNodes()) {
|
||||
selectedNode.setSelected(false);
|
||||
}
|
||||
}
|
||||
|
||||
async function treeInitialized() {
|
||||
if (noteDetailService.getTabContexts().length > 0) {
|
||||
// this is just tree reload - tabs are already in place
|
||||
@ -427,13 +295,15 @@ async function treeInitialized() {
|
||||
async function reload() {
|
||||
const notes = await loadTreeData();
|
||||
|
||||
const activeNotePath = getActiveNode() !== null ? await treeUtils.getNotePath(getActiveNode()) : null;
|
||||
const activeNode = appContext.getMainNoteTree().getActiveNode();
|
||||
|
||||
await tree.reload(notes);
|
||||
const activeNotePath = activeNode !== null ? await treeUtils.getNotePath(activeNode) : null;
|
||||
|
||||
await appContext.getMainNoteTree().reload(notes);
|
||||
|
||||
// reactivate originally activated node, but don't trigger note loading
|
||||
if (activeNotePath) {
|
||||
const node = await getNodeFromPath(activeNotePath, true);
|
||||
const node = await appContext.getMainNoteTree().getNodeFromPath(activeNotePath, true);
|
||||
|
||||
await node.setActive(true, {noEvents: true});
|
||||
}
|
||||
@ -461,37 +331,8 @@ async function loadTreeData() {
|
||||
return await treeBuilder.prepareTree();
|
||||
}
|
||||
|
||||
async function collapseTree(node = null) {
|
||||
if (!node) {
|
||||
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
|
||||
|
||||
node = getNodesByNoteId(hoistedNoteId)[0];
|
||||
}
|
||||
|
||||
node.setExpanded(false);
|
||||
|
||||
node.visit(node => node.setExpanded(false));
|
||||
}
|
||||
|
||||
function focusTree() {
|
||||
tree.setFocus();
|
||||
}
|
||||
|
||||
async function scrollToActiveNote() {
|
||||
const activeContext = noteDetailService.getActiveTabContext();
|
||||
|
||||
if (activeContext && activeContext.notePath) {
|
||||
focusTree();
|
||||
|
||||
const node = await expandToNote(activeContext.notePath);
|
||||
|
||||
await node.makeVisible({scrollIntoView: true});
|
||||
node.setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
function setProtected(noteId, isProtected) {
|
||||
getNodesByNoteId(noteId).map(node => {
|
||||
appContext.getMainNoteTree().getNodesByNoteId(noteId).map(node => {
|
||||
node.data.isProtected = isProtected;
|
||||
node.toggleClass("protected", isProtected);
|
||||
});
|
||||
@ -504,7 +345,7 @@ async function setNoteTitle(noteId, title) {
|
||||
|
||||
note.title = title;
|
||||
|
||||
for (const clone of getNodesByNoteId(noteId)) {
|
||||
for (const clone of appContext.getMainNoteTree().getNodesByNoteId(noteId)) {
|
||||
await setNodeTitleWithPrefix(clone);
|
||||
}
|
||||
}
|
||||
@ -512,7 +353,7 @@ async function setNoteTitle(noteId, title) {
|
||||
async function createNewTopLevelNote() {
|
||||
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
|
||||
|
||||
const rootNode = getNodesByNoteId(hoistedNoteId)[0];
|
||||
const rootNode = appContext.getMainNoteTree().getNodesByNoteId(hoistedNoteId)[0];
|
||||
|
||||
await createNote(rootNode, hoistedNoteId, "into");
|
||||
}
|
||||
@ -605,13 +446,11 @@ async function createNote(node, parentNoteId, target, extraOptions = {}) {
|
||||
await newNode.setActive(true);
|
||||
}
|
||||
|
||||
clearSelectedNodes(); // to unmark previously active node
|
||||
|
||||
// need to refresh because original doesn't have methods like .getParent()
|
||||
newNodeData = getNodesByNoteId(branchEntity.noteId)[0];
|
||||
newNodeData = appContext.getMainNoteTree().getNodesByNoteId(branchEntity.noteId)[0];
|
||||
|
||||
// following for cycle will make sure that also clones of a parent are refreshed
|
||||
for (const newParentNode of getNodesByNoteId(parentNoteId)) {
|
||||
for (const newParentNode of appContext.getMainNoteTree().getNodesByNoteId(parentNoteId)) {
|
||||
if (newParentNode.key === newNodeData.getParent().key) {
|
||||
// we've added a note into this one so no need to refresh
|
||||
continue;
|
||||
@ -619,7 +458,7 @@ async function createNote(node, parentNoteId, target, extraOptions = {}) {
|
||||
|
||||
await newParentNode.load(true); // force reload to show up new note
|
||||
|
||||
await checkFolderStatus(newParentNode);
|
||||
await appContext.getMainNoteTree().checkFolderStatus(newParentNode);
|
||||
}
|
||||
|
||||
return {note, branch};
|
||||
@ -688,7 +527,7 @@ ws.subscribeToOutsideSyncMessages(async syncData => {
|
||||
});
|
||||
|
||||
keyboardActionService.setGlobalActionHandler('CreateNoteAfter', async () => {
|
||||
const node = getActiveNode();
|
||||
const node = appContext.getMainNoteTree().getActiveNode();
|
||||
const parentNoteId = node.data.parentNoteId;
|
||||
const isProtected = await treeUtils.getParentProtectedStatus(node);
|
||||
|
||||
@ -703,7 +542,7 @@ keyboardActionService.setGlobalActionHandler('CreateNoteAfter', async () => {
|
||||
});
|
||||
|
||||
async function createNoteInto(saveSelection = false) {
|
||||
const node = getActiveNode();
|
||||
const node = appContext.getMainNoteTree().getActiveNode();
|
||||
|
||||
if (node) {
|
||||
await createNote(node, node.data.noteId, 'into', {
|
||||
@ -713,15 +552,6 @@ async function createNoteInto(saveSelection = false) {
|
||||
}
|
||||
}
|
||||
|
||||
async function checkFolderStatus(node) {
|
||||
const note = await treeCache.getNote(node.data.noteId);
|
||||
|
||||
node.folder = note.type === 'search' || note.getChildNoteIds().length > 0;
|
||||
node.icon = await treeBuilder.getIcon(note);
|
||||
node.extraClasses = await treeBuilder.getExtraClasses(note);
|
||||
node.renderTitle();
|
||||
}
|
||||
|
||||
async function reloadNotes(noteIds, activateNotePath = null) {
|
||||
if (noteIds.length === 0) {
|
||||
return;
|
||||
@ -734,7 +564,7 @@ async function reloadNotes(noteIds, activateNotePath = null) {
|
||||
}
|
||||
|
||||
for (const noteId of noteIds) {
|
||||
for (const node of getNodesByNoteId(noteId)) {
|
||||
for (const node of appContext.getMainNoteTree().getNodesByNoteId(noteId)) {
|
||||
const branch = treeCache.getBranch(node.data.branchId, true);
|
||||
|
||||
if (!branch) {
|
||||
@ -743,13 +573,13 @@ async function reloadNotes(noteIds, activateNotePath = null) {
|
||||
else {
|
||||
await node.load(true);
|
||||
|
||||
await checkFolderStatus(node);
|
||||
await appContext.getMainNoteTree().checkFolderStatus(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (activateNotePath) {
|
||||
const node = await getNodeFromPath(activateNotePath);
|
||||
const node = await appContext.getMainNoteTree().getNodeFromPath(activateNotePath);
|
||||
|
||||
if (node && !node.isActive()) {
|
||||
await node.setActive(true);
|
||||
@ -763,7 +593,7 @@ keyboardActionService.setGlobalActionHandler('CutIntoNote', () => createNoteInto
|
||||
|
||||
keyboardActionService.setGlobalActionHandler('CreateNoteInto', createNoteInto);
|
||||
|
||||
keyboardActionService.setGlobalActionHandler('ScrollToActiveNote', scrollToActiveNote);
|
||||
keyboardActionService.setGlobalActionHandler('ScrollToActiveNote', () => appContext.getMainNoteTree().scrollToActiveNote());
|
||||
|
||||
$(window).bind('hashchange', async function() {
|
||||
if (isNotePathInAddress()) {
|
||||
@ -784,40 +614,23 @@ async function duplicateNote(noteId, parentNoteId) {
|
||||
toastService.showMessage(`Note "${origNote.title}" has been duplicated`);
|
||||
}
|
||||
|
||||
function getNodeByKey(key) {
|
||||
return tree.getNodeByKey(key);
|
||||
}
|
||||
|
||||
frontendLoaded.then(bundle.executeStartupBundles);
|
||||
|
||||
export default {
|
||||
reload,
|
||||
collapseTree,
|
||||
setProtected,
|
||||
activateNote,
|
||||
getFocusedNode,
|
||||
getActiveNode,
|
||||
setNoteTitle,
|
||||
setPrefix,
|
||||
createNote,
|
||||
getSelectedNodes,
|
||||
getSelectedOrActiveNodes,
|
||||
clearSelectedNodes,
|
||||
sortAlphabetically,
|
||||
loadTreeData,
|
||||
treeInitialized,
|
||||
setExpandedToServer,
|
||||
getNodesByNoteId,
|
||||
checkFolderStatus,
|
||||
reloadNotes,
|
||||
expandToNote,
|
||||
getNodeFromPath,
|
||||
resolveNotePath,
|
||||
getSomeNotePath,
|
||||
focusTree,
|
||||
scrollToActiveNote,
|
||||
createNewTopLevelNote,
|
||||
duplicateNote,
|
||||
getNodeByKey,
|
||||
setTree
|
||||
getRunPath
|
||||
};
|
@ -87,7 +87,7 @@ function getTemplates(treeWidget) {
|
||||
return false;
|
||||
},
|
||||
"AddNoteAboveToSelection": () => {
|
||||
const node = treeService.getFocusedNode();
|
||||
const node = treeWidget.getFocusedNode();
|
||||
|
||||
if (!node) {
|
||||
return;
|
||||
|
@ -1,4 +1,5 @@
|
||||
import BasicWidget from "./basic_widget.js";
|
||||
import appContext from "../services/app_context.js";
|
||||
|
||||
const WIDGET_TPL = `
|
||||
<style>
|
||||
@ -34,7 +35,7 @@ class GlobalButtonsWidget extends BasicWidget {
|
||||
|
||||
$createTopLevelNoteButton.on('click', () => this.trigger('createTopLevelNote'));
|
||||
$collapseTreeButton.on('click', () => this.trigger('collapseTree'));
|
||||
$scrollToActiveNoteButton.on('click', () => this.trigger('scrollToActiveNote'));
|
||||
$scrollToActiveNoteButton.on('click', () => appContext.getMainNoteTree().scrollToActiveNote());
|
||||
$toggleSearchButton.on('click', () => this.trigger('toggleSearch'));
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import treeCache from "../services/tree_cache.js";
|
||||
import treeBuilder from "../services/tree_builder.js";
|
||||
import TreeContextMenu from "../services/tree_context_menu.js";
|
||||
import treeChangesService from "../services/branches.js";
|
||||
import ws from "../services/ws.js";
|
||||
|
||||
const TPL = `
|
||||
<style>
|
||||
@ -47,8 +48,8 @@ export default class NoteTreeWidget extends BasicWidget {
|
||||
$tree.on("click", ".unhoist-button", hoistedNoteService.unhoist);
|
||||
$tree.on("click", ".refresh-search-button", searchNotesService.refreshSearch);
|
||||
|
||||
// this does not belong here ...
|
||||
keyboardActionService.setGlobalActionHandler('CollapseTree', () => treeService.collapseTree()); // don't use shortened form since collapseTree() accepts argument
|
||||
// FIXME this does not belong here ...
|
||||
keyboardActionService.setGlobalActionHandler('CollapseTree', () => this.collapseTree()); // don't use shortened form since collapseTree() accepts argument
|
||||
|
||||
// fancytree doesn't support middle click so this is a way to support it
|
||||
$widget.on('mousedown', '.fancytree-title', e => {
|
||||
@ -92,7 +93,7 @@ export default class NoteTreeWidget extends BasicWidget {
|
||||
else {
|
||||
node.setActive();
|
||||
|
||||
treeService.clearSelectedNodes();
|
||||
this.clearSelectedNodes();
|
||||
}
|
||||
|
||||
return false;
|
||||
@ -231,8 +232,6 @@ export default class NoteTreeWidget extends BasicWidget {
|
||||
});
|
||||
|
||||
this.tree = $.ui.fancytree.getTree("#tree");
|
||||
|
||||
treeService.setTree(this.tree);
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode[]} */
|
||||
@ -241,11 +240,11 @@ export default class NoteTreeWidget extends BasicWidget {
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode[]} */
|
||||
getSelectedOrActiveNodes(node) {
|
||||
getSelectedOrActiveNodes(node = null) {
|
||||
let notes = this.getSelectedNodes(true);
|
||||
|
||||
if (notes.length === 0) {
|
||||
notes.push(node);
|
||||
notes.push(node ? node : this.getActiveNode());
|
||||
}
|
||||
|
||||
return notes;
|
||||
@ -263,6 +262,13 @@ export default class NoteTreeWidget extends BasicWidget {
|
||||
node.visit(node => node.setExpanded(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {FancytreeNode|null}
|
||||
*/
|
||||
getActiveNode() {
|
||||
return this.tree.getActiveNode();
|
||||
}
|
||||
|
||||
/**
|
||||
* focused & not active node can happen during multiselection where the node is selected but not activated
|
||||
* (its content is not displayed in the detail)
|
||||
@ -278,9 +284,125 @@ export default class NoteTreeWidget extends BasicWidget {
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME since this operates on note details tab context it seems it does not really belong here
|
||||
async scrollToActiveNote() {
|
||||
const activeContext = noteDetailService.getActiveTabContext();
|
||||
|
||||
if (activeContext && activeContext.notePath) {
|
||||
this.tree.setFocus();
|
||||
|
||||
const node = await this.expandToNote(activeContext.notePath);
|
||||
|
||||
await node.makeVisible({scrollIntoView: true});
|
||||
node.setFocus();
|
||||
}
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode} */
|
||||
async getNodeFromPath(notePath, expand = false, expandOpts = {}) {
|
||||
utils.assertArguments(notePath);
|
||||
|
||||
const hoistedNoteId = await hoistedNoteService.getHoistedNoteId();
|
||||
/** @var {FancytreeNode} */
|
||||
let parentNode = null;
|
||||
|
||||
const runPath = await treeService.getRunPath(notePath);
|
||||
|
||||
if (!runPath) {
|
||||
console.error("Could not find run path for notePath:", notePath);
|
||||
return;
|
||||
}
|
||||
|
||||
for (const childNoteId of runPath) {
|
||||
if (childNoteId === hoistedNoteId) {
|
||||
// there must be exactly one node with given hoistedNoteId
|
||||
parentNode = this.getNodesByNoteId(childNoteId)[0];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// we expand only after hoisted note since before then nodes are not actually present in the tree
|
||||
if (parentNode) {
|
||||
if (!parentNode.isLoaded()) {
|
||||
await parentNode.load();
|
||||
}
|
||||
|
||||
if (expand) {
|
||||
await parentNode.setExpanded(true, expandOpts);
|
||||
}
|
||||
|
||||
await this.checkFolderStatus(parentNode);
|
||||
|
||||
let foundChildNode = this.findChildNode(parentNode, childNoteId);
|
||||
|
||||
if (!foundChildNode) { // note might be recently created so we'll force reload and try again
|
||||
await parentNode.load(true);
|
||||
|
||||
foundChildNode = this.findChildNode(parentNode, childNoteId);
|
||||
|
||||
if (!foundChildNode) {
|
||||
ws.logError(`Can't find node for child node of noteId=${childNoteId} for parent of noteId=${parentNode.data.noteId} and hoistedNoteId=${hoistedNoteId}, requested path is ${notePath}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
parentNode = foundChildNode;
|
||||
}
|
||||
}
|
||||
|
||||
return parentNode;
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode} */
|
||||
findChildNode(parentNode, childNoteId) {
|
||||
let foundChildNode = null;
|
||||
|
||||
for (const childNode of parentNode.getChildren()) {
|
||||
if (childNode.data.noteId === childNoteId) {
|
||||
foundChildNode = childNode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return foundChildNode;
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode} */
|
||||
async expandToNote(notePath, expandOpts) {
|
||||
return this.getNodeFromPath(notePath, true, expandOpts);
|
||||
}
|
||||
|
||||
async checkFolderStatus(node) {
|
||||
const note = await treeCache.getNote(node.data.noteId);
|
||||
|
||||
node.folder = note.type === 'search' || note.getChildNoteIds().length > 0;
|
||||
node.icon = await treeBuilder.getIcon(note);
|
||||
node.extraClasses = await treeBuilder.getExtraClasses(note);
|
||||
node.renderTitle();
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode[]} */
|
||||
async getNodesByBranchId(branchId) {
|
||||
utils.assertArguments(branchId);
|
||||
|
||||
const branch = treeCache.getBranch(branchId);
|
||||
|
||||
return this.getNodesByNoteId(branch.noteId).filter(node => node.data.branchId === branchId);
|
||||
}
|
||||
|
||||
/** @return {FancytreeNode[]} */
|
||||
getNodesByNoteId(noteId) {
|
||||
utils.assertArguments(noteId);
|
||||
|
||||
const list = this.tree.getNodesByRef(noteId);
|
||||
return list ? list : []; // if no nodes with this refKey are found, fancy tree returns null
|
||||
}
|
||||
|
||||
async reload(notes) {
|
||||
await this.tree.reload(notes);
|
||||
}
|
||||
|
||||
createTopLevelNoteListener() { treeService.createNewTopLevelNote(); }
|
||||
|
||||
collapseTreeListener() { this.collapseTree(); }
|
||||
|
||||
scrollToActiveNoteListener() { treeService.scrollToActiveNote(); }
|
||||
}
|
@ -2,6 +2,7 @@ import BasicWidget from "./basic_widget.js";
|
||||
import treeService from "../services/tree.js";
|
||||
import treeCache from "../services/tree_cache.js";
|
||||
import toastService from "../services/toast.js";
|
||||
import appContext from "../services/app_context.js";
|
||||
|
||||
const helpText = `
|
||||
<strong>Search tips</strong> - also see <button class="btn btn-sm" type="button" data-help-page="Search">complete help on search</button>
|
||||
@ -118,7 +119,7 @@ export default class SearchBoxWidget extends BasicWidget {
|
||||
return;
|
||||
}
|
||||
|
||||
let activeNode = treeService.getActiveNode();
|
||||
let activeNode = appContext.getMainNoteTree().getActiveNode();
|
||||
const parentNote = await treeCache.getNote(activeNode.data.noteId);
|
||||
|
||||
if (parentNote.type === 'search') {
|
||||
|
Loading…
x
Reference in New Issue
Block a user