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-1599 Rewards page #1239

Merged
merged 7 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions public/_locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -3168,5 +3168,44 @@
},
"dropdownNoItems": {
"message": "No items"
},
"earnings": {
"message": "Earnings"
},
"earningsInfoTooltip": {
"message": "Earnings are only an estimation of your daily and monthly activity, including ad views and interactions with referral links. At the end of each month, your RP (reward points) will be converted into TKEY and distributed to your Tezos address. The more RP you accumulate, the greater your rewards will be."
},
"ads": {
"message": "Ads"
},
"today": {
"message": "Today"
},
"activeFeatures": {
"message": "Active features"
},
"advertisingFeatureDescription": {
"message": "Get RP by viewing ads"
},
"advertisingFeatureTooltip": {
"message": "The advertising feature is used to display ads within Temple Wallet and on websites. To earn TKEY and enable ads, we request your permission to share your wallet address and IP, this information is used solely to serve you relevant ads and distribute rewards."
},
"referralLinksFeatureDescription": {
"message": "Get RP while use favorite websites"
},
"referralLinksFeatureTooltip": {
"message": "The referral links feature allows users to engage with affiliate offers from our partners. These offers will appear as links while you browse your favorite websites. To enable this feature, we request your permission to share your wallet address and IP address, this information is used solely to serve you relevant offers and distribute rewards."
},
"achievements": {
"message": "Achievements"
},
"lifetimeEarnings": {
"message": "Lifetime earnings"
},
"noEarningsFound": {
"message": "No earnings found"
},
"somethingWentWrong": {
"message": "Something went wrong"
}
}
2 changes: 2 additions & 0 deletions src/app/PageRouter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import { Notifications, NotificationsItem } from 'lib/notifications/components';
import { useTempleClient } from 'lib/temple/front';
import * as Woozie from 'lib/woozie';

import { RewardsPage } from './pages/Rewards';
import { StakingPage } from './pages/Staking';
import { WithDataLoading } from './WithDataLoading';

Expand Down Expand Up @@ -98,6 +99,7 @@ const ROUTE_MAP = Woozie.createMap<RouteContext>([
['/attention', onlyReady(onlyInFullPage(() => <AttentionPage />))],
['/notifications', onlyReady(() => <Notifications />)],
['/notifications/:id', onlyReady(({ id }) => <NotificationsItem id={Number(id) ?? 0} />)],
['/rewards', onlyReady(() => <RewardsPage />)],
['*', () => <Woozie.Redirect to="/" />]
]);

Expand Down
28 changes: 0 additions & 28 deletions src/app/atoms/DonationBanner/DonationBanner.tsx

This file was deleted.

3 changes: 0 additions & 3 deletions src/app/atoms/DonationBanner/selectors.ts

This file was deleted.

47 changes: 47 additions & 0 deletions src/app/hooks/use-partners-promotion-settings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { ChangeEvent } from 'react';

import { useDispatch } from 'react-redux';

import { togglePartnersPromotionAction } from 'app/store/partners-promotion/actions';
import { useShouldShowPartnersPromoSelector } from 'app/store/partners-promotion/selectors';
import { t } from 'lib/i18n';
import { useConfirm } from 'lib/ui/dialog';

export const usePartnersPromotionSettings = () => {
const dispatch = useDispatch();
const confirm = useConfirm();

const isEnabled = useShouldShowPartnersPromoSelector();

const handleHidePromotion = async () => {
const confirmed = await confirm({
title: t('closePartnersPromotion'),
children: t('closePartnersPromoConfirm'),
comfirmButtonText: t('disable')
});

if (confirmed) {
dispatch(togglePartnersPromotionAction(false));
}
};

const handleShowPromotion = async () => {
const confirmed = await confirm({
title: t('enablePartnersPromotionConfirm'),
children: t('enablePartnersPromotionDescriptionConfirm'),
comfirmButtonText: t('enable')
});

if (confirmed) {
dispatch(togglePartnersPromotionAction(true));
}
};

const setEnabled = (toChecked: boolean, event?: ChangeEvent<HTMLInputElement>) => {
event?.preventDefault();

return toChecked ? handleShowPromotion() : handleHidePromotion();
};

return { isEnabled, setEnabled };
};
67 changes: 67 additions & 0 deletions src/app/hooks/use-referral-links-settings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React, { ChangeEvent, useCallback } from 'react';

import { useDispatch } from 'react-redux';

import { setAcceptedTermsVersionAction, setReferralLinksEnabledAction } from 'app/store/settings/actions';
import { useAcceptedTermsVersionSelector, useReferralLinksEnabledSelector } from 'app/store/settings/selectors';
import {
PRIVACY_POLICY_URL,
RECENT_TERMS_VERSION,
TERMS_OF_USE_URL,
TERMS_WITH_REFERRALS_VERSION
} from 'lib/constants';
import { t, T } from 'lib/i18n';
import { useConfirm } from 'lib/ui/dialog';

export const useReferralLinksSettings = () => {
const dispatch = useDispatch();
const enabled = useReferralLinksEnabledSelector();
const acceptedTermsVersion = useAcceptedTermsVersionSelector();
const confirm = useConfirm();

const setEnabled = useCallback(
async (toChecked: boolean, event?: ChangeEvent<HTMLInputElement>) => {
event?.preventDefault();

if (toChecked && acceptedTermsVersion < TERMS_WITH_REFERRALS_VERSION) {
const confirmed = await confirm({
title: <T id="confirmEnableReferralLinksTitle" />,
description: (
<T
id="confirmEnableReferralLinksDescription"
substitutions={[
<a
href={TERMS_OF_USE_URL}
target="_blank"
rel="noopener noreferrer"
className="underline text-secondary"
>
<T id="termsOfUsage" key="termsLink" />
</a>,
<a
href={PRIVACY_POLICY_URL}
target="_blank"
rel="noopener noreferrer"
className="underline text-secondary"
>
<T id="privacyPolicy" key="privacyPolicyLink" />
</a>
]}
/>
),
comfirmButtonText: t('agreeAndContinue')
});

if (!confirmed) {
return;
}
}

dispatch(setAcceptedTermsVersionAction(RECENT_TERMS_VERSION));
dispatch(setReferralLinksEnabledAction(toChecked));
},
[acceptedTermsVersion, confirm, dispatch]
);

return { isEnabled: enabled, setEnabled };
};
4 changes: 4 additions & 0 deletions src/app/icons/triangle-down.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion src/app/layouts/PageLayout.selectors.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export enum PageLayoutSelectors {
backButton = 'Page Layout/Back Button',
skipButton = 'Page Layout/Skip Button'
skipButton = 'Page Layout/Skip Button',
rewardsButton = 'Page Layout/Rewards Button'
}
25 changes: 8 additions & 17 deletions src/app/layouts/PageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import clsx from 'clsx';

import DocBg from 'app/a11y/DocBg';
import { Button } from 'app/atoms/Button';
import { DonationBanner } from 'app/atoms/DonationBanner/DonationBanner';
import Spinner from 'app/atoms/Spinner/Spinner';
import { useAppEnv } from 'app/env';
import ErrorBoundary from 'app/ErrorBoundary';
Expand All @@ -35,6 +34,7 @@ import Header from './PageLayout/Header';
import { NewsletterOverlay } from './PageLayout/NewsletterOverlay/NewsletterOverlay';
import { OnRampOverlay } from './PageLayout/OnRampOverlay/OnRampOverlay';
import { ReactivateAdsOverlay } from './PageLayout/ReactivateAdsOverlay';
import { RewardsButton } from './PageLayout/RewardsButton';
import { ShortcutAccountSwitchOverlay } from './PageLayout/ShortcutAccountSwitchOverlay';
import { PageLayoutSelectors } from './PageLayout.selectors';

Expand Down Expand Up @@ -116,7 +116,6 @@ type ToolbarProps = {
hasBackAction?: boolean;
step?: number;
setStep?: (step: number) => void;
adShow?: boolean;
skip?: boolean;
attention?: boolean;
};
Expand All @@ -126,15 +125,7 @@ export let ToolbarElement: HTMLDivElement | null = null;
/** Defined for reference in code to highlight relation between multiple sticky elements & their sizes */
export const TOOLBAR_IS_STICKY = true;

const Toolbar: FC<ToolbarProps> = ({
pageTitle,
hasBackAction = true,
step,
setStep,
adShow = false,
skip,
attention
}) => {
const Toolbar: FC<ToolbarProps> = ({ pageTitle, hasBackAction = true, step, setStep, skip, attention }) => {
const { historyPosition, pathname } = useLocation();
const { fullPage } = useAppEnv();
const { setOnboardingCompleted } = useOnboardingProgress();
Expand Down Expand Up @@ -203,10 +194,8 @@ const Toolbar: FC<ToolbarProps> = ({

return (
<div ref={updateRootRef} className={className}>
<div className="flex-1">
{!isBackButtonAvailable && adShow && <DonationBanner />}

{isBackButtonAvailable && (
{isBackButtonAvailable ? (
<div className="flex-1">
<Button
className={clsx(
'rounded px-2 py-1',
Expand All @@ -223,8 +212,10 @@ const Toolbar: FC<ToolbarProps> = ({
<ChevronLeftIcon className="-ml-2 h-5 w-auto stroke-current stroke-2" />
<T id="back" />
</Button>
)}
</div>
</div>
) : (
<RewardsButton testID={PageLayoutSelectors.rewardsButton} />
)}

{pageTitle && (
<h2 className="px-1 flex items-center text-ulg text-gray-700 font-normal overflow-hidden">{pageTitle}</h2>
Expand Down
67 changes: 67 additions & 0 deletions src/app/layouts/PageLayout/RewardsButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import React, { memo, useCallback, useRef, useState } from 'react';

import clsx from 'clsx';

import { TestIDProps } from 'lib/analytics';
import { T } from 'lib/i18n';
import { useWillUnmount } from 'lib/ui/hooks/useWillUnmount';
import { Link } from 'lib/woozie';

import starAnimation from './star_animation_small.gif';

const GIF_DURATION = 3000;
const ORIGINAL_IMG_SIZE = 48;

export const RewardsButton = memo<TestIDProps>(props => {
const [isHovered, setIsHovered] = useState(false);
const [loopComplete, setLoopComplete] = useState(false);
const loopTimeoutRef = useRef<NodeJS.Timeout>();
const animationRef = useRef<HTMLImageElement>(null);
const canvasRef = useRef<HTMLCanvasElement>(null);

useWillUnmount(() => void (loopTimeoutRef.current !== undefined && clearTimeout(loopTimeoutRef.current)));
const handleAnimationLoad = useCallback(() => {
loopTimeoutRef.current = setTimeout(() => setLoopComplete(true), GIF_DURATION);

const ctx = canvasRef.current!.getContext('2d')!;
ctx.imageSmoothingEnabled = false;
ctx.drawImage(animationRef.current!, 0, 0, ORIGINAL_IMG_SIZE, ORIGINAL_IMG_SIZE);
}, []);

const handleHover = useCallback(() => setIsHovered(true), []);
const handleUnhover = useCallback(() => setIsHovered(false), []);

const isAnimated = isHovered || !loopComplete;

return (
<Link
to="/rewards"
className="bg-blue-150 text-blue-650 rounded-lg px-2 py-1 text-sm font-semibold leading-tight capitalize"
onMouseEnter={handleHover}
onMouseLeave={handleUnhover}
{...props}
>
<div className="flex items-center gap-1 relative">
<img
src={starAnimation}
alt=""
className={clsx('w-4 h-4', !isAnimated && 'invisible')}
onLoad={handleAnimationLoad}
ref={animationRef}
/>
<canvas
width={ORIGINAL_IMG_SIZE}
height={ORIGINAL_IMG_SIZE}
aria-hidden
role="presentation"
className={clsx(
isAnimated && 'hidden',
'absolute top-1/2 transform scale-1/3 -translate-y-1/2 -translate-x-1/3 left-0'
)}
ref={canvasRef}
/>
<T id="rewards" />
</div>
</Link>
);
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion src/app/pages/Home/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@ const Home = memo<Props>(({ assetSlug }) => {
) : null
}
attention={true}
adShow
>
{fullPage && (
<div className="w-full max-w-sm mx-auto">
Expand Down
24 changes: 24 additions & 0 deletions src/app/pages/Rewards/achievements/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React, { memo } from 'react';

import clsx from 'clsx';

import { T, t } from 'lib/i18n';

import { Section } from '../section';

export const Achievements = memo(() => (
<Section title={t('achievements')}>
<div
className={clsx(
'bg-gray-100 rounded-2xl flex flex-col justify-center items-center text-center gap-1',
'text-sm leading-tight text-gray-500 font-medium'
)}
style={{ height: '8.625rem' }}
>
<p>🚧</p>
<p>
<T id="comingSoon" />
</p>
</div>
</Section>
));
3 changes: 3 additions & 0 deletions src/app/pages/Rewards/active-features/ads.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/app/pages/Rewards/active-features/disabled.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading