Skip to content

Commit

Permalink
[1912] Add the possibility to send feedback messages to the frontend
Browse files Browse the repository at this point in the history
Bug: #1912
Signed-off-by: Florian Rouëné <florian.rouene@obeosoft.com>
  • Loading branch information
frouene authored and gcoutable committed May 25, 2023
1 parent 5a2f8ff commit a809ed1
Show file tree
Hide file tree
Showing 75 changed files with 601 additions and 1,068 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
- [ADR-098] Use the editing context to compute the metamodels
- [ADR-099] Filter tree based representations
- [ADR-100] Add support for custom widgets
- [ADR-101] Feedback messages on actions

=== Breaking changes

Expand Down Expand Up @@ -76,6 +77,8 @@ See link:how-to/contribute-custom-widget.adoc[the documentation] for more detail
- https://github.com/eclipse-sirius/sirius-components/issues/1985[|#1985] [sirius-web] It is now possible to use in-memory View-based representations by registering them in the new `InMemoryViewRegistry`.
These representations can be created programmatically or loaded from `.view` EMF models on startup, and do not need to be stored as documents inside a project in the database.

- https://github.com/eclipse-sirius/sirius-components/issues/1912[#1912] [core] Add the possibility to send feedback messages to the frontend after an action.

=== Improvements

- https://github.com/eclipse-sirius/sirius-components/issues/1869[#1869] [tree] Navigate to the first child with the right arrow if a node is expanded.
Expand Down
62 changes: 62 additions & 0 deletions doc/adrs/101_feedback_messages_on_actions.adoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
= ADR-101 - Add the possibility to send feedback messages to the frontend after an action

== Context

After an action, only technical error messages can be displayed on the UI.
We want to offer the specifier the ability to send back custom return messages to the end user.

== Decision

We will provide a new Java service that specifiers can invoke during the execution of their actions (tools behaviors) to stack messages.
These messages will be displayed on the UI after the action resolution.

=== Backend

The interface will provide the following method :

[source,java]
----
interface IFeedbackMessageService {
void addFeedbackMessage(String message, FeedbackLevel level);
}
----
with
[source,java]
----
enum FeedbackLevel {
DEBUG,
INFO,
WARNING,
ERROR,
}
----

The implementation must have a scope to limit the _bean_ life cycle to the action only, in order to just return at the end of the action the messages stacked by the services called by the action.
The spring scope of type _request_ provides this limitation.

At first, we will join all the _string messages_ stacked in a single string, and append it to the existing `Failure` (`IStatus` implementation) _message_ parameter.
In doing so, we can keep the actual _frontend_ representation for this message.

To differentiate the level applied to the message, we will prefix it with an emoji representing this level.

=== Frontend

To begin, we will not change the actual representation for an action error message.
But in order to facilitate the maintenance and the evolutions, we will mutualize all these representations in a single custom component `Toast`.

with

[source,typescript]
----
export interface ToastProps {
message?: string;
open?: boolean;
onClose?: () => void;
}
----

Next, we will change this representation to display, for each stacked message, a unique _notification_ with a style depending on the chosen level.

== Status

Accepted.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*******************************************************************************
* Copyright (c) 2023 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.components.core.api;

/**
* Enumeration used to indicate the feedback message level.
*
* @author frouene
*/
public enum FeedbackLevel {
DEBUG, INFO, WARNING, ERROR,
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright (c) 2023 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.components.core.api;

import java.util.Objects;

/**
* Record used to represent a feedback message.
*
* @author frouene
*/
public record FeedbackMessage(String message, FeedbackLevel level) {

public FeedbackMessage {
Objects.requireNonNull(message);
Objects.requireNonNull(level);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*******************************************************************************
* Copyright (c) 2023 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.components.core.api;

import java.util.List;

/**
* Interface of the service interacting with the list of stacked feedback messages.
*
* @author frouene
*/
public interface IFeedbackMessageService {

void addFeedbackMessage(String message, FeedbackLevel level);

List<String> getFeedbackMessages();

/**
* Implementation which does nothing, used for mocks in unit tests.
*
* @author frouene
*/
class NoOp implements IFeedbackMessageService {

@Override
public void addFeedbackMessage(String message, FeedbackLevel level) {

}

@Override
public List<String> getFeedbackMessages() {
return List.of();
}
}
}
1 change: 1 addition & 0 deletions packages/core/frontend/sirius-components-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export * from './materialui';
export * from './theme';
export * from './workbench/Panels';
export * from './workbench/RepresentationContext';
export * from './workbench/Toast';
export * from './workbench/Workbench';
export * from './workbench/Workbench.types';
export * from './workbench/WorkbenchViewContribution';
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*******************************************************************************
* Copyright (c) 2023 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
import IconButton from '@material-ui/core/IconButton';
import Snackbar from '@material-ui/core/Snackbar';
import CloseIcon from '@material-ui/icons/Close';
import { ToastProps } from './Toast.types';

export const Toast = ({ message, open, onClose }: ToastProps) => {
return (
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right',
}}
open={open}
autoHideDuration={3000}
onClose={onClose}
message={message}
action={
<IconButton size="small" aria-label="close" color="inherit" onClick={() => onClose()}>
<CloseIcon fontSize="small" />
</IconButton>
}
data-testid="toast"
/>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/*******************************************************************************
* Copyright (c) 2023 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/

export interface ToastProps {
message?: string;
open?: boolean;
onClose?: () => void;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,13 @@
* Obeo - initial API and implementation
*******************************************************************************/
import { gql, useSubscription } from '@apollo/client';
import IconButton from '@material-ui/core/IconButton';
import Snackbar from '@material-ui/core/Snackbar';
import { makeStyles } from '@material-ui/core/styles';
import CloseIcon from '@material-ui/icons/Close';
import { useMachine } from '@xstate/react';
import React, { useCallback, useContext, useEffect } from 'react';
import { Panels } from './Panels';
import { RepresentationContext } from './RepresentationContext';
import { RepresentationNavigation } from './RepresentationNavigation';
import { Toast } from './Toast';
import {
GQLEditingContextEventSubscription,
Representation,
Expand Down Expand Up @@ -205,25 +203,10 @@ export const Workbench = ({
rightPanelInitialSize={300}
mainArea={main}
/>
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right',
}}
<Toast
message={message}
open={toast === 'visible'}
autoHideDuration={3000}
onClose={() => dispatch({ type: 'HIDE_TOAST' } as HideToastEvent)}
message={message}
action={
<IconButton
size="small"
aria-label="close"
color="inherit"
onClick={() => dispatch({ type: 'HIDE_TOAST' } as HideToastEvent)}>
<CloseIcon fontSize="small" />
</IconButton>
}
data-testid="error"
/>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,13 @@
* Obeo - initial API and implementation
*******************************************************************************/
import { gql, useMutation } from '@apollo/client';
import { Toast } from '@eclipse-sirius/sirius-components-core';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogContentText from '@material-ui/core/DialogContentText';
import DialogTitle from '@material-ui/core/DialogTitle';
import IconButton from '@material-ui/core/IconButton';
import Snackbar from '@material-ui/core/Snackbar';
import CloseIcon from '@material-ui/icons/Close';
import { useMachine } from '@xstate/react';
import {
DeleteProjectModalContext,
Expand Down Expand Up @@ -123,25 +121,10 @@ export const DeleteProjectModal = ({ projectId, onDelete, onClose }: DeleteProje
</Button>
</DialogActions>
</Dialog>
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right',
}}
<Toast
message={message}
open={toast === 'visible'}
autoHideDuration={3000}
onClose={() => dispatch({ type: 'HIDE_TOAST' } as HideToastEvent)}
message={message}
action={
<IconButton
size="small"
aria-label="close"
color="inherit"
onClick={() => dispatch({ type: 'HIDE_TOAST' } as HideToastEvent)}>
<CloseIcon fontSize="small" />
</IconButton>
}
data-testid="error"
/>
</>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,16 @@
* Obeo - initial API and implementation
*******************************************************************************/
import { gql, useMutation, useQuery } from '@apollo/client';
import { Toast } from '@eclipse-sirius/sirius-components-core';
import Button from '@material-ui/core/Button';
import Dialog from '@material-ui/core/Dialog';
import DialogActions from '@material-ui/core/DialogActions';
import DialogContent from '@material-ui/core/DialogContent';
import DialogTitle from '@material-ui/core/DialogTitle';
import IconButton from '@material-ui/core/IconButton';
import InputLabel from '@material-ui/core/InputLabel';
import MenuItem from '@material-ui/core/MenuItem';
import Select from '@material-ui/core/Select';
import Snackbar from '@material-ui/core/Snackbar';
import { makeStyles } from '@material-ui/core/styles';
import CloseIcon from '@material-ui/icons/Close';
import { useMachine } from '@xstate/react';
import React, { useEffect } from 'react';
import {
Expand Down Expand Up @@ -207,25 +205,10 @@ export const NewObjectModal = ({ editingContextId, item, onObjectCreated, onClos
</Button>
</DialogActions>
</Dialog>
<Snackbar
anchorOrigin={{
vertical: 'bottom',
horizontal: 'right',
}}
<Toast
message={message}
open={toast === 'visible'}
autoHideDuration={3000}
onClose={() => dispatch({ type: 'HIDE_TOAST' } as HideToastEvent)}
message={message}
action={
<IconButton
size="small"
aria-label="close"
color="inherit"
onClick={() => dispatch({ type: 'HIDE_TOAST' } as HideToastEvent)}>
<CloseIcon fontSize="small" />
</IconButton>
}
data-testid="error"
/>
</>
);
Expand Down
Loading

0 comments on commit a809ed1

Please sign in to comment.