mirror of
https://github.com/elyby/accounts-frontend.git
synced 2025-02-19 00:38:11 +05:30
#85: updated to react router 4 and migrated code to support a new api
This commit is contained in:
parent
6f4eb97b48
commit
6b81385dc5
@ -195,10 +195,10 @@
|
||||
"react/jsx-indent-props": "warn",
|
||||
"react/jsx-key": "warn",
|
||||
"react/jsx-max-props-per-line": ["warn", {"maximum": 3}],
|
||||
"react/jsx-no-bind": "warn",
|
||||
"react/jsx-no-bind": "off",
|
||||
"react/jsx-no-duplicate-props": "warn",
|
||||
"react/jsx-no-literals": "warn",
|
||||
"react/jsx-no-undef": "warn",
|
||||
"react/jsx-no-undef": "error",
|
||||
"react/jsx-pascal-case": "warn",
|
||||
"react/jsx-uses-react": "warn",
|
||||
"react/jsx-uses-vars": "warn",
|
||||
|
77
npm-shrinkwrap.json
generated
77
npm-shrinkwrap.json
generated
@ -3789,9 +3789,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"history": {
|
||||
"version": "3.3.0",
|
||||
"from": "history@>=3.0.0 <4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/history/-/history-3.3.0.tgz"
|
||||
"version": "4.6.1",
|
||||
"from": "history@>=4.5.1 <5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/history/-/history-4.6.1.tgz"
|
||||
},
|
||||
"hoek": {
|
||||
"version": "2.16.3",
|
||||
@ -5991,7 +5991,8 @@
|
||||
"query-string": {
|
||||
"version": "4.3.4",
|
||||
"from": "query-string@>=4.1.0 <5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz"
|
||||
"resolved": "https://registry.npmjs.org/query-string/-/query-string-4.3.4.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"querystring": {
|
||||
"version": "0.2.0",
|
||||
@ -6168,9 +6169,53 @@
|
||||
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-5.0.4.tgz"
|
||||
},
|
||||
"react-router": {
|
||||
"version": "3.0.5",
|
||||
"from": "react-router@3.0.5",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-3.0.5.tgz"
|
||||
"version": "4.1.1",
|
||||
"from": "react-router@>=4.1.1 <5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-4.1.1.tgz",
|
||||
"dependencies": {
|
||||
"invariant": {
|
||||
"version": "2.2.2",
|
||||
"from": "invariant@>=2.2.2 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.2.tgz"
|
||||
},
|
||||
"isarray": {
|
||||
"version": "0.0.1",
|
||||
"from": "isarray@0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz"
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "3.0.1",
|
||||
"from": "js-tokens@^3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz"
|
||||
},
|
||||
"loose-envify": {
|
||||
"version": "1.3.1",
|
||||
"from": "loose-envify@^1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz"
|
||||
},
|
||||
"path-to-regexp": {
|
||||
"version": "1.7.0",
|
||||
"from": "path-to-regexp@>=1.5.3 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-router-dom": {
|
||||
"version": "4.1.1",
|
||||
"from": "react-router-dom@latest",
|
||||
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.1.1.tgz",
|
||||
"dependencies": {
|
||||
"js-tokens": {
|
||||
"version": "3.0.1",
|
||||
"from": "js-tokens@>=3.0.0 <4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.1.tgz"
|
||||
},
|
||||
"loose-envify": {
|
||||
"version": "1.3.1",
|
||||
"from": "loose-envify@>=1.3.1 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.3.1.tgz"
|
||||
}
|
||||
}
|
||||
},
|
||||
"react-side-effect": {
|
||||
"version": "1.1.0",
|
||||
@ -6569,6 +6614,11 @@
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-1.0.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"resolve-pathname": {
|
||||
"version": "2.1.0",
|
||||
"from": "resolve-pathname@>=2.0.0 <3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.1.0.tgz"
|
||||
},
|
||||
"restore-cursor": {
|
||||
"version": "1.0.1",
|
||||
"from": "restore-cursor@>=1.0.1 <2.0.0",
|
||||
@ -7052,7 +7102,8 @@
|
||||
"strict-uri-encode": {
|
||||
"version": "1.1.0",
|
||||
"from": "strict-uri-encode@>=1.0.0 <2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz"
|
||||
"resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"string_decoder": {
|
||||
"version": "0.10.31",
|
||||
@ -7520,6 +7571,11 @@
|
||||
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.1.7.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"url-search-params-polyfill": {
|
||||
"version": "1.2.0",
|
||||
"from": "url-search-params-polyfill@latest",
|
||||
"resolved": "https://registry.npmjs.org/url-search-params-polyfill/-/url-search-params-polyfill-1.2.0.tgz"
|
||||
},
|
||||
"user-home": {
|
||||
"version": "1.1.1",
|
||||
"from": "user-home@>=1.1.1 <2.0.0",
|
||||
@ -7608,6 +7664,11 @@
|
||||
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.1.tgz",
|
||||
"dev": true
|
||||
},
|
||||
"value-equal": {
|
||||
"version": "0.2.1",
|
||||
"from": "value-equal@>=0.2.0 <0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.2.1.tgz"
|
||||
},
|
||||
"varstream": {
|
||||
"version": "0.3.2",
|
||||
"from": "varstream@>=0.3.2 <0.4.0",
|
||||
|
@ -38,10 +38,11 @@
|
||||
"react-intl": "^2.0.0",
|
||||
"react-motion": "^0.4.0",
|
||||
"react-redux": "^5.0.0",
|
||||
"react-router": "^3.0.0",
|
||||
"react-router-dom": "^4.1.1",
|
||||
"redux": "^3.0.4",
|
||||
"redux-localstorage": "^0.4.1",
|
||||
"redux-thunk": "^2.0.0",
|
||||
"url-search-params-polyfill": "^1.2.0",
|
||||
"webfontloader": "^1.6.26",
|
||||
"whatwg-fetch": "^2.0.0"
|
||||
},
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import classNames from 'classnames';
|
||||
import { Link } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { FormattedMessage as Message } from 'react-intl';
|
||||
|
||||
import loader from 'services/loader';
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { browserHistory } from 'react-router';
|
||||
import { browserHistory } from 'services/history';
|
||||
|
||||
import { sessionStorage } from 'services/localStorage';
|
||||
import authentication from 'services/api/authentication';
|
||||
|
@ -1,9 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
export default class OAuthInit extends Component {
|
||||
static displayName = 'OAuthInit';
|
||||
|
||||
render() {
|
||||
return <span />;
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { FormattedMessage as Message } from 'react-intl';
|
||||
import { Link } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import icons from 'components/ui/icons.scss';
|
||||
import BaseAuthBody from 'components/auth/BaseAuthBody';
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { browserHistory } from 'react-router';
|
||||
import { browserHistory } from 'services/history';
|
||||
|
||||
import logger from 'services/logger';
|
||||
import localStorage from 'services/localStorage';
|
||||
|
@ -13,15 +13,17 @@ export default class ActivationBody extends BaseAuthBody {
|
||||
static panelId = 'activation';
|
||||
|
||||
static propTypes = {
|
||||
params: PropTypes.shape({
|
||||
key: PropTypes.string
|
||||
match: PropTypes.shape({
|
||||
params: PropTypes.shape({
|
||||
key: PropTypes.string
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
autoFocusField = this.props.params && this.props.params.key ? null : 'key';
|
||||
autoFocusField = this.props.match.params && this.props.match.params.key ? null : 'key';
|
||||
|
||||
render() {
|
||||
const {key} = this.props.params;
|
||||
const {key} = this.props.match.params;
|
||||
const email = this.context.user.email;
|
||||
|
||||
return (
|
||||
|
@ -16,16 +16,18 @@ export default class RecoverPasswordBody extends BaseAuthBody {
|
||||
static hasGoBack = true;
|
||||
|
||||
static propTypes = {
|
||||
params: PropTypes.shape({
|
||||
key: PropTypes.string
|
||||
match: PropTypes.shape({
|
||||
params: PropTypes.shape({
|
||||
key: PropTypes.string
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
autoFocusField = this.props.params && this.props.params.key ? 'newPassword' : 'key';
|
||||
autoFocusField = this.props.match.params && this.props.match.params.key ? 'newPassword' : 'key';
|
||||
|
||||
render() {
|
||||
const {user} = this.context;
|
||||
const {key} = this.props.params;
|
||||
const {key} = this.props.match.params;
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { FormattedMessage as Message } from 'react-intl';
|
||||
import { Link } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import { Input, Checkbox, Captcha } from 'components/ui/form';
|
||||
import BaseAuthBody from 'components/auth/BaseAuthBody';
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { Link } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { FormattedMessage as Message } from 'react-intl';
|
||||
|
||||
import { LangMenu } from 'components/langMenu';
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import { FormattedMessage as Message, FormattedRelative as Relative, FormattedHTMLMessage as HTMLMessage } from 'react-intl';
|
||||
import { Link } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import Helmet from 'react-helmet';
|
||||
|
||||
import { userShape } from 'components/user/User';
|
||||
@ -15,7 +15,7 @@ import messages from './Profile.intl.json';
|
||||
|
||||
import RulesPage from 'pages/rules/RulesPage';
|
||||
|
||||
export default class Profile extends Component {
|
||||
class Profile extends Component {
|
||||
static displayName = 'Profile';
|
||||
static propTypes = {
|
||||
user: userShape
|
||||
@ -130,3 +130,9 @@ export default class Profile extends Component {
|
||||
this.UUID = el;
|
||||
}
|
||||
}
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
export default connect((state) => ({
|
||||
user: state.user
|
||||
}))(Profile);
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { Link } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import styles from './profile.scss';
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { FormattedMessage as Message } from 'react-intl';
|
||||
import { Link } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import FormComponent from 'components/ui/form/FormComponent';
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
|
||||
import { browserHistory } from 'react-router';
|
||||
import { browserHistory } from 'services/history';
|
||||
|
||||
import styles from './popup.scss';
|
||||
|
||||
@ -18,7 +18,7 @@ export class PopupStack extends Component {
|
||||
|
||||
componentWillMount() {
|
||||
document.addEventListener('keyup', this.onKeyPress);
|
||||
this.unlistenTransition = browserHistory.listenBefore(this.onRouteLeave);
|
||||
this.unlistenTransition = browserHistory.listen(this.onRouteLeave);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
|
@ -1,49 +0,0 @@
|
||||
/**
|
||||
* Implements scroll to animation with momentum effect
|
||||
*
|
||||
* @see http://ariya.ofilabs.com/2013/11/javascript-kinetic-scrolling-part-2.html
|
||||
*/
|
||||
|
||||
import { rAF, getScrollTop } from 'functions';
|
||||
|
||||
const TIME_CONSTANT = 100; // higher numbers - slower animation
|
||||
export function scrollTo(y) {
|
||||
const start = Date.now();
|
||||
let scrollWasTouched = false;
|
||||
rAF(() => { // wrap in rAF to optimize initial reading of scrollTop
|
||||
const contentHeight = document.documentElement.scrollHeight;
|
||||
const windowHeight = window.innerHeight;
|
||||
if (contentHeight < y + windowHeight) {
|
||||
y = contentHeight - windowHeight;
|
||||
}
|
||||
|
||||
const amplitude = y - getScrollTop();
|
||||
|
||||
(function animateScroll() {
|
||||
const elapsed = Date.now() - start;
|
||||
|
||||
let delta = -amplitude * Math.exp(-elapsed / TIME_CONSTANT);
|
||||
|
||||
if (Math.abs(delta) > 0.5 && !scrollWasTouched) {
|
||||
rAF(animateScroll);
|
||||
} else {
|
||||
delta = 0;
|
||||
document.removeEventListener('mousewheel', markScrollTouched);
|
||||
document.removeEventListener('touchstart', markScrollTouched);
|
||||
}
|
||||
|
||||
if (scrollWasTouched) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newScrollTop = y + delta;
|
||||
window.scrollTo(0, newScrollTop);
|
||||
}());
|
||||
});
|
||||
|
||||
document.addEventListener('mousewheel', markScrollTouched);
|
||||
document.addEventListener('touchstart', markScrollTouched);
|
||||
function markScrollTouched() {
|
||||
scrollWasTouched = true;
|
||||
}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { Link } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { FormattedMessage as Message } from 'react-intl';
|
||||
|
||||
import buttons from 'components/ui/buttons.scss';
|
||||
|
19
src/containers/AuthFlowRoute.jsx
Normal file
19
src/containers/AuthFlowRoute.jsx
Normal file
@ -0,0 +1,19 @@
|
||||
import { PropTypes } from 'react';
|
||||
import { Route } from 'react-router-dom';
|
||||
|
||||
import AuthFlowRouteContents from './AuthFlowRouteContents';
|
||||
|
||||
export default function AuthFlowRoute(props) {
|
||||
const {component: Component, ...routeProps} = props;
|
||||
|
||||
return (
|
||||
<Route {...routeProps} render={(props) => (
|
||||
<AuthFlowRouteContents routerProps={props} component={Component} />
|
||||
)}/>
|
||||
);
|
||||
}
|
||||
|
||||
AuthFlowRoute.propTypes = {
|
||||
component: PropTypes.any,
|
||||
routerProps: PropTypes.object
|
||||
};
|
51
src/containers/AuthFlowRouteContents.jsx
Normal file
51
src/containers/AuthFlowRouteContents.jsx
Normal file
@ -0,0 +1,51 @@
|
||||
import { Component, PropTypes } from 'react';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
|
||||
import authFlow from 'services/authFlow';
|
||||
|
||||
export default class AuthFlowRouteContents extends Component {
|
||||
static propTypes = {
|
||||
component: PropTypes.any,
|
||||
routerProps: PropTypes.object
|
||||
};
|
||||
|
||||
state = {
|
||||
component: null
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.handleProps(this.props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.handleProps(nextProps);
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.state.component;
|
||||
}
|
||||
|
||||
handleProps(props) {
|
||||
const {routerProps} = props;
|
||||
|
||||
authFlow.handleRequest({
|
||||
path: routerProps.location.pathname,
|
||||
params: routerProps.match.params,
|
||||
query: routerProps.location.query
|
||||
}, this.onRedirect.bind(this), this.onRouteAllowed.bind(this, props));
|
||||
}
|
||||
|
||||
onRedirect(path) {
|
||||
this.setState({
|
||||
component: <Redirect to={path} />
|
||||
});
|
||||
}
|
||||
|
||||
onRouteAllowed(props) {
|
||||
const {component: Component} = props;
|
||||
|
||||
this.setState({
|
||||
component: <Component {...props.routerProps} />
|
||||
});
|
||||
}
|
||||
}
|
20
src/containers/PrivateRoute.jsx
Normal file
20
src/containers/PrivateRoute.jsx
Normal file
@ -0,0 +1,20 @@
|
||||
import authFlow from 'services/authFlow';
|
||||
import { Route, Redirect } from 'react-router-dom';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
const PrivateRoute = ({user, component: Component, ...rest}) => (
|
||||
<Route {...rest} render={(props) => (
|
||||
user.isGuest ? (
|
||||
<Redirect to={{
|
||||
pathname: '/login',
|
||||
state: { from: props.location }
|
||||
}}/>
|
||||
) : (
|
||||
<Component {...props}/>
|
||||
)
|
||||
)}/>
|
||||
);
|
||||
|
||||
export default connect((state) => ({
|
||||
user: state.user
|
||||
}))(PrivateRoute);
|
@ -92,3 +92,83 @@ export function getScrollTop() {
|
||||
const doc = document.documentElement;
|
||||
return (window.pageYOffset || doc.scrollTop) - (doc.clientTop || 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Implements scroll to animation with momentum effect
|
||||
*
|
||||
* @see http://ariya.ofilabs.com/2013/11/javascript-kinetic-scrolling-part-2.html
|
||||
*/
|
||||
|
||||
const TIME_CONSTANT = 100; // higher numbers - slower animation
|
||||
export function scrollTo(y) {
|
||||
const start = Date.now();
|
||||
let scrollWasTouched = false;
|
||||
rAF(() => { // wrap in rAF to optimize initial reading of scrollTop
|
||||
const contentHeight = document.documentElement.scrollHeight;
|
||||
const windowHeight = window.innerHeight;
|
||||
if (contentHeight < y + windowHeight) {
|
||||
y = contentHeight - windowHeight;
|
||||
}
|
||||
|
||||
const amplitude = y - getScrollTop();
|
||||
|
||||
(function animateScroll() {
|
||||
const elapsed = Date.now() - start;
|
||||
|
||||
let delta = -amplitude * Math.exp(-elapsed / TIME_CONSTANT);
|
||||
|
||||
if (Math.abs(delta) > 0.5 && !scrollWasTouched) {
|
||||
rAF(animateScroll);
|
||||
} else {
|
||||
delta = 0;
|
||||
document.removeEventListener('mousewheel', markScrollTouched);
|
||||
document.removeEventListener('touchstart', markScrollTouched);
|
||||
}
|
||||
|
||||
if (scrollWasTouched) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newScrollTop = y + delta;
|
||||
window.scrollTo(0, newScrollTop);
|
||||
}());
|
||||
});
|
||||
|
||||
document.addEventListener('mousewheel', markScrollTouched);
|
||||
document.addEventListener('touchstart', markScrollTouched);
|
||||
function markScrollTouched() {
|
||||
scrollWasTouched = true;
|
||||
}
|
||||
}
|
||||
|
||||
const SCROLL_ANCHOR_OFFSET = 80; // 50 + 30 (header height + some spacing)
|
||||
// Первый скролл выполняется сразу после загрузки страницы, так что чтобы снизить
|
||||
// нагрузку на рендеринг мы откладываем первый скрол на 200ms
|
||||
let isFirstScroll = true;
|
||||
/**
|
||||
* Scrolls to page's top or #anchor link, if any
|
||||
*/
|
||||
export function restoreScroll() {
|
||||
const {hash} = location;
|
||||
|
||||
setTimeout(() => {
|
||||
isFirstScroll = false;
|
||||
const id = hash.replace('#', '');
|
||||
const el = id ? document.getElementById(id) : null;
|
||||
const viewPort = document.body;
|
||||
|
||||
if (!viewPort) {
|
||||
console.log('Can not find viewPort element'); // eslint-disable-line
|
||||
return;
|
||||
}
|
||||
|
||||
let y = 0;
|
||||
if (el) {
|
||||
const {top} = el.getBoundingClientRect();
|
||||
|
||||
y = getScrollTop() + top - SCROLL_ANCHOR_OFFSET;
|
||||
}
|
||||
|
||||
scrollTo(y, viewPort);
|
||||
}, isFirstScroll ? 200 : 0);
|
||||
}
|
||||
|
60
src/index.js
60
src/index.js
@ -4,17 +4,19 @@ import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
import { Provider as ReduxProvider } from 'react-redux';
|
||||
import { Router, browserHistory } from 'react-router';
|
||||
import { Router, Route, Switch } from 'react-router-dom';
|
||||
|
||||
import { factory as userFactory } from 'components/user/factory';
|
||||
import { IntlProvider } from 'components/i18n';
|
||||
import routesFactory from 'routes';
|
||||
import authFlow from 'services/authFlow';
|
||||
import storeFactory from 'storeFactory';
|
||||
import bsodFactory from 'components/ui/bsod/factory';
|
||||
import loader from 'services/loader';
|
||||
import logger from 'services/logger';
|
||||
import font from 'services/font';
|
||||
import history from 'services/history';
|
||||
import history, { browserHistory } from 'services/history';
|
||||
import RootPage from 'pages/root/RootPage';
|
||||
import AuthFlowRoute from 'containers/AuthFlowRoute';
|
||||
|
||||
history.init();
|
||||
|
||||
@ -24,7 +26,8 @@ logger.init({
|
||||
|
||||
const store = storeFactory();
|
||||
|
||||
bsodFactory(store, stopLoading);
|
||||
bsodFactory(store, () => loader.hide());
|
||||
authFlow.setStore(store);
|
||||
|
||||
Promise.all([
|
||||
userFactory(store),
|
||||
@ -34,11 +37,11 @@ Promise.all([
|
||||
ReactDOM.render(
|
||||
<ReduxProvider store={store}>
|
||||
<IntlProvider>
|
||||
<Router history={browserHistory} onUpdate={() => {
|
||||
restoreScroll();
|
||||
stopLoading();
|
||||
}}>
|
||||
{routesFactory(store)}
|
||||
<Router history={browserHistory}>
|
||||
<Switch>
|
||||
<AuthFlowRoute path="/oauth2/:version/:clientId?" component={() => null} />
|
||||
<Route path="/" component={RootPage} />
|
||||
</Switch>
|
||||
</Router>
|
||||
</IntlProvider>
|
||||
</ReduxProvider>,
|
||||
@ -48,45 +51,6 @@ Promise.all([
|
||||
initAnalytics();
|
||||
});
|
||||
|
||||
|
||||
function stopLoading() {
|
||||
loader.hide();
|
||||
}
|
||||
|
||||
import { scrollTo } from 'components/ui/scrollTo';
|
||||
import { getScrollTop } from 'functions';
|
||||
const SCROLL_ANCHOR_OFFSET = 80; // 50 + 30 (header height + some spacing)
|
||||
// Первый скролл выполняется сразу после загрузки страницы, так что чтобы снизить
|
||||
// нагрузку на рендеринг мы откладываем первый скрол на 200ms
|
||||
let isFirstScroll = true;
|
||||
/**
|
||||
* Scrolls to page's top or #anchor link, if any
|
||||
*/
|
||||
function restoreScroll() {
|
||||
const {hash} = location;
|
||||
|
||||
setTimeout(() => {
|
||||
isFirstScroll = false;
|
||||
const id = hash.replace('#', '');
|
||||
const el = id ? document.getElementById(id) : null;
|
||||
const viewPort = document.body;
|
||||
|
||||
if (!viewPort) {
|
||||
console.log('Can not find viewPort element'); // eslint-disable-line
|
||||
return;
|
||||
}
|
||||
|
||||
let y = 0;
|
||||
if (el) {
|
||||
const {top} = el.getBoundingClientRect();
|
||||
|
||||
y = getScrollTop() + top - SCROLL_ANCHOR_OFFSET;
|
||||
}
|
||||
|
||||
scrollTo(y, viewPort);
|
||||
}, isFirstScroll ? 200 : 0);
|
||||
}
|
||||
|
||||
import { loadScript, debounce } from 'functions';
|
||||
const trackPageView = debounce(_trackPageView);
|
||||
function initAnalytics() {
|
||||
|
@ -2,7 +2,7 @@ import React from 'react';
|
||||
|
||||
import { FooterMenu } from 'components/footerMenu';
|
||||
|
||||
import { Link } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { FormattedMessage as Message } from 'react-intl';
|
||||
import Helmet from 'react-helmet';
|
||||
|
||||
|
@ -1,10 +1,22 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
import { Component, PropTypes } from 'react';
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { Route, Switch, Redirect } from 'react-router-dom';
|
||||
|
||||
import AppInfo from 'components/auth/appInfo/AppInfo';
|
||||
import PanelTransition from 'components/auth/PanelTransition';
|
||||
|
||||
import Register from 'components/auth/register/Register';
|
||||
import Login from 'components/auth/login/Login';
|
||||
import Permissions from 'components/auth/permissions/Permissions';
|
||||
import ChooseAccount from 'components/auth/chooseAccount/ChooseAccount';
|
||||
import Activation from 'components/auth/activation/Activation';
|
||||
import ResendActivation from 'components/auth/resendActivation/ResendActivation';
|
||||
import Password from 'components/auth/password/Password';
|
||||
import AcceptRules from 'components/auth/acceptRules/AcceptRules';
|
||||
import ForgotPassword from 'components/auth/forgotPassword/ForgotPassword';
|
||||
import RecoverPassword from 'components/auth/recoverPassword/RecoverPassword';
|
||||
import Finish from 'components/auth/finish/Finish';
|
||||
|
||||
import styles from './auth.scss';
|
||||
|
||||
class AuthPage extends Component {
|
||||
@ -31,7 +43,20 @@ class AuthPage extends Component {
|
||||
<AppInfo {...client} onGoToAuth={this.onGoToAuth} />
|
||||
</div>
|
||||
<div className={styles.content}>
|
||||
<PanelTransition {...this.props} />
|
||||
<Switch>
|
||||
<Route path="/login" render={renderPanelTransition(Login)} />
|
||||
<Route path="/password" render={renderPanelTransition(Password)} />
|
||||
<Route path="/register" render={renderPanelTransition(Register)} />
|
||||
<Route path="/activation/:key?" render={renderPanelTransition(Activation)} />
|
||||
<Route path="/resend-activation" render={renderPanelTransition(ResendActivation)} />
|
||||
<Route path="/oauth/permissions" render={renderPanelTransition(Permissions)} />
|
||||
<Route path="/oauth/choose-account" render={renderPanelTransition(ChooseAccount)} />
|
||||
<Route path="/oauth/finish" component={Finish} />
|
||||
<Route path="/accept-rules" render={renderPanelTransition(AcceptRules)} />
|
||||
<Route path="/forgot-password" render={renderPanelTransition(ForgotPassword)} />
|
||||
<Route path="/recover-password/:key?" render={renderPanelTransition(RecoverPassword)} />
|
||||
<Redirect to="/404" />
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@ -44,7 +69,23 @@ class AuthPage extends Component {
|
||||
};
|
||||
}
|
||||
|
||||
function renderPanelTransition(factory) {
|
||||
const {Title, Body, Footer, Links} = factory();
|
||||
return (props) => (
|
||||
<PanelTransition
|
||||
key="panel-transition"
|
||||
Title={<Title />}
|
||||
Body={<Body {...props} />}
|
||||
Footer={<Footer />}
|
||||
Links={<Links />}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect((state) => ({
|
||||
import { connect } from 'react-redux';
|
||||
import { withRouter } from 'react-router';
|
||||
|
||||
export default withRouter(connect((state) => ({
|
||||
client: state.auth.client
|
||||
}))(AuthPage);
|
||||
}))(AuthPage));
|
||||
|
@ -1,22 +0,0 @@
|
||||
import React, { Component } from 'react';
|
||||
|
||||
import ProfilePage from 'pages/profile/ProfilePage';
|
||||
import Profile from 'components/profile/Profile';
|
||||
|
||||
class IndexPage extends Component {
|
||||
displayName = 'IndexPage';
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ProfilePage>
|
||||
<Profile {...this.props} />
|
||||
</ProfilePage>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
export default connect((state) => ({
|
||||
user: state.user
|
||||
}))(IndexPage);
|
@ -10,32 +10,33 @@ class ChangeEmailPage extends Component {
|
||||
static propTypes = {
|
||||
email: PropTypes.string.isRequired,
|
||||
lang: PropTypes.string.isRequired,
|
||||
params: PropTypes.shape({
|
||||
step: PropTypes.oneOf(['step1', 'step2', 'step3']),
|
||||
code: PropTypes.string
|
||||
history: PropTypes.shape({
|
||||
push: PropTypes.func
|
||||
}).isRequired,
|
||||
match: PropTypes.shape({
|
||||
params: PropTypes.shape({
|
||||
step: PropTypes.oneOf(['step1', 'step2', 'step3']),
|
||||
code: PropTypes.string
|
||||
})
|
||||
})
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.shape({
|
||||
push: PropTypes.func
|
||||
}).isRequired,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
goToProfile: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
componentWillMount() {
|
||||
const step = this.props.params.step;
|
||||
const step = this.props.match.params.step;
|
||||
|
||||
if (step && !/^step\d$/.test(step)) {
|
||||
if (step && !/^step[123]$/.test(step)) {
|
||||
// wrong param value
|
||||
// TODO: probably we should decide with something better here
|
||||
this.context.router.push('/');
|
||||
this.props.history.push('/404');
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {params: {step = 'step1', code}} = this.props;
|
||||
const {step = 'step1', code} = this.props.match.params;
|
||||
|
||||
return (
|
||||
<ChangeEmail
|
||||
@ -50,7 +51,7 @@ class ChangeEmailPage extends Component {
|
||||
}
|
||||
|
||||
onChangeStep = (step) => {
|
||||
this.context.router.push(`/profile/change-email/step${++step}`);
|
||||
this.props.history.push(`/profile/change-email/step${++step}`);
|
||||
};
|
||||
|
||||
onSubmit = (step, form) => {
|
||||
|
@ -1,7 +1,14 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { Route, Switch, Redirect } from 'react-router-dom';
|
||||
|
||||
import logger from 'services/logger';
|
||||
|
||||
import Profile from 'components/profile/Profile';
|
||||
import ChangePasswordPage from 'pages/profile/ChangePasswordPage';
|
||||
import ChangeUsernamePage from 'pages/profile/ChangeUsernamePage';
|
||||
import ChangeEmailPage from 'pages/profile/ChangeEmailPage';
|
||||
|
||||
import { FooterMenu } from 'components/footerMenu';
|
||||
|
||||
import styles from './profile.scss';
|
||||
@ -31,7 +38,13 @@ class ProfilePage extends Component {
|
||||
render() {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
{this.props.children}
|
||||
<Switch>
|
||||
<Route path="/profile/change-password" component={ChangePasswordPage} />
|
||||
<Route path="/profile/change-username" component={ChangeUsernamePage} />
|
||||
<Route path="/profile/change-email/:step?/:code?" component={ChangeEmailPage} />
|
||||
<Route path="/" exact component={Profile} />
|
||||
<Redirect to="/404" />
|
||||
</Switch>
|
||||
|
||||
<div className={styles.footer}>
|
||||
<FooterMenu />
|
||||
@ -42,7 +55,7 @@ class ProfilePage extends Component {
|
||||
}
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { browserHistory } from 'react-router';
|
||||
import { browserHistory } from 'services/history';
|
||||
import { fetchUserData } from 'components/user/actions';
|
||||
import { create as createPopup } from 'components/ui/popup/actions';
|
||||
import PasswordRequestForm from 'components/profile/passwordRequestForm/PasswordRequestForm';
|
||||
|
@ -1,14 +1,22 @@
|
||||
import React, { PropTypes } from 'react';
|
||||
import { Component, PropTypes } from 'react';
|
||||
|
||||
import { FormattedMessage as Message } from 'react-intl';
|
||||
import { Link } from 'react-router';
|
||||
import { Route, Link, Switch } from 'react-router-dom';
|
||||
import classNames from 'classnames';
|
||||
|
||||
import AuthPage from 'pages/auth/AuthPage';
|
||||
import ProfilePage from 'pages/profile/ProfilePage';
|
||||
import RulesPage from 'pages/rules/RulesPage';
|
||||
import PageNotFound from 'pages/404/PageNotFound';
|
||||
|
||||
import { restoreScroll } from 'functions';
|
||||
import PrivateRoute from 'containers/PrivateRoute';
|
||||
import AuthFlowRoute from 'containers/AuthFlowRoute';
|
||||
import Userbar from 'components/userbar/Userbar';
|
||||
import PopupStack from 'components/ui/popup/PopupStack';
|
||||
import loader from 'services/loader';
|
||||
|
||||
import styles from './root.scss';
|
||||
|
||||
import messages from './RootPage.intl.json';
|
||||
|
||||
/* global process: false */
|
||||
@ -19,36 +27,59 @@ if (process.env.NODE_ENV === 'production') {
|
||||
DevTools = require('containers/DevTools').default;
|
||||
}
|
||||
|
||||
function RootPage(props) {
|
||||
const isRegisterPage = props.location.pathname === '/register';
|
||||
class RootPage extends Component {
|
||||
componentDidMount() {
|
||||
this.onPageUpdate();
|
||||
}
|
||||
|
||||
document.body.style.overflow = props.isPopupActive ? 'hidden' : '';
|
||||
componentDidUpdate() {
|
||||
this.onPageUpdate();
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<div id="view-port" className={classNames(styles.viewPort, {
|
||||
[styles.isPopupActive]: props.isPopupActive
|
||||
})}>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.headerContent}>
|
||||
<Link to="/" className={styles.logo} onClick={props.resetAuth}>
|
||||
<Message {...messages.siteName} />
|
||||
</Link>
|
||||
<div className={styles.userbar}>
|
||||
<Userbar {...props}
|
||||
guestAction={isRegisterPage ? 'login' : 'register'}
|
||||
/>
|
||||
onPageUpdate() {
|
||||
loader.hide();
|
||||
restoreScroll();
|
||||
}
|
||||
|
||||
render() {
|
||||
const props = this.props;
|
||||
const isRegisterPage = props.location.pathname === '/register';
|
||||
|
||||
document.body.style.overflow = props.isPopupActive ? 'hidden' : '';
|
||||
|
||||
return (
|
||||
<div className={styles.root}>
|
||||
<div id="view-port" className={classNames(styles.viewPort, {
|
||||
[styles.isPopupActive]: props.isPopupActive
|
||||
})}>
|
||||
<div className={styles.header}>
|
||||
<div className={styles.headerContent}>
|
||||
<Link to="/" className={styles.logo} onClick={props.onLogoClick}>
|
||||
<Message {...messages.siteName} />
|
||||
</Link>
|
||||
<div className={styles.userbar}>
|
||||
<Userbar {...props}
|
||||
guestAction={isRegisterPage ? 'login' : 'register'}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.body}>
|
||||
<Switch>
|
||||
<PrivateRoute path="/profile" component={ProfilePage} />
|
||||
<Route path="/404" component={PageNotFound} />
|
||||
<Route path="/rules" component={RulesPage} />
|
||||
<AuthFlowRoute exact path="/" component={ProfilePage} />
|
||||
<AuthFlowRoute path="/" component={AuthPage} />
|
||||
<Route component={PageNotFound} />
|
||||
</Switch>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.body}>
|
||||
{props.children}
|
||||
</div>
|
||||
<PopupStack />
|
||||
<DevTools />
|
||||
</div>
|
||||
<PopupStack />
|
||||
<DevTools />
|
||||
</div>
|
||||
);
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
RootPage.displayName = 'RootPage';
|
||||
@ -56,17 +87,21 @@ RootPage.propTypes = {
|
||||
location: PropTypes.shape({
|
||||
pathname: PropTypes.string
|
||||
}).isRequired,
|
||||
user: PropTypes.shape({
|
||||
isGuest: PropTypes.boolean
|
||||
}),
|
||||
children: PropTypes.element,
|
||||
resetAuth: PropTypes.func.isRequired,
|
||||
onLogoClick: PropTypes.func.isRequired,
|
||||
isPopupActive: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
import { connect } from 'react-redux';
|
||||
import { resetAuth } from 'components/auth/actions';
|
||||
import { withRouter } from 'react-router';
|
||||
|
||||
export default connect((state) => ({
|
||||
export default withRouter(connect((state) => ({
|
||||
user: state.user,
|
||||
isPopupActive: state.popup.popups.length > 0
|
||||
}), {
|
||||
resetAuth
|
||||
})(RootPage);
|
||||
onLogoClick: resetAuth
|
||||
})(RootPage));
|
||||
|
@ -1,6 +1,6 @@
|
||||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
import { Link } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { FormattedMessage as Message } from 'react-intl';
|
||||
import Helmet from 'react-helmet';
|
||||
|
||||
@ -61,19 +61,18 @@ const rules = [
|
||||
export default class RulesPage extends Component {
|
||||
static propTypes = {
|
||||
location: PropTypes.shape({
|
||||
pathname: PropTypes.string,
|
||||
search: PropTypes.string,
|
||||
hash: PropTypes.string
|
||||
})
|
||||
};
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.shape({
|
||||
createLocation: PropTypes.func.required,
|
||||
replace: PropTypes.func.required
|
||||
}).isRequired,
|
||||
history: PropTypes.shape({
|
||||
replace: PropTypes.func
|
||||
}).isRequired
|
||||
};
|
||||
|
||||
render() {
|
||||
let {hash} = this.props.location;
|
||||
|
||||
if (hash) {
|
||||
hash = hash.substring(1);
|
||||
}
|
||||
@ -136,9 +135,9 @@ export default class RulesPage extends Component {
|
||||
}
|
||||
|
||||
const {id} = event.currentTarget;
|
||||
const {router} = this.context;
|
||||
const newLocation = router.createLocation({...location, hash: `#${id}`});
|
||||
router.replace(newLocation);
|
||||
const newPath = `${this.props.location.pathname}${this.props.location.search}#${id}`;
|
||||
|
||||
this.props.history.replace(newPath);
|
||||
}
|
||||
|
||||
static getTitleHash(sectionIndex) {
|
||||
|
@ -1,4 +1,5 @@
|
||||
import 'babel-polyfill';
|
||||
import 'url-search-params-polyfill';
|
||||
import 'whatwg-fetch';
|
||||
import { shim as shimPromiseFinaly } from 'promise.prototype.finally';
|
||||
|
||||
|
@ -1,82 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Route, IndexRoute } from 'react-router';
|
||||
|
||||
import RootPage from 'pages/root/RootPage';
|
||||
import IndexPage from 'pages/index/IndexPage';
|
||||
import AuthPage from 'pages/auth/AuthPage';
|
||||
|
||||
import RulesPage from 'pages/rules/RulesPage';
|
||||
import PageNotFound from 'pages/404/PageNotFound';
|
||||
|
||||
import ProfilePage from 'pages/profile/ProfilePage';
|
||||
import ProfileChangePasswordPage from 'pages/profile/ChangePasswordPage';
|
||||
import ProfileChangeUsernamePage from 'pages/profile/ChangeUsernamePage';
|
||||
import ProfileChangeEmailPage from 'pages/profile/ChangeEmailPage';
|
||||
|
||||
import OAuthInit from 'components/auth/OAuthInit';
|
||||
import Register from 'components/auth/register/Register';
|
||||
import Login from 'components/auth/login/Login';
|
||||
import Permissions from 'components/auth/permissions/Permissions';
|
||||
import ChooseAccount from 'components/auth/chooseAccount/ChooseAccount';
|
||||
import Activation from 'components/auth/activation/Activation';
|
||||
import ResendActivation from 'components/auth/resendActivation/ResendActivation';
|
||||
import Password from 'components/auth/password/Password';
|
||||
import AcceptRules from 'components/auth/acceptRules/AcceptRules';
|
||||
import ForgotPassword from 'components/auth/forgotPassword/ForgotPassword';
|
||||
import RecoverPassword from 'components/auth/recoverPassword/RecoverPassword';
|
||||
import Finish from 'components/auth/finish/Finish';
|
||||
|
||||
import authFlow from 'services/authFlow';
|
||||
|
||||
export default function routesFactory(store) {
|
||||
authFlow.setStore(store);
|
||||
|
||||
const startAuthFlow = {
|
||||
onEnter: ({location: {query, pathname: path}, params}, replace, callback) =>
|
||||
authFlow.handleRequest({path, params, query}, replace, callback)
|
||||
};
|
||||
|
||||
const userOnly = {
|
||||
onEnter: (nextState, replace) => {
|
||||
const {user} = store.getState();
|
||||
|
||||
if (user.isGuest) {
|
||||
replace('/');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: when react-router v3 is out, should update to oauth2(/v1)(/:clientId)
|
||||
// to oauth2(/:version)(/:clientId) with the help of new route matching options
|
||||
return (
|
||||
<Route path="/" component={RootPage}>
|
||||
<IndexRoute component={IndexPage} {...startAuthFlow} />
|
||||
|
||||
<Route path="rules" component={RulesPage} />
|
||||
|
||||
<Route path="oauth2(/v1)(/:clientId)" component={OAuthInit} {...startAuthFlow} />
|
||||
|
||||
<Route path="auth" component={AuthPage}>
|
||||
<Route path="/login" components={new Login()} {...startAuthFlow} />
|
||||
<Route path="/password" components={new Password()} {...startAuthFlow} />
|
||||
<Route path="/register" components={new Register()} {...startAuthFlow} />
|
||||
<Route path="/activation(/:key)" components={new Activation()} {...startAuthFlow} />
|
||||
<Route path="/resend-activation" components={new ResendActivation()} {...startAuthFlow} />
|
||||
<Route path="/oauth/permissions" components={new Permissions()} {...startAuthFlow} />
|
||||
<Route path="/oauth/choose-account" components={new ChooseAccount()} {...startAuthFlow} />
|
||||
<Route path="/oauth/finish" component={Finish} {...startAuthFlow} />
|
||||
<Route path="/accept-rules" components={new AcceptRules()} {...startAuthFlow} />
|
||||
<Route path="/forgot-password" components={new ForgotPassword()} {...startAuthFlow} />
|
||||
<Route path="/recover-password(/:key)" components={new RecoverPassword()} {...startAuthFlow} />
|
||||
</Route>
|
||||
|
||||
<Route path="profile" component={ProfilePage} {...userOnly}>
|
||||
<Route path="change-password" component={ProfileChangePasswordPage} />
|
||||
<Route path="change-username" component={ProfileChangeUsernamePage} />
|
||||
<Route path="change-email(/:step)(/:code)" component={ProfileChangeEmailPage} />
|
||||
</Route>
|
||||
|
||||
<Route path="*" component={PageNotFound} />
|
||||
</Route>
|
||||
);
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { browserHistory } from 'react-router';
|
||||
import { browserHistory } from 'services/history';
|
||||
|
||||
import logger from 'services/logger';
|
||||
import localStorage from 'services/localStorage';
|
||||
@ -100,7 +100,7 @@ export default class AuthFlow {
|
||||
getRequest() {
|
||||
return {
|
||||
path: '',
|
||||
query: {},
|
||||
query: new URLSearchParams(),
|
||||
params: {},
|
||||
...this.currentRequest
|
||||
};
|
||||
|
@ -6,14 +6,14 @@ export default class OAuthState extends AbstractState {
|
||||
const {query, params} = context.getRequest();
|
||||
|
||||
return context.run('oAuthValidate', {
|
||||
clientId: query.client_id || params.clientId,
|
||||
redirectUrl: query.redirect_uri,
|
||||
responseType: query.response_type,
|
||||
description: query.description,
|
||||
scope: query.scope,
|
||||
prompt: query.prompt,
|
||||
loginHint: query.login_hint,
|
||||
state: query.state
|
||||
clientId: query.get('client_id') || params.clientId,
|
||||
redirectUrl: query.get('redirect_uri'),
|
||||
responseType: query.get('response_type'),
|
||||
description: query.get('description'),
|
||||
scope: query.get('scope'),
|
||||
prompt: query.get('prompt'),
|
||||
loginHint: query.get('login_hint'),
|
||||
state: query.get('state')
|
||||
}).then(() => context.setState(new CompleteState()));
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
|
||||
import { FormattedMessage as Message, FormattedRelative as Relative } from 'react-intl';
|
||||
import { Link } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
import messages from './errorsDict.intl.json';
|
||||
|
||||
|
@ -2,6 +2,22 @@
|
||||
* A helper wrapper service around window.history
|
||||
*/
|
||||
|
||||
import createBrowserHistory from 'history/createBrowserHistory';
|
||||
|
||||
export const browserHistory = createBrowserHistory();
|
||||
|
||||
browserHistory.listen(() => {
|
||||
patchHistory(browserHistory);
|
||||
});
|
||||
|
||||
function patchHistory(history) {
|
||||
Object.assign(history.location,
|
||||
{query: new URLSearchParams(history.location.search)}
|
||||
);
|
||||
}
|
||||
|
||||
patchHistory(browserHistory);
|
||||
|
||||
export default {
|
||||
init() {
|
||||
this.initialLength = window.history.length;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import expect from 'unexpected';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import { browserHistory } from 'react-router';
|
||||
import { browserHistory } from 'services/history';
|
||||
|
||||
import logger from 'services/logger';
|
||||
import { InternalServerError } from 'services/request';
|
||||
|
@ -1,4 +1,5 @@
|
||||
import expect from 'unexpected';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import AuthFlow from 'services/authFlow/AuthFlow';
|
||||
|
||||
@ -33,7 +34,7 @@ describe('AuthFlow.functional', () => {
|
||||
navigate = function navigate(path, extra = {}) { // emulates router behaviour
|
||||
if (navigate.lastUrl !== path) {
|
||||
navigate.lastUrl = path;
|
||||
flow.handleRequest({path, query: {}, params: {}, ...extra}, navigate);
|
||||
flow.handleRequest({path, query: new URLSearchParams(), params: {}, ...extra}, navigate);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import expect from 'unexpected';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import AuthFlow from 'services/authFlow/AuthFlow';
|
||||
import AbstractState from 'services/authFlow/AbstractState';
|
||||
@ -352,7 +353,7 @@ describe('AuthFlow', () => {
|
||||
|
||||
expect(flow.getRequest(), 'to equal', {
|
||||
...request,
|
||||
query: {},
|
||||
query: new URLSearchParams(),
|
||||
params: {}
|
||||
});
|
||||
});
|
||||
|
@ -1,3 +1,5 @@
|
||||
import sinon from 'sinon';
|
||||
|
||||
import OAuthState from 'services/authFlow/OAuthState';
|
||||
import CompleteState from 'services/authFlow/CompleteState';
|
||||
|
||||
@ -29,11 +31,14 @@ describe('OAuthState', () => {
|
||||
description: 'description',
|
||||
scope: 'scope',
|
||||
prompt: 'none',
|
||||
login_hint: 1,
|
||||
login_hint: '1',
|
||||
state: 'state'
|
||||
};
|
||||
|
||||
context.getRequest.returns({query, params: {}});
|
||||
context.getRequest.returns({
|
||||
query: new URLSearchParams(query),
|
||||
params: {}
|
||||
});
|
||||
|
||||
expectRun(
|
||||
mock,
|
||||
@ -63,7 +68,7 @@ describe('OAuthState', () => {
|
||||
};
|
||||
|
||||
context.getRequest.returns({
|
||||
query,
|
||||
query: new URLSearchParams(query),
|
||||
params: {clientId}
|
||||
});
|
||||
|
||||
@ -93,7 +98,7 @@ describe('OAuthState', () => {
|
||||
};
|
||||
|
||||
context.getRequest.returns({
|
||||
query,
|
||||
query: new URLSearchParams(query),
|
||||
params: {clientId}
|
||||
});
|
||||
|
||||
@ -115,7 +120,7 @@ describe('OAuthState', () => {
|
||||
it('should transition to complete state on success', () => {
|
||||
const promise = Promise.resolve();
|
||||
|
||||
context.getRequest.returns({query: {}, params: {}});
|
||||
context.getRequest.returns({query: new URLSearchParams(), params: {}});
|
||||
|
||||
mock.expects('run').returns(promise);
|
||||
expectState(mock, CompleteState);
|
||||
|
@ -52,4 +52,19 @@ describe('services/request', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#buildQuery', () => {
|
||||
it('should build query', () => {
|
||||
const data = {
|
||||
notSet: undefined,
|
||||
numeric: 1,
|
||||
complexString: 'sdfgs sdfg ',
|
||||
positive: true,
|
||||
negative: false
|
||||
};
|
||||
const expectedQs = 'notSet=&numeric=1&complexString=sdfgs%20sdfg%20&positive=1&negative=0';
|
||||
|
||||
expect(request.buildQuery(data), 'to equal', expectedQs);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -103,7 +103,7 @@ const webpackConfig = {
|
||||
'react/addons': true
|
||||
} : {},
|
||||
|
||||
devtool: isTest ? 'inline-source-map' : 'eval',
|
||||
devtool: 'cheap-module-eval-source-map',
|
||||
|
||||
plugins: [
|
||||
new webpack.DefinePlugin({
|
||||
|
Loading…
x
Reference in New Issue
Block a user