feat(Flash): Get flash messages on load

This commit is contained in:
Berkeley Martinez
2018-01-12 11:09:09 -08:00
parent 387eafbf33
commit 2bb27e8dc6
7 changed files with 73 additions and 48 deletions

View File

@ -4,8 +4,8 @@ import { CloseButton } from 'react-bootstrap';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import ns from './ns.json'; import ns from './ns.json';
import { alertTypes } from './redux/utils.js';
import { import {
alertTypes,
latestMessageSelector, latestMessageSelector,
clickOnClose clickOnClose
} from './redux'; } from './redux';

View File

@ -0,0 +1,17 @@
import { Observable } from 'rx';
import { ofType } from 'redux-epic';
import {
fetchMessagesComplete,
fetchMessagesError
} from './';
import { types as app } from '../../redux';
import { getJSON$ } from '../../../utils/ajax-stream.js';
export default function getMessagesEpic(actions) {
return actions::ofType(app.appMounted)
.flatMap(() => getJSON$('/api/users/get-messages')
.map(fetchMessagesComplete)
.catch(err => Observable.of(fetchMessagesError(err)))
);
}

View File

@ -2,45 +2,25 @@ import _ from 'lodash/fp';
import { import {
createTypes, createTypes,
createAction, createAction,
createAsyncTypes,
composeReducers, composeReducers,
handleActions handleActions
} from 'berkeleys-redux-utils'; } from 'berkeleys-redux-utils';
import * as utils from './utils.js';
import getMessagesEpic from './get-messages-epic.js';
import ns from '../ns.json'; import ns from '../ns.json';
export const alertTypes = _.keyBy(_.identity)([ export const epics = [getMessagesEpic];
'success',
'info',
'warning',
'danger'
]);
export const normalizeAlertType = alertType => alertTypes[alertType] || 'info';
export const getFlashAction = _.flow(
_.property('meta'),
_.property('flash')
);
export const isFlashAction = _.flow(
getFlashAction,
Boolean
);
export const types = createTypes([ export const types = createTypes([
'clickOnClose', 'clickOnClose',
'messagesFoundOnBoot' 'messagesFoundOnBoot',
createAsyncTypes('fetchMessages')
], ns); ], ns);
export const clickOnClose = createAction(types.clickOnClose, _.noop); export const clickOnClose = createAction(types.clickOnClose, _.noop);
export const messagesFoundOnBoot = createAction(types.messagesFoundOnBoot); export const fetchMessagesComplete = createAction(types.fetchMessages.complete);
export const fetchMessagesError = createAction(types.fetchMessages.error);
export const expressToStack = _.flow(
_.toPairs,
_.flatMap(([ type, messages ]) => messages.map(({ msg }) => ({
message: msg,
alertType: normalizeAlertType(type)
})))
);
const defaultState = []; const defaultState = [];
@ -57,20 +37,20 @@ export default composeReducers(
handleActions( handleActions(
() => ({ () => ({
[types.clickOnClose]: _.tail, [types.clickOnClose]: _.tail,
[types.messagesFoundOnBoot]: (state, { payload }) => [ [types.fetchMessages.complete]: (state, { payload }) => [
...state, ...state,
...expressToStack(payload) ...utils.expressToStack(payload)
] ]
}), }),
defaultState, defaultState,
), ),
function metaReducer(state = defaultState, action) { function metaReducer(state = defaultState, action) {
if (isFlashAction(action)) { if (utils.isFlashAction(action)) {
const { payload: { alertType, message } } = getFlashAction(action); const { payload: { alertType, message } } = utils.getFlashAction(action);
return [ return [
...state, ...state,
{ {
alertType: normalizeAlertType(alertType), alertType: utils.normalizeAlertType(alertType),
message: _.escape(message) message: _.escape(message)
} }
]; ];

View File

@ -0,0 +1,29 @@
import _ from 'lodash/fp';
export const alertTypes = _.keyBy(_.identity)([
'success',
'info',
'warning',
'danger'
]);
export const normalizeAlertType = alertType => alertTypes[alertType] || 'info';
export const getFlashAction = _.flow(
_.property('meta'),
_.property('flash')
);
export const isFlashAction = _.flow(
getFlashAction,
Boolean
);
export const expressToStack = _.flow(
_.toPairs,
_.flatMap(([ type, messages ]) => messages.map(({ msg }) => ({
message: msg,
alertType: normalizeAlertType(type)
})))
);

View File

@ -1,15 +1,17 @@
import { epics as app } from './redux'; import { epics as app } from './redux';
import { epics as challenge } from './routes/Challenges/redux'; import { epics as challenge } from './routes/Challenges/redux';
import { epics as settings } from './routes/Settings/redux'; import { epics as flash } from './Flash/redux';
import { epics as nav } from './Nav/redux';
import { epics as map } from './Map/redux'; import { epics as map } from './Map/redux';
import { epics as nav } from './Nav/redux';
import { epics as panes } from './Panes/redux'; import { epics as panes } from './Panes/redux';
import { epics as settings } from './routes/Settings/redux';
export default [ export default [
...app, ...app,
...challenge, ...challenge,
...settings, ...flash,
...nav,
...map, ...map,
...panes ...nav,
...panes,
...settings
]; ];

View File

@ -776,9 +776,7 @@ module.exports = function(User) {
}); });
}; };
User.getMessages = function getMessages(messages) { User.getMessages = messages => Promise.resolve(messages);
return Promise.resolve(messages);
};
User.remoteMethod('getMessages', { User.remoteMethod('getMessages', {
http: { http: {
@ -788,11 +786,7 @@ module.exports = function(User) {
accepts: { accepts: {
arg: 'messages', arg: 'messages',
type: 'object', type: 'object',
http: ctx => { http: ctx => ctx.req.flash()
const messages = ctx.req.flash();
console.log('messages: ', messages);
return messages;
}
}, },
returns: [ returns: [
{ {

View File

@ -9,7 +9,10 @@ export default function sessionsMiddleware() {
return session({ return session({
// 900 day session cookie // 900 day session cookie
cookie: { maxAge: 900 * 24 * 60 * 60 * 1000 }, cookie: { maxAge: 900 * 24 * 60 * 60 * 1000 },
resave: true, // resave forces session to be resaved
// regardless of whether it was modified
// this causes race conditions during parallel req
resave: false,
saveUninitialized: true, saveUninitialized: true,
secret: sessionSecret, secret: sessionSecret,
store: new MongoStore({ url }) store: new MongoStore({ url })