From cd726eeacc85d3a41efb755bc1da621e30ac6158 Mon Sep 17 00:00:00 2001 From: Antoine Detailleur <detailleur@fortiss.org> Date: Fri, 21 May 2021 15:44:25 +0200 Subject: [PATCH] [NRRPLT-8276] toast notifications for warning and progress functional (not CSSed) --- package-lock.json | 5 ++ package.json | 1 + src/components/dialog/error-dialog.js | 11 ++- src/components/dialog/toast-notification.css | 10 +++ src/components/dialog/toast-notification.js | 79 +++++++++++-------- .../experiment-files-viewer.js | 1 - .../experiment-list/experiment-list.css | 2 +- .../experiment-overview.js | 10 ++- src/services/dialog-service.js | 9 +-- .../execution/experiment-execution-service.js | 4 +- .../files/remote-experiment-files-service.js | 12 ++- src/utility/browser-name.js | 37 +++++++++ 12 files changed, 133 insertions(+), 48 deletions(-) create mode 100644 src/components/dialog/toast-notification.css create mode 100644 src/utility/browser-name.js diff --git a/package-lock.json b/package-lock.json index 4a62205..6f3d091 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10346,6 +10346,11 @@ } } }, + "jquery": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.6.0.tgz", + "integrity": "sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==" + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", diff --git a/package.json b/package.json index e4fa562..4fe6a73 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "@material-ui/lab": "4.0.0-alpha.57", "bootstrap": "4.5", "jszip": "3.2.0", + "jquery": "3.6.0", "react": "^17.0.1", "react-bootstrap": "1.4.0", "react-dom": "^17.0.1", diff --git a/src/components/dialog/error-dialog.js b/src/components/dialog/error-dialog.js index d5a6897..6c69fa7 100644 --- a/src/components/dialog/error-dialog.js +++ b/src/components/dialog/error-dialog.js @@ -15,10 +15,15 @@ class ErrorDialog extends React.Component{ } async componentDidMount() { + this.onError = this.onError.bind(this); DialogService.instance.addListener( - DialogService.EVENTS.ERROR, (error) => { - this.onError(error); - } + DialogService.EVENTS.ERROR, this.onError + ); + } + + componentWillUnmount() { + DialogService.instance.removeListener( + DialogService.EVENTS.ERROR, this.onError ); } diff --git a/src/components/dialog/toast-notification.css b/src/components/dialog/toast-notification.css new file mode 100644 index 0000000..9d29ecd --- /dev/null +++ b/src/components/dialog/toast-notification.css @@ -0,0 +1,10 @@ +.toast-notification-wrapper{ + position: fixed; + bottom: 0; + right: 0; + z-index: 999; +} + +.no-style{ + list-style-type: none; +} \ No newline at end of file diff --git a/src/components/dialog/toast-notification.js b/src/components/dialog/toast-notification.js index 564b137..892938f 100644 --- a/src/components/dialog/toast-notification.js +++ b/src/components/dialog/toast-notification.js @@ -1,60 +1,71 @@ -import { Toast } from 'bootstrap'; -import React from 'react' +import React from 'react'; +import { Toast } from 'react-bootstrap'; -import DialogService from '../../services/dialog-service.js' +import DialogService from '../../services/dialog-service.js'; + +import './toast-notification.css'; class NotificationDialog extends React.Component{ constructor(props){ super(props); this.state = { - notifications: [], + notifications: [] }; } async componentDidMount() { + this.onNotification = this.onNotification.bind(this); DialogService.instance.addListener( - DialogService.EVENTS.NOTIFICATION, (notification) => { - this.onNotification(notification); - } + DialogService.EVENTS.NOTIFICATION, this.onNotification + ); + } + + componentWillUnmount() { + DialogService.instance.removeListener( + DialogService.EVENTS.NOTIFICATION, this.onNotification ); } onNotification(notification) { this.setState({ - notifications: this.state.notifications.append(notification) + notifications: [...this.state.notifications, notification] }); } - onClose(index) { + handleClose(index) { + var copy = [...this.state.notifications]; + copy.splice(index, 1); this.setState({ - notifications: notifications.splice(index, 1) + notifications: copy }); } render(){ + let notifications = this.state.notifications; return( - <div> - {this.state.notifications? - <ol> - {this.state.notifications.map(notification => { - return ( - <div className="toast-dialog-wrapper"> - <Toast.Dialog onClose={this.onClose(index)}> - <Toast.Header> - <h4>{notification.type}</h4> - </Toast.Header> - <Toast.Body> - {notification.message} - </Toast.Body> - </Toast.Dialog> - </div> - ); - }) - } - </ol> - : null - } - </div> - ) + <div className='toast-notification-wrapper'> + {!notifications.length==0? + <ol> + {notifications.map((notification, index) => { + return ( + <li key={index} className='no-style'> + <Toast onClose={(index) => this.handleClose(index)}> + <Toast.Header> + <h4>{notification.type}</h4> + </Toast.Header> + <Toast.Body> + {notification.message} + </Toast.Body> + </Toast> + </li> + ); + })} + </ol> + : null + } + </div> + ); } -} \ No newline at end of file +} + +export default NotificationDialog; \ No newline at end of file diff --git a/src/components/experiment-files-viewer/experiment-files-viewer.js b/src/components/experiment-files-viewer/experiment-files-viewer.js index 222c160..7accbc6 100644 --- a/src/components/experiment-files-viewer/experiment-files-viewer.js +++ b/src/components/experiment-files-viewer/experiment-files-viewer.js @@ -99,7 +99,6 @@ export default class ExperimentFilesViewer extends React.Component { render() { let selectedExperimentFiles = this.state.selectedExperiment ? RemoteExperimentFilesService.instance.mapFileInfos.get(this.state.selectedExperiment.uuid) : undefined; - return ( <div> {RemoteExperimentFilesService.instance.isSupported() ? diff --git a/src/components/experiment-list/experiment-list.css b/src/components/experiment-list/experiment-list.css index 4eb57d7..0c37870 100644 --- a/src/components/experiment-list/experiment-list.css +++ b/src/components/experiment-list/experiment-list.css @@ -1,4 +1,4 @@ -li.nostyle { +.no-style { list-style-type: none; } diff --git a/src/components/experiment-overview/experiment-overview.js b/src/components/experiment-overview/experiment-overview.js index 43bc7ff..efca5cc 100644 --- a/src/components/experiment-overview/experiment-overview.js +++ b/src/components/experiment-overview/experiment-overview.js @@ -6,6 +6,7 @@ import ExperimentStorageService from '../../services/experiments/files/experimen import PublicExperimentsService from '../../services/experiments/files/public-experiments-service.js'; import ExperimentServerService from '../../services/experiments/execution/server-resources-service.js'; import ExperimentExecutionService from '../../services/experiments/execution/experiment-execution-service.js'; +import RemoteExperimentFilesService from '../../services/experiments/files/remote-experiment-files-service.js'; import ImportExperimentButtons from '../experiment-list/import-experiment-buttons.js'; import ExperimentList from '../experiment-list/experiment-list.js'; @@ -109,6 +110,13 @@ export default class ExperimentOverview extends React.Component { }); } + onSelectTab(index, lastIndex){ + this.setState({selectedTabIndex: index}); + if (index===3 && lastIndex!==3){ + RemoteExperimentFilesService.instance.notifyNotSupported(); + } + } + render() { return ( <div className='experiment-overview-wrapper'> @@ -118,7 +126,7 @@ export default class ExperimentOverview extends React.Component { <Tabs className="tabs-view" id="tabs-experiment-lists" selectedIndex={this.state.selectedTabIndex} - onSelect={(index) => this.setState({ selectedTabIndex: index })} > + onSelect={(index, lastIndex) => this.onSelectTab(index, lastIndex)} > <TabList> <Tab>My Experiments</Tab> <Tab>New Experiment</Tab> diff --git a/src/services/dialog-service.js b/src/services/dialog-service.js index 9a0f6b9..1398e8d 100644 --- a/src/services/dialog-service.js +++ b/src/services/dialog-service.js @@ -47,14 +47,13 @@ class DialogService extends EventEmitter { progressNotification(notification) { notification.type = 'Progress Status'; - this.emit(DialogService.EVENTS.NOTIFICATION, notification) + this.emit(DialogService.EVENTS.NOTIFICATION, notification); } warningNotification(notification) { - notification.type = 'Warning' - this.emit(DialogService.EVENTS.NOTIFICATION, notification) + notification.type = 'Warning'; + this.emit(DialogService.EVENTS.NOTIFICATION, notification); } - } DialogService.EVENTS = Object.freeze({ @@ -63,6 +62,6 @@ DialogService.EVENTS = Object.freeze({ DialogService.EVENTS = Object.freeze({ NOTIFICATION: 'NOTIFICATION' -}) +}); export default DialogService; \ No newline at end of file diff --git a/src/services/experiments/execution/experiment-execution-service.js b/src/services/experiments/execution/experiment-execution-service.js index bcadae7..4858fb3 100644 --- a/src/services/experiments/execution/experiment-execution-service.js +++ b/src/services/experiments/execution/experiment-execution-service.js @@ -61,8 +61,8 @@ class ExperimentExecutionService extends HttpService { let brainProcesses = launchSingleMode ? 1 : experiment.configuration.brainProcesses; //TODO: placeholder, register actual progress callback later - let progressCallback = (notification) => { - DialogService.instance.progressNotification(notification); + let progressCallback = () => { + DialogService.instance.progressNotification({message:'The experiment is loading'}); }; let launchOnNextServer = async () => { diff --git a/src/services/experiments/files/remote-experiment-files-service.js b/src/services/experiments/files/remote-experiment-files-service.js index c9625df..4e49413 100644 --- a/src/services/experiments/files/remote-experiment-files-service.js +++ b/src/services/experiments/files/remote-experiment-files-service.js @@ -1,6 +1,8 @@ import { HttpService } from '../../http-service.js'; import ExperimentStorageService from './experiment-storage-service'; import getMimeByExtension from '../../../utility/mime-type'; +import DialogService from '../../dialog-service'; +import browserName from '../../../utility/browser-name'; let _instance = null; const SINGLETON_ENFORCER = Symbol(); @@ -35,10 +37,18 @@ class RemoteExperimentFilesService extends HttpService { return _instance; } - isSupported() { + isSupported (){ return window.showDirectoryPicker !== undefined && window.showDirectoryPicker !== null; } + notifyNotSupported() { + if (!this.isSupported()){ + DialogService.instance.warningNotification({ + message : 'The remote experiment files is not supported on ' + browserName() + }); + } + } + toggleAutoSync() { this.autoSync = !this.autoSync; } diff --git a/src/utility/browser-name.js b/src/utility/browser-name.js new file mode 100644 index 0000000..af1274f --- /dev/null +++ b/src/utility/browser-name.js @@ -0,0 +1,37 @@ + +function browserName(){ + // CHROME + if (navigator.userAgent.indexOf('Chrome') !== -1 ) { + return 'Chrome'; + } + // FIREFOX + else if (navigator.userAgent.indexOf('Firefox') !== -1 ) { + return 'Firefox'; + } + // INTERNET EXPLORER + else if (navigator.userAgent.indexOf('MSIE') !== -1 ) { + return 'Internet Explorer'; + } + // EDGE + else if (navigator.userAgent.indexOf('Edge') !== -1 ) { + return 'Edge'; + } + // SAFARI + else if (navigator.userAgent.indexOf('Safari') !== -1 ) { + return 'Safari'; + } + // OPERA + else if (navigator.userAgent.indexOf('Opera') !== -1 ) { + return 'Opera'; + } + // YANDEX + else if (navigator.userAgent.indexOf('YaBrowser') !== -1 ) { + return 'YaBrowser'; + } + // OTHER + else { + return 'Other'; + } +} + +export default browserName; \ No newline at end of file -- GitLab