Feature(settings): Update url/challenge lang onChange

This commit is contained in:
Berkeley Martinez
2016-07-20 15:06:44 -07:00
parent 232e0713d1
commit e5efcbb6c6
6 changed files with 54 additions and 29 deletions

View File

@ -9,7 +9,8 @@ import {
initWindowHeight, initWindowHeight,
updateNavHeight, updateNavHeight,
toggleMapDrawer, toggleMapDrawer,
toggleMainChat toggleMainChat,
updateAppLang
} from './redux/actions'; } from './redux/actions';
import { submitChallenge } from './routes/challenges/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 Toasts from './toasts/Toasts.jsx';
import { userSelector } from './redux/selectors'; import { userSelector } from './redux/selectors';
const bindableActions = {
initWindowHeight,
updateNavHeight,
fetchUser,
submitChallenge,
toggleMapDrawer,
toggleMainChat,
updateAppLang
};
const mapStateToProps = createSelector( const mapStateToProps = createSelector(
userSelector, userSelector,
state => state.app.shouldShowSignIn, 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 plain class for testing
export class FreeCodeCamp extends React.Component { export class FreeCodeCamp extends React.Component {
static displayName = 'FreeCodeCamp'; static displayName = 'FreeCodeCamp';
@ -74,9 +76,16 @@ export class FreeCodeCamp extends React.Component {
toggleMainChat: PropTypes.func, toggleMainChat: PropTypes.func,
fetchUser: PropTypes.func, fetchUser: PropTypes.func,
shouldShowSignIn: PropTypes.bool, 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() { componentDidMount() {
this.props.initWindowHeight(); this.props.initWindowHeight();
if (!this.props.isSignedIn) { if (!this.props.isSignedIn) {

View File

@ -40,6 +40,7 @@ export const updateUserLang = createAction(
types.updateUserLang, types.updateUserLang,
(username, lang) => ({ username, lang }) (username, lang) => ({ username, lang })
); );
export const updateAppLang = createAction(types.updateAppLang);
// updateCompletedChallenges(username: String) => Action // updateCompletedChallenges(username: String) => Action
export const updateCompletedChallenges = createAction( export const updateCompletedChallenges = createAction(
types.updateCompletedChallenges types.updateCompletedChallenges

View File

@ -25,6 +25,10 @@ export default handleActions(
user, user,
shouldShowSignIn: true shouldShowSignIn: true
}), }),
[types.updateAppLang]: (state, { payload = 'en' }) =>({
...state,
lang: payload
}),
[types.showSignIn]: state => ({ [types.showSignIn]: state => ({
...state, ...state,
shouldShowSignIn: true shouldShowSignIn: true

View File

@ -2,6 +2,7 @@ import createTypes from '../utils/create-types';
export default createTypes([ export default createTypes([
'updateTitle', 'updateTitle',
'updateAppLang',
'fetchUser', 'fetchUser',
'addUser', 'addUser',

View File

@ -8,11 +8,11 @@ import { userSelector } from '../../../redux/selectors';
import langs from '../../../../utils/supported-languages'; import langs from '../../../../utils/supported-languages';
const mapStateToProps = createSelector( const mapStateToProps = createSelector(
userSelector, userSelector,
({ user: { languageTag } }) => ({ ({ user: { languageTag } }) => ({
// send null to prevent redux-form from initialize empty // send null to prevent redux-form from initialize empty
initialValues: languageTag ? { lang: languageTag } : null initialValues: languageTag ? { lang: languageTag } : null
}) })
); );
const actions = { updateMyLang }; const actions = { updateMyLang };
const fields = [ 'lang' ]; const fields = [ 'lang' ];

View File

@ -1,14 +1,17 @@
import { Observable } from 'rx'; import { Observable } from 'rx';
import { push } from 'react-router-redux';
import { types } from './actions'; import { types } from './actions';
import combineSagas from '../../../utils/combine-sagas'; import combineSagas from '../../../utils/combine-sagas';
import { makeToast } from '../../../toasts/redux/actions'; import { makeToast } from '../../../toasts/redux/actions';
import { fetchChallenges } from '../../challenges/redux/actions';
import { import {
updateUserFlag, updateUserFlag,
updateUserEmail, updateUserEmail,
updateUserLang, updateUserLang,
doActionOnError doActionOnError
} from '../../../redux/actions'; } from '../../../redux/actions';
import { userSelector } from '../../../redux/selectors';
import { postJSON$ } from '../../../../utils/ajax-stream'; import { postJSON$ } from '../../../../utils/ajax-stream';
import langs from '../../../../utils/supported-languages'; import langs from '../../../../utils/supported-languages';
@ -47,26 +50,33 @@ export function updateUserLangSaga(actions$, getState) {
.filter(({ type, payload }) => ( .filter(({ type, payload }) => (
type === types.updateMyLang && !!langs[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$ const ajaxUpdate$ = updateLang$
.flatMap(lang => { .debounce(250)
const { .flatMap(({ lang, oldLang }) => {
app: { user: username, csrfToken: _csrf }, const { app: { user: username, csrfToken: _csrf } } = getState();
entities: { user: userMap } const body = { _csrf, lang };
} = getState();
const { languageTag: oldLang } = userMap[username] || {};
const body = {
_csrf,
lang
};
return postJSON$('/update-my-lang', body) 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(() => { .catch(doActionOnError(() => {
return updateUserLang(username, oldLang); return updateUserLang(username, oldLang);
})); }));
}); });
const optimistic$ = updateLang$ const optimistic$ = updateLang$
.map(lang => { .map(({ lang }) => {
const { app: { user: username } } = getState(); const { app: { user: username } } = getState();
return updateUserLang(username, lang); return updateUserLang(username, lang);
}); });