Commit 1373af74 authored by Florent Chehab's avatar Florent Chehab
Browse files

Added notistack for crasy easy handling of notifications.

Notifications in the editor are handled again.
parent c3ddb098
...@@ -8521,6 +8521,15 @@ ...@@ -8521,6 +8521,15 @@
"resolved": "https://registry.npmjs.org/normalize-scroll-left/-/normalize-scroll-left-0.1.2.tgz", "resolved": "https://registry.npmjs.org/normalize-scroll-left/-/normalize-scroll-left-0.1.2.tgz",
"integrity": "sha512-F9YMRls0zCF6BFIE2YnXDRpHPpfd91nOIaNdDgrx5YMoPLo8Wqj+6jNXHQsYBavJeXP4ww8HCt0xQAKc5qk2Fg==" "integrity": "sha512-F9YMRls0zCF6BFIE2YnXDRpHPpfd91nOIaNdDgrx5YMoPLo8Wqj+6jNXHQsYBavJeXP4ww8HCt0xQAKc5qk2Fg=="
}, },
"notistack": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/notistack/-/notistack-0.4.3.tgz",
"integrity": "sha512-YVG4IqA7+eCmMRH42UE+KZOIXTPg/3HEWU+pE1S+Tc8mUNSTQlZMdLy7XwDF1+WimyLiA7Glm+y+ClSu/4t+WA==",
"requires": {
"classnames": "^2.2.6",
"prop-types": "^15.6.2"
}
},
"npm-run-path": { "npm-run-path": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
"leaflet": "^1.4.0", "leaflet": "^1.4.0",
"lodash": "^4.17.11", "lodash": "^4.17.11",
"material-ui-pickers": "^2.2.1", "material-ui-pickers": "^2.2.1",
"notistack": "^0.4.3",
"react": "^16.8.3", "react": "^16.8.3",
"react-awesome-slider": "^0.5.2", "react-awesome-slider": "^0.5.2",
"react-dom": "^16.8.3", "react-dom": "^16.8.3",
......
...@@ -6,8 +6,6 @@ ...@@ -6,8 +6,6 @@
// ie common fot the entire app // ie common fot the entire app
export const OPEN_FULL_SCREEN_DIALOG = "OPEN_FULL_SCREEN_DIALOG"; export const OPEN_FULL_SCREEN_DIALOG = "OPEN_FULL_SCREEN_DIALOG";
export const CLOSE_FULL_SCREEN_DIALOG = "CLOSE_UNIVERSITY_BEING_VIEWED"; export const CLOSE_FULL_SCREEN_DIALOG = "CLOSE_UNIVERSITY_BEING_VIEWED";
export const CREATE_NOTIFICATION = "CREATE_NOTIFICATION";
export const DELETE_NOTIFICATION = "DELETE_NOTIFICATION";
// Other redux actions // Other redux actions
export const SAVE_MAIN_MAP_STATUS = "SAVE_MAIN_MAP_STATUS"; export const SAVE_MAIN_MAP_STATUS = "SAVE_MAIN_MAP_STATUS";
......
/*
* This file contains the redux actions related to the handling of notifications in the app
*/
import {
CREATE_NOTIFICATION,
DELETE_NOTIFICATION,
} from "./action-types";
/**
* Action: Create a notifiacation in the app
*
* @export
* @param {object} attrs
* @returns {object}
*/
export function createNotification(attrs) {
return {
type: CREATE_NOTIFICATION,
attrs
};
}
/**
* Action: Delete a notification
* TODO add support for multiple
*
* @export
* @returns {object}
*/
export function deleteNotification() {
return {
type: DELETE_NOTIFICATION,
};
}
...@@ -18,7 +18,6 @@ import ChevronLeftIcon from "@material-ui/icons/ChevronLeft"; ...@@ -18,7 +18,6 @@ import ChevronLeftIcon from "@material-ui/icons/ChevronLeft";
import SchoolIcon from "@material-ui/icons/School"; import SchoolIcon from "@material-ui/icons/School";
import { mainListItems, secondaryListItems, thirdListItems } from "./appRelated/listItems"; import { mainListItems, secondaryListItems, thirdListItems } from "./appRelated/listItems";
import FullScreenDialog from "./appRelated/FullScreenDialog"; import FullScreenDialog from "./appRelated/FullScreenDialog";
import Notifications from "./appRelated/Notifications";
import { connect } from "react-redux"; import { connect } from "react-redux";
import CustomComponentForAPI from "./CustomComponentForAPI"; import CustomComponentForAPI from "./CustomComponentForAPI";
...@@ -113,8 +112,6 @@ class App extends CustomComponentForAPI { ...@@ -113,8 +112,6 @@ class App extends CustomComponentForAPI {
</Drawer> </Drawer>
<Notifications />
<FullScreenDialog /> <FullScreenDialog />
<main className={classNames(classes.content, classes.noPaddingTop)}> <main className={classNames(classes.content, classes.noPaddingTop)}>
......
...@@ -11,6 +11,7 @@ import TextField from "../shared/fields/TextField"; ...@@ -11,6 +11,7 @@ import TextField from "../shared/fields/TextField";
import getMapStateToPropsForEditor from "../shared/editorFunctions/getMapStateToPropsForEditor"; import getMapStateToPropsForEditor from "../shared/editorFunctions/getMapStateToPropsForEditor";
import getMapDispatchToPropsForEditor from "../shared/editorFunctions/getMapDispatchToPropsForEditor"; import getMapDispatchToPropsForEditor from "../shared/editorFunctions/getMapDispatchToPropsForEditor";
import { withSnackbar } from "notistack";
const styles = theme => ({ const styles = theme => ({
...editorStyle(theme) ...editorStyle(theme)
...@@ -58,6 +59,7 @@ class UniversityGeneralEditor extends Editor { ...@@ -58,6 +59,7 @@ class UniversityGeneralEditor extends Editor {
export default compose( export default compose(
withSnackbar,
withStyles(styles, { withTheme: true }), withStyles(styles, { withTheme: true }),
connect( connect(
getMapStateToPropsForEditor("universities"), getMapStateToPropsForEditor("universities"),
......
...@@ -9,6 +9,7 @@ import Typography from "@material-ui/core/Typography"; ...@@ -9,6 +9,7 @@ import Typography from "@material-ui/core/Typography";
import CloseIcon from "@material-ui/icons/Close"; import CloseIcon from "@material-ui/icons/Close";
import Alert from "./Alert"; import Alert from "./Alert";
// import renderFieldsMixIn from "./editorFunctions/renderFieldsMixIn"; // import renderFieldsMixIn from "./editorFunctions/renderFieldsMixIn";
// Form is imported only for type hints // Form is imported only for type hints
...@@ -37,13 +38,13 @@ class Editor extends Component { ...@@ -37,13 +38,13 @@ class Editor extends Component {
alert: { alert: {
open: false, open: false,
}, },
notification: {
open: false,
},
} }
formRef = React.createRef(); formRef = React.createRef();
// Store the "saving" notification so that we can delete it when done saving.
savingNotification = null;
/** /**
* Returns the form instance associated with the editor * Returns the form instance associated with the editor
* *
...@@ -127,7 +128,7 @@ class Editor extends Component { ...@@ -127,7 +128,7 @@ class Editor extends Component {
* @param {Boolean} somethingWasSaved * @param {Boolean} somethingWasSaved
* @memberof Editor * @memberof Editor
*/ */
closeEditor(somethingWasSaved){ closeEditor(somethingWasSaved) {
this.props.closeFullScreenDialog(); this.props.closeFullScreenDialog();
this.props.handleEditorWasClosed(somethingWasSaved); this.props.handleEditorWasClosed(somethingWasSaved);
} }
...@@ -225,7 +226,7 @@ class Editor extends Component { ...@@ -225,7 +226,7 @@ class Editor extends Component {
</Toolbar> </Toolbar>
</AppBar> </AppBar>
<Paper className={classes.paper}> <Paper className={classes.paper}>
{this.props.open ? this.renderForm() : <div></div>} {this.renderForm()}
</Paper> </Paper>
</div> </div>
); );
...@@ -242,13 +243,6 @@ class Editor extends Component { ...@@ -242,13 +243,6 @@ class Editor extends Component {
// user request handlers // user request handlers
handleCloseNotificationRequest() {
this.setState({
// backup message to prevent graphic artefact
notification: { open: false, message: this.state.notification.message }
});
}
handleCloseAlertRequest() { handleCloseAlertRequest() {
this.setState({ this.setState({
alert: { open: false } alert: { open: false }
...@@ -258,51 +252,20 @@ class Editor extends Component { ...@@ -258,51 +252,20 @@ class Editor extends Component {
// Notifications and alerts from JS // Notifications and alerts from JS
notifyNoChangesDetected() { notifyNoChangesDetected() {
console.log("no changes detected") this.props.enqueueSnackbar("Aucun changement n'a été repéré.", { variant: "info" });
// this.setState({
// notification: {
// open: true,
// message: "Aucun changement n'a été repéré.",
// success: true,
// }
// });
} }
notifyFormHasErrors(errors) { notifyFormHasErrors(errors) {
console.log("errors in the form") this.props.enqueueSnackbar(`Le formulaire semble incohérent, merci de vérifier son contenu. ${errors}`, { variant: "error" });
// this.setState({
// notification: {
// open: true,
// message: "Le formulaire semble incohérent, merci de vérifier son contenu.",
// error: true,
// errors,
// handleClose: this.handleCloseNotificationRequest()
// }
// });
} }
notifyIsSaving() { notifyIsSaving() {
console.log("is saving") this.savingNotification = this.props.enqueueSnackbar("Enregistrement en cours", { variant: "info", persist: true, action: null });
// this.setState({
// notification: {
// open: true,
// message: "Enregistrement en cours",
// isLoading: true,
// duration: null
// }
// });
} }
notifySaveSuccessful(message) { notifySaveSuccessful(message) {
console.log("save success") this.props.closeSnackbar(this.savingNotification);
// this.setState({ this.props.enqueueSnackbar(message, { variant: "success", autoHideDuration: 5000});
// notification: {
// open: true,
// message,
// success: true,
// preventClickAway: false
// }
// });
} }
alertSaveFailed(error) { alertSaveFailed(error) {
...@@ -362,6 +325,9 @@ Editor.propTypes = { ...@@ -362,6 +325,9 @@ Editor.propTypes = {
openFullScreenDialog: PropTypes.func.isRequired, openFullScreenDialog: PropTypes.func.isRequired,
closeFullScreenDialog: PropTypes.func.isRequired, closeFullScreenDialog: PropTypes.func.isRequired,
saveData: PropTypes.func.isRequired, saveData: PropTypes.func.isRequired,
// Notifications related
enqueueSnackbar: PropTypes.func.isRequired,
closeSnackbar: PropTypes.func.isRequired,
}; };
Editor.defaultProps = { Editor.defaultProps = {
......
import React from "react";
import PropTypes from "prop-types";
import { withStyles } from "@material-ui/core/styles";
import Snackbar from "@material-ui/core/Snackbar";
import DoneIcon from "@material-ui/icons/Done";
import Loading from "../../other/Loading";
import IconButton from "@material-ui/core/IconButton";
import CloseIcon from "@material-ui/icons/Close";
const styles = theme => ({
close: {
width: theme.spacing.unit * 4,
height: theme.spacing.unit * 4,
},
error: {
color: "red",
display: "block"
}
});
class Notification extends React.Component {
handleClose = (event, reason) => {
if (reason === "clickaway" && this.props.preventClickAway) {
return;
}
this.props.handleClose();
};
renderMessage() {
const { classes } = this.props;
return (
<div>
<span id="message-id">{this.props.message}</span>
{this.props.errors.map((error, idx) => (<span key={idx} className={classes.error}>{error}</span>))}
</div>
);
}
render() {
const { classes } = this.props;
let actions = Array();
if (this.props.isLoading) {
actions.push(
<Loading size={20} />
);
} else if (this.props.success) {
actions.push(
<DoneIcon color='primary' />
);
} else if (this.props.error) {
actions.push(
<IconButton
key="close"
aria-label="Fermer"
color="inherit"
className={classes.close}
onClick={this.props.handleClose}
>
<CloseIcon />
</IconButton>
);
}
return (
<div>
<Snackbar
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
open={this.props.open}
autoHideDuration={this.props.duration}
onClose={this.handleClose}
ContentProps={{
"aria-describedby": "message-id",
}}
message={this.renderMessage()}
action={actions}
/>
</div>
);
}
}
Notification.defaultProps = {
open: false,
duration: 5000,
message: "",
preventClickAway: true,
success: false,
error: false,
errors: [],
isLoading: false,
// eslint-disable-next-line no-console
handleClose: () => console.error("cloossee notif")
};
Notification.propTypes = {
classes: PropTypes.object.isRequired,
open: PropTypes.bool.isRequired,
duration: PropTypes.number,
message: PropTypes.string.isRequired,
preventClickAway: PropTypes.bool.isRequired,
success: PropTypes.bool.isRequired,
error: PropTypes.bool.isRequired,
errors: PropTypes.arrayOf(PropTypes.string),
isLoading: PropTypes.bool.isRequired,
handleClose: PropTypes.func.isRequired,
};
export default withStyles(styles)(Notification);
...@@ -2,6 +2,8 @@ import React from "react"; ...@@ -2,6 +2,8 @@ import React from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import { Provider } from "react-redux"; import { Provider } from "react-redux";
import { Route } from "react-router-dom"; import { Route } from "react-router-dom";
import { SnackbarProvider } from "notistack"; // provider to easily handle notifications across the app
import Button from "@material-ui/core/Button";
import store from "./store/index"; import store from "./store/index";
import App from "./components/App"; import App from "./components/App";
...@@ -11,7 +13,21 @@ const MainReactEntry = () => ( ...@@ -11,7 +13,21 @@ const MainReactEntry = () => (
<Provider store={store}> <Provider store={store}>
{/* <React.StrictMode> */} {/* <React.StrictMode> */}
<ThemeProvider> <ThemeProvider>
<Route path="/:filter?" component={App} /> <SnackbarProvider
maxSnack={3}
anchorOrigin={{
vertical: "top",
horizontal: "right",
}}
action={[
<Button key={0} color="secondary" size="small">
{"Fermer"}
</Button>
]}
>
<Route path="/:filter?" component={App} />
</SnackbarProvider>
</ThemeProvider> </ThemeProvider>
{/* </React.StrictMode> */} {/* </React.StrictMode> */}
</Provider> </Provider>
......
import { combineReducers } from "redux"; import { combineReducers } from "redux";
import { fullScreenDialog } from "./fullScreenDialog"; import { fullScreenDialog } from "./fullScreenDialog";
import { notifications } from "./notifications";
import { saveMainMapStatus } from "./map"; import { saveMainMapStatus } from "./map";
import { saveSelectedUniversities, saveFilterConfig } from "./filter"; import { saveSelectedUniversities, saveFilterConfig } from "./filter";
import { saveAppTheme, saveAppColorPicker } from "./theme"; import { saveAppTheme, saveAppColorPicker } from "./theme";
...@@ -11,7 +10,6 @@ import { apiReducers } from "../api/buildApiActionsAndReducers"; ...@@ -11,7 +10,6 @@ import { apiReducers } from "../api/buildApiActionsAndReducers";
// Combine the reducers related to the app // Combine the reducers related to the app
const app = combineReducers({ const app = combineReducers({
fullScreenDialog, fullScreenDialog,
notifications,
mainMap: saveMainMapStatus, mainMap: saveMainMapStatus,
selectedUniversities: saveSelectedUniversities, selectedUniversities: saveSelectedUniversities,
filter: saveFilterConfig, filter: saveFilterConfig,
......
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