mirror of
				https://github.com/elyby/accounts-frontend.git
				synced 2025-05-31 14:11:58 +05:30 
			
		
		
		
	Merge branch 'master' into profile
This commit is contained in:
		@@ -18,11 +18,11 @@
 | 
				
			|||||||
    "history": "^1.17.0",
 | 
					    "history": "^1.17.0",
 | 
				
			||||||
    "intl-format-cache": "^2.0.4",
 | 
					    "intl-format-cache": "^2.0.4",
 | 
				
			||||||
    "intl-messageformat": "^1.1.0",
 | 
					    "intl-messageformat": "^1.1.0",
 | 
				
			||||||
    "react": "^0.14.0",
 | 
					    "react": "^15.0.0-rc.1",
 | 
				
			||||||
    "react-dom": "^0.14.3",
 | 
					    "react-dom": "^15.0.0-rc.1",
 | 
				
			||||||
    "react-height": "^2.0.3",
 | 
					    "react-height": "^2.0.3",
 | 
				
			||||||
    "react-helmet": "^2.3.1",
 | 
					    "react-helmet": "^2.3.1",
 | 
				
			||||||
    "react-intl": "=2.0.0-beta-2",
 | 
					    "react-intl": "=v2.0.0-rc-1",
 | 
				
			||||||
    "react-motion": "^0.4.0",
 | 
					    "react-motion": "^0.4.0",
 | 
				
			||||||
    "react-redux": "^4.0.0",
 | 
					    "react-redux": "^4.0.0",
 | 
				
			||||||
    "react-router": "^2.0.0",
 | 
					    "react-router": "^2.0.0",
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,30 +3,33 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
import React, { Component, PropTypes } from 'react';
 | 
					import React, { Component, PropTypes } from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import AuthError from './AuthError';
 | 
					import AuthError from 'components/auth/authError/AuthError';
 | 
				
			||||||
 | 
					import { userShape } from 'components/user/User';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class BaseAuthBody extends Component {
 | 
					export default class BaseAuthBody extends Component {
 | 
				
			||||||
    static propTypes = {
 | 
					    static contextTypes = {
 | 
				
			||||||
        clearErrors: PropTypes.func.isRequired,
 | 
					        clearErrors: PropTypes.func.isRequired,
 | 
				
			||||||
        resolve: PropTypes.func.isRequired,
 | 
					        resolve: PropTypes.func.isRequired,
 | 
				
			||||||
        reject: PropTypes.func.isRequired,
 | 
					        reject: PropTypes.func.isRequired,
 | 
				
			||||||
        auth: PropTypes.shape({
 | 
					        auth: PropTypes.shape({
 | 
				
			||||||
            error: PropTypes.string
 | 
					            error: PropTypes.string,
 | 
				
			||||||
        })
 | 
					            scopes: PropTypes.array
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					        user: userShape
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    renderErrors() {
 | 
					    renderErrors() {
 | 
				
			||||||
        return this.props.auth.error
 | 
					        return this.context.auth.error
 | 
				
			||||||
            ? <AuthError error={this.props.auth.error} onClose={this.onClearErrors} />
 | 
					            ? <AuthError error={this.context.auth.error} onClose={this.onClearErrors} />
 | 
				
			||||||
            : ''
 | 
					            : ''
 | 
				
			||||||
            ;
 | 
					            ;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    onFormSubmit() {
 | 
					    onFormSubmit() {
 | 
				
			||||||
        this.props.resolve(this.serialize());
 | 
					        this.context.resolve(this.serialize());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    onClearErrors = this.props.clearErrors;
 | 
					    onClearErrors = this.context.clearErrors;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    form = {};
 | 
					    form = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -39,6 +42,35 @@ export default class BaseAuthBody extends Component {
 | 
				
			|||||||
        };
 | 
					        };
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /**
 | 
				
			||||||
 | 
					     * Fixes some issues with scroll, when input beeing focused
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * When an element is focused, by default browsers will scroll its parents to display
 | 
				
			||||||
 | 
					     * focused item to user. This behavior may cause unexpected visual effects, when
 | 
				
			||||||
 | 
					     * you animating apearing of an input (e.g. transform) and auto focusing it. In
 | 
				
			||||||
 | 
					     * that case the browser will scroll the parent container so that input will be
 | 
				
			||||||
 | 
					     * visible.
 | 
				
			||||||
 | 
					     * This method will fix that issue by finding parent with overflow: hidden and
 | 
				
			||||||
 | 
					     * reseting its scrollLeft value to 0.
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * Usage:
 | 
				
			||||||
 | 
					     * <input autoFocus onFocus={this.fixAutoFocus} />
 | 
				
			||||||
 | 
					     *
 | 
				
			||||||
 | 
					     * @param {Object} event
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    fixAutoFocus = (event) => {
 | 
				
			||||||
 | 
					        let el = event.target;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while (el.parentNode) {
 | 
				
			||||||
 | 
					            el = el.parentNode;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (getComputedStyle(el).overflow === 'hidden') {
 | 
				
			||||||
 | 
					                el.scrollLeft = 0;
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    serialize() {
 | 
					    serialize() {
 | 
				
			||||||
        return Object.keys(this.form).reduce((acc, key) => {
 | 
					        return Object.keys(this.form).reduce((acc, key) => {
 | 
				
			||||||
            acc[key] = this.form[key].getValue();
 | 
					            acc[key] = this.form[key].getValue();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -10,6 +10,7 @@ import {helpLinks as helpLinksStyles} from 'components/auth/helpLinks.scss';
 | 
				
			|||||||
import panelStyles from 'components/ui/panel.scss';
 | 
					import panelStyles from 'components/ui/panel.scss';
 | 
				
			||||||
import icons from 'components/ui/icons.scss';
 | 
					import icons from 'components/ui/icons.scss';
 | 
				
			||||||
import authFlow from 'services/authFlow';
 | 
					import authFlow from 'services/authFlow';
 | 
				
			||||||
 | 
					import { userShape } from 'components/user/User';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import * as actions from './actions';
 | 
					import * as actions from './actions';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -21,6 +22,7 @@ class PanelTransition extends Component {
 | 
				
			|||||||
    static displayName = 'PanelTransition';
 | 
					    static displayName = 'PanelTransition';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static propTypes = {
 | 
					    static propTypes = {
 | 
				
			||||||
 | 
					        // context props
 | 
				
			||||||
        auth: PropTypes.shape({
 | 
					        auth: PropTypes.shape({
 | 
				
			||||||
            error: PropTypes.string,
 | 
					            error: PropTypes.string,
 | 
				
			||||||
            login: PropTypes.shape({
 | 
					            login: PropTypes.shape({
 | 
				
			||||||
@@ -28,15 +30,45 @@ class PanelTransition extends Component {
 | 
				
			|||||||
                password: PropTypes.string
 | 
					                password: PropTypes.string
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
        }).isRequired,
 | 
					        }).isRequired,
 | 
				
			||||||
 | 
					        user: userShape.isRequired,
 | 
				
			||||||
        setError: React.PropTypes.func.isRequired,
 | 
					        setError: React.PropTypes.func.isRequired,
 | 
				
			||||||
        clearErrors: React.PropTypes.func.isRequired,
 | 
					        clearErrors: React.PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					        resolve: React.PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					        reject: React.PropTypes.func.isRequired,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // local props
 | 
				
			||||||
        path: PropTypes.string.isRequired,
 | 
					        path: PropTypes.string.isRequired,
 | 
				
			||||||
        Title: PropTypes.element.isRequired,
 | 
					        Title: PropTypes.element,
 | 
				
			||||||
        Body: PropTypes.element.isRequired,
 | 
					        Body: PropTypes.element,
 | 
				
			||||||
        Footer: PropTypes.element.isRequired,
 | 
					        Footer: PropTypes.element,
 | 
				
			||||||
        Links: PropTypes.element.isRequired
 | 
					        Links: PropTypes.element,
 | 
				
			||||||
 | 
					        children: PropTypes.element
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static childContextTypes = {
 | 
				
			||||||
 | 
					        auth: PropTypes.shape({
 | 
				
			||||||
 | 
					            error: PropTypes.string,
 | 
				
			||||||
 | 
					            login: PropTypes.shape({
 | 
				
			||||||
 | 
					                login: PropTypes.string,
 | 
				
			||||||
 | 
					                password: PropTypes.string
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					        }),
 | 
				
			||||||
 | 
					        user: userShape,
 | 
				
			||||||
 | 
					        clearErrors: React.PropTypes.func,
 | 
				
			||||||
 | 
					        resolve: PropTypes.func,
 | 
				
			||||||
 | 
					        reject: PropTypes.func
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    getChildContext() {
 | 
				
			||||||
 | 
					        return {
 | 
				
			||||||
 | 
					            auth: this.props.auth,
 | 
				
			||||||
 | 
					            user: this.props.user,
 | 
				
			||||||
 | 
					            clearErrors: this.props.clearErrors,
 | 
				
			||||||
 | 
					            resolve: this.props.resolve,
 | 
				
			||||||
 | 
					            reject: this.props.reject
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    state = {
 | 
					    state = {
 | 
				
			||||||
        height: {},
 | 
					        height: {},
 | 
				
			||||||
        contextHeight: 0
 | 
					        contextHeight: 0
 | 
				
			||||||
@@ -70,6 +102,12 @@ class PanelTransition extends Component {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        const {path, Title, Body, Footer, Links} = this.props;
 | 
					        const {path, Title, Body, Footer, Links} = this.props;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (this.props.children) {
 | 
				
			||||||
 | 
					            return this.props.children;
 | 
				
			||||||
 | 
					        } else if (!Title || !Body || !Footer || !Links) {
 | 
				
			||||||
 | 
					            throw new Error('Title, Body, Footer and Links are required');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <TransitionMotion
 | 
					            <TransitionMotion
 | 
				
			||||||
                styles={[
 | 
					                styles={[
 | 
				
			||||||
@@ -91,7 +129,7 @@ class PanelTransition extends Component {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    const contentHeight = {
 | 
					                    const contentHeight = {
 | 
				
			||||||
                        overflow: 'hidden',
 | 
					                        overflow: 'hidden',
 | 
				
			||||||
                        height: forceHeight ? common.switchContextHeightSpring : 'auto'
 | 
					                        height: forceHeight ? common.style.switchContextHeightSpring : 'auto'
 | 
				
			||||||
                    };
 | 
					                    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    const bodyHeight = {
 | 
					                    const bodyHeight = {
 | 
				
			||||||
@@ -141,6 +179,7 @@ class PanelTransition extends Component {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @param  {Object} config
 | 
					     * @param  {Object} config
 | 
				
			||||||
 | 
					     * @param  {string} config.key
 | 
				
			||||||
     * @param  {Object} [options]
 | 
					     * @param  {Object} [options]
 | 
				
			||||||
     * @param  {Object} [options.isLeave=false] - true, if this is a leave transition
 | 
					     * @param  {Object} [options.isLeave=false] - true, if this is a leave transition
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
@@ -155,7 +194,7 @@ class PanelTransition extends Component {
 | 
				
			|||||||
            '/password': 1,
 | 
					            '/password': 1,
 | 
				
			||||||
            '/activation': 1,
 | 
					            '/activation': 1,
 | 
				
			||||||
            '/oauth/permissions': -1,
 | 
					            '/oauth/permissions': -1,
 | 
				
			||||||
            '/password-change': 1,
 | 
					            '/change-password': 1,
 | 
				
			||||||
            '/forgot-password': 1
 | 
					            '/forgot-password': 1
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        const sign = map[key];
 | 
					        const sign = map[key];
 | 
				
			||||||
@@ -177,7 +216,7 @@ class PanelTransition extends Component {
 | 
				
			|||||||
            '/register': not('/activation') ? 'Y' : 'X',
 | 
					            '/register': not('/activation') ? 'Y' : 'X',
 | 
				
			||||||
            '/activation': not('/register') ? 'Y' : 'X',
 | 
					            '/activation': not('/register') ? 'Y' : 'X',
 | 
				
			||||||
            '/oauth/permissions': 'Y',
 | 
					            '/oauth/permissions': 'Y',
 | 
				
			||||||
            '/password-change': 'Y',
 | 
					            '/change-password': 'Y',
 | 
				
			||||||
            '/forgot-password': not('/password') && not('/login') ? 'Y' : 'X'
 | 
					            '/forgot-password': not('/password') && not('/login') ? 'Y' : 'X'
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -235,7 +274,7 @@ class PanelTransition extends Component {
 | 
				
			|||||||
            <div key={`header${key}`} style={style}>
 | 
					            <div key={`header${key}`} style={style}>
 | 
				
			||||||
                {hasBackButton ? backButton : null}
 | 
					                {hasBackButton ? backButton : null}
 | 
				
			||||||
                <div style={scrollStyle}>
 | 
					                <div style={scrollStyle}>
 | 
				
			||||||
                    {React.cloneElement(Title, this.props)}
 | 
					                    {Title}
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
@@ -263,7 +302,6 @@ class PanelTransition extends Component {
 | 
				
			|||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <ReactHeight key={`body${key}`} style={style} onHeightReady={this.onUpdateHeight}>
 | 
					            <ReactHeight key={`body${key}`} style={style} onHeightReady={this.onUpdateHeight}>
 | 
				
			||||||
                {React.cloneElement(Body, {
 | 
					                {React.cloneElement(Body, {
 | 
				
			||||||
                    ...this.props,
 | 
					 | 
				
			||||||
                    ref: (body) => {
 | 
					                    ref: (body) => {
 | 
				
			||||||
                        this.body = body;
 | 
					                        this.body = body;
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
@@ -279,7 +317,7 @@ class PanelTransition extends Component {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <div key={`footer${key}`} style={style}>
 | 
					            <div key={`footer${key}`} style={style}>
 | 
				
			||||||
                {React.cloneElement(Footer, this.props)}
 | 
					                {Footer}
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -291,15 +329,15 @@ class PanelTransition extends Component {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <div key={`links${key}`} style={style}>
 | 
					            <div key={`links${key}`} style={style}>
 | 
				
			||||||
                {React.cloneElement(Links, this.props)}
 | 
					                {Links}
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * @param  {string} key
 | 
					     * @param  {string} key
 | 
				
			||||||
     * @param  {Object} props
 | 
					     * @param  {Object} style
 | 
				
			||||||
     * @param  {number} props.opacitySpring
 | 
					     * @param  {number} style.opacitySpring
 | 
				
			||||||
     *
 | 
					     *
 | 
				
			||||||
     * @return {Object}
 | 
					     * @return {Object}
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
import { routeActions } from 'react-router-redux';
 | 
					import { routeActions } from 'react-router-redux';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { updateUser, logout as logoutUser, authenticate } from 'components/user/actions';
 | 
					import { updateUser, logout as logoutUser, changePassword as changeUserPassword, authenticate } from 'components/user/actions';
 | 
				
			||||||
import request from 'services/request';
 | 
					import request from 'services/request';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function login({login = '', password = '', rememberMe = false}) {
 | 
					export function login({login = '', password = '', rememberMe = false}) {
 | 
				
			||||||
@@ -32,7 +32,7 @@ export function login({login = '', password = '', rememberMe = false}) {
 | 
				
			|||||||
                    username: login,
 | 
					                    username: login,
 | 
				
			||||||
                    email: login
 | 
					                    email: login
 | 
				
			||||||
                }));
 | 
					                }));
 | 
				
			||||||
            } else {
 | 
					            } else if (resp.errors) {
 | 
				
			||||||
                if (resp.errors.login === LOGIN_REQUIRED && password) {
 | 
					                if (resp.errors.login === LOGIN_REQUIRED && password) {
 | 
				
			||||||
                    dispatch(logout());
 | 
					                    dispatch(logout());
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -46,6 +46,25 @@ export function login({login = '', password = '', rememberMe = false}) {
 | 
				
			|||||||
        ;
 | 
					        ;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function changePassword({
 | 
				
			||||||
 | 
					    password = '',
 | 
				
			||||||
 | 
					    newPassword = '',
 | 
				
			||||||
 | 
					    newRePassword = ''
 | 
				
			||||||
 | 
					}) {
 | 
				
			||||||
 | 
					    return (dispatch) =>
 | 
				
			||||||
 | 
					        dispatch(changeUserPassword({password, newPassword, newRePassword}))
 | 
				
			||||||
 | 
					            .catch((resp) => {
 | 
				
			||||||
 | 
					                if (resp.errors) {
 | 
				
			||||||
 | 
					                    const errorMessage = resp.errors[Object.keys(resp.errors)[0]];
 | 
				
			||||||
 | 
					                    dispatch(setError(errorMessage));
 | 
				
			||||||
 | 
					                    throw new Error(errorMessage);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                // TODO: log unexpected errors
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					            ;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function register({
 | 
					export function register({
 | 
				
			||||||
    email = '',
 | 
					    email = '',
 | 
				
			||||||
    username = '',
 | 
					    username = '',
 | 
				
			||||||
@@ -67,9 +86,11 @@ export function register({
 | 
				
			|||||||
            dispatch(routeActions.push('/activation'));
 | 
					            dispatch(routeActions.push('/activation'));
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
        .catch((resp) => {
 | 
					        .catch((resp) => {
 | 
				
			||||||
            const errorMessage = resp.errors[Object.keys(resp.errors)[0]];
 | 
					            if (resp.errors) {
 | 
				
			||||||
            dispatch(setError(errorMessage));
 | 
					                const errorMessage = resp.errors[Object.keys(resp.errors)[0]];
 | 
				
			||||||
            throw new Error(errorMessage);
 | 
					                dispatch(setError(errorMessage));
 | 
				
			||||||
 | 
					                throw new Error(errorMessage);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // TODO: log unexpected errors
 | 
					            // TODO: log unexpected errors
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
@@ -151,6 +172,7 @@ export function oAuthComplete(params = {}) {
 | 
				
			|||||||
            if (resp.statusCode === 401 && resp.error === 'access_denied') {
 | 
					            if (resp.statusCode === 401 && resp.error === 'access_denied') {
 | 
				
			||||||
                // user declined permissions
 | 
					                // user declined permissions
 | 
				
			||||||
                return {
 | 
					                return {
 | 
				
			||||||
 | 
					                    success: false,
 | 
				
			||||||
                    redirectUri: resp.redirectUri
 | 
					                    redirectUri: resp.redirectUri
 | 
				
			||||||
                };
 | 
					                };
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
@@ -168,6 +190,18 @@ export function oAuthComplete(params = {}) {
 | 
				
			|||||||
                error.acceptRequired = true;
 | 
					                error.acceptRequired = true;
 | 
				
			||||||
                throw error;
 | 
					                throw error;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        .then((resp) => {
 | 
				
			||||||
 | 
					            if (resp.redirectUri === 'static_page' || resp.redirectUri === 'static_page_with_code') {
 | 
				
			||||||
 | 
					                resp.displayCode = resp.redirectUri === 'static_page_with_code';
 | 
				
			||||||
 | 
					                dispatch(setOAuthCode({
 | 
				
			||||||
 | 
					                    success: resp.success,
 | 
				
			||||||
 | 
					                    code: resp.code,
 | 
				
			||||||
 | 
					                    displayCode: resp.displayCode
 | 
				
			||||||
 | 
					                }));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return resp;
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -224,6 +258,18 @@ export function setOAuthRequest(oauth) {
 | 
				
			|||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const SET_OAUTH_RESULT = 'set_oauth_result';
 | 
				
			||||||
 | 
					export function setOAuthCode(oauth) {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					        type: SET_OAUTH_RESULT,
 | 
				
			||||||
 | 
					        payload: {
 | 
				
			||||||
 | 
					            success: oauth.success,
 | 
				
			||||||
 | 
					            code: oauth.code,
 | 
				
			||||||
 | 
					            displayCode: oauth.displayCode
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const SET_SCOPES = 'set_scopes';
 | 
					export const SET_SCOPES = 'set_scopes';
 | 
				
			||||||
export function setScopes(scopes) {
 | 
					export function setScopes(scopes) {
 | 
				
			||||||
    if (!(scopes instanceof Array)) {
 | 
					    if (!(scopes instanceof Array)) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
import React, { PropTypes } from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { FormattedMessage as Message } from 'react-intl';
 | 
					import { FormattedMessage as Message } from 'react-intl';
 | 
				
			||||||
import Helmet from 'react-helmet';
 | 
					import Helmet from 'react-helmet';
 | 
				
			||||||
@@ -6,20 +6,12 @@ import Helmet from 'react-helmet';
 | 
				
			|||||||
import buttons from 'components/ui/buttons.scss';
 | 
					import buttons from 'components/ui/buttons.scss';
 | 
				
			||||||
import { Input } from 'components/ui/Form';
 | 
					import { Input } from 'components/ui/Form';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import BaseAuthBody from './BaseAuthBody';
 | 
					import BaseAuthBody from 'components/auth/BaseAuthBody';
 | 
				
			||||||
import styles from './activation.scss';
 | 
					import styles from './activation.scss';
 | 
				
			||||||
import messages from './Activation.messages';
 | 
					import messages from './Activation.messages';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Body extends BaseAuthBody {
 | 
					class Body extends BaseAuthBody {
 | 
				
			||||||
    static propTypes = {
 | 
					    static displayName = 'ActivationBody';
 | 
				
			||||||
        ...BaseAuthBody.propTypes,
 | 
					 | 
				
			||||||
        auth: PropTypes.shape({
 | 
					 | 
				
			||||||
            error: PropTypes.string,
 | 
					 | 
				
			||||||
            login: PropTypes.shape({
 | 
					 | 
				
			||||||
                login: PropTypes.stirng
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
@@ -31,7 +23,7 @@ class Body extends BaseAuthBody {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
                    <div className={styles.descriptionText}>
 | 
					                    <div className={styles.descriptionText}>
 | 
				
			||||||
                        <Message {...messages.activationMailWasSent} values={{
 | 
					                        <Message {...messages.activationMailWasSent} values={{
 | 
				
			||||||
                            email: (<b>{this.props.user.email}</b>)
 | 
					                            email: (<b>{this.context.user.email}</b>)
 | 
				
			||||||
                        }} />
 | 
					                        }} />
 | 
				
			||||||
                    </div>
 | 
					                    </div>
 | 
				
			||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
@@ -40,6 +32,7 @@ class Body extends BaseAuthBody {
 | 
				
			|||||||
                        color="blue"
 | 
					                        color="blue"
 | 
				
			||||||
                        className={styles.activationCodeInput}
 | 
					                        className={styles.activationCodeInput}
 | 
				
			||||||
                        autoFocus
 | 
					                        autoFocus
 | 
				
			||||||
 | 
					                        onFocus={this.fixAutoFocus}
 | 
				
			||||||
                        required
 | 
					                        required
 | 
				
			||||||
                        placeholder={messages.enterTheCode}
 | 
					                        placeholder={messages.enterTheCode}
 | 
				
			||||||
                    />
 | 
					                    />
 | 
				
			||||||
@@ -11,8 +11,8 @@ export default class AppInfo extends Component {
 | 
				
			|||||||
    static displayName = 'AppInfo';
 | 
					    static displayName = 'AppInfo';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static propTypes = {
 | 
					    static propTypes = {
 | 
				
			||||||
        name: PropTypes.string.isRequired,
 | 
					        name: PropTypes.string,
 | 
				
			||||||
        description: PropTypes.string.isRequired,
 | 
					        description: PropTypes.string,
 | 
				
			||||||
        onGoToAuth: PropTypes.func.isRequired
 | 
					        onGoToAuth: PropTypes.func.isRequired
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -71,6 +71,10 @@ export default class AuthError extends Component {
 | 
				
			|||||||
        'error.you_must_accept_rules': () => this.errorsMap['error.rulesAgreement_required'](),
 | 
					        'error.you_must_accept_rules': () => this.errorsMap['error.rulesAgreement_required'](),
 | 
				
			||||||
        'error.key_required': () => <Message {...messages.keyRequired} />,
 | 
					        'error.key_required': () => <Message {...messages.keyRequired} />,
 | 
				
			||||||
        'error.key_is_required': () => this.errorsMap['error.key_required'](),
 | 
					        'error.key_is_required': () => this.errorsMap['error.key_required'](),
 | 
				
			||||||
        'error.key_not_exists': () => <Message {...messages.keyNotExists} />
 | 
					        'error.key_not_exists': () => <Message {...messages.keyNotExists} />,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        'error.newPassword_required': () => <Message {...messages.newPasswordRequired} />,
 | 
				
			||||||
 | 
					        'error.newRePassword_required': () => <Message {...messages.newRePasswordRequired} />,
 | 
				
			||||||
 | 
					        'error.newRePassword_does_not_match': () => <Message {...messages.passwordsDoesNotMatch} />
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -31,6 +31,16 @@ export default defineMessages({
 | 
				
			|||||||
        defaultMessage: 'Please enter password'
 | 
					        defaultMessage: 'Please enter password'
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    newPasswordRequired: {
 | 
				
			||||||
 | 
					        id: 'newPasswordRequired',
 | 
				
			||||||
 | 
					        defaultMessage: 'Please enter new password'
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    newRePasswordRequired: {
 | 
				
			||||||
 | 
					        id: 'newRePasswordRequired',
 | 
				
			||||||
 | 
					        defaultMessage: 'Please repeat new password'
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    usernameRequired: {
 | 
					    usernameRequired: {
 | 
				
			||||||
        id: 'usernameRequired',
 | 
					        id: 'usernameRequired',
 | 
				
			||||||
        defaultMessage: 'Username is required'
 | 
					        defaultMessage: 'Username is required'
 | 
				
			||||||
@@ -2,21 +2,17 @@ import React, { PropTypes } from 'react';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import { FormattedMessage as Message } from 'react-intl';
 | 
					import { FormattedMessage as Message } from 'react-intl';
 | 
				
			||||||
import Helmet from 'react-helmet';
 | 
					import Helmet from 'react-helmet';
 | 
				
			||||||
import { Link } from 'react-router';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
import buttons from 'components/ui/buttons.scss';
 | 
					import buttons from 'components/ui/buttons.scss';
 | 
				
			||||||
import { Input } from 'components/ui/Form';
 | 
					import { Input } from 'components/ui/Form';
 | 
				
			||||||
 | 
					import BaseAuthBody from 'components/auth/BaseAuthBody';
 | 
				
			||||||
import BaseAuthBody from './BaseAuthBody';
 | 
					 | 
				
			||||||
import passwordChangedMessages from './PasswordChange.messages';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import icons from 'components/ui/icons.scss';
 | 
					import icons from 'components/ui/icons.scss';
 | 
				
			||||||
import styles from './passwordChange.scss';
 | 
					
 | 
				
			||||||
 | 
					import messages from './ChangePassword.messages';
 | 
				
			||||||
 | 
					import styles from './changePassword.scss';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Body extends BaseAuthBody {
 | 
					class Body extends BaseAuthBody {
 | 
				
			||||||
    static propTypes = {
 | 
					    static displayName = 'ChangePasswordBody';
 | 
				
			||||||
        ...BaseAuthBody.propTypes
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
@@ -28,49 +24,66 @@ class Body extends BaseAuthBody {
 | 
				
			|||||||
                </div>
 | 
					                </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <p className={styles.descriptionText}>
 | 
					                <p className={styles.descriptionText}>
 | 
				
			||||||
                    <Message {...passwordChangedMessages.changePasswordMessage} />
 | 
					                    <Message {...messages.changePasswordMessage} />
 | 
				
			||||||
                </p>
 | 
					                </p>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                <Input {...this.bindField('password')}
 | 
				
			||||||
 | 
					                    icon="key"
 | 
				
			||||||
 | 
					                    color="darkBlue"
 | 
				
			||||||
 | 
					                    type="password"
 | 
				
			||||||
 | 
					                    autoFocus
 | 
				
			||||||
 | 
					                    onFocus={this.fixAutoFocus}
 | 
				
			||||||
 | 
					                    required
 | 
				
			||||||
 | 
					                    placeholder={messages.currentPassword}
 | 
				
			||||||
 | 
					                />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <Input {...this.bindField('newPassword')}
 | 
					                <Input {...this.bindField('newPassword')}
 | 
				
			||||||
                    icon="key"
 | 
					                    icon="key"
 | 
				
			||||||
                    color="darkBlue"
 | 
					                    color="darkBlue"
 | 
				
			||||||
                    autoFocus
 | 
					                    type="password"
 | 
				
			||||||
                    required
 | 
					                    required
 | 
				
			||||||
                    placeholder={passwordChangedMessages.newPassword}
 | 
					                    placeholder={messages.newPassword}
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                <Input {...this.bindField('newRePassword')}
 | 
					                <Input {...this.bindField('newRePassword')}
 | 
				
			||||||
                    icon="key"
 | 
					                    icon="key"
 | 
				
			||||||
                    color="darkBlue"
 | 
					                    color="darkBlue"
 | 
				
			||||||
 | 
					                    type="password"
 | 
				
			||||||
                    required
 | 
					                    required
 | 
				
			||||||
                    placeholder={passwordChangedMessages.newRePassword}
 | 
					                    placeholder={messages.newRePassword}
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
            </div>
 | 
					            </div>
 | 
				
			||||||
        );
 | 
					        );
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function PasswordChange() {
 | 
					export default function ChangePassword() {
 | 
				
			||||||
    return {
 | 
					    const componentsMap = {
 | 
				
			||||||
        Title: () => ( // TODO: separate component for PageTitle
 | 
					        Title: () => ( // TODO: separate component for PageTitle
 | 
				
			||||||
            <Message {...passwordChangedMessages.changePasswordTitle}>
 | 
					            <Message {...messages.changePasswordTitle}>
 | 
				
			||||||
                {(msg) => <span>{msg}<Helmet title={msg} /></span>}
 | 
					                {(msg) => <span>{msg}<Helmet title={msg} /></span>}
 | 
				
			||||||
            </Message>
 | 
					            </Message>
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        Body,
 | 
					        Body,
 | 
				
			||||||
        Footer: () => (
 | 
					        Footer: () => (
 | 
				
			||||||
            <button className={buttons.darkBlue} type="submit">
 | 
					            <button className={buttons.darkBlue} type="submit">
 | 
				
			||||||
                <Message {...passwordChangedMessages.change} />
 | 
					                <Message {...messages.change} />
 | 
				
			||||||
            </button>
 | 
					            </button>
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        Links: (props) => (
 | 
					        Links: (props, context) => (
 | 
				
			||||||
            <a href="#" onClick={(event) => {
 | 
					            <a href="#" onClick={(event) => {
 | 
				
			||||||
                event.preventDefault();
 | 
					                event.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                props.reject();
 | 
					                context.reject();
 | 
				
			||||||
            }}>
 | 
					            }}>
 | 
				
			||||||
                <Message {...passwordChangedMessages.skipThisStep} />
 | 
					                <Message {...messages.skipThisStep} />
 | 
				
			||||||
            </a>
 | 
					            </a>
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    componentsMap.Links.contextTypes = {
 | 
				
			||||||
 | 
					        reject: PropTypes.func.isRequired
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return componentsMap;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -17,6 +17,10 @@ export default defineMessages({
 | 
				
			|||||||
        id: 'change',
 | 
					        id: 'change',
 | 
				
			||||||
        defaultMessage: 'Change'
 | 
					        defaultMessage: 'Change'
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    currentPassword: {
 | 
				
			||||||
 | 
					        id: 'currentPassword',
 | 
				
			||||||
 | 
					        defaultMessage: 'Enter current password'
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    newPassword: {
 | 
					    newPassword: {
 | 
				
			||||||
        id: 'newPassword',
 | 
					        id: 'newPassword',
 | 
				
			||||||
        defaultMessage: 'Enter new password'
 | 
					        defaultMessage: 'Enter new password'
 | 
				
			||||||
							
								
								
									
										122
									
								
								src/components/auth/finish/Finish.jsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								src/components/auth/finish/Finish.jsx
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,122 @@
 | 
				
			|||||||
 | 
					import React, { Component, PropTypes } from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import { connect } from 'react-redux';
 | 
				
			||||||
 | 
					import { FormattedMessage as Message } from 'react-intl';
 | 
				
			||||||
 | 
					import classNames from 'classnames';
 | 
				
			||||||
 | 
					import Helmet from 'react-helmet';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import buttons from 'components/ui/buttons.scss';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import messages from './Finish.messages';
 | 
				
			||||||
 | 
					import styles from './finish.scss';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Finish extends Component {
 | 
				
			||||||
 | 
					    static displayName = 'Finish';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static propTypes = {
 | 
				
			||||||
 | 
					        appName: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					        code: PropTypes.string.isRequired,
 | 
				
			||||||
 | 
					        displayCode: PropTypes.bool,
 | 
				
			||||||
 | 
					        success: PropTypes.bool
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    state = {
 | 
				
			||||||
 | 
					        isCopySupported: document.queryCommandSupported && document.queryCommandSupported('copy')
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    render() {
 | 
				
			||||||
 | 
					        const {appName, code, state, displayCode, success} = this.props;
 | 
				
			||||||
 | 
					        const {isCopySupported} = this.state;
 | 
				
			||||||
 | 
					        const authData = JSON.stringify({
 | 
				
			||||||
 | 
					            auth_code: code,
 | 
				
			||||||
 | 
					            state: state
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        history.pushState(null, null, `#${authData}`);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return (
 | 
				
			||||||
 | 
					            <div className={styles.finishPage}>
 | 
				
			||||||
 | 
					                <Helmet title={authData} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                {success ? (
 | 
				
			||||||
 | 
					                    <div>
 | 
				
			||||||
 | 
					                        <div className={styles.successBackground}></div>
 | 
				
			||||||
 | 
					                        <div className={styles.greenTitle}>
 | 
				
			||||||
 | 
					                            <Message {...messages.authForAppSuccessful} values={{
 | 
				
			||||||
 | 
					                                appName: (<span className={styles.appName}>{appName}</span>)
 | 
				
			||||||
 | 
					                            }} />
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        {displayCode ? (
 | 
				
			||||||
 | 
					                            <div>
 | 
				
			||||||
 | 
					                                <div className={styles.description}>
 | 
				
			||||||
 | 
					                                    <Message {...messages.passCodeToApp} values={{appName}} />
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                                <div className={styles.codeContainer}>
 | 
				
			||||||
 | 
					                                    <div className={styles.code} ref={this.setCode}>{code}</div>
 | 
				
			||||||
 | 
					                                </div>
 | 
				
			||||||
 | 
					                                {isCopySupported ? (
 | 
				
			||||||
 | 
					                                    <button
 | 
				
			||||||
 | 
					                                        className={classNames(buttons.smallButton, buttons.green)}
 | 
				
			||||||
 | 
					                                        onClick={this.handleCopyClick}
 | 
				
			||||||
 | 
					                                    >
 | 
				
			||||||
 | 
					                                        <Message {...messages.copy} />
 | 
				
			||||||
 | 
					                                    </button>
 | 
				
			||||||
 | 
					                                ) : (
 | 
				
			||||||
 | 
					                                    ''
 | 
				
			||||||
 | 
					                                )}
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        ) : (
 | 
				
			||||||
 | 
					                            <div className={styles.description}>
 | 
				
			||||||
 | 
					                                <Message {...messages.waitAppReaction} />
 | 
				
			||||||
 | 
					                            </div>
 | 
				
			||||||
 | 
					                        )}
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                ) : (
 | 
				
			||||||
 | 
					                    <div>
 | 
				
			||||||
 | 
					                        <div className={styles.failBackground}></div>
 | 
				
			||||||
 | 
					                        <div className={styles.redTitle}>
 | 
				
			||||||
 | 
					                            <Message {...messages.authForAppFailed} values={{
 | 
				
			||||||
 | 
					                                appName: (<span className={styles.appName}>{appName}</span>)
 | 
				
			||||||
 | 
					                            }} />
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                        <div className={styles.description}>
 | 
				
			||||||
 | 
					                            <Message {...messages.waitAppReaction} />
 | 
				
			||||||
 | 
					                        </div>
 | 
				
			||||||
 | 
					                    </div>
 | 
				
			||||||
 | 
					                )}
 | 
				
			||||||
 | 
					            </div>
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    handleCopyClick = (event) => {
 | 
				
			||||||
 | 
					        event.preventDefault();
 | 
				
			||||||
 | 
					        // http://stackoverflow.com/a/987376/5184751
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            const selection = window.getSelection();
 | 
				
			||||||
 | 
					            const range = document.createRange();
 | 
				
			||||||
 | 
					            range.selectNodeContents(this.code);
 | 
				
			||||||
 | 
					            selection.removeAllRanges();
 | 
				
			||||||
 | 
					            selection.addRange(range);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const successful = document.execCommand('copy');
 | 
				
			||||||
 | 
					            selection.removeAllRanges();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // TODO: было бы ещё неплохо сделать какую-то анимацию, вроде "Скопировано",
 | 
				
			||||||
 | 
					            // ибо сейчас после клика как-то неубедительно, скопировалось оно или нет
 | 
				
			||||||
 | 
					            console.log('Copying text command was ' + (successful ? 'successful' : 'unsuccessful'));
 | 
				
			||||||
 | 
					        } catch (err) {}
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    setCode = (el) => {
 | 
				
			||||||
 | 
					        this.code = el;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default connect(({auth}) => ({
 | 
				
			||||||
 | 
					    appName: auth.client.name,
 | 
				
			||||||
 | 
					    code: auth.oauth.code,
 | 
				
			||||||
 | 
					    displayCode: auth.oauth.displayCode,
 | 
				
			||||||
 | 
					    state: auth.oauth.state,
 | 
				
			||||||
 | 
					    success: auth.oauth.success
 | 
				
			||||||
 | 
					}))(Finish);
 | 
				
			||||||
							
								
								
									
										29
									
								
								src/components/auth/finish/Finish.messages.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/components/auth/finish/Finish.messages.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,29 @@
 | 
				
			|||||||
 | 
					import { defineMessages } from 'react-intl';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default defineMessages({
 | 
				
			||||||
 | 
					    authForAppSuccessful: {
 | 
				
			||||||
 | 
					        id: 'authForAppSuccessful',
 | 
				
			||||||
 | 
					        defaultMessage: 'Authorization for {appName} was successfully completed'
 | 
				
			||||||
 | 
					        // defaultMessage: 'Авторизация для {appName} успешно выполнена'
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    authForAppFailed: {
 | 
				
			||||||
 | 
					        id: 'authForAppFailed',
 | 
				
			||||||
 | 
					        defaultMessage: 'Authorization for {appName} was failed'
 | 
				
			||||||
 | 
					        // defaultMessage: 'Авторизация для {appName} не удалась'
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    waitAppReaction: {
 | 
				
			||||||
 | 
					        id: 'waitAppReaction',
 | 
				
			||||||
 | 
					        defaultMessage: 'Please, wait till your application response'
 | 
				
			||||||
 | 
					        // defaultMessage: 'Пожалуйста, дождитесь реакции вашего приложения'
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    passCodeToApp: {
 | 
				
			||||||
 | 
					        id: 'passCodeToApp',
 | 
				
			||||||
 | 
					        defaultMessage: 'To complete authorization process, please, provide the following code to {appName}'
 | 
				
			||||||
 | 
					        // defaultMessage: 'Чтобы завершить процесс авторизации, пожалуйста, передай {appName} этот код'
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    copy: {
 | 
				
			||||||
 | 
					        id: 'copy',
 | 
				
			||||||
 | 
					        defaultMessage: 'Copy'
 | 
				
			||||||
 | 
					        // defaultMessage: 'Скопировать'
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
							
								
								
									
										76
									
								
								src/components/auth/finish/finish.scss
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								src/components/auth/finish/finish.scss
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					@import '~components/ui/colors.scss';
 | 
				
			||||||
 | 
					@import '~components/ui/fonts.scss';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.finishPage {
 | 
				
			||||||
 | 
					    font-family: $font-family-title;
 | 
				
			||||||
 | 
					    position: relative;
 | 
				
			||||||
 | 
					    max-width: 515px;
 | 
				
			||||||
 | 
					    padding-top: 40px;
 | 
				
			||||||
 | 
					    margin: 0 auto;
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.iconBackground {
 | 
				
			||||||
 | 
					    position: absolute;
 | 
				
			||||||
 | 
					    top: -15px;
 | 
				
			||||||
 | 
					    transform: translateX(-50%);
 | 
				
			||||||
 | 
					    font-size: 200px;
 | 
				
			||||||
 | 
					    color: #e0d9cf;
 | 
				
			||||||
 | 
					    z-index: -1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.successBackground {
 | 
				
			||||||
 | 
					    composes: checkmark from 'components/ui/icons.scss';
 | 
				
			||||||
 | 
					    @extend .iconBackground;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.failBackground {
 | 
				
			||||||
 | 
					    composes: close from 'components/ui/icons.scss';
 | 
				
			||||||
 | 
					    @extend .iconBackground;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.title {
 | 
				
			||||||
 | 
					    font-size: 22px;
 | 
				
			||||||
 | 
					    margin-bottom: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.greenTitle {
 | 
				
			||||||
 | 
					    composes: title;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    color: $green;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .appName {
 | 
				
			||||||
 | 
					        color: darker($green);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.redTitle {
 | 
				
			||||||
 | 
					    composes: title;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    color: $red;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    .appName {
 | 
				
			||||||
 | 
					        color: darker($red);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.description {
 | 
				
			||||||
 | 
					    font-size: 18px;
 | 
				
			||||||
 | 
					    margin-bottom: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.codeContainer {
 | 
				
			||||||
 | 
					    margin-bottom: 5px;
 | 
				
			||||||
 | 
					    margin-top: 35px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.code {
 | 
				
			||||||
 | 
					    $border: 5px solid darker($green);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    display: inline-block;
 | 
				
			||||||
 | 
					    border-right: $border;
 | 
				
			||||||
 | 
					    border-left: $border;
 | 
				
			||||||
 | 
					    padding: 5px 10px;
 | 
				
			||||||
 | 
					    word-break: break-all;
 | 
				
			||||||
 | 
					    text-align: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
import React, { PropTypes } from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { FormattedMessage as Message } from 'react-intl';
 | 
					import { FormattedMessage as Message } from 'react-intl';
 | 
				
			||||||
import Helmet from 'react-helmet';
 | 
					import Helmet from 'react-helmet';
 | 
				
			||||||
@@ -6,22 +6,13 @@ import Helmet from 'react-helmet';
 | 
				
			|||||||
import buttons from 'components/ui/buttons.scss';
 | 
					import buttons from 'components/ui/buttons.scss';
 | 
				
			||||||
import { Input } from 'components/ui/Form';
 | 
					import { Input } from 'components/ui/Form';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import BaseAuthBody from './BaseAuthBody';
 | 
					import BaseAuthBody from 'components/auth/BaseAuthBody';
 | 
				
			||||||
import messages from './ForgotPassword.messages';
 | 
					import messages from './ForgotPassword.messages';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import styles from './forgotPassword.scss';
 | 
					import styles from './forgotPassword.scss';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Body extends BaseAuthBody {
 | 
					class Body extends BaseAuthBody {
 | 
				
			||||||
    static propTypes = {
 | 
					    static displayName = 'ForgotPasswordBody';
 | 
				
			||||||
        ...BaseAuthBody.propTypes,
 | 
					 | 
				
			||||||
        //login: PropTypes.func.isRequired,
 | 
					 | 
				
			||||||
        auth: PropTypes.shape({
 | 
					 | 
				
			||||||
            error: PropTypes.string,
 | 
					 | 
				
			||||||
            login: PropTypes.shape({
 | 
					 | 
				
			||||||
                email: PropTypes.stirng
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Если юзер вводил своё мыло во время попытки авторизации, то почему бы его сюда автоматически не подставить?
 | 
					    // Если юзер вводил своё мыло во время попытки авторизации, то почему бы его сюда автоматически не подставить?
 | 
				
			||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
@@ -37,6 +28,7 @@ class Body extends BaseAuthBody {
 | 
				
			|||||||
                    icon="envelope"
 | 
					                    icon="envelope"
 | 
				
			||||||
                    color="lightViolet"
 | 
					                    color="lightViolet"
 | 
				
			||||||
                    autoFocus
 | 
					                    autoFocus
 | 
				
			||||||
 | 
					                    onFocus={this.fixAutoFocus}
 | 
				
			||||||
                    required
 | 
					                    required
 | 
				
			||||||
                    placeholder={messages.accountEmail}
 | 
					                    placeholder={messages.accountEmail}
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
@@ -49,7 +41,6 @@ class Body extends BaseAuthBody {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    onFormSubmit() {
 | 
					    onFormSubmit() {
 | 
				
			||||||
        // TODO: обработчик отправки письма с инструкцией по смене аккаунта
 | 
					        // TODO: обработчик отправки письма с инструкцией по смене аккаунта
 | 
				
			||||||
        //this.props.login(this.serialize());
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
import React, { PropTypes } from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { FormattedMessage as Message } from 'react-intl';
 | 
					import { FormattedMessage as Message } from 'react-intl';
 | 
				
			||||||
import Helmet from 'react-helmet';
 | 
					import Helmet from 'react-helmet';
 | 
				
			||||||
@@ -7,20 +7,12 @@ import { Link } from 'react-router';
 | 
				
			|||||||
import buttons from 'components/ui/buttons.scss';
 | 
					import buttons from 'components/ui/buttons.scss';
 | 
				
			||||||
import { Input } from 'components/ui/Form';
 | 
					import { Input } from 'components/ui/Form';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import BaseAuthBody from './BaseAuthBody';
 | 
					import BaseAuthBody from 'components/auth/BaseAuthBody';
 | 
				
			||||||
 | 
					import passwordMessages from 'components/auth/password/Password.messages';
 | 
				
			||||||
import messages from './Login.messages';
 | 
					import messages from './Login.messages';
 | 
				
			||||||
import passwordMessages from './Password.messages';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Body extends BaseAuthBody {
 | 
					class Body extends BaseAuthBody {
 | 
				
			||||||
    static propTypes = {
 | 
					    static displayName = 'LoginBody';
 | 
				
			||||||
        ...BaseAuthBody.propTypes,
 | 
					 | 
				
			||||||
        auth: PropTypes.shape({
 | 
					 | 
				
			||||||
            error: PropTypes.string,
 | 
					 | 
				
			||||||
            login: PropTypes.shape({
 | 
					 | 
				
			||||||
                login: PropTypes.stirng
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
@@ -30,6 +22,7 @@ class Body extends BaseAuthBody {
 | 
				
			|||||||
                <Input {...this.bindField('login')}
 | 
					                <Input {...this.bindField('login')}
 | 
				
			||||||
                    icon="envelope"
 | 
					                    icon="envelope"
 | 
				
			||||||
                    autoFocus
 | 
					                    autoFocus
 | 
				
			||||||
 | 
					                    onFocus={this.fixAutoFocus}
 | 
				
			||||||
                    required
 | 
					                    required
 | 
				
			||||||
                    placeholder={messages.emailOrUsername}
 | 
					                    placeholder={messages.emailOrUsername}
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
import React, { PropTypes } from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { FormattedMessage as Message } from 'react-intl';
 | 
					import { FormattedMessage as Message } from 'react-intl';
 | 
				
			||||||
import Helmet from 'react-helmet';
 | 
					import Helmet from 'react-helmet';
 | 
				
			||||||
@@ -8,24 +8,15 @@ import buttons from 'components/ui/buttons.scss';
 | 
				
			|||||||
import icons from 'components/ui/icons.scss';
 | 
					import icons from 'components/ui/icons.scss';
 | 
				
			||||||
import { Input, Checkbox } from 'components/ui/Form';
 | 
					import { Input, Checkbox } from 'components/ui/Form';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import BaseAuthBody from './BaseAuthBody';
 | 
					import BaseAuthBody from 'components/auth/BaseAuthBody';
 | 
				
			||||||
import styles from './password.scss';
 | 
					import styles from './password.scss';
 | 
				
			||||||
import messages from './Password.messages';
 | 
					import messages from './Password.messages';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Body extends BaseAuthBody {
 | 
					class Body extends BaseAuthBody {
 | 
				
			||||||
    static propTypes = {
 | 
					    static displayName = 'PasswordBody';
 | 
				
			||||||
        ...BaseAuthBody.propTypes,
 | 
					 | 
				
			||||||
        auth: PropTypes.shape({
 | 
					 | 
				
			||||||
            error: PropTypes.string,
 | 
					 | 
				
			||||||
            login: PropTypes.shape({
 | 
					 | 
				
			||||||
                login: PropTypes.stirng,
 | 
					 | 
				
			||||||
                password: PropTypes.stirng
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
        const {user} = this.props;
 | 
					        const {user} = this.context;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <div>
 | 
					            <div>
 | 
				
			||||||
@@ -46,6 +37,7 @@ class Body extends BaseAuthBody {
 | 
				
			|||||||
                    icon="key"
 | 
					                    icon="key"
 | 
				
			||||||
                    type="password"
 | 
					                    type="password"
 | 
				
			||||||
                    autoFocus
 | 
					                    autoFocus
 | 
				
			||||||
 | 
					                    onFocus={this.fixAutoFocus}
 | 
				
			||||||
                    required
 | 
					                    required
 | 
				
			||||||
                    placeholder={messages.accountPassword}
 | 
					                    placeholder={messages.accountPassword}
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
@@ -7,22 +7,16 @@ import buttons from 'components/ui/buttons.scss';
 | 
				
			|||||||
import icons from 'components/ui/icons.scss';
 | 
					import icons from 'components/ui/icons.scss';
 | 
				
			||||||
import { PanelBodyHeader } from 'components/ui/Panel';
 | 
					import { PanelBodyHeader } from 'components/ui/Panel';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import BaseAuthBody from './BaseAuthBody';
 | 
					import BaseAuthBody from 'components/auth/BaseAuthBody';
 | 
				
			||||||
import styles from './permissions.scss';
 | 
					import styles from './permissions.scss';
 | 
				
			||||||
import messages from './Permissions.messages';
 | 
					import messages from './Permissions.messages';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Body extends BaseAuthBody {
 | 
					class Body extends BaseAuthBody {
 | 
				
			||||||
    static propTypes = {
 | 
					    static displayName = 'PermissionsBody';
 | 
				
			||||||
        ...BaseAuthBody.propTypes,
 | 
					 | 
				
			||||||
        auth: PropTypes.shape({
 | 
					 | 
				
			||||||
            error: PropTypes.string,
 | 
					 | 
				
			||||||
            scopes: PropTypes.array.isRequired
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
        const {user} = this.props;
 | 
					        const {user} = this.context;
 | 
				
			||||||
        const scopes = this.props.auth.scopes;
 | 
					        const scopes = this.context.auth.scopes;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <div>
 | 
					            <div>
 | 
				
			||||||
@@ -61,7 +55,7 @@ class Body extends BaseAuthBody {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default function Permissions() {
 | 
					export default function Permissions() {
 | 
				
			||||||
    return {
 | 
					    const componentsMap = {
 | 
				
			||||||
        Title: () => ( // TODO: separate component for PageTitle
 | 
					        Title: () => ( // TODO: separate component for PageTitle
 | 
				
			||||||
            <Message {...messages.permissionsTitle}>
 | 
					            <Message {...messages.permissionsTitle}>
 | 
				
			||||||
                {(msg) => <span>{msg}<Helmet title={msg} /></span>}
 | 
					                {(msg) => <span>{msg}<Helmet title={msg} /></span>}
 | 
				
			||||||
@@ -73,14 +67,20 @@ export default function Permissions() {
 | 
				
			|||||||
                <Message {...messages.approve} />
 | 
					                <Message {...messages.approve} />
 | 
				
			||||||
            </button>
 | 
					            </button>
 | 
				
			||||||
        ),
 | 
					        ),
 | 
				
			||||||
        Links: (props) => (
 | 
					        Links: (props, context) => (
 | 
				
			||||||
            <a href="#" onClick={(event) => {
 | 
					            <a href="#" onClick={(event) => {
 | 
				
			||||||
                event.preventDefault();
 | 
					                event.preventDefault();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                props.reject();
 | 
					                context.reject();
 | 
				
			||||||
            }}>
 | 
					            }}>
 | 
				
			||||||
                <Message {...messages.decline} />
 | 
					                <Message {...messages.decline} />
 | 
				
			||||||
            </a>
 | 
					            </a>
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    componentsMap.Links.contextTypes = {
 | 
				
			||||||
 | 
					        reject: PropTypes.func.isRequired
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return componentsMap;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
import { combineReducers } from 'redux';
 | 
					import { combineReducers } from 'redux';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { ERROR, SET_CLIENT, SET_OAUTH, SET_SCOPES } from './actions';
 | 
					import { ERROR, SET_CLIENT, SET_OAUTH, SET_OAUTH_RESULT, SET_SCOPES } from './actions';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default combineReducers({
 | 
					export default combineReducers({
 | 
				
			||||||
    error,
 | 
					    error,
 | 
				
			||||||
@@ -56,6 +56,14 @@ function oauth(
 | 
				
			|||||||
                state: payload.state
 | 
					                state: payload.state
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case SET_OAUTH_RESULT:
 | 
				
			||||||
 | 
					            return {
 | 
				
			||||||
 | 
					                ...state,
 | 
				
			||||||
 | 
					                success: payload.success,
 | 
				
			||||||
 | 
					                code: payload.code,
 | 
				
			||||||
 | 
					                displayCode: payload.displayCode
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        default:
 | 
					        default:
 | 
				
			||||||
            return state;
 | 
					            return state;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
import React, { PropTypes } from 'react';
 | 
					import React from 'react';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import { FormattedMessage as Message } from 'react-intl';
 | 
					import { FormattedMessage as Message } from 'react-intl';
 | 
				
			||||||
import Helmet from 'react-helmet';
 | 
					import Helmet from 'react-helmet';
 | 
				
			||||||
@@ -6,27 +6,14 @@ import Helmet from 'react-helmet';
 | 
				
			|||||||
import buttons from 'components/ui/buttons.scss';
 | 
					import buttons from 'components/ui/buttons.scss';
 | 
				
			||||||
import { Input, Checkbox } from 'components/ui/Form';
 | 
					import { Input, Checkbox } from 'components/ui/Form';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import BaseAuthBody from './BaseAuthBody';
 | 
					import BaseAuthBody from 'components/auth/BaseAuthBody';
 | 
				
			||||||
 | 
					import activationMessages from 'components/auth/activation/Activation.messages';
 | 
				
			||||||
import messages from './Register.messages';
 | 
					import messages from './Register.messages';
 | 
				
			||||||
import activationMessages from './Activation.messages';
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: password and username can be validate for length and sameness
 | 
					// TODO: password and username can be validate for length and sameness
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Body extends BaseAuthBody {
 | 
					class Body extends BaseAuthBody {
 | 
				
			||||||
    static propTypes = {
 | 
					    static displayName = 'RegisterBody';
 | 
				
			||||||
        ...BaseAuthBody.propTypes,
 | 
					 | 
				
			||||||
        register: PropTypes.func.isRequired,
 | 
					 | 
				
			||||||
        auth: PropTypes.shape({
 | 
					 | 
				
			||||||
            error: PropTypes.string,
 | 
					 | 
				
			||||||
            register: PropTypes.shape({
 | 
					 | 
				
			||||||
                email: PropTypes.string,
 | 
					 | 
				
			||||||
                username: PropTypes.stirng,
 | 
					 | 
				
			||||||
                password: PropTypes.stirng,
 | 
					 | 
				
			||||||
                rePassword: PropTypes.stirng,
 | 
					 | 
				
			||||||
                rulesAgreement: PropTypes.boolean
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
@@ -38,6 +25,7 @@ class Body extends BaseAuthBody {
 | 
				
			|||||||
                    color="blue"
 | 
					                    color="blue"
 | 
				
			||||||
                    type="text"
 | 
					                    type="text"
 | 
				
			||||||
                    autoFocus
 | 
					                    autoFocus
 | 
				
			||||||
 | 
					                    onFocus={this.fixAutoFocus}
 | 
				
			||||||
                    required
 | 
					                    required
 | 
				
			||||||
                    placeholder={messages.yourNickname}
 | 
					                    placeholder={messages.yourNickname}
 | 
				
			||||||
                />
 | 
					                />
 | 
				
			||||||
@@ -14,7 +14,7 @@ export class Input extends Component {
 | 
				
			|||||||
            id: PropTypes.string
 | 
					            id: PropTypes.string
 | 
				
			||||||
        }),
 | 
					        }),
 | 
				
			||||||
        icon: PropTypes.string,
 | 
					        icon: PropTypes.string,
 | 
				
			||||||
        color: PropTypes.oneOf(['green', 'blue', 'red'])
 | 
					        color: PropTypes.oneOf(['green', 'blue', 'red', 'lightViolet', 'darkBlue'])
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    static contextTypes = {
 | 
					    static contextTypes = {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,7 +24,6 @@ export function logout() {
 | 
				
			|||||||
    return setUser({isGuest: true});
 | 
					    return setUser({isGuest: true});
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
export function fetchUserData() {
 | 
					export function fetchUserData() {
 | 
				
			||||||
    return (dispatch) =>
 | 
					    return (dispatch) =>
 | 
				
			||||||
        request.get('/api/accounts/current')
 | 
					        request.get('/api/accounts/current')
 | 
				
			||||||
@@ -45,6 +44,26 @@ export function fetchUserData() {
 | 
				
			|||||||
        });
 | 
					        });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function changePassword({
 | 
				
			||||||
 | 
					    password = '',
 | 
				
			||||||
 | 
					    newPassword = '',
 | 
				
			||||||
 | 
					    newRePassword = ''
 | 
				
			||||||
 | 
					}) {
 | 
				
			||||||
 | 
					    return (dispatch) =>
 | 
				
			||||||
 | 
					        request.post(
 | 
				
			||||||
 | 
					            '/api/accounts/change-password',
 | 
				
			||||||
 | 
					            {password, newPassword, newRePassword}
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        .then((resp) => {
 | 
				
			||||||
 | 
					            dispatch(updateUser({
 | 
				
			||||||
 | 
					                shouldChangePassword: false
 | 
				
			||||||
 | 
					            }));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            return resp;
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        ;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export function authenticate(token) {
 | 
					export function authenticate(token) {
 | 
				
			||||||
    if (!token || token.split('.').length !== 3) {
 | 
					    if (!token || token.split('.').length !== 3) {
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										13
									
								
								src/index.js
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								src/index.js
									
									
									
									
									
								
							@@ -22,19 +22,6 @@ import routesFactory from 'routes';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import 'index.scss';
 | 
					import 'index.scss';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TODO: временная мера против Intl, который беспощадно спамит консоль
 | 
					 | 
				
			||||||
if (process.env.NODE_ENV !== 'production') {
 | 
					 | 
				
			||||||
    const originalConsoleError = console.error;
 | 
					 | 
				
			||||||
    if (console.error === originalConsoleError) {
 | 
					 | 
				
			||||||
        console.error = (...args) => {
 | 
					 | 
				
			||||||
            if (args[0].indexOf('[React Intl] Missing message:') === 0) {
 | 
					 | 
				
			||||||
                return;
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            originalConsoleError.call(console, ...args);
 | 
					 | 
				
			||||||
        };
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
const reducer = combineReducers({
 | 
					const reducer = combineReducers({
 | 
				
			||||||
    ...reducers,
 | 
					    ...reducers,
 | 
				
			||||||
    routing: routeReducer
 | 
					    routing: routeReducer
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@ import React, { Component, PropTypes } from 'react';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import { connect } from 'react-redux';
 | 
					import { connect } from 'react-redux';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import AppInfo from 'components/auth/AppInfo';
 | 
					import AppInfo from 'components/auth/appInfo/AppInfo';
 | 
				
			||||||
import PanelTransition from 'components/auth/PanelTransition';
 | 
					import PanelTransition from 'components/auth/PanelTransition';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import styles from './auth.scss';
 | 
					import styles from './auth.scss';
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,17 +4,9 @@ import { connect } from 'react-redux';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import ProfilePage from 'pages/profile/ProfilePage';
 | 
					import ProfilePage from 'pages/profile/ProfilePage';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import authFlow from 'services/authFlow';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class IndexPage extends Component {
 | 
					class IndexPage extends Component {
 | 
				
			||||||
    displayName = 'IndexPage';
 | 
					    displayName = 'IndexPage';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    componentWillMount() {
 | 
					 | 
				
			||||||
        if (this.props.user.isGuest) {
 | 
					 | 
				
			||||||
            authFlow.login();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    render() {
 | 
					    render() {
 | 
				
			||||||
        return (
 | 
					        return (
 | 
				
			||||||
            <div>
 | 
					            <div>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,14 +8,15 @@ import AuthPage from 'pages/auth/AuthPage';
 | 
				
			|||||||
import { authenticate } from 'components/user/actions';
 | 
					import { authenticate } from 'components/user/actions';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import OAuthInit from 'components/auth/OAuthInit';
 | 
					import OAuthInit from 'components/auth/OAuthInit';
 | 
				
			||||||
import Register from 'components/auth/Register';
 | 
					import Register from 'components/auth/register/Register';
 | 
				
			||||||
import Login from 'components/auth/Login';
 | 
					import Login from 'components/auth/login/Login';
 | 
				
			||||||
import Permissions from 'components/auth/Permissions';
 | 
					import Permissions from 'components/auth/permissions/Permissions';
 | 
				
			||||||
import Activation from 'components/auth/Activation';
 | 
					import Activation from 'components/auth/activation/Activation';
 | 
				
			||||||
import Password from 'components/auth/Password';
 | 
					import Password from 'components/auth/password/Password';
 | 
				
			||||||
import Logout from 'components/auth/Logout';
 | 
					import Logout from 'components/auth/Logout';
 | 
				
			||||||
import PasswordChange from 'components/auth/PasswordChange';
 | 
					import ChangePassword from 'components/auth/changePassword/ChangePassword';
 | 
				
			||||||
import ForgotPassword from 'components/auth/ForgotPassword';
 | 
					import ForgotPassword from 'components/auth/forgotPassword/ForgotPassword';
 | 
				
			||||||
 | 
					import Finish from 'components/auth/finish/Finish';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import authFlow from 'services/authFlow';
 | 
					import authFlow from 'services/authFlow';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -34,7 +35,7 @@ export default function routesFactory(store) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    return (
 | 
					    return (
 | 
				
			||||||
        <Route path="/" component={RootPage}>
 | 
					        <Route path="/" component={RootPage}>
 | 
				
			||||||
            <IndexRoute component={IndexPage} />
 | 
					            <IndexRoute component={IndexPage} {...onEnter} />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            <Route path="oauth" component={OAuthInit} {...onEnter} />
 | 
					            <Route path="oauth" component={OAuthInit} {...onEnter} />
 | 
				
			||||||
            <Route path="logout" component={Logout} {...onEnter} />
 | 
					            <Route path="logout" component={Logout} {...onEnter} />
 | 
				
			||||||
@@ -45,7 +46,8 @@ export default function routesFactory(store) {
 | 
				
			|||||||
                <Route path="/register" components={new Register()} {...onEnter} />
 | 
					                <Route path="/register" components={new Register()} {...onEnter} />
 | 
				
			||||||
                <Route path="/activation" components={new Activation()} {...onEnter} />
 | 
					                <Route path="/activation" components={new Activation()} {...onEnter} />
 | 
				
			||||||
                <Route path="/oauth/permissions" components={new Permissions()} {...onEnter} />
 | 
					                <Route path="/oauth/permissions" components={new Permissions()} {...onEnter} />
 | 
				
			||||||
                <Route path="/password-change" components={new PasswordChange()} {...onEnter} />
 | 
					                <Route path="/oauth/finish" component={Finish} {...onEnter} />
 | 
				
			||||||
 | 
					                <Route path="/change-password" components={new ChangePassword()} {...onEnter} />
 | 
				
			||||||
                <Route path="/forgot-password" components={new ForgotPassword()} {...onEnter} />
 | 
					                <Route path="/forgot-password" components={new ForgotPassword()} {...onEnter} />
 | 
				
			||||||
            </Route>
 | 
					            </Route>
 | 
				
			||||||
        </Route>
 | 
					        </Route>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -23,7 +23,6 @@ export default class AuthFlow {
 | 
				
			|||||||
            const {routing} = this.getState();
 | 
					            const {routing} = this.getState();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            if (routing.location.pathname !== route) {
 | 
					            if (routing.location.pathname !== route) {
 | 
				
			||||||
                this.ignoreRequest = true; // TODO: remove me
 | 
					 | 
				
			||||||
                if (this.replace) {
 | 
					                if (this.replace) {
 | 
				
			||||||
                    this.replace(route);
 | 
					                    this.replace(route);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -62,10 +61,10 @@ export default class AuthFlow {
 | 
				
			|||||||
            throw new Error('State is required');
 | 
					            throw new Error('State is required');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (this.state instanceof state.constructor) {
 | 
					        // if (this.state instanceof state.constructor) {
 | 
				
			||||||
            // already in this state
 | 
					        //     // already in this state
 | 
				
			||||||
            return;
 | 
					        //     return;
 | 
				
			||||||
        }
 | 
					        // }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.state && this.state.leave(this);
 | 
					        this.state && this.state.leave(this);
 | 
				
			||||||
        this.state = state;
 | 
					        this.state = state;
 | 
				
			||||||
@@ -74,9 +73,10 @@ export default class AuthFlow {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    handleRequest(path, replace) {
 | 
					    handleRequest(path, replace) {
 | 
				
			||||||
        this.replace = replace;
 | 
					        this.replace = replace;
 | 
				
			||||||
        if (this.ignoreRequest) {
 | 
					
 | 
				
			||||||
            this.ignoreRequest = false;
 | 
					        if (path === '/') {
 | 
				
			||||||
            return;
 | 
					            // reset oauth data if user tried to navigate to index route
 | 
				
			||||||
 | 
					            this.run('setOAuthRequest', {});
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        switch (path) {
 | 
					        switch (path) {
 | 
				
			||||||
@@ -92,11 +92,13 @@ export default class AuthFlow {
 | 
				
			|||||||
                this.setState(new ForgotPasswordState());
 | 
					                this.setState(new ForgotPasswordState());
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            case '/':
 | 
				
			||||||
            case '/login':
 | 
					            case '/login':
 | 
				
			||||||
            case '/password':
 | 
					            case '/password':
 | 
				
			||||||
            case '/activation':
 | 
					            case '/activation':
 | 
				
			||||||
            case '/password-change':
 | 
					            case '/change-password':
 | 
				
			||||||
            case '/oauth/permissions':
 | 
					            case '/oauth/permissions':
 | 
				
			||||||
 | 
					            case '/oauth/finish':
 | 
				
			||||||
                this.setState(new LoginState());
 | 
					                this.setState(new LoginState());
 | 
				
			||||||
                break;
 | 
					                break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,7 +3,12 @@ import CompleteState from './CompleteState';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export default class ChangePasswordState extends AbstractState {
 | 
					export default class ChangePasswordState extends AbstractState {
 | 
				
			||||||
    enter(context) {
 | 
					    enter(context) {
 | 
				
			||||||
        context.navigate('/password-change');
 | 
					        context.navigate('/change-password');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    resolve(context, payload) {
 | 
				
			||||||
 | 
					        context.run('changePassword', payload)
 | 
				
			||||||
 | 
					            .then(() => context.setState(new CompleteState()));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    reject(context) {
 | 
					    reject(context) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,8 +3,18 @@ import LoginState from './LoginState';
 | 
				
			|||||||
import PermissionsState from './PermissionsState';
 | 
					import PermissionsState from './PermissionsState';
 | 
				
			||||||
import ActivationState from './ActivationState';
 | 
					import ActivationState from './ActivationState';
 | 
				
			||||||
import ChangePasswordState from './ChangePasswordState';
 | 
					import ChangePasswordState from './ChangePasswordState';
 | 
				
			||||||
 | 
					import FinishState from './FinishState';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class CompleteState extends AbstractState {
 | 
					export default class CompleteState extends AbstractState {
 | 
				
			||||||
 | 
					    constructor(options = {}) {
 | 
				
			||||||
 | 
					        super(options);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ('accept' in options) {
 | 
				
			||||||
 | 
					            this.isPermissionsAccepted = options.accept;
 | 
				
			||||||
 | 
					            this.isUserReviewedPermissions = true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    enter(context) {
 | 
					    enter(context) {
 | 
				
			||||||
        const {auth, user} = context.getState();
 | 
					        const {auth, user} = context.getState();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -14,17 +24,33 @@ export default class CompleteState extends AbstractState {
 | 
				
			|||||||
            context.setState(new ActivationState());
 | 
					            context.setState(new ActivationState());
 | 
				
			||||||
        } else if (user.shouldChangePassword) {
 | 
					        } else if (user.shouldChangePassword) {
 | 
				
			||||||
            context.setState(new ChangePasswordState());
 | 
					            context.setState(new ChangePasswordState());
 | 
				
			||||||
        } else if (auth.oauth) {
 | 
					        } else if (auth.oauth && auth.oauth.clientId) {
 | 
				
			||||||
            context.run('oAuthComplete').then((resp) => {
 | 
					            if (auth.oauth.code) {
 | 
				
			||||||
                location.href = resp.redirectUri;
 | 
					                context.setState(new FinishState());
 | 
				
			||||||
            }, (resp) => {
 | 
					            } else {
 | 
				
			||||||
                // TODO
 | 
					                let data = {};
 | 
				
			||||||
                if (resp.unauthorized) {
 | 
					                if (this.isUserReviewedPermissions) {
 | 
				
			||||||
                    context.setState(new LoginState());
 | 
					                    data.accept = this.isPermissionsAccepted;
 | 
				
			||||||
                } else if (resp.acceptRequired) {
 | 
					 | 
				
			||||||
                    context.setState(new PermissionsState());
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            });
 | 
					                context.run('oAuthComplete', data).then((resp) => {
 | 
				
			||||||
 | 
					                    switch (resp.redirectUri) {
 | 
				
			||||||
 | 
					                        case 'static_page':
 | 
				
			||||||
 | 
					                        case 'static_page_with_code':
 | 
				
			||||||
 | 
					                            context.setState(new FinishState());
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                        default:
 | 
				
			||||||
 | 
					                            location.href = resp.redirectUri;
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }, (resp) => {
 | 
				
			||||||
 | 
					                    // TODO
 | 
				
			||||||
 | 
					                    if (resp.unauthorized) {
 | 
				
			||||||
 | 
					                        context.setState(new LoginState());
 | 
				
			||||||
 | 
					                    } else if (resp.acceptRequired) {
 | 
				
			||||||
 | 
					                        context.setState(new PermissionsState());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                });
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            context.navigate('/');
 | 
					            context.navigate('/');
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										7
									
								
								src/services/authFlow/FinishState.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								src/services/authFlow/FinishState.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,7 @@
 | 
				
			|||||||
 | 
					import AbstractState from './AbstractState';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default class CompleteState extends AbstractState {
 | 
				
			||||||
 | 
					    enter(context) {
 | 
				
			||||||
 | 
					        context.navigate('/oauth/finish');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -1,4 +1,5 @@
 | 
				
			|||||||
import AbstractState from './AbstractState';
 | 
					import AbstractState from './AbstractState';
 | 
				
			||||||
 | 
					import CompleteState from './CompleteState';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default class PermissionsState extends AbstractState {
 | 
					export default class PermissionsState extends AbstractState {
 | 
				
			||||||
    enter(context) {
 | 
					    enter(context) {
 | 
				
			||||||
@@ -14,8 +15,8 @@ export default class PermissionsState extends AbstractState {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    process(context, accept) {
 | 
					    process(context, accept) {
 | 
				
			||||||
        context.run('oAuthComplete', {
 | 
					        context.setState(new CompleteState({
 | 
				
			||||||
            accept
 | 
					            accept
 | 
				
			||||||
        }).then((resp) => location.href = resp.redirectUri);
 | 
					        }));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user