Skip to content

Commit

Permalink
TW-1599: Rewards page (#1239)
Browse files Browse the repository at this point in the history
* TW-1599 Add layout for 'Rewards' page

* TW-1599 Implement fetching rewards

* TW-1599 Remove an unused property

* TW-1599 Refactoring according to comments

* TW-1599 Implement star animation as a webm video

* TW-1599 Fix star animation for Chrome
  • Loading branch information
keshan3262 authored Dec 23, 2024
1 parent 73a0de2 commit 7298c6e
Show file tree
Hide file tree
Showing 52 changed files with 1,199 additions and 186 deletions.
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { memo, useEffect, useRef } from 'react';

const starAnimationVideo = require('./star_animation.webm');
const starAnimationPoster = require('./star_animation_poster.gif');

interface Props {
loop: boolean;
}

export const FirefoxStarAnimation = memo<Props>(({ loop }) => {
const videoRef = useRef<HTMLVideoElement>(null);
const prevLoopRef = useRef(loop);

useEffect(() => {
if (loop && !prevLoopRef.current) {
videoRef.current!.play();
} else if (!loop && prevLoopRef.current) {
videoRef.current!.currentTime = 0;
videoRef.current!.pause();
}
prevLoopRef.current = loop;
}, [loop]);

return (
<video
className="w-4 h-4"
src={starAnimationVideo}
autoPlay
muted
loop={loop}
ref={videoRef}
poster={starAnimationPoster}
controls={false}
playsInline
disablePictureInPicture
disableRemotePlayback
/>
);
});
34 changes: 34 additions & 0 deletions src/app/layouts/PageLayout/RewardsButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React, { memo, useCallback, useState } from 'react';

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

import { FirefoxStarAnimation } from './firefox-star-animation';
import { StarAnimation } from './star-animation';

export const RewardsButton = memo<TestIDProps>(props => {
const [isHovered, setIsHovered] = useState(false);

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

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">
{process.env.TARGET_BROWSER === 'firefox' ? (
<FirefoxStarAnimation loop={isHovered} />
) : (
<StarAnimation loop={isHovered} />
)}
<T id="rewards" />
</div>
</Link>
);
});
Loading

0 comments on commit 7298c6e

Please sign in to comment.