Merge branch 'develop' into arbiter-management

This commit is contained in:
Daniel Ternyak 2019-02-10 15:53:29 -06:00 committed by GitHub
commit f15c110f92
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 104 additions and 15 deletions

View File

@ -156,10 +156,7 @@ class User(db.Model, UserMixin):
db.session.commit()
if _send_email:
send_email(user.email_address, 'signup', {
'display_name': user.display_name,
'confirm_url': make_url(f'/email/verify?code={ev.code}')
})
user.send_verification_email()
return user
@ -212,6 +209,12 @@ class User(db.Model, UserMixin):
def login(self):
login_user(self)
def send_verification_email(self):
send_email(self.email_address, 'signup', {
'display_name': self.display_name,
'confirm_url': make_url(f'/email/verify?code={self.email_verification.code}')
})
def send_recovery_email(self):
existing = self.email_recovery
if existing:

View File

@ -172,11 +172,18 @@ def update_user_password(current_password, password):
def update_user_email(email, password):
if not g.current_user.check_password(password):
return {"message": "Password is incorrect"}, 403
print('set_email')
g.current_user.set_email(email)
return None, 200
@blueprint.route("/me/resend-verification", methods=["PUT"])
@requires_auth
@endpoint.api()
def resend_email_verification():
g.current_user.send_verification_email()
return None, 200
@blueprint.route("/logout", methods=["POST"])
@requires_auth
@endpoint.api()

View File

@ -306,3 +306,7 @@ export function getRFP(rfpId: number | string): Promise<{ data: RFP }> {
return res;
});
}
export function resendEmailVerification(): Promise<{ data: void }> {
return axios.put(`/api/v1/users/me/resend-verification`);
}

View File

