mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-09-24 23:01:18 +08:00
feat: turn donation modal element into react components (#51670)
Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
co-authored by
Oliver Eyton-Williams
parent
7faf636051
commit
606ca74deb
@@ -12,7 +12,8 @@ import {
|
||||
PaymentContext,
|
||||
subscriptionAmounts,
|
||||
defaultDonation,
|
||||
defaultTierAmount
|
||||
defaultTierAmount,
|
||||
type DonationAmount
|
||||
} from '../../../../shared/config/donation-settings';
|
||||
import BearProgressModal from '../../assets/images/components/bear-progress-modal';
|
||||
import BearBlockCompletion from '../../assets/images/components/bear-block-completion-modal';
|
||||
@@ -56,7 +57,7 @@ type DonateModalProps = {
|
||||
show: boolean;
|
||||
};
|
||||
|
||||
const RenderIlustration = ({
|
||||
const Ilustration = ({
|
||||
recentlyClaimedBlock
|
||||
}: {
|
||||
recentlyClaimedBlock: RecentlyClaimedBlock;
|
||||
@@ -74,78 +75,20 @@ function getctaNumberBetween1To10() {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
function DonateModal({
|
||||
show,
|
||||
closeDonationModal,
|
||||
executeGA,
|
||||
location,
|
||||
function ModalHeader({
|
||||
closeLabel,
|
||||
ctaNumber,
|
||||
showMultiTier,
|
||||
recentlyClaimedBlock
|
||||
}: DonateModalProps): JSX.Element {
|
||||
const [closeLabel, setCloseLabel] = useState(false);
|
||||
const [ctaNumber, setCtaNumber] = useState(0);
|
||||
const [isDisabled, setIsDisabled] = useState(true);
|
||||
const [showSkipButton, setShowSkipButton] = useState(false);
|
||||
const [showDonateForm, setShowDonateForm] = useState(true);
|
||||
const [donationAmount, setDonationAmount] = useState(
|
||||
defaultDonation.donationAmount
|
||||
);
|
||||
const loadElementsIndividually = useFeature('load_elements_individually').on;
|
||||
const showMultiTier = useFeature('multi-tier').on;
|
||||
}: {
|
||||
closeLabel: boolean;
|
||||
ctaNumber: number;
|
||||
showMultiTier: boolean;
|
||||
recentlyClaimedBlock: RecentlyClaimedBlock;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
// test wheather the conversions are being distributed properly
|
||||
useFeature('aa-test-in-component');
|
||||
|
||||
const handleProcessing = () => {
|
||||
setCloseLabel(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (loadElementsIndividually) {
|
||||
const timer = setTimeout(() => {
|
||||
setIsDisabled(false);
|
||||
setShowSkipButton(true);
|
||||
}, 4000);
|
||||
return () => clearTimeout(timer);
|
||||
} else {
|
||||
setIsDisabled(false);
|
||||
setShowSkipButton(true);
|
||||
}
|
||||
}, [loadElementsIndividually]);
|
||||
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
void playTone('donation');
|
||||
executeGA({ event: 'pageview', pagePath: '/donation-modal' });
|
||||
executeGA({
|
||||
event: 'donation_view',
|
||||
action: `Displayed ${
|
||||
recentlyClaimedBlock !== null ? 'Block' : 'Progress'
|
||||
} Donation Modal`
|
||||
});
|
||||
}
|
||||
}, [show, recentlyClaimedBlock, executeGA]);
|
||||
|
||||
useEffect(() => {
|
||||
if (show) setCtaNumber(getctaNumberBetween1To10());
|
||||
}, [show]);
|
||||
|
||||
useEffect(() => {
|
||||
if (showMultiTier) {
|
||||
setShowDonateForm(false);
|
||||
setDonationAmount(defaultTierAmount);
|
||||
}
|
||||
}, [showMultiTier]);
|
||||
|
||||
const handleModalHide = () => {
|
||||
// If modal is open on a SuperBlock page
|
||||
if (isLocationSuperBlock(location)) {
|
||||
goToAnchor('claim-cert-block');
|
||||
}
|
||||
};
|
||||
|
||||
const modalHeader = (
|
||||
<div className=' text-center block-modal-text'>
|
||||
return (
|
||||
<div className='text-center block-modal-text'>
|
||||
<Row>
|
||||
{!closeLabel && (
|
||||
<Col sm={10} smOffset={1} xs={12}>
|
||||
@@ -169,37 +112,27 @@ function DonateModal({
|
||||
<Spacer size='small' />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const closeButtonRow = (
|
||||
<>
|
||||
<Row>
|
||||
<Col
|
||||
sm={4}
|
||||
smOffset={4}
|
||||
xs={8}
|
||||
xsOffset={2}
|
||||
className={showSkipButton ? 'no-delay-fade-in' : 'no-opacity'}
|
||||
>
|
||||
<Button
|
||||
bsSize='sm'
|
||||
bsStyle='primary'
|
||||
className='btn-link close-button'
|
||||
onClick={closeDonationModal}
|
||||
tabIndex='0'
|
||||
disabled={isDisabled}
|
||||
>
|
||||
{closeLabel ? t('buttons.close') : t('buttons.ask-later')}
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
</>
|
||||
);
|
||||
|
||||
const selectionTabs = (
|
||||
function SelectionTabs({
|
||||
loadElementsIndividually,
|
||||
donationAmount,
|
||||
setDonationAmount,
|
||||
setShowDonateForm
|
||||
}: {
|
||||
loadElementsIndividually: boolean;
|
||||
donationAmount: DonationAmount;
|
||||
setDonationAmount: React.Dispatch<React.SetStateAction<DonationAmount>>;
|
||||
setShowDonateForm: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Row className={'donate-btn-group'}>
|
||||
<Col
|
||||
xs={12}
|
||||
className={loadElementsIndividually && 'two-seconds-delay-fade-in'}
|
||||
{...(loadElementsIndividually && {
|
||||
className: 'two-seconds-delay-fade-in'
|
||||
})}
|
||||
>
|
||||
<b>
|
||||
{t('donate.confirm-monthly', {
|
||||
@@ -255,12 +188,114 @@ function DonateModal({
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
const donationFormRow = (
|
||||
function CloseButtonRow({
|
||||
showSkipButton,
|
||||
isDisabled,
|
||||
closeLabel,
|
||||
closeDonationModal
|
||||
}: {
|
||||
showSkipButton: boolean;
|
||||
isDisabled: boolean;
|
||||
closeLabel: boolean;
|
||||
closeDonationModal: () => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Row>
|
||||
<Col
|
||||
sm={4}
|
||||
smOffset={4}
|
||||
xs={8}
|
||||
xsOffset={2}
|
||||
className={showSkipButton ? 'no-delay-fade-in' : 'no-opacity'}
|
||||
>
|
||||
<Button
|
||||
bsSize='sm'
|
||||
bsStyle='primary'
|
||||
className='btn-link close-button'
|
||||
onClick={closeDonationModal}
|
||||
tabIndex='0'
|
||||
disabled={isDisabled}
|
||||
>
|
||||
{closeLabel ? t('buttons.close') : t('buttons.ask-later')}
|
||||
</Button>
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
function DonationBody({
|
||||
closeLabel,
|
||||
ctaNumber,
|
||||
showMultiTier,
|
||||
recentlyClaimedBlock,
|
||||
donationAmount,
|
||||
loadElementsIndividually,
|
||||
handleProcessing,
|
||||
setShowDonateForm,
|
||||
closeDonationModal,
|
||||
isDisabled,
|
||||
showSkipButton
|
||||
}: {
|
||||
closeLabel: boolean;
|
||||
ctaNumber: number;
|
||||
showMultiTier: boolean;
|
||||
recentlyClaimedBlock: RecentlyClaimedBlock;
|
||||
donationAmount: DonationAmount;
|
||||
loadElementsIndividually: boolean;
|
||||
handleProcessing: () => void;
|
||||
setShowDonateForm: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
closeDonationModal: () => void;
|
||||
isDisabled: boolean;
|
||||
showSkipButton: boolean;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<ModalHeader
|
||||
closeLabel={closeLabel}
|
||||
ctaNumber={ctaNumber}
|
||||
recentlyClaimedBlock={recentlyClaimedBlock}
|
||||
showMultiTier={showMultiTier}
|
||||
/>
|
||||
<DonationFormRow
|
||||
donationAmount={donationAmount}
|
||||
handleProcessing={handleProcessing}
|
||||
showMultiTier={showMultiTier}
|
||||
loadElementsIndividually={loadElementsIndividually}
|
||||
setShowDonateForm={setShowDonateForm}
|
||||
/>
|
||||
<CloseButtonRow
|
||||
closeDonationModal={closeDonationModal}
|
||||
closeLabel={closeLabel}
|
||||
isDisabled={isDisabled}
|
||||
showSkipButton={showSkipButton}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function DonationFormRow({
|
||||
handleProcessing,
|
||||
loadElementsIndividually,
|
||||
showMultiTier,
|
||||
setShowDonateForm,
|
||||
donationAmount
|
||||
}: {
|
||||
handleProcessing: () => void;
|
||||
loadElementsIndividually: boolean;
|
||||
showMultiTier: boolean;
|
||||
setShowDonateForm: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
donationAmount: DonationAmount;
|
||||
}) {
|
||||
return (
|
||||
<Row>
|
||||
<Col
|
||||
xs={12}
|
||||
className={loadElementsIndividually && 'two-seconds-delay-fade-in'}
|
||||
{...(loadElementsIndividually && {
|
||||
className: 'two-seconds-delay-fade-in'
|
||||
})}
|
||||
>
|
||||
<DonateForm
|
||||
handleProcessing={handleProcessing}
|
||||
@@ -275,28 +310,148 @@ function DonateModal({
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
}
|
||||
|
||||
const multiTierModalBody = (
|
||||
function MultiTierDonationBody({
|
||||
closeLabel,
|
||||
ctaNumber,
|
||||
showMultiTier,
|
||||
recentlyClaimedBlock,
|
||||
donationAmount,
|
||||
loadElementsIndividually,
|
||||
setDonationAmount,
|
||||
setShowDonateForm,
|
||||
closeDonationModal,
|
||||
isDisabled,
|
||||
showSkipButton,
|
||||
showDonateForm,
|
||||
handleProcessing
|
||||
}: {
|
||||
closeLabel: boolean;
|
||||
ctaNumber: number;
|
||||
showMultiTier: boolean;
|
||||
recentlyClaimedBlock: RecentlyClaimedBlock;
|
||||
donationAmount: DonationAmount;
|
||||
loadElementsIndividually: boolean;
|
||||
setDonationAmount: React.Dispatch<React.SetStateAction<DonationAmount>>;
|
||||
setShowDonateForm: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
closeDonationModal: () => void;
|
||||
isDisabled: boolean;
|
||||
showSkipButton: boolean;
|
||||
showDonateForm: boolean;
|
||||
handleProcessing: () => void;
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<div className={showDonateForm ? 'hide' : ''}>
|
||||
{modalHeader}
|
||||
{selectionTabs}
|
||||
{closeButtonRow}
|
||||
<div {...(showDonateForm && { className: 'hide' })}>
|
||||
<ModalHeader
|
||||
closeLabel={closeLabel}
|
||||
ctaNumber={ctaNumber}
|
||||
recentlyClaimedBlock={recentlyClaimedBlock}
|
||||
showMultiTier={showMultiTier}
|
||||
/>
|
||||
<SelectionTabs
|
||||
donationAmount={donationAmount}
|
||||
loadElementsIndividually={loadElementsIndividually}
|
||||
setDonationAmount={setDonationAmount}
|
||||
setShowDonateForm={setShowDonateForm}
|
||||
/>
|
||||
<CloseButtonRow
|
||||
closeDonationModal={closeDonationModal}
|
||||
closeLabel={closeLabel}
|
||||
isDisabled={isDisabled}
|
||||
showSkipButton={showSkipButton}
|
||||
/>
|
||||
</div>
|
||||
<div className={!showDonateForm ? 'hide' : ''}>
|
||||
{donationFormRow}
|
||||
{closeLabel && closeButtonRow}
|
||||
<div {...(!showDonateForm && { className: 'hide' })}>
|
||||
<DonationFormRow
|
||||
donationAmount={donationAmount}
|
||||
handleProcessing={handleProcessing}
|
||||
showMultiTier={showMultiTier}
|
||||
loadElementsIndividually={loadElementsIndividually}
|
||||
setShowDonateForm={setShowDonateForm}
|
||||
/>
|
||||
{closeLabel && (
|
||||
<CloseButtonRow
|
||||
closeDonationModal={closeDonationModal}
|
||||
closeLabel={closeLabel}
|
||||
isDisabled={isDisabled}
|
||||
showSkipButton={showSkipButton}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const defaultModalBody = (
|
||||
<>
|
||||
{modalHeader}
|
||||
{donationFormRow}
|
||||
{closeButtonRow}
|
||||
</>
|
||||
function DonateModal({
|
||||
show,
|
||||
closeDonationModal,
|
||||
executeGA,
|
||||
location,
|
||||
recentlyClaimedBlock
|
||||
}: DonateModalProps): JSX.Element {
|
||||
const [closeLabel, setCloseLabel] = useState(false);
|
||||
const [ctaNumber, setCtaNumber] = useState(0);
|
||||
const [isDisabled, setIsDisabled] = useState(true);
|
||||
const [showSkipButton, setShowSkipButton] = useState(false);
|
||||
const [showDonateForm, setShowDonateForm] = useState(true);
|
||||
const [donationAmount, setDonationAmount] = useState(
|
||||
defaultDonation.donationAmount
|
||||
);
|
||||
const loadElementsIndividually = useFeature('load_elements_individually').on;
|
||||
const showMultiTier = useFeature('multi-tier').on;
|
||||
|
||||
// test wheather the conversions are being distributed properly
|
||||
useFeature('aa-test-in-component');
|
||||
|
||||
const handleProcessing = () => {
|
||||
setCloseLabel(true);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (loadElementsIndividually) {
|
||||
const timer = setTimeout(() => {
|
||||
setIsDisabled(false);
|
||||
setShowSkipButton(true);
|
||||
}, 4000);
|
||||
return () => clearTimeout(timer);
|
||||
} else {
|
||||
setIsDisabled(false);
|
||||
setShowSkipButton(true);
|
||||
}
|
||||
}, [loadElementsIndividually]);
|
||||
|
||||
useEffect(() => {
|
||||
if (show) {
|
||||
void playTone('donation');
|
||||
executeGA({ event: 'pageview', pagePath: '/donation-modal' });
|
||||
executeGA({
|
||||
event: 'donation_view',
|
||||
action: `Displayed ${
|
||||
recentlyClaimedBlock !== null ? 'Block' : 'Progress'
|
||||
} Donation Modal`
|
||||
});
|
||||
}
|
||||
}, [show, recentlyClaimedBlock, executeGA]);
|
||||
|
||||
useEffect(() => {
|
||||
if (show) setCtaNumber(getctaNumberBetween1To10());
|
||||
}, [show]);
|
||||
|
||||
useEffect(() => {
|
||||
if (showMultiTier) {
|
||||
setShowDonateForm(false);
|
||||
setDonationAmount(defaultTierAmount);
|
||||
}
|
||||
}, [showMultiTier]);
|
||||
|
||||
const handleModalHide = () => {
|
||||
// If modal is open on a SuperBlock page
|
||||
if (isLocationSuperBlock(location)) {
|
||||
goToAnchor('claim-cert-block');
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@@ -305,11 +460,41 @@ function DonateModal({
|
||||
onExited={handleModalHide}
|
||||
show={show}
|
||||
>
|
||||
<Modal.Body className={'no-delay-fade-in'}>
|
||||
<Modal.Body className='no-delay-fade-in'>
|
||||
<div className='donation-icon-container'>
|
||||
<RenderIlustration recentlyClaimedBlock={recentlyClaimedBlock} />
|
||||
<Ilustration recentlyClaimedBlock={recentlyClaimedBlock} />
|
||||
</div>
|
||||
{showMultiTier ? multiTierModalBody : defaultModalBody}
|
||||
{showMultiTier ? (
|
||||
<MultiTierDonationBody
|
||||
recentlyClaimedBlock={recentlyClaimedBlock}
|
||||
setDonationAmount={setDonationAmount}
|
||||
setShowDonateForm={setShowDonateForm}
|
||||
showDonateForm={showDonateForm}
|
||||
showMultiTier={showMultiTier}
|
||||
showSkipButton={showSkipButton}
|
||||
closeDonationModal={closeDonationModal}
|
||||
closeLabel={closeLabel}
|
||||
ctaNumber={ctaNumber}
|
||||
donationAmount={donationAmount}
|
||||
handleProcessing={handleProcessing}
|
||||
isDisabled={isDisabled}
|
||||
loadElementsIndividually={loadElementsIndividually}
|
||||
/>
|
||||
) : (
|
||||
<DonationBody
|
||||
closeDonationModal={closeDonationModal}
|
||||
closeLabel={closeLabel}
|
||||
ctaNumber={ctaNumber}
|
||||
donationAmount={donationAmount}
|
||||
isDisabled={isDisabled}
|
||||
handleProcessing={handleProcessing}
|
||||
loadElementsIndividually={loadElementsIndividually}
|
||||
recentlyClaimedBlock={recentlyClaimedBlock}
|
||||
setShowDonateForm={setShowDonateForm}
|
||||
showMultiTier={showMultiTier}
|
||||
showSkipButton={showSkipButton}
|
||||
/>
|
||||
)}
|
||||
</Modal.Body>
|
||||
</Modal>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user