diff --git a/src/mol-plugin/behavior/static/state.ts b/src/mol-plugin/behavior/static/state.ts index 6d28606edd90159610d37359df45cea1bf140af0..e41e7e5649caf03f0011532bc2cfc8e2ecbf2a48 100644 --- a/src/mol-plugin/behavior/static/state.ts +++ b/src/mol-plugin/behavior/static/state.ts @@ -135,6 +135,10 @@ export function Snapshots(ctx: PluginContext) { PluginCommands.State.Snapshots.Apply.subscribe(ctx, ({ id }) => { const snapshot = ctx.state.snapshots.setCurrent(id); if (!snapshot) return; + return PluginCommands.State.Snapshots.Set.dispatch(ctx, { snapshot }); + }); + + PluginCommands.State.Snapshots.Set.subscribe(ctx, ({ snapshot }) => { return ctx.state.setSnapshot(snapshot); }); @@ -150,9 +154,9 @@ export function Snapshots(ctx: PluginContext) { PluginCommands.State.Snapshots.Fetch.subscribe(ctx, async ({ url }) => { const json = await ctx.runTask(ctx.fetch({ url, type: 'json' })); // fetch(url, { referrer: 'no-referrer' }); - const current = ctx.state.snapshots.setRemoteSnapshot(json.data); - if (!current) return; - return ctx.state.setSnapshot(current); + const snapshot = ctx.state.snapshots.setRemoteSnapshot(json.data); + if (!snapshot) return; + return PluginCommands.State.Snapshots.Set.dispatch(ctx, { snapshot }); }); PluginCommands.State.Snapshots.DownloadToFile.subscribe(ctx, ({ name }) => { @@ -169,8 +173,8 @@ export function Snapshots(ctx: PluginContext) { PluginCommands.State.Snapshots.OpenFile.subscribe(ctx, async ({ file }) => { try { const data = await readFromFile(file, 'string').run(); - const json = JSON.parse(data as string); - await ctx.state.setSnapshot(json); + const snapshot = JSON.parse(data as string); + return PluginCommands.State.Snapshots.Set.dispatch(ctx, { snapshot }); } catch (e) { ctx.log.error(`Reading JSON state: ${e}`); } diff --git a/src/mol-plugin/command.ts b/src/mol-plugin/command.ts index 29e8929f1ac8d5840eb3520fa1a2bb72abc8ac8e..b373682d495960ad2e33f55eed5901a8e24e983e 100644 --- a/src/mol-plugin/command.ts +++ b/src/mol-plugin/command.ts @@ -33,6 +33,8 @@ export const PluginCommands = { Apply: PluginCommand<{ id: string }>({ isImmediate: true }), Clear: PluginCommand<{}>({ isImmediate: true }), + Set: PluginCommand<{ snapshot: PluginState.Snapshot }>(), + Upload: PluginCommand<{ name?: string, description?: string, serverUrl: string }>({ isImmediate: true }), Fetch: PluginCommand<{ url: string }>(), diff --git a/src/mol-plugin/context.ts b/src/mol-plugin/context.ts index 7fd0a9a0c3dfb71f633dc80cbbe4e630d9be3510..9f25b9e955f48852e297cba68b55bbfeca1d6037 100644 --- a/src/mol-plugin/context.ts +++ b/src/mol-plugin/context.ts @@ -55,6 +55,7 @@ export class PluginContext { removed: merge(this.state.dataState.events.object.removed, this.state.behaviorState.events.object.removed), updated: merge(this.state.dataState.events.object.updated, this.state.behaviorState.events.object.updated) }, + isUpdating: merge(this.state.dataState.events.isUpdating, this.state.behaviorState.events.isUpdating), cameraSnapshots: this.state.cameraSnapshots.events, snapshots: this.state.snapshots.events, }, diff --git a/src/mol-plugin/ui/controls.tsx b/src/mol-plugin/ui/controls.tsx index 847b7b215264217b8cef2dd802fd5d5dce376e7a..00517be8e85e82032ed0b3c5ccc348c23cee6e5d 100644 --- a/src/mol-plugin/ui/controls.tsx +++ b/src/mol-plugin/ui/controls.tsx @@ -91,6 +91,7 @@ export class StateSnapshotViewportControls extends PluginUIComponent<{}, { isBus componentDidMount() { // TODO: this needs to be diabled when the state is updating! this.subscribe(this.plugin.state.snapshots.events.changed, () => this.forceUpdate()); + this.subscribe(this.plugin.events.state.isUpdating, (isBusy) => this.setState({ isBusy })); } async update(id: string) { diff --git a/src/mol-state/state.ts b/src/mol-state/state.ts index 129969ad06fc7bd42e7c587ce4204f8fb36073ef..4da90c9051973657063640f83dd9143e380d5924 100644 --- a/src/mol-state/state.ts +++ b/src/mol-state/state.ts @@ -42,7 +42,8 @@ class State { removed: this.ev<State.ObjectEvent & { obj?: StateObject }>() }, log: this.ev<LogEntry>(), - changed: this.ev<void>() + changed: this.ev<void>(), + isUpdating: this.ev<boolean>() }; readonly behaviors = { @@ -130,6 +131,7 @@ class State { updateTree(tree: StateTree | StateBuilder, options?: Partial<State.UpdateOptions>): Task<void> updateTree(tree: StateTree | StateBuilder, options?: Partial<State.UpdateOptions>): Task<any> { return Task.create('Update Tree', async taskCtx => { + this.events.isUpdating.next(true); let updated = false; const ctx = this.updateTreeAndCreateCtx(tree, taskCtx, options); try { @@ -140,6 +142,7 @@ class State { } } finally { if (updated) this.events.changed.next(); + this.events.isUpdating.next(false); for (const ref of ctx.stateChanges) { this.events.cell.stateUpdated.next({ state: this, ref, cellState: this.tree.cellStates.get(ref) });