diff --git a/apps/client/src/widgets/note_tree.ts b/apps/client/src/widgets/note_tree.ts index 87d4e39e2..3d0cf503f 100644 --- a/apps/client/src/widgets/note_tree.ts +++ b/apps/client/src/widgets/note_tree.ts @@ -153,7 +153,7 @@ const TPL = /*html*/` const MAX_SEARCH_RESULTS_IN_TREE = 100; // this has to be hanged on the actual elements to effectively intercept and stop click event -const cancelClickPropagation: (e: JQuery.ClickEvent | MouseEvent) => void = (e) => e.stopPropagation(); +const cancelClickPropagation: (e: Event) => void = (e) => e.stopPropagation(); // TODO: Fix once we remove Node.js API from public type Timeout = NodeJS.Timeout | string | number | undefined; @@ -190,6 +190,9 @@ export interface DragData { export const TREE_CLIPBOARD_TYPE = "application/x-fancytree-node"; +/** Entity changes below the given threshold will be processed without batching to avoid performance degradation. */ +const BATCH_UPDATE_THRESHOLD = 10; + export default class NoteTreeWidget extends NoteContextAwareWidget { private $tree!: JQuery; private $treeActions!: JQuery; @@ -1306,7 +1309,7 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { } async #executeTreeUpdates(refreshCtx: RefreshContext, loadResults: LoadResults) { - const batchUpdate = async () => { + const performUpdates = async () => { for (const noteId of refreshCtx.noteIdsToReload) { for (const node of this.getNodesByNoteId(noteId)) { await node.load(true); @@ -1324,11 +1327,16 @@ export default class NoteTreeWidget extends NoteContextAwareWidget { } }; - if (refreshCtx.noteIdsToReload.size + refreshCtx.noteIdsToUpdate.size >= 10) { - // Despite the name, batchUpdate is actually slowing things down for smaller updates. - await this.batchUpdate(batchUpdate); + if (refreshCtx.noteIdsToReload.size + refreshCtx.noteIdsToUpdate.size >= BATCH_UPDATE_THRESHOLD) { + /** + * Batch updates are used for large number of updates to prevent multiple re-renders, however in the context of small updates (such as changing a note title) + * it can cause up to 400ms of delay for ~8k notes which is not acceptable. Therefore we use batching only for larger number of updates. + * Without batching, the updates would take a couple of milliseconds. + * We still keep the batching for potential cases where there are many updates, for example in a sync. + */ + await this.batchUpdate(performUpdates); } else { - await batchUpdate(); + await performUpdates(); } // for some reason, node update cannot be in the batchUpdate() block (node is not re-rendered) @@ -1808,7 +1816,6 @@ function buildEnhanceTitle() { const createChildTemplate = document.createElement("span"); createChildTemplate.className = "tree-item-button tn-icon add-note-button bx bx-plus"; createChildTemplate.title = t("note_tree.create-child-note"); - createChildTemplate.addEventListener("click", cancelClickPropagation); return async function enhanceTitle(event: Event, data: { @@ -1859,7 +1866,9 @@ function buildEnhanceTitle() { && !note.isLaunchBarConfig() && !note.noteId.startsWith("_help") ) { - node.span.append(createChildTemplate.cloneNode()); + const createChildItem = createChildTemplate.cloneNode(); + createChildItem.addEventListener("click", cancelClickPropagation); + node.span.append(createChildItem); } if (isHoistedNote) {