From 4ec58585ea2ce8fd4a6b0293e125106a7b251d10 Mon Sep 17 00:00:00 2001 From: Martin Fleck Date: Tue, 9 Jan 2024 17:29:44 +0100 Subject: [PATCH] Allow optional actions to avoid console errors --- packages/client/src/base/action-dispatcher.ts | 6 +++- .../src/base/model/glsp-model-source.ts | 28 +++++++++++++++++-- .../change-bounds-tool-feedback.ts | 11 ++++++-- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/packages/client/src/base/action-dispatcher.ts b/packages/client/src/base/action-dispatcher.ts index 74ebb45c..f3301f23 100644 --- a/packages/client/src/base/action-dispatcher.ts +++ b/packages/client/src/base/action-dispatcher.ts @@ -13,8 +13,9 @@ * * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { inject, injectable } from 'inversify'; import { Action, ActionDispatcher, RequestAction, ResponseAction } from '@eclipse-glsp/sprotty'; +import { inject, injectable } from 'inversify'; +import { OptionalAction } from './model/glsp-model-source'; import { ModelInitializationConstraint } from './model/model-initialization-constraint'; @injectable() @@ -76,6 +77,9 @@ export class GLSPActionDispatcher extends ActionDispatcher { action.responseId = ''; } } + if (!this.hasHandler(action) && OptionalAction.is(action)) { + return Promise.resolve(); + } return super.handleAction(action); } diff --git a/packages/client/src/base/model/glsp-model-source.ts b/packages/client/src/base/model/glsp-model-source.ts index 1687bef6..bdc2a790 100644 --- a/packages/client/src/base/model/glsp-model-source.ts +++ b/packages/client/src/base/model/glsp-model-source.ts @@ -14,7 +14,6 @@ * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 ********************************************************************************/ -import { inject, injectable, preDestroy } from 'inversify'; import { Action, ActionMessage, @@ -29,8 +28,10 @@ import { ModelSource, TYPES } from '@eclipse-glsp/sprotty'; +import { inject, injectable, preDestroy } from 'inversify'; import { GLSPActionHandlerRegistry } from '../action-handler-registry'; import { IDiagramOptions } from './diagram-loader'; + /** * A helper interface that allows the client to mark actions that have been received from the server. */ @@ -44,7 +45,7 @@ export namespace ServerAction { } /** - * Mark the given action as {@link ServerAction} by attaching the "_receivedFromServer" property + * Mark the given action as {@link ServerAction} by attaching the "__receivedFromServer" property * @param action The action that should be marked as server action */ export function mark(action: Action): void { @@ -52,6 +53,29 @@ export namespace ServerAction { } } +/** + * A helper interface that allows the client to mark actions that can be considered optional and should not throw an error if + * no handler is available. + */ +export interface OptionalAction extends Action { + __skipErrorIfNoHandler: true; +} + +export namespace OptionalAction { + export function is(object: unknown): object is ServerAction { + return Action.is(object) && '__skipErrorIfNoHandler' in object && object.__skipErrorIfNoHandler === true; + } + + /** + * Mark the given action as {@link OptionalAction} by attaching the "__skipErrorIfNoHandler" property + * @param action The action that should be marked as optional action + */ + export function mark(action: T): T & OptionalAction { + (action as unknown as OptionalAction).__skipErrorIfNoHandler = true; + return action as T & OptionalAction; + } +} + /** * Central component for enabling the client-server action flow with the help of an underlying {@link GLSPClient}. * Handles & forwards actions that are intended for the GLSP server. In addition, it handles {@link ActionMessage}s received diff --git a/packages/client/src/features/tools/change-bounds/change-bounds-tool-feedback.ts b/packages/client/src/features/tools/change-bounds/change-bounds-tool-feedback.ts index f6984c36..f91b03b3 100644 --- a/packages/client/src/features/tools/change-bounds/change-bounds-tool-feedback.ts +++ b/packages/client/src/features/tools/change-bounds/change-bounds-tool-feedback.ts @@ -36,6 +36,7 @@ import { DebouncedFunc, debounce } from 'lodash'; import { DragAwareMouseListener } from '../../../base/drag-aware-mouse-listener'; import { CursorCSS, cursorFeedbackAction } from '../../../base/feedback/css-feedback'; import { FeedbackCommand } from '../../../base/feedback/feedback-command'; +import { OptionalAction } from '../../../base/model/glsp-model-source'; import { forEachElement } from '../../../utils/gmodel-util'; import { SResizeHandle, addResizeHandles, isResizable, removeResizeHandles } from '../../change-bounds/model'; import { createMovementRestrictionFeedback, removeMovementRestrictionFeedback } from '../../change-bounds/movement-restrictor'; @@ -125,7 +126,7 @@ export namespace MoveInitializedEventAction { } export function create(): MoveInitializedEventAction { - return { kind: KIND }; + return OptionalAction.mark({ kind: KIND }); } } @@ -141,7 +142,7 @@ export namespace MoveFinishedEventAction { } export function create(): MoveFinishedEventAction { - return { kind: KIND }; + return OptionalAction.mark({ kind: KIND }); } } @@ -157,6 +158,7 @@ export class FeedbackMoveMouseListener extends DragAwareMouseListener implements protected positionUpdater; protected elementId2startPos = new Map(); protected pendingMoveInitialized?: DebouncedFunc<() => void>; + protected moveInitialized = false; constructor(protected tool: ChangeBoundsTool) { super(); @@ -178,10 +180,12 @@ export class FeedbackMoveMouseListener extends DragAwareMouseListener implements } protected scheduleMoveInitialized(): void { + this.moveInitialized = false; this.pendingMoveInitialized?.cancel(); this.pendingMoveInitialized = debounce(() => { this.tool.registerFeedback([MoveInitializedEventAction.create()], this); this.pendingMoveInitialized = undefined; + this.moveInitialized = true; }, 750); this.pendingMoveInitialized(); } @@ -329,11 +333,12 @@ export class FeedbackMoveMouseListener extends DragAwareMouseListener implements const moveAction = MoveAction.create(elementMoves, { animate: false, finished: true }); this.tool.deregisterFeedback(this, [moveAction]); } - } else if (resetFeedback) { + } else if (resetFeedback && this.moveInitialized) { this.tool.deregisterFeedback(this, [MoveFinishedEventAction.create()]); } this.positionUpdater.resetPosition(); this._isMouseDrag = false; + this.moveInitialized = false; this.rootElement = undefined; this.elementId2startPos.clear(); }