From 58ad13b50131a98fda83f53733bbffc99a1a549f Mon Sep 17 00:00:00 2001 From: ycnmhd <33285087+ycnmhd@users.noreply.github.com> Date: Tue, 21 Jan 2025 22:04:07 +0100 Subject: [PATCH] fix(align-branch): scrolls too late when creating a card in outline mode --- .lintstagedrc.json | 2 +- src/helpers/delay.ts | 16 +- src/helpers/logger.ts | 14 ++ .../align-element-horizontally.ts | 2 +- .../align-element/align-element-vertically.ts | 2 +- .../align-group-of-elements-vertically.ts | 2 +- .../helpers/calculate-scroll-top-relative.ts | 2 +- .../wait-for-active-node-to-stop-moving.ts | 7 +- src/stores/view/default-view-state.ts | 3 +- .../helpers/compare-active-branch.spec.ts | 129 +++++++++++++ .../document/helpers/compare-active-branch.ts | 34 ++++ .../helpers/update-active-branch.spec.ts | 2 +- .../document/helpers/update-active-branch.ts | 66 +++---- .../actions/update-active-branch.ts | 29 +-- .../effects/align-branch/align-branch.ts | 179 +++++++----------- .../align-branch/constants/action-category.ts | 175 ----------------- .../align-branch/constants/action-priority.ts | 51 +++++ .../create-align-branch-actions.ts | 9 +- .../lazy-vertical-scrolling-mode.ts | 6 +- .../align-child-columns.ts | 2 +- .../align-child-group-of-column.ts | 2 +- .../align-inactive-columns.ts | 2 +- .../align-branch/helpers/create-context.ts | 60 ++++++ .../align-branch/helpers/delay-align.ts | 11 +- .../actions/align-parents-nodes.ts | 2 +- .../scroll-first-column-to-the-left.ts | 2 +- .../run-align-branch-actions.ts | 4 +- .../subscriptions/on-document-state-update.ts | 7 +- .../view/subscriptions/on-view-mount.ts | 3 +- .../subscriptions/on-view-state-update.ts | 9 +- src/stores/view/view-reducer.ts | 10 +- 31 files changed, 478 insertions(+), 366 deletions(-) create mode 100644 src/stores/view/reducers/document/helpers/compare-active-branch.spec.ts create mode 100644 src/stores/view/reducers/document/helpers/compare-active-branch.ts delete mode 100644 src/stores/view/subscriptions/effects/align-branch/constants/action-category.ts create mode 100644 src/stores/view/subscriptions/effects/align-branch/constants/action-priority.ts create mode 100644 src/stores/view/subscriptions/effects/align-branch/helpers/create-context.ts diff --git a/.lintstagedrc.json b/.lintstagedrc.json index f2ed7c7..b7e1ca4 100644 --- a/.lintstagedrc.json +++ b/.lintstagedrc.json @@ -1,6 +1,6 @@ { "*.{js,jsx,ts,tsx, svelte}": [ "prettier --write", - "eslint --fix" + "eslint" ] } diff --git a/src/helpers/delay.ts b/src/helpers/delay.ts index 55c3364..4d5a8fd 100644 --- a/src/helpers/delay.ts +++ b/src/helpers/delay.ts @@ -1,3 +1,15 @@ -export const delay = async (milliseconds: number): Promise => { - return new Promise((resolve) => setTimeout(resolve, milliseconds)); +export const delay = async ( + milliseconds: number, + signal?: AbortSignal, +): Promise => { + return new Promise((resolve) => { + const timeout = setTimeout(() => resolve(), milliseconds); + + if (signal) { + signal.addEventListener('abort', () => { + clearTimeout(timeout); + resolve(); + }); + } + }); }; diff --git a/src/helpers/logger.ts b/src/helpers/logger.ts index 00c56f2..f3cc2bf 100644 --- a/src/helpers/logger.ts +++ b/src/helpers/logger.ts @@ -1,4 +1,5 @@ /* eslint-disable no-console */ + type Logger = { debug: (...message: unknown[]) => void; info: (...message: unknown[]) => void; @@ -34,3 +35,16 @@ const createLogger = (): Logger => { }; export const logger = createLogger(); + +/*let i = 0; +export const AlignBranchLogger = (action: PluginAction) => { + const id = i++; + let t = 0; + return { + log: (...params: any[]) => { + const delta = t > 0 ? Date.now() - t : 0; + t = Date.now(); + console.log(`[${id}] [${action.type}] [${delta}]`, ...params); + }, + }; +}*/ diff --git a/src/lib/align-element/align-element-horizontally.ts b/src/lib/align-element/align-element-horizontally.ts index 7bfcb7b..a7f719a 100644 --- a/src/lib/align-element/align-element-horizontally.ts +++ b/src/lib/align-element/align-element-horizontally.ts @@ -1,7 +1,7 @@ import { calculateScrollLeft } from 'src/lib/align-element/helpers/calculate-scroll-left'; import { THRESHOLD } from 'src/lib/align-element/constants'; -import { AlignBranchContext } from 'src/stores/view/subscriptions/effects/align-branch/align-branch'; +import { AlignBranchContext } from 'src/stores/view/subscriptions/effects/align-branch/helpers/create-context'; import { getElementById } from 'src/lib/align-element/helpers/get-element-by-id'; export const alignElementHorizontally = ( diff --git a/src/lib/align-element/align-element-vertically.ts b/src/lib/align-element/align-element-vertically.ts index 64ca389..c08d2c2 100644 --- a/src/lib/align-element/align-element-vertically.ts +++ b/src/lib/align-element/align-element-vertically.ts @@ -3,7 +3,7 @@ import { THRESHOLD } from 'src/lib/align-element/constants'; import { AlignBranchContext, PartialDOMRect, -} from 'src/stores/view/subscriptions/effects/align-branch/align-branch'; +} from 'src/stores/view/subscriptions/effects/align-branch/helpers/create-context'; import { calculateScrollTopRelative } from 'src/lib/align-element/helpers/calculate-scroll-top-relative'; import { getElementById } from 'src/lib/align-element/helpers/get-element-by-id'; diff --git a/src/lib/align-element/align-group-of-elements-vertically.ts b/src/lib/align-element/align-group-of-elements-vertically.ts index 73f7032..0471b35 100644 --- a/src/lib/align-element/align-group-of-elements-vertically.ts +++ b/src/lib/align-element/align-group-of-elements-vertically.ts @@ -1,5 +1,5 @@ import { getCombinedBoundingClientRect } from 'src/lib/align-element/helpers/get-combined-client-rect'; -import { AlignBranchContext } from 'src/stores/view/subscriptions/effects/align-branch/align-branch'; +import { AlignBranchContext } from 'src/stores/view/subscriptions/effects/align-branch/helpers/create-context'; import { alignVertically } from 'src/lib/align-element/align-element-vertically'; import { getElementById } from 'src/lib/align-element/helpers/get-element-by-id'; diff --git a/src/lib/align-element/helpers/calculate-scroll-top-relative.ts b/src/lib/align-element/helpers/calculate-scroll-top-relative.ts index 350286c..d428d7a 100644 --- a/src/lib/align-element/helpers/calculate-scroll-top-relative.ts +++ b/src/lib/align-element/helpers/calculate-scroll-top-relative.ts @@ -3,7 +3,7 @@ import { TOOLBAR_HEIGHT, } from 'src/lib/align-element/constants'; -import { PartialDOMRect } from 'src/stores/view/subscriptions/effects/align-branch/align-branch'; +import { PartialDOMRect } from 'src/stores/view/subscriptions/effects/align-branch/helpers/create-context'; export const calculateScrollTopRelative = ( elementRect: DOMRect, diff --git a/src/lib/align-element/helpers/wait-for-active-node-to-stop-moving.ts b/src/lib/align-element/helpers/wait-for-active-node-to-stop-moving.ts index 2e667b0..4613082 100644 --- a/src/lib/align-element/helpers/wait-for-active-node-to-stop-moving.ts +++ b/src/lib/align-element/helpers/wait-for-active-node-to-stop-moving.ts @@ -6,7 +6,10 @@ const LOOP_DELAY_MS = 10; const MAX_ATTEMPTS = 100; const REQUIRED_MATCHES = 5; -export const waitForActiveNodeToStopMoving = async (view: LineageView) => { +export const waitForActiveNodeToStopMoving = async ( + view: LineageView, + signal: AbortSignal, +) => { const activeBranch = view.viewStore.getValue().document.activeBranch; let columnEl: HTMLElement | undefined; @@ -18,7 +21,7 @@ export const waitForActiveNodeToStopMoving = async (view: LineageView) => { let lastScrollLeft = -1; const container = view.container!; - while (retries < MAX_ATTEMPTS) { + while (retries < MAX_ATTEMPTS && !signal.aborted) { if (!columnEl) { columnEl = getElementById(container, activeBranch.column)!; } else { diff --git a/src/stores/view/default-view-state.ts b/src/stores/view/default-view-state.ts index e73623b..eea4000 100644 --- a/src/stores/view/default-view-state.ts +++ b/src/stores/view/default-view-state.ts @@ -1,5 +1,4 @@ import { ViewState } from 'src/stores/view/view-state-type'; -import { NodeId } from 'src/stores/document/document-state-type'; export const defaultViewState = (): ViewState => ({ search: { @@ -75,7 +74,7 @@ export const defaultViewState = (): ViewState => ({ }); export type ActiveBranch = { childGroups: Set; - sortedParentNodes: NodeId[]; + sortedParentNodes: string[]; group: string; column: string; node: string; diff --git a/src/stores/view/reducers/document/helpers/compare-active-branch.spec.ts b/src/stores/view/reducers/document/helpers/compare-active-branch.spec.ts new file mode 100644 index 0000000..d102545 --- /dev/null +++ b/src/stores/view/reducers/document/helpers/compare-active-branch.spec.ts @@ -0,0 +1,129 @@ +import { describe, expect, it } from 'vitest'; +import { ActiveBranch } from 'src/stores/view/reducers/document/helpers/update-active-branch'; +import { compareActiveBranch } from 'src/stores/view/reducers/document/helpers/compare-active-branch'; + +describe('compareActiveBranch', () => { + it('should return true for identical branches', () => { + const branch: ActiveBranch = { + childGroups: new Set(['n1.1', 'n2.1']), + sortedParentNodes: ['n1', 'n2'], + group: 'g1', + column: 'col1', + node: 'n2.1.1', + }; + + expect(compareActiveBranch(branch, { ...branch })).toBe(true); + }); + + it('should return true for same reference', () => { + const branch: ActiveBranch = { + childGroups: new Set(['n1.1']), + sortedParentNodes: ['n1'], + group: 'g1', + column: 'col1', + node: 'n1.1', + }; + + expect(compareActiveBranch(branch, branch)).toBe(true); + }); + + it('should return false for different primitive values', () => { + const branch1: ActiveBranch = { + childGroups: new Set(['n1.1']), + sortedParentNodes: ['n1'], + group: 'g1', + column: 'col1', + node: 'n1.1', + }; + + const branch2: ActiveBranch = { + childGroups: new Set(['n1.1']), + sortedParentNodes: ['n1'], + group: 'g2', // different group + column: 'col1', + node: 'n1.1', + }; + + expect(compareActiveBranch(branch1, branch2)).toBe(false); + }); + + it('should return false for different sortedParentNodes', () => { + const branch1: ActiveBranch = { + childGroups: new Set(['n1.1']), + sortedParentNodes: ['n1', 'n2'], + group: 'g1', + column: 'col1', + node: 'n2.1', + }; + + const branch2: ActiveBranch = { + childGroups: new Set(['n1.1']), + sortedParentNodes: ['n1'], // different length + group: 'g1', + column: 'col1', + node: 'n2.1', + }; + + expect(compareActiveBranch(branch1, branch2)).toBe(false); + + const branch3: ActiveBranch = { + childGroups: new Set(['n1.1']), + sortedParentNodes: ['n2', 'n1'], // different order + group: 'g1', + column: 'col1', + node: 'n2.1', + }; + + expect(compareActiveBranch(branch1, branch3)).toBe(false); + }); + + it('should return false for different childGroups', () => { + const branch1: ActiveBranch = { + childGroups: new Set(['n1.1', 'n2.1']), + sortedParentNodes: ['n1', 'n2'], + group: 'g1', + column: 'col1', + node: 'n2.1.1', + }; + + const branch2: ActiveBranch = { + childGroups: new Set(['n1.1']), // missing n2.1 + sortedParentNodes: ['n1', 'n2'], + group: 'g1', + column: 'col1', + node: 'n2.1.1', + }; + + expect(compareActiveBranch(branch1, branch2)).toBe(false); + + const branch3: ActiveBranch = { + childGroups: new Set(['n1.1', 'n2.2']), // different value + sortedParentNodes: ['n1', 'n2'], + group: 'g1', + column: 'col1', + node: 'n2.1.1', + }; + + expect(compareActiveBranch(branch1, branch3)).toBe(false); + }); + + it('should return true for identical states', () => { + const branch1: ActiveBranch = { + childGroups: new Set(['n1.1', 'n2.1', 'n2.1.1']), + sortedParentNodes: ['n1', 'n2', 'n2.1'], + group: 'g1', + column: 'col1', + node: 'n2.1.1', + }; + + const branch2: ActiveBranch = { + childGroups: new Set(['n1.1', 'n2.1', 'n2.1.1']), + sortedParentNodes: ['n1', 'n2', 'n2.1'], + group: 'g1', + column: 'col1', + node: 'n2.1.1', + }; + + expect(compareActiveBranch(branch1, branch2)).toBe(true); + }); +}); diff --git a/src/stores/view/reducers/document/helpers/compare-active-branch.ts b/src/stores/view/reducers/document/helpers/compare-active-branch.ts new file mode 100644 index 0000000..2474927 --- /dev/null +++ b/src/stores/view/reducers/document/helpers/compare-active-branch.ts @@ -0,0 +1,34 @@ +import { ActiveBranch } from 'src/stores/view/reducers/document/helpers/update-active-branch'; + +export const compareActiveBranch = ( + a: ActiveBranch, + b: ActiveBranch, +): boolean => { + if (a === b) return true; + + if (a.group !== b.group || a.column !== b.column || a.node !== b.node) { + return false; + } + + if (a.sortedParentNodes.length !== b.sortedParentNodes.length) { + return false; + } + + if (a.childGroups.size !== b.childGroups.size) { + return false; + } + + for (let i = 0; i < a.sortedParentNodes.length; i++) { + if (a.sortedParentNodes[i] !== b.sortedParentNodes[i]) { + return false; + } + } + + for (const item of a.childGroups) { + if (!b.childGroups.has(item)) { + return false; + } + } + + return true; +}; diff --git a/src/stores/view/reducers/document/helpers/update-active-branch.spec.ts b/src/stores/view/reducers/document/helpers/update-active-branch.spec.ts index b729ce4..0750935 100644 --- a/src/stores/view/reducers/document/helpers/update-active-branch.spec.ts +++ b/src/stores/view/reducers/document/helpers/update-active-branch.spec.ts @@ -107,7 +107,7 @@ describe('update-tree-state', () => { node: activeNodeId, } satisfies ActiveBranch, }; - updateActiveBranch(input.state, input.document.columns, 'structure'); + updateActiveBranch(input.state, input.document.columns, true); expect(input.state.activeBranch).toEqual(output.activeBranch); }); }); diff --git a/src/stores/view/reducers/document/helpers/update-active-branch.ts b/src/stores/view/reducers/document/helpers/update-active-branch.ts index 5adbc68..75a561f 100644 --- a/src/stores/view/reducers/document/helpers/update-active-branch.ts +++ b/src/stores/view/reducers/document/helpers/update-active-branch.ts @@ -5,17 +5,30 @@ import { findGroupByNodeId } from 'src/lib/tree-utils/find/find-group-by-node-id import { findNodeColumn } from 'src/lib/tree-utils/find/find-node-column'; import { DocumentViewState } from 'src/stores/view/view-state-type'; import { removeStaleActiveNodes } from 'src/stores/view/reducers/document/helpers/remove-stale-active-nodes'; +import { compareActiveBranch } from 'src/stores/view/reducers/document/helpers/compare-active-branch'; +import { DocumentStoreAction } from 'src/stores/document/document-store-actions'; -export type ChangeType = 'none' | 'structure'; +export type UpdateActiveBranchAction = + | { + type: 'view/update-active-branch?source=view'; + context: { + columns: Column[]; + }; + } + | { + type: 'view/update-active-branch?source=document'; + context: { + columns: Column[]; + documentAction: DocumentStoreAction; + }; + }; -export type UpdateActiveBranchAction = { - type: 'UPDATE_ACTIVE_BRANCH'; - payload: { - columns: Column[]; - }; - context: { - changeType: ChangeType; - }; +export type ActiveBranch = { + childGroups: Set; + sortedParentNodes: string[]; + group: string; + column: string; + node: string; }; export const updateActiveBranch = ( @@ -24,7 +37,7 @@ export const updateActiveBranch = ( 'activeBranch' | 'activeNode' | 'activeNodesOfColumn' >, columns: Column[], - changeType: ChangeType, + isDocumentAction: boolean, ) => { if (!state.activeNode) return; const sortedParents = traverseUp(columns, state.activeNode).reverse(); @@ -34,33 +47,22 @@ export const updateActiveBranch = ( throw new Error('could not find group for node ' + state.activeNode); const columnId = columns[findNodeColumn(columns, state.activeNode)].id; - const needsUpdate = - state.activeNode !== state.activeBranch.node || - childGroups.length !== state.activeBranch.childGroups.size || - sortedParents.length !== state.activeBranch.sortedParentNodes.length || - group.parentId !== state.activeBranch.group || - columnId !== state.activeBranch.column || - childGroups.some( - (group) => !state.activeBranch.childGroups.has(group), - ) || - sortedParents.some( - (node, i) => node !== state.activeBranch.sortedParentNodes[i], - ); - - if (needsUpdate) { - state.activeBranch = { - childGroups: new Set(childGroups), - sortedParentNodes: sortedParents, - group: group.parentId, - column: columnId, - node: state.activeNode, - }; + const newActiveBranch = { + childGroups: new Set(childGroups), + sortedParentNodes: sortedParents, + group: group.parentId, + column: columnId, + node: state.activeNode, + }; + const same = compareActiveBranch(state.activeBranch, newActiveBranch); + if (!same) { + state.activeBranch = newActiveBranch; } if (!state.activeNodesOfColumn[columnId]) state.activeNodesOfColumn[columnId] = {}; state.activeNodesOfColumn[columnId][group.parentId] = state.activeNode; - if (changeType === 'structure') { + if (isDocumentAction) { state.activeNodesOfColumn = removeStaleActiveNodes( columns, state.activeNodesOfColumn, diff --git a/src/stores/view/subscriptions/actions/update-active-branch.ts b/src/stores/view/subscriptions/actions/update-active-branch.ts index a049204..1a10889 100644 --- a/src/stores/view/subscriptions/actions/update-active-branch.ts +++ b/src/stores/view/subscriptions/actions/update-active-branch.ts @@ -1,19 +1,26 @@ import { ViewStore } from 'src/view/view'; import { DocumentState } from 'src/stores/document/document-state-type'; -import { ChangeType } from 'src/stores/view/reducers/document/helpers/update-active-branch'; +import { DocumentStoreAction } from 'src/stores/document/document-store-actions'; export const updateActiveBranch = ( viewStore: ViewStore, documentState: DocumentState, - changeType: ChangeType, + documentAction?: DocumentStoreAction, ) => { - viewStore.dispatch({ - type: 'UPDATE_ACTIVE_BRANCH', - payload: { - columns: documentState.document.columns, - }, - context: { - changeType, - }, - }); + if (documentAction) { + viewStore.dispatch({ + type: 'view/update-active-branch?source=document', + context: { + columns: documentState.document.columns, + documentAction, + }, + }); + } else { + viewStore.dispatch({ + type: 'view/update-active-branch?source=view', + context: { + columns: documentState.document.columns, + }, + }); + } }; diff --git a/src/stores/view/subscriptions/effects/align-branch/align-branch.ts b/src/stores/view/subscriptions/effects/align-branch/align-branch.ts index 68277ff..b69fe59 100644 --- a/src/stores/view/subscriptions/effects/align-branch/align-branch.ts +++ b/src/stores/view/subscriptions/effects/align-branch/align-branch.ts @@ -1,9 +1,6 @@ import { LineageView } from 'src/view/view'; import { delay } from 'src/helpers/delay'; import { delayAlign } from 'src/stores/view/subscriptions/effects/align-branch/helpers/delay-align'; -import { ActiveNodesOfColumn } from 'src/stores/view/view-state-type'; -import { Column } from 'src/stores/document/document-state-type'; -import { adjustScrollBehavior } from 'src/stores/view/subscriptions/effects/align-branch/helpers/adjust-scroll-behavior'; import { ActiveBranch } from 'src/stores/view/default-view-state'; import { createAlignBranchActions } from 'src/stores/view/subscriptions/effects/align-branch/create-align-branch-actions/create-align-branch-actions'; import { runAlignBranchActions } from 'src/stores/view/subscriptions/effects/align-branch/run-align-branch-actions/run-align-branch-actions'; @@ -15,38 +12,11 @@ import { } from 'src/stores/view/view-store-actions'; import { SettingsActions } from 'src/stores/settings/settings-reducer'; import { DocumentsStoreAction } from 'src/stores/documents/documents-store-actions'; -import { - ActionCategory, - actionCategory, - actionCategoryPriority, -} from 'src/stores/view/subscriptions/effects/align-branch/constants/action-category'; import { waitForActiveNodeToStopMoving } from 'src/lib/align-element/helpers/wait-for-active-node-to-stop-moving'; +import { createContext } from 'src/stores/view/subscriptions/effects/align-branch/helpers/create-context'; import { SilentError } from 'src/lib/errors/errors'; - -export type PartialDOMRect = Pick; - -export type AlignBranchState = { - rects: Map; -}; - -export type AlignBranchSettings = { - behavior: ScrollBehavior; - centerActiveNodeH: boolean; - centerActiveNodeV: boolean; - zoomLevel: number; -}; - -export type AlignBranchContext = { - previousActiveBranch: ActiveBranch | null; - columns: Column[]; - activeBranch: ActiveBranch; - container: HTMLElement; - containerRect: DOMRect; - outlineMode: boolean; - alignBranchSettings: AlignBranchSettings; - state: AlignBranchState; - activeNodesOfColumn: ActiveNodesOfColumn; -}; +import { actionPriority } from 'src/stores/view/subscriptions/effects/align-branch/constants/action-priority'; +import { logger } from 'src/helpers/logger'; export type PluginAction = | DocumentStoreAction @@ -63,87 +33,82 @@ export type PreviousScrollBehavior = { behavior: ScrollBehavior; }; -export type AlignEvent = { - category: ActionCategory; - action: PluginAction['type']; - priority: number; - ts: number; -}; - export class AlignBranch { + private isRunning: boolean = false; + private currentEvent: { + action: PluginAction; + controller: AbortController; + priority: number; + } | null = null; + private previousActiveBranch: ActiveBranch | null = null; - private previousEvent: AlignEvent | null = null; constructor(public view: LineageView) {} - align = async (action: PluginAction) => { - if (skipAlign(this.view, action)) return; - - const delay_ms = delayAlign(action); - if (delay_ms > 0) await delay(delay_ms); - await this.waitForPreviousEvent(action); - - const context = this.createContext(action); - const actions = createAlignBranchActions(context, action); - - requestAnimationFrame(() => { - runAlignBranchActions(context, actions); - }); - this.saveActiveBranch(context); - }; - - private createContext = (action: PluginAction) => { - const settings = this.view.plugin.settings.getValue(); - const container = this.view.container!; - const documentState = this.view.documentStore.getValue(); - const viewState = this.view.viewStore.getValue(); - const activeBranch = viewState.document.activeBranch; - - const behavior = adjustScrollBehavior(action); - - const context: AlignBranchContext = { - previousActiveBranch: this.previousActiveBranch, - activeBranch: activeBranch, - columns: documentState.document.columns, - container, - activeNodesOfColumn: viewState.document.activeNodesOfColumn, - containerRect: container.getBoundingClientRect(), - outlineMode: settings.view.outlineMode, - alignBranchSettings: { - centerActiveNodeH: settings.view.scrolling.centerActiveNodeH, - centerActiveNodeV: settings.view.scrolling.centerActiveNodeV, - zoomLevel: settings.view.zoomLevel, - behavior: behavior, - }, - state: { - rects: new Map(), - }, - }; - return context; - }; - - private saveActiveBranch(context: AlignBranchContext) { - this.previousActiveBranch = context.activeBranch; - } - - private waitForPreviousEvent = async (action: PluginAction) => { - const category = actionCategory.get(action.type)!; - if (category === 'other') { - throw new SilentError('unsupported event: ' + action.type); + align = (action: PluginAction) => { + const priority = actionPriority.get(action.type); + if (typeof priority !== 'number') { + throw new SilentError(action.type + ' not allowed'); } - const event: AlignEvent = { - action: action.type, - category: category!, - priority: actionCategoryPriority.get(category)!, - ts: Date.now(), - }; - if (this.previousEvent) { - if (event.priority < this.previousEvent.priority) { - if (event.ts - this.previousEvent.ts < 500) { - await waitForActiveNodeToStopMoving(this.view); - } + + if (this.currentEvent) { + if (priority >= this.currentEvent.priority) { + this.currentEvent.controller.abort(); + } else { + return; } } - this.previousEvent = event; + this.currentEvent = { + action, + priority, + controller: new AbortController(), + }; + if (!this.isRunning) { + this.run(); + } + }; + + private run = async () => { + this.isRunning = true; + while (this.currentEvent) { + const event = this.currentEvent; + try { + if (skipAlign(this.view, event.action)) { + if (this.currentEvent === event) this.currentEvent = null; + continue; + } + + const delay_ms = delayAlign(event.action); + if (delay_ms > 0) { + await delay(delay_ms, event.controller.signal); + } + + const context = createContext( + this.view, + event.action, + this.previousActiveBranch, + ); + + const actions = createAlignBranchActions(context, event.action); + + requestAnimationFrame(() => { + runAlignBranchActions( + context, + actions, + event.controller.signal, + ); + this.previousActiveBranch = context.activeBranch; + }); + await waitForActiveNodeToStopMoving( + this.view, + event.controller.signal, + ); + } catch (e) { + logger.error(e); + } + if (this.currentEvent === event) this.currentEvent = null; + } + + this.isRunning = false; }; } diff --git a/src/stores/view/subscriptions/effects/align-branch/constants/action-category.ts b/src/stores/view/subscriptions/effects/align-branch/constants/action-category.ts deleted file mode 100644 index cc07771..0000000 --- a/src/stores/view/subscriptions/effects/align-branch/constants/action-category.ts +++ /dev/null @@ -1,175 +0,0 @@ -import { PluginAction } from 'src/stores/view/subscriptions/effects/align-branch/align-branch'; - -export type ActionCategory = - | 'document/structure' - | 'document/reset' - | 'document/content' - | 'view/active-node/keyboard' - | 'view/active-node/mouse' - | 'view/active-node/document' - | 'view/active-node/search' - | 'view/life-cycle' - | 'view/resize' - | 'view/editor' - | 'view/outline' - | 'manual' - | 'settings/zoom' - | 'settings/layout' - | 'settings/scrolling' - | 'other'; - -const actionCategoryDict: Partial< - Record -> = { - 'view/align-branch/center-node': 'manual', - 'view/align-branch/reveal-node': 'manual', - 'DOCUMENT/LOAD_FILE': 'document/reset', - RESET_STORE: 'document/reset', - 'DOCUMENT/INSERT_NODE': 'document/structure', - 'DOCUMENT/DROP_NODE': 'document/structure', - 'DOCUMENT/DELETE_NODE': 'document/structure', - 'DOCUMENT/MERGE_NODE': 'document/structure', - 'DOCUMENT/MOVE_NODE': 'document/structure', - 'DOCUMENT/SPLIT_NODE': 'document/structure', - 'DOCUMENT/PASTE_NODE': 'document/structure', - 'DOCUMENT/EXTRACT_BRANCH': 'document/structure', - 'DOCUMENT/CUT_NODE': 'document/structure', - 'HISTORY/APPLY_PREVIOUS_SNAPSHOT': 'document/structure', - 'HISTORY/APPLY_NEXT_SNAPSHOT': 'document/structure', - 'HISTORY/SELECT_SNAPSHOT': 'document/structure', - 'DOCUMENT/SET_NODE_CONTENT': 'document/content', - 'DOCUMENT/FORMAT_HEADINGS': 'document/content', - 'view/main/disable-edit': 'view/editor', - 'view/main/enable-edit': 'view/editor', - - 'DOCUMENT/JUMP_TO_NODE': 'view/active-node/keyboard', - 'DOCUMENT/NAVIGATE_USING_KEYBOARD': 'view/active-node/keyboard', - 'NAVIGATION/SELECT_NEXT_NODE': 'view/active-node/mouse', - 'NAVIGATION/NAVIGATE_BACK': 'view/active-node/mouse', - 'NAVIGATION/NAVIGATE_FORWARD': 'view/active-node/mouse', - 'view/outline/toggle-collapse-node': 'view/outline', - 'view/outline/refresh-collapsed-nodes': 'view/outline', - 'view/outline/toggle-collapse-all': 'view/outline', - // todo: add a separate event for effect source - 'view/set-active-node/mouse': 'view/active-node/mouse', - 'view/set-active-node/mouse-silent': 'other', - 'view/set-active-node/document': 'view/active-node/document', - 'view/set-active-node/search': 'view/active-node/search', - 'view/left-sidebar/set-width': 'settings/layout', - 'WORKSPACE/ACTIVE_LEAF_CHANGE': 'view/life-cycle', - 'WORKSPACE/RESIZE': 'view/resize', - 'WORKSPACE/LAYOUT_READY': 'view/life-cycle', - 'WORKSPACE/SET_ACTIVE_LINEAGE_VIEW': 'view/life-cycle', - 'view/life-cycle/mount': 'view/life-cycle', - 'VIEW/TOGGLE_MINIMAP': 'settings/layout', - 'view/left-sidebar/toggle': 'settings/layout', - SET_CARD_WIDTH: 'settings/layout', - SET_CARDS_GAP: 'settings/layout', - SET_MIN_CARD_HEIGHT: 'settings/layout', - 'VIEW/SCROLLING/TOGGLE_SCROLLING_MODE': 'settings/scrolling', - 'settings/view/scrolling/toggle-vertical-scrolling-mode': - 'settings/scrolling', - SET_LIMIT_PREVIEW_HEIGHT: 'settings/layout', - 'UI/CHANGE_ZOOM_LEVEL': 'settings/zoom', - 'view/modes/gap-between-cards/toggle': 'settings/layout', - 'settings/view/modes/toggle-outline-mode': 'settings/layout', - 'settings/view/set-node-indentation-width': 'settings/layout', - 'SEARCH/SET_RESULTS': 'view/active-node/search', - 'search/toggle-show-all-nodes': 'view/active-node/search', - 'SEARCH/SET_QUERY': 'view/active-node/search', - 'SEARCH/TOGGLE_INPUT': 'view/active-node/search', - 'SEARCH/TOGGLE_FUZZY_MODE': 'view/active-node/search', - 'FS/SET_FILE_PATH': 'other', - 'DOCUMENT/COPY_NODE': 'other', - 'FILE/UPDATE_FRONTMATTER': 'other', - 'document/pinned-nodes/pin': 'other', - 'document/pinned-nodes/unpin': 'other', - 'document/pinned-nodes/remove-stale-nodes': 'other', - 'document/pinned-nodes/load-from-settings': 'other', - 'META/REFRESH_GROUP_PARENT_IDS': 'other', - SET_DRAG_STARTED: 'other', - 'DOCUMENT/SET_DRAG_ENDED': 'other', - UPDATE_ACTIVE_BRANCH: 'other', - 'view/confirmation/reset/disable-edit': 'other', - 'view/confirmation/reset/delete-node': 'other', - 'view/confirmation/confirm/delete-node': 'other', - 'view/confirmation/confirm/disable-edit': 'other', - 'DOCUMENT/CLEAR_SELECTION': 'other', - - 'UI/TOGGLE_HELP_SIDEBAR': 'other', - 'UI/TOGGLE_HISTORY_SIDEBAR': 'other', - 'UI/TOGGLE_SETTINGS_SIDEBAR': 'other', - CLOSE_MODALS: 'other', - 'view/modals/toggle-style-rules': 'other', - 'NAVIGATION/REMOVE_OBSOLETE': 'other', - 'view/pinned-nodes/set-active-node': 'other', - 'view/recent-nodes/set-active-node': 'other', - 'view/sidebar/enable-edit': 'other', - 'view/sidebar/disable-edit': 'other', - 'view/style-rules/update-results': 'other', - 'view/keyboard/shift/down': 'other', - 'view/keyboard/shift/up': 'other', - 'view/hotkeys/set-search-term': 'other', - 'view/hotkeys/update-conflicts': 'other', - 'view/selection/set-selection': 'other', - SET_DOCUMENT_TYPE: 'other', - SET_VIEW_TYPE: 'other', - DELETE_DOCUMENT_PREFERENCES: 'other', - 'HISTORY/UPDATE_DOCUMENT_PATH': 'other', - SET_CUSTOM_HOTKEYS: 'other', - SET_FONT_SIZE: 'other', - SET_CONTAINER_BG: 'other', - SET_ACTIVE_BRANCH_BG: 'other', - UPDATE_DOCUMENTS_DICTIONARY: 'other', - 'settings/document/persist-active-section': 'other', - 'GENERAL/SET_DEFAULT_DOCUMENT_FORMAT': 'other', - 'view/left-sidebar/set-active-tab': 'other', - 'settings/pinned-nodes/persist': 'other', - 'settings/pinned-nodes/persist-active-node': 'other', - 'settings/style-rules/add': 'other', - 'settings/style-rules/update': 'other', - 'settings/style-rules/delete': 'other', - 'settings/style-rules/move': 'other', - 'settings/style-rules/update-condition': 'other', - 'settings/style-rules/enable-rule': 'other', - 'settings/style-rules/disable-rule': 'other', - 'settings/view/set-maintain-edit-mode': 'other', - 'settings/view/theme/set-inactive-node-opacity': 'other', - 'settings/view/theme/set-active-branch-color': 'other', - 'settings/hotkeys/set-custom-hotkey': 'other', - 'settings/hotkeys/reset-custom-hotkey': 'other', - 'settings/hotkeys/reset-all': 'other', - 'settings/hotkeys/apply-preset': 'other', - 'settings/hotkeys/toggle-editor-state': 'other', - 'settings/hotkeys/set-blank': 'other', - 'DOCUMENTS/DELETE_DOCUMENT': 'other', - 'DOCUMENTS/UPDATE_DOCUMENT_PATH': 'other', - 'DOCUMENTS/ADD_DOCUMENT': 'other', -} as const; - -export const actionCategory = new Map( - Object.entries(actionCategoryDict), -) as Map; - -const actionCategoryPriorityDict: Record = { - 'document/reset': 100, - 'view/life-cycle': 100, - 'view/active-node/keyboard': 90, - 'view/active-node/mouse': 90, - 'document/structure': 80, - 'document/content': 70, - 'view/active-node/document': 30, - 'view/active-node/search': 30, - 'view/resize': 20, - 'view/editor': 20, - 'view/outline': 20, - 'settings/zoom': 20, - 'settings/layout': 20, - 'settings/scrolling': 20, - manual: 10, - other: 0, -}; - -export const actionCategoryPriority = new Map( - Object.entries(actionCategoryPriorityDict), -) as Map; diff --git a/src/stores/view/subscriptions/effects/align-branch/constants/action-priority.ts b/src/stores/view/subscriptions/effects/align-branch/constants/action-priority.ts new file mode 100644 index 0000000..6c2fb7a --- /dev/null +++ b/src/stores/view/subscriptions/effects/align-branch/constants/action-priority.ts @@ -0,0 +1,51 @@ +import { PluginAction } from 'src/stores/view/subscriptions/effects/align-branch/align-branch'; + +const actionPriorityRecord: Partial> = { + 'view/life-cycle/mount': 100, + 'view/update-active-branch?source=document': 100, + 'DOCUMENT/JUMP_TO_NODE': 90, + 'NAVIGATION/SELECT_NEXT_NODE': 90, + 'DOCUMENT/NAVIGATE_USING_KEYBOARD': 90, + 'NAVIGATION/NAVIGATE_BACK': 90, + 'NAVIGATION/NAVIGATE_FORWARD': 90, + 'view/set-active-node/mouse': 90, + 'view/set-active-node/mouse-silent': 0, + 'view/set-active-node/search': 30, + 'view/align-branch/center-node': 30, + 'view/align-branch/reveal-node': 30, + 'DOCUMENT/SET_NODE_CONTENT': 30, + 'view/set-active-node/document': 20, + 'view/update-active-branch?source=view': 20, + 'view/main/disable-edit': 20, + 'view/main/enable-edit': 20, + 'view/outline/toggle-collapse-node': 10, + 'view/outline/refresh-collapsed-nodes': 10, + 'view/outline/toggle-collapse-all': 10, + 'DOCUMENT/FORMAT_HEADINGS': 10, + 'view/left-sidebar/set-width': 10, + 'WORKSPACE/ACTIVE_LEAF_CHANGE': 10, + 'WORKSPACE/RESIZE': 10, + 'WORKSPACE/LAYOUT_READY': 10, + 'WORKSPACE/SET_ACTIVE_LINEAGE_VIEW': 10, + 'VIEW/TOGGLE_MINIMAP': 10, + 'view/left-sidebar/toggle': 10, + SET_CARD_WIDTH: 10, + SET_CARDS_GAP: 10, + SET_MIN_CARD_HEIGHT: 10, + 'view/modes/gap-between-cards/toggle': 10, + 'SEARCH/SET_RESULTS': 10, + 'search/toggle-show-all-nodes': 10, + 'SEARCH/SET_QUERY': 10, + 'SEARCH/TOGGLE_INPUT': 10, + 'SEARCH/TOGGLE_FUZZY_MODE': 10, + 'VIEW/SCROLLING/TOGGLE_SCROLLING_MODE': 10, + 'settings/view/scrolling/toggle-vertical-scrolling-mode': 10, + SET_LIMIT_PREVIEW_HEIGHT: 10, + 'UI/CHANGE_ZOOM_LEVEL': 10, + 'settings/view/modes/toggle-outline-mode': 10, + 'settings/view/set-node-indentation-width': 10, +}; + +export const actionPriority = new Map( + Object.entries(actionPriorityRecord) as [PluginAction['type'], number][], +); diff --git a/src/stores/view/subscriptions/effects/align-branch/create-align-branch-actions/create-align-branch-actions.ts b/src/stores/view/subscriptions/effects/align-branch/create-align-branch-actions/create-align-branch-actions.ts index 3922916..bab1429 100644 --- a/src/stores/view/subscriptions/effects/align-branch/create-align-branch-actions/create-align-branch-actions.ts +++ b/src/stores/view/subscriptions/effects/align-branch/create-align-branch-actions/create-align-branch-actions.ts @@ -1,9 +1,7 @@ import { forceCenterActiveNodeV } from 'src/stores/view/subscriptions/effects/align-branch/create-align-branch-actions/force-center-active-node-v'; import { lazyVerticalScrollingMode } from 'src/stores/view/subscriptions/effects/align-branch/create-align-branch-actions/lazy-vertical-scrolling-mode'; -import { - AlignBranchContext, - PluginAction, -} from 'src/stores/view/subscriptions/effects/align-branch/align-branch'; +import { AlignBranchContext } from 'src/stores/view/subscriptions/effects/align-branch/helpers/create-context'; +import { PluginAction } from 'src/stores/view/subscriptions/effects/align-branch/align-branch'; import { outlineScrollingActions } from 'src/stores/view/subscriptions/effects/align-branch/create-align-branch-actions/outline-scrolling-actions'; import { forceCenterActiveNodeH } from 'src/stores/view/subscriptions/effects/align-branch/create-align-branch-actions/force-center-active-node-h'; @@ -34,6 +32,9 @@ export const createAlignBranchActions = ( action: PluginAction, ): AlignBranchAction[] => { const actions: AlignBranchAction[] = []; + if (action.type === 'view/update-active-branch?source=document') { + action = action.context.documentAction; + } if (action.type === 'view/align-branch/reveal-node') { /* used to keep active node visible while editing*/ diff --git a/src/stores/view/subscriptions/effects/align-branch/create-align-branch-actions/lazy-vertical-scrolling-mode.ts b/src/stores/view/subscriptions/effects/align-branch/create-align-branch-actions/lazy-vertical-scrolling-mode.ts index 041cc23..aa5787b 100644 --- a/src/stores/view/subscriptions/effects/align-branch/create-align-branch-actions/lazy-vertical-scrolling-mode.ts +++ b/src/stores/view/subscriptions/effects/align-branch/create-align-branch-actions/lazy-vertical-scrolling-mode.ts @@ -1,8 +1,6 @@ import { AlignBranchAction } from 'src/stores/view/subscriptions/effects/align-branch/create-align-branch-actions/create-align-branch-actions'; -import { - AlignBranchContext, - PluginAction, -} from 'src/stores/view/subscriptions/effects/align-branch/align-branch'; +import { PluginAction } from 'src/stores/view/subscriptions/effects/align-branch/align-branch'; +import { AlignBranchContext } from 'src/stores/view/subscriptions/effects/align-branch/helpers/create-context'; export const lazyVerticalScrollingMode = ( context: Pick, diff --git a/src/stores/view/subscriptions/effects/align-branch/helpers/align-child-columns/align-child-columns.ts b/src/stores/view/subscriptions/effects/align-branch/helpers/align-child-columns/align-child-columns.ts index 45dcaaa..ad50937 100644 --- a/src/stores/view/subscriptions/effects/align-branch/helpers/align-child-columns/align-child-columns.ts +++ b/src/stores/view/subscriptions/effects/align-branch/helpers/align-child-columns/align-child-columns.ts @@ -1,4 +1,4 @@ -import { AlignBranchContext } from 'src/stores/view/subscriptions/effects/align-branch/align-branch'; +import { AlignBranchContext } from 'src/stores/view/subscriptions/effects/align-branch/helpers/create-context'; import { alignChildGroupOfColumn } from 'src/stores/view/subscriptions/effects/align-branch/helpers/align-child-columns/align-child-group-of-column'; import { alignElementVertically } from 'src/lib/align-element/align-element-vertically'; import { findNodeColumn } from 'src/lib/tree-utils/find/find-node-column'; diff --git a/src/stores/view/subscriptions/effects/align-branch/helpers/align-child-columns/align-child-group-of-column.ts b/src/stores/view/subscriptions/effects/align-branch/helpers/align-child-columns/align-child-group-of-column.ts index e0c2423..4221040 100644 --- a/src/stores/view/subscriptions/effects/align-branch/helpers/align-child-columns/align-child-group-of-column.ts +++ b/src/stores/view/subscriptions/effects/align-branch/helpers/align-child-columns/align-child-group-of-column.ts @@ -1,6 +1,6 @@ import { getElementById } from 'src/lib/align-element/helpers/get-element-by-id'; import { alignGroupOfElementsVertically } from 'src/lib/align-element/align-group-of-elements-vertically'; -import { AlignBranchContext } from 'src/stores/view/subscriptions/effects/align-branch/align-branch'; +import { AlignBranchContext } from 'src/stores/view/subscriptions/effects/align-branch/helpers/create-context'; import { Column } from 'src/stores/document/document-state-type'; export const alignChildGroupOfColumn = ( diff --git a/src/stores/view/subscriptions/effects/align-branch/helpers/align-child-columns/align-inactive-columns.ts b/src/stores/view/subscriptions/effects/align-branch/helpers/align-child-columns/align-inactive-columns.ts index 384fd55..90cc4fb 100644 --- a/src/stores/view/subscriptions/effects/align-branch/helpers/align-child-columns/align-inactive-columns.ts +++ b/src/stores/view/subscriptions/effects/align-branch/helpers/align-child-columns/align-inactive-columns.ts @@ -1,5 +1,5 @@ import { alignElementVertically } from 'src/lib/align-element/align-element-vertically'; -import { AlignBranchContext } from 'src/stores/view/subscriptions/effects/align-branch/align-branch'; +import { AlignBranchContext } from 'src/stores/view/subscriptions/effects/align-branch/helpers/create-context'; import { findNodeColumn } from 'src/lib/tree-utils/find/find-node-column'; export const alignInactiveColumns = (context: AlignBranchContext) => { diff --git a/src/stores/view/subscriptions/effects/align-branch/helpers/create-context.ts b/src/stores/view/subscriptions/effects/align-branch/helpers/create-context.ts new file mode 100644 index 0000000..c67d82b --- /dev/null +++ b/src/stores/view/subscriptions/effects/align-branch/helpers/create-context.ts @@ -0,0 +1,60 @@ +import { LineageView } from 'src/view/view'; +import { ActiveBranch } from 'src/stores/view/default-view-state'; +import { adjustScrollBehavior } from 'src/stores/view/subscriptions/effects/align-branch/helpers/adjust-scroll-behavior'; +import { PluginAction } from 'src/stores/view/subscriptions/effects/align-branch/align-branch'; +import { Column } from 'src/stores/document/document-state-type'; +import { ActiveNodesOfColumn } from 'src/stores/view/view-state-type'; + +export type PartialDOMRect = Pick; +export type AlignBranchState = { + rects: Map; +}; +export type AlignBranchSettings = { + behavior: ScrollBehavior; + centerActiveNodeH: boolean; + centerActiveNodeV: boolean; + zoomLevel: number; +}; +export type AlignBranchContext = { + previousActiveBranch: ActiveBranch | null; + columns: Column[]; + activeBranch: ActiveBranch; + container: HTMLElement; + containerRect: DOMRect; + outlineMode: boolean; + alignBranchSettings: AlignBranchSettings; + state: AlignBranchState; + activeNodesOfColumn: ActiveNodesOfColumn; +}; +export const createContext = ( + view: LineageView, + action: PluginAction, + previousActiveBranch: ActiveBranch | null, +) => { + const settings = view.plugin.settings.getValue(); + const container = view.container!; + const documentState = view.documentStore.getValue(); + const viewState = view.viewStore.getValue(); + const activeBranch = viewState.document.activeBranch; + + const behavior = adjustScrollBehavior(action); + const context: AlignBranchContext = { + previousActiveBranch: previousActiveBranch, + activeBranch: activeBranch, + columns: documentState.document.columns, + container, + activeNodesOfColumn: viewState.document.activeNodesOfColumn, + containerRect: container.getBoundingClientRect(), + outlineMode: settings.view.outlineMode, + alignBranchSettings: { + centerActiveNodeH: settings.view.scrolling.centerActiveNodeH, + centerActiveNodeV: settings.view.scrolling.centerActiveNodeV, + zoomLevel: settings.view.zoomLevel, + behavior: behavior, + }, + state: { + rects: new Map(), + }, + }; + return context; +}; diff --git a/src/stores/view/subscriptions/effects/align-branch/helpers/delay-align.ts b/src/stores/view/subscriptions/effects/align-branch/helpers/delay-align.ts index fcb18ba..f57234c 100644 --- a/src/stores/view/subscriptions/effects/align-branch/helpers/delay-align.ts +++ b/src/stores/view/subscriptions/effects/align-branch/helpers/delay-align.ts @@ -7,8 +7,15 @@ export const delayAlign = (action: PluginAction) => { action.type === 'VIEW/TOGGLE_MINIMAP' ) { delay = 300; - } else if (action.type === 'DOCUMENT/DROP_NODE') { - delay = 32; + } else if (action.type === 'WORKSPACE/RESIZE') { + delay = 50; + } else if (action.type === 'view/life-cycle/mount') { + delay = 16; + } else if (action.type === 'view/update-active-branch?source=document') { + action = action.context.documentAction; + if (action.type === 'DOCUMENT/INSERT_NODE') { + delay = 32; + } } return delay; }; diff --git a/src/stores/view/subscriptions/effects/align-branch/run-align-branch-actions/actions/align-parents-nodes.ts b/src/stores/view/subscriptions/effects/align-branch/run-align-branch-actions/actions/align-parents-nodes.ts index 87e0c8c..d2c0bf4 100644 --- a/src/stores/view/subscriptions/effects/align-branch/run-align-branch-actions/actions/align-parents-nodes.ts +++ b/src/stores/view/subscriptions/effects/align-branch/run-align-branch-actions/actions/align-parents-nodes.ts @@ -1,4 +1,4 @@ -import { AlignBranchContext } from 'src/stores/view/subscriptions/effects/align-branch/align-branch'; +import { AlignBranchContext } from 'src/stores/view/subscriptions/effects/align-branch/helpers/create-context'; import { alignElementVertically } from 'src/lib/align-element/align-element-vertically'; export const alignParentsNodes = ( diff --git a/src/stores/view/subscriptions/effects/align-branch/run-align-branch-actions/actions/scroll-first-column-to-the-left.ts b/src/stores/view/subscriptions/effects/align-branch/run-align-branch-actions/actions/scroll-first-column-to-the-left.ts index 9c4165d..237c7c1 100644 --- a/src/stores/view/subscriptions/effects/align-branch/run-align-branch-actions/actions/scroll-first-column-to-the-left.ts +++ b/src/stores/view/subscriptions/effects/align-branch/run-align-branch-actions/actions/scroll-first-column-to-the-left.ts @@ -1,5 +1,5 @@ import { alignElementHorizontally } from 'src/lib/align-element/align-element-horizontally'; -import { AlignBranchContext } from 'src/stores/view/subscriptions/effects/align-branch/align-branch'; +import { AlignBranchContext } from 'src/stores/view/subscriptions/effects/align-branch/helpers/create-context'; export const scrollFirstColumnToTheLeft = (context: AlignBranchContext) => { const firstColumnId = context.columns[0]?.id; diff --git a/src/stores/view/subscriptions/effects/align-branch/run-align-branch-actions/run-align-branch-actions.ts b/src/stores/view/subscriptions/effects/align-branch/run-align-branch-actions/run-align-branch-actions.ts index 15cc44c..d753344 100644 --- a/src/stores/view/subscriptions/effects/align-branch/run-align-branch-actions/run-align-branch-actions.ts +++ b/src/stores/view/subscriptions/effects/align-branch/run-align-branch-actions/run-align-branch-actions.ts @@ -1,4 +1,4 @@ -import { AlignBranchContext } from 'src/stores/view/subscriptions/effects/align-branch/align-branch'; +import { AlignBranchContext } from 'src/stores/view/subscriptions/effects/align-branch/helpers/create-context'; import { AlignBranchAction } from 'src/stores/view/subscriptions/effects/align-branch/create-align-branch-actions/create-align-branch-actions'; import { scrollFirstColumnToTheLeft } from 'src/stores/view/subscriptions/effects/align-branch/run-align-branch-actions/actions/scroll-first-column-to-the-left'; import { alignElementVertically } from 'src/lib/align-element/align-element-vertically'; @@ -10,10 +10,12 @@ import { alignInactiveColumns } from 'src/stores/view/subscriptions/effects/alig export const runAlignBranchActions = ( context: AlignBranchContext, actions: AlignBranchAction[], + signal: AbortSignal, ) => { actions = actions.sort((a, b) => a.action.localeCompare(b.action)); const activeNode = context.activeBranch.node; for (const action of actions) { + if (signal.aborted) return; const type = action.action; if (type === '10/first-column/horizontal/move-to-the-left') { scrollFirstColumnToTheLeft(context); diff --git a/src/stores/view/subscriptions/on-document-state-update.ts b/src/stores/view/subscriptions/on-document-state-update.ts index 4ef9df1..08eb519 100644 --- a/src/stores/view/subscriptions/on-document-state-update.ts +++ b/src/stores/view/subscriptions/on-document-state-update.ts @@ -39,7 +39,7 @@ export const onDocumentStateUpdate = ( e.createOrDelete || e.dropOrMove || e.changeHistory || e.clipboard; if (structuralChange) { setActiveNode(view, action); - updateActiveBranch(viewStore, documentState, 'structure'); + updateActiveBranch(viewStore, documentState, action); viewStore.dispatch({ type: 'view/outline/refresh-collapsed-nodes', payload: { @@ -72,9 +72,10 @@ export const onDocumentStateUpdate = ( } // effects - if (structuralChange || e.content) { + if (e.content) { view.alignBranch.align(action); - + } + if (structuralChange || e.content) { view.rulesProcessor.onDocumentUpdate(action); } diff --git a/src/stores/view/subscriptions/on-view-mount.ts b/src/stores/view/subscriptions/on-view-mount.ts index e6c6ec7..bcbfbe7 100644 --- a/src/stores/view/subscriptions/on-view-mount.ts +++ b/src/stores/view/subscriptions/on-view-mount.ts @@ -44,7 +44,7 @@ export const onViewMount = (view: LineageView) => { if (!view.file) return subscriptions; setInitialActiveNode(view); loadCollapsedSectionsFromSettings(view); - updateActiveBranch(viewStore, documentState, 'none'); + updateActiveBranch(viewStore, documentState); if (view.isActive && isEmptyDocument(documentState.document.content)) { enableEditMode(viewStore, documentState); } @@ -62,7 +62,6 @@ export const onViewMount = (view: LineageView) => { view.rulesProcessor.onRulesUpdate(); view.zoomFactor = view.plugin.settings.getValue().view.zoomLevel; - view.alignBranch.align({ type: 'view/life-cycle/mount' }); subscriptions.add(watchViewSize(view)); return subscriptions; }; diff --git a/src/stores/view/subscriptions/on-view-state-update.ts b/src/stores/view/subscriptions/on-view-state-update.ts index 2f315fb..e39543a 100644 --- a/src/stores/view/subscriptions/on-view-state-update.ts +++ b/src/stores/view/subscriptions/on-view-state-update.ts @@ -39,7 +39,7 @@ export const onViewStateUpdate = ( } if (activeNodeChange && activeNodeHasChanged) { // this should be handled internally - updateActiveBranch(viewStore, documentState, 'none'); + updateActiveBranch(viewStore, documentState); persistActiveNodeInPluginSettings(view); view.plugin.statusBar.updateProgressIndicatorAndChildCount(view); } @@ -68,7 +68,12 @@ export const onViewStateUpdate = ( } // effects - if (activeNodeChange || e.search || e.editMainSplit) { + if ( + e.search || + e.editMainSplit || + action.type === 'view/update-active-branch?source=document' || + action.type === 'view/update-active-branch?source=view' + ) { view.alignBranch.align(action); } if (!container || !view.isViewOfFile) return; diff --git a/src/stores/view/view-reducer.ts b/src/stores/view/view-reducer.ts index a8a6c43..2a45132 100644 --- a/src/stores/view/view-reducer.ts +++ b/src/stores/view/view-reducer.ts @@ -127,12 +127,10 @@ const updateDocumentState = (state: ViewState, action: ViewStoreAction) => { onDragStart(state.document, action); } else if (action.type === 'DOCUMENT/SET_DRAG_ENDED') { onDragEnd(state.document); - } else if (action.type === 'UPDATE_ACTIVE_BRANCH') { - updateActiveBranch( - state.document, - action.payload.columns, - action.context.changeType, - ); + } else if (action.type === 'view/update-active-branch?source=view') { + updateActiveBranch(state.document, action.context.columns, false); + } else if (action.type === 'view/update-active-branch?source=document') { + updateActiveBranch(state.document, action.context.columns, true); } else if (action.type === 'NAVIGATION/NAVIGATE_FORWARD') { navigateActiveNodeHistory(state.document, state, true); } else if (action.type === 'NAVIGATION/NAVIGATE_BACK') {