Feature(mousetrap): Add mousetrap features to redux
This commit is contained in:
@ -45,7 +45,8 @@ const sagaOptions = {
|
||||
isDev,
|
||||
window,
|
||||
document: window.document,
|
||||
location: window.location
|
||||
location: window.location,
|
||||
history: window.history
|
||||
};
|
||||
|
||||
|
||||
|
@ -1,16 +1,10 @@
|
||||
import { hardGoTo } from '../../common/app/redux/types';
|
||||
|
||||
export default function hardGoToSaga(action$, getState, { location }) {
|
||||
export default function hardGoToSaga(action$, getState, { history }) {
|
||||
return action$
|
||||
.filter(({ type }) => type === hardGoTo)
|
||||
.map(({ payload = '/map' }) => {
|
||||
if (!location.pathname) {
|
||||
return {
|
||||
type: 'app.error',
|
||||
error: new Error('no location object found')
|
||||
};
|
||||
}
|
||||
location.pathname = payload;
|
||||
.map(({ payload = '/settings' }) => {
|
||||
history.push(history.state, null, payload);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import executeChallengeSaga from './execute-challenge-saga';
|
||||
import frameSaga from './frame-saga';
|
||||
import codeStorageSaga from './code-storage-saga';
|
||||
import gitterSaga from './gitter-saga';
|
||||
import mouseTrapSaga from './mouse-trap-saga';
|
||||
|
||||
export default [
|
||||
errSaga,
|
||||
@ -15,5 +16,6 @@ export default [
|
||||
executeChallengeSaga,
|
||||
frameSaga,
|
||||
codeStorageSaga,
|
||||
gitterSaga
|
||||
gitterSaga,
|
||||
mouseTrapSaga
|
||||
];
|
||||
|
41
client/sagas/mouse-trap-saga.js
Normal file
41
client/sagas/mouse-trap-saga.js
Normal file
@ -0,0 +1,41 @@
|
||||
import { Observable } from 'rx';
|
||||
import MouseTrap from 'mousetrap';
|
||||
import { push } from 'react-router-redux';
|
||||
import {
|
||||
toggleNightMode,
|
||||
toggleMapDrawer,
|
||||
toggleMainChat,
|
||||
hardGoTo
|
||||
} from '../../common/app/redux/actions';
|
||||
|
||||
function bindKey$(key, actionCreator) {
|
||||
return Observable.fromEventPattern(
|
||||
h => MouseTrap.bind(key, h),
|
||||
h => MouseTrap.unbind(key, h)
|
||||
)
|
||||
.map(actionCreator);
|
||||
}
|
||||
|
||||
const softRedirects = {
|
||||
'g n n': '/challenges/next-challenge',
|
||||
'g n a': '/about',
|
||||
'g n m': '/map',
|
||||
'g n w': '/wiki',
|
||||
'g n s': '/shop',
|
||||
'g n o': '/settings'
|
||||
};
|
||||
|
||||
export default function mouseTrapSaga(actions$) {
|
||||
const traps$ = [
|
||||
...Object.keys(softRedirects)
|
||||
.map(key => bindKey$(key, () => push(softRedirects[key]))),
|
||||
bindKey$(
|
||||
'g n r',
|
||||
() => hardGoTo('https://github.com/freecodecamp/freecodecamp')
|
||||
),
|
||||
bindKey$('g m', toggleMapDrawer),
|
||||
bindKey$('g t n', toggleNightMode),
|
||||
bindKey$('g c', toggleMainChat)
|
||||
];
|
||||
return Observable.merge(traps$).takeUntil(actions$.last());
|
||||
}
|
@ -1,26 +1,22 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { hardGoTo } from '../../redux/actions';
|
||||
|
||||
const win = typeof window !== 'undefined' ? window : {};
|
||||
|
||||
function goToServer(path) {
|
||||
win.location = '/' + path;
|
||||
}
|
||||
|
||||
export default class extends React.Component {
|
||||
export class NotFound extends React.Component {
|
||||
static displayName = 'NotFound';
|
||||
|
||||
static propTypes = {
|
||||
params: PropTypes.object
|
||||
location: PropTypes.object,
|
||||
hardGoTo: PropTypes.func
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
goToServer(this.props.params.splat);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.props.hardGoTo(this.props.location.pathname);
|
||||
}
|
||||
|
||||
render() {
|
||||
return <span></span>;
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(null, { hardGoTo })(NotFound);
|
||||
|
@ -56,3 +56,4 @@ export const toggleWikiDrawer = createAction(types.toggleWikiDrawer);
|
||||
|
||||
// chat
|
||||
export const toggleMainChat = createAction(types.toggleMainChat);
|
||||
export const toggleNightMode = createAction(types.toggleNightMode);
|
||||
|
@ -9,6 +9,7 @@ export default createTypes([
|
||||
'makeToast',
|
||||
'updatePoints',
|
||||
'handleError',
|
||||
'toggleNightMode',
|
||||
// used to hit the server
|
||||
'hardGoTo',
|
||||
'delayedRedirect',
|
||||
|
@ -1,8 +1,16 @@
|
||||
import { modernChallenges, map, challenges } from './challenges';
|
||||
import NotFound from '../components/NotFound/index.jsx';
|
||||
import { addLang } from '../utils/add-lang';
|
||||
|
||||
export default {
|
||||
path: '/:lang',
|
||||
indexRoute: {
|
||||
onEnter(nextState, replace) {
|
||||
const { lang } = nextState.params;
|
||||
const { pathname } = nextState.location;
|
||||
replace(addLang(pathname, lang));
|
||||
}
|
||||
},
|
||||
childRoutes: [
|
||||
challenges,
|
||||
modernChallenges,
|
||||
|
@ -1,19 +1,7 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Link } from 'react-router';
|
||||
import supportedLanguages from '../../utils/supported-languages';
|
||||
|
||||
const toLowerCase = String.prototype.toLowerCase;
|
||||
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}`;
|
||||
}
|
||||
import { addLang } from './add-lang';
|
||||
|
||||
const mapStateToProps = state => ({ lang: state.app.lang });
|
||||
|
||||
|
13
common/app/utils/add-lang.js
Normal file
13
common/app/utils/add-lang.js
Normal file
@ -0,0 +1,13 @@
|
||||
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}`;
|
||||
}
|
@ -66,6 +66,7 @@
|
||||
"moment-timezone": "^0.5.0",
|
||||
"mongodb": "^2.0.33",
|
||||
"morgan": "^1.6.1",
|
||||
"mousetrap": "^1.6.0",
|
||||
"node-uuid": "^1.4.3",
|
||||
"nodemailer": "^2.1.0",
|
||||
"nodemailer-ses-transport": "^1.3.0",
|
||||
|
Reference in New Issue
Block a user