diff --git a/common/app/Flash/Flash.jsx b/common/app/Flash/Flash.jsx index 5e9834296b..d4334e2826 100644 --- a/common/app/Flash/Flash.jsx +++ b/common/app/Flash/Flash.jsx @@ -1,15 +1,28 @@ import React from 'react'; +import PropTypes from 'prop-types'; import { CloseButton } from 'react-bootstrap'; +import { connect } from 'react-redux'; +import { createSelector } from 'reselect'; import ns from './ns.json'; +import { alertTypes, latestMessageSelector } from './redux'; -const propTypes = {}; -export default function Flash() { +const propTypes = { + alertType: PropTypes.oneOf(Object.keys(alertTypes)), + message: PropTypes.string +}; +const mapStateToProps = latestMessageSelector; +const mapDispatchToProps = null; + +export function Flash({ alertType, message }) { + if (!message) { + return null; + } return ( -
+
- Content + { message }
@@ -19,3 +32,8 @@ export default function Flash() { Flash.displayName = 'Flash'; Flash.propTypes = propTypes; + +export default connect( + mapStateToProps, + mapDispatchToProps +)(Flash); diff --git a/common/app/Flash/redux/index.js b/common/app/Flash/redux/index.js index 8b13789179..e79dd0fd8c 100644 --- a/common/app/Flash/redux/index.js +++ b/common/app/Flash/redux/index.js @@ -1 +1,54 @@ +import { composeReducers } from 'berkeleys-redux-utils'; +import _ from 'lodash/fp'; +import ns from '../ns.json'; + +export const alertTypes = _.keyBy(_.identity)([ + 'success', + 'info', + 'warning', + 'danger' +]); + +export const getFlashAction = _.flow( + _.property('meta'), + _.property('flash') +); + +export const isFlashAction = _.flow( + getFlashAction, + Boolean +); + +const defaultState = { + stack: [{ alertType: 'danger', message: 'foo bar' }] +}; + +const getNS = _.property(ns); + +export const latestMessageSelector = _.flow( + getNS, + _.property('stack'), + _.head, + _.defaultTo(_.stubObject) +); + +export default composeReducers( + ns, + function metaReducer(state = defaultState, action) { + if (isFlashAction(action)) { + const { payload: { alertType, message } } = getFlashAction(action); + return { + ...state, + stack: [ + ...state.stack, + { + alertType: alertTypes[alertType] || 'info', + message: _.escape(message) + } + ] + }; + } + return state; + } +); diff --git a/common/app/reducer.js b/common/app/reducer.js index e8915b6e91..7c5d882ce7 100644 --- a/common/app/reducer.js +++ b/common/app/reducer.js @@ -8,6 +8,7 @@ import nav from './Nav/redux'; import routes from './routes/redux'; import toasts from './Toasts/redux'; import files from './files'; +import flash from './Flash/redux'; // not ideal but should go away once we move to react-redux-form import { projectNormalizer } from './routes/Challenges/redux'; @@ -22,5 +23,6 @@ export default combineReducers( routes, toasts, files, + flash, _formReducer );