mirror of
https://github.com/scratchfoundation/scratch-www.git
synced 2024-12-02 03:47:28 -05:00
Merge pull request #3276 from picklesrus/captcha-join
Initial work for captcha in new join flow.
This commit is contained in:
commit
73c353ba2c
3 changed files with 74 additions and 4 deletions
|
@ -33,6 +33,11 @@ env:
|
||||||
- CLOUDDATA_HOST_VAR=CLOUDDATA_HOST_$TRAVIS_BRANCH
|
- CLOUDDATA_HOST_VAR=CLOUDDATA_HOST_$TRAVIS_BRANCH
|
||||||
- CLOUDDATA_HOST=${!CLOUDDATA_HOST_VAR}
|
- CLOUDDATA_HOST=${!CLOUDDATA_HOST_VAR}
|
||||||
- CLOUDDATA_HOST=${CLOUDDATA_HOST:-$CLOUDDATA_HOST_STAGING}
|
- CLOUDDATA_HOST=${CLOUDDATA_HOST:-$CLOUDDATA_HOST_STAGING}
|
||||||
|
- RECAPTCHA_SITE_KEY_master=6LeRbUwUAAAAAFYhKgk3G9OKWqE_OJ7Z-7VTUCbl
|
||||||
|
- RECAPTCHA_SITE_KEY_STAGING=6LfukK4UAAAAAFR44yoZMhv8fj6xh-PMiIxwryG3
|
||||||
|
- RECAPTCHA_SITE=RECAPTCHA_SITE_KEY_$TRAVIS_BRANCH
|
||||||
|
- RECAPTCHA_SITE_KEY=${!RECAPTCHA_SITE_KEY_VAR}
|
||||||
|
- RECAPTCHA_SITE_KEY=${RECAPTCHA_SITE_KEY:-$RECAPTCHA_SITE_KEY_STAGING}
|
||||||
- ROOT_URL_master=https://scratch.mit.edu
|
- ROOT_URL_master=https://scratch.mit.edu
|
||||||
- ROOT_URL_STAGING=https://scratch.ly
|
- ROOT_URL_STAGING=https://scratch.ly
|
||||||
- ROOT_URL_VAR=ROOT_URL_$TRAVIS_BRANCH
|
- ROOT_URL_VAR=ROOT_URL_$TRAVIS_BRANCH
|
||||||
|
|
|
@ -20,16 +20,59 @@ class EmailStep extends React.Component {
|
||||||
'handleSetEmailRef',
|
'handleSetEmailRef',
|
||||||
'handleValidSubmit',
|
'handleValidSubmit',
|
||||||
'validateEmail',
|
'validateEmail',
|
||||||
'validateForm'
|
'validateForm',
|
||||||
|
'setCaptchaRef',
|
||||||
|
'captchaSolved',
|
||||||
|
'onCaptchaLoad',
|
||||||
|
'onCaptchaError'
|
||||||
]);
|
]);
|
||||||
|
this.state = {
|
||||||
|
captchaIsLoading: true
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
// automatically start with focus on username field
|
// automatically start with focus on username field
|
||||||
if (this.emailInput) this.emailInput.focus();
|
if (this.emailInput) this.emailInput.focus();
|
||||||
|
|
||||||
|
// If grecaptcha doesn't exist on window, we havent loaded the captcha js yet. Load it.
|
||||||
|
if (!window.grecaptcha) {
|
||||||
|
// ReCaptcha calls a callback when the grecatpcha object is usable. That callback
|
||||||
|
// needs to be global so set it on the window.
|
||||||
|
window.grecaptchaOnLoad = this.onCaptchaLoad;
|
||||||
|
// Load Google ReCaptcha script.
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.async = true;
|
||||||
|
script.onerror = this.onCaptchaError;
|
||||||
|
script.src = `https://www.recaptcha.net/recaptcha/api.js?onload=grecaptchaOnLoad&render=explicit&hl=${window._locale}`;
|
||||||
|
document.body.appendChild(script);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
componentWillUnmount () {
|
||||||
|
window.grecaptchaOnLoad = null;
|
||||||
}
|
}
|
||||||
handleSetEmailRef (emailInputRef) {
|
handleSetEmailRef (emailInputRef) {
|
||||||
this.emailInput = emailInputRef;
|
this.emailInput = emailInputRef;
|
||||||
}
|
}
|
||||||
|
onCaptchaError () {
|
||||||
|
// TODO send user to error step once we have one.
|
||||||
|
}
|
||||||
|
onCaptchaLoad () {
|
||||||
|
this.setState({captchaIsLoading: false});
|
||||||
|
this.grecaptcha = window.grecaptcha;
|
||||||
|
if (!this.grecaptcha) {
|
||||||
|
// According to the reCaptcha documentation, this callback shouldn't get
|
||||||
|
// called unless window.grecaptcha exists. This is just here to be extra defensive.
|
||||||
|
// TODO: Put up the error screen when we have one.
|
||||||
|
}
|
||||||
|
// TODO: Add in error callback for render once we have an error screen.
|
||||||
|
this.widgetId = this.grecaptcha.render(this.captchaRef,
|
||||||
|
{
|
||||||
|
callback: this.captchaSolved,
|
||||||
|
sitekey: process.env.RECAPTCHA_SITE_KEY
|
||||||
|
},
|
||||||
|
true);
|
||||||
|
}
|
||||||
validateEmail (email) {
|
validateEmail (email) {
|
||||||
if (!email) return this.props.intl.formatMessage({id: 'general.required'});
|
if (!email) return this.props.intl.formatMessage({id: 'general.required'});
|
||||||
const isValidLocally = emailValidator.validate(email);
|
const isValidLocally = emailValidator.validate(email);
|
||||||
|
@ -42,8 +85,21 @@ class EmailStep extends React.Component {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
handleValidSubmit (formData, formikBag) {
|
handleValidSubmit (formData, formikBag) {
|
||||||
formikBag.setSubmitting(false);
|
this.formData = formData;
|
||||||
this.props.onNextStep(formData);
|
this.formikBag = formikBag;
|
||||||
|
// Change set submitting to false so that if the user clicks out of
|
||||||
|
// the captcha, the button is clickable again (instead of a disabled button with a spinner).
|
||||||
|
this.formikBag.setSubmitting(false);
|
||||||
|
this.grecaptcha.execute(this.widgetId);
|
||||||
|
}
|
||||||
|
captchaSolved (token) {
|
||||||
|
// Now thatcaptcha is done, we can tell Formik we're submitting.
|
||||||
|
this.formikBag.setSubmitting(true);
|
||||||
|
this.formData['g-recaptcha-response'] = token;
|
||||||
|
this.props.onNextStep(this.formData);
|
||||||
|
}
|
||||||
|
setCaptchaRef (ref) {
|
||||||
|
this.captchaRef = ref;
|
||||||
}
|
}
|
||||||
render () {
|
render () {
|
||||||
return (
|
return (
|
||||||
|
@ -88,7 +144,7 @@ class EmailStep extends React.Component {
|
||||||
innerClassName="join-flow-inner-email-step"
|
innerClassName="join-flow-inner-email-step"
|
||||||
nextButton={this.props.intl.formatMessage({id: 'registration.createAccount'})}
|
nextButton={this.props.intl.formatMessage({id: 'registration.createAccount'})}
|
||||||
title={this.props.intl.formatMessage({id: 'registration.emailStepTitle'})}
|
title={this.props.intl.formatMessage({id: 'registration.emailStepTitle'})}
|
||||||
waiting={isSubmitting}
|
waiting={isSubmitting || this.state.captchaIsLoading}
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
>
|
>
|
||||||
<FormikInput
|
<FormikInput
|
||||||
|
@ -116,6 +172,13 @@ class EmailStep extends React.Component {
|
||||||
name="subscribe"
|
name="subscribe"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<div
|
||||||
|
className="g-recaptcha"
|
||||||
|
data-badge="bottomright"
|
||||||
|
data-sitekey={process.env.RECAPTCHA_SITE_KEY}
|
||||||
|
data-size="invisible"
|
||||||
|
ref={this.setCaptchaRef}
|
||||||
|
/>
|
||||||
</JoinFlowStep>
|
</JoinFlowStep>
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -173,6 +173,8 @@ module.exports = {
|
||||||
new webpack.DefinePlugin({
|
new webpack.DefinePlugin({
|
||||||
'process.env.NODE_ENV': '"' + (process.env.NODE_ENV || 'development') + '"',
|
'process.env.NODE_ENV': '"' + (process.env.NODE_ENV || 'development') + '"',
|
||||||
'process.env.API_HOST': '"' + (process.env.API_HOST || 'https://api.scratch.mit.edu') + '"',
|
'process.env.API_HOST': '"' + (process.env.API_HOST || 'https://api.scratch.mit.edu') + '"',
|
||||||
|
'process.env.RECAPTCHA_SITE_KEY': '"' +
|
||||||
|
(process.env.RECAPTCHA_SITE_KEY || '6Lf6kK4UAAAAABKTyvdSqgcSVASEnMrCquiAkjVW') + '"',
|
||||||
'process.env.ASSET_HOST': '"' + (process.env.ASSET_HOST || 'https://assets.scratch.mit.edu') + '"',
|
'process.env.ASSET_HOST': '"' + (process.env.ASSET_HOST || 'https://assets.scratch.mit.edu') + '"',
|
||||||
'process.env.BACKPACK_HOST': '"' + (process.env.BACKPACK_HOST || 'https://backpack.scratch.mit.edu') + '"',
|
'process.env.BACKPACK_HOST': '"' + (process.env.BACKPACK_HOST || 'https://backpack.scratch.mit.edu') + '"',
|
||||||
'process.env.CLOUDDATA_HOST': '"' + (process.env.CLOUDDATA_HOST || 'clouddata.scratch.mit.edu') + '"',
|
'process.env.CLOUDDATA_HOST': '"' + (process.env.CLOUDDATA_HOST || 'clouddata.scratch.mit.edu') + '"',
|
||||||
|
|
Loading…
Reference in a new issue