From 9a2dfca0fc7a07f949ce89f8c1217a0db15bdab2 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Tue, 19 Jul 2016 16:36:34 -0700 Subject: [PATCH] Feature(settings): complete settings page logic --- common/app/redux/actions.js | 24 ++- common/app/redux/entities-reducer.js | 43 +++++- common/app/redux/types.js | 2 + .../settings/components/Email-Setting.jsx | 22 +-- .../settings/components/Language-Settings.jsx | 87 +++++++++-- .../routes/settings/components/Settings.jsx | 52 ++++++- common/app/routes/settings/index.js | 6 +- common/app/routes/settings/redux/actions.js | 9 +- common/app/routes/settings/redux/selectors.js | 0 .../routes/settings/redux/update-user-saga.js | 77 +++++++++- .../routes/update-email/Update-Email.jsx | 144 ++++++++++++++++++ .../settings/routes/update-email/index.js | 6 + common/app/utils/combine-sagas.js | 9 ++ common/models/user.js | 98 ++++-------- server/boot/react.js | 3 +- server/boot/settings.js | 44 ++++++ 16 files changed, 514 insertions(+), 112 deletions(-) create mode 100644 common/app/routes/settings/redux/selectors.js create mode 100644 common/app/routes/settings/routes/update-email/Update-Email.jsx create mode 100644 common/app/routes/settings/routes/update-email/index.js create mode 100644 common/app/utils/combine-sagas.js diff --git a/common/app/redux/actions.js b/common/app/redux/actions.js index 435d3e93cd..05abf5cdee 100644 --- a/common/app/redux/actions.js +++ b/common/app/redux/actions.js @@ -9,7 +9,7 @@ export const updateTitle = createAction(types.updateTitle); // used in combination with fetch-user-saga export const fetchUser = createAction(types.fetchUser); -// setUser( +// addUser( // entities: { [userId]: User } // ) => Action export const addUser = createAction( @@ -25,11 +25,21 @@ export const updateUserPoints = createAction( types.updateUserPoints, (username, points) => ({ username, points }) ); -// updateUserPoints(username: String, flag: String) => Action +// updateUserFlag(username: String, flag: String) => Action export const updateUserFlag = createAction( types.updateUserFlag, (username, flag) => ({ username, flag }) ); +// updateUserEmail(username: String, email: String) => Action +export const updateUserEmail = createAction( + types.updateUserFlag, + (username, email) => ({ username, email }) +); +// updateUserLang(username: String, lang: String) => Action +export const updateUserLang = createAction( + types.updateUserLang, + (username, lang) => ({ username, lang }) +); // updateCompletedChallenges(username: String) => Action export const updateCompletedChallenges = createAction( types.updateCompletedChallenges @@ -55,6 +65,16 @@ export const createErrorObservable = error => Observable.just({ type: types.handleError, error }); +// doActionOnError( +// actionCreator: (() => Action|Null) +// ) => (error: Error) => Observable[Action] +export const doActionOnError = actionCreator => error => Observable.of( + { + type: types.handleError, + error + }, + actionCreator() +); // drawers diff --git a/common/app/redux/entities-reducer.js b/common/app/redux/entities-reducer.js index 250ae395cc..ad271471c6 100644 --- a/common/app/redux/entities-reducer.js +++ b/common/app/redux/entities-reducer.js @@ -8,8 +8,13 @@ const initialState = { user: {} }; +// future refactor(berks): Several of the actions here are just updating props +// on the main user. These can be refactors into one response for all actions export default function entities(state = initialState, action) { - const { type, payload: { username, points, flag } = {} } = action; + const { + type, + payload: { email, username, points, flag, languageTag } = {} + } = action; if (type === updateCompletedChallenges) { const username = action.payload; const completedChallengeMap = state.user[username].challengeMap || {}; @@ -26,6 +31,12 @@ export default function entities(state = initialState, action) { }, {}) }; } + if (action.meta && action.meta.entities) { + return { + ...state, + ...action.meta.entities + }; + } if (type === updateUserPoints) { return { ...state, @@ -38,12 +49,6 @@ export default function entities(state = initialState, action) { } }; } - if (action.meta && action.meta.entities) { - return { - ...state, - ...action.meta.entities - }; - } if (action.type === types.updateUserFlag) { return { ...state, @@ -56,5 +61,29 @@ export default function entities(state = initialState, action) { } }; } + if (action.type === types.updateUserEmail) { + return { + ...state, + user: { + ...state.user, + [username]: { + ...state.user[username], + email + } + } + }; + } + if (action.type === types.updateUserLang) { + return { + ...state, + user: { + ...state.user, + [username]: { + ...state.user[username], + languageTag + } + } + }; + } return state; } diff --git a/common/app/redux/types.js b/common/app/redux/types.js index 3ab9197c8b..2a6025d1b0 100644 --- a/common/app/redux/types.js +++ b/common/app/redux/types.js @@ -8,6 +8,8 @@ export default createTypes([ 'updateThisUser', 'updateUserPoints', 'updateUserFlag', + 'updateUserEmail', + 'updateUserLang', 'updateCompletedChallenges', 'showSignIn', diff --git a/common/app/routes/settings/components/Email-Setting.jsx b/common/app/routes/settings/components/Email-Setting.jsx index a9332d56e4..0a6ad36012 100644 --- a/common/app/routes/settings/components/Email-Setting.jsx +++ b/common/app/routes/settings/components/Email-Setting.jsx @@ -1,20 +1,22 @@ import React, { PropTypes } from 'react'; +import { Link } from 'react-router'; import { Button, Row, Col } from 'react-bootstrap'; import FA from 'react-fontawesome'; import classnames from 'classnames'; export function UpdateEmailButton() { return ( - + + + ); } diff --git a/common/app/routes/settings/components/Language-Settings.jsx b/common/app/routes/settings/components/Language-Settings.jsx index f8b11aaec1..c8e3b7306c 100644 --- a/common/app/routes/settings/components/Language-Settings.jsx +++ b/common/app/routes/settings/components/Language-Settings.jsx @@ -1,7 +1,27 @@ import React, { PropTypes } from 'react'; +import { createSelector } from 'reselect'; +import { reduxForm } from 'redux-form'; import { FormControl } from 'react-bootstrap'; + +import { updateMyLang } from '../redux/actions'; +import { userSelector } from '../../../redux/selectors'; import langs from '../../../../utils/supported-languages'; +const mapStateToProps = createSelector( + userSelector, + ({ user: { languageTag } }) => ({ + // send null to prevent redux-form from initialize empty + initialValues: languageTag ? { lang: languageTag } : null + }) +); +const actions = { updateMyLang }; +const fields = [ 'lang' ]; +const validator = values => { + if (!langs[values.lang]) { + return { lang: `${values.lang} is unsupported` }; + } + return {}; +}; const options = [(