Some updates to the redux structure

1. split `auth.js` up so it's 1 reducer per file
2. consolidate the conference schedule reducers
3. make conference reducer names more descriptive
4. add error handling for `body === 'undefined'`

Thanks @rschamp for all the help!
This commit is contained in:
Matthew Taylor 2016-05-19 16:55:25 -04:00
parent bca90bd835
commit a860b27941
11 changed files with 109 additions and 131 deletions

View file

@ -2,7 +2,7 @@ var connect = require('react-redux').connect;
var omit = require('lodash.omit');
var React = require('react');
var actions = require('../../redux/auth.js');
var sessionActions = require('../../redux/session.js');
var Modal = require('../modal/modal.jsx');
var Registration = require('../registration/registration.jsx');
@ -48,7 +48,7 @@ var Intro = React.createClass({
this.setState({'registrationOpen': false});
},
completeRegistration: function () {
this.props.dispatch(actions.refreshSession());
this.props.dispatch(sessionActions.refreshSession());
this.closeRegistration();
},
render: function () {

View file

@ -5,7 +5,7 @@ var ReactIntl = require('react-intl');
var FormattedMessage = ReactIntl.FormattedMessage;
var injectIntl = ReactIntl.injectIntl;
var actions = require('../../../redux/auth.js');
var sessionActions = require('../../../redux/session.js');
var Api = require('../../../mixins/api.jsx');
var Avatar = require('../../avatar/avatar.jsx');
@ -132,7 +132,7 @@ var Navigation = React.createClass({
this.showCanceledDeletion();
}
}.bind(this));
this.props.dispatch(actions.refreshSession());
this.props.dispatch(sessionActions.refreshSession());
}
}
// JS error already logged by api mixin
@ -149,7 +149,7 @@ var Navigation = React.createClass({
}, function (err) {
if (err) log.error(err);
this.closeLogin();
this.props.dispatch(actions.refreshSession());
this.props.dispatch(sessionActions.refreshSession());
}.bind(this));
},
handleAccountNavClick: function (e) {
@ -169,7 +169,7 @@ var Navigation = React.createClass({
this.setState({'registrationOpen': false});
},
completeRegistration: function () {
this.props.dispatch(actions.refreshSession());
this.props.dispatch(sessionActions.refreshSession());
this.closeRegistration();
},
render: function () {

View file

@ -6,7 +6,7 @@ var ReactDOM = require('react-dom');
var StoreProvider = require('react-redux').Provider;
var IntlProvider = require('./intl.jsx').IntlProvider;
var authActions = require('../redux/auth.js');
var sessionActions = require('../redux/session.js');
var reducer = require('../redux/reducer.js');
require('../main.scss');
@ -43,7 +43,7 @@ var render = function (jsx, element) {
);
// Get initial session
store.dispatch(authActions.refreshSession());
store.dispatch(sessionActions.refreshSession());
};
module.exports = render;

View file

@ -74,6 +74,9 @@ module.exports.getDetails = function (id) {
dispatch(module.exports.setDetailsError('Not Found'));
}
return;
} else {
dispatch(module.exports.setDetailsError('An unexpected error occurred'));
return;
}
});
};

View file

@ -2,30 +2,17 @@ var keyMirror = require('keymirror');
var api = require('../mixins/api.jsx').api;
var Types = keyMirror({
SET_DAY: null,
SET_SCHEDULE: null,
SET_SCHEDULE_FETCHING: null,
SET_DAY_ERROR: null,
SET_SCHEDULE_ERROR: null
});
module.exports.dayReducer = function (state, action) {
if (typeof state === 'undefined') {
state = '';
}
switch (action.type) {
case Types.SET_DAY:
return action.day;
case Types.SET_DAY_ERROR:
return state;
default:
return state;
}
};
module.exports.scheduleReducer = function (state, action) {
if (typeof state === 'undefined') {
state = [];
state = {
chunks: [],
day: ''
};
}
switch (action.type) {
case Types.SET_SCHEDULE:
@ -39,20 +26,6 @@ module.exports.scheduleReducer = function (state, action) {
}
};
module.exports.setDayError = function (error) {
return {
type: Types.SET_DAY_ERROR,
error: error
};
};
module.exports.setDay = function (day) {
return {
type: Types.SET_DAY,
day: day
};
};
module.exports.setSchedule = function (schedule) {
return {
type: Types.SET_SCHEDULE,
@ -93,7 +66,6 @@ module.exports.getDaySchedule = function (day) {
uri: '/conference/schedule/' + day
}, function (err, body) {
if (err) {
dispatch(module.exports.setDayError(err));
dispatch(module.exports.setScheduleError(err));
return;
}
@ -150,8 +122,13 @@ module.exports.getDaySchedule = function (day) {
items: scheduleByChunk.chunks[scheduleByChunk.info[i].name]
});
}
dispatch(module.exports.setDay(day));
dispatch(module.exports.setSchedule(schedule));
dispatch(module.exports.setSchedule({
chunks: schedule,
day: day
}));
return;
} else {
dispatch(module.exports.setScheduleError('An unexpected error occurred'));
return;
}
});

