feat(Flash): Get flash messages on load
This commit is contained in:
@ -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';
|
||||||
|
17
common/app/Flash/redux/get-messages-epic.js
Normal file
17
common/app/Flash/redux/get-messages-epic.js
Normal 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)))
|
||||||
|
);
|
||||||
|
}
|
@ -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)
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
29
common/app/Flash/redux/utils.js
Normal file
29
common/app/Flash/redux/utils.js
Normal 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)
|
||||||
|
})))
|
||||||
|
);
|
||||||
|
|
@ -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
|
||||||
];
|
];
|
||||||
|
@ -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: [
|
||||||
{
|
{
|
||||||
|
@ -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 })
|
||||||
|
Reference in New Issue
Block a user