From 2e9b37a02bfca356196be7ee62610b49bd721fd7 Mon Sep 17 00:00:00 2001 From: Ahmad Abdolsaheb Date: Thu, 10 Mar 2022 12:13:10 +0300 Subject: [PATCH] feat: A/B test firing a confetti on block donation modal (#45393) --- client/i18n/locales/english/translations.json | 1 - client/package.json | 2 + .../components/Donation/donation-modal.tsx | 20 ++------ client/src/redux/donation-saga.js | 10 +++- client/src/utils/fire-confetti.ts | 48 +++++++++++++++++++ package-lock.json | 28 +++++++++++ 6 files changed, 92 insertions(+), 17 deletions(-) create mode 100644 client/src/utils/fire-confetti.ts diff --git a/client/i18n/locales/english/translations.json b/client/i18n/locales/english/translations.json index c06784727d..892149051c 100644 --- a/client/i18n/locales/english/translations.json +++ b/client/i18n/locales/english/translations.json @@ -332,7 +332,6 @@ "duration-2": "Become a monthly supporter of our nonprofit.", "duration-3": "Become an annual supporter of our nonprofit", "duration-4": "Become a supporter of our nonprofit", - "duration-5": "Support our nonprofit and earn a supporter profile badge.", "nicely-done": "Nicely done. You just completed {{block}}.", "credit-card": "Credit Card", "credit-card-2": "Or donate with a credit card:", diff --git a/client/package.json b/client/package.json index 5909e1c5ce..bf83d46ab9 100644 --- a/client/package.json +++ b/client/package.json @@ -52,6 +52,7 @@ "@reach/router": "1.3.4", "@stripe/react-stripe-js": "1.7.0", "@stripe/stripe-js": "1.24.0", + "@types/canvas-confetti": "1.4.2", "@types/react-scrollable-anchor": "0.6.1", "algoliasearch": "4.12.2", "assert": "2.0.0", @@ -60,6 +61,7 @@ "bezier-easing": "2.1.0", "browser-cookies": "1.2.0", "buffer": "6.0.3", + "canvas-confetti": "1.5.1", "chai": "4.3.6", "crypto-browserify": "3.12.0", "date-fns": "2.27.0", diff --git a/client/src/components/Donation/donation-modal.tsx b/client/src/components/Donation/donation-modal.tsx index 27ffa552c7..1829e7f435 100644 --- a/client/src/components/Donation/donation-modal.tsx +++ b/client/src/components/Donation/donation-modal.tsx @@ -14,8 +14,7 @@ import { closeDonationModal, isDonationModalOpenSelector, recentlyClaimedBlockSelector, - executeGA, - isAVariantSelector + executeGA } from '../../redux'; import { isLocationSuperBlock } from '../../utils/path-parsers'; import { playTone } from '../../utils/tone'; @@ -25,11 +24,9 @@ import DonateForm from './donate-form'; const mapStateToProps = createSelector( isDonationModalOpenSelector, recentlyClaimedBlockSelector, - isAVariantSelector, - (show: boolean, recentlyClaimedBlock: string, isAVariant: boolean) => ({ + (show: boolean, recentlyClaimedBlock: string) => ({ show, - recentlyClaimedBlock, - isAVariant + recentlyClaimedBlock }) ); @@ -49,7 +46,6 @@ type DonateModalProps = { location: WindowLocation | undefined; recentlyClaimedBlock: string; show: boolean; - isAVariant: boolean; }; function DonateModal({ @@ -57,8 +53,7 @@ function DonateModal({ closeDonationModal, executeGA, location, - recentlyClaimedBlock, - isAVariant + recentlyClaimedBlock }: DonateModalProps): JSX.Element { const [closeLabel, setCloseLabel] = React.useState(false); const { t } = useTranslation(); @@ -134,11 +129,6 @@ function DonateModal({ ); - const renderABtestProgressText = () => { - if (isAVariant) return getDonationText(); - else {t('donate.duration-5')}; - }; - const progressDonationText = (
@@ -147,7 +137,7 @@ function DonateModal({ {!closeLabel && ( - {renderABtestProgressText()} + {getDonationText()} )} diff --git a/client/src/redux/donation-saga.js b/client/src/redux/donation-saga.js index d24fa9038c..4ff0539162 100644 --- a/client/src/redux/donation-saga.js +++ b/client/src/redux/donation-saga.js @@ -7,6 +7,7 @@ import { call, take } from 'redux-saga/effects'; +import { fireConfetti } from '../utils/fire-confetti'; import { addDonation, @@ -26,7 +27,8 @@ import { postChargeStripeComplete, postChargeStripeError, postChargeStripeCardComplete, - postChargeStripeCardError + postChargeStripeCardError, + isAVariantSelector } from './'; const defaultDonationErrorMessage = `Something is not right. Please contact donors@freecodecamp.org`; @@ -37,6 +39,12 @@ function* showDonateModalSaga() { yield delay(200); const recentlyClaimedBlock = yield select(recentlyClaimedBlockSelector); yield put(openDonationModal()); + if (recentlyClaimedBlock) { + const isAVariant = yield select(isAVariantSelector); + if (isAVariant === false) { + fireConfetti(); + } + } yield take(appTypes.closeDonationModal); if (recentlyClaimedBlock) { yield put(preventBlockDonationRequests()); diff --git a/client/src/utils/fire-confetti.ts b/client/src/utils/fire-confetti.ts new file mode 100644 index 0000000000..2b36c2e3e6 --- /dev/null +++ b/client/src/utils/fire-confetti.ts @@ -0,0 +1,48 @@ +import confetti from 'canvas-confetti'; + +export const fireConfetti = () => { + const count = 200; + const defaults = { + origin: { y: 0.7 }, + zIndex: 10000 + }; + + function fire( + particleRatio: number, + opts: { + spread?: number; + startVelocity?: number; + decay?: number; + scalar?: number; + } + ) { + confetti( + Object.assign({}, defaults, opts, { + particleCount: Math.floor(count * particleRatio) + }) + )?.catch(err => console.log(err)); + } + + fire(0.25, { + spread: 26, + startVelocity: 55 + }); + fire(0.2, { + spread: 60 + }); + fire(0.35, { + spread: 100, + decay: 0.91, + scalar: 0.8 + }); + fire(0.1, { + spread: 120, + startVelocity: 25, + decay: 0.92, + scalar: 1.2 + }); + fire(0.1, { + spread: 120, + startVelocity: 45 + }); +}; diff --git a/package-lock.json b/package-lock.json index 2bb15dc510..dd2fea95d0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -985,6 +985,7 @@ "@reach/router": "1.3.4", "@stripe/react-stripe-js": "1.7.0", "@stripe/stripe-js": "1.24.0", + "@types/canvas-confetti": "1.4.2", "@types/react-scrollable-anchor": "0.6.1", "algoliasearch": "4.12.2", "assert": "2.0.0", @@ -993,6 +994,7 @@ "bezier-easing": "2.1.0", "browser-cookies": "1.2.0", "buffer": "6.0.3", + "canvas-confetti": "1.5.1", "chai": "4.3.6", "crypto-browserify": "3.12.0", "date-fns": "2.27.0", @@ -15171,6 +15173,11 @@ "@types/responselike": "*" } }, + "node_modules/@types/canvas-confetti": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/canvas-confetti/-/canvas-confetti-1.4.2.tgz", + "integrity": "sha512-t45KUDHlwrD9PJVRHc5z1SlXhO82BQEgMKUXGEV1KnWLFMPA6Y5LfUsLTHHzH9KcKDHZLEiYYH5nIDcjRKWNTg==" + }, "node_modules/@types/chai": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz", @@ -20396,6 +20403,15 @@ "resolved": "https://registry.npmjs.org/canonical-json/-/canonical-json-0.0.4.tgz", "integrity": "sha1-ZXnAcsPbXEd+xB3JePvyuPQQdKM=" }, + "node_modules/canvas-confetti": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/canvas-confetti/-/canvas-confetti-1.5.1.tgz", + "integrity": "sha512-Ncz+oZJP6OvY7ti4E1slxVlyAV/3g7H7oQtcCDXgwGgARxPnwYY9PW5Oe+I8uvspYNtuHviAdgA0LfcKFWJfpg==", + "funding": { + "type": "donate", + "url": "https://www.paypal.me/kirilvatev" + } + }, "node_modules/capture-exit": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", @@ -56676,6 +56692,7 @@ "@stripe/stripe-js": "1.24.0", "@testing-library/jest-dom": "5.16.2", "@testing-library/react": "12.1.3", + "@types/canvas-confetti": "*", "@types/react-scrollable-anchor": "0.6.1", "algoliasearch": "4.12.2", "assert": "2.0.0", @@ -56686,6 +56703,7 @@ "bezier-easing": "2.1.0", "browser-cookies": "1.2.0", "buffer": "6.0.3", + "canvas-confetti": "1.5.1", "chai": "4.3.6", "chokidar": "3.5.3", "copy-webpack-plugin": "9.1.0", @@ -65756,6 +65774,11 @@ "@types/responselike": "*" } }, + "@types/canvas-confetti": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@types/canvas-confetti/-/canvas-confetti-1.4.2.tgz", + "integrity": "sha512-t45KUDHlwrD9PJVRHc5z1SlXhO82BQEgMKUXGEV1KnWLFMPA6Y5LfUsLTHHzH9KcKDHZLEiYYH5nIDcjRKWNTg==" + }, "@types/chai": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.0.tgz", @@ -70219,6 +70242,11 @@ "resolved": "https://registry.npmjs.org/canonical-json/-/canonical-json-0.0.4.tgz", "integrity": "sha1-ZXnAcsPbXEd+xB3JePvyuPQQdKM=" }, + "canvas-confetti": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/canvas-confetti/-/canvas-confetti-1.5.1.tgz", + "integrity": "sha512-Ncz+oZJP6OvY7ti4E1slxVlyAV/3g7H7oQtcCDXgwGgARxPnwYY9PW5Oe+I8uvspYNtuHviAdgA0LfcKFWJfpg==" + }, "capture-exit": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz",