mirror of
https://github.com/wassname/talk.git
synced 2026-08-14 12:50:17 +08:00
Merge branch 'master' into forgot-password-change
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
.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: 320px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
font-family: Helvetica,Helvetica Neue,Verdana,sans-serif;
|
||||
color:#3B4A53;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 1.3em;
|
||||
margin: 15px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 1em;
|
||||
line-height: 20px;
|
||||
margin: 0;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.list {
|
||||
padding: 0;
|
||||
margin: 20px 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.itemIcon {
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.text {
|
||||
flex-grow: 1;
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
> i.itemIcon {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
color: #787D80;
|
||||
border-radius: 2px;
|
||||
background-color: transparent;
|
||||
height: 30px;
|
||||
font-size: 0.9em;
|
||||
line-height: normal;
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
line-height: 30px;
|
||||
font-size: 1em;
|
||||
|
||||
&:hover {
|
||||
background-color: #eaeaea;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&.cancel {
|
||||
background-color: transparent;
|
||||
color: #787D80;
|
||||
}
|
||||
|
||||
&.proceed {
|
||||
background-color: #3498DB;
|
||||
color: white;
|
||||
}
|
||||
|
||||
&.danger {
|
||||
background-color: #FA4643;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
import React from 'react';
|
||||
import isMatch from 'lodash/isEqual';
|
||||
import isEqualWith from 'lodash/isEqual';
|
||||
import PropTypes from 'prop-types';
|
||||
import { Dialog } from 'plugin-api/beta/client/components/ui';
|
||||
import validate from 'coral-framework/helpers/validate';
|
||||
import errorMsj from 'coral-framework/helpers/error';
|
||||
import { getErrorMessages } from 'coral-framework/utils';
|
||||
import styles from './AddEmailAddressDialog.css';
|
||||
|
||||
import AddEmailContent from './AddEmailContent';
|
||||
import VerifyEmailAddress from './VerifyEmailAddress';
|
||||
import EmailAddressAdded from './EmailAddressAdded';
|
||||
|
||||
const initialState = {
|
||||
step: 0,
|
||||
showErrors: false,
|
||||
errors: {},
|
||||
formData: {},
|
||||
};
|
||||
|
||||
class AddEmailAddressDialog extends React.Component {
|
||||
state = initialState;
|
||||
validKeys = ['emailAddress', 'confirmPassword', 'confirmEmailAddress'];
|
||||
|
||||
onChange = e => {
|
||||
const { name, value, type } = e.target;
|
||||
this.setState(
|
||||
state => ({
|
||||
formData: {
|
||||
...state.formData,
|
||||
[name]: value,
|
||||
},
|
||||
}),
|
||||
() => {
|
||||
this.fieldValidation(value, type, name);
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
fieldValidation = (value, type, name) => {
|
||||
if (!value.length) {
|
||||
this.addError({
|
||||
[name]: 'Field is required',
|
||||
});
|
||||
} else if (!validate[type](value)) {
|
||||
this.addError({ [name]: errorMsj[type] });
|
||||
} else {
|
||||
this.removeError(name);
|
||||
}
|
||||
};
|
||||
|
||||
addError = err => {
|
||||
this.setState(({ errors }) => ({
|
||||
errors: { ...errors, ...err },
|
||||
}));
|
||||
};
|
||||
|
||||
removeError = errKey => {
|
||||
this.setState(state => {
|
||||
const { [errKey]: _, ...errors } = state.errors;
|
||||
return {
|
||||
errors,
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
hasError = err => {
|
||||
return Object.keys(this.state.errors).indexOf(err) !== -1;
|
||||
};
|
||||
|
||||
formHasError = () => {
|
||||
const formHasErrors = !!Object.keys(this.state.errors).length;
|
||||
const formIncomplete = !isEqualWith(
|
||||
Object.keys(this.state.formData),
|
||||
this.validKeys,
|
||||
isMatch
|
||||
);
|
||||
|
||||
return formHasErrors || formIncomplete;
|
||||
};
|
||||
|
||||
showErrors = () => {
|
||||
this.setState({
|
||||
showErrors: true,
|
||||
});
|
||||
};
|
||||
|
||||
confirmChanges = async () => {
|
||||
if (this.formHasError()) {
|
||||
this.showErrors();
|
||||
return;
|
||||
}
|
||||
|
||||
const { emailAddress, confirmPassword } = this.state.formData;
|
||||
const { attachLocalAuth } = this.props;
|
||||
|
||||
try {
|
||||
await attachLocalAuth({
|
||||
email: emailAddress,
|
||||
password: confirmPassword,
|
||||
});
|
||||
this.props.notify('success', 'Email Added!');
|
||||
this.goToNextStep();
|
||||
} catch (err) {
|
||||
this.props.notify('error', getErrorMessages(err));
|
||||
}
|
||||
};
|
||||
|
||||
goToNextStep = () => {
|
||||
this.setState(({ step }) => ({
|
||||
step: step + 1,
|
||||
}));
|
||||
};
|
||||
|
||||
render() {
|
||||
const { errors, formData, showErrors, step } = this.state;
|
||||
const { root: { settings } } = this.props;
|
||||
|
||||
return (
|
||||
<Dialog className={styles.dialog} open={true}>
|
||||
{step === 0 && (
|
||||
<AddEmailContent
|
||||
formData={formData}
|
||||
errors={errors}
|
||||
showErrors={showErrors}
|
||||
confirmChanges={this.confirmChanges}
|
||||
onChange={this.onChange}
|
||||
/>
|
||||
)}
|
||||
{step === 1 &&
|
||||
!settings.requireEmailConfirmation && (
|
||||
<EmailAddressAdded done={() => {}} />
|
||||
)}
|
||||
{step === 1 &&
|
||||
settings.requireEmailConfirmation && (
|
||||
<VerifyEmailAddress
|
||||
emailAddress={formData.emailAddress}
|
||||
done={() => {}}
|
||||
/>
|
||||
)}
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
AddEmailAddressDialog.propTypes = {
|
||||
attachLocalAuth: PropTypes.func.isRequired,
|
||||
notify: PropTypes.func.isRequired,
|
||||
root: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
export default AddEmailAddressDialog;
|
||||
@@ -0,0 +1,106 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import styles from './AddEmailAddressDialog.css';
|
||||
import { Icon } from 'plugin-api/beta/client/components/ui';
|
||||
import cn from 'classnames';
|
||||
import InputField from './InputField';
|
||||
import { t } from 'plugin-api/beta/client/services';
|
||||
|
||||
const AddEmailContent = ({
|
||||
formData,
|
||||
errors,
|
||||
showErrors,
|
||||
confirmChanges,
|
||||
onChange,
|
||||
}) => (
|
||||
<div>
|
||||
<h4 className={styles.title}>
|
||||
{t('talk-plugin-local-auth.add_email.content.title')}
|
||||
</h4>
|
||||
<p className={styles.description}>
|
||||
{t('talk-plugin-local-auth.add_email.content.description')}
|
||||
</p>
|
||||
<ul className={styles.list}>
|
||||
<li className={styles.item}>
|
||||
<Icon name="done" className={styles.itemIcon} />
|
||||
<span className={styles.text}>
|
||||
{t('talk-plugin-local-auth.add_email.content.item_1')}
|
||||
</span>
|
||||
</li>
|
||||
<li className={styles.item}>
|
||||
<Icon name="done" className={styles.itemIcon} />
|
||||
<span className={styles.text}>
|
||||
{t('talk-plugin-local-auth.add_email.content.item_2')}
|
||||
</span>
|
||||
</li>
|
||||
<li className={styles.item}>
|
||||
<Icon name="done" className={styles.itemIcon} />
|
||||
<span className={styles.text}>
|
||||
{t('talk-plugin-local-auth.add_email.content.item_3')}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<form autoComplete="off">
|
||||
<InputField
|
||||
id="emailAddress"
|
||||
label={t('talk-plugin-local-auth.add_email.enter_email_address')}
|
||||
name="emailAddress"
|
||||
type="email"
|
||||
onChange={onChange}
|
||||
defaultValue=""
|
||||
hasError={!formData.emailAddress || errors.emailAddress}
|
||||
errorMsg={t('talk-plugin-local-auth.add_email.invalid_email_address')}
|
||||
showError={showErrors}
|
||||
columnDisplay
|
||||
showSuccess={false}
|
||||
/>
|
||||
<InputField
|
||||
id="confirmEmailAddress"
|
||||
label={t('talk-plugin-local-auth.add_email.confirm_email_address')}
|
||||
name="confirmEmailAddress"
|
||||
type="email"
|
||||
onChange={onChange}
|
||||
defaultValue=""
|
||||
hasError={
|
||||
!formData.emailAddress ||
|
||||
formData.emailAddress !== formData.confirmEmailAddress
|
||||
}
|
||||
errorMsg={t('talk-plugin-local-auth.add_email.email_does_not_match')}
|
||||
showError={showErrors}
|
||||
columnDisplay
|
||||
showSuccess={false}
|
||||
/>
|
||||
<InputField
|
||||
id="confirmPassword"
|
||||
label={t('talk-plugin-local-auth.add_email.insert_password')}
|
||||
name="confirmPassword"
|
||||
type="password"
|
||||
onChange={onChange}
|
||||
defaultValue=""
|
||||
hasError={!formData.confirmPassword}
|
||||
showError={showErrors}
|
||||
columnDisplay
|
||||
showSuccess={false}
|
||||
/>
|
||||
<div className={styles.actions}>
|
||||
<a
|
||||
className={cn(styles.button, styles.proceed)}
|
||||
onClick={confirmChanges}
|
||||
>
|
||||
{t('talk-plugin-local-auth.add_email.add_email_address')}
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
|
||||
AddEmailContent.propTypes = {
|
||||
formData: PropTypes.object.isRequired,
|
||||
errors: PropTypes.object.isRequired,
|
||||
showErrors: PropTypes.bool.isRequired,
|
||||
confirmChanges: PropTypes.func.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default AddEmailContent;
|
||||
@@ -0,0 +1,32 @@
|
||||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import styles from './AddEmailAddressDialog.css';
|
||||
import { t } from 'plugin-api/beta/client/services';
|
||||
|
||||
const EmailAddressAdded = ({ done }) => (
|
||||
<div>
|
||||
<h4 className={styles.title}>
|
||||
{t('talk-plugin-local-auth.add_email.added.title')}
|
||||
</h4>
|
||||
<p className={styles.description}>
|
||||
{t('talk-plugin-local-auth.add_email.added.description')}
|
||||
</p>
|
||||
<strong>{t('talk-plugin-local-auth.add_email.added.subtitle')}</strong>
|
||||
<p className={styles.description}>
|
||||
{t('talk-plugin-local-auth.add_email.added.description_2')}{' '}
|
||||
<strong>{t('talk-plugin-local-auth.add_email.added.path')}</strong>.
|
||||
</p>
|
||||
<div className={styles.actions}>
|
||||
<a className={cn(styles.button, styles.proceed)} onClick={done}>
|
||||
{t('talk-plugin-local-auth.add_email.done')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
EmailAddressAdded.propTypes = {
|
||||
done: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default EmailAddressAdded;
|
||||
@@ -62,6 +62,7 @@
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.detailItemMessage {
|
||||
@@ -81,4 +82,4 @@
|
||||
|
||||
.warningIcon {
|
||||
color: #FA4643;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ const InputField = ({
|
||||
<div
|
||||
className={cn(
|
||||
styles.detailInput,
|
||||
{ [styles.error]: hasError },
|
||||
{ [styles.error]: hasError && showError },
|
||||
{ [styles.disabled]: disabled }
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import React from 'react';
|
||||
import cn from 'classnames';
|
||||
import PropTypes from 'prop-types';
|
||||
import styles from './AddEmailAddressDialog.css';
|
||||
import { t } from 'plugin-api/beta/client/services';
|
||||
|
||||
const VerifyEmailAddress = ({ emailAddress, done }) => (
|
||||
<div>
|
||||
<h4 className={styles.title}>
|
||||
{t('talk-plugin-local-auth.add_email.verify.title')}
|
||||
</h4>
|
||||
<p className={styles.description}>
|
||||
{t('talk-plugin-local-auth.add_email.verify.description', emailAddress)}
|
||||
</p>
|
||||
<div className={styles.actions}>
|
||||
<a className={cn(styles.button, styles.proceed)} onClick={done}>
|
||||
{t('talk-plugin-local-auth.add_email.done')}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
VerifyEmailAddress.propTypes = {
|
||||
emailAddress: PropTypes.string.isRequired,
|
||||
done: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default VerifyEmailAddress;
|
||||
Reference in New Issue
Block a user