From 73a0de2610872455a5a7ee315b2d24d3b5587f34 Mon Sep 17 00:00:00 2001 From: Inokentii Mazhara Date: Mon, 23 Dec 2024 18:03:38 +0200 Subject: [PATCH 1/3] TW-1615: Make limits for dexes amount in a swap stricter (#1240) --- src/app/templates/SwapForm/SwapForm.tsx | 26 ++++++++++++------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/app/templates/SwapForm/SwapForm.tsx b/src/app/templates/SwapForm/SwapForm.tsx index 474d26a69..de8cd6a7e 100644 --- a/src/app/templates/SwapForm/SwapForm.tsx +++ b/src/app/templates/SwapForm/SwapForm.tsx @@ -58,10 +58,10 @@ import { slippageToleranceInputValidationFn } from './SwapFormInput/SlippageTole import { SwapFormInput } from './SwapFormInput/SwapFormInput'; import { SwapMinimumReceived } from './SwapMinimumReceived/SwapMinimumReceived'; -// These values have been set after some experimentation. They are different to the respective values in -// templewallet-mobile because the mobile app still uses taquito v19.0.0, which has a different gas estimation algorithm. -const SINGLE_SWAP_IN_BATCH_MAX_DEXES = 12; -const LB_OPERATION_DEXES_COST = 3; +const CASHBACK_SWAP_MAX_DEXES = 3; +// Actually, at most 2 dexes for each of underlying SIRS -> tzBTC -> X swap and SIRS -> XTZ -> X swap +const MAIN_SIRS_SWAP_MAX_DEXES = 4; +const MAIN_NON_SIRS_SWAP_MAX_DEXES = 3; export const SwapForm: FC = () => { const dispatch = useDispatch(); @@ -134,17 +134,13 @@ export const SwapForm: FC = () => { const isOutputTokenTempleToken = outputAssetSlug === KNOWN_TOKENS_SLUGS.TEMPLE; const isSirsSwap = inputAssetSlug === KNOWN_TOKENS_SLUGS.SIRS || outputAssetSlug === KNOWN_TOKENS_SLUGS.SIRS; const isSwapAmountMoreThreshold = inputAmountInUsd.isGreaterThanOrEqualTo(SWAP_THRESHOLD_TO_GET_CASHBACK); - const totalMaxDexes = SINGLE_SWAP_IN_BATCH_MAX_DEXES - (isSirsSwap ? LB_OPERATION_DEXES_COST : 0); - const cashbackSwapMaxDexes = Math.ceil(totalMaxDexes / (isSirsSwap ? 3 : 2)); - const mainSwapMaxDexes = - totalMaxDexes - (isSwapAmountMoreThreshold && !isInputTokenTempleToken ? cashbackSwapMaxDexes : 0); + const mainSwapMaxDexes = isSirsSwap ? MAIN_SIRS_SWAP_MAX_DEXES : MAIN_NON_SIRS_SWAP_MAX_DEXES; return { isInputTokenTempleToken, isOutputTokenTempleToken, isSwapAmountMoreThreshold, - mainSwapMaxDexes, - cashbackSwapMaxDexes + mainSwapMaxDexes }; }, [allUsdToTokenRates] @@ -321,8 +317,10 @@ export const SwapForm: FC = () => { return; } - const { isInputTokenTempleToken, isOutputTokenTempleToken, isSwapAmountMoreThreshold, cashbackSwapMaxDexes } = - getSwapWithFeeParams(inputValue, outputValue); + const { isInputTokenTempleToken, isOutputTokenTempleToken, isSwapAmountMoreThreshold } = getSwapWithFeeParams( + inputValue, + outputValue + ); if (isInputTokenTempleToken && isSwapAmountMoreThreshold) { const routingInputFeeOpParams = await getRoutingFeeTransferParams( @@ -348,7 +346,7 @@ export const SwapForm: FC = () => { toSymbol: TEMPLE_TOKEN.symbol, toTokenDecimals: TEMPLE_TOKEN.decimals, amount: atomsToTokens(routingFeeFromInputAtomic, fromRoute3Token.decimals).toFixed(), - dexesLimit: cashbackSwapMaxDexes, + dexesLimit: CASHBACK_SWAP_MAX_DEXES, rpcUrl: tezos.rpc.getRpcUrl() }); @@ -393,7 +391,7 @@ export const SwapForm: FC = () => { toSymbol: TEMPLE_TOKEN.symbol, toTokenDecimals: TEMPLE_TOKEN.decimals, amount: atomsToTokens(routingFeeFromOutputAtomic, toRoute3Token.decimals).toFixed(), - dexesLimit: cashbackSwapMaxDexes, + dexesLimit: CASHBACK_SWAP_MAX_DEXES, rpcUrl: tezos.rpc.getRpcUrl() }); From 7298c6e63822a629069ed94bc39a38cbd24358fe Mon Sep 17 00:00:00 2001 From: Inokentii Mazhara Date: Mon, 23 Dec 2024 18:04:01 +0200 Subject: [PATCH 2/3] TW-1599: Rewards page (#1239) * 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 --- public/_locales/en/messages.json | 39 +++++ src/app/PageRouter.tsx | 2 + .../atoms/DonationBanner/DonationBanner.tsx | 28 ---- src/app/atoms/DonationBanner/selectors.ts | 3 - .../hooks/use-partners-promotion-settings.tsx | 47 ++++++ src/app/hooks/use-referral-links-settings.tsx | 67 +++++++++ src/app/icons/triangle-down.svg | 4 + src/app/layouts/PageLayout.selectors.ts | 3 +- src/app/layouts/PageLayout.tsx | 25 +--- .../RewardsButton/firefox-star-animation.tsx | 39 +++++ .../PageLayout/RewardsButton/index.tsx | 34 +++++ .../RewardsButton/star-animation.tsx | 65 +++++++++ .../RewardsButton/star_animation.webm | Bin 0 -> 224818 bytes .../RewardsButton/star_animation_poster.gif | Bin 0 -> 216 bytes .../RewardsButton/star_animation_small.gif | Bin 0 -> 6885 bytes src/app/pages/Home/Home.tsx | 1 - src/app/pages/Rewards/achievements/index.tsx | 24 +++ src/app/pages/Rewards/active-features/ads.svg | 3 + .../Rewards/active-features/disabled.svg | 3 + .../pages/Rewards/active-features/enabled.svg | 3 + .../Rewards/active-features/feature-item.tsx | 76 ++++++++++ .../pages/Rewards/active-features/icon_bg.svg | 3 + .../pages/Rewards/active-features/index.tsx | 41 ++++++ .../Rewards/active-features/referrals.svg | 3 + src/app/pages/Rewards/index.tsx | 92 ++++++++++++ .../pages/Rewards/lifetime-earnings/index.tsx | 138 ++++++++++++++++++ .../pages/Rewards/recent-earnings/index.tsx | 54 +++++++ .../recent-earnings.module.css | 9 ++ .../Rewards/recent-earnings/stats-card.tsx | 70 +++++++++ src/app/pages/Rewards/section.tsx | 32 ++++ src/app/pages/Rewards/selectors.ts | 8 + src/app/pages/Rewards/tooltip.tsx | 33 +++++ src/app/pages/Rewards/utils.ts | 7 + src/app/store/rewards/actions.ts | 22 +++ src/app/store/rewards/epics.ts | 44 ++++++ src/app/store/rewards/reducers.ts | 35 +++++ src/app/store/rewards/selectors.ts | 47 ++++++ src/app/store/rewards/state.mock.ts | 7 + src/app/store/rewards/state.ts | 14 ++ src/app/store/root-state.epics.ts | 4 +- src/app/store/root-state.mock.ts | 4 +- src/app/store/root-state.reducer.ts | 4 +- .../partners-promotion-settings.tsx | 49 +------ .../referral-links-settings.tsx | 68 +-------- src/lib/apis/ads-api.ts | 58 ++++++-- src/lib/apis/utils.ts | 9 ++ src/lib/icons/assets/bell-read.svg | 16 ++ src/lib/icons/assets/bell-unread.svg | 20 +++ src/lib/icons/index.ts | 2 + src/lib/notifications/components/bell.tsx | 20 +-- src/lib/utils/numbers.ts | 2 + tailwind.config.js | 4 + 52 files changed, 1199 insertions(+), 186 deletions(-) delete mode 100644 src/app/atoms/DonationBanner/DonationBanner.tsx delete mode 100644 src/app/atoms/DonationBanner/selectors.ts create mode 100644 src/app/hooks/use-partners-promotion-settings.tsx create mode 100644 src/app/hooks/use-referral-links-settings.tsx create mode 100644 src/app/icons/triangle-down.svg create mode 100644 src/app/layouts/PageLayout/RewardsButton/firefox-star-animation.tsx create mode 100644 src/app/layouts/PageLayout/RewardsButton/index.tsx create mode 100644 src/app/layouts/PageLayout/RewardsButton/star-animation.tsx create mode 100644 src/app/layouts/PageLayout/RewardsButton/star_animation.webm create mode 100644 src/app/layouts/PageLayout/RewardsButton/star_animation_poster.gif create mode 100644 src/app/layouts/PageLayout/RewardsButton/star_animation_small.gif create mode 100644 src/app/pages/Rewards/achievements/index.tsx create mode 100644 src/app/pages/Rewards/active-features/ads.svg create mode 100644 src/app/pages/Rewards/active-features/disabled.svg create mode 100644 src/app/pages/Rewards/active-features/enabled.svg create mode 100644 src/app/pages/Rewards/active-features/feature-item.tsx create mode 100644 src/app/pages/Rewards/active-features/icon_bg.svg create mode 100644 src/app/pages/Rewards/active-features/index.tsx create mode 100644 src/app/pages/Rewards/active-features/referrals.svg create mode 100644 src/app/pages/Rewards/index.tsx create mode 100644 src/app/pages/Rewards/lifetime-earnings/index.tsx create mode 100644 src/app/pages/Rewards/recent-earnings/index.tsx create mode 100644 src/app/pages/Rewards/recent-earnings/recent-earnings.module.css create mode 100644 src/app/pages/Rewards/recent-earnings/stats-card.tsx create mode 100644 src/app/pages/Rewards/section.tsx create mode 100644 src/app/pages/Rewards/selectors.ts create mode 100644 src/app/pages/Rewards/tooltip.tsx create mode 100644 src/app/pages/Rewards/utils.ts create mode 100644 src/app/store/rewards/actions.ts create mode 100644 src/app/store/rewards/epics.ts create mode 100644 src/app/store/rewards/reducers.ts create mode 100644 src/app/store/rewards/selectors.ts create mode 100644 src/app/store/rewards/state.mock.ts create mode 100644 src/app/store/rewards/state.ts create mode 100644 src/lib/apis/utils.ts create mode 100644 src/lib/icons/assets/bell-read.svg create mode 100644 src/lib/icons/assets/bell-unread.svg diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json index 222063a91..fea6aff84 100644 --- a/public/_locales/en/messages.json +++ b/public/_locales/en/messages.json @@ -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" } } diff --git a/src/app/PageRouter.tsx b/src/app/PageRouter.tsx index 3097171b5..69b6661f5 100644 --- a/src/app/PageRouter.tsx +++ b/src/app/PageRouter.tsx @@ -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'; @@ -98,6 +99,7 @@ const ROUTE_MAP = Woozie.createMap([ ['/attention', onlyReady(onlyInFullPage(() => ))], ['/notifications', onlyReady(() => )], ['/notifications/:id', onlyReady(({ id }) => )], + ['/rewards', onlyReady(() => )], ['*', () => ] ]); diff --git a/src/app/atoms/DonationBanner/DonationBanner.tsx b/src/app/atoms/DonationBanner/DonationBanner.tsx deleted file mode 100644 index a3310101f..000000000 --- a/src/app/atoms/DonationBanner/DonationBanner.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import React, { FC } from 'react'; - -import { Anchor } from 'app/atoms/Anchor'; -import { ReactComponent as Ukraine } from 'app/icons/ukraine.svg'; -import { T } from 'lib/i18n'; - -import { DonationBannerSelectors } from './selectors'; - -const DONATE_MAD_FISH_URL = 'https://donate.mad.fish'; - -export const DonationBanner: FC = () => ( - -
-
- - - -
-
- -
-
-
-); diff --git a/src/app/atoms/DonationBanner/selectors.ts b/src/app/atoms/DonationBanner/selectors.ts deleted file mode 100644 index cc6fca5c8..000000000 --- a/src/app/atoms/DonationBanner/selectors.ts +++ /dev/null @@ -1,3 +0,0 @@ -export enum DonationBannerSelectors { - ukraineDonationBanner = 'DonationBanner/Ukraine Donation Banner' -} diff --git a/src/app/hooks/use-partners-promotion-settings.tsx b/src/app/hooks/use-partners-promotion-settings.tsx new file mode 100644 index 000000000..7ad32e9b0 --- /dev/null +++ b/src/app/hooks/use-partners-promotion-settings.tsx @@ -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) => { + event?.preventDefault(); + + return toChecked ? handleShowPromotion() : handleHidePromotion(); + }; + + return { isEnabled, setEnabled }; +}; diff --git a/src/app/hooks/use-referral-links-settings.tsx b/src/app/hooks/use-referral-links-settings.tsx new file mode 100644 index 000000000..309982e46 --- /dev/null +++ b/src/app/hooks/use-referral-links-settings.tsx @@ -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) => { + event?.preventDefault(); + + if (toChecked && acceptedTermsVersion < TERMS_WITH_REFERRALS_VERSION) { + const confirmed = await confirm({ + title: , + description: ( + + + , + + + + ]} + /> + ), + comfirmButtonText: t('agreeAndContinue') + }); + + if (!confirmed) { + return; + } + } + + dispatch(setAcceptedTermsVersionAction(RECENT_TERMS_VERSION)); + dispatch(setReferralLinksEnabledAction(toChecked)); + }, + [acceptedTermsVersion, confirm, dispatch] + ); + + return { isEnabled: enabled, setEnabled }; +}; diff --git a/src/app/icons/triangle-down.svg b/src/app/icons/triangle-down.svg new file mode 100644 index 000000000..cff0a93f4 --- /dev/null +++ b/src/app/icons/triangle-down.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/app/layouts/PageLayout.selectors.ts b/src/app/layouts/PageLayout.selectors.ts index 17e613c8e..60e409f64 100644 --- a/src/app/layouts/PageLayout.selectors.ts +++ b/src/app/layouts/PageLayout.selectors.ts @@ -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' } diff --git a/src/app/layouts/PageLayout.tsx b/src/app/layouts/PageLayout.tsx index 0ecf2594a..c3e81736d 100644 --- a/src/app/layouts/PageLayout.tsx +++ b/src/app/layouts/PageLayout.tsx @@ -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'; @@ -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'; @@ -116,7 +116,6 @@ type ToolbarProps = { hasBackAction?: boolean; step?: number; setStep?: (step: number) => void; - adShow?: boolean; skip?: boolean; attention?: boolean; }; @@ -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 = ({ - pageTitle, - hasBackAction = true, - step, - setStep, - adShow = false, - skip, - attention -}) => { +const Toolbar: FC = ({ pageTitle, hasBackAction = true, step, setStep, skip, attention }) => { const { historyPosition, pathname } = useLocation(); const { fullPage } = useAppEnv(); const { setOnboardingCompleted } = useOnboardingProgress(); @@ -203,10 +194,8 @@ const Toolbar: FC = ({ return (
-
- {!isBackButtonAvailable && adShow && } - - {isBackButtonAvailable && ( + {isBackButtonAvailable ? ( +
- )} -
+
+ ) : ( + + )} {pageTitle && (

{pageTitle}

diff --git a/src/app/layouts/PageLayout/RewardsButton/firefox-star-animation.tsx b/src/app/layouts/PageLayout/RewardsButton/firefox-star-animation.tsx new file mode 100644 index 000000000..40a6e403c --- /dev/null +++ b/src/app/layouts/PageLayout/RewardsButton/firefox-star-animation.tsx @@ -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(({ loop }) => { + const videoRef = useRef(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 ( +