@ -33,6 +33,7 @@ const commands: { [key in MARKDOWN_TYPE]: ReactMdeProps['commands'] } = {
};
interface Props {
readOnly?: boolean | null;
type?: MARKDOWN_TYPE;
initialMarkdown?: string;
onChange(markdown: string): void;
@ -62,6 +63,7 @@ export default class MarkdownEditor extends React.PureComponent<Props, State> {
render() {
const type = this.props.type || MARKDOWN_TYPE.FULL;
const { readOnly } = this.props;
const { mdeState, randomKey } = this.state;
return (
<div
@ -76,6 +78,7 @@ export default class MarkdownEditor extends React.PureComponent<Props, State> {
editorState={mdeState as ReactMdeTypes.MdeState}
generateMarkdownPreview={this.generatePreview}
commands={commands[type]}
readOnly={!!readOnly}
layout="tabbed"
/>
</div>

View File

@ -9,7 +9,7 @@ import {
getIsFetchingComments,
getProposalComments,
} from 'modules/proposals/selectors';
import { getIsVerified } from 'modules/auth/selectors';
import { getIsVerified, getIsSignedIn } from 'modules/auth/selectors';
import Comments from 'components/Comments';
import Placeholder from 'components/Placeholder';
import Loader from 'components/Loader';
@ -27,6 +27,7 @@ interface StateProps {
isPostCommentPending: AppState['proposal']['isPostCommentPending'];
postCommentError: AppState['proposal']['postCommentError'];
isVerified: ReturnType<typeof getIsVerified>;
isSignedIn: ReturnType<typeof getIsSignedIn>;
}
interface DispatchProps {
@ -79,6 +80,7 @@ class ProposalComments extends React.Component<Props, State> {
commentsError,
isPostCommentPending,
isVerified,
isSignedIn,
} = this.props;
const { comment } = this.state;
let content = null;
@ -108,7 +110,7 @@ class ProposalComments extends React.Component<Props, State> {
return (
<div>
<div className="ProposalComments-post">
{isVerified ? (
{isVerified && (
<>
<MarkdownEditor
ref={el => (this.editor = el)}
@ -124,12 +126,20 @@ class ProposalComments extends React.Component<Props, State> {
Submit comment
</Button>
</>
) : (
<Placeholder
title="Your email is not verified"
subtitle="Please verify your email to post a comment."
/>
)}
{isSignedIn &&
!isVerified && (
<>
<h4 className="ProposalComments-verify">
Please verify your email to post a comment.
</h4>
<MarkdownEditor
onChange={this.handleCommentChange}
type={MARKDOWN_TYPE.REDUCED}
readOnly={true}
/>
</>
)}
</div>
{content}
</div>
@ -153,6 +163,7 @@ export default connect<StateProps, DispatchProps, OwnProps, AppState>(
isPostCommentPending: state.proposal.isPostCommentPending,
postCommentError: state.proposal.postCommentError,
isVerified: getIsVerified(state),
isSignedIn: getIsSignedIn(state),
}),
{
fetchProposalComments,

View File

@ -3,4 +3,9 @@
margin-bottom: 2rem;
max-width: 780px;
}
&-verify {
font-size: 1rem;
opacity: 0.7;
margin-bottom: 1rem;
}
}

View File

@ -1,5 +1,10 @@
.AccountSettings {
&-form {
&-resend {
position: relative;
margin-bottom: 1rem;
}
& > .ant-form-item {
margin-bottom: 0.75rem;
}

View File

@ -1,13 +1,15 @@
import React from 'react';
import { connect } from 'react-redux';
import { Form, Input, Button, Alert } from 'antd';
import { Form, Input, Button, Alert, message } from 'antd';
import { FormComponentProps } from 'antd/lib/form';
import { updateUserEmail } from 'api/api';
import Loader from 'components/Loader';
import { updateUserEmail, resendEmailVerification } from 'api/api';
import { AppState } from 'store/reducers';
import './Account.less';
interface StateProps {
email: string;
emailVerified: boolean;
}
type Props = FormComponentProps & StateProps;
@ -17,6 +19,8 @@ const STATE = {
emailChangePending: false,
emailChangeSuccess: false,
emailChangeError: '',
isResendingVerification: false,
hasResentVerification: false,
};
type State = typeof STATE;
@ -25,12 +29,14 @@ class AccountSettings extends React.Component<Props, State> {
state: State = { ...STATE };
render() {
const { email, form } = this.props;
const { email, emailVerified, form } = this.props;
const {
emailChangeError,
emailChangePending,
emailChangeSuccess,
newEmail,
isResendingVerification,
hasResentVerification,
} = this.state;
return (
@ -40,6 +46,36 @@ class AccountSettings extends React.Component<Props, State> {
onSubmit={this.handleSubmit}
layout="vertical"
>
{!emailVerified &&
!hasResentVerification && (
<Alert
showIcon
className="AccountSettings-form-resend"
type="warning"
message="Your email has not been verified"
description={
<>
You should have a verification in your inbox. If you can't find it,
check your spam folder. Still don't see it?{' '}
<a onClick={this.resendEmailVerification}>Click here to resend</a>.
{isResendingVerification && <Loader overlay />}
</>
}
/>
)}
{!emailVerified &&
hasResentVerification && (
<Alert
showIcon
className="AccountSettings-form-resend"
type="success"
message="Verification has been sent"
description={`
It should be in your inbox shortly. If you dont see it soon,
check your spam folder or contact support for help.
`}
/>
)}
<Form.Item label="Email">
{form.getFieldDecorator('email', {
initialValue: newEmail || email,
@ -139,10 +175,25 @@ class AccountSettings extends React.Component<Props, State> {
}
});
};
private resendEmailVerification = async () => {
if (this.state.isResendingVerification) {
return;
}
this.setState({ isResendingVerification: true });
try {
await resendEmailVerification();
this.setState({ hasResentVerification: true });
} catch (err) {
message.error(err.message || err.toString());
}
this.setState({ isResendingVerification: false });
};
}
const FormWrappedAccountSettings = Form.create()(AccountSettings);
export default connect<StateProps, {}, {}, AppState>(state => ({
email: state.auth.user ? state.auth.user.emailAddress || '' : '',
emailVerified: !!state.auth.user && !!state.auth.user.emailVerified,
}))(FormWrappedAccountSettings);