123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103 |
- /**
- * Copyright (c) 2018 mol* contributors, licensed under MIT, See LICENSE file for more info.
- *
- * @author David Sehnal <david.sehnal@gmail.com>
- */
- import { PluginContext } from './context';
- import { UUID } from '../mol-util';
- export { PluginCommand, PluginCommandManager };
- interface PluginCommand<T = unknown> {
- (ctx: PluginContext, params?: T): Promise<void>,
- readonly id: UUID,
- subscribe(ctx: PluginContext, action: PluginCommand.Action<T>): PluginCommand.Subscription
- }
- function PluginCommand<T>(): PluginCommand<T> {
- const ret: PluginCommand<T> = ((ctx, params) => ctx.commands.dispatch(ret, params || {} as any)) as PluginCommand<T>;
- ret.subscribe = (ctx, action) => ctx.commands.subscribe(ret, action);
- (ret.id as UUID) = UUID.create22();
- return ret;
- }
- namespace PluginCommand {
- export type Id = string & { '@type': 'plugin-command-id' }
- export interface Subscription {
- unsubscribe(): void
- }
- export type Action<T> = (params: T) => unknown | Promise<unknown>
- }
- type Instance = { cmd: PluginCommand<any>, params: any, resolve: () => void, reject: (e: any) => void }
- class PluginCommandManager {
- private subs = new Map<string, PluginCommand.Action<any>[]>();
- private disposing = false;
- subscribe<T>(cmd: PluginCommand<T>, action: PluginCommand.Action<T>): PluginCommand.Subscription {
- let actions = this.subs.get(cmd.id);
- if (!actions) {
- actions = [];
- this.subs.set(cmd.id, actions);
- }
- actions.push(action);
- return {
- unsubscribe: () => {
- const actions = this.subs.get(cmd.id);
- if (!actions) return;
- const idx = actions.indexOf(action);
- if (idx < 0) return;
- for (let i = idx + 1; i < actions.length; i++) {
- actions[i - 1] = actions[i];
- }
- actions.pop();
- }
- };
- }
- /** Resolves after all actions have completed */
- dispatch<T>(cmd: PluginCommand<T>, params: T) {
- return new Promise<void>((resolve, reject) => {
- if (this.disposing) {
- reject('disposed');
- return;
- }
- const actions = this.subs.get(cmd.id);
- if (!actions) {
- resolve();
- return;
- }
- this.resolve({ cmd, params, resolve, reject });
- });
- }
- dispose() {
- this.subs.clear();
- }
- private async resolve(instance: Instance) {
- const actions = this.subs.get(instance.cmd.id);
- if (!actions) {
- instance.resolve();
- return;
- }
- try {
- // TODO: should actions be called "asynchronously" ("setImmediate") instead?
- for (const a of actions) {
- await a(instance.params);
- }
- instance.resolve();
- } catch (e) {
- instance.reject(e);
- }
- }
- }
|