From 95aab958aa97da13730ccbb90fd2bbf7f0d88b4f Mon Sep 17 00:00:00 2001
From: Berkeley Martinez
Date: Mon, 20 Jun 2016 21:01:14 -0700
Subject: [PATCH] Feature(langauge): Make client history language aware Remove
need for language aware links
---
client/index.js | 3 +-
client/use-lang-routes.js | 44 +++++++++++++++++++
common/app/App.jsx | 6 +--
common/app/redux/reducer.js | 1 +
.../challenges/components/map/Block.jsx | 6 +--
common/app/routes/index.js | 2 +-
common/app/utils/Language-Link.jsx | 30 -------------
common/app/utils/add-lang.js | 13 ------
common/app/utils/lang.js | 39 ++++++++++++++++
common/utils/supported-languages.js | 1 +
server/middlewares/add-lang.js | 6 ++-
11 files changed, 98 insertions(+), 53 deletions(-)
create mode 100644 client/use-lang-routes.js
delete mode 100644 common/app/utils/Language-Link.jsx
delete mode 100644 common/app/utils/add-lang.js
create mode 100644 common/app/utils/lang.js
diff --git a/client/index.js b/client/index.js
index 296870365f..ed75f99364 100644
--- a/client/index.js
+++ b/client/index.js
@@ -10,6 +10,7 @@ import {
} from 'react-router-redux';
import { render } from 'redux-epic';
import { createHistory } from 'history';
+import useLangRoutes from './use-lang-routes.js';
import createApp from '../common/app';
import provideStore from '../common/app/provide-store';
@@ -36,7 +37,7 @@ initialState.app.csrfToken = csrfToken;
const serviceOptions = { xhrPath: '/services', context: { _csrf: csrfToken } };
-const history = createHistory();
+const history = useLangRoutes(createHistory)();
const devTools = window.devToolsExtension ? window.devToolsExtension() : f => f;
const adjustUrlOnReplay = !!window.devToolsExtension;
diff --git a/client/use-lang-routes.js b/client/use-lang-routes.js
new file mode 100644
index 0000000000..d18921521b
--- /dev/null
+++ b/client/use-lang-routes.js
@@ -0,0 +1,44 @@
+import { addLang, getLangFromPath } from '../common/app/utils/lang.js';
+
+function addLangToLocation(location, lang) {
+ if (!location) {
+ return location;
+ }
+ if (typeof location === 'string') {
+ return addLang(location, lang);
+ }
+ return {
+ ...location,
+ pathname: addLang(location.pathname, lang)
+ };
+}
+
+function getLangFromLocation(location) {
+ if (!location) {
+ return location;
+ }
+ if (typeof location === 'string') {
+ return getLangFromPath(location);
+ }
+ return getLangFromPath(location.pathname);
+}
+
+export default function useLangRoutes(createHistory) {
+ return (options = {}) => {
+ let lang = 'en';
+ const history = createHistory(options);
+ const unsubscribeFromHistory = history.listen(nextLocation => {
+ lang = getLangFromLocation(nextLocation);
+ });
+ const push = location => history.push(addLangToLocation(location, lang));
+ const replace = location => history.replace(
+ addLangToLocation(location, lang)
+ );
+ return {
+ ...history,
+ push,
+ replace,
+ unsubscribe() { unsubscribeFromHistory(); }
+ };
+ };
+}
diff --git a/common/app/App.jsx b/common/app/App.jsx
index e6554b7711..307f9e9044 100644
--- a/common/app/App.jsx
+++ b/common/app/App.jsx
@@ -36,15 +36,15 @@ const mapStateToProps = createSelector(
isMapAlreadyLoaded,
showChallengeComplete
) => ({
- shouldShowSignIn,
- isSignedIn: !!username,
username,
points,
picture,
toast,
+ shouldShowSignIn,
isMapDrawerOpen,
isMapAlreadyLoaded,
- showChallengeComplete
+ showChallengeComplete,
+ isSignedIn: !!username
})
);
diff --git a/common/app/redux/reducer.js b/common/app/redux/reducer.js
index 066bac4c13..c26f11cfa5 100644
--- a/common/app/redux/reducer.js
+++ b/common/app/redux/reducer.js
@@ -5,6 +5,7 @@ const initialState = {
title: 'Learn To Code | Free Code Camp',
shouldShowSignIn: false,
user: '',
+ lang: '',
csrfToken: '',
windowHeight: 0,
navHeight: 0,
diff --git a/common/app/routes/challenges/components/map/Block.jsx b/common/app/routes/challenges/components/map/Block.jsx
index 57837f68a1..8126df25ea 100644
--- a/common/app/routes/challenges/components/map/Block.jsx
+++ b/common/app/routes/challenges/components/map/Block.jsx
@@ -1,5 +1,5 @@
import React, { PropTypes } from 'react';
-import LangLink from '../../../../utils/Language-Link.jsx';
+import { Link } from 'react-router';
import { connect } from 'react-redux';
import FA from 'react-fontawesome';
import PureComponent from 'react-pure-render/component';
@@ -61,7 +61,7 @@ export class Block extends PureComponent {
className={ challengeClassName }
key={ title }
>
-
+
updateCurrentChallenge(challenge) }
>
@@ -73,7 +73,7 @@ export class Block extends PureComponent {
''
}
-
+
);
});
diff --git a/common/app/routes/index.js b/common/app/routes/index.js
index 0d2d3928b1..36a4bd2d55 100644
--- a/common/app/routes/index.js
+++ b/common/app/routes/index.js
@@ -1,6 +1,6 @@
import { modernChallenges, map, challenges } from './challenges';
import NotFound from '../components/NotFound/index.jsx';
-import { addLang } from '../utils/add-lang';
+import { addLang } from '../utils/lang';
export default {
path: '/:lang',
diff --git a/common/app/utils/Language-Link.jsx b/common/app/utils/Language-Link.jsx
deleted file mode 100644
index 8caf5f2610..0000000000
--- a/common/app/utils/Language-Link.jsx
+++ /dev/null
@@ -1,30 +0,0 @@
-import React, { PropTypes } from 'react';
-import { connect } from 'react-redux';
-import { Link } from 'react-router';
-import { addLang } from './add-lang';
-
-const mapStateToProps = state => ({ lang: state.app.lang });
-
-export class LangLink extends React.Component {
- static displayName = 'LangLink';
- static propTypes = {
- to: PropTypes.string,
- lang: PropTypes.string
- };
-
- render() {
- const {
- to,
- lang,
- ...props
- } = this.props;
- return (
-
- );
- }
-}
-
-export default connect(mapStateToProps)(LangLink);
diff --git a/common/app/utils/add-lang.js b/common/app/utils/add-lang.js
deleted file mode 100644
index ef66a4a2b5..0000000000
--- a/common/app/utils/add-lang.js
+++ /dev/null
@@ -1,13 +0,0 @@
-import supportedLanguages from '../../utils/supported-languages';
-
-const toLowerCase = String.prototype.toLowerCase;
-export function addLang(url, lang) {
- const maybeLang = toLowerCase.call(url.split('/')[1]);
- if (supportedLanguages[maybeLang]) {
- return url;
- }
- if (supportedLanguages[lang]) {
- return `/${lang}${url}`;
- }
- return `/en${url}`;
-}
diff --git a/common/app/utils/lang.js b/common/app/utils/lang.js
new file mode 100644
index 0000000000..f727331f66
--- /dev/null
+++ b/common/app/utils/lang.js
@@ -0,0 +1,39 @@
+import
+ supportedLanguages,
+ { langTagRegex }
+from '../../utils/supported-languages';
+
+const toLowerCase = String.prototype.toLowerCase;
+
+export function getLangFromPath(path) {
+ const maybeLang = toLowerCase.call(path.split('/')[1]);
+ if (supportedLanguages[maybeLang]) {
+ return maybeLang;
+ }
+ return 'en';
+}
+
+export function addLang(path, lang, primaryLang) {
+ // if maybeLang is supported continue
+ // if maybeLang is unsupported lang, remove and use lang
+ // if maybeLang is not lang tag, add lang tag.
+ // if both primary and lang are not lang tags default en
+ const maybeLang = toLowerCase.call(path.split('/')[1]);
+ const restUrl = path.split('/').slice(2).join('/');
+
+ if (supportedLanguages[maybeLang]) {
+ return path;
+ }
+
+ if (
+ langTagRegex.test(maybeLang) &&
+ !supportedLanguages[maybeLang]
+ ) {
+ return `/${primaryLang || lang }/${restUrl}`;
+ }
+
+ if (supportedLanguages[primaryLang || lang]) {
+ return `/${primaryLang || lang}${path}`;
+ }
+ return `/en${path}`;
+}
diff --git a/common/utils/supported-languages.js b/common/utils/supported-languages.js
index 536ad617f4..b074d02c43 100644
--- a/common/utils/supported-languages.js
+++ b/common/utils/supported-languages.js
@@ -1,3 +1,4 @@
+export const langTagRegex = /^[a-z]{2}(?:-[a-zA-Z]{2,3})?$/;
export default {
en: 'English',
es: 'Spanish'
diff --git a/server/middlewares/add-lang.js b/server/middlewares/add-lang.js
index e59f58078d..a1c7916382 100644
--- a/server/middlewares/add-lang.js
+++ b/server/middlewares/add-lang.js
@@ -1,9 +1,11 @@
-import supportedLanguages from '../../common/utils/supported-languages';
+import
+ supportedLanguages,
+ { langTagRegex }
+from '../../common/utils/supported-languages';
import passthroughs from '../utils/lang-passthrough-urls';
import debug from 'debug';
const log = debug('fcc:middlewares:lang');
-const langTagRegex = /^[a-z]{2}(?:-[a-zA-Z]{2,3})?$/;
const toLowerCase = String.prototype.toLowerCase;
// redirect(statusOrUrl: String|Number, url?: String) => Void