View file

@ -1,15 +1,15 @@
var combineReducers = require('redux').combineReducers;
var authReducers = require('./auth.js');
var conferenceScheduleReducers = require('./conference-schedule.js');
var conferenceDetailsReducers = require('./conference-details.js');
var scheduleReducer = require('./conference-schedule.js').scheduleReducer;
var detailsReducer = require('./conference-details.js').detailsReducer;
var sessionReducer = require('./session.js').sessionReducer;
var tokenReducer = require('./token.js').tokenReducer;
var appReducer = combineReducers({
session: authReducers.sessionReducer,
token: authReducers.tokenReducer,
day: conferenceScheduleReducers.dayReducer,
schedule: conferenceScheduleReducers.scheduleReducer,
details: conferenceDetailsReducers.detailsReducer
session: sessionReducer,
token: tokenReducer,
conferenceSchedule: scheduleReducer,
conferenceDetails: detailsReducer
});
module.exports = appReducer;

58
src/redux/session.js Normal file
View file

@ -0,0 +1,58 @@
var keyMirror = require('keymirror');
var api = require('../mixins/api.jsx').api;
var Types = keyMirror({
SET_SESSION: null,
SET_SESSION_ERROR: null
});
module.exports.sessionReducer = function (state, action) {
// Reducer for handling changes to session state
if (typeof state === 'undefined') {
state = {};
}
switch (action.type) {
case Types.SET_SESSION:
return action.session;
case Types.SET_SESSION_ERROR:
// TODO: do something with action.error
return state;
default:
return state;
}
};
module.exports.setSessionError = function (error) {
return {
type: Types.SET_SESSION_ERROR,
error: error
};
};
module.exports.setSession = function (session) {
return {
type: Types.SET_SESSION,
session: session
};
};
module.exports.refreshSession = function () {
return function (dispatch) {
api({
host: '',
uri: '/session/'
}, function (err, body) {
if (err) return dispatch(module.exports.setSessionError(err));
if (typeof body !== 'undefined') {
if (body.banned) {
return window.location = body.url;
} else {
dispatch(module.exports.getToken());
dispatch(module.exports.setSession(body));
return;
}
}
});
};
};

View file

@ -1,31 +1,12 @@
var keyMirror = require('keymirror');
var api = require('../mixins/api.jsx').api;
var jar = require('../lib/jar.js');
var Types = keyMirror({
SET_SESSION: null,
SET_SESSION_ERROR: null,
SET_TOKEN: null,
SET_TOKEN_ERROR: null,
USE_TOKEN: null
});
module.exports.sessionReducer = function (state, action) {
// Reducer for handling changes to session state
if (typeof state === 'undefined') {
state = {};
}
switch (action.type) {
case Types.SET_SESSION:
return action.session;
case Types.SET_SESSION_ERROR:
// TODO: do something with action.error
return state;
default:
return state;
}
};
module.exports.tokenReducer = function (state, action) {
// Reducer for updating the api token
if (typeof state === 'undefined') {
@ -42,41 +23,6 @@ module.exports.tokenReducer = function (state, action) {
}
};
module.exports.setSessionError = function (error) {
return {
type: Types.SET_SESSION_ERROR,
error: error
};
};
module.exports.setSession = function (session) {
return {
type: Types.SET_SESSION,
session: session
};
};
module.exports.refreshSession = function () {
return function (dispatch) {
api({
host: '',
uri: '/session/'
}, function (err, body) {
if (err) return dispatch(module.exports.setSessionError(err));
if (typeof body !== 'undefined') {
if (body.banned) {
return window.location = body.url;
} else {
dispatch(module.exports.getToken());
dispatch(module.exports.setSession(body));
return;
}
}
});
};
};
module.exports.getToken = function () {
return function (dispatch) {
jar.get('scratchsessionsid', function (err, value) {

View file

@ -13,7 +13,7 @@ var ConferenceDetails = React.createClass({
type: 'ConferenceDetails',
propTypes: {
detailsId: React.PropTypes.number,
details: React.PropTypes.object
conferenceDetails: React.PropTypes.object
},
componentDidMount: function () {
var pathname = window.location.pathname.toLowerCase();
@ -26,13 +26,13 @@ var ConferenceDetails = React.createClass({
},
render: function () {
var backUri = '/conference/schedule';
if (!this.props.details.error && !this.props.details.fetching) {
backUri = backUri + '#' + this.props.details.Day;
if (!this.props.conferenceDetails.error && !this.props.conferenceDetails.fetching) {
backUri = backUri + '#' + this.props.conferenceDetails.Day;
}
var classes = classNames({
'inner': true,
'details': true,
'fetching': this.props.details.fetching
'fetching': this.props.conferenceDetails.fetching
});
return (
<div className={classes}>
@ -41,33 +41,33 @@ var ConferenceDetails = React.createClass({
&larr; Back to Full Schedule
</a>
</div>
{this.props.details.error ? [
{this.props.conferenceDetails.error ? [
<h2>Agenda Item Not Found</h2>
] : [
<h2>{this.props.details.Title}</h2>,
<h2>{this.props.conferenceDetails.Title}</h2>,
<ul className="logistics">
<li>
<img src="/svgs/conference/schedule/presenter-icon.svg" alt="presenter icon" />
{this.props.details.Presenter}
{this.props.conferenceDetails.Presenter}
</li>
<li>
<img src="/svgs/conference/schedule/time-icon.svg" alt="time icon" />
{this.props.details.Start} &ndash; {this.props.details.End}
{this.props.conferenceDetails.Start} &ndash; {this.props.conferenceDetails.End}
</li>
<li>
<img src="/svgs/conference/schedule/event-icon.svg" alt="event icon" />
{this.props.details.Type}
{this.props.conferenceDetails.Type}
</li>
<li>
<img src="/svgs/conference/schedule/location-icon.svg" alt="location icon" />
{this.props.details.Location}
{this.props.conferenceDetails.Location}
</li>
</ul>,
<div className="description">
{this.props.details.Description}
{this.props.conferenceDetails.Description}
</div>,
<div className="back">
{this.props.details.fetching ? [] : [
{this.props.conferenceDetails.fetching ? [] : [
<a href={backUri}>
&larr; Back to Full Schedule
</a>
@ -81,9 +81,7 @@ var ConferenceDetails = React.createClass({
var mapStateToProps = function (state) {
return {
details: state.details,
fetching: state.fetching,
error: state.error
conferenceDetails: state.conferenceDetails
};
};

View file

@ -15,8 +15,7 @@ require('./schedule.scss');
var ConferenceSchedule = React.createClass({
type: 'ConferenceSchedule',
propTypes: {
day: React.PropTypes.string,
schedule: React.PropTypes.array
conferenceSchedule: React.PropTypes.object
},
componentDidMount: function () {
var day = window.location.hash.substr(1) || 'thursday';
@ -71,14 +70,14 @@ var ConferenceSchedule = React.createClass({
render: function () {
var tabClasses = {
'thursday': classNames({
'selected': (this.props.day === 'thursday')
'selected': (this.props.conferenceSchedule.day === 'thursday')
}),
'friday': classNames({
'selected': (this.props.day === 'friday')
'selected': (this.props.conferenceSchedule.day === 'friday')
}),
'saturday': classNames({
'last': true,
'selected': (this.props.day === 'saturday')
'selected': (this.props.conferenceSchedule.day === 'saturday')
})
};
return (
@ -106,7 +105,7 @@ var ConferenceSchedule = React.createClass({
</li>
</SubNavigation>
<div className="inner">
{this.props.schedule.map(function (chunk) {
{this.props.conferenceSchedule.chunks.map(function (chunk) {
return ([
<h2 key={chunk.info.name} className="breaking-title">
<span>{chunk.info.name} {chunk.info.time}</span>
@ -122,10 +121,7 @@ var ConferenceSchedule = React.createClass({
var mapStateToProps = function (state) {
return {
day: state.day,
schedule: state.schedule,
fetching: state.fetching,
error: state.error
conferenceSchedule: state.conferenceSchedule
};
};

View file

@ -4,7 +4,7 @@ var omit = require('lodash.omit');
var React = require('react');
var render = require('../../lib/render.jsx');
var authActions = require('../../redux/actions.js');
var sessionActions = require('../../redux/session.js');
var Api = require('../../mixins/api.jsx');
@ -167,7 +167,7 @@ var Splash = injectIntl(React.createClass({
useCsrf: true,
json: {cue: cue, value: false}
}, function (err) {
if (!err) this.props.dispatch(authActions.refreshSession());
if (!err) this.props.dispatch(sessionActions.refreshSession());
});
},
shouldShowWelcome: function () {