From 0fb18c465041337e314b268c13b0f7535c15cf94 Mon Sep 17 00:00:00 2001 From: Georgi Angelov Date: Wed, 7 Aug 2024 15:19:28 +0300 Subject: [PATCH 01/73] Split sign up email messaging based on if user is under 13 --- src/components/join-flow/email-step.jsx | 20 +++++++++--- src/components/join-flow/join-flow.jsx | 41 +++++++++++++++++++++++-- src/l10n.json | 4 ++- src/views/join/join.jsx | 1 - 4 files changed, 56 insertions(+), 10 deletions(-) diff --git a/src/components/join-flow/email-step.jsx b/src/components/join-flow/email-step.jsx index 6195563f7..c90bea304 100644 --- a/src/components/join-flow/email-step.jsx +++ b/src/components/join-flow/email-step.jsx @@ -39,7 +39,7 @@ class EmailStep extends React.Component { if (this.props.sendAnalytics) { 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(); } handleSetEmailRef (emailInputRef) { @@ -48,7 +48,7 @@ class EmailStep extends React.Component { handleCaptchaLoad () { this.setState({captchaIsLoading: false}); } - // simple function to memoize remote requests for usernames + // simple function to memoize remote requests for emails validateEmailRemotelyWithCache (email) { if (Object.prototype.hasOwnProperty.call(this.emailRemoteCache, email)) { return Promise.resolve(this.emailRemoteCache[email]); @@ -89,7 +89,7 @@ class EmailStep extends React.Component { this.captchaRef.executeCaptcha(); } 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.formData['g-recaptcha-response'] = token; this.props.onNextStep(this.formData); @@ -98,6 +98,14 @@ class EmailStep extends React.Component { this.captchaRef = ref; } 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 ( @@ -205,7 +214,8 @@ EmailStep.propTypes = { onCaptchaError: PropTypes.func, onNextStep: PropTypes.func, sendAnalytics: PropTypes.func.isRequired, - waiting: PropTypes.bool + waiting: PropTypes.bool, + under13: PropTypes.bool }; diff --git a/src/components/join-flow/join-flow.jsx b/src/components/join-flow/join-flow.jsx index dfc5bbdef..88390150f 100644 --- a/src/components/join-flow/join-flow.jsx +++ b/src/components/join-flow/join-flow.jsx @@ -61,7 +61,7 @@ class JoinFlow extends React.Component { formData: defaults({}, newFormData, this.state.formData) }; this.setState(newState, () => { - this.handleSubmitRegistration(this.state.formData); + this.handleSubmitRegistration(this.state.formData, this.isUnder13()); }); } getErrorsFromResponse (err, body, res) { @@ -175,7 +175,7 @@ class JoinFlow extends React.Component { }); }); } - handleSubmitRegistration (formData) { + handleSubmitRegistration (formData, isUnder13) { this.setState({ registrationError: null, // clear any existing error waiting: true @@ -191,6 +191,7 @@ class JoinFlow extends React.Component { 'password': formData.password, 'birth_month': formData.birth_month, 'birth_year': formData.birth_year, + 'under_13': isUnder13, 'g-recaptcha-response': formData['g-recaptcha-response'], 'gender': formData.gender, 'country': formData.country, @@ -213,7 +214,7 @@ class JoinFlow extends React.Component { } handleErrorNext () { if (this.canTryAgain()) { - this.handleSubmitRegistration(this.state.formData); + this.handleSubmitRegistration(this.state.formData, this.isUnder13()); } else { 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 () { return (
@@ -264,6 +298,7 @@ class JoinFlow extends React.Component { diff --git a/src/l10n.json b/src/l10n.json index 0c66069e4..e7e9d479f 100644 --- a/src/l10n.json +++ b/src/l10n.json @@ -208,6 +208,8 @@ "registration.genderOptionAnother": "Another gender:", "registration.genderOptionPreferNotToSay": "Prefer not to say", "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.goToClass": "Go to Class", "registration.invitedBy": "invited by", @@ -462,7 +464,7 @@ "bluetooth.enableLocationServicesTitle": "Make sure you have location services enabled on Chromebooks or Android tablets", "bluetooth.enableLocationServicesText": "Bluetooth can be used to provide location data to the app. In addition to granting the Scratch App permission to access location, location must be enabled in your general device settings. Search for 'Location' in your settings, and make sure it is on. On Chromebooks search for 'Location' in the Google Play Store Android preferences.", "privacyBanner.update": "The Scratch privacy policy has been updated, effective May 25, 2023. You can see the new policy here.", - + "renameAccount.accountBlocked": "Account Blocked", "renameAccount.toRecover": "To recover access to your account, change your username.", "renameAccount.yourScratchAccount": "Your scratch account has been temporarily blocked because your username appears to contain personal information.", diff --git a/src/views/join/join.jsx b/src/views/join/join.jsx index f394d6dcc..ec663cd50 100644 --- a/src/views/join/join.jsx +++ b/src/views/join/join.jsx @@ -16,7 +16,6 @@ const Register = () => ( src="/images/logo_sm.png" /> - Date: Fri, 16 Aug 2024 17:39:29 +0300 Subject: [PATCH 02/73] Change messaging on welcome step of registration for under 13 --- src/components/join-flow/join-flow.jsx | 1 + src/components/join-flow/welcome-step.jsx | 9 +++++++-- src/l10n.json | 1 + 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/join-flow/join-flow.jsx b/src/components/join-flow/join-flow.jsx index 88390150f..fd4bc015e 100644 --- a/src/components/join-flow/join-flow.jsx +++ b/src/components/join-flow/join-flow.jsx @@ -307,6 +307,7 @@ class JoinFlow extends React.Component { email={this.state.formData.email} sendAnalytics={this.sendAnalytics} username={this.state.formData.username} + under13={this.isUnder13()} onNextStep={this.props.onCompleteRegistration} /> diff --git a/src/components/join-flow/welcome-step.jsx b/src/components/join-flow/welcome-step.jsx index b3f19a06f..a8807ef06 100644 --- a/src/components/join-flow/welcome-step.jsx +++ b/src/components/join-flow/welcome-step.jsx @@ -75,7 +75,11 @@ class WelcomeStep extends React.Component {
Date: Fri, 16 Aug 2024 17:59:06 +0300 Subject: [PATCH 03/73] Add "Under-13 not approved" page --- src/redux/session.js | 8 ++++++++ src/routes.json | 6 ++++++ src/views/under-13/account-disabled.jsx | 25 +++++++++++++++++++++++++ src/views/under-13/l10n.json | 4 ++++ 4 files changed, 43 insertions(+) create mode 100644 src/views/under-13/account-disabled.jsx create mode 100644 src/views/under-13/l10n.json diff --git a/src/redux/session.js b/src/redux/session.js index 0b0c7f3fc..eacbd50b8 100644 --- a/src/redux/session.js +++ b/src/redux/session.js @@ -103,6 +103,14 @@ const handleSessionResponse = (dispatch, body) => { ) { window.location = '/classes/student_password_reset/'; return; + } else if ( + body.flags && + body.flags.with_parent_email && + body.flags.with_parent_email.account_disabled && + window.location.pathname !== '/under-13-account-disabled' + ) { + window.location = '/under-13-account-disabled'; + return; } dispatch(module.exports.setSession(body)); dispatch(module.exports.setStatus(module.exports.Status.FETCHED)); diff --git a/src/routes.json b/src/routes.json index 06e47904d..fb6ec5ebf 100644 --- a/src/routes.json +++ b/src/routes.json @@ -745,5 +745,11 @@ "name": "bird-redirect", "pattern": "^/bird/?$", "redirect": "/ideas" + }, + { + "name": "under-13-account-disabled", + "pattern": "^/under-13-account-disabled/?$", + "view": "under-13/account-disabled", + "title": "Account Not Confirmed by Parent" } ] diff --git a/src/views/under-13/account-disabled.jsx b/src/views/under-13/account-disabled.jsx new file mode 100644 index 000000000..b1262b794 --- /dev/null +++ b/src/views/under-13/account-disabled.jsx @@ -0,0 +1,25 @@ +const React = require('react'); +const FormattedMessage = require('react-intl').FormattedMessage; + +const Page = require('../../components/page/www/page.jsx'); +const render = require('../../lib/render.jsx'); + +const InformationPage = require('../../components/informationpage/informationpage.jsx'); + +const Under13AccountDisabled = () => ( + + } + > +
+ + +

+ + +
+
+); + +render(, document.getElementById('app')); diff --git a/src/views/under-13/l10n.json b/src/views/under-13/l10n.json new file mode 100644 index 000000000..cdc71c768 --- /dev/null +++ b/src/views/under-13/l10n.json @@ -0,0 +1,4 @@ +{ + "under13AccountDisabled.title": "Account disabled due to missing parent approval", + "under13AccountDisabled.description": "We require accounts for children under 13 years old to be approved by a parent/guardian within 10 days of registration. Your account was not confirmed within that timeframe and was disabled." +} From 1564358bac91b3f6cf5094dc295cfc5a9bf1cbe2 Mon Sep 17 00:00:00 2001 From: Georgi Angelov Date: Mon, 19 Aug 2024 14:44:06 +0300 Subject: [PATCH 04/73] Update messaging on the email confirmation banner and modal for under-13 users --- .../email-confirmation/banner.jsx | 28 +++++++++++--- .../modal/email-confirmation/modal.jsx | 37 ++++++++++++------- src/l10n.json | 18 +++++++++ src/views/preview/presentation.jsx | 3 ++ src/views/preview/project-view.jsx | 6 ++- src/views/splash/presentation.jsx | 4 +- src/views/splash/splash.jsx | 2 + 7 files changed, 77 insertions(+), 21 deletions(-) diff --git a/src/components/dropdown-banner/email-confirmation/banner.jsx b/src/components/dropdown-banner/email-confirmation/banner.jsx index 16f59136b..8840e1f22 100644 --- a/src/components/dropdown-banner/email-confirmation/banner.jsx +++ b/src/components/dropdown-banner/email-confirmation/banner.jsx @@ -7,13 +7,14 @@ const FormattedMessage = require('react-intl').FormattedMessage; const EmailConfirmationModal = require('../../../components/modal/email-confirmation/modal.jsx'); -const EmailConfirmationBanner = ({onRequestDismiss}) => { +const EmailConfirmationBanner = ({onRequestDismiss, userUsesParentEmail}) => { const [showEmailConfirmationModal, setShowEmailConfirmationModal] = useState(false); return ( {(showEmailConfirmationModal && { setShowEmailConfirmationModal(false); }} @@ -24,7 +25,11 @@ const EmailConfirmationBanner = ({onRequestDismiss}) => { onRequestDismiss={onRequestDismiss} > { setShowEmailConfirmationModal(true); }} > - + ), faqLink: ( - + ) }} @@ -49,7 +66,8 @@ const EmailConfirmationBanner = ({onRequestDismiss}) => { }; EmailConfirmationBanner.propTypes = { - onRequestDismiss: PropTypes.func + onRequestDismiss: PropTypes.func, + userUsesParentEmail: PropTypes.bool }; module.exports = EmailConfirmationBanner; diff --git a/src/components/modal/email-confirmation/modal.jsx b/src/components/modal/email-confirmation/modal.jsx index e0c0ede51..d47e1c522 100644 --- a/src/components/modal/email-confirmation/modal.jsx +++ b/src/components/modal/email-confirmation/modal.jsx @@ -8,10 +8,18 @@ const Modal = require('../base/modal.jsx'); require('./modal.scss'); const EmailConfirmationModal = ({ - email, onRequestClose, isOpen + email, + onRequestClose, + isOpen, + userUsesParentEmail }) => { const [showEmailTips, setShowEmailTips] = useState(false); + const i18nPrefix = + userUsesParentEmail ? + 'emailConfirmationModal.under13' : + 'emailConfirmationModal'; + return ( {showEmailTips ? ( -

+

    -
  • -
  • +
  • +
  • - + ) } } @@ -48,12 +56,12 @@ const EmailConfirmationModal = ({
) : ( -

-

-

+

+

+

{email}

- +
) } @@ -63,11 +71,11 @@ const EmailConfirmationModal = ({ {showEmailTips ? ( - + ) } } @@ -75,7 +83,7 @@ const EmailConfirmationModal = ({ ) : ( { // eslint-disable-line react/jsx-no-bind @@ -83,7 +91,7 @@ const EmailConfirmationModal = ({ setShowEmailTips(true); }} > - + )}} /> )} @@ -94,6 +102,7 @@ const EmailConfirmationModal = ({ EmailConfirmationModal.propTypes = { email: PropTypes.string, isOpen: PropTypes.bool, + userUsesParentEmail: PropTypes.bool, onRequestClose: PropTypes.func }; const mapStateToProps = state => ({ diff --git a/src/l10n.json b/src/l10n.json index 13d459b5a..62cfa1fdf 100644 --- a/src/l10n.json +++ b/src/l10n.json @@ -272,6 +272,10 @@ "emailConfirmationBanner.confirmLinkText": "Confirm your email", "emailConfirmationBanner.faqLinkText": "Having trouble?", + "emailConfirmationBanner.under13.confirm": "A parent/guardian needs to {confirmLink} within 10 days of registration. {faqLink}", + "emailConfirmationBanner.under13.confirmLinkText": "confirm your account", + "emailConfirmationBanner.under13.faqLinkText": "Having trouble?", + "emailConfirmationModal.confirm": "Confirm your email", "emailConfirmationModal.wantToShare": "Want to share on Scratch?", "emailConfirmationModal.clickEmailLink": "Confirm your email address by clicking the link in the email we sent to:", @@ -286,6 +290,20 @@ "emailConfirmationModal.havingTrouble": "Having Trouble? {tipsLink}", "emailConfirmationModal.checkOutTips": "Check out these tips", + "emailConfirmationModal.under13.confirm": "Confirm your account", + "emailConfirmationModal.under13.wantToShare": "Want to share on Scratch?", + "emailConfirmationModal.under13.clickEmailLink": "Your parent/guardian needs to confirm your email address by clicking the link in the email we sent to:", + "emailConfirmationModal.under13.resendEmail": "Resend confirmation email", + "emailConfirmationModal.under13.confirmingTips": "Tips for confirming your email address", + "emailConfirmationModal.under13.tipWaitTenMinutes": "Wait for ten minutes. The email may take a while to arrive.", + "emailConfirmationModal.under13.tipCheckSpam": "Check your spam folder.", + "emailConfirmationModal.under13.correctEmail": "Make sure your parent's email address is correct, see {accountSettings}.", + "emailConfirmationModal.under13.accountSettings": "Account Settings", + "emailConfirmationModal.under13.wantMoreInfo": "Want more information? {FAQLink}", + "emailConfirmationModal.under13.checkOutFAQ": "Check out the FAQ", + "emailConfirmationModal.under13.havingTrouble": "Having Trouble? {tipsLink}", + "emailConfirmationModal.under13.checkOutTips": "Check out these tips", + "thumbnail.by": "by", "report.error": "Something went wrong when trying to send your message. Please try again.", "report.project": "Report Project", diff --git a/src/views/preview/presentation.jsx b/src/views/preview/presentation.jsx index a1578d102..5e1ece922 100644 --- a/src/views/preview/presentation.jsx +++ b/src/views/preview/presentation.jsx @@ -141,6 +141,7 @@ const PreviewPresentation = ({ singleCommentId, socialOpen, userOwnsProject, + userUsesParentEmail, visibilityInfo }) => { const shareDate = ((projectInfo.history && projectInfo.history.shared)) ? projectInfo.history.shared : ''; @@ -242,6 +243,7 @@ const PreviewPresentation = ({ { projectInfo && projectInfo.author && projectInfo.author.id && ( {showEmailConfirmationBanner && onBannerDismiss('confirmed_email')} /* eslint-enable react/jsx-no-bind */ @@ -801,6 +803,7 @@ PreviewPresentation.propTypes = { singleCommentId: PropTypes.oneOfType([PropTypes.number, PropTypes.bool]), socialOpen: PropTypes.bool, userOwnsProject: PropTypes.bool, + userUsesParentEmail: PropTypes.bool, visibilityInfo: PropTypes.shape({ censored: PropTypes.bool, censoredByAdmin: PropTypes.bool, diff --git a/src/views/preview/project-view.jsx b/src/views/preview/project-view.jsx index bc53feb39..2a2d4e956 100644 --- a/src/views/preview/project-view.jsx +++ b/src/views/preview/project-view.jsx @@ -803,6 +803,7 @@ class Preview extends React.Component { singleCommentId={this.state.singleCommentId} socialOpen={this.state.socialOpen} userOwnsProject={this.props.userOwnsProject} + userUsesParentEmail={this.props.userUsesParentEmail} visibilityInfo={this.props.visibilityInfo} onAddComment={this.handleAddComment} onAddToStudioClicked={this.handleAddToStudioClick} @@ -991,6 +992,7 @@ Preview.propTypes = { classroomId: PropTypes.string }), userOwnsProject: PropTypes.bool, + userUsesParentEmail: PropTypes.bool, userPresent: PropTypes.bool, visibilityInfo: PropTypes.shape({ censored: PropTypes.bool, @@ -1037,7 +1039,8 @@ const mapStateToProps = state => { state.session.session.flags.has_outstanding_email_confirmation && state.session.session.flags.confirm_email_banner; const isTotallyNormal = state.session.session.flags && selectIsTotallyNormal(state); - + const userUsesParentEmail = state.session.session.flags && !!state.session.session.flags.with_parent_email; + // if we don't have projectInfo, assume it's shared until we know otherwise const isShared = !projectInfoPresent || state.preview.projectInfo.is_published; @@ -1088,6 +1091,7 @@ const mapStateToProps = state => { useScratch3Registration: state.navigation.useScratch3Registration, user: state.session.session.user, userOwnsProject: userOwnsProject, + userUsesParentEmail: userUsesParentEmail, userPresent: userPresent, visibilityInfo: state.preview.visibilityInfo }; diff --git a/src/views/splash/presentation.jsx b/src/views/splash/presentation.jsx index 832ad308a..3128b9c50 100644 --- a/src/views/splash/presentation.jsx +++ b/src/views/splash/presentation.jsx @@ -357,6 +357,7 @@ class SplashPresentation extends React.Component { // eslint-disable-line react/
{(this.props.shouldShowEmailConfirmation && { // eslint-disable-line react/jsx-no-bind this.props.onDismiss('confirmed_email'); }} @@ -553,7 +554,8 @@ SplashPresentation.propTypes = { shouldShowHOCTopBanner: PropTypes.bool.isRequired, shouldShowIntro: PropTypes.bool.isRequired, shouldShowWelcome: PropTypes.bool.isRequired, - user: PropTypes.object.isRequired // eslint-disable-line react/forbid-prop-types + user: PropTypes.object.isRequired, // eslint-disable-line react/forbid-prop-types + userUsesParentEmail: PropTypes.bool }; SplashPresentation.defaultProps = { diff --git a/src/views/splash/splash.jsx b/src/views/splash/splash.jsx index 68a408b5b..8a0fe9958 100644 --- a/src/views/splash/splash.jsx +++ b/src/views/splash/splash.jsx @@ -190,6 +190,7 @@ class Splash extends React.Component { const showIntro = this.shouldShowIntro() || false; const showWelcome = this.shouldShowWelcome(); const homepageRefreshStatus = this.getHomepageRefreshStatus(); + const userUsesParentEmail = this.props.flags && !!this.props.flags.with_parent_email; return ( Date: Wed, 21 Aug 2024 12:05:35 +0300 Subject: [PATCH 05/73] Remove code for 'account disabled' state of under 13 --- src/redux/session.js | 8 -------- src/routes.json | 6 ------ src/views/under-13/account-disabled.jsx | 25 ------------------------- src/views/under-13/l10n.json | 4 ---- 4 files changed, 43 deletions(-) delete mode 100644 src/views/under-13/account-disabled.jsx delete mode 100644 src/views/under-13/l10n.json diff --git a/src/redux/session.js b/src/redux/session.js index eacbd50b8..0b0c7f3fc 100644 --- a/src/redux/session.js +++ b/src/redux/session.js @@ -103,14 +103,6 @@ const handleSessionResponse = (dispatch, body) => { ) { window.location = '/classes/student_password_reset/'; return; - } else if ( - body.flags && - body.flags.with_parent_email && - body.flags.with_parent_email.account_disabled && - window.location.pathname !== '/under-13-account-disabled' - ) { - window.location = '/under-13-account-disabled'; - return; } dispatch(module.exports.setSession(body)); dispatch(module.exports.setStatus(module.exports.Status.FETCHED)); diff --git a/src/routes.json b/src/routes.json index fb6ec5ebf..06e47904d 100644 --- a/src/routes.json +++ b/src/routes.json @@ -745,11 +745,5 @@ "name": "bird-redirect", "pattern": "^/bird/?$", "redirect": "/ideas" - }, - { - "name": "under-13-account-disabled", - "pattern": "^/under-13-account-disabled/?$", - "view": "under-13/account-disabled", - "title": "Account Not Confirmed by Parent" } ] diff --git a/src/views/under-13/account-disabled.jsx b/src/views/under-13/account-disabled.jsx deleted file mode 100644 index b1262b794..000000000 --- a/src/views/under-13/account-disabled.jsx +++ /dev/null @@ -1,25 +0,0 @@ -const React = require('react'); -const FormattedMessage = require('react-intl').FormattedMessage; - -const Page = require('../../components/page/www/page.jsx'); -const render = require('../../lib/render.jsx'); - -const InformationPage = require('../../components/informationpage/informationpage.jsx'); - -const Under13AccountDisabled = () => ( - - } - > -
- - -

- - -
-
-); - -render(, document.getElementById('app')); diff --git a/src/views/under-13/l10n.json b/src/views/under-13/l10n.json deleted file mode 100644 index cdc71c768..000000000 --- a/src/views/under-13/l10n.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "under13AccountDisabled.title": "Account disabled due to missing parent approval", - "under13AccountDisabled.description": "We require accounts for children under 13 years old to be approved by a parent/guardian within 10 days of registration. Your account was not confirmed within that timeframe and was disabled." -} From b1ac5e27bb0201c9bc3221b9b33eee7605100b99 Mon Sep 17 00:00:00 2001 From: Georgi Angelov Date: Wed, 21 Aug 2024 13:01:23 +0300 Subject: [PATCH 06/73] Update key naming on the email confirmation dialogs --- .../email-confirmation/banner.jsx | 28 +++++---------- src/components/join-flow/welcome-step.jsx | 2 +- .../modal/email-confirmation/modal.jsx | 2 +- src/l10n.json | 34 +++++++++---------- src/views/preview/project-view.jsx | 2 +- src/views/splash/splash.jsx | 2 +- 6 files changed, 29 insertions(+), 41 deletions(-) diff --git a/src/components/dropdown-banner/email-confirmation/banner.jsx b/src/components/dropdown-banner/email-confirmation/banner.jsx index 8840e1f22..74a5c21ba 100644 --- a/src/components/dropdown-banner/email-confirmation/banner.jsx +++ b/src/components/dropdown-banner/email-confirmation/banner.jsx @@ -8,8 +8,12 @@ const FormattedMessage = require('react-intl').FormattedMessage; const EmailConfirmationModal = require('../../../components/modal/email-confirmation/modal.jsx'); const EmailConfirmationBanner = ({onRequestDismiss, userUsesParentEmail}) => { - const [showEmailConfirmationModal, setShowEmailConfirmationModal] = useState(false); + const i18nPrefix = + userUsesParentEmail ? + "emailConfirmationBanner.parentEmail" : + "emailConfirmationBanner"; + return ( {(showEmailConfirmationModal && { onRequestDismiss={onRequestDismiss} > { setShowEmailConfirmationModal(true); }} > - + ), faqLink: ( - + ) }} diff --git a/src/components/join-flow/welcome-step.jsx b/src/components/join-flow/welcome-step.jsx index a8807ef06..91e173cf8 100644 --- a/src/components/join-flow/welcome-step.jsx +++ b/src/components/join-flow/welcome-step.jsx @@ -77,7 +77,7 @@ class WelcomeStep extends React.Component { { state.session.session.flags.has_outstanding_email_confirmation && state.session.session.flags.confirm_email_banner; const isTotallyNormal = state.session.session.flags && selectIsTotallyNormal(state); - const userUsesParentEmail = state.session.session.flags && !!state.session.session.flags.with_parent_email; + const userUsesParentEmail = state.session.session.flags && state.session.session.flags.with_parent_email; // if we don't have projectInfo, assume it's shared until we know otherwise const isShared = !projectInfoPresent || state.preview.projectInfo.is_published; diff --git a/src/views/splash/splash.jsx b/src/views/splash/splash.jsx index 8a0fe9958..6985e2345 100644 --- a/src/views/splash/splash.jsx +++ b/src/views/splash/splash.jsx @@ -190,7 +190,7 @@ class Splash extends React.Component { const showIntro = this.shouldShowIntro() || false; const showWelcome = this.shouldShowWelcome(); const homepageRefreshStatus = this.getHomepageRefreshStatus(); - const userUsesParentEmail = this.props.flags && !!this.props.flags.with_parent_email; + const userUsesParentEmail = this.props.flags && this.props.flags.with_parent_email; return ( Date: Wed, 21 Aug 2024 13:13:40 +0300 Subject: [PATCH 07/73] Remove the 10 days grace period from messaging --- src/l10n.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/l10n.json b/src/l10n.json index 7818d254c..da13fa1cc 100644 --- a/src/l10n.json +++ b/src/l10n.json @@ -263,7 +263,7 @@ "registration.welcomeStepDescription": "You have successfully set up a Scratch account! You are now a member of the class:", "registration.welcomeStepDescriptionNonEducator": "You’re now logged in! You can start exploring and creating projects.", "registration.welcomeStepInstructions": "Want to share and comment? Click the link on the email we sent to {email}.", - "registration.under13.welcomeStepInstructions": "Your parent/guardian needs to confirm your account within 10 days or you will lose access to Scratch. They need to click the link on the email we sent to {email}.", + "registration.under13.welcomeStepInstructions": "Want to share and comment? Your parent/guardian needs to confirm your account using the link on the email we sent to {email}.", "registration.welcomeStepPrompt": "To get started, click on the button below.", "registration.welcomeStepTitle": "Hurray! Welcome to Scratch!", "registration.welcomeStepTitleNonEducator": "Welcome to Scratch, {username}!", @@ -272,7 +272,7 @@ "emailConfirmationBanner.confirmLinkText": "Confirm your email", "emailConfirmationBanner.faqLinkText": "Having trouble?", - "emailConfirmationBanner.parentEmail.confirm": "A parent/guardian needs to {confirmLink} within 10 days of registration. {faqLink}", + "emailConfirmationBanner.parentEmail.confirm": "A parent/guardian needs to {confirmLink}. {faqLink}", "emailConfirmationBanner.parentEmail.confirmLinkText": "confirm your account", "emailConfirmationBanner.parentEmail.faqLinkText": "Having trouble?", From 3defee50cf736fa4ed62bc8072234da59fd6d788 Mon Sep 17 00:00:00 2001 From: Georgi Angelov Date: Wed, 21 Aug 2024 17:16:36 +0300 Subject: [PATCH 08/73] Fix lint errors --- .../email-confirmation/banner.jsx | 4 ++-- src/components/join-flow/email-step.jsx | 2 +- src/components/join-flow/join-flow.jsx | 18 +++++++++--------- src/components/join-flow/welcome-step.jsx | 4 ++-- src/views/splash/splash.jsx | 3 ++- 5 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/components/dropdown-banner/email-confirmation/banner.jsx b/src/components/dropdown-banner/email-confirmation/banner.jsx index 74a5c21ba..30421b9d2 100644 --- a/src/components/dropdown-banner/email-confirmation/banner.jsx +++ b/src/components/dropdown-banner/email-confirmation/banner.jsx @@ -11,8 +11,8 @@ const EmailConfirmationBanner = ({onRequestDismiss, userUsesParentEmail}) => { const [showEmailConfirmationModal, setShowEmailConfirmationModal] = useState(false); const i18nPrefix = userUsesParentEmail ? - "emailConfirmationBanner.parentEmail" : - "emailConfirmationBanner"; + 'emailConfirmationBanner.parentEmail' : + 'emailConfirmationBanner'; return ( diff --git a/src/components/join-flow/email-step.jsx b/src/components/join-flow/email-step.jsx index c90bea304..d50cb354a 100644 --- a/src/components/join-flow/email-step.jsx +++ b/src/components/join-flow/email-step.jsx @@ -104,7 +104,7 @@ class EmailStep extends React.Component { const description = this.props.under13 ? this.props.intl.formatMessage({id: 'registration.under13.emailStepDescription'}) : - undefined; + null; return ( 13) { return false; - } else if (yearDiff < 13) { - return true; - } else { - const currentMonth1Based = now.getMonth() + 1; - const monthsLeftToBirthday = birthMonth - currentMonth1Based; - - return monthsLeftToBirthday >= 0; } + + if (yearDiff < 13) { + return true; + } + + const currentMonth1Based = now.getMonth() + 1; + const monthsLeftToBirthday = birthMonth - currentMonth1Based; + + return monthsLeftToBirthday >= 0; } render () { diff --git a/src/components/join-flow/welcome-step.jsx b/src/components/join-flow/welcome-step.jsx index 91e173cf8..4c5598455 100644 --- a/src/components/join-flow/welcome-step.jsx +++ b/src/components/join-flow/welcome-step.jsx @@ -77,8 +77,8 @@ class WelcomeStep extends React.Component { Date: Mon, 14 Oct 2024 14:45:20 +0300 Subject: [PATCH 09/73] feat: uepr-12: extract community guidelines component --- .../community-guidelines.jsx | 124 ++++++++++++ .../community-guidelines.scss | 113 +++++++++++ .../onboarding-navigation.jsx | 66 +++++++ .../onboarding-navigation.scss | 60 ++++++ .../become-a-scratcher/become-a-scratcher.jsx | 176 ++---------------- .../become-a-scratcher.scss | 86 --------- 6 files changed, 383 insertions(+), 242 deletions(-) create mode 100644 src/components/community-guidelines/community-guidelines.jsx create mode 100644 src/components/community-guidelines/community-guidelines.scss create mode 100644 src/components/onboarding-navigation/onboarding-navigation.jsx create mode 100644 src/components/onboarding-navigation/onboarding-navigation.scss diff --git a/src/components/community-guidelines/community-guidelines.jsx b/src/components/community-guidelines/community-guidelines.jsx new file mode 100644 index 000000000..387d0a917 --- /dev/null +++ b/src/components/community-guidelines/community-guidelines.jsx @@ -0,0 +1,124 @@ +import React, {useEffect} from 'react'; +import {FormattedMessage} from 'react-intl'; +import thumbnailUrl from '../../lib/user-thumbnail'; +import OnboardingNavigation from '../onboarding-navigation/onboarding-navigation.jsx'; + +import './community-guidelines.scss'; +import PropTypes from 'prop-types'; + +export const communityGuidelines = [ + { + section: 'becomeAScratcher.guidelines.respectSection', + header: 'becomeAScratcher.guidelines.respectHeader', + body: 'becomeAScratcher.guidelines.respectBody', + image: 'respect-illustration.svg', + imageLeft: true + }, + { + section: 'becomeAScratcher.guidelines.safeSection', + header: 'becomeAScratcher.guidelines.safeHeader', + body: 'becomeAScratcher.guidelines.safeBody', + image: 'safe-illustration.svg' + }, + { + section: 'becomeAScratcher.guidelines.feedbackSection', + header: 'becomeAScratcher.guidelines.feedbackHeader', + body: 'becomeAScratcher.guidelines.feedbackBody', + image: 'feedback-illustration.svg', + imageLeft: true + }, + { + section: 'becomeAScratcher.guidelines.remix1Section', + header: 'becomeAScratcher.guidelines.remix1Header', + body: 'becomeAScratcher.guidelines.remix1Body', + image: 'remix-illustration-1.svg' + }, + { + section: 'becomeAScratcher.guidelines.remix2Section', + header: 'becomeAScratcher.guidelines.remix2Header', + body: 'becomeAScratcher.guidelines.remix2Body', + image: 'remix-illustration-2.svg' + }, + { + section: 'becomeAScratcher.guidelines.remix3Section', + header: 'becomeAScratcher.guidelines.remix3Header', + body: 'becomeAScratcher.guidelines.remix3Body', + image: 'remix-illustration-3.svg' + }, + { + section: 'becomeAScratcher.guidelines.honestSection', + header: 'becomeAScratcher.guidelines.honestHeader', + body: 'becomeAScratcher.guidelines.honestBody', + image: 'honest-illustration.svg', + imageLeft: true + }, + { + section: 'becomeAScratcher.guidelines.friendlySection', + header: 'becomeAScratcher.guidelines.friendlyHeader', + body: 'becomeAScratcher.guidelines.friendlyBody', + image: 'friendly-illustration.svg' + } +]; + +const CommunityGuidelines = ({constructHeader = () => null, userId, currentPage, onNextPage, onBackPage}) => { + useEffect(() => { + communityGuidelines.forEach(guideline => { + new Image().src = `/images/onboarding/${guideline.image}`; + }); + }, []); + + console.log('==user id', userId); + + const guideline = communityGuidelines[currentPage - 2]; + return ( +
+ {constructHeader(guideline)} +
+ {guideline.imageLeft && ( +
+ +
+ )} +
+

+
+ +
+
+ {!guideline.imageLeft && ( +
+
+ + {currentPage === 3 && } +
+
+ )} +
+ +
+ ); +}; + +CommunityGuidelines.propTypes = { + currentPage: PropTypes.number, + userId: PropTypes.string, + constructHeader: PropTypes.func, + onNextPage: PropTypes.func, + onBackPage: PropTypes.func +}; + +export default CommunityGuidelines; diff --git a/src/components/community-guidelines/community-guidelines.scss b/src/components/community-guidelines/community-guidelines.scss new file mode 100644 index 000000000..d086cd093 --- /dev/null +++ b/src/components/community-guidelines/community-guidelines.scss @@ -0,0 +1,113 @@ +@import "../../colors"; +@import "../../frameless"; + +.onboarding { + display: flex; + flex-direction: row; + min-height: 100vh; + line-height: 1.8rem; + font-size: 1.1rem; + + h1{ + line-height: 3rem; + font-size: 2rem; + margin-bottom: 20px; + } + + // TODO: Can we remove? + button{ + padding: .3em .9em; + } + + .content{ + flex: 1; + display: flex; + max-width: 1000px; + padding: auto; + margin: auto; + @media #{$medium-and-smaller} { + flex-direction: column; + } + } + + .image-content{ + display: flex; + position: relative; + flex: 4; + justify-content: center; + align-items: center; + @media #{$medium-and-smaller} { + flex: 12; + order: 1; + align-items: flex-end; + } + + img{ + @media #{$medium-and-smaller} { + width: 300px; + height: 300px; + } + } + } + + .image-inner-content{ + position: relative; + } + + // TODO: Can we remove? + .image-content-1{ + display: flex; + flex: 4; + justify-content: center; + align-items: center; + @media #{$medium-and-smaller} { + flex: 12; + order: 1; + align-items: flex-end; + height: 250px; + } + + img{ + z-index: 100; + @media #{$medium-and-smaller} { + width: 300px; + height: 300px; + } + } + + @media #{$medium-and-smaller} { + background-color: #E9F1FC; + } + } + + .text-content{ + flex: 8; + display: flex; + justify-content: center; + align-items: flex-start; + flex-direction: column; + padding: 40px; + @media #{$medium-and-smaller} { + flex: 12; + order: 2; + padding-top: 0px; + justify-content: flex-start; + } + } +} + + +.security-avatar{ + position: absolute; + border-radius: 100%; + width: 65px; + height: 65px; + top: 172px; + left: 149px; + @media #{$medium-and-smaller} { + width: 60px !important; + height: 60px !important; + top: 140.5px; + left: 121.5px; + } +} diff --git a/src/components/onboarding-navigation/onboarding-navigation.jsx b/src/components/onboarding-navigation/onboarding-navigation.jsx new file mode 100644 index 000000000..791eb5ad4 --- /dev/null +++ b/src/components/onboarding-navigation/onboarding-navigation.jsx @@ -0,0 +1,66 @@ +import React, {useEffect} from 'react'; +import Button from '../forms/button.jsx'; +import {FormattedMessage} from 'react-intl'; +import PropTypes from 'prop-types'; + +import './onboarding-navigation.scss'; + +const OnboardingNavigation = ({currentPage, totalDots, onNextPage, onBackPage, nextButtonText}) => { + const dots = []; + + useEffect(() => { + new Image().src = '/images/onboarding/right-arrow.svg'; + new Image().src = '/images/onboarding/left-arrow.svg'; + }, []); + + if (currentPage && totalDots){ + for (let i = 0; i < totalDots; i++){ + // First two pages don't have dots + dots.push(
); + } + } + + return ( +
+ + {(currentPage && totalDots) && +
+ {dots} +
} + +
+ + ); +}; +OnboardingNavigation.propTypes = { + currentPage: PropTypes.number, + totalDots: PropTypes.number, + onNextPage: PropTypes.func, + onBackPage: PropTypes.func, + nextButtonText: PropTypes.string +}; + +export default OnboardingNavigation; diff --git a/src/components/onboarding-navigation/onboarding-navigation.scss b/src/components/onboarding-navigation/onboarding-navigation.scss new file mode 100644 index 000000000..f0b976cfc --- /dev/null +++ b/src/components/onboarding-navigation/onboarding-navigation.scss @@ -0,0 +1,60 @@ +@import "../../colors"; +@import "../../frameless"; + +.button{ + display: flex; + justify-content: center; + align-items: center; + border-radius: .3rem; + + .left-arrow{ + padding-right: 4px; + @media #{$small} { + padding: 0px; + } + } + .right-arrow{ + padding-left: 4px; + @media #{$small} { + padding: 0px; + } + } +} + +.navigation{ + display: flex; + width: 100%; + justify-content: space-around; + align-items: center; + margin-bottom: 40px; + + .dotRow{ + display: flex; + justify-content: space-between; + width: 244px; + min-width: 0px; + padding: 0px 5px; + } + + .dot{ + width: 20px; + height: 20px; + border-radius: 100%; + background-color: #d9d9d9; + border: none; + display: flex; + justify-content: center; + align-items: center; + } + .active{ + border: solid 2px #4280d9; + background-color: #4c97fe; + margin-top: -2px; + } + + .navText{ + @media #{$small} { + display: none; + } + } +} \ No newline at end of file diff --git a/src/views/become-a-scratcher/become-a-scratcher.jsx b/src/views/become-a-scratcher/become-a-scratcher.jsx index 2a05cad3d..e4c782e97 100644 --- a/src/views/become-a-scratcher/become-a-scratcher.jsx +++ b/src/views/become-a-scratcher/become-a-scratcher.jsx @@ -1,5 +1,5 @@ /* eslint-disable react/jsx-no-bind */ -import React, {useState, useEffect} from 'react'; +import React, {useState, useEffect, useCallback} from 'react'; import useWindowSize from 'react-use/lib/useWindowSize'; import Confetti from 'react-confetti'; import {FormattedMessage, injectIntl} from 'react-intl'; @@ -14,63 +14,11 @@ import Button from '../../components/forms/button.jsx'; import Modal from '../../components/modal/base/modal.jsx'; import NotAvailable from '../../components/not-available/not-available.jsx'; import WarningBanner from '../../components/title-banner/warning-banner.jsx'; +import OnboardingNavigation from '../../components/onboarding-navigation/onboarding-navigation.jsx'; +import CommunityGuidelines, {communityGuidelines} from '../../components/community-guidelines/community-guidelines.jsx'; import './become-a-scratcher.scss'; -const communityGuidelines = [ - { - section: 'becomeAScratcher.guidelines.respectSection', - header: 'becomeAScratcher.guidelines.respectHeader', - body: 'becomeAScratcher.guidelines.respectBody', - image: 'respect-illustration.svg', - imageLeft: true - }, - { - section: 'becomeAScratcher.guidelines.safeSection', - header: 'becomeAScratcher.guidelines.safeHeader', - body: 'becomeAScratcher.guidelines.safeBody', - image: 'safe-illustration.svg' - }, - { - section: 'becomeAScratcher.guidelines.feedbackSection', - header: 'becomeAScratcher.guidelines.feedbackHeader', - body: 'becomeAScratcher.guidelines.feedbackBody', - image: 'feedback-illustration.svg', - imageLeft: true - }, - { - section: 'becomeAScratcher.guidelines.remix1Section', - header: 'becomeAScratcher.guidelines.remix1Header', - body: 'becomeAScratcher.guidelines.remix1Body', - image: 'remix-illustration-1.svg' - }, - { - section: 'becomeAScratcher.guidelines.remix2Section', - header: 'becomeAScratcher.guidelines.remix2Header', - body: 'becomeAScratcher.guidelines.remix2Body', - image: 'remix-illustration-2.svg' - }, - { - section: 'becomeAScratcher.guidelines.remix3Section', - header: 'becomeAScratcher.guidelines.remix3Header', - body: 'becomeAScratcher.guidelines.remix3Body', - image: 'remix-illustration-3.svg' - }, - { - section: 'becomeAScratcher.guidelines.honestSection', - header: 'becomeAScratcher.guidelines.honestHeader', - body: 'becomeAScratcher.guidelines.honestBody', - image: 'honest-illustration.svg', - imageLeft: true - }, - { - section: 'becomeAScratcher.guidelines.friendlySection', - header: 'becomeAScratcher.guidelines.friendlyHeader', - body: 'becomeAScratcher.guidelines.friendlyBody', - image: 'friendly-illustration.svg' - } -]; - /* eslint-disable max-len */ const confettiPaths = [ new Path2D('M34.549 3.1361L40.613 15.4191L54.1718 17.3947C58.7918 18.0636 60.6345 23.7378 57.2946 26.9979L47.4788 36.5612L49.7954 50.0668C50.5839 54.6646 45.7557 58.1728 41.6274 56.0024L29.4994 49.6239L17.3714 56.0024C13.2386 58.1728 8.4149 54.6646 9.19893 50.0668L11.52 36.5612L1.70862 26.9979C-1.63567 23.7378 0.20701 18.0636 4.82699 17.3947L18.3902 15.4191L24.4497 3.1361C26.5183 -1.04537 32.4805 -1.04537 34.549 3.1361Z'), @@ -78,9 +26,8 @@ const confettiPaths = [ new Path2D('M10.9967 45.1305C5.99892 36.9346 5.59074 25.9968 9.99448 17.3259C14.5575 8.00641 22.2367 3.87569 25.3229 2.53264C27.178 1.67111 29.1327 0.999586 31.2366 0.504948L33.3008 0.0594462C34.8174 -0.25175 36.3273 0.68839 36.6558 2.18868C36.9877 3.69225 36.022 5.17289 34.4988 5.50047L32.491 5.93286C30.8218 6.32595 29.2223 6.87628 27.6625 7.6035C25.6349 8.4814 18.9281 11.8783 15.0586 19.786C12.1217 25.5612 11.2456 34.7431 15.8683 42.3199C20.0564 49.6183 28.6315 54.0929 36.7654 53.3395C44.4578 52.7859 51.5164 47.6659 53.992 40.8851C56.5042 34.5072 54.835 27.9688 52.0772 24.2869C48.7553 19.75 44.6204 18.3545 42.9976 17.9647C42.7554 17.8926 37.074 16.186 32.0563 18.6985C29.8959 19.7336 27.1017 22.0627 25.605 25.5612C23.9889 29.1743 24.407 33.8193 26.5973 36.9051C28.8008 40.2136 32.9556 42.0873 36.5297 41.4518C40.0872 40.8917 42.4932 38.2449 43.064 35.9191C43.7343 33.4197 42.7056 31.3068 41.9025 30.5468C40.5386 29.2005 39.3605 29.1776 39.3107 29.1743C38.7532 29.1579 38.4777 29.2005 38.3118 29.2267C37.6746 29.4593 36.772 29.9703 36.5065 30.5075C36.46 30.596 36.3406 30.8384 36.5696 31.4444C37.1171 32.8825 36.3771 34.4908 34.9202 35.0313C33.47 35.5751 31.8373 34.8446 31.2864 33.4033C30.3804 31.0186 30.8815 29.1514 31.4623 28.0049C32.9789 25.0207 36.4999 23.9397 36.8981 23.825C37.0806 23.7693 37.2731 23.7366 37.4623 23.7202C37.8505 23.6612 38.5342 23.5695 39.46 23.6055C41.5806 23.6317 43.9965 24.7389 45.8483 26.5701C48.0352 28.6338 49.7476 32.7809 48.533 37.2883C47.3483 42.1135 42.7952 46.1034 37.4822 46.9387C31.7676 47.9575 25.3329 45.1403 21.9347 40.0334C18.6593 35.431 18.032 28.696 20.4281 23.3533C23.0564 17.2014 28.0773 14.4171 29.5508 13.7095C36.6359 10.1717 44.2089 12.5073 44.5275 12.6056C46.5651 13.0871 52.2299 14.9838 56.6336 21.0013C60.0186 25.5284 62.763 33.9831 59.2752 42.8342C56.0894 51.5575 47.0197 58.1843 37.2399 58.8886C36.4733 58.9607 35.6968 59 34.9236 59C25.4325 59 15.8186 53.5295 10.9967 45.1305Z') ]; -const OnboardingHeader = ({user, sectionText, secondary}) => { +const OnboardingHeader = ({user, section, secondary}) => { const [showModal, setShowModal] = useState(false); - return (
{/* Finish Later Modal */} @@ -126,7 +73,10 @@ const OnboardingHeader = ({user, sectionText, secondary}) => {
- {sectionText} + + {/* TODO: Ask whether this was omitted on purpose: {section ? section : null} */} + {null} + - {(currentPage && totalDots) && -
- {dots} -
} - -
- - ); -}; -OnboardingNavigation.propTypes = { - currentPage: PropTypes.number, - totalDots: PropTypes.number, - onNextPage: PropTypes.func, - onBackPage: PropTypes.func, - nextButtonText: PropTypes.string -}; const BecomeAScratcher = ({user, invitedScratcher, scratcher, sessionStatus}) => { const [currentPage, setCurrentPage] = useState(0); @@ -231,8 +131,6 @@ const BecomeAScratcher = ({user, invitedScratcher, scratcher, sessionStatus}) => }); new Image().src = '/images/onboarding/community-guidelines.svg'; new Image().src = '/images/onboarding/create-a-project.svg'; - new Image().src = '/images/onboarding/right-arrow.svg'; - new Image().src = '/images/onboarding/left-arrow.svg'; }, []); useEffect(() => { @@ -265,6 +163,10 @@ const BecomeAScratcher = ({user, invitedScratcher, scratcher, sessionStatus}) => setCurrentPage(Math.max(currentPage - 1, 0)); }; + const constructHeader = useCallback(guideline => ()} + />), [user]); if (sessionStatus === sessionActions.Status.FETCHED){ // Not logged in @@ -386,51 +288,13 @@ const BecomeAScratcher = ({user, invitedScratcher, scratcher, sessionStatus}) =>
); } else if (currentPage < 2 + communityGuidelines.length) { - const guideline = communityGuidelines[currentPage - 2]; - return ( -
- )} - /> -
- {guideline.imageLeft && ( -
- -
- )} -
-

-
- -
-
- {!guideline.imageLeft && ( -
-
- - {currentPage === 3 && } -
-
- )} -
- -
- ); + return (); } else if (currentPage === lastPage - 1) { return (
{ diff --git a/src/views/become-a-scratcher/become-a-scratcher.scss b/src/views/become-a-scratcher/become-a-scratcher.scss index 47f1fdf6d..3cf2abba0 100644 --- a/src/views/become-a-scratcher/become-a-scratcher.scss +++ b/src/views/become-a-scratcher/become-a-scratcher.scss @@ -24,19 +24,6 @@ html,body{ justify-content: center; align-items: center; border-radius: .3rem; - - .left-arrow{ - padding-right: 4px; - @media #{$small} { - padding: 0px; - } - } - .right-arrow{ - padding-left: 4px; - @media #{$small} { - padding: 0px; - } - } } .no-invitation{ @@ -229,45 +216,6 @@ html,body{ } } -.navigation{ - display: flex; - width: 100%; - justify-content: space-around; - align-items: center; - margin-bottom: 40px; - - .dotRow{ - display: flex; - justify-content: space-between; - width: 244px; - min-width: 0px; - padding: 0px 5px; - } - - .dot{ - width: 20px; - height: 20px; - border-radius: 100%; - background-color: #d9d9d9; - border: none; - display: flex; - justify-content: center; - align-items: center; - } - .active{ - border: solid 2px #4280d9; - background-color: #4c97fe; - margin-top: -2px; - } - - .navText{ - @media #{$small} { - display: none; - } - } -} - - .onboarding { display: flex; flex-direction: row; @@ -333,10 +281,6 @@ html,body{ } } } - - .image-inner-content{ - position: relative; - } .image-content-1{ display: flex; @@ -363,21 +307,6 @@ html,body{ } } - .text-content{ - flex: 8; - display: flex; - justify-content: center; - align-items: flex-start; - flex-direction: column; - padding: 40px; - @media #{$medium-and-smaller} { - flex: 12; - order: 2; - padding-top: 0px; - justify-content: flex-start; - } - } - .opening-text-content{ flex: 6; display: flex; @@ -456,18 +385,3 @@ html,body{ left: -18px; right: 0; } - -.security-avatar{ - position: absolute; - border-radius: 100%; - width: 65px; - height: 65px; - top: 172px; - left: 149px; - @media #{$medium-and-smaller} { - width: 60px !important; - height: 60px !important; - top: 140.5px; - left: 121.5px; - } -} From bdec4169eb70b1e45c0beeaa78e8c965b2fb3bd8 Mon Sep 17 00:00:00 2001 From: Kaloyan Manolov Date: Tue, 15 Oct 2024 13:14:16 +0300 Subject: [PATCH 10/73] Add community guidelines modal to homepage after registration --- .../community-guidelines.jsx | 21 ++++--- .../community-guidelines.scss | 32 ++-------- .../onboarding-navigation-wrapper.jsx | 50 ++++++++++++++++ .../onboarding-navigation.jsx | 46 +++++++++------ .../onboarding-navigation.scss | 8 +++ src/l10n.json | 59 ++++++++++++++++++- src/redux/navigation.js | 18 +++++- .../become-a-scratcher/become-a-scratcher.jsx | 45 +++++++------- src/views/splash/splash.jsx | 32 ++++++++-- 9 files changed, 232 insertions(+), 79 deletions(-) create mode 100644 src/components/onboarding-navigation/onboarding-navigation-wrapper.jsx diff --git a/src/components/community-guidelines/community-guidelines.jsx b/src/components/community-guidelines/community-guidelines.jsx index 387d0a917..8dede98bb 100644 --- a/src/components/community-guidelines/community-guidelines.jsx +++ b/src/components/community-guidelines/community-guidelines.jsx @@ -1,6 +1,6 @@ import React, {useEffect} from 'react'; import {FormattedMessage} from 'react-intl'; -import thumbnailUrl from '../../lib/user-thumbnail'; +import thumbnailUrl from '../../lib/user-thumbnail.js'; import OnboardingNavigation from '../onboarding-navigation/onboarding-navigation.jsx'; import './community-guidelines.scss'; @@ -60,16 +60,21 @@ export const communityGuidelines = [ } ]; -const CommunityGuidelines = ({constructHeader = () => null, userId, currentPage, onNextPage, onBackPage}) => { +export const CommunityGuidelines = ({ + constructHeader = () => null, + userId, + currentPage, + nextButtonText, + onNextPage, + onBackPage +}) => { useEffect(() => { communityGuidelines.forEach(guideline => { new Image().src = `/images/onboarding/${guideline.image}`; }); }, []); - console.log('==user id', userId); - - const guideline = communityGuidelines[currentPage - 2]; + const guideline = communityGuidelines[currentPage]; return (
{constructHeader(guideline)} @@ -95,7 +100,7 @@ const CommunityGuidelines = ({constructHeader = () => null, userId, currentPage, alt="" src={`/images/onboarding/${guideline.image}`} /> - {currentPage === 3 && } @@ -106,6 +111,7 @@ const CommunityGuidelines = ({constructHeader = () => null, userId, currentPage, @@ -117,8 +123,7 @@ CommunityGuidelines.propTypes = { currentPage: PropTypes.number, userId: PropTypes.string, constructHeader: PropTypes.func, + nextButtonText: PropTypes.string, onNextPage: PropTypes.func, onBackPage: PropTypes.func }; - -export default CommunityGuidelines; diff --git a/src/components/community-guidelines/community-guidelines.scss b/src/components/community-guidelines/community-guidelines.scss index d086cd093..93512b217 100644 --- a/src/components/community-guidelines/community-guidelines.scss +++ b/src/components/community-guidelines/community-guidelines.scss @@ -1,6 +1,11 @@ @import "../../colors"; @import "../../frameless"; +.col{ + display: flex; + flex-direction: column !important; +} + .onboarding { display: flex; flex-direction: row; @@ -14,7 +19,6 @@ margin-bottom: 20px; } - // TODO: Can we remove? button{ padding: .3em .9em; } @@ -53,32 +57,6 @@ .image-inner-content{ position: relative; } - - // TODO: Can we remove? - .image-content-1{ - display: flex; - flex: 4; - justify-content: center; - align-items: center; - @media #{$medium-and-smaller} { - flex: 12; - order: 1; - align-items: flex-end; - height: 250px; - } - - img{ - z-index: 100; - @media #{$medium-and-smaller} { - width: 300px; - height: 300px; - } - } - - @media #{$medium-and-smaller} { - background-color: #E9F1FC; - } - } .text-content{ flex: 8; diff --git a/src/components/onboarding-navigation/onboarding-navigation-wrapper.jsx b/src/components/onboarding-navigation/onboarding-navigation-wrapper.jsx new file mode 100644 index 000000000..305271934 --- /dev/null +++ b/src/components/onboarding-navigation/onboarding-navigation-wrapper.jsx @@ -0,0 +1,50 @@ +import React, {useCallback, useState, useEffect} from 'react'; +import {CommunityGuidelines, communityGuidelines} from './community-guidelines.jsx'; +import PropTypes from 'prop-types'; +import {injectIntl} from 'react-intl'; +const ReactModal = require('react-modal'); + +const CommunityGuidelinesModal = props => { + const [currentPage, setCurrentPage] = useState(0); + const onNextPage = useCallback(() => setCurrentPage(currentPage + 1), [currentPage]); + const onBackPage = useCallback(() => setCurrentPage(currentPage - 1), [currentPage]); + const onComplete = props.onComplete ?? (() => true); + + useEffect(() => { + const body = document.querySelector('body'); + body.style.overflow = props.isOpen ? 'hidden' : 'auto'; + }, [props.isOpen]); + + return ( + + 0 ? onBackPage : null} + /> + ); +}; + +CommunityGuidelinesModal.propTypes = { + userId: PropTypes.string, + onComplete: PropTypes.func, + isOpen: PropTypes.bool +}; + +export const IntlCommunityGuidelinesWrapper = injectIntl(CommunityGuidelinesModal); diff --git a/src/components/onboarding-navigation/onboarding-navigation.jsx b/src/components/onboarding-navigation/onboarding-navigation.jsx index 791eb5ad4..8a89ff135 100644 --- a/src/components/onboarding-navigation/onboarding-navigation.jsx +++ b/src/components/onboarding-navigation/onboarding-navigation.jsx @@ -4,8 +4,15 @@ import {FormattedMessage} from 'react-intl'; import PropTypes from 'prop-types'; import './onboarding-navigation.scss'; +import classNames from 'classnames'; -const OnboardingNavigation = ({currentPage, totalDots, onNextPage, onBackPage, nextButtonText}) => { +const OnboardingNavigation = ({ + currentPage, + totalDots, + onNextPage, + onBackPage, + nextButtonText +}) => { const dots = []; useEffect(() => { @@ -13,31 +20,36 @@ const OnboardingNavigation = ({currentPage, totalDots, onNextPage, onBackPage, n new Image().src = '/images/onboarding/left-arrow.svg'; }, []); - if (currentPage && totalDots){ + if (currentPage >= 0 && totalDots){ for (let i = 0; i < totalDots; i++){ - // First two pages don't have dots dots.push(
); } } return (
- - {(currentPage && totalDots) && + { + } + {(currentPage >= 0 && totalDots) &&
{dots}
} diff --git a/src/components/onboarding-navigation/onboarding-navigation.scss b/src/components/onboarding-navigation/onboarding-navigation.scss index f0b976cfc..dbfa2e11d 100644 --- a/src/components/onboarding-navigation/onboarding-navigation.scss +++ b/src/components/onboarding-navigation/onboarding-navigation.scss @@ -21,6 +21,10 @@ } } +.hidden{ + visibility: hidden; +} + .navigation{ display: flex; width: 100%; @@ -57,4 +61,8 @@ display: none; } } + + .buttonPlaceholder{ + width: 20px, + } } \ No newline at end of file diff --git a/src/l10n.json b/src/l10n.json index 0c66069e4..5f510f25a 100644 --- a/src/l10n.json +++ b/src/l10n.json @@ -479,5 +479,62 @@ "renameAccount.scratchsCommunityGuidelines": "Scratch's Community Guidelines", "renameAccount.change": "Change", "renameAccount.goToProfile": "Go to your profile", - "renameAccount.pastNotifications": "Here are your past admin notifications" + "renameAccount.pastNotifications": "Here are your past admin notifications", + + "becomeAScratcher.buttons.back": "Back", + "becomeAScratcher.buttons.next": "Next", + "becomeAScratcher.buttons.communityGuidelines": "Community Guidelines", + "becomeAScratcher.buttons.getStarted": "Get Started", + "becomeAScratcher.buttons.finishLater": "Finish Later", + "becomeAScratcher.buttons.goBack": "Go Back", + "becomeAScratcher.buttons.iAgree": "I Agree", + "becomeAScratcher.buttons.takeMeBack": "Take me back to Scratch", + "becomeAScratcher.buttons.backToProfile": "Back to Profile Page", + "becomeAScratcher.buttons.finishLater": "Finish Later", + "becomeAScratcher.congratulations.header": "Congratulations, {username}! You have shown that you are ready to become a Scratcher.", + "becomeAScratcher.congratulations.body": "Scratch is a friendly and welcoming community for everyone, where people create, share, and learn together. We welcome people of all ages, races, ethnicities, religions, abilities, sexual orientations, and gender identities.", + "becomeAScratcher.toBeAScratcher.header": "What does it mean to be a Scratcher?", + "becomeAScratcher.toBeAScratcher.body": "You might notice on your profile page that you are currently a “New Scratcher”. Now that you have spent some time on Scratch, we invite you to become a “Scratcher”.", + "becomeAScratcher.toBeAScratcher.definition": "Scratchers have a bit more experience on Scratch and are excited to both contribute to the community and to make it a supportive and welcoming space for others.", + "becomeAScratcher.toBeAScratcher.canDo": "Here are some things Scratchers do:", + "becomeAScratcher.toBeAScratcher.createStudios": "Create studios", + "becomeAScratcher.toBeAScratcher.helpOut": "Help out in the community", + "becomeAScratcher.toBeAScratcher.communityGuidelines": "Next, we will take you through the community guidelines and explain what these are.", + "becomeAScratcher.guidelines.respectSection": "Become a Scratcher - Treat everyone with respect", + "becomeAScratcher.guidelines.respectHeader": "Scratchers treat everyone with respect.", + "becomeAScratcher.guidelines.respectBody": "Everyone on Scratch is encouraged to share things that excite them and are important to them—we hope that you find ways to celebrate your own identity on Scratch, and allow others to do the same.", + "becomeAScratcher.guidelines.safeSection": "Become a Scratcher - Be safe", + "becomeAScratcher.guidelines.safeHeader": "Scratchers are safe: we keep personal and contact information private.", + "becomeAScratcher.guidelines.safeBody": "This includes not sharing real last names, phone numbers, addresses, hometowns, school names, email addresses, usernames or links to social media sites, video chatting applications, or websites with private chat functionality.", + "becomeAScratcher.guidelines.feedbackSection": "Become a Scratcher - Give helpful feedback", + "becomeAScratcher.guidelines.feedbackHeader": "Scratchers give helpful feedback.", + "becomeAScratcher.guidelines.feedbackBody": "When commenting on a project, remember to say something you like about it, offer suggestions, and be kind, not critical.", + "becomeAScratcher.guidelines.remix1Section": "Become a Scratcher - Embrace remix culture", + "becomeAScratcher.guidelines.remix1Header": "Scratchers embrace remix culture.", + "becomeAScratcher.guidelines.remix1Body": "Remixing is when you build upon someone else’s projects, code, ideas, images, or anything else they share on Scratch to make your own unique creation.", + "becomeAScratcher.guidelines.remix2Section": "Become a Scratcher - Embrace remix culture", + "becomeAScratcher.guidelines.remix2Header": "Remixing is a great way to collaborate and connect with other Scratchers.", + "becomeAScratcher.guidelines.remix2Body": "You are encouraged to use anything you find on Scratch in your own creations, as long as you provide credit to everyone whose work you used and make a meaningful change to it. ", + "becomeAScratcher.guidelines.remix3Section": "Become a Scratcher - Embrace remix culture", + "becomeAScratcher.guidelines.remix3Header": "Remixing means sharing with others.", + "becomeAScratcher.guidelines.remix3Body": "When you share something on Scratch, you are giving permission to all Scratchers to use your work in their creations, too.", + "becomeAScratcher.guidelines.honestSection": "Become a Scratcher - Be honest", + "becomeAScratcher.guidelines.honestHeader": "Scratchers are honest.", + "becomeAScratcher.guidelines.honestBody": "It’s important to be honest and authentic when interacting with others on Scratch, and remember that there is a person behind every Scratch account.", + "becomeAScratcher.guidelines.friendlySection": "Become a Scratcher - Keep the site friendly", + "becomeAScratcher.guidelines.friendlyHeader": "Scratchers help keep the site friendly.", + "becomeAScratcher.guidelines.friendlyBody": "It’s important to keep your creations and conversations friendly and appropriate for all ages. If you think something on Scratch is mean, insulting, too violent, or otherwise disruptive to the community, click “Report” to let us know about it.", + "becomeAScratcher.invitation.header": "{username}, we invite you to become a Scratcher.", + "becomeAScratcher.invitation.body": "Scratch is a friendly and welcoming community for everyone. If you agree to be respectful, be safe, give helpful feedback, embrace remix culture, be honest, and help keep the site friendly, click “I agree!”", + "becomeAScratcher.invitation.finishLater": "You get to decide if you want to become a Scratcher. If you do not want to be a Scratcher yet, just click “Finish Later” above.", + "registration.success.error": "Sorry, an unexpected error occurred.", + "becomeAScratcher.success.header": "Hooray! You are now officially a Scratcher.", + "becomeAScratcher.success.body": "Here are some links that might be helpful for you.", + "becomeAScratcher.success.communityGuidelines": "Community Guidelines", + "becomeAScratcher.success.createAProject": "Create a Project", + "becomeAScratcher.noInvitation.header": "Whoops! Looks like you haven’t received an invitation to become a Scratcher yet.", + "becomeAScratcher.noInvitation.body": "To become a Scratcher, you must be active on Scratch for a while, share several projects, and comment constructively in the community. After a few weeks, you will receive a notification inviting you to become a Scratcher. Scratch on!", + "becomeAScratcher.finishLater.header": "No worries, take your time!", + "becomeAScratcher.finishLater.body": "By leaving this page, you will not finish the process to become a Scratcher and will stay as a New Scratcher. If you change your mind later, you can always come back via your profile page.", + "becomeAScratcher.finishLater.clickBecomeAScratcher": "Just click on “★ Become a Scratcher!” below your username." } diff --git a/src/redux/navigation.js b/src/redux/navigation.js index 5c2c37623..a2fdeea6f 100644 --- a/src/redux/navigation.js +++ b/src/redux/navigation.js @@ -15,7 +15,9 @@ const Types = keyMirror({ TOGGLE_LOGIN_OPEN: null, SET_CANCELED_DELETION_OPEN: null, SET_REGISTRATION_OPEN: null, - HANDLE_REGISTRATION_REQUESTED: null + HANDLE_REGISTRATION_REQUESTED: null, + HANDLE_REGISTRATION_COMPLETED: null, + REVIEW_COMMUNITY_GUIDELINES: null }); module.exports.getInitialState = () => ({ @@ -25,6 +27,7 @@ module.exports.getInitialState = () => ({ loginError: null, loginOpen: false, registrationOpen: false, + shouldReviewCommunityGuidelines: false, searchTerm: '' }); @@ -56,6 +59,10 @@ module.exports.navigationReducer = (state, action) => { return state; } return defaults({registrationOpen: true}, state); + case Types.HANDLE_REGISTRATION_COMPLETED: + return defaults({shouldReviewCommunityGuidelines: true}, state); + case Types.REVIEW_COMMUNITY_GUIDELINES: + return defaults({shouldReviewCommunityGuidelines: false}, state); default: return state; } @@ -103,6 +110,14 @@ module.exports.handleRegistrationRequested = () => ({ type: Types.HANDLE_REGISTRATION_REQUESTED }); +module.exports.handleRegistrationCompleted = () => ({ + type: Types.HANDLE_REGISTRATION_COMPLETED +}); + +module.exports.reviewCommunityGuidelines = () => ({ + type: Types.REVIEW_COMMUNITY_GUIDELINES +}); + module.exports.handleCompleteRegistration = createProject => (dispatch => { if (createProject) { // TODO: Ideally this would take you to the editor with the getting started @@ -114,6 +129,7 @@ module.exports.handleCompleteRegistration = createProject => (dispatch => { dispatch(module.exports.setRegistrationOpen(false)) ); } + dispatch(module.exports.handleRegistrationCompleted()); }); module.exports.handleLogIn = (formData, callback) => (dispatch => { diff --git a/src/views/become-a-scratcher/become-a-scratcher.jsx b/src/views/become-a-scratcher/become-a-scratcher.jsx index e4c782e97..c4096a2c5 100644 --- a/src/views/become-a-scratcher/become-a-scratcher.jsx +++ b/src/views/become-a-scratcher/become-a-scratcher.jsx @@ -15,7 +15,10 @@ import Modal from '../../components/modal/base/modal.jsx'; import NotAvailable from '../../components/not-available/not-available.jsx'; import WarningBanner from '../../components/title-banner/warning-banner.jsx'; import OnboardingNavigation from '../../components/onboarding-navigation/onboarding-navigation.jsx'; -import CommunityGuidelines, {communityGuidelines} from '../../components/community-guidelines/community-guidelines.jsx'; +import { + CommunityGuidelines, + communityGuidelines +} from '../../components/community-guidelines/community-guidelines.jsx'; import './become-a-scratcher.scss'; @@ -174,25 +177,25 @@ const BecomeAScratcher = ({user, invitedScratcher, scratcher, sessionStatus}) => return (); } - // New scratcher who is not invited - if (!invitedScratcher && !scratcher){ - return (
- -

- -

-
- -
-
); - } + // // New scratcher who is not invited + // if (!invitedScratcher && !scratcher){ + // return (
+ // + //

+ // + //

+ //
+ // + //
+ //
); + // } // Invited Scratcher if (currentPage === 0){ @@ -289,7 +292,7 @@ const BecomeAScratcher = ({user, invitedScratcher, scratcher, sessionStatus}) => ); } else if (currentPage < 2 + communityGuidelines.length) { return (); + } + return ( ({ sessionStatus: state.session.status, shared: state.splash.shared.rows, studios: state.splash.studios.rows, - user: state.session.session.user + user: state.session.session.user, + shouldReviewCommunityGuidelines: state.navigation.shouldReviewCommunityGuidelines }); const mapDispatchToProps = dispatch => ({ @@ -308,19 +329,22 @@ const mapDispatchToProps = dispatch => ({ }, setRows: (type, rows) => { dispatch(splashActions.setRows(type, rows)); + }, + reviewCommunityGuidelines: () => { + dispatch(navigationActions.reviewCommunityGuidelines()); } }); -const ConnectedSplash = connect( +const IntlConnectedSplash = injectIntl(connect( mapStateToProps, mapDispatchToProps -)(Splash); +)(Splash)); render( - + , document.getElementById('app'), {splash: splashActions.splashReducer} From 7110c6f806dae08ba83e93f6f7a905846535473f Mon Sep 17 00:00:00 2001 From: Georgi Angelov Date: Tue, 15 Oct 2024 13:35:59 +0300 Subject: [PATCH 11/73] chore: copy updates for the under-13 flow --- .../email-confirmation/banner.jsx | 2 +- .../modal/email-confirmation/modal.jsx | 10 ++++++---- src/l10n.json | 17 +++++++---------- 3 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/components/dropdown-banner/email-confirmation/banner.jsx b/src/components/dropdown-banner/email-confirmation/banner.jsx index 30421b9d2..e8c3a7000 100644 --- a/src/components/dropdown-banner/email-confirmation/banner.jsx +++ b/src/components/dropdown-banner/email-confirmation/banner.jsx @@ -44,7 +44,7 @@ const EmailConfirmationBanner = ({onRequestDismiss, userUsesParentEmail}) => { ), faqLink: ( - + ) }} diff --git a/src/components/modal/email-confirmation/modal.jsx b/src/components/modal/email-confirmation/modal.jsx index e8e5d0eae..20a156c28 100644 --- a/src/components/modal/email-confirmation/modal.jsx +++ b/src/components/modal/email-confirmation/modal.jsx @@ -20,6 +20,8 @@ const EmailConfirmationModal = ({ 'emailConfirmationModal.parentEmail' : 'emailConfirmationModal'; + const showFooter = !showEmailTips || !userUsesParentEmail; + return (
-
+ {showFooter && (
{showEmailTips ? ( - + ) } } @@ -95,7 +97,7 @@ const EmailConfirmationModal = ({ )}} /> )} -
+
)} ); }; diff --git a/src/l10n.json b/src/l10n.json index da13fa1cc..856406d84 100644 --- a/src/l10n.json +++ b/src/l10n.json @@ -208,8 +208,8 @@ "registration.genderOptionAnother": "Another gender:", "registration.genderOptionPreferNotToSay": "Prefer not to say", "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.under13.emailStepTitle": "What's your parent's email address?", + "registration.under13.emailStepDescription": "We'll send them a link to verify 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.invitedBy": "invited by", @@ -263,7 +263,7 @@ "registration.welcomeStepDescription": "You have successfully set up a Scratch account! You are now a member of the class:", "registration.welcomeStepDescriptionNonEducator": "You’re now logged in! You can start exploring and creating projects.", "registration.welcomeStepInstructions": "Want to share and comment? Click the link on the email we sent to {email}.", - "registration.under13.welcomeStepInstructions": "Want to share and comment? Your parent/guardian needs to confirm your account using the link on the email we sent to {email}.", + "registration.under13.welcomeStepInstructions": "In order to share projects and participate in the Scratch community, your parent needs to confirm your account. They can click on the link in the email we sent to {email}.", "registration.welcomeStepPrompt": "To get started, click on the button below.", "registration.welcomeStepTitle": "Hurray! Welcome to Scratch!", "registration.welcomeStepTitleNonEducator": "Welcome to Scratch, {username}!", @@ -272,9 +272,8 @@ "emailConfirmationBanner.confirmLinkText": "Confirm your email", "emailConfirmationBanner.faqLinkText": "Having trouble?", - "emailConfirmationBanner.parentEmail.confirm": "A parent/guardian needs to {confirmLink}. {faqLink}", + "emailConfirmationBanner.parentEmail.confirm": "A parent needs to {confirmLink} before you can share projects.", "emailConfirmationBanner.parentEmail.confirmLinkText": "confirm your account", - "emailConfirmationBanner.parentEmail.faqLinkText": "Having trouble?", "emailConfirmationModal.confirm": "Confirm your email", "emailConfirmationModal.wantToShare": "Want to share on Scratch?", @@ -292,15 +291,13 @@ "emailConfirmationModal.parentEmail.confirm": "Confirm your account", "emailConfirmationModal.parentEmail.wantToShare": "Want to share on Scratch?", - "emailConfirmationModal.parentEmail.clickEmailLink": "Your parent/guardian needs to confirm your email address by clicking the link in the email we sent to:", + "emailConfirmationModal.parentEmail.clickEmailLink": "Your parent needs to click on the link in the email we sent to:", "emailConfirmationModal.parentEmail.resendEmail": "Resend confirmation email", "emailConfirmationModal.parentEmail.confirmingTips": "Tips for confirming your email address", "emailConfirmationModal.parentEmail.tipWaitTenMinutes": "Wait for ten minutes. The email may take a while to arrive.", - "emailConfirmationModal.parentEmail.tipCheckSpam": "Check your spam folder.", - "emailConfirmationModal.parentEmail.correctEmail": "Make sure your parent's email address is correct, see {accountSettings}.", + "emailConfirmationModal.parentEmail.tipCheckSpam": "Ask your parent to check their spam folder.", + "emailConfirmationModal.parentEmail.correctEmail": "Make sure your parent's email address is correct. Check your {accountSettings}.", "emailConfirmationModal.parentEmail.accountSettings": "Account Settings", - "emailConfirmationModal.parentEmail.wantMoreInfo": "Want more information? {FAQLink}", - "emailConfirmationModal.parentEmail.checkOutFAQ": "Check out the FAQ", "emailConfirmationModal.parentEmail.havingTrouble": "Having Trouble? {tipsLink}", "emailConfirmationModal.parentEmail.checkOutTips": "Check out these tips", From 697c8367fd584680c731c2f89c1e9f840ebe6b1e Mon Sep 17 00:00:00 2001 From: Georgi Angelov Date: Tue, 15 Oct 2024 15:16:13 +0300 Subject: [PATCH 12/73] feat: make the parents page display parent email confirmation screen --- src/views/parents/l10n.json | 1 + src/views/parents/parents.jsx | 22 +++++++++++++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/views/parents/l10n.json b/src/views/parents/l10n.json index f48cccf0a..ecf30e3aa 100644 --- a/src/views/parents/l10n.json +++ b/src/views/parents/l10n.json @@ -1,5 +1,6 @@ { "parents.title": "For Parents", + "parents.emailConfirmedTitle": "Your child's account has been confirmed", "parents.intro": "Scratch is a programming language and an online community where children\n can program and share interactive media such as stories, games, and \nanimation with people from all over the world. As children create with \nScratch, they learn to think creatively, work collaboratively, and \nreason systematically. Scratch is designed, developed, and moderated by the {scratchFoundation}, a nonprofit organization. ", "parents.scratchFoundationLinkText": "Scratch Foundation", "parents.overview": "How it works", diff --git a/src/views/parents/parents.jsx b/src/views/parents/parents.jsx index cc2e7733f..fb5cd2854 100644 --- a/src/views/parents/parents.jsx +++ b/src/views/parents/parents.jsx @@ -10,12 +10,24 @@ const render = require('../../lib/render.jsx'); require('./parents.scss'); -const Landing = () => ( -
+const Landing = () => { + const isParentConfirmingChildEmail = React.useMemo(() => { + const query = window.location.search; + + return query.indexOf('from_confirmation=true') >= 0; + }, [window.location.search]); + + return (

- +

@@ -266,7 +278,7 @@ const Landing = () => (

-
-); +
); +}; render(, document.getElementById('app')); From 731ca7a7b2399a4b9f067be2a632b3cf9a8bd81d Mon Sep 17 00:00:00 2001 From: Kaloyan Manolov Date: Wed, 16 Oct 2024 11:46:57 +0300 Subject: [PATCH 13/73] feat: uepr-12: viusalise community guidelines modal on home screen after registration --- .../community-guidelines-modal.jsx} | 10 ++- .../community-guidelines.jsx | 48 +++++------ .../onboarding-navigation.jsx | 6 +- src/l10n.json | 83 ++++++------------- src/redux/navigation.js | 6 +- .../become-a-scratcher/become-a-scratcher.jsx | 42 +++++----- src/views/become-a-scratcher/l10n.json | 25 ------ src/views/splash/splash.jsx | 18 ++-- 8 files changed, 95 insertions(+), 143 deletions(-) rename src/components/{onboarding-navigation/onboarding-navigation-wrapper.jsx => community-guidelines/community-guidelines-modal.jsx} (84%) diff --git a/src/components/onboarding-navigation/onboarding-navigation-wrapper.jsx b/src/components/community-guidelines/community-guidelines-modal.jsx similarity index 84% rename from src/components/onboarding-navigation/onboarding-navigation-wrapper.jsx rename to src/components/community-guidelines/community-guidelines-modal.jsx index 305271934..246e8ff2a 100644 --- a/src/components/onboarding-navigation/onboarding-navigation-wrapper.jsx +++ b/src/components/community-guidelines/community-guidelines-modal.jsx @@ -1,7 +1,7 @@ import React, {useCallback, useState, useEffect} from 'react'; import {CommunityGuidelines, communityGuidelines} from './community-guidelines.jsx'; import PropTypes from 'prop-types'; -import {injectIntl} from 'react-intl'; +import {FormattedMessage, injectIntl} from 'react-intl'; const ReactModal = require('react-modal'); const CommunityGuidelinesModal = props => { @@ -17,7 +17,7 @@ const CommunityGuidelinesModal = props => { return ( { userId={props.userId} currentPage={currentPage} onNextPage={currentPage < communityGuidelines.length - 1 ? onNextPage : onComplete} - nextButtonText={currentPage === communityGuidelines.length - 1 ? 'I Understand' : null} + nextButtonText={currentPage === communityGuidelines.length - 1 ? + : + null} onBackPage={currentPage > 0 ? onBackPage : null} /> ); @@ -47,4 +49,4 @@ CommunityGuidelinesModal.propTypes = { isOpen: PropTypes.bool }; -export const IntlCommunityGuidelinesWrapper = injectIntl(CommunityGuidelinesModal); +export const IntlCommunityGuidelinesModal = injectIntl(CommunityGuidelinesModal); diff --git a/src/components/community-guidelines/community-guidelines.jsx b/src/components/community-guidelines/community-guidelines.jsx index 8dede98bb..fa7252c4e 100644 --- a/src/components/community-guidelines/community-guidelines.jsx +++ b/src/components/community-guidelines/community-guidelines.jsx @@ -8,54 +8,54 @@ import PropTypes from 'prop-types'; export const communityGuidelines = [ { - section: 'becomeAScratcher.guidelines.respectSection', - header: 'becomeAScratcher.guidelines.respectHeader', - body: 'becomeAScratcher.guidelines.respectBody', + section: 'communityGuidelines.guidelines.respectSection', + header: 'communityGuidelines.guidelines.respectHeader', + body: 'communityGuidelines.guidelines.respectBody', image: 'respect-illustration.svg', imageLeft: true }, { - section: 'becomeAScratcher.guidelines.safeSection', - header: 'becomeAScratcher.guidelines.safeHeader', - body: 'becomeAScratcher.guidelines.safeBody', + section: 'communityGuidelines.guidelines.safeSection', + header: 'communityGuidelines.guidelines.safeHeader', + body: 'communityGuidelines.guidelines.safeBody', image: 'safe-illustration.svg' }, { - section: 'becomeAScratcher.guidelines.feedbackSection', - header: 'becomeAScratcher.guidelines.feedbackHeader', - body: 'becomeAScratcher.guidelines.feedbackBody', + section: 'communityGuidelines.guidelines.feedbackSection', + header: 'communityGuidelines.guidelines.feedbackHeader', + body: 'communityGuidelines.guidelines.feedbackBody', image: 'feedback-illustration.svg', imageLeft: true }, { - section: 'becomeAScratcher.guidelines.remix1Section', - header: 'becomeAScratcher.guidelines.remix1Header', - body: 'becomeAScratcher.guidelines.remix1Body', + section: 'communityGuidelines.guidelines.remix1Section', + header: 'communityGuidelines.guidelines.remix1Header', + body: 'communityGuidelines.guidelines.remix1Body', image: 'remix-illustration-1.svg' }, { - section: 'becomeAScratcher.guidelines.remix2Section', - header: 'becomeAScratcher.guidelines.remix2Header', - body: 'becomeAScratcher.guidelines.remix2Body', + section: 'communityGuidelines.guidelines.remix2Section', + header: 'communityGuidelines.guidelines.remix2Header', + body: 'communityGuidelines.guidelines.remix2Body', image: 'remix-illustration-2.svg' }, { - section: 'becomeAScratcher.guidelines.remix3Section', - header: 'becomeAScratcher.guidelines.remix3Header', - body: 'becomeAScratcher.guidelines.remix3Body', + section: 'communityGuidelines.guidelines.remix3Section', + header: 'communityGuidelines.guidelines.remix3Header', + body: 'communityGuidelines.guidelines.remix3Body', image: 'remix-illustration-3.svg' }, { - section: 'becomeAScratcher.guidelines.honestSection', - header: 'becomeAScratcher.guidelines.honestHeader', - body: 'becomeAScratcher.guidelines.honestBody', + section: 'communityGuidelines.guidelines.honestSection', + header: 'communityGuidelines.guidelines.honestHeader', + body: 'communityGuidelines.guidelines.honestBody', image: 'honest-illustration.svg', imageLeft: true }, { - section: 'becomeAScratcher.guidelines.friendlySection', - header: 'becomeAScratcher.guidelines.friendlyHeader', - body: 'becomeAScratcher.guidelines.friendlyBody', + section: 'communityGuidelines.guidelines.friendlySection', + header: 'communityGuidelines.guidelines.friendlyHeader', + body: 'communityGuidelines.guidelines.friendlyBody', image: 'friendly-illustration.svg' } ]; diff --git a/src/components/onboarding-navigation/onboarding-navigation.jsx b/src/components/onboarding-navigation/onboarding-navigation.jsx index 8a89ff135..9c6dddd5d 100644 --- a/src/components/onboarding-navigation/onboarding-navigation.jsx +++ b/src/components/onboarding-navigation/onboarding-navigation.jsx @@ -45,7 +45,7 @@ const OnboardingNavigation = ({ /> {} } @@ -55,7 +55,7 @@ const OnboardingNavigation = ({
}