diff --git a/app/javascript/src/components/AlertsContainer/components/Alert.jsx b/app/javascript/src/components/AlertsContainer/components/Alert.jsx index 944e4560c..e8da50db0 100644 --- a/app/javascript/src/components/AlertsContainer/components/Alert.jsx +++ b/app/javascript/src/components/AlertsContainer/components/Alert.jsx @@ -74,13 +74,8 @@ class Alert extends Component { Alert.propTypes = { message: PropTypes.string.isRequired, type: PropTypes.string.isRequired, - timeout: PropTypes.number, - onClose: PropTypes.func -}; - -Alert.defaultProps = { - timeout: 5000, - onClose: undefined + timeout: PropTypes.number.isRequired, + onClose: PropTypes.func.isRequired }; export default Alert; \ No newline at end of file diff --git a/app/javascript/src/components/AlertsContainer/index.jsx b/app/javascript/src/components/AlertsContainer/index.jsx index b31c76c0d..274521423 100644 --- a/app/javascript/src/components/AlertsContainer/index.jsx +++ b/app/javascript/src/components/AlertsContainer/index.jsx @@ -1,8 +1,10 @@ import React, { Component } from "react"; +import { connect } from "react-redux"; import styled from "styled-components"; -import update from "immutability-helper"; import TransitionGroup from 'react-transition-group/TransitionGroup'; import CSSTransition from 'react-transition-group/CSSTransition'; +import PropTypes from "prop-types"; +import { clearAlert } from "../actions/AlertsActions"; import Alert from "./components/Alert"; const Wrapper = styled.div` @@ -15,36 +17,7 @@ class AlertsContainer extends Component { constructor(props) { super(props); - this.state = { - alerts: [] - }; - - this.add = this.add.bind(this); - this.clearAll = this.clearAll.bind(this); - this.clear = this.clear.bind(this); this.renderAlert = this.renderAlert.bind(this); - - // Bind self to global namespace - window.alerts = this; - } - - add(message, type, timeout) { - this.setState( - update( - this.state, - { alerts: { $push: [{ message, type, timeout }] } } - ) - ); - } - - clearAll() { - this.setState({ alerts: [] }); - } - - clear(alert) { - const index = this.state.alerts.indexOf(alert); - const alerts = update(this.state.alerts, { $splice: [[index, 1]] }); - this.setState({ alerts }); } renderAlert(alert) { @@ -52,7 +25,7 @@ class AlertsContainer extends Component { this.clear(alert)} + onClose={() => this.props.onAlertClose(alert.id)} /> ); } @@ -61,11 +34,11 @@ class AlertsContainer extends Component { return ( - {this.state.alerts.map((alert, index) => - + - {this.renderAlert(alert, index)} + {this.renderAlert(alert)} )} @@ -74,4 +47,25 @@ class AlertsContainer extends Component { } } -export default AlertsContainer; \ No newline at end of file +AlertsContainer.propTypes = { + alerts: PropTypes.arrayOf( + PropTypes.shape({ + message: PropTypes.string.isRequired, + type: PropTypes.string.isRequired, + id: PropTypes.string.isRequired, + timeout: PropTypes.number, + onClose: PropTypes.func + }).isRequired + ).isRequired, + onAlertClose: PropTypes.func.isRequired +} + +const mapStateToProps = ({ alerts }) => ({ alerts }); + +const mapDispatchToProps = dispatch => ({ + onAlertClose(id) { + dispatch(clearAlert(id)); + } +}); + +export default connect(mapStateToProps, mapDispatchToProps)(AlertsContainer); \ No newline at end of file diff --git a/app/javascript/src/components/actions/AlertsActions.js b/app/javascript/src/components/actions/AlertsActions.js new file mode 100644 index 000000000..f6ff5b175 --- /dev/null +++ b/app/javascript/src/components/actions/AlertsActions.js @@ -0,0 +1,29 @@ +import shortid from "shortid"; +import { + ADD_ALERT, + CLEAR_ALERT, + CLEAR_ALL_ALERTS +} from "../../config/action_types"; + +export function addAlert(message, + type, + id = shortid.generate(), + timeout = 5000) { + return { + payload: { + message, + type, + id, + timeout + }, + type: ADD_ALERT + }; +} + +export function clearAlert(id) { + return { payload: id, type: CLEAR_ALERT } +} + +export function clearAllAlerts() { + return { type: CLEAR_ALL_ALERTS }; +} \ No newline at end of file diff --git a/app/javascript/src/components/reducers/AlertsReducers.js b/app/javascript/src/components/reducers/AlertsReducers.js new file mode 100644 index 000000000..098459ca6 --- /dev/null +++ b/app/javascript/src/components/reducers/AlertsReducers.js @@ -0,0 +1,31 @@ +import { + ADD_ALERT, + CLEAR_ALERT, + CLEAR_ALL_ALERTS +} from "../../config/action_types"; + +export const alerts = ( + state = [], + action +) => { + switch(action.type) { + case ADD_ALERT: + return [ + ...state, + { + message: action.payload.message, + type: action.payload.type, + id: action.payload.id, + timeout: action.payload.timeout + } + ]; + case CLEAR_ALERT: + return state.filter((alert) => ( + alert.id !== action.payload + )); + case CLEAR_ALL_ALERTS: + return []; + default: + return state; + } +}; \ No newline at end of file diff --git a/app/javascript/src/config/action_types.js b/app/javascript/src/config/action_types.js index b2d3a7443..732a81db4 100644 --- a/app/javascript/src/config/action_types.js +++ b/app/javascript/src/config/action_types.js @@ -35,3 +35,8 @@ export const UPDATE_TEAM_DESCRIPTION_MODAL = "UPDATE_TEAM_DESCRIPTION_MODAL"; // spinner export const SPINNER_ON = "SPINNER_ON"; export const SPINNER_OFF = "SPINNER_OFF"; + +// alerts +export const ADD_ALERT = "ADD_ALERT"; +export const CLEAR_ALERT = "CLEAR_ALERT"; +export const CLEAR_ALL_ALERTS = "CLEAR_ALL_ALERTS"; \ No newline at end of file diff --git a/app/javascript/src/config/reducers.js b/app/javascript/src/config/reducers.js index 2f00797ea..5561d4cb3 100644 --- a/app/javascript/src/config/reducers.js +++ b/app/javascript/src/config/reducers.js @@ -6,11 +6,13 @@ import { } from "../components/reducers/TeamReducers"; import { globalActivities } from "../components/reducers/ActivitiesReducers"; import { currentUser } from "../components/reducers/UsersReducer"; +import { alerts } from "../components/reducers/AlertsReducers"; export default combineReducers({ current_team: setCurrentTeam, all_teams: getListOfTeams, global_activities: globalActivities, current_user: currentUser, - showLeaveTeamModal + showLeaveTeamModal, + alerts }); diff --git a/package.json b/package.json index ef8f1c710..f87213bdd 100644 --- a/package.json +++ b/package.json @@ -83,6 +83,7 @@ "redux-thunk": "^2.2.0", "resolve-url-loader": "^2.1.0", "sass-loader": "^6.0.6", + "shortid": "^2.2.8", "style-loader": "^0.18.2", "styled-components": "^2.1.1", "webpack": "^3.2.0", diff --git a/yarn.lock b/yarn.lock index 959663dcb..8a69711b4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5313,6 +5313,10 @@ shelljs@^0.7.5: interpret "^1.0.0" rechoir "^0.6.2" +shortid@^2.2.8: + version "2.2.8" + resolved "https://registry.yarnpkg.com/shortid/-/shortid-2.2.8.tgz#033b117d6a2e975804f6f0969dbe7d3d0b355131" + signal-exit@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"