Split sign up email messaging based on if user is under 13

This commit is contained in:
Georgi Angelov 2024-08-07 15:19:28 +03:00
parent 14132591e8
commit 0fb18c4650
4 changed files with 56 additions and 10 deletions

View file

@ -39,7 +39,7 @@ class EmailStep extends React.Component {
if (this.props.sendAnalytics) { if (this.props.sendAnalytics) {
this.props.sendAnalytics('join-email'); this.props.sendAnalytics('join-email');
} }
// automatically start with focus on username field // automatically start with focus on email field
if (this.emailInput) this.emailInput.focus(); if (this.emailInput) this.emailInput.focus();
} }
handleSetEmailRef (emailInputRef) { handleSetEmailRef (emailInputRef) {
@ -48,7 +48,7 @@ class EmailStep extends React.Component {
handleCaptchaLoad () { handleCaptchaLoad () {
this.setState({captchaIsLoading: false}); this.setState({captchaIsLoading: false});
} }
// simple function to memoize remote requests for usernames // simple function to memoize remote requests for emails
validateEmailRemotelyWithCache (email) { validateEmailRemotelyWithCache (email) {
if (Object.prototype.hasOwnProperty.call(this.emailRemoteCache, email)) { if (Object.prototype.hasOwnProperty.call(this.emailRemoteCache, email)) {
return Promise.resolve(this.emailRemoteCache[email]); return Promise.resolve(this.emailRemoteCache[email]);
@ -89,7 +89,7 @@ class EmailStep extends React.Component {
this.captchaRef.executeCaptcha(); this.captchaRef.executeCaptcha();
} }
handleCaptchaSolved (token) { handleCaptchaSolved (token) {
// Now thatcaptcha is done, we can tell Formik we're submitting. // Now that captcha is done, we can tell Formik we're submitting.
this.formikBag.setSubmitting(true); this.formikBag.setSubmitting(true);
this.formData['g-recaptcha-response'] = token; this.formData['g-recaptcha-response'] = token;
this.props.onNextStep(this.formData); this.props.onNextStep(this.formData);
@ -98,6 +98,14 @@ class EmailStep extends React.Component {
this.captchaRef = ref; this.captchaRef = ref;
} }
render () { render () {
const title = this.props.under13 ?
this.props.intl.formatMessage({id: 'registration.under13.emailStepTitle'}) :
this.props.intl.formatMessage({id: 'registration.emailStepTitle'});
const description = this.props.under13 ?
this.props.intl.formatMessage({id: 'registration.under13.emailStepDescription'}) :
undefined;
return ( return (
<Formik <Formik
initialValues={{ initialValues={{
@ -149,8 +157,9 @@ class EmailStep extends React.Component {
headerImgSrc="/images/join-flow/email-header.png" headerImgSrc="/images/join-flow/email-header.png"
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={title}
titleClassName="join-flow-email-title" titleClassName="join-flow-email-title"
description={description}
waiting={this.props.waiting || isSubmitting || this.state.captchaIsLoading} waiting={this.props.waiting || isSubmitting || this.state.captchaIsLoading}
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
@ -205,7 +214,8 @@ EmailStep.propTypes = {
onCaptchaError: PropTypes.func, onCaptchaError: PropTypes.func,
onNextStep: PropTypes.func, onNextStep: PropTypes.func,
sendAnalytics: PropTypes.func.isRequired, sendAnalytics: PropTypes.func.isRequired,
waiting: PropTypes.bool waiting: PropTypes.bool,
under13: PropTypes.bool
}; };

View file

@ -61,7 +61,7 @@ class JoinFlow extends React.Component {
formData: defaults({}, newFormData, this.state.formData) formData: defaults({}, newFormData, this.state.formData)
}; };
this.setState(newState, () => { this.setState(newState, () => {
this.handleSubmitRegistration(this.state.formData); this.handleSubmitRegistration(this.state.formData, this.isUnder13());
}); });
} }
getErrorsFromResponse (err, body, res) { getErrorsFromResponse (err, body, res) {
@ -175,7 +175,7 @@ class JoinFlow extends React.Component {
}); });
}); });
} }
handleSubmitRegistration (formData) { handleSubmitRegistration (formData, isUnder13) {
this.setState({ this.setState({
registrationError: null, // clear any existing error registrationError: null, // clear any existing error
waiting: true waiting: true
@ -191,6 +191,7 @@ class JoinFlow extends React.Component {
'password': formData.password, 'password': formData.password,
'birth_month': formData.birth_month, 'birth_month': formData.birth_month,
'birth_year': formData.birth_year, 'birth_year': formData.birth_year,
'under_13': isUnder13,
'g-recaptcha-response': formData['g-recaptcha-response'], 'g-recaptcha-response': formData['g-recaptcha-response'],
'gender': formData.gender, 'gender': formData.gender,
'country': formData.country, 'country': formData.country,
@ -213,7 +214,7 @@ class JoinFlow extends React.Component {
} }
handleErrorNext () { handleErrorNext () {
if (this.canTryAgain()) { if (this.canTryAgain()) {
this.handleSubmitRegistration(this.state.formData); this.handleSubmitRegistration(this.state.formData, this.isUnder13());
} else { } else {
this.resetState(); this.resetState();
} }
@ -229,6 +230,39 @@ class JoinFlow extends React.Component {
}); });
} }
parseDateComponent (fieldValue) {
// The dates are set to either `'null'` (before the BirthDateStep) or a string representation of a number.
if (fieldValue && fieldValue !== 'null') {
return Number(fieldValue);
} else {
return undefined;
}
}
isUnder13 () {
const birthYear = this.parseDateComponent(this.state.formData.birth_year);
const birthMonth = this.parseDateComponent(this.state.formData.birth_month);
if (!birthYear || !birthMonth) {
// We're not yet at the point where the user has specified their birth date
return false;
}
const now = new Date();
const yearDiff = now.getFullYear() - birthYear;
if (yearDiff > 13) {
return false;
} else if (yearDiff < 13) {
return true;
} else {
const currentMonth1Based = now.getMonth() + 1;
const monthsLeftToBirthday = birthMonth - currentMonth1Based;
return monthsLeftToBirthday >= 0;
}
}
render () { render () {
return ( return (
<main> <main>
@ -264,6 +298,7 @@ class JoinFlow extends React.Component {
<EmailStep <EmailStep
sendAnalytics={this.sendAnalytics} sendAnalytics={this.sendAnalytics}
waiting={this.state.waiting} waiting={this.state.waiting}
under13={this.isUnder13()}
onCaptchaError={this.handleCaptchaError} onCaptchaError={this.handleCaptchaError}
onNextStep={this.handlePrepareToRegister} onNextStep={this.handlePrepareToRegister}
/> />

View file

@ -208,6 +208,8 @@
"registration.genderOptionAnother": "Another gender:", "registration.genderOptionAnother": "Another gender:",
"registration.genderOptionPreferNotToSay": "Prefer not to say", "registration.genderOptionPreferNotToSay": "Prefer not to say",
"registration.emailStepTitle": "What's your email?", "registration.emailStepTitle": "What's your email?",
"registration.under13.emailStepTitle": "What's your parent/adult email address?",
"registration.under13.emailStepDescription": "They will need to verify your account through an email link.",
"registration.emailStepInfo": "This will help if you forget your password. This information will not be made public on your account.", "registration.emailStepInfo": "This will help if you forget your password. This information will not be made public on your account.",
"registration.goToClass": "Go to Class", "registration.goToClass": "Go to Class",
"registration.invitedBy": "invited by", "registration.invitedBy": "invited by",

View file

@ -16,7 +16,6 @@ const Register = () => (
src="/images/logo_sm.png" src="/images/logo_sm.png"
/> />
</a> </a>
</nav> </nav>
<Scratch3Registration <Scratch3Registration
createProjectOnComplete createProjectOnComplete