2017-06-08_509bba0/509bba0_unpacked_with_node_.../discord_app/components/Login.js

191 lines
6.4 KiB
JavaScript
Executable File

import React from 'react';
import PureRenderMixin from 'react-addons-pure-render-mixin';
import {Link} from 'react-router';
import classNames from 'classnames';
import Flux from '../lib/flux';
import BaseAuthForm from './BaseAuthForm';
import ConfirmModal from './ConfirmModal';
import SaveButton from './common/SaveButton';
import AuthenticationActionCreators from '../actions/AuthenticationActionCreators';
import ModalActionCreators from '../actions/ModalActionCreators';
import AuthenticationStore from '../stores/AuthenticationStore';
import querystring from 'querystring';
import i18n from '../i18n';
import RouterUtils from '../utils/RouterUtils';
import {ActionTypes, ChannelTypes, Endpoints, Routes, LoginStates} from '../Constants';
import {isSafeRedirect} from '../utils/RedirectUtils';
import HTTPUtils from '../utils/HTTPUtils';
import Dispatcher from '../Dispatcher';
const CODE_REF = 'code';
const Login = React.createClass({
mixins: [PureRenderMixin, Flux.StoreListenerMixin(AuthenticationStore)],
getStateFromStores() {
return {
loginStatus: AuthenticationStore.getLoginStatus(),
mfaTicket: AuthenticationStore.getMFATicket(),
errors: AuthenticationStore.getErrors(),
authenticated: AuthenticationStore.isAuthenticated(),
};
},
componentDidMount() {
AuthenticationActionCreators.fingerprint('Login');
this.loginOrSSO();
},
componentDidUpdate() {
this.loginOrSSO();
},
loginOrSSO() {
if (this.state.authenticated) {
// We should double-check that the user's token is *valid* before forwarding them to their destination
HTTPUtils.get({
url: Endpoints.ME,
}).then(
() => {
const {query} = this.props.location;
if (query['redirect_to'] && isSafeRedirect(query['redirect_to'])) {
window.location = query['redirect_to'];
} else if (query['service'] == null) {
RouterUtils.transitionTo(Routes.ME);
} else {
const prefix = location.protocol + process.env.API_ENDPOINT + Endpoints.SSO;
window.location = `${prefix}?token=${AuthenticationStore.getToken()}&${querystring.stringify(query)}`;
}
},
() => {
Dispatcher.dispatch({type: ActionTypes.LOGOUT});
}
);
}
},
handleSubmit(e) {
e.preventDefault();
AuthenticationActionCreators.login(this.refs.email.value, this.refs.password.value);
},
handleTokenSubmit(e) {
e.preventDefault();
this.setState({loggingInViaMFA: true});
AuthenticationActionCreators.loginMFA(this.refs[CODE_REF].value, this.state.mfaTicket);
},
handleForgotPassword() {
const email = this.refs.email.value;
AuthenticationActionCreators.forgotPassword(email, () => {
ModalActionCreators.push(props => {
return (
<ConfirmModal
header={i18n.Messages.EMAIL_VERIFICATION_INSTRUCTIONS_HEADER}
confirmText={i18n.Messages.OKAY}
red={false}
{...props}>
<p>{i18n.Messages.EMAIL_VERIFICATION_INSTRUCTIONS_BODY.format({email})}</p>
</ConfirmModal>
);
});
});
},
render() {
const {errors, loginStatus} = this.state;
const hasError = field => errors[field] != null;
const renderError = field => {
if (hasError(field)) {
return <span className="error">({errors[field]})</span>;
} else {
return null;
}
};
let header;
let registerLink;
const invite = this.props.invite;
if (invite != null) {
const channelName = invite.channel.type === ChannelTypes.GUILD_TEXT
? `#${invite.channel.name}`
: invite.channel.name;
const serverName = invite.guild.name;
header = (
<h1>
{i18n.Messages.LOGIN_TITLE}
<p>{i18n.Messages.INSTANT_INVITE_RESOLVED_BODY.format({channelName, serverName})}</p>
</h1>
);
registerLink = {
pathname: Routes.INVITE(invite.code),
query: {
mode: 'register',
},
};
} else {
header = <h1>{i18n.Messages.LOGIN_TITLE}</h1>;
registerLink = {
pathname: Routes.REGISTER,
query: this.props.location.query,
};
}
if (this.state.mfaTicket || this.state.loggingInViaMFA) {
return (
<BaseAuthForm onSubmit={this.handleTokenSubmit} invite={invite}>
{header}
<div className={classNames({'control-group': true, error: hasError(CODE_REF)})}>
<label htmlFor="mfa-code">{i18n.Messages.TWO_FA_ENTER_TOKEN_LABEL} {renderError(CODE_REF)}</label>
<input
id="mfa-code"
key={CODE_REF}
ref={CODE_REF}
type="text"
autoComplete="off"
spellCheck="false"
maxLength={10}
autoFocus
defaultValue=""
/>
<p className="token-tip">{i18n.Messages.TWO_FA_ENTER_TOKEN_BODY}</p>
</div>
<SaveButton className="btn btn-primary" disabled={loginStatus === LoginStates.LOGGING_IN_MFA}>
{i18n.Messages.LOGIN}
</SaveButton>
</BaseAuthForm>
);
}
return (
<BaseAuthForm onSubmit={this.handleSubmit} invite={invite}>
{header}
<div className={classNames({'control-group': true, error: hasError('email')})}>
<label htmlFor="register-email">{i18n.Messages.FORM_LABEL_EMAIL} {renderError('email')}</label>
<input id="register-email" ref="email" type="email" autoComplete="off" spellCheck="false" autoFocus />
</div>
<div className={classNames({'control-group': true, error: hasError('password')})}>
<label htmlFor="register-password">{i18n.Messages.FORM_LABEL_PASSWORD} {renderError('password')}</label>
<input id="register-password" ref="password" type="password" autoComplete="off" spellCheck="false" />
<button className="btn-forgot-password" type="button" onClick={this.handleForgotPassword}>
{i18n.Messages.FORGOT_PASSWORD}
</button>
</div>
<SaveButton className="btn btn-primary" disabled={loginStatus === LoginStates.LOGGING_IN}>
{i18n.Messages.LOGIN}
</SaveButton>
<footer>
{i18n.Messages.NEED_ACCOUNT} <Link to={registerLink}>{i18n.Messages.REGISTER}</Link>
</footer>
</BaseAuthForm>
);
},
});
export default Login;
// WEBPACK FOOTER //
// ./discord_app/components/Login.js