mirror of
https://github.com/wassname/talk.git
synced 2026-07-02 10:30:00 +08:00
Auth Refactor part 2
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
|
||||
.header, .cta, .success {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.success {
|
||||
cursor: pointer;
|
||||
padding: 8px 14px;
|
||||
}
|
||||
|
||||
.signInLink {
|
||||
color: blue;
|
||||
font-weight: normal;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.signInLink:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styles from './ForgotPassword.css';
|
||||
import { Button, TextField, Alert, Success } from 'coral-ui';
|
||||
|
||||
class ForgotPassword extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
handleEmailChange = e => this.props.onEmailChange(e.target.value);
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
this.props.onSubmit();
|
||||
};
|
||||
|
||||
handleSignInLink = e => {
|
||||
e.preventDefault();
|
||||
this.props.onSignInLink();
|
||||
};
|
||||
|
||||
renderSuccess() {
|
||||
return (
|
||||
<div className={styles.success} onClick={this.handleSignInLink}>
|
||||
{this.props.success}{' '}
|
||||
<a
|
||||
className={styles.signInLink}
|
||||
href="#"
|
||||
onClick={this.handleSignInLink}
|
||||
>
|
||||
Sign in
|
||||
</a>
|
||||
<Success />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
renderForm() {
|
||||
const { email, errorMessage } = this.props;
|
||||
return (
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
{errorMessage && <Alert>{errorMessage}</Alert>}
|
||||
<TextField
|
||||
label="Email Address"
|
||||
value={email}
|
||||
onChange={this.handleEmailChange}
|
||||
/>
|
||||
<Button type="submit" cStyle="black" full>
|
||||
Reset Password
|
||||
</Button>
|
||||
<p className={styles.cta}>
|
||||
Go back to{' '}
|
||||
<a
|
||||
href="#"
|
||||
className={styles.signInLink}
|
||||
onClick={this.handleSignInLink}
|
||||
>
|
||||
Sign In
|
||||
</a>
|
||||
.
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
return this.props.success ? this.renderSuccess() : this.renderForm();
|
||||
}
|
||||
}
|
||||
|
||||
ForgotPassword.propTypes = {
|
||||
success: PropTypes.bool.isRequired,
|
||||
email: PropTypes.string.isRequired,
|
||||
onEmailChange: PropTypes.func.isRequired,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
errorMessage: PropTypes.string.isRequired,
|
||||
onSignInLink: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default ForgotPassword;
|
||||
@@ -1,37 +1,18 @@
|
||||
.layout {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.loginLayout {
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.loginHeader, .loginCTA, .forgotPasswordCTA, .passwordRequestSuccess {
|
||||
.header, .cta {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.forgotPasswordLink, .signInLink {
|
||||
color: blue;
|
||||
font-weight: normal;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.forgotPasswordLink:hover, .signInLink:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.layout h1 {
|
||||
font-size: 40px;
|
||||
}
|
||||
|
||||
.loginHeader {
|
||||
.header {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.passwordRequestSuccess {
|
||||
cursor: pointer;
|
||||
padding: 8px 14px;
|
||||
}
|
||||
|
||||
@@ -1,89 +1,37 @@
|
||||
import React from 'react';
|
||||
import React, { Component } from 'react';
|
||||
import SignIn from '../containers/SignIn';
|
||||
import ForgotPassword from '../containers/ForgotPassword';
|
||||
import PropTypes from 'prop-types';
|
||||
import Layout from 'coral-admin/src/components/Layout';
|
||||
import styles from './Login.css';
|
||||
import { Button, TextField, Alert } from 'coral-ui';
|
||||
import Layout from 'coral-admin/src/components/Layout';
|
||||
import cn from 'classnames';
|
||||
|
||||
class AdminLogin extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
class LoginContainer extends Component {
|
||||
renderForm() {
|
||||
return this.props.forgotPassword ? (
|
||||
<ForgotPassword onSignInLink={this.props.onSignInLink} />
|
||||
) : (
|
||||
<SignIn onForgotPasswordLink={this.props.onForgotPasswordLink} />
|
||||
);
|
||||
}
|
||||
|
||||
handleForgotPassword = e => {
|
||||
e.preventDefault();
|
||||
this.props.onForgotPassword();
|
||||
};
|
||||
handleEmailChange = e => this.props.onEmailChange(e.target.value);
|
||||
handlePasswordChange = e => this.props.onPasswordChange(e.target.value);
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
this.props.onSubmit();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { email, password, errorMessage } = this.props;
|
||||
return (
|
||||
<Layout fixedDrawer restricted={true}>
|
||||
<div className={cn(styles.loginLayout, 'talk-admin-login')}>
|
||||
<h1 className={styles.loginHeader}>Team sign in</h1>
|
||||
<p className={styles.loginCTA}>
|
||||
Sign in to interact with your community.
|
||||
</p>
|
||||
<form
|
||||
className="talk-admin-login-sign-in"
|
||||
onSubmit={this.handleSubmit}
|
||||
>
|
||||
{errorMessage && <Alert>{errorMessage}</Alert>}
|
||||
<TextField
|
||||
id="email"
|
||||
label="Email Address"
|
||||
value={email}
|
||||
onChange={this.handleEmailChange}
|
||||
/>
|
||||
<TextField
|
||||
id="password"
|
||||
label="Password"
|
||||
value={password}
|
||||
onChange={this.handlePasswordChange}
|
||||
type="password"
|
||||
/>
|
||||
<div style={{ height: 10 }} />
|
||||
<Button
|
||||
className="talk-admin-login-sign-in-button"
|
||||
type="submit"
|
||||
cStyle="black"
|
||||
full
|
||||
>
|
||||
Sign In
|
||||
</Button>
|
||||
<p className={styles.forgotPasswordCTA}>
|
||||
Forgot your password?{' '}
|
||||
<a
|
||||
href="#"
|
||||
className={styles.forgotPasswordLink}
|
||||
onClick={this.handleForgotPassword}
|
||||
>
|
||||
Request a new one.
|
||||
</a>
|
||||
</p>
|
||||
</form>
|
||||
<div className={cn(styles.layout, 'talk-admin-login')}>
|
||||
<h1 className={styles.header}>Team sign in</h1>
|
||||
<p className={styles.cta}>Sign in to interact with your community.</p>
|
||||
{this.renderForm()}
|
||||
</div>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AdminLogin.propTypes = {
|
||||
email: PropTypes.string,
|
||||
password: PropTypes.string,
|
||||
onEmailChange: PropTypes.func,
|
||||
onPasswordChange: PropTypes.func,
|
||||
onForgotPassword: PropTypes.func,
|
||||
onSubmit: PropTypes.func,
|
||||
errorMessage: PropTypes.string,
|
||||
requireRecaptcha: PropTypes.string,
|
||||
LoginContainer.propTypes = {
|
||||
forgotPassword: PropTypes.bool.isRequired,
|
||||
onForgotPasswordLink: PropTypes.func.isRequired,
|
||||
onSignInLink: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default AdminLogin;
|
||||
export default LoginContainer;
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
.forgotPasswordCTA {
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.forgotPasswordLink:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.forgotPasswordLink {
|
||||
color: blue;
|
||||
font-weight: normal;
|
||||
text-decoration: none;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styles from './SignIn.css';
|
||||
import { Button, TextField, Alert } from 'coral-ui';
|
||||
|
||||
class SignIn extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
handleForgotPasswordLink = e => {
|
||||
e.preventDefault();
|
||||
this.props.onForgotPasswordLink();
|
||||
};
|
||||
handleEmailChange = e => this.props.onEmailChange(e.target.value);
|
||||
handlePasswordChange = e => this.props.onPasswordChange(e.target.value);
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
this.props.onSubmit();
|
||||
};
|
||||
|
||||
render() {
|
||||
const { email, password, errorMessage } = this.props;
|
||||
return (
|
||||
<form className="talk-admin-login-sign-in" onSubmit={this.handleSubmit}>
|
||||
{errorMessage && <Alert>{errorMessage}</Alert>}
|
||||
<TextField
|
||||
id="email"
|
||||
label="Email Address"
|
||||
value={email}
|
||||
onChange={this.handleEmailChange}
|
||||
/>
|
||||
<TextField
|
||||
id="password"
|
||||
label="Password"
|
||||
value={password}
|
||||
onChange={this.handlePasswordChange}
|
||||
type="password"
|
||||
/>
|
||||
<div style={{ height: 10 }} />
|
||||
<Button
|
||||
className="talk-admin-login-sign-in-button"
|
||||
type="submit"
|
||||
cStyle="black"
|
||||
full
|
||||
>
|
||||
Sign In
|
||||
</Button>
|
||||
<p className={styles.forgotPasswordCTA}>
|
||||
Forgot your password?{' '}
|
||||
<a
|
||||
href="#"
|
||||
className={styles.forgotPasswordLink}
|
||||
onClick={this.handleForgotPasswordLink}
|
||||
>
|
||||
Request a new one.
|
||||
</a>
|
||||
</p>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SignIn.propTypes = {
|
||||
email: PropTypes.string.isRequired,
|
||||
password: PropTypes.string.isRequired,
|
||||
onEmailChange: PropTypes.func.isRequired,
|
||||
onPasswordChange: PropTypes.func.isRequired,
|
||||
onForgotPasswordLink: PropTypes.func.isRequired,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
errorMessage: PropTypes.string.isRequired,
|
||||
requireRecaptcha: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default SignIn;
|
||||
@@ -0,0 +1,42 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withForgotPassword } from 'coral-framework/hocs';
|
||||
import { compose } from 'recompose';
|
||||
import ForgotPassword from '../components/ForgotPassword';
|
||||
|
||||
class ForgotPasswordContainer extends Component {
|
||||
state = {
|
||||
email: '',
|
||||
password: '',
|
||||
};
|
||||
|
||||
handleSubmit = () => {
|
||||
this.props.forgotPassword(this.state.email);
|
||||
};
|
||||
|
||||
handleEmailChange = email => {
|
||||
this.setState({ email });
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<ForgotPassword
|
||||
onSubmit={this.handleSubmit}
|
||||
onEmailChange={this.handleEmailChange}
|
||||
email={this.state.email}
|
||||
errorMessage={this.props.errorMessage}
|
||||
success={this.props.success}
|
||||
onSignInLink={this.props.onSignInLink}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ForgotPasswordContainer.propTypes = {
|
||||
success: PropTypes.bool.isRequired,
|
||||
forgotPassword: PropTypes.func.isRequired,
|
||||
errorMessage: PropTypes.string.isRequired,
|
||||
onSignInLink: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default compose(withForgotPassword)(ForgotPasswordContainer);
|
||||
@@ -1,44 +1,30 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withLogin } from 'coral-framework/hocs';
|
||||
import { compose } from 'recompose';
|
||||
import Login from '../components/Login';
|
||||
|
||||
class LoginContainer extends Component {
|
||||
state = {
|
||||
email: '',
|
||||
password: '',
|
||||
forgotPassword: false,
|
||||
};
|
||||
|
||||
handleSubmit = () => {
|
||||
this.props.login(this.state.email, this.state.password);
|
||||
switchToForgotPassword = () => {
|
||||
this.setState({ forgotPassword: true });
|
||||
};
|
||||
|
||||
handleEmailChange = email => {
|
||||
this.setState({ email });
|
||||
};
|
||||
|
||||
handlePasswordChange = password => {
|
||||
this.setState({ password });
|
||||
switchToSignIn = () => {
|
||||
this.setState({ forgotPassword: false });
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Login
|
||||
onSubmit={this.handleSubmit}
|
||||
onEmailChange={this.handleEmailChange}
|
||||
onPasswordChange={this.handlePasswordChange}
|
||||
email={this.state.email}
|
||||
password={this.state.password}
|
||||
errorMessage={this.props.errorMessage}
|
||||
forgotPassword={this.state.forgotPassword}
|
||||
onForgotPasswordLink={this.switchToForgotPassword}
|
||||
onSignInLink={this.switchToSignIn}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
LoginContainer.propTypes = {
|
||||
login: PropTypes.func,
|
||||
errorMessage: PropTypes.string,
|
||||
};
|
||||
LoginContainer.propTypes = {};
|
||||
|
||||
export default compose(withLogin)(LoginContainer);
|
||||
export default LoginContainer;
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { withSignIn } from 'coral-framework/hocs';
|
||||
import { compose } from 'recompose';
|
||||
import SignIn from '../components/SignIn';
|
||||
|
||||
class SignInContainer extends Component {
|
||||
state = {
|
||||
email: '',
|
||||
password: '',
|
||||
};
|
||||
|
||||
handleSubmit = () => {
|
||||
this.props.signIn(this.state.email, this.state.password);
|
||||
};
|
||||
|
||||
handleEmailChange = email => {
|
||||
this.setState({ email });
|
||||
};
|
||||
|
||||
handlePasswordChange = password => {
|
||||
this.setState({ password });
|
||||
};
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SignIn
|
||||
onSubmit={this.handleSubmit}
|
||||
onEmailChange={this.handleEmailChange}
|
||||
onPasswordChange={this.handlePasswordChange}
|
||||
email={this.state.email}
|
||||
password={this.state.password}
|
||||
errorMessage={this.props.errorMessage}
|
||||
onForgotPasswordLink={this.props.onForgotPasswordLink}
|
||||
requireRecaptcha={this.props.requireRecaptcha}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SignInContainer.propTypes = {
|
||||
signIn: PropTypes.func.isRequired,
|
||||
errorMessage: PropTypes.string.isRequired,
|
||||
onForgotPasswordLink: PropTypes.func.isRequired,
|
||||
requireRecaptcha: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default compose(withSignIn)(SignInContainer);
|
||||
@@ -6,4 +6,5 @@ export { default as withEmit } from './withEmit';
|
||||
export { default as excludeIf } from './excludeIf';
|
||||
export { default as connect } from './connect';
|
||||
export { default as withMergedSettings } from './withMergedSettings';
|
||||
export { default as withLogin } from './withLogin';
|
||||
export { default as withSignIn } from './withSignIn';
|
||||
export { default as withForgotPassword } from './withForgotPassword';
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
import hoistStatics from 'recompose/hoistStatics';
|
||||
import PropTypes from 'prop-types';
|
||||
import { translateError } from '../utils';
|
||||
|
||||
/**
|
||||
* WithForgotPassword provides properties `forgotPasssword`, `loading`, `errorMessage`, `success`.
|
||||
*/
|
||||
export default hoistStatics(WrappedComponent => {
|
||||
class WithForgotPassword extends React.Component {
|
||||
static contextTypes = {
|
||||
store: PropTypes.object,
|
||||
rest: PropTypes.func,
|
||||
};
|
||||
|
||||
state = {
|
||||
error: null,
|
||||
loading: false,
|
||||
success: false,
|
||||
};
|
||||
|
||||
forgotPassword = email => {
|
||||
const { rest } = this.context;
|
||||
const redirectUri = location.href;
|
||||
this.setState({ loading: true, error: null, success: false });
|
||||
|
||||
rest('/account/password/reset', {
|
||||
method: 'POST',
|
||||
body: { email, loc: redirectUri },
|
||||
})
|
||||
.then(() => {
|
||||
this.setState({ loading: false, error: null, success: true });
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
this.setState({ loading: false, error });
|
||||
});
|
||||
};
|
||||
|
||||
getErrorMessage() {
|
||||
if (!this.state.error) {
|
||||
return '';
|
||||
}
|
||||
return translateError(this.state.error);
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<WrappedComponent
|
||||
{...this.props}
|
||||
forgotPassword={this.forgotPassword}
|
||||
success={this.state.success}
|
||||
loading={this.state.loading}
|
||||
errorMessage={this.getErrorMessage()}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return WithForgotPassword;
|
||||
});
|
||||
+5
-5
@@ -6,10 +6,10 @@ import { translateError } from '../utils';
|
||||
import { t } from '../services/i18n';
|
||||
|
||||
/**
|
||||
* WithLogin provides properties `login`, `loading` and `errorMessage`, `requireRecaptcha`.
|
||||
* WithSignIn provides properties `signIn`, `loading` and `errorMessage`, `requireRecaptcha`.
|
||||
*/
|
||||
export default hoistStatics(WrappedComponent => {
|
||||
class WithLogin extends React.Component {
|
||||
class WithSignIn extends React.Component {
|
||||
static contextTypes = {
|
||||
store: PropTypes.object,
|
||||
rest: PropTypes.func,
|
||||
@@ -20,7 +20,7 @@ export default hoistStatics(WrappedComponent => {
|
||||
loading: false,
|
||||
};
|
||||
|
||||
login = (email, password, recaptchaResponse) => {
|
||||
signIn = (email, password, recaptchaResponse) => {
|
||||
const { store, rest } = this.context;
|
||||
const params = {
|
||||
method: 'POST',
|
||||
@@ -62,7 +62,7 @@ export default hoistStatics(WrappedComponent => {
|
||||
return (
|
||||
<WrappedComponent
|
||||
{...this.props}
|
||||
login={this.login}
|
||||
signIn={this.signIn}
|
||||
loading={this.state.loading}
|
||||
errorMessage={this.getErrorMessage()}
|
||||
requireRecaptcha={false}
|
||||
@@ -71,5 +71,5 @@ export default hoistStatics(WrappedComponent => {
|
||||
}
|
||||
}
|
||||
|
||||
return WithLogin;
|
||||
return WithSignIn;
|
||||
});
|
||||
Reference in New Issue
Block a user