Commit 96b690f5 authored by Florent Chehab's avatar Florent Chehab

feat(front): Created a notification service. Migrated all code to use it.

parent 0e2adf44
......@@ -4,6 +4,8 @@
###### _TBD_
- [refacto] WIP drop CustomComponentForAPI
- [feat/refacto] Notification Service, FullScreen dialog service (no more redux)
- [frontend linting] Config update, big refactoring to conform
- [prettier] added for frontend
- [update] Frontend dependancies and frontend docker image
......
......@@ -4,7 +4,7 @@
import React from "react";
import { compose } from "recompose";
import { Route, Switch } from "react-router-dom";
import FullScreenDialog from "../services/FullScreenDialog";
import FullScreenDialogServiceComponent from "../services/FullScreenDialogServiceComponent";
import { withErrorBoundary } from "../common/ErrorBoundary";
import PageMap from "../pages/PageMap";
......@@ -27,6 +27,8 @@ import PageAboutUnlinkedPartners from "../pages/PageAboutUnlinkedPartners";
import PageLogout from "../pages/PageLogout";
import withNetworkWrapper, { NetWrapParam } from "../../hoc/withNetworkWrapper";
import FullScreenDialogService from "../../services/FullScreenDialogService";
import NotificationServiceComponent from "../services/NotificationServiceComponent";
import NotificationService from "../../services/NotificationService";
// import PageFiles from "../pages/PageFiles";
......@@ -44,7 +46,10 @@ function App() {
}}
>
<MainAppFrame>
<FullScreenDialog ref={FullScreenDialogService.setComponent} />
<FullScreenDialogServiceComponent
ref={FullScreenDialogService.setComponent}
/>
<NotificationServiceComponent ref={NotificationService.setComponent} />
<NotifierImportantInformation />
<main>
<Switch>
......
......@@ -2,27 +2,21 @@ import React from "react";
import { compose } from "recompose";
import PropTypes from "prop-types";
import { withErrorBoundary } from "../common/ErrorBoundary";
import Notifier from "../common/Notifier";
import RequestParams from "../../redux/api/RequestParams";
import withNetworkWrapper, { NetWrapParam } from "../../hoc/withNetworkWrapper";
import NotificationService from "../../services/NotificationService";
function NotifierImportantInformation({ informationList }) {
return (
<>
{informationList
.filter(el => el.should_notify)
.map(el => (
<Notifier
key={el.id}
message={el.message}
options={{
variant: el.variant,
autoHideDuration: null
}}
/>
))}
</>
);
informationList
.filter(el => el.should_notify)
.forEach(el =>
NotificationService.notify(el.message, {
variant: el.variant,
autoHideDuration: null
})
);
return <></>;
}
NotifierImportantInformation.propTypes = {
......
import { IconButton } from "@material-ui/core";
import { Close as IconClose } from "@material-ui/icons";
import { useSnackbar } from "notistack";
import React from "react";
import PropTypes from "prop-types";
import NotificationService from "../../services/NotificationService";
function SnackbarCloseButton({ notiKey }) {
const { closeSnackbar } = useSnackbar();
return (
<IconButton onClick={() => closeSnackbar(notiKey)}>
<IconButton onClick={() => NotificationService.closeNotification(notiKey)}>
<IconClose />
</IconButton>
);
......
......@@ -3,8 +3,8 @@ import React, { Component } from "react";
import PropTypes from "prop-types";
import Loading from "./Loading";
import { apiDataIsUsable, getLatestRead } from "../../redux/api/utils";
import Notifier from "./Notifier";
import RequestParams from "../../redux/api/RequestParams";
import NotificationService from "../../services/NotificationService";
/**
* Custom react component to be used when called to the api are required to display data of the component.
......@@ -149,9 +149,11 @@ class CustomComponentForAPI extends Component {
// Override of the default react render behavior to wait for data to arrive.
// You should use `customRender` instead of `render` in your components.
if (this.checkPropsFailed()) {
NotificationService.error(
"Une erreur est survenue sur le serveur. Merci de recharger le page."
);
return (
<>
<Notifier message="Une erreur est survenue sur le serveur. Merci de recharger le page." />
<p>
Une erreur est survenue lors du téléchargement des données. Merci de
recharger la page et si l'erreur persiste, merci de contacter le
......
import React, { Component } from "react";
import { withSnackbar } from "notistack";
import PropTypes from "prop-types";
/**
* Dumb component to display a one shot notification:
* notification will be created as the component is added to the DOM.
*
* @class Notifier
* @extends {Component}
*/
class Notifier extends Component {
constructor(props) {
super(props);
props.enqueueSnackbar(props.message, props.options);
}
render() {
return <></>;
}
}
Notifier.defaultProps = {
options: {
variant: "error",
autoHideDuration: 5000
}
};
Notifier.propTypes = {
enqueueSnackbar: PropTypes.func.isRequired,
message: PropTypes.string.isRequired,
options: PropTypes.object
};
export default withSnackbar(Notifier);
......@@ -13,6 +13,7 @@ import Alert from "../common/Alert";
// eslint-disable-next-line no-unused-vars
import Form from "../form/Form";
import FullScreenDialogService from "../../services/FullScreenDialogService";
import NotificationService from "../../services/NotificationService";
/**
* Class to handle editions of models on the frontend. It should be extended, eg:
......@@ -330,20 +331,17 @@ class Editor extends Component {
// Notifications related
notifyNoChangesDetected() {
this.props.enqueueSnackbar("Aucun changement n'a été repéré.", {
variant: "info"
});
NotificationService.info("Aucun changement n'a été repéré.");
}
notifyFormHasErrors() {
this.props.enqueueSnackbar(
"Le formulaire semble incohérent, merci de vérifier son contenu.",
{ variant: "error" }
NotificationService.error(
"Le formulaire semble incohérent, merci de vérifier son contenu."
);
}
notifyIsSaving() {
this.savingNotification = this.props.enqueueSnackbar(
this.savingNotification = NotificationService.notify(
"Enregistrement en cours",
{
variant: "info",
......@@ -355,7 +353,7 @@ class Editor extends Component {
notifySaveSuccessful(message) {
setTimeout(() => this.removeSavingNotification(), 1500); // add a little delay for smoothing
this.props.enqueueSnackbar(message, {
NotificationService.notify(message, {
variant: "success",
autoHideDuration: 5000
});
......@@ -363,7 +361,7 @@ class Editor extends Component {
removeSavingNotification() {
if (this.savingNotification) {
this.props.closeSnackbar(this.savingNotification);
NotificationService.closeNotification(this.savingNotification);
this.savingNotification = null;
}
}
......@@ -380,10 +378,7 @@ Editor.propTypes = {
clearSaveError: PropTypes.func.isRequired,
lastUpdateTimeInModel: PropTypes.string,
hasPendingModeration: PropTypes.bool,
saveData: PropTypes.func.isRequired,
// Notifications related
enqueueSnackbar: PropTypes.func.isRequired,
closeSnackbar: PropTypes.func.isRequired
saveData: PropTypes.func.isRequired
};
Editor.defaultProps = {
......
......@@ -3,7 +3,7 @@ import Dialog from "@material-ui/core/Dialog";
import Slide from "@material-ui/core/Slide";
/**
* Component to enable the FullScreenDialog to have nice transitions
* Component to enable the FullScreenDialogServiceComponent to have nice transitions
* @returns
*/
// eslint-disable-next-line react/display-name
......@@ -17,7 +17,7 @@ const Transition = React.forwardRef((props, ref) => (
*
* WARNING must be a class for the full screen dialog service to work !
*/
class FullScreenDialog extends Component {
class FullScreenDialogServiceComponent extends Component {
state = { open: false, children: undefined };
render() {
......@@ -31,4 +31,4 @@ class FullScreenDialog extends Component {
}
}
export default FullScreenDialog;
export default FullScreenDialogServiceComponent;
import React, { Component } from "react";
import { withSnackbar } from "notistack";
import PropTypes from "prop-types";
/**
* Component Used by the notification service to access the enqueueSnackbar method.
*
* WARNING: must be a class to work with ref direct access.
*/
// eslint-disable-next-line react/prefer-stateless-function
class NotificationServiceComponent extends Component {
render() {
return <></>;
}
}
NotificationServiceComponent.propTypes = {
// eslint-disable-next-line react/no-unused-prop-types
enqueueSnackbar: PropTypes.func.isRequired
};
export default withSnackbar(NotificationServiceComponent);
......@@ -4,7 +4,6 @@ import withStyles from "@material-ui/core/styles/withStyles";
import compose from "recompose/compose";
import { connect } from "react-redux";
import { withSnackbar } from "notistack";
import Editor from "../../editor/Editor";
import Form from "../../form/Form";
import editorStyle from "../../editor/editorStyle";
......@@ -45,7 +44,6 @@ CountryDriEditor.propTypes = {
};
export default compose(
withSnackbar,
withStyles(styles, { withTheme: true }),
connect(
getMapStateToPropsForEditor("countryDri"),
......
......@@ -4,7 +4,6 @@ import withStyles from "@material-ui/core/styles/withStyles";
import compose from "recompose/compose";
import { connect } from "react-redux";
import { withSnackbar } from "notistack";
import ScholarshipForm from "./common/ScholarshipForm";
import Editor from "../../editor/Editor";
import editorStyle from "../../editor/editorStyle";
......@@ -37,7 +36,6 @@ CountryScholarshipEditor.propTypes = {
};
export default compose(
withSnackbar,
withStyles(styles, { withTheme: true }),
connect(
getMapStateToPropsForEditor("countryScholarships"),
......
......@@ -3,7 +3,6 @@ import withStyles from "@material-ui/core/styles/withStyles";
import compose from "recompose/compose";
import { connect } from "react-redux";
import { withSnackbar } from "notistack";
import Editor from "../../editor/Editor";
import Form from "../../form/Form";
import editorStyle from "../../editor/editorStyle";
......@@ -51,7 +50,6 @@ class SharedUnivFeedbackEditor extends Editor {
}
export default compose(
withSnackbar,
withStyles(styles, { withTheme: true }),
connect(
getMapStateToPropsForEditor("sharedUnivFeedbacks"),
......
......@@ -3,7 +3,6 @@ import withStyles from "@material-ui/core/styles/withStyles";
import compose from "recompose/compose";
import { connect } from "react-redux";
import { withSnackbar } from "notistack";
import Editor from "../../editor/Editor";
import Form from "../../form/Form";
import editorStyle from "../../editor/editorStyle";
......@@ -48,7 +47,6 @@ class TaggedItemEditor extends Editor {
TaggedItemEditor.propTypes = {};
export const CountryTaggedItemEditor = compose(
withSnackbar,
withStyles(styles, { withTheme: true }),
connect(
getMapStateToPropsForEditor("countryTaggedItems"),
......@@ -57,7 +55,6 @@ export const CountryTaggedItemEditor = compose(
)(TaggedItemEditor);
export const UniversityTaggedItemEditor = compose(
withSnackbar,
withStyles(styles, { withTheme: true }),
connect(
getMapStateToPropsForEditor("universityTaggedItems"),
......
......@@ -4,7 +4,6 @@ import withStyles from "@material-ui/core/styles/withStyles";
import compose from "recompose/compose";
import { connect } from "react-redux";
import { withSnackbar } from "notistack";
import Editor from "../../editor/Editor";
import Form from "../../form/Form";
import editorStyle from "../../editor/editorStyle";
......@@ -45,7 +44,6 @@ UniversityDriEditor.propTypes = {
};
export default compose(
withSnackbar,
withStyles(styles, { withTheme: true }),
connect(
getMapStateToPropsForEditor("universityDri"),
......
......@@ -3,7 +3,6 @@ import withStyles from "@material-ui/core/styles/withStyles";
import compose from "recompose/compose";
import { connect } from "react-redux";
import { withSnackbar } from "notistack";
import Editor from "../../editor/Editor";
import Form from "../../form/Form";
import editorStyle from "../../editor/editorStyle";
......@@ -62,7 +61,6 @@ class UniversityGeneralEditor extends Editor {
}
export default compose(
withSnackbar,
withStyles(styles, { withTheme: true }),
connect(
getMapStateToPropsForEditor("universities"),
......
......@@ -3,7 +3,6 @@ import PropTypes from "prop-types";
import withStyles from "@material-ui/core/styles/withStyles";
import compose from "recompose/compose";
import { connect } from "react-redux";
import { withSnackbar } from "notistack";
import ScholarshipForm from "./common/ScholarshipForm";
import Editor from "../../editor/Editor";
......@@ -39,7 +38,6 @@ UniversityScholarshipEditor.propTypes = {
};
export default compose(
withSnackbar,
withStyles(styles, { withTheme: true }),
connect(
getMapStateToPropsForEditor("universityScholarships"),
......
......@@ -3,7 +3,6 @@ import withStyles from "@material-ui/core/styles/withStyles";
import compose from "recompose/compose";
import { connect } from "react-redux";
import { withSnackbar } from "notistack";
import dateStrToDate from "../../../utils/dateStrToDate";
import DateField from "../../form/fields/DateField";
......@@ -91,7 +90,6 @@ class UniversitySemestersDatesEditor extends Editor {
}
export default compose(
withSnackbar,
withStyles(styles, { withTheme: true }),
connect(
getMapStateToPropsForEditor("universitiesSemestersDates"),
......
......@@ -3,7 +3,6 @@ import withStyles from "@material-ui/core/styles/withStyles";
import compose from "recompose/compose";
import { connect } from "react-redux";
import { withSnackbar } from "notistack";
import Form from "../../../../form/Form";
import editorStyle from "../../../../editor/editorStyle";
import MarkdownField from "../../../../form/fields/MarkdownField";
......@@ -82,7 +81,6 @@ class CourseFeedbackEditor extends Editor {
}
export default compose(
withSnackbar,
withStyles(styles, { withTheme: true }),
connect(
getMapStateToPropsForEditor("courseFeedbacks"),
......
......@@ -3,7 +3,6 @@ import withStyles from "@material-ui/core/styles/withStyles";
import compose from "recompose/compose";
import { connect } from "react-redux";
import { withSnackbar } from "notistack";
import Form from "../../../../form/Form";
import editorStyle from "../../../../editor/editorStyle";
import MarkdownField from "../../../../form/fields/MarkdownField";
......@@ -72,7 +71,6 @@ class GeneralFeedbackEditor extends Editor {
}
export default compose(
withSnackbar,
withStyles(styles, { withTheme: true }),
connect(
getMapStateToPropsForEditor("exchangeFeedbacks"),
......
......@@ -3,7 +3,6 @@ import withStyles from "@material-ui/core/styles/withStyles";
import compose from "recompose/compose";
import { connect } from "react-redux";
import { withSnackbar } from "notistack";
import PropTypes from "prop-types";
import Form from "../form/Form";
import editorStyle from "../editor/editorStyle";
......@@ -80,7 +79,6 @@ class PictureEditor extends Editor {
}
export default compose(
withSnackbar,
withStyles(styles, { withTheme: true }),
connect(
getMapStateToPropsForEditor("pictures"),
......
......@@ -3,7 +3,6 @@ import withStyles from "@material-ui/core/styles/withStyles";
import compose from "recompose/compose";
import { connect } from "react-redux";
import { withSnackbar } from "notistack";
import Form from "../form/Form";
import editorStyle from "../editor/editorStyle";
import TextField from "../form/fields/TextField";
......@@ -72,7 +71,6 @@ class UserInfoEditor extends Editor {
}
export default compose(
withSnackbar,
withStyles(styles, { withTheme: true }),
connect(
getMapStateToPropsForEditor("users"),
......
/**
* Manage the full screen dialog
*/
class NotificationService {
/**
* @type {React.Component}
* @private
*/
_component = undefined;
/**
* TO BE USED ONLY in App.jsx to get the ref to the component
* @param ref
*/
setComponent = ref => {
this._component = ref;
};
notify = (message, option) =>
this._component.props.enqueueSnackbar(message, option);
info = message => this.notify(message, { variant: "info" });
error = message => this.notify(message, { variant: "error" });
closeNotification = notificationKey =>
this._component.props.closeSnackbar(notificationKey);
}
export default new NotificationService();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment