From e5efcbb6c6f648eb838886dd19c7b6e2de7eb2f7 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Wed, 20 Jul 2016 15:06:44 -0700 Subject: [PATCH] Feature(settings): Update url/challenge lang onChange --- common/app/App.jsx | 31 ++++++++++------ common/app/redux/actions.js | 1 + common/app/redux/reducer.js | 4 +++ common/app/redux/types.js | 1 + .../settings/components/Language-Settings.jsx | 10 +++--- .../routes/settings/redux/update-user-saga.js | 36 ++++++++++++------- 6 files changed, 54 insertions(+), 29 deletions(-) diff --git a/common/app/App.jsx b/common/app/App.jsx index f8939b5654..36983813e2 100644 --- a/common/app/App.jsx +++ b/common/app/App.jsx @@ -9,7 +9,8 @@ import { initWindowHeight, updateNavHeight, toggleMapDrawer, - toggleMainChat + toggleMainChat, + updateAppLang } from './redux/actions'; import { submitChallenge } from './routes/challenges/redux/actions'; @@ -18,6 +19,16 @@ import Nav from './components/Nav'; import Toasts from './toasts/Toasts.jsx'; import { userSelector } from './redux/selectors'; +const bindableActions = { + initWindowHeight, + updateNavHeight, + fetchUser, + submitChallenge, + toggleMapDrawer, + toggleMainChat, + updateAppLang +}; + const mapStateToProps = createSelector( userSelector, state => state.app.shouldShowSignIn, @@ -43,15 +54,6 @@ const mapStateToProps = createSelector( }) ); -const bindableActions = { - initWindowHeight, - updateNavHeight, - fetchUser, - submitChallenge, - toggleMapDrawer, - toggleMainChat -}; - // export plain class for testing export class FreeCodeCamp extends React.Component { static displayName = 'FreeCodeCamp'; @@ -74,9 +76,16 @@ export class FreeCodeCamp extends React.Component { toggleMainChat: PropTypes.func, fetchUser: PropTypes.func, shouldShowSignIn: PropTypes.bool, - params: PropTypes.object + params: PropTypes.object, + updateAppLang: PropTypes.func.isRequired }; + componentWillReceiveProps(nextProps) { + if (this.props.params.lang !== nextProps.params.lang) { + this.props.updateAppLang(nextProps.params.lang); + } + } + componentDidMount() { this.props.initWindowHeight(); if (!this.props.isSignedIn) { diff --git a/common/app/redux/actions.js b/common/app/redux/actions.js index 05abf5cdee..6593354c3b 100644 --- a/common/app/redux/actions.js +++ b/common/app/redux/actions.js @@ -40,6 +40,7 @@ export const updateUserLang = createAction( types.updateUserLang, (username, lang) => ({ username, lang }) ); +export const updateAppLang = createAction(types.updateAppLang); // updateCompletedChallenges(username: String) => Action export const updateCompletedChallenges = createAction( types.updateCompletedChallenges diff --git a/common/app/redux/reducer.js b/common/app/redux/reducer.js index 0b9064644b..3458f747d3 100644 --- a/common/app/redux/reducer.js +++ b/common/app/redux/reducer.js @@ -25,6 +25,10 @@ export default handleActions( user, shouldShowSignIn: true }), + [types.updateAppLang]: (state, { payload = 'en' }) =>({ + ...state, + lang: payload + }), [types.showSignIn]: state => ({ ...state, shouldShowSignIn: true diff --git a/common/app/redux/types.js b/common/app/redux/types.js index 2a6025d1b0..26789493b8 100644 --- a/common/app/redux/types.js +++ b/common/app/redux/types.js @@ -2,6 +2,7 @@ import createTypes from '../utils/create-types'; export default createTypes([ 'updateTitle', + 'updateAppLang', 'fetchUser', 'addUser', diff --git a/common/app/routes/settings/components/Language-Settings.jsx b/common/app/routes/settings/components/Language-Settings.jsx index 535d45f1e4..8abf10a96a 100644 --- a/common/app/routes/settings/components/Language-Settings.jsx +++ b/common/app/routes/settings/components/Language-Settings.jsx @@ -8,11 +8,11 @@ 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 - }) + userSelector, + ({ user: { languageTag } }) => ({ + // send null to prevent redux-form from initialize empty + initialValues: languageTag ? { lang: languageTag } : null + }) ); const actions = { updateMyLang }; const fields = [ 'lang' ]; diff --git a/common/app/routes/settings/redux/update-user-saga.js b/common/app/routes/settings/redux/update-user-saga.js index 1a08f829c7..a004f96740 100644 --- a/common/app/routes/settings/redux/update-user-saga.js +++ b/common/app/routes/settings/redux/update-user-saga.js @@ -1,14 +1,17 @@ import { Observable } from 'rx'; +import { push } from 'react-router-redux'; import { types } from './actions'; import combineSagas from '../../../utils/combine-sagas'; import { makeToast } from '../../../toasts/redux/actions'; +import { fetchChallenges } from '../../challenges/redux/actions'; import { updateUserFlag, updateUserEmail, updateUserLang, doActionOnError } from '../../../redux/actions'; +import { userSelector } from '../../../redux/selectors'; import { postJSON$ } from '../../../../utils/ajax-stream'; import langs from '../../../../utils/supported-languages'; @@ -47,26 +50,33 @@ export function updateUserLangSaga(actions$, getState) { .filter(({ type, payload }) => ( type === types.updateMyLang && !!langs[payload] )) - .map(({ payload }) => payload); + .map(({ payload }) => { + const state = getState(); + const { user: { languageTag } } = userSelector(state); + return { lang: payload, oldLang: languageTag }; + }); const ajaxUpdate$ = updateLang$ - .flatMap(lang => { - const { - app: { user: username, csrfToken: _csrf }, - entities: { user: userMap } - } = getState(); - const { languageTag: oldLang } = userMap[username] || {}; - const body = { - _csrf, - lang - }; + .debounce(250) + .flatMap(({ lang, oldLang }) => { + const { app: { user: username, csrfToken: _csrf } } = getState(); + const body = { _csrf, lang }; return postJSON$('/update-my-lang', body) - .map(({ message }) => makeToast({ message })) + .flatMap(({ message }) => { + return Observable.of( + // show user that we have updated their lang + makeToast({ message }), + // update url to reflect change + push(`/${lang}/settings`), + // refetch challenges in new language + fetchChallenges() + ); + }) .catch(doActionOnError(() => { return updateUserLang(username, oldLang); })); }); const optimistic$ = updateLang$ - .map(lang => { + .map(({ lang }) => { const { app: { user: username } } = getState(); return updateUserLang(username, lang); });