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

TW-755: AB-Testing for ads acceptance banner #930

Merged
merged 8 commits into from
Jul 12, 2023
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"test": "jest",
"test:coverage": "jest --coverage",
"ts": "tsc --pretty",
"lint": "eslint src",
"lint": "eslint --quiet src",
"lint:fix": "prettier \"./**/*\" --write --ignore-unknown",
"clear:lint": "rimraf node_modules/.cache/.eslintcache",
"find-deadcode": "ts-prune --error"
Expand Down
16 changes: 14 additions & 2 deletions public/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -991,8 +991,20 @@
"enable": {
"message": "enable"
},
"enableAds": {
"message": "Enable Ads"
"enableAds": { "message": "Enable Ads" },
"payMeForEveryAdISee": { "message": "Pay me for every ad I see" },
"noThanksIhateFreeMoney": { "message": "No thanks, I hate free money" },
"acceptAdsBannerTitle_A": {
"message": "Get paid to discover exciting services and dApps!"
},
"acceptAdsBannerTitle_B": {
"message": "Earn by viewing ads in Temple Wallet"
},
"acceptAdsBannerText_A": {
"message": "Here's the deal: share some data with us (wallet address, IP) to see the most relevant ads and we'll *pay you* a fair share monthly. By doing so, you support the developers of Temple Wallet. Change your mind? Easily disable sharing in settings.\n\nStart earning now!"
},
"acceptAdsBannerText_B": {
"message": "Support the development team and earn tokens by viewing ads inside the wallet. To enable this feature, we request your permission to trace your Wallet Address and IP address. You can always disable ads in the settings."
},
"dAppsCheckmarkPrompt": {
"message": "Click on the checkmark to $action$ DApps interaction feature",
Expand Down
5 changes: 1 addition & 4 deletions src/app/atoms/Alert.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,7 @@ export const Alert: FC<AlertProps> = ({
</h2>
)}
{description && (
<div
className={classNames('pb-3 text-sm font-light break-words', 'overflow-y-auto', textColorClassName)}
style={{ maxHeight: '8rem' }}
>
<div className={classNames('pb-3 text-sm max-h-32 font-light break-words overflow-y-auto', textColorClassName)}>
{description}
</div>
)}
Expand Down
63 changes: 41 additions & 22 deletions src/app/atoms/Banner.tsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,57 @@
import React, { FC } from 'react';

import { TID, t } from '../../lib/i18n';
import clsx from 'clsx';

import { TestIDProps } from 'lib/analytics';
import { TID, T } from 'lib/i18n';

import { FormSecondaryButton } from './FormSecondaryButton';
import { FormSubmitButton } from './FormSubmitButton';

interface Props {
text: string;
description: string;
onEnable?: EmptyFn;
onDisable?: EmptyFn;
enableButtonText?: TID;
disableButtonText?: TID;
title: React.ReactElement;
description: TID;
enableButton: BannerButtonProps;
disableButton: BannerButtonProps;
}

export interface BannerButtonProps extends TestIDProps {
title: TID;
capitalize?: boolean;
onClick: EmptyFn;
}

export const Banner: FC<Props> = ({
text,
description,
onEnable,
onDisable,
enableButtonText = 'enable',
disableButtonText = 'disable'
}) => (
export const Banner: FC<Props> = ({ title, description, enableButton, disableButton }) => (
<div className="p-3 border border-gray-300 rounded-md bg-white mx-4 sm:mx-0 mb-3">
<p className="text-sm font-medium text-gray-900 mb-1">{text}</p>
<h5 className="text-sm font-medium text-gray-900 mb-1 whitespace-pre-line">{title}</h5>

<p className="text-xs font-normal text-gray-700 mb-4">{description}</p>
<p className="text-xs font-normal text-gray-700 mb-4 whitespace-pre-line">
<T id={description} />
</p>

<div className="flex justify-between">
<FormSecondaryButton small className="flex-1 mr-4 h-9 rounded-md" type="button" onClick={onDisable}>
<span className="capitalize text-base text-center w-full">{t(disableButtonText)}</span>
<div className="flex flex-wrap gap-x-4 gap-y-2">
<FormSecondaryButton
small
className="flex-1 h-2.25 rounded-md"
onClick={disableButton.onClick}
testID={disableButton.testID}
testIDProperties={disableButton.testIDProperties}
>
<span className={clsx('text-base text-center w-full whitespace-pre', disableButton.capitalize && 'capitalize')}>
<T id={disableButton.title} />
</span>
</FormSecondaryButton>

<FormSubmitButton small className="flex-1 h-9 rounded-md" onClick={onEnable}>
<span className="capitalize text-base text-center w-full whitespace-nowrap">{t(enableButtonText)}</span>
<FormSubmitButton
small
className="flex-1 h-2.25 rounded-md"
onClick={enableButton.onClick}
testID={enableButton.testID}
testIDProperties={enableButton.testIDProperties}
>
<span className={clsx('text-base text-center w-full whitespace-pre', enableButton.capitalize && 'capitalize')}>
<T id={enableButton.title} />
</span>
</FormSubmitButton>
</div>
</div>
Expand Down
4 changes: 3 additions & 1 deletion src/app/pages/Home/OtherComponents/Assets.selectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,7 @@ export enum AssetsSelectors {
assetItemFiatBalanceButton = 'Assets/Asset Item Fiat Balance Button',
assetItemDelegateButton = 'Assets/Asset Item Delegate Button',
searchAssetsInputTokens = 'Assets/Search Assets Input (Tokens)',
searchAssetsInputCollectibles = 'Assets/Search Assets Input (Collectibles)'
searchAssetsInputCollectibles = 'Assets/Search Assets Input (Collectibles)',
acceptAdsBannerEnableButton = 'Assets (Accept Ads Banner)/Enable Button',
acceptAdsBannerDisableButton = 'Assets (Accept Ads Banner)/Disable Button'
}
118 changes: 118 additions & 0 deletions src/app/pages/Home/OtherComponents/Tokens/AcceptAdsBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import React, { FC, useCallback, useMemo } from 'react';

import { useDispatch } from 'react-redux';

import ABContainer from 'app/atoms/ABContainer';
import { Banner, BannerButtonProps } from 'app/atoms/Banner';
import { togglePartnersPromotionAction } from 'app/store/partners-promotion/actions';
import { setAdsBannerVisibilityAction } from 'app/store/settings/actions';
import { T } from 'lib/i18n';
import { EmojiInlineIcon } from 'lib/icons/emoji';

import { AssetsSelectors } from '../Assets.selectors';

export const AcceptAdsBanner: FC = () => {
const dispatch = useDispatch();

const onEnableButtonClick = useCallback(() => {
dispatch(togglePartnersPromotionAction(true));
dispatch(setAdsBannerVisibilityAction(false));
}, [dispatch]);

const onDisableButtonClick = useCallback(() => {
dispatch(togglePartnersPromotionAction(false));
dispatch(setAdsBannerVisibilityAction(false));
}, [dispatch]);

return (
<ABContainer
groupAComponent={
<AcceptAdsBannerForTestGroupA
onEnableButtonClick={onEnableButtonClick}
onDisableButtonClick={onDisableButtonClick}
/>
}
groupBComponent={
<AcceptAdsBannerForTestGroupB
onEnableButtonClick={onEnableButtonClick}
onDisableButtonClick={onDisableButtonClick}
/>
}
/>
);
};

interface Props {
onEnableButtonClick: EmptyFn;
onDisableButtonClick: EmptyFn;
}

const AcceptAdsBannerForTestGroupA: FC<Props> = ({ onEnableButtonClick, onDisableButtonClick }) => {
const enableButton: BannerButtonProps = useMemo(
() => ({
title: 'payMeForEveryAdISee',
testID: AssetsSelectors.acceptAdsBannerEnableButton,
testIDProperties: { buttonText: 'Pay me for every ad I see' },
onClick: onEnableButtonClick
}),
[onEnableButtonClick]
);

const disableButton: BannerButtonProps = useMemo(
() => ({
title: 'noThanksIhateFreeMoney',
testID: AssetsSelectors.acceptAdsBannerDisableButton,
testIDProperties: { buttonText: 'No thanks, I hate free money' },
onClick: onDisableButtonClick
}),
[onDisableButtonClick]
);

return (
<Banner
title={
<>
<T id="acceptAdsBannerTitle_A" />
<br />
<EmojiInlineIcon name="eyes-1f440" />
<EmojiInlineIcon name="money-bag-1f4b0" />
</>
}
description="acceptAdsBannerText_A"
enableButton={enableButton}
disableButton={disableButton}
/>
);
};

const AcceptAdsBannerForTestGroupB: FC<Props> = ({ onEnableButtonClick, onDisableButtonClick }) => {
const enableButton: BannerButtonProps = useMemo(
() => ({
title: 'enableAds',
testID: AssetsSelectors.acceptAdsBannerEnableButton,
testIDProperties: { buttonText: 'Enable Ads' },
onClick: onEnableButtonClick
}),
[onEnableButtonClick]
);

const disableButton: BannerButtonProps = useMemo(
() => ({
title: 'disable',
capitalize: true,
testID: AssetsSelectors.acceptAdsBannerDisableButton,
testIDProperties: { buttonText: 'Disable' },
onClick: onDisableButtonClick
}),
[onDisableButtonClick]
);

return (
<Banner
title={<T id="acceptAdsBannerTitle_B" />}
description="acceptAdsBannerText_B"
enableButton={enableButton}
disableButton={disableButton}
/>
);
};
39 changes: 8 additions & 31 deletions src/app/pages/Home/OtherComponents/Tokens/Tokens.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ import { useAppEnv } from 'app/env';
import { useBalancesWithDecimals } from 'app/hooks/use-balances-with-decimals.hook';
import { ReactComponent as AddToListIcon } from 'app/icons/add-to-list.svg';
import { ReactComponent as SearchIcon } from 'app/icons/search.svg';
import { loadPartnersPromoAction, togglePartnersPromotionAction } from 'app/store/partners-promotion/actions';
import { loadPartnersPromoAction } from 'app/store/partners-promotion/actions';
import { useShouldShowPartnersPromoSelector } from 'app/store/partners-promotion/selectors';
import { useIsEnabledAdsBannerSelector } from 'app/store/settings/selectors';
import SearchAssetField from 'app/templates/SearchAssetField';
import { OptimalPromoVariantEnum } from 'lib/apis/optimal';
import { TEMPLE_TOKEN_SLUG } from 'lib/assets';
Expand All @@ -20,11 +22,8 @@ import { useSyncTokens } from 'lib/temple/front/sync-tokens';
import { filterUnique } from 'lib/utils';
import { Link, navigate } from 'lib/woozie';

import { Banner } from '../../../../atoms/Banner';
import { useShouldShowPartnersPromoSelector } from '../../../../store/partners-promotion/selectors';
import { setAdsBannerVisibilityAction } from '../../../../store/settings/actions';
import { useIsEnabledAdsBannerSelector } from '../../../../store/settings/selectors';
import { AssetsSelectors } from '../Assets.selectors';
import { AcceptAdsBanner } from './AcceptAdsBanner';
import { ListItem } from './components/ListItem';
import { toExploreAssetLink } from './utils';

Expand Down Expand Up @@ -124,16 +123,6 @@ export const TokensTab: FC = () => {
return () => window.removeEventListener('keyup', handleKeyup);
}, [activeAssetSlug, setActiveIndex]);

const handleEnableBannerButton = async () => {
dispatch(togglePartnersPromotionAction(true));
dispatch(setAdsBannerVisibilityAction(false));
};

const handleDisableBannerButton = () => {
dispatch(togglePartnersPromotionAction(false));
dispatch(setAdsBannerVisibilityAction(false));
};

return (
<div className="w-full max-w-sm mx-auto">
<div className={classNames('mt-3', popup && 'mx-4')}>
Expand All @@ -149,14 +138,10 @@ export const TokensTab: FC = () => {
<Link
to="/manage-assets"
className={classNames(
'ml-2 flex-shrink-0',
'px-3 py-1',
'rounded overflow-hidden',
'flex items-center',
'text-gray-600 text-sm',
'flex-shrink-0 flex items-center px-3 py-1 ml-2',
'text-gray-600 text-sm rounded overflow-hidden',
'transition ease-in-out duration-200',
'hover:bg-gray-100',
'opacity-75 hover:opacity-100 focus:opacity-100'
'hover:bg-gray-100 opacity-75 hover:opacity-100 focus:opacity-100'
)}
testID={AssetsSelectors.manageButton}
>
Expand All @@ -166,15 +151,7 @@ export const TokensTab: FC = () => {
</div>
</div>

{isEnabledAdsBanner && (
<Banner
text="Earn by viewing ads in Temple Wallet"
description="Support the development team and earn tokens by viewing ads inside the wallet. To enable this feature, we request your permission to trace your Wallet Address and IP address. You can always disable ads in the settings."
enableButtonText="enableAds"
onDisable={handleDisableBannerButton}
onEnable={handleEnableBannerButton}
/>
)}
{isEnabledAdsBanner && <AcceptAdsBanner />}

{filteredAssets.length === 0 ? (
<div className="my-8 flex flex-col items-center justify-center text-gray-500">
Expand Down
Binary file added src/lib/icons/emoji/eyes-1f440.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
20 changes: 20 additions & 0 deletions src/lib/icons/emoji/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React, { FC, useMemo } from 'react';

import Icon_Eyes_1f440 from './eyes-1f440.png';
import Icon_MoneyBag_1f4b0 from './money-bag-1f4b0.png';

interface Props extends React.ImgHTMLAttributes<HTMLImageElement> {
name: keyof typeof KnownEmojies;
}

export const EmojiInlineIcon: FC<Props> = ({ name, style, ...props }) => {
const styleMemo = useMemo(() => ({ display: 'inline-block', height: '1em', ...style }), [style]);

// eslint-disable-next-line jsx-a11y/alt-text
return <img {...props} src={KnownEmojies[name]} style={styleMemo} />;
};

const KnownEmojies = {
'eyes-1f440': Icon_Eyes_1f440,
'money-bag-1f4b0': Icon_MoneyBag_1f4b0
};
Binary file added src/lib/icons/emoji/money-bag-1f4b0.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading