diff --git a/examples/workflow-standalone/src/app.ts b/examples/workflow-standalone/src/app.ts index e79fbf75d..78d3ff4fe 100644 --- a/examples/workflow-standalone/src/app.ts +++ b/examples/workflow-standalone/src/app.ts @@ -49,7 +49,7 @@ async function initialize(connectionProvider: MessageConnection, isReconnecting container = createContainer({ clientId, diagramType, glspClientProvider: async () => glspClient, sourceUri: examplePath }); const actionDispatcher = container.get(GLSPActionDispatcher); const diagramLoader = container.get(DiagramLoader); - await diagramLoader.load({ isReconnecting }); + await diagramLoader.load({ requestModelOptions: { isReconnecting } }); if (isReconnecting) { const message = `Connection to the ${id} glsp server got closed. Connection was successfully re-established.`; diff --git a/packages/client/src/base/model/diagram-loader.ts b/packages/client/src/base/model/diagram-loader.ts index ec2e893cc..885bbd7d3 100644 --- a/packages/client/src/base/model/diagram-loader.ts +++ b/packages/client/src/base/model/diagram-loader.ts @@ -21,6 +21,7 @@ import { Args, EMPTY_ROOT, GLSPClient, + InitializeParameters, MaybePromise, RequestModelAction, ServerStatusAction, @@ -30,6 +31,7 @@ import { } from '~glsp-sprotty'; import { GLSPActionDispatcher } from '../action-dispatcher'; import { Ranked } from '../ranked'; +import { GLSPModelSource } from './glsp-model-source'; /** * Configuration options for a specific GLSP diagram instance. @@ -92,6 +94,33 @@ export namespace IDiagramStartup { } } +export interface DiagramLoadingOptions { + /** + * Optional custom options that should be used the initial {@link RequestModelAction}. + * These options will be merged with the default options (`diagramType` and `sourceUri`). + * Defaults to an empty object if not defined. + */ + requestModelOptions?: Args; + + /** + * Optional partial {@link InitializeParameters} that should be used for `initializeServer` request if the underlying + * {@link GLSPClient} has not been initialized yet. + */ + initializeParameters?: Partial; + + /** + * Flag to enable/disable client side notifications during the loading process. + * Defaults to `true` if not defined + */ + enableNotifications?: boolean; +} + +export interface ResolvedDiagramLoadingOptions { + requestModelOptions: Args; + initializeParameters: InitializeParameters; + enableNotifications: boolean; +} + /** * The central component responsible for initializing the diagram and loading the graphical model * from the GLSP server. @@ -110,20 +139,34 @@ export class DiagramLoader { @optional() protected diagramStartups: IDiagramStartup[] = []; - protected enableLoadingNotifications = true; + @inject(GLSPModelSource) + protected modelSource: GLSPModelSource; @postConstruct() protected postConstruct(): void { this.diagramStartups.sort((a, b) => Ranked.getRank(a) - Ranked.getRank(b)); } - async load(requestModelOptions: Args = {}): Promise { + async load(options: DiagramLoadingOptions = {}): Promise { + const resolvedOptions: ResolvedDiagramLoadingOptions = { + requestModelOptions: { + sourceUri: this.options.sourceUri ?? '', + diagramType: this.options.diagramType, + ...options.requestModelOptions + }, + initializeParameters: { + applicationId: ApplicationIdProvider.get(), + protocolVersion: GLSPClient.protocolVersion, + ...options.initializeParameters + }, + enableNotifications: options.enableNotifications ?? true + }; // Set placeholder model until real model from server is available await this.actionDispatcher.dispatch(SetModelAction.create(EMPTY_ROOT)); await this.invokeStartupHook('preInitialize'); - await this.configureGLSPClient(); + await this.initialize(resolvedOptions); await this.invokeStartupHook('preRequestModel'); - await this.requestModel(requestModelOptions); + await this.requestModel(resolvedOptions); await this.invokeStartupHook('postRequestModel'); } @@ -133,29 +176,26 @@ export class DiagramLoader { } } - protected requestModel(requestModelOptions: Args = {}): Promise { - const options = { sourceUri: this.options.sourceUri, diagramType: this.options.diagramType, ...requestModelOptions } as Args; - const result = this.actionDispatcher.dispatch(RequestModelAction.create({ options })); - if (this.enableLoadingNotifications) { + protected requestModel(options: ResolvedDiagramLoadingOptions): Promise { + const result = this.actionDispatcher.dispatch(RequestModelAction.create({ options: options.requestModelOptions })); + if (options.enableNotifications) { this.actionDispatcher.dispatch(ServerStatusAction.create('', { severity: 'NONE' })); } return result; } - protected async configureGLSPClient(): Promise { - const glspClient = await this.options.glspClientProvider(); - - if (this.enableLoadingNotifications) { + protected async initialize(options: ResolvedDiagramLoadingOptions): Promise { + await this.actionDispatcher.initialize(); + if (options.enableNotifications) { this.actionDispatcher.dispatch(ServerStatusAction.create('Initializing...', { severity: 'INFO' })); } + const glspClient = await this.options.glspClientProvider(); + await glspClient.start(); if (!glspClient.initializeResult) { - await glspClient.initializeServer({ - applicationId: ApplicationIdProvider.get(), - protocolVersion: GLSPClient.protocolVersion - }); + await glspClient.initializeServer(options.initializeParameters); } } } diff --git a/packages/client/src/base/model/glsp-model-source.ts b/packages/client/src/base/model/glsp-model-source.ts index 307bae132..cdac1885e 100644 --- a/packages/client/src/base/model/glsp-model-source.ts +++ b/packages/client/src/base/model/glsp-model-source.ts @@ -14,7 +14,7 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { inject, injectable, postConstruct } from 'inversify'; +import { inject, injectable, postConstruct, preDestroy } from 'inversify'; import { Action, ActionHandlerRegistry, @@ -26,20 +26,19 @@ import { InitializeResult, ModelSource, SModelRootSchema, - TYPES, - Writable + TYPES } from '~glsp-sprotty'; import { IDiagramOptions } from './diagram-loader'; /** * A helper interface that allows the client to mark actions that have been received from the server. */ export interface ServerAction extends Action { - _receivedFromServer: true; + __receivedFromServer: true; } export namespace ServerAction { export function is(object: unknown): object is ServerAction { - return Action.is(object) && '_receivedFromServer' in object && object._receivedFromServer === true; + return Action.is(object) && '__receivedFromServer' in object && object.__receivedFromServer === true; } /** @@ -47,7 +46,7 @@ export namespace ServerAction { * @param action The action that should be marked as server action */ export function mark(action: Action): void { - (action as ServerAction)._receivedFromServer = true; + (action as ServerAction).__receivedFromServer = true; } } @@ -71,7 +70,8 @@ export class GLSPModelSource extends ModelSource implements Disposable { protected toDispose = new DisposableCollection(); clientId: string; - readonly glspClient: GLSPClient | undefined; + + protected _glspClient: GLSPClient | undefined; protected _currentRoot: SModelRootSchema; get diagramType(): string { @@ -82,6 +82,10 @@ export class GLSPModelSource extends ModelSource implements Disposable { return this.options.sourceUri; } + get glspClient(): GLSPClient | undefined { + return this._glspClient; + } + @postConstruct() protected postConstruct(): void { this.clientId = this.options.clientId ?? this.viewerOptions.baseDiv; @@ -115,11 +119,14 @@ export class GLSPModelSource extends ModelSource implements Disposable { } this.options.glspClientProvider().then(glspClient => { - (this as Writable).glspClient = glspClient; + this._glspClient = glspClient; if (glspClient.initializeResult) { this.configure(registry, glspClient.initializeResult); } else { - glspClient.onServerInitialized(result => this.configure(registry, result)); + const initializeListener = glspClient.onServerInitialized(result => { + this.configure(registry, result); + initializeListener.dispose(); + }); } }); } @@ -163,6 +170,7 @@ export class GLSPModelSource extends ModelSource implements Disposable { return this._currentRoot; } + @preDestroy() dispose(): void { this.toDispose.dispose(); }