Browse Source

fixed issue that prevented delayed isBusy and enabled it again
- lesson learned: do not raise events inside property getters

David Sehnal 5 years ago
parent
commit
57bbcf9425

+ 22 - 7
src/mol-plugin-state/manager/structure/hierarchy.ts

@@ -17,6 +17,7 @@ import { buildStructureHierarchy, HierarchyRef, ModelRef, StructureComponentRef,
 export class StructureHierarchyManager extends PluginComponent {
     private state = {
         syncedTree: this.dataState.tree,
+        notified: false,
 
         hierarchy: StructureHierarchy(),
         selection: {
@@ -58,12 +59,12 @@ export class StructureHierarchyManager extends PluginComponent {
     }
 
     get current() {
-        this.sync();
+        this.sync(false);
         return this.state.hierarchy;
     }
 
     get selection() {
-        this.sync();
+        this.sync(false);
         return this.state.selection;
     }
 
@@ -80,8 +81,17 @@ export class StructureHierarchyManager extends PluginComponent {
         return newCurrent;
     }
 
-    private sync() {
-        if (this.state.syncedTree === this.dataState.tree) return;
+    private sync(notify: boolean) {
+        if (!notify && this.dataState.behaviors.isUpdating.value) return;
+
+        if (this.state.syncedTree === this.dataState.tree) {
+            if (notify && !this.state.notified) {
+                this.state.notified = true;
+                this.behaviors.selection.next({ hierarchy: this.state.hierarchy, ...this.state.selection });
+            }
+
+            return;
+        }
 
         this.state.syncedTree = this.dataState.tree;
 
@@ -103,7 +113,12 @@ export class StructureHierarchyManager extends PluginComponent {
         this.state.selection.models = models;
         this.state.selection.structures = structures;
 
-        this.behaviors.selection.next({ hierarchy, trajectories, models, structures });
+        if (notify) {
+            this.state.notified = true;
+            this.behaviors.selection.next({ hierarchy, trajectories, models, structures });
+        } else {
+            this.state.notified = false;
+        }
     }
 
     updateCurrent(refs: HierarchyRef[], action: 'add' | 'remove') {
@@ -189,11 +204,11 @@ export class StructureHierarchyManager extends PluginComponent {
 
         this.subscribe(plugin.state.data.events.changed, e => {
             if (e.inTransaction || plugin.behaviors.state.isAnimating.value) return;
-            this.sync();
+            this.sync(true);
         });
 
         this.subscribe(plugin.behaviors.state.isAnimating, isAnimating => {
-            if (!isAnimating && !plugin.behaviors.state.isUpdating.value) this.sync();
+            if (!isAnimating && !plugin.behaviors.state.isUpdating.value) this.sync(true);
         });
     }
 }

+ 3 - 5
src/mol-plugin-ui/structure/components.tsx

@@ -185,12 +185,10 @@ class ComponentOptionsControls extends PurePluginUIComponent<{ isDisabled: boole
 }
 
 class ComponentListControls extends PurePluginUIComponent {
-    get current() {
-        return this.plugin.managers.structure.hierarchy.behaviors.selection;
-    }
-
     componentDidMount() {
-        this.subscribe(this.current, () => this.forceUpdate());
+        this.subscribe(this.plugin.managers.structure.hierarchy.behaviors.selection, () => {
+            this.forceUpdate()
+        });
     }
 
     render() {

+ 1 - 1
src/mol-plugin-ui/viewport/screenshot.tsx

@@ -93,7 +93,7 @@ export class DownloadScreenshotControls extends PluginUIComponent<{ close: () =>
             this.updateQueue.next();
         })
 
-        this.subscribe(this.plugin.state.data.events.isUpdating, v => {
+        this.subscribe(this.plugin.state.data.behaviors.isUpdating, v => {
             this.setState({ isDisabled: v })
             if (!v) this.updateQueue.next();
         })

+ 3 - 0
src/mol-plugin/config.ts

@@ -18,6 +18,9 @@ function item<T>(key: string, defaultValue?: T) { return new PluginConfigItem(ke
 
 export const PluginConfig = {
     item,
+    General: {
+        IsBusyTimeoutMs: item('plugin-config.is-busy-timeout', 750)
+    },
     State: {
         DefaultServer: item('plugin-state.server', 'https://webchem.ncbr.muni.cz/molstar-state'),
         CurrentServer: item('plugin-state.server', 'https://webchem.ncbr.muni.cz/molstar-state')

+ 24 - 7
src/mol-plugin/context.ts

@@ -45,7 +45,7 @@ import { BuiltInPluginBehaviors } from './behavior';
 import { PluginBehavior } from './behavior/behavior';
 import { PluginCommandManager } from './command';
 import { PluginCommands } from './commands';
-import { PluginConfigManager } from './config';
+import { PluginConfigManager, PluginConfig } from './config';
 import { LeftPanelTabName, PluginLayout } from './layout';
 import { PluginSpec } from './spec';
 import { PluginState } from './state';
@@ -237,18 +237,35 @@ export class PluginContext {
     }
 
     private initBehaviorEvents() {
-        merge(this.state.data.events.isUpdating, this.state.behaviors.events.isUpdating).subscribe(u => {
-            this.behaviors.state.isUpdating.next(u);
+        merge(this.state.data.behaviors.isUpdating, this.state.behaviors.behaviors.isUpdating).subscribe(u => {
+            if (this.behaviors.state.isUpdating.value !== u) this.behaviors.state.isUpdating.next(u);
         });
 
+        const timeoutMs = this.config.get(PluginConfig.General.IsBusyTimeoutMs) || 750;
+        const isBusy = this.behaviors.state.isBusy;
+
+        let timeout: any = void 0;
+        const setBusy = () => {
+            isBusy.next(true);
+        }
+
         merge(this.behaviors.state.isUpdating, this.behaviors.state.isAnimating).subscribe(v => {
             const isUpdating = this.behaviors.state.isUpdating.value;
             const isAnimating = this.behaviors.state.isAnimating.value;
-            const isBusy = this.behaviors.state.isBusy;
 
-            if ((isUpdating || isAnimating) && !isBusy.value) isBusy.next(true);
-            else if (isBusy.value) isBusy.next(false);
-        })
+            if ((isUpdating || isAnimating) && !isBusy.value) {
+                if (timeout !== void 0) clearTimeout(timeout);
+                timeout = setTimeout(setBusy, timeoutMs);
+                // isBusy.next(true);
+            } else {
+                if (timeout !== void 0) clearTimeout(timeout);
+                timeout = void 0;
+                if (isBusy.value) {
+                    // console.log('busy false')
+                    isBusy.next(false);
+                }
+            }
+        });
     }
 
     private initBuiltInBehavior() {

+ 6 - 6
src/mol-state/state.ts

@@ -48,12 +48,12 @@ class State {
         },
         log: this.ev<LogEntry>(),
         changed: this.ev<{ state: State, inTransaction: boolean }>(),
-        isUpdating: this.ev<boolean>(),
         historyUpdated: this.ev<{ state: State }>()
     };
 
     readonly behaviors = {
-        currentObject: this.ev.behavior<State.ObjectEvent>({ state: this, ref: StateTransform.RootRef })
+        currentObject: this.ev.behavior<State.ObjectEvent>({ state: this, ref: StateTransform.RootRef }),
+        isUpdating: this.ev.behavior<boolean>(false),
     };
 
     readonly actions = new StateActionManager();
@@ -182,7 +182,7 @@ class State {
             const snapshot = this._tree.asImmutable();
             let restored = false;
             try {
-                if (!isNested) this.events.isUpdating.next(true);
+                if (!isNested) this.behaviors.isUpdating.next(true);
                 this.inTransaction = true;
                 await edits();
 
@@ -203,7 +203,7 @@ class State {
                 if (!isNested) {
                     this.inTransaction = false;
                     this.events.changed.next({ state: this, inTransaction: false });
-                    this.events.isUpdating.next(false);
+                    this.behaviors.isUpdating.next(false);
 
                     if (!restored) {
                         if (options?.canUndo) this.addHistory(snapshot, typeof options.canUndo === 'string' ? options.canUndo : void 0);
@@ -232,7 +232,7 @@ class State {
             const snapshot = options?.canUndo ? this._tree.asImmutable() : void 0;
             let reverted = false;
 
-            if (!this.inTransaction) this.events.isUpdating.next(true);
+            if (!this.inTransaction) this.behaviors.isUpdating.next(true);
             try {
                 this.reverted = false;
                 const ret = options && (options.revertIfAborted || options.revertOnError)
@@ -244,7 +244,7 @@ class State {
             } finally {
                 this.updateQueue.handled(params);
                 if (!this.inTransaction) {
-                    this.events.isUpdating.next(false);
+                    this.behaviors.isUpdating.next(false);
                     if (!options?.canUndo) {
                         if (!this.undoingHistory) this.clearHistory();
                     } else if (!reverted) {