Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: [UEPR-187] [UEPR-192] [UEPR-194] ToU modal display logic #9248

Open
wants to merge 58 commits into
base: ux-integration
Choose a base branch
from

Conversation

Bogomil-Stoyanov
Copy link
Contributor

@Bogomil-Stoyanov Bogomil-Stoyanov commented Feb 19, 2025

Ticket:
UEPR-192
UEPR-194
UEPR-187

Adds the logic for displaying the new ToU modals + banner

Banner:
image

Reminder modal (opens from banner):
image

ToU Modal under 16:
image

ToU Modal over 16:
image

Locked Account modal:
image

renovate bot added 30 commits February 9, 2025 13:37
…vate/lock-file-maintenance

fix(deps): lock file maintenance
…vate/scratch-l10n-5.x

fix(deps): update dependency scratch-l10n to v5.0.120
…vate/scratch-l10n-5.x

fix(deps): update dependency scratch-l10n to v5.0.121
…vate/babel-monorepo

chore(deps): update babel monorepo to v7.26.8
…vate/scratch-l10n-5.x

fix(deps): update dependency scratch-l10n to v5.0.122
…vate/scratch-l10n-5.x

fix(deps): update dependency scratch-l10n to v5.0.123
…vate/scratch-storage-4.x

fix(deps): update dependency scratch-storage to ^4.0.49
…vate/formatjs-monorepo

chore(deps): update formatjs monorepo
…vate/scratch-l10n-5.x

fix(deps): update dependency scratch-l10n to v5.0.124
…vate/lock-file-maintenance

fix(deps): lock file maintenance
…vate/postcss-8.x

chore(deps): update dependency postcss to v8.5.2
…vate/scratch-l10n-5.x

fix(deps): update dependency scratch-l10n to v5.0.127
…vate/lock-file-maintenance

fix(deps): lock file maintenance
…vate/scratch-l10n-5.x

fix(deps): update dependency scratch-l10n to v5.0.128
…vate/scratch-storage-4.x

fix(deps): update dependency scratch-storage to ^4.0.50
className="tou-banner-link"
// eslint-disable-next-line react/jsx-no-bind
onClick={() => {
setIsModalVisible(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not wrap in useCallback instead of ignoring warning?

@@ -17,6 +18,7 @@ const Page = ({
showDonorRecognition
}) => (
<ErrorBoundary componentName="Page">
<TermsOfServiceModal />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TermsOfUseModal to keep the naming consistent?

@@ -0,0 +1,44 @@
import React, {useState} from 'react';
import {injectIntl, FormattedMessage} from 'react-intl';
import TosModalReminderUnder16 from './variations/tou-reminder-under-16.jsx';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about using the whole name TermsOfUse ? Additionally, I think we can omit the under-16 since that's a detail regarding how it's used and since we don't have a reminder modal in the other case, we don't need the distinction. So, finally, the name of the file can become terms-of-use-reminder

@@ -0,0 +1,44 @@
import React, {useState} from 'react';
import {injectIntl, FormattedMessage} from 'react-intl';
import TosModalReminderUnder16 from './variations/tou-reminder-under-16.jsx';
Copy link
Contributor

@KManolov3 KManolov3 Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Regarding the file hierarchy - there is a folder under components - modal, where other modals are located. If you want to group terms of use modals together, I'd recommend a hierarchy such as
components/modal/terms-of-use/index.jsx
components/modal/terms-of-use/over-16/index.jsx
components/modal/terms-of-use/under-16/index.jsx
components/modal/terms-of-use/under-16/reminder.jsx
components/modal/terms-of-use/under-16/account-locked.jsx

That way we can even keep the under-16 / over-16 (since in the case of the initial modal it is a valid distinction) in the path name.

@@ -60,7 +60,20 @@ module.exports.requestSessionWithRetry = (resolve, reject, retriesLeft, totalDel
nextTimeout
);
}
return resolve(body);
console.log('session body:', body);
Copy link
Contributor

@KManolov3 KManolov3 Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reminder not to forget that when merging

...body,
flags: {
...body.flags,
hasAgreedToLatestTermsOfService: false,
Copy link
Contributor

@KManolov3 KManolov3 Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you update the logic to use the fields described here - https://scratchfoundation.atlassian.net/wiki/spaces/SET/pages/935100436/Terms+of+Service+confirmation#Backend-changes%3A

Namely, the exposed fields in session will be

has_accepted_terms_of_use → calculated based on TermsOfUseConsent.accepted_date && TERMS_OF_USE_UPDATED_DATE > TermsOfUseConsent.accepted_date

is_in_terms_of_use_grace_period -> calculated based on TermsOfUseConsent.grace_period_expiry_date && TermsOfUseConsent.grace_period_expiry_date >= now().toDate()

is_after_terms_of_use_grace_period → calculated based on !has_accepted_terms_of_use && TermsOfUseConsent.grace_period_expiry_date < now().toDate()`

I know that the fields described in the task description were different, but the initial idea had to evolve since then. Can you also update the task description? 🙇

@@ -356,8 +358,16 @@ class SplashPresentation extends React.Component { // eslint-disable-line react/
'teacherbanner.faqButton': formatMessage({id: 'teacherbanner.faqButton'})
};


const shouldShowToUBanner = (this.props.userUsesParentEmail &&
!this.props.hasAgreedToLatestTermsOfService &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should then depend on this.props.userUsesParentEmail && !this.props.hasAgreedToTermsOfUse && this.props.isInTermsOfUseGracePeriod.

Also, please verify with Anton once more that we don't want to show the banner after the grace period.

onClose={() => {
setIsModalVisible(false);
}}
isOpen={isModalVisible}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't it redundant to have isModalVisible both as a condition for visualising the modal, and in the isOpen prop here? Or is this necessary for some case?

@rosics-code
Copy link

I don't see a reason for a pop-up though? The last update to ToU banner had no pop-up, just saying it has been updated and a link to the ToU.


};

// eslint-disable-next-line arrow-body-style
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't have to disable the rule but omit the return. And do it like this:
const TermsOfUseBanner = () => ( <div className="tou-banner"> <FormattedMessage id="tos.banner.parentAgreement" values={{ a: TermsOfUseLink }} /> </div> );
Same for the other similar components

};

// eslint-disable-next-line arrow-body-style
const Step1 = ({onNextStep}) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we change the name of the component to something more explanatory? Same for src/components/tou-modal/variations/tou-reminder-under-16.jsx and src/components/tou-modal/variations/tou-under-16.jsx etc

<ProjectView.View />,
<div>
<TermsOfServiceModal />
<ProjectView.View />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Project page returns either PreviewPresentation wrapped in Page or IntlGUIWithProjectHandler. Probably we can add TermsOfServiceModal only to IntlGUIWithProjectHandler

}) => {

const [isModalVisible, setIsModalVisible] = useState(true);

Copy link
Contributor

@KManolov3 KManolov3 Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove extra empty line

usesParentEmail,
email
}) => {

Copy link
Contributor

@KManolov3 KManolov3 Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: remove empty line



const page = window.location.pathname.split('/')[1];
if (noShowModalPages.includes(page)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move this logic in the page component. It makes more sense for the parent of a component to know when a child component should be rendered, than the component itself.

</p>
<input className="tou-input" />
<div className="tou-modal-button-container">
<button
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is src/components/forms/button.jsx we can use it

Comment on lines 29 to 36
{currentStep === 1 ? (
<Step1
onNextStep={handleNextStep}
email={email}
/>
) : (
<TosEmailSentStep />
)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering and this is not really a must but do you think it would be better to have arrays with the steps and to display the component on the index corresponding to the currentStep?

}

.tou-modal-link {
color: $ui-mint-green !important;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we rewrite this to avoid the !important or is it necessary?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

without it the style is not properly applied

// eslint-disable-next-line arrow-body-style
const TosModalReminderUnder16 = ({onClose, isOpen, email}) => {
const [currentStep, setCurrentStep] = useState(1);
const handleNextStep = () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

useCallback



// eslint-disable-next-line arrow-body-style
const TosModalReminderUnder16 = ({onClose, isOpen, email}) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it's more convenient like that, but let's write components adhering to the style guide of the project, so we don't need to ignore eslint

/>
</p>
<p>
<FormattedMessage id="tos.over16.acceptConfirmation" />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tos -> termsOfUse

generally it's better to avoid abbreviations where possible, as that tends to confuse readers not familiar with that part of the code

defaultValue={email}
/>
<div className="tou-modal-button-container">
<button
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed replacing the button component here.

onActivateDeck={this.props.onActivateDeck}
/>
<>
<TermsOfServiceModal />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagined that we would put TermsOfServiceModal inside IntlGUIWithProjectHandler as the component already acts as a wrapper to gui.

import React, {useCallback, useState} from 'react';
import {injectIntl, FormattedMessage} from 'react-intl';
import TermsOfUseReminderModal from './under16/terms-of-use-reminder.jsx';

Copy link
Contributor

@KManolov3 KManolov3 Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO, following this file hierarchy, there is no need to include terms-of-use in the naming of the file themselves as that is already qualified by the route. It is still good to include it in the naming of the component function

@MiroslavDionisiev
Copy link
Contributor

There seems to be some linting issues according to the workflow


require('./terms-of-use-modal.scss');

export const noShowTermsOfUseModalPages = ['terms_of_use', 'privacy_policy', 'dmca', 'cookies'];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's move this const to the page component, where it is used

@AmpElectrecuted
Copy link

I don't see a reason for a pop-up though? The last update to ToU banner had no pop-up, just saying it has been updated and a link to the ToU.

The ST would probably end up in a lot of legal traps if they silently update the ToU. Just one typo fix would be fine for a silent update, but this is changing user rights, which requires a notification.

@Bogomil-Stoyanov Bogomil-Stoyanov changed the base branch from develop to ux-integration February 25, 2025 10:54
Copy link

@AmpElectrecuted AmpElectrecuted left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: "the Scratch's" should just be "Scratch's"
@Bogomil-Stoyanov

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants