mirror of
https://github.com/wassname/talk.git
synced 2026-07-15 11:26:58 +08:00
Refactor talk-plugin-auth part 1
This commit is contained in:
@@ -12,3 +12,12 @@
|
||||
font-weight: normal;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.recaptcha {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.signInButton {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styles from './SignIn.css';
|
||||
import { Button, TextField, Alert } from 'coral-ui';
|
||||
import cn from 'classnames';
|
||||
import Recaptcha from 'coral-framework/components/Recaptcha';
|
||||
|
||||
class SignIn extends React.Component {
|
||||
@@ -27,9 +28,6 @@ class SignIn extends React.Component {
|
||||
|
||||
handleRecaptchaRef = ref => {
|
||||
this.recaptcha = ref;
|
||||
setTimeout(() => {
|
||||
console.log(ref);
|
||||
}, 1000)
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -50,9 +48,16 @@ class SignIn extends React.Component {
|
||||
onChange={this.handlePasswordChange}
|
||||
type="password"
|
||||
/>
|
||||
<div style={{ height: 10 }} />
|
||||
{requireRecaptcha && (
|
||||
<div className={styles.recaptcha}>
|
||||
<Recaptcha
|
||||
ref={this.handleRecaptchaRef}
|
||||
onVerify={this.props.onRecaptchaVerify}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<Button
|
||||
className="talk-admin-login-sign-in-button"
|
||||
className={cn(styles.signInButton, 'talk-admin-login-sign-in-button')}
|
||||
type="submit"
|
||||
cStyle="black"
|
||||
full
|
||||
@@ -69,12 +74,6 @@ class SignIn extends React.Component {
|
||||
Request a new one.
|
||||
</a>
|
||||
</p>
|
||||
{requireRecaptcha && (
|
||||
<Recaptcha
|
||||
ref={this.handleRecaptchaRef}
|
||||
onVerify={this.props.onRecaptchaVerify}
|
||||
/>
|
||||
)}
|
||||
</form>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
import React from 'react';
|
||||
import Slot from 'coral-framework/components/Slot';
|
||||
|
||||
export const LoginContainer = () => <Slot fill="login" />;
|
||||
@@ -31,6 +31,8 @@ class Recaptcha extends React.Component {
|
||||
theme={this.props.theme}
|
||||
onloadCallback={this.props.onLoad}
|
||||
verifyCallback={this.props.onVerify}
|
||||
size={this.props.size}
|
||||
className={this.props.className}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -38,7 +40,8 @@ class Recaptcha extends React.Component {
|
||||
|
||||
Recaptcha.defaultProps = {
|
||||
render: 'explicit',
|
||||
theme: 'dark',
|
||||
theme: 'light',
|
||||
size: 'normal',
|
||||
};
|
||||
|
||||
Recaptcha.propTypes = {
|
||||
@@ -46,7 +49,8 @@ Recaptcha.propTypes = {
|
||||
onVerify: PropTypes.func.isRequired,
|
||||
theme: PropTypes.string,
|
||||
render: PropTypes.string,
|
||||
sitekey: PropTypes.string.isRequired,
|
||||
size: PropTypes.string,
|
||||
className: PropTypes.string,
|
||||
};
|
||||
|
||||
export default Recaptcha;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { translateError } from '../utils';
|
||||
import { t } from '../services/i18n';
|
||||
|
||||
/**
|
||||
* WithSignIn provides properties `signIn`, `loading` and `errorMessage`, `requireRecaptcha`.
|
||||
* WithSignIn provides properties `signIn`, `loading`, `errorMessage`, `requireRecaptcha`, 'success'.
|
||||
*/
|
||||
export default hoistStatics(WrappedComponent => {
|
||||
class WithSignIn extends React.Component {
|
||||
@@ -18,6 +18,7 @@ export default hoistStatics(WrappedComponent => {
|
||||
state = {
|
||||
error: null,
|
||||
loading: false,
|
||||
success: false,
|
||||
requireRecaptcha: false,
|
||||
};
|
||||
|
||||
@@ -39,14 +40,14 @@ export default hoistStatics(WrappedComponent => {
|
||||
|
||||
rest('/auth/local', params)
|
||||
.then(({ user, token }) => {
|
||||
this.setState({ loading: false, error: null });
|
||||
this.setState({ success: true, loading: false, error: null });
|
||||
store.dispatch(handleSuccessfulLogin(user, token));
|
||||
})
|
||||
.catch(error => {
|
||||
if (!error.status || error.status !== 401) {
|
||||
console.error(error);
|
||||
}
|
||||
const changeSet = { loading: false, error };
|
||||
const changeSet = { success: false, loading: false, error };
|
||||
if (error.translation_key === 'LOGIN_MAXIMUM_EXCEEDED') {
|
||||
changeSet.requireRecaptcha = !!this.context.store.getState().config
|
||||
.static.TALK_RECAPTCHA_PUBLIC;
|
||||
@@ -72,6 +73,7 @@ export default hoistStatics(WrappedComponent => {
|
||||
loading={this.state.loading}
|
||||
errorMessage={this.getErrorMessage()}
|
||||
requireRecaptcha={this.state.requireRecaptcha}
|
||||
success={this.state.success}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import React from 'react';
|
||||
import Slot from 'coral-framework/components/Slot';
|
||||
|
||||
class Main extends React.Component {
|
||||
render() {
|
||||
return <div>Hello World</div>;
|
||||
return <Slot fill="login" />;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
.root {
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
margin-bottom: 10px;
|
||||
padding: 8px 0px 10px 0px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.button {
|
||||
color: #2a2a2a;
|
||||
margin: 5px 10px 5px 0px;
|
||||
background: none;
|
||||
padding: 0px;
|
||||
border: none;
|
||||
font-size: inherit;
|
||||
vertical-align: middle;
|
||||
|
||||
&:hover {
|
||||
color: #767676;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 12px;
|
||||
padding: 0 2px 0 5px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.authorName {
|
||||
margin-right: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
import React from 'react';
|
||||
import t from 'coral-framework/services/i18n';
|
||||
import ReplyButton from 'coral-embed-stream/src/tabs/stream/components/ReplyButton';
|
||||
import styles from './FakeComment.css';
|
||||
import { Icon } from 'plugin-api/beta/client/components/ui';
|
||||
import { CommentTimestamp } from 'plugin-api/beta/client/components';
|
||||
|
||||
export const FakeComment = ({ username, created_at, body }) => (
|
||||
<div className={styles.root}>
|
||||
<span className={styles.authorName}>{username}</span>
|
||||
<CommentTimestamp created_at={created_at} />
|
||||
<div className={styles.body}>{body}</div>
|
||||
<div className={styles.footer}>
|
||||
<div>
|
||||
<button className={styles.button}>
|
||||
<span className={styles.label}>{t('like')}</span>
|
||||
<Icon name="thumb_up" className={styles.icon} />
|
||||
</button>
|
||||
<ReplyButton
|
||||
onClick={() => {}}
|
||||
parentCommentId={'commentID'}
|
||||
currentUserId={{}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<button className={styles.button}>
|
||||
<span className={styles.label}>{t('permalink')}</span>
|
||||
<Icon name="link" className={styles.icon} />
|
||||
</button>
|
||||
<button className={styles.button}>
|
||||
<span className={styles.label}>{t('report')}</span>
|
||||
<Icon name="flag" className={styles.icon} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -1,81 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styles from './styles.css';
|
||||
import { Button, TextField } from 'plugin-api/beta/client/components/ui';
|
||||
import t from 'coral-framework/services/i18n';
|
||||
|
||||
class ForgotContent extends React.Component {
|
||||
state = { value: '' };
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
this.props.fetchForgotPassword(this.state.value);
|
||||
};
|
||||
|
||||
handleChangeEmail = e => {
|
||||
const { value } = e.target;
|
||||
this.setState({ value });
|
||||
};
|
||||
|
||||
render() {
|
||||
const { changeView, auth } = this.props;
|
||||
const { passwordRequestSuccess, passwordRequestFailure } = auth;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.header}>
|
||||
<h1>{t('sign_in.recover_password')}</h1>
|
||||
</div>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<div className={styles.textField}>
|
||||
<TextField
|
||||
type="email"
|
||||
style={{ fontSize: 16 }}
|
||||
id="email"
|
||||
name="email"
|
||||
label={t('sign_in.email')}
|
||||
onChange={this.handleChangeEmail}
|
||||
value={this.state.value}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
type="submit"
|
||||
cStyle="black"
|
||||
className={styles.signInButton}
|
||||
full
|
||||
>
|
||||
{t('sign_in.recover_password')}
|
||||
</Button>
|
||||
{passwordRequestSuccess ? (
|
||||
<p className={styles.passwordRequestSuccess}>
|
||||
{passwordRequestSuccess}
|
||||
</p>
|
||||
) : null}
|
||||
{passwordRequestFailure ? (
|
||||
<p className={styles.passwordRequestFailure}>
|
||||
{passwordRequestFailure}
|
||||
</p>
|
||||
) : null}
|
||||
</form>
|
||||
<div className={styles.footer}>
|
||||
<span>
|
||||
{t('sign_in.need_an_account')}{' '}
|
||||
<a onClick={() => changeView('SIGNUP')}>{t('sign_in.register')}</a>
|
||||
</span>
|
||||
<span>
|
||||
{t('sign_in.already_have_an_account')}{' '}
|
||||
<a onClick={() => changeView('SIGNIN')}>{t('sign_in.sign_in')}</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ForgotContent.propTypes = {
|
||||
auth: PropTypes.object,
|
||||
changeView: PropTypes.func,
|
||||
fetchForgotPassword: PropTypes.func,
|
||||
};
|
||||
|
||||
export default ForgotContent;
|
||||
@@ -1,15 +0,0 @@
|
||||
.header {
|
||||
margin-bottom: 20px;
|
||||
text-align: center;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.notVerified {
|
||||
padding: 10px;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 2px;
|
||||
border: solid 1px #dddd00;
|
||||
background: #FFFF9C;
|
||||
color: #777700;
|
||||
}
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
Button,
|
||||
Spinner,
|
||||
Success,
|
||||
Alert,
|
||||
} from 'plugin-api/beta/client/components/ui';
|
||||
import PropTypes from 'prop-types';
|
||||
import styles from './ResendVerification.css';
|
||||
import t from 'coral-framework/services/i18n';
|
||||
|
||||
class ResendVerification extends React.Component {
|
||||
render() {
|
||||
const { resendVerification, error, loading, success, email } = this.props;
|
||||
return (
|
||||
<div className="talk-resend-verification">
|
||||
<h1 className={styles.header}>{t('sign_in.email_verify_cta')}</h1>
|
||||
|
||||
{error && (
|
||||
<Alert>
|
||||
{error.translation_key
|
||||
? t(`error.${error.translation_key}`)
|
||||
: error.toString()}
|
||||
</Alert>
|
||||
)}
|
||||
<div className={styles.notVerified}>
|
||||
{t('error.email_not_verified', email)}
|
||||
</div>
|
||||
<div>
|
||||
<Button
|
||||
id="resendConfirmEmail"
|
||||
cStyle="black"
|
||||
onClick={resendVerification}
|
||||
full
|
||||
>
|
||||
{t('sign_in.request_new_verify_email')}
|
||||
</Button>
|
||||
{loading && <Spinner />}
|
||||
{success && <Success />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ResendVerification.propTypes = {
|
||||
resendVerification: PropTypes.bool.isRequired,
|
||||
error: PropTypes.object,
|
||||
loading: PropTypes.bool,
|
||||
success: PropTypes.bool,
|
||||
email: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
export default ResendVerification;
|
||||
@@ -1,32 +0,0 @@
|
||||
import React from 'react';
|
||||
import { Dialog } from 'plugin-api/beta/client/components/ui';
|
||||
import styles from './styles.css';
|
||||
|
||||
import SignInContent from './SignInContent';
|
||||
import SignUpContent from './SignUpContent';
|
||||
import ForgotContent from './ForgotContent';
|
||||
import ResendVerification from './ResendVerification';
|
||||
|
||||
const SignDialog = ({ open, view, resetSignInDialog, ...props }) => (
|
||||
<Dialog className={styles.dialog} id="signInDialog" open={open}>
|
||||
{view !== 'SIGNIN' && (
|
||||
<span className={styles.close} onClick={resetSignInDialog}>
|
||||
×
|
||||
</span>
|
||||
)}
|
||||
{view === 'SIGNIN' && <SignInContent {...props} />}
|
||||
{view === 'SIGNUP' && <SignUpContent {...props} />}
|
||||
{view === 'FORGOT' && <ForgotContent {...props} />}
|
||||
{view === 'RESEND_VERIFICATION' && (
|
||||
<ResendVerification
|
||||
resendVerification={props.resendVerification}
|
||||
error={props.auth.emailVerificationFailure}
|
||||
success={props.auth.emailVerificationSuccess}
|
||||
loading={props.auth.emailVerificationLoading}
|
||||
email={props.auth.email}
|
||||
/>
|
||||
)}
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
export default SignDialog;
|
||||
@@ -1,202 +0,0 @@
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import SignDialog from './SignDialog';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import t from 'coral-framework/services/i18n';
|
||||
import errorMsj from 'coral-framework/helpers/error';
|
||||
import validate from 'coral-framework/helpers/validate';
|
||||
|
||||
import {
|
||||
changeView,
|
||||
fetchSignUp,
|
||||
fetchSignIn,
|
||||
hideSignInDialog,
|
||||
fetchSignInFacebook,
|
||||
fetchSignUpFacebook,
|
||||
fetchForgotPassword,
|
||||
requestConfirmEmail,
|
||||
resetSignInDialog,
|
||||
facebookCallback,
|
||||
invalidForm,
|
||||
validForm,
|
||||
} from 'coral-embed-stream/src/actions/login';
|
||||
|
||||
class SignInContainer extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
formData: {
|
||||
email: '',
|
||||
username: '',
|
||||
password: '',
|
||||
confirmPassword: '',
|
||||
},
|
||||
errors: {},
|
||||
showErrors: false,
|
||||
};
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.listenToStorageChanges();
|
||||
const { formData } = this.state;
|
||||
const errors = Object.keys(formData).reduce((map, prop) => {
|
||||
map[prop] = t('sign_in.required_field');
|
||||
return map;
|
||||
}, {});
|
||||
this.setState({ errors });
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.unlisten();
|
||||
}
|
||||
|
||||
listenToStorageChanges() {
|
||||
window.addEventListener('storage', this.handleAuth);
|
||||
}
|
||||
|
||||
unlisten() {
|
||||
window.removeEventListener('storage', this.handleAuth);
|
||||
}
|
||||
|
||||
handleAuth = e => {
|
||||
// Listening to FB changes
|
||||
// FB localStorage key is 'auth'
|
||||
const authCallback = this.props.facebookCallback;
|
||||
|
||||
if (e.key === 'auth') {
|
||||
const { err, data } = JSON.parse(e.newValue);
|
||||
authCallback(err, data);
|
||||
this.unlisten();
|
||||
localStorage.removeItem('auth');
|
||||
}
|
||||
};
|
||||
|
||||
handleChange = e => {
|
||||
const { name, value } = e.target;
|
||||
this.setState(
|
||||
state => ({
|
||||
...state,
|
||||
formData: {
|
||||
...state.formData,
|
||||
[name]: value,
|
||||
},
|
||||
}),
|
||||
() => {
|
||||
this.validation(name, value);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
resendVerification = () => {
|
||||
this.props.requestConfirmEmail(this.props.auth.email).then(() => {
|
||||
setTimeout(() => {
|
||||
// allow success UI to be shown for a second, and then close the modal
|
||||
this.props.resetSignInDialog();
|
||||
}, 2500);
|
||||
});
|
||||
};
|
||||
|
||||
addError = (name, error) => {
|
||||
return this.setState(state => ({
|
||||
errors: {
|
||||
...state.errors,
|
||||
[name]: error,
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
validation = (name, value) => {
|
||||
const { addError } = this;
|
||||
const { formData } = this.state;
|
||||
|
||||
if (!value.length) {
|
||||
addError(name, t('sign_in.required_field'));
|
||||
} else if (
|
||||
name === 'confirmPassword' &&
|
||||
formData.confirmPassword !== formData.password
|
||||
) {
|
||||
addError('confirmPassword', t('sign_in.passwords_dont_match'));
|
||||
} else if (!validate[name](value)) {
|
||||
addError(name, errorMsj[name]);
|
||||
} else {
|
||||
const {[name]: prop, ...errors} = this.state.errors; // eslint-disable-line
|
||||
// Removes Error
|
||||
this.setState(state => ({ ...state, errors }));
|
||||
}
|
||||
};
|
||||
|
||||
isCompleted = () => {
|
||||
const { formData } = this.state;
|
||||
return !Object.keys(formData).filter(prop => !formData[prop].length).length;
|
||||
};
|
||||
|
||||
displayErrors = (show = true) => {
|
||||
this.setState({ showErrors: show });
|
||||
};
|
||||
|
||||
handleSignUp = e => {
|
||||
e.preventDefault();
|
||||
const { errors } = this.state;
|
||||
const { fetchSignUp, validForm, invalidForm } = this.props;
|
||||
this.displayErrors();
|
||||
if (this.isCompleted() && !Object.keys(errors).length) {
|
||||
fetchSignUp(this.state.formData);
|
||||
validForm();
|
||||
} else {
|
||||
invalidForm(t('sign_in.check_the_form'));
|
||||
}
|
||||
};
|
||||
|
||||
handleSignIn = e => {
|
||||
e.preventDefault();
|
||||
this.props.fetchSignIn(this.state.formData);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { auth } = this.props;
|
||||
const {
|
||||
requireEmailConfirmation,
|
||||
emailVerificationLoading,
|
||||
emailVerificationSuccess,
|
||||
} = auth;
|
||||
|
||||
return (
|
||||
<SignDialog
|
||||
open={true}
|
||||
view={auth.view}
|
||||
emailVerificationEnabled={requireEmailConfirmation}
|
||||
emailVerificationLoading={emailVerificationLoading}
|
||||
emailVerificationSuccess={emailVerificationSuccess}
|
||||
{...this}
|
||||
{...this.state}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
auth: state.auth,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators(
|
||||
{
|
||||
facebookCallback,
|
||||
fetchSignUp,
|
||||
fetchSignIn,
|
||||
fetchSignInFacebook,
|
||||
fetchSignUpFacebook,
|
||||
fetchForgotPassword,
|
||||
requestConfirmEmail,
|
||||
changeView,
|
||||
hideSignInDialog,
|
||||
resetSignInDialog,
|
||||
invalidForm,
|
||||
validForm,
|
||||
},
|
||||
dispatch
|
||||
);
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(SignInContainer);
|
||||
@@ -1,108 +0,0 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Button,
|
||||
TextField,
|
||||
Spinner,
|
||||
Alert,
|
||||
} from 'plugin-api/beta/client/components/ui';
|
||||
import styles from './styles.css';
|
||||
import t from 'coral-framework/services/i18n';
|
||||
|
||||
const SignInContent = ({
|
||||
handleChange,
|
||||
formData,
|
||||
changeView,
|
||||
handleSignIn,
|
||||
auth,
|
||||
fetchSignInFacebook,
|
||||
}) => {
|
||||
return (
|
||||
<div className="coral-sign-in">
|
||||
<div className={`${styles.header} header`}>
|
||||
<h1>{t('sign_in.sign_in_to_join')}</h1>
|
||||
</div>
|
||||
{auth.error && (
|
||||
<Alert>
|
||||
{auth.error.translation_key
|
||||
? t(`error.${auth.error.translation_key}`)
|
||||
: auth.error.toString()}
|
||||
</Alert>
|
||||
)}
|
||||
<div>
|
||||
<div className={`${styles.socialConnections} social-connections`}>
|
||||
<Button cStyle="facebook" onClick={fetchSignInFacebook} full>
|
||||
{t('sign_in.facebook_sign_in')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className={styles.separator}>
|
||||
<h1>{t('sign_in.or')}</h1>
|
||||
</div>
|
||||
<form onSubmit={handleSignIn}>
|
||||
<TextField
|
||||
id="email"
|
||||
type="email"
|
||||
label={t('sign_in.email')}
|
||||
value={formData.email}
|
||||
style={{ fontSize: 16 }}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextField
|
||||
id="password"
|
||||
type="password"
|
||||
label={t('sign_in.password')}
|
||||
value={formData.password}
|
||||
style={{ fontSize: 16 }}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<div className={styles.action}>
|
||||
{!auth.isLoading ? (
|
||||
<Button
|
||||
id="coralLogInButton"
|
||||
type="submit"
|
||||
cStyle="black"
|
||||
className={styles.signInButton}
|
||||
full
|
||||
>
|
||||
{t('sign_in.sign_in')}
|
||||
</Button>
|
||||
) : (
|
||||
<Spinner />
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div className={`${styles.footer} footer`}>
|
||||
<span>
|
||||
<a onClick={() => changeView('FORGOT')}>
|
||||
{t('sign_in.forgot_your_pass')}
|
||||
</a>
|
||||
</span>
|
||||
<span>
|
||||
{t('sign_in.need_an_account')}
|
||||
<a onClick={() => changeView('SIGNUP')} id="coralRegister">
|
||||
{t('sign_in.register')}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
SignInContent.propTypes = {
|
||||
auth: PropTypes.shape({
|
||||
isLoading: PropTypes.bool.isRequired,
|
||||
error: PropTypes.string,
|
||||
emailVerificationFailure: PropTypes.bool,
|
||||
}).isRequired,
|
||||
fetchSignInFacebook: PropTypes.func.isRequired,
|
||||
handleSignIn: PropTypes.func.isRequired,
|
||||
handleChange: PropTypes.func.isRequired,
|
||||
changeView: PropTypes.func.isRequired,
|
||||
emailVerificationLoading: PropTypes.bool.isRequired,
|
||||
emailVerificationSuccess: PropTypes.bool.isRequired,
|
||||
resendVerification: PropTypes.func.isRequired,
|
||||
formData: PropTypes.object,
|
||||
};
|
||||
|
||||
export default SignInContent;
|
||||
@@ -1,143 +0,0 @@
|
||||
import styles from './styles.css';
|
||||
import React from 'react';
|
||||
import {
|
||||
Button,
|
||||
TextField,
|
||||
Spinner,
|
||||
Success,
|
||||
Alert,
|
||||
} from 'plugin-api/beta/client/components/ui';
|
||||
import t from 'coral-framework/services/i18n';
|
||||
|
||||
class SignUpContent extends React.Component {
|
||||
componentWillReceiveProps(next) {
|
||||
if (
|
||||
!this.props.emailVerificationEnabled &&
|
||||
!this.props.auth.successSignUp &&
|
||||
next.auth.successSignUp
|
||||
) {
|
||||
setTimeout(() => {
|
||||
this.props.changeView('SIGNIN');
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const {
|
||||
handleChange,
|
||||
formData,
|
||||
emailVerificationEnabled,
|
||||
auth,
|
||||
errors,
|
||||
showErrors,
|
||||
changeView,
|
||||
handleSignUp,
|
||||
fetchSignUpFacebook,
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className={styles.header}>
|
||||
<h1>{t('sign_in.sign_up')}</h1>
|
||||
</div>
|
||||
|
||||
{auth.error && <Alert>{auth.error}</Alert>}
|
||||
{!auth.successSignUp && (
|
||||
<div>
|
||||
<div className={styles.socialConnections}>
|
||||
<Button cStyle="facebook" onClick={fetchSignUpFacebook} full>
|
||||
{t('sign_in.facebook_sign_up')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className={styles.separator}>
|
||||
<h1>{t('sign_in.or')}</h1>
|
||||
</div>
|
||||
<form onSubmit={handleSignUp}>
|
||||
<TextField
|
||||
id="email"
|
||||
type="email"
|
||||
label={t('sign_in.email')}
|
||||
value={formData.email}
|
||||
style={{ fontSize: 16 }}
|
||||
showErrors={showErrors}
|
||||
errorMsg={errors.email}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextField
|
||||
id="username"
|
||||
type="text"
|
||||
label={t('sign_in.username')}
|
||||
value={formData.username}
|
||||
showErrors={showErrors}
|
||||
style={{ fontSize: 16 }}
|
||||
errorMsg={errors.username}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
<TextField
|
||||
id="password"
|
||||
type="password"
|
||||
label={t('sign_in.password')}
|
||||
value={formData.password}
|
||||
showErrors={showErrors}
|
||||
style={{ fontSize: 16 }}
|
||||
errorMsg={errors.password}
|
||||
onChange={handleChange}
|
||||
minLength="8"
|
||||
/>
|
||||
{errors.password && (
|
||||
<span className={styles.hint}>
|
||||
{' '}
|
||||
Password must be at least 8 characters.{' '}
|
||||
</span>
|
||||
)}
|
||||
<TextField
|
||||
id="confirmPassword"
|
||||
type="password"
|
||||
label={t('sign_in.confirm_password')}
|
||||
value={formData.confirmPassword}
|
||||
style={{ fontSize: 16 }}
|
||||
showErrors={showErrors}
|
||||
errorMsg={errors.confirmPassword}
|
||||
onChange={handleChange}
|
||||
minLength="8"
|
||||
/>
|
||||
<div className={styles.action}>
|
||||
<Button
|
||||
type="submit"
|
||||
cStyle="black"
|
||||
id="coralSignUpButton"
|
||||
className={styles.signInButton}
|
||||
full
|
||||
>
|
||||
{t('sign_in.sign_up')}
|
||||
</Button>
|
||||
{auth.isLoading && <Spinner />}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
)}
|
||||
{auth.successSignUp && (
|
||||
<div>
|
||||
<Success />
|
||||
{emailVerificationEnabled && (
|
||||
<p>
|
||||
{t('sign_in.verify_email')}
|
||||
<br />
|
||||
<br />
|
||||
{t('sign_in.verify_email2')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.footer}>
|
||||
{t('sign_in.already_have_an_account')}{' '}
|
||||
<a id="coralSignInViewTrigger" onClick={() => changeView('SIGNIN')}>
|
||||
{t('sign_in.sign_in')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SignUpContent;
|
||||
@@ -1,33 +0,0 @@
|
||||
import React from 'react';
|
||||
import styles from './styles.css';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import t from 'coral-framework/services/i18n';
|
||||
import { logout } from 'coral-framework/actions/auth';
|
||||
|
||||
const UserBox = ({ user, logout, onShowProfile }) => (
|
||||
<div>
|
||||
{user ? (
|
||||
<div className={`${styles.userBox} talk-stream-auth-userbox`}>
|
||||
<span className={styles.userBoxLoggedIn}>
|
||||
{t('sign_in.logged_in_as')}
|
||||
</span>
|
||||
<a onClick={onShowProfile}>{user.username}</a>. {t('sign_in.not_you')}
|
||||
<a
|
||||
className={`${styles.logout} talk-stream-userbox-logout`}
|
||||
onClick={() => logout()}
|
||||
>
|
||||
{t('sign_in.logout')}
|
||||
</a>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
|
||||
const mapStateToProps = ({ auth }) => ({
|
||||
user: auth.user,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => bindActionCreators({ logout }, dispatch);
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(UserBox);
|
||||
@@ -1,167 +0,0 @@
|
||||
.dialog {
|
||||
border: none;
|
||||
box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14), 0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2);
|
||||
width: 280px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
.header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.header h1, .separator h1{
|
||||
text-align: center;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin: 20px auto 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer span {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: #2c69b6;
|
||||
cursor: pointer;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.socialConnections {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.signInButton {
|
||||
margin-top: 10px;
|
||||
background-color: #2a2a2a;
|
||||
}
|
||||
|
||||
.close {
|
||||
font-size: 20px;
|
||||
line-height: 14px;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
position: absolute;
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
color: #363636;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.close:hover {
|
||||
color: #6b6b6b;
|
||||
}
|
||||
|
||||
input.error{
|
||||
border: solid 2px #f44336;
|
||||
}
|
||||
|
||||
.errorMsg, .hint {
|
||||
color: grey;
|
||||
font-weight: 600;
|
||||
padding: 3px 0 16px;
|
||||
}
|
||||
|
||||
.userBox {
|
||||
margin: 10px 0 20px;
|
||||
letter-spacing: 0.1px;
|
||||
}
|
||||
|
||||
.userBoxLoggedIn {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.userBox a {
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
margin: 0px;
|
||||
margin-left: 4px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.userBox .logout {
|
||||
border-bottom: solid 1px black;
|
||||
}
|
||||
|
||||
.attention {
|
||||
display: inline-block;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
background: #B71C1C;
|
||||
color: #FFEBEE;
|
||||
font-weight: bolder;
|
||||
padding: 4px;
|
||||
vertical-align: middle;
|
||||
border-radius: 20px;
|
||||
box-sizing: border-box;
|
||||
font-size: 9px;
|
||||
line-height: 7px;
|
||||
text-align: center;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.action {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.passwordRequestSuccess {
|
||||
border: 1px solid green;
|
||||
background-color: lightgreen;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.passwordRequestFailure {
|
||||
border: 1px solid orange;
|
||||
background-color: 1px solid coral;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.emailConfirmDialog {
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.confirmLabel {
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Change username Dialog*/
|
||||
|
||||
.dialogusername {
|
||||
border: none;
|
||||
box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14), 0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2);
|
||||
width: 400px;
|
||||
top: 10px;
|
||||
}
|
||||
|
||||
.yourusername {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.example {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.ifyoudont {
|
||||
display: block;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.saveusername {
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.savebutton {
|
||||
display: inline;
|
||||
background-color: rgb(105,105,105);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.fakeComment {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
@@ -1,13 +1,12 @@
|
||||
import UserBox from './components/UserBox';
|
||||
import SignInButton from './components/SignInButton';
|
||||
import SignInContainer from './components/SignInContainer';
|
||||
import ChangeUserNameContainer from './components/ChangeUsername';
|
||||
import UserBox from './stream/containers/UserBox';
|
||||
import SignInButton from './stream/containers/SignInButton';
|
||||
import translations from './translations.yml';
|
||||
import Login from './login/containers/Main';
|
||||
|
||||
export default {
|
||||
translations,
|
||||
slots: {
|
||||
stream: [UserBox, SignInButton, ChangeUserNameContainer],
|
||||
login: [SignInContainer],
|
||||
stream: [UserBox, SignInButton],
|
||||
login: [Login],
|
||||
},
|
||||
};
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
.dialog {
|
||||
border: none;
|
||||
box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14), 0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2);
|
||||
width: 280px;
|
||||
top: 10px;
|
||||
font-family: Helvetica, 'Helvetica Neue', Verdana, sans-serif;
|
||||
font-size: 14px;
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
import React from 'react';
|
||||
import { Dialog } from 'plugin-api/beta/client/components/ui';
|
||||
import styles from './Main.css';
|
||||
|
||||
import SignIn from '../containers/SignIn';
|
||||
|
||||
const SignDialog = () => (
|
||||
<Dialog className={styles.dialog} id="signInDialog" open={true}>
|
||||
<SignIn />
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
export default SignDialog;
|
||||
@@ -0,0 +1,47 @@
|
||||
.header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.header h1, .separator h1{
|
||||
text-align: center;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.socialConnections {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.separator h1{
|
||||
text-align: center;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.action {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
.signInButton {
|
||||
margin-top: 10px;
|
||||
background-color: #2a2a2a;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin: 20px auto 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer span {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
color: #2c69b6;
|
||||
cursor: pointer;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
.recaptcha {
|
||||
margin-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
Button,
|
||||
TextField,
|
||||
Spinner,
|
||||
Alert,
|
||||
} from 'plugin-api/beta/client/components/ui';
|
||||
import styles from './SignIn.css';
|
||||
import t from 'coral-framework/services/i18n';
|
||||
import cn from 'classnames';
|
||||
import Recaptcha from 'coral-framework/components/Recaptcha';
|
||||
|
||||
class SignIn extends React.Component {
|
||||
recaptcha = null;
|
||||
|
||||
handleForgotPasswordLink = e => {
|
||||
e.preventDefault();
|
||||
this.props.onForgotPasswordLink();
|
||||
};
|
||||
handleRegisterLink = e => {
|
||||
e.preventDefault();
|
||||
this.props.onRegisterLink();
|
||||
};
|
||||
handleEmailChange = e => this.props.onEmailChange(e.target.value);
|
||||
handlePasswordChange = e => this.props.onPasswordChange(e.target.value);
|
||||
|
||||
handleSubmit = e => {
|
||||
e.preventDefault();
|
||||
this.props.onSubmit();
|
||||
|
||||
// Reset recaptcha because each response can only
|
||||
// be used once.
|
||||
if (this.recaptcha) {
|
||||
this.recaptcha.reset();
|
||||
}
|
||||
};
|
||||
|
||||
handleRecaptchaRef = ref => {
|
||||
this.recaptcha = ref;
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
email,
|
||||
password,
|
||||
errorMessage,
|
||||
requireRecaptcha,
|
||||
loading,
|
||||
} = this.props;
|
||||
return (
|
||||
<div className="coral-sign-in">
|
||||
<div className={cn(styles.header, 'header')}>
|
||||
<h1>{t('sign_in.sign_in_to_join')}</h1>
|
||||
</div>
|
||||
{errorMessage && <Alert>{errorMessage}</Alert>}
|
||||
<div>
|
||||
<div className={cn(styles.socialConnections, 'social-connections')}>
|
||||
Social
|
||||
</div>
|
||||
<div className={styles.separator}>
|
||||
<h1>{t('sign_in.or')}</h1>
|
||||
</div>
|
||||
<form onSubmit={this.handleSubmit}>
|
||||
<TextField
|
||||
id="email"
|
||||
type="email"
|
||||
label={t('sign_in.email')}
|
||||
value={email}
|
||||
style={{ fontSize: 16 }}
|
||||
onChange={this.handleEmailChange}
|
||||
/>
|
||||
<TextField
|
||||
id="password"
|
||||
type="password"
|
||||
label={t('sign_in.password')}
|
||||
value={password}
|
||||
style={{ fontSize: 16 }}
|
||||
onChange={this.handlePasswordChange}
|
||||
/>
|
||||
{requireRecaptcha && (
|
||||
<div className={styles.recaptcha}>
|
||||
<Recaptcha
|
||||
className={styles.recaptcha}
|
||||
ref={this.handleRecaptchaRef}
|
||||
onVerify={this.props.onRecaptchaVerify}
|
||||
size="compact"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div className={styles.action}>
|
||||
{!loading ? (
|
||||
<Button
|
||||
id="coralLogInButton"
|
||||
type="submit"
|
||||
cStyle="black"
|
||||
className={styles.signInButton}
|
||||
full
|
||||
>
|
||||
{t('sign_in.sign_in')}
|
||||
</Button>
|
||||
) : (
|
||||
<Spinner />
|
||||
)}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div className={cn(styles.footer, 'footer')}>
|
||||
<span>
|
||||
<a onClick={this.handleForgotPasswordLink}>
|
||||
{t('sign_in.forgot_your_pass')}
|
||||
</a>
|
||||
</span>
|
||||
<span>
|
||||
{t('sign_in.need_an_account')}
|
||||
<a onClick={this.handleRegisterLink} id="coralRegister">
|
||||
{t('sign_in.register')}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SignIn.propTypes = {
|
||||
loading: PropTypes.bool.isRequired,
|
||||
email: PropTypes.string.isRequired,
|
||||
password: PropTypes.string.isRequired,
|
||||
onEmailChange: PropTypes.func.isRequired,
|
||||
onPasswordChange: PropTypes.func.isRequired,
|
||||
onForgotPasswordLink: PropTypes.func.isRequired,
|
||||
onRegisterLink: PropTypes.func.isRequired,
|
||||
onRecaptchaVerify: PropTypes.func.isRequired,
|
||||
onSubmit: PropTypes.func.isRequired,
|
||||
errorMessage: PropTypes.string.isRequired,
|
||||
requireRecaptcha: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default SignIn;
|
||||
@@ -0,0 +1,10 @@
|
||||
import React from 'react';
|
||||
import Main from '../components/Main';
|
||||
|
||||
class MainContainer extends React.Component {
|
||||
render() {
|
||||
return <Main />;
|
||||
}
|
||||
}
|
||||
|
||||
export default MainContainer;
|
||||
@@ -0,0 +1,65 @@
|
||||
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: '',
|
||||
recaptchaResponse: '',
|
||||
};
|
||||
|
||||
handleSubmit = () => {
|
||||
this.props.signIn(
|
||||
this.state.email,
|
||||
this.state.password,
|
||||
this.state.recaptchaResponse
|
||||
);
|
||||
};
|
||||
|
||||
handleEmailChange = email => {
|
||||
this.setState({ email });
|
||||
};
|
||||
|
||||
handlePasswordChange = password => {
|
||||
this.setState({ password });
|
||||
};
|
||||
|
||||
handleRecaptchaVerify = recaptchaResponse => {
|
||||
this.setState({ recaptchaResponse });
|
||||
};
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
if (nextProps.success) {
|
||||
window.close();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<SignIn
|
||||
onSubmit={this.handleSubmit}
|
||||
onEmailChange={this.handleEmailChange}
|
||||
onPasswordChange={this.handlePasswordChange}
|
||||
email={this.state.email}
|
||||
password={this.state.password}
|
||||
errorMessage={this.props.errorMessage}
|
||||
onRecaptchaVerify={this.handleRecaptchaVerify}
|
||||
requireRecaptcha={this.props.requireRecaptcha}
|
||||
loading={this.props.loading}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
SignInContainer.propTypes = {
|
||||
signIn: PropTypes.func.isRequired,
|
||||
errorMessage: PropTypes.string.isRequired,
|
||||
requireRecaptcha: PropTypes.bool.isRequired,
|
||||
loading: PropTypes.bool.isRequired,
|
||||
success: PropTypes.bool.isRequired,
|
||||
};
|
||||
|
||||
export default compose(withSignIn)(SignInContainer);
|
||||
+3
-3
@@ -10,7 +10,7 @@ import {
|
||||
import { FakeComment } from './FakeComment';
|
||||
import t from 'coral-framework/services/i18n';
|
||||
|
||||
const CreateUsernameDialog = ({
|
||||
const SetUsernameDialog = ({
|
||||
open,
|
||||
handleClose,
|
||||
formData,
|
||||
@@ -70,7 +70,7 @@ const CreateUsernameDialog = ({
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
CreateUsernameDialog.propTypes = {
|
||||
SetUsernameDialog.propTypes = {
|
||||
open: PropTypes.bool,
|
||||
handleClose: PropTypes.func,
|
||||
formData: PropTypes.object,
|
||||
@@ -80,4 +80,4 @@ CreateUsernameDialog.propTypes = {
|
||||
errors: PropTypes.object,
|
||||
};
|
||||
|
||||
export default CreateUsernameDialog;
|
||||
export default SetUsernameDialog;
|
||||
+6
-10
@@ -1,8 +1,6 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Button } from 'plugin-api/beta/client/components/ui';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { showSignInDialog } from 'coral-embed-stream/src/actions/login';
|
||||
import t from 'coral-framework/services/i18n';
|
||||
|
||||
const SignInButton = ({ currentUser, showSignInDialog }) => (
|
||||
@@ -15,11 +13,9 @@ const SignInButton = ({ currentUser, showSignInDialog }) => (
|
||||
</div>
|
||||
);
|
||||
|
||||
const mapStateToProps = ({ auth }) => ({
|
||||
currentUser: auth.user,
|
||||
});
|
||||
SignInButton.propTypes = {
|
||||
currentUser: PropTypes.object,
|
||||
showSignInDialog: PropTypes.func,
|
||||
};
|
||||
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators({ showSignInDialog }, dispatch);
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(SignInButton);
|
||||
export default SignInButton;
|
||||
@@ -0,0 +1,21 @@
|
||||
.userBox {
|
||||
margin: 10px 0 20px;
|
||||
letter-spacing: 0.1px;
|
||||
}
|
||||
|
||||
.userBoxLoggedIn {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.userBox a {
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
margin: 0px;
|
||||
margin-left: 4px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.userBox .logout {
|
||||
border-bottom: solid 1px black;
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styles from './UserBox.css';
|
||||
import t from 'coral-framework/services/i18n';
|
||||
import cn from 'classnames';
|
||||
|
||||
const UserBox = ({ user, logout, onShowProfile }) => (
|
||||
<div>
|
||||
{user ? (
|
||||
<div className={cn(styles.userBox, 'talk-stream-auth-userbox')}>
|
||||
<span className={styles.userBoxLoggedIn}>
|
||||
{t('sign_in.logged_in_as')}
|
||||
</span>
|
||||
<a onClick={onShowProfile}>{user.username}</a>. {t('sign_in.not_you')}
|
||||
<a
|
||||
className={cn(styles.logout, 'talk-stream-userbox-logout')}
|
||||
onClick={() => logout()}
|
||||
>
|
||||
{t('sign_in.logout')}
|
||||
</a>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
|
||||
UserBox.propTypes = {
|
||||
user: PropTypes.object,
|
||||
logout: PropTypes.func,
|
||||
onShowProfile: PropTypes.func,
|
||||
};
|
||||
|
||||
export default UserBox;
|
||||
+3
-3
@@ -19,7 +19,7 @@ import {
|
||||
updateUsername,
|
||||
} from 'coral-embed-stream/src/actions/login';
|
||||
|
||||
class ChangeUsernameContainer extends React.Component {
|
||||
class SetUsernameDialog extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -152,7 +152,7 @@ class ChangeUsernameContainer extends React.Component {
|
||||
}
|
||||
}
|
||||
|
||||
ChangeUsernameContainer.propTypes = {
|
||||
SetUsernameDialog.propTypes = {
|
||||
auth: PropTypes.object,
|
||||
hideCreateUsernameDialog: PropTypes.func,
|
||||
validForm: PropTypes.func,
|
||||
@@ -180,4 +180,4 @@ const mapDispatchToProps = dispatch =>
|
||||
export default compose(
|
||||
withSetUsername,
|
||||
connect(mapStateToProps, mapDispatchToProps)
|
||||
)(ChangeUsernameContainer);
|
||||
)(SetUsernameDialog);
|
||||
@@ -0,0 +1,13 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { showSignInDialog } from 'coral-embed-stream/src/actions/login';
|
||||
import SignInButton from '../components//SignInButton';
|
||||
|
||||
const mapStateToProps = ({ auth }) => ({
|
||||
currentUser: auth.user,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch =>
|
||||
bindActionCreators({ showSignInDialog }, dispatch);
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(SignInButton);
|
||||
@@ -0,0 +1,12 @@
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators } from 'redux';
|
||||
import { logout } from 'coral-framework/actions/auth';
|
||||
import UserBox from '../components/UserBox';
|
||||
|
||||
const mapStateToProps = ({ auth }) => ({
|
||||
user: auth.user,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => bindActionCreators({ logout }, dispatch);
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(UserBox);
|
||||
@@ -15,6 +15,7 @@
|
||||
</head>
|
||||
<body>
|
||||
<div id="talk-login-container"></div>
|
||||
<script src='https://www.google.com/recaptcha/api.js?render=explicit' async defer></script>
|
||||
<script src="<%= STATIC_URL %>static/coral-login/bundle.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -7697,8 +7697,8 @@ react-portal@^4.1.2:
|
||||
prop-types "^15.5.8"
|
||||
|
||||
react-recaptcha@^2.2.6:
|
||||
version "2.3.5"
|
||||
resolved "https://registry.yarnpkg.com/react-recaptcha/-/react-recaptcha-2.3.5.tgz#a5db337125bb00fb13c2fa2e4ebfbe8b0cd06bb7"
|
||||
version "2.3.6"
|
||||
resolved "https://registry.yarnpkg.com/react-recaptcha/-/react-recaptcha-2.3.6.tgz#afe07b5552f3ea4d37ecd22d9881c2776719ec2b"
|
||||
|
||||
react-redux@^4.4.5:
|
||||
version "4.4.8"
|
||||
|
||||
Reference in New Issue
Block a user