From c95dd295e620ca7a9840588e09a66ab83aa8abef Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Tue, 13 Aug 2024 18:24:23 +0200 Subject: [PATCH 01/61] Implement `AcrePointsCard` component Added `Countdown` component, adjusted `Button` styles and mocked `useAcrePoints` hook --- dapp/src/components/shared/Countdown.tsx | 42 +++++++++++ dapp/src/hooks/index.ts | 1 + dapp/src/hooks/useAcrePoints.ts | 46 ++++++++++++ .../pages/DashboardPage/AcrePointsCard.tsx | 70 +++++++++++-------- dapp/src/theme/Button.ts | 41 +++++++---- dapp/src/theme/Countdown.ts | 52 ++++++++++++++ dapp/src/theme/index.ts | 2 + 7 files changed, 211 insertions(+), 43 deletions(-) create mode 100644 dapp/src/components/shared/Countdown.tsx create mode 100644 dapp/src/hooks/useAcrePoints.ts create mode 100644 dapp/src/theme/Countdown.ts diff --git a/dapp/src/components/shared/Countdown.tsx b/dapp/src/components/shared/Countdown.tsx new file mode 100644 index 000000000..667e0e7fe --- /dev/null +++ b/dapp/src/components/shared/Countdown.tsx @@ -0,0 +1,42 @@ +import React from "react" +import { BoxProps, Box, useMultiStyleConfig } from "@chakra-ui/react" +import { useCountdown } from "#/hooks" +import { TimeUnits } from "#/types" + +type CountdownProps = { + timestamp: number + addLeadingZeros?: boolean + units?: (keyof TimeUnits)[] + size?: "xs" | "sm" | "md" | "lg" | "xl" | "2xl" +} & BoxProps + +function Countdown(props: CountdownProps) { + const { + timestamp, + addLeadingZeros = true, + units = ["hours", "minutes", "seconds"], + size = "md", + ...restProps + } = props + + const styles = useMultiStyleConfig("Countdown", { size }) + + const timeUnits = useCountdown(timestamp, addLeadingZeros) + + return ( + + {units.map((unit, index) => ( + + {timeUnits[unit]} + {index < units.length - 1 && ( + + : + + )} + + ))} + + ) +} + +export default Countdown diff --git a/dapp/src/hooks/index.ts b/dapp/src/hooks/index.ts index b055549d0..c41ee9cfd 100644 --- a/dapp/src/hooks/index.ts +++ b/dapp/src/hooks/index.ts @@ -34,3 +34,4 @@ export { default as useLocalStorage } from "./useLocalStorage" export { default as useDetectReferral } from "./useDetectReferral" export { default as useReferral } from "./useReferral" export { default as useMats } from "./useMats" +export { default as useAcrePoints } from "./useAcrePoints" diff --git a/dapp/src/hooks/useAcrePoints.ts b/dapp/src/hooks/useAcrePoints.ts new file mode 100644 index 000000000..de651696d --- /dev/null +++ b/dapp/src/hooks/useAcrePoints.ts @@ -0,0 +1,46 @@ +import { numberToLocaleString } from "#/utils" + +const FORMATTED_DECIMALS = 0 +const getFormattedAmount = (amount: number) => + numberToLocaleString(amount, FORMATTED_DECIMALS) + +type UseAcrePointsReturnType = { + data: { + totalPointsAmount: number + dailyPointsAmount: number + claimablePointsAmount: number + nextDropTimestamp: number + } + formatted: { + totalPointsAmount: string + dailyPointsAmount: string + claimablePointsAmount: string + } +} + +export default function useAcrePoints(): UseAcrePointsReturnType { + const totalPointsAmount = 2749993 // TODO: Fetch from the API + const dailyPointsAmount = 1201 // TODO: Fetch from the API + const claimablePointsAmount = 2402 // TODO: Fetch from the API + const nextDropTimestamp = 1634160000000 // TODO: Fetch from the API + + const formattedTotalPointsAmount = getFormattedAmount(totalPointsAmount) + const formattedDailyPointsAmount = getFormattedAmount(dailyPointsAmount) + const formattedClaimablePointsAmount = getFormattedAmount( + claimablePointsAmount, + ) + + return { + data: { + totalPointsAmount, + dailyPointsAmount, + claimablePointsAmount, + nextDropTimestamp, + }, + formatted: { + totalPointsAmount: formattedTotalPointsAmount, + dailyPointsAmount: formattedDailyPointsAmount, + claimablePointsAmount: formattedClaimablePointsAmount, + }, + } +} diff --git a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx index edb57de64..436ea9a2e 100644 --- a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx +++ b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx @@ -1,56 +1,68 @@ import React from "react" -import { TextLg, TextMd, TextSm } from "#/components/shared/Typography" +import { H4, TextMd } from "#/components/shared/Typography" import { + Box, + Button, Card, CardBody, CardHeader, CardProps, - Tag, - TagLeftIcon, VStack, } from "@chakra-ui/react" -import acrePointsCardPlaceholderSrc from "#/assets/images/acre-points-card-placeholder.png" import UserDataSkeleton from "#/components/shared/UserDataSkeleton" -import { IconPlayerTrackNextFilled } from "@tabler/icons-react" +import Countdown from "#/components/shared/Countdown" +import useAcrePoints from "#/hooks/useAcrePoints" export default function AcrePointsCard(props: CardProps) { + const { data, formatted } = useAcrePoints() + return ( - - - Acre points + + + Acre points balance - - - - + {formatted.totalPointsAmount} + +  PTS + + + + + {formatted.dailyPointsAmount} PTS/day + + + + + Next drop in + + + + {data.claimablePointsAmount && ( + + )} diff --git a/dapp/src/theme/Button.ts b/dapp/src/theme/Button.ts index a9e1e05f3..269b19267 100644 --- a/dapp/src/theme/Button.ts +++ b/dapp/src/theme/Button.ts @@ -23,21 +23,31 @@ export const buttonTheme: ComponentSingleStyleConfig = { }, }, variants: { - solid: { - bg: "brand.400", - color: "white", - _hover: { - bg: "brand.500", - }, - _active: { - bg: "brand.400", - }, - _loading: { - _disabled: { - background: "gold.300", - opacity: 1, + solid: ({ colorScheme }: StyleFunctionProps) => { + let baseBg = `${colorScheme}.400` + let hoverBg = `${colorScheme}.500` + + if (colorScheme === "green") { + baseBg = `${colorScheme}.500` + hoverBg = `${colorScheme}.600` + } + + return { + bg: baseBg, + color: "white", + _hover: { + bg: hoverBg, }, - }, + _active: { + bg: baseBg, + }, + _loading: { + _disabled: { + background: "gold.300", + opacity: 1, + }, + }, + } }, outline: ({ colorScheme }: StyleFunctionProps) => { const defaultStyles = { @@ -153,4 +163,7 @@ export const buttonTheme: ComponentSingleStyleConfig = { fontWeight: "medium", }, }, + defaultProps: { + colorScheme: "brand", + }, } diff --git a/dapp/src/theme/Countdown.ts b/dapp/src/theme/Countdown.ts new file mode 100644 index 000000000..e3410be37 --- /dev/null +++ b/dapp/src/theme/Countdown.ts @@ -0,0 +1,52 @@ +import { createMultiStyleConfigHelpers, defineStyle } from "@chakra-ui/react" + +const PARTS = ["container", "unit", "separator"] + +const containerBaseStyle = defineStyle({ + fontWeight: "bold", + textAlign: "center", +}) + +const unitBaseStyle = defineStyle({ + display: "inline-block", + color: "grey.700", + whiteSpace: "nowrap", +}) + +const separatorBaseStyle = defineStyle({ + display: "inline-block", + color: "grey.300", +}) + +const multiStyleConfig = createMultiStyleConfigHelpers(PARTS) + +const baseStyle = multiStyleConfig.definePartsStyle(() => ({ + container: containerBaseStyle, + unit: unitBaseStyle, + separator: separatorBaseStyle, +})) + +const getSizeStyles = (size: string) => ({ + unit: { + w: "1.25em", // based on size proportions + fontSize: size, + lineHeight: size, + }, + separator: { + w: "0.3125em", // based on size proportions + fontSize: size, + lineHeight: size, + }, +}) + +const sizes = Object.fromEntries( + ["xs", "sm", "md", "lg", "xl", "2xl"].map((size) => [ + size, + getSizeStyles(size), + ]), +) + +export const countdownTheme = multiStyleConfig.defineMultiStyleConfig({ + baseStyle, + sizes, +}) diff --git a/dapp/src/theme/index.ts b/dapp/src/theme/index.ts index 664fc7c27..316b7edaa 100644 --- a/dapp/src/theme/index.ts +++ b/dapp/src/theme/index.ts @@ -27,6 +27,7 @@ import { linkTheme } from "./Link" import { skeletonTheme } from "./Skeleton" import { closeButtonTheme } from "./CloseButton" import { progressTheme } from "./Progress" +import { countdownTheme } from "./Countdown" const defaultTheme = { // TODO: Remove when dark mode is ready @@ -65,6 +66,7 @@ const defaultTheme = { Tooltip: tooltipTheme, Skeleton: skeletonTheme, Progress: progressTheme, + Countdown: countdownTheme, }, } From 134911c97a5fef57f0cfacf883f40720530834fc Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Tue, 13 Aug 2024 18:27:25 +0200 Subject: [PATCH 02/61] Implement `AcreRankCard` component --- dapp/src/hooks/useAcrePoints.ts | 18 ++- dapp/src/pages/DashboardPage/AcreRankCard.tsx | 109 ++++++++++++++++++ dapp/src/pages/DashboardPage/index.tsx | 2 + dapp/src/utils/time.ts | 5 + 4 files changed, 131 insertions(+), 3 deletions(-) create mode 100644 dapp/src/pages/DashboardPage/AcreRankCard.tsx diff --git a/dapp/src/hooks/useAcrePoints.ts b/dapp/src/hooks/useAcrePoints.ts index de651696d..1febc0a71 100644 --- a/dapp/src/hooks/useAcrePoints.ts +++ b/dapp/src/hooks/useAcrePoints.ts @@ -1,8 +1,8 @@ import { numberToLocaleString } from "#/utils" -const FORMATTED_DECIMALS = 0 -const getFormattedAmount = (amount: number) => - numberToLocaleString(amount, FORMATTED_DECIMALS) +const getFormattedAmount = (amount: number) => numberToLocaleString(amount, 0) + +const DEFAULT_USERNAME = "Stacrer" type UseAcrePointsReturnType = { data: { @@ -10,6 +10,10 @@ type UseAcrePointsReturnType = { dailyPointsAmount: number claimablePointsAmount: number nextDropTimestamp: number + rankPosition: number + estimatedRankPosition: number + userName: string + lastClaimedTimestamp: number } formatted: { totalPointsAmount: string @@ -23,6 +27,10 @@ export default function useAcrePoints(): UseAcrePointsReturnType { const dailyPointsAmount = 1201 // TODO: Fetch from the API const claimablePointsAmount = 2402 // TODO: Fetch from the API const nextDropTimestamp = 1634160000000 // TODO: Fetch from the API + const rankPosition = 8724 // TODO: Fetch from the API + const estimatedRankPosition = 2082 // TODO: Fetch from the API + const userName = DEFAULT_USERNAME // Eventually an entry point for future implementation + const lastClaimedTimestamp = 1634160000000 // TODO: Fetch from the API const formattedTotalPointsAmount = getFormattedAmount(totalPointsAmount) const formattedDailyPointsAmount = getFormattedAmount(dailyPointsAmount) @@ -36,6 +44,10 @@ export default function useAcrePoints(): UseAcrePointsReturnType { dailyPointsAmount, claimablePointsAmount, nextDropTimestamp, + rankPosition, + estimatedRankPosition, + userName, + lastClaimedTimestamp, }, formatted: { totalPointsAmount: formattedTotalPointsAmount, diff --git a/dapp/src/pages/DashboardPage/AcreRankCard.tsx b/dapp/src/pages/DashboardPage/AcreRankCard.tsx new file mode 100644 index 000000000..93d5b18aa --- /dev/null +++ b/dapp/src/pages/DashboardPage/AcreRankCard.tsx @@ -0,0 +1,109 @@ +import React from "react" +import { TextMd, TextXl } from "#/components/shared/Typography" +import { + Card, + CardBody, + CardHeader, + CardProps, + Flex, + HStack, + Icon, + Tag, + TagLabel, +} from "@chakra-ui/react" +import UserDataSkeleton from "#/components/shared/UserDataSkeleton" +import useAcrePoints from "#/hooks/useAcrePoints" +import ButtonLink from "#/components/shared/ButtonLink" +import { IconTrendingDown, IconTrendingUp } from "@tabler/icons-react" +import { isWithinPeriod } from "#/utils" +import { ONE_SEC_IN_MILLISECONDS, ONE_WEEK_IN_SECONDS } from "#/constants" + +type AcreRankCardProps = CardProps & { + withTrendingIcon?: boolean +} + +export default function AcreRankCard(props: AcreRankCardProps) { + const { withTrendingIcon = true, ...restProps } = props + + const { data } = useAcrePoints() + + const hasClaimedLastWeek = isWithinPeriod( + data.lastClaimedTimestamp, + ONE_WEEK_IN_SECONDS * ONE_SEC_IN_MILLISECONDS, + ) + const isRankTrendingUp = data.estimatedRankPosition > data.rankPosition + + return ( + + + + Your rank + + + + Leaderboard + + + + + + + + #{data.estimatedRankPosition} + + {withTrendingIcon && hasClaimedLastWeek && ( + + )} + + + + {data.userName} + + + + #{data.rankPosition} + + + + + + + + ) +} diff --git a/dapp/src/pages/DashboardPage/index.tsx b/dapp/src/pages/DashboardPage/index.tsx index 2453b6012..c412bd744 100644 --- a/dapp/src/pages/DashboardPage/index.tsx +++ b/dapp/src/pages/DashboardPage/index.tsx @@ -13,6 +13,7 @@ import { PageLayout, PageLayoutColumn } from "./PageLayout" import AcrePointsCard from "./AcrePointsCard" import { CurrentSeasonCard } from "./CurrentSeasonCard" import BeehiveCard from "./BeehiveCard" +import AcreRankCard from "./AcreRankCard" export default function DashboardPage() { const isMobileMode = useMobileMode() @@ -36,6 +37,7 @@ export default function DashboardPage() { + ) diff --git a/dapp/src/utils/time.ts b/dapp/src/utils/time.ts index bd40d351d..fd1786440 100644 --- a/dapp/src/utils/time.ts +++ b/dapp/src/utils/time.ts @@ -94,3 +94,8 @@ export const getExpirationTimestamp = (duration: number, startDate?: Date) => { return dateToUnixTimestamp(expirationDate) } + +export const isWithinPeriod = ( + timestamp: number, + periodInMilliseconds: number, +) => Date.now() - timestamp <= periodInMilliseconds && timestamp <= Date.now() From dfa369b670c67b0287dcd57255f60dee5320795a Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Tue, 13 Aug 2024 18:40:01 +0200 Subject: [PATCH 03/61] Adjust layout Changed width, added links, moved `CardButton` component to shared. --- .../shared}/CardButton.tsx | 0 .../pages/DashboardPage/PageLayout/PageLayout.tsx | 2 +- .../DashboardPage/PageLayout/PageLayoutColumn.tsx | 2 +- dapp/src/pages/DashboardPage/index.tsx | 15 ++++++++++++++- dapp/src/pages/LandingPage/components/index.ts | 2 +- dapp/src/pages/LandingPage/index.tsx | 2 +- 6 files changed, 18 insertions(+), 5 deletions(-) rename dapp/src/{pages/LandingPage/components => components/shared}/CardButton.tsx (100%) diff --git a/dapp/src/pages/LandingPage/components/CardButton.tsx b/dapp/src/components/shared/CardButton.tsx similarity index 100% rename from dapp/src/pages/LandingPage/components/CardButton.tsx rename to dapp/src/components/shared/CardButton.tsx diff --git a/dapp/src/pages/DashboardPage/PageLayout/PageLayout.tsx b/dapp/src/pages/DashboardPage/PageLayout/PageLayout.tsx index 98155ba31..aff4da979 100644 --- a/dapp/src/pages/DashboardPage/PageLayout/PageLayout.tsx +++ b/dapp/src/pages/DashboardPage/PageLayout/PageLayout.tsx @@ -17,7 +17,7 @@ function PageLayout(props: GridProps) { base: "1fr", md: "repeat(2, 1fr)", lg: "1fr 0.6fr", - "2xl": "minmax(396px, auto) 1fr minmax(396px, auto)", + "2xl": "minmax(396px, auto) 1fr minmax(480px, auto)", }} {...restProps} > diff --git a/dapp/src/pages/DashboardPage/PageLayout/PageLayoutColumn.tsx b/dapp/src/pages/DashboardPage/PageLayout/PageLayoutColumn.tsx index dda976593..6e06c7e8f 100644 --- a/dapp/src/pages/DashboardPage/PageLayout/PageLayoutColumn.tsx +++ b/dapp/src/pages/DashboardPage/PageLayout/PageLayoutColumn.tsx @@ -10,7 +10,7 @@ function PageLayoutColumn(props: PageLayoutColumnProps) { return ( + + + Documentation + + + Blog + + + FAQ + + ) diff --git a/dapp/src/pages/LandingPage/components/index.ts b/dapp/src/pages/LandingPage/components/index.ts index c68c93f46..ef356a10c 100644 --- a/dapp/src/pages/LandingPage/components/index.ts +++ b/dapp/src/pages/LandingPage/components/index.ts @@ -1,5 +1,5 @@ export { default as BenefitCard } from "./BenefitCard" -export { default as CardButton } from "./CardButton" +export { default as CardButton } from "../../../components/shared/CardButton" export { default as ContentCard } from "./ContentCard" export { default as HeroSection } from "./HeroSection" export { default as HighlightedValueCard } from "./HighlightedValueCard" diff --git a/dapp/src/pages/LandingPage/index.tsx b/dapp/src/pages/LandingPage/index.tsx index 92a5b9ba2..7ecbafe23 100644 --- a/dapp/src/pages/LandingPage/index.tsx +++ b/dapp/src/pages/LandingPage/index.tsx @@ -12,8 +12,8 @@ import { EXTERNAL_HREF, PARTNER_LOGOS } from "#/constants" import { CurrentSeasonSection, HeroSection, - CardButton, } from "#/pages/LandingPage/components" +import CardButton from "#/components/shared/CardButton" import MobileModeBanner from "#/components/MobileModeBanner" import { H4 } from "#/components/shared/Typography" From 8ffc54f5948c11477377410879b9bf93e0f36855 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Tue, 13 Aug 2024 19:33:16 +0200 Subject: [PATCH 04/61] Fix layout row height --- dapp/src/pages/DashboardPage/PageLayout/PageLayout.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/dapp/src/pages/DashboardPage/PageLayout/PageLayout.tsx b/dapp/src/pages/DashboardPage/PageLayout/PageLayout.tsx index aff4da979..3366c5b80 100644 --- a/dapp/src/pages/DashboardPage/PageLayout/PageLayout.tsx +++ b/dapp/src/pages/DashboardPage/PageLayout/PageLayout.tsx @@ -13,6 +13,7 @@ function PageLayout(props: GridProps) { py={9} gap={8} alignItems="start" + gridTemplateRows="auto 1fr" gridTemplateColumns={{ base: "1fr", md: "repeat(2, 1fr)", From 6b65ea897e4bb50d2af730e4675a1ed9c8bcfbc6 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Wed, 14 Aug 2024 13:47:24 +0200 Subject: [PATCH 05/61] Fix Button styles --- dapp/src/theme/Button.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/dapp/src/theme/Button.ts b/dapp/src/theme/Button.ts index 269b19267..bc502a9da 100644 --- a/dapp/src/theme/Button.ts +++ b/dapp/src/theme/Button.ts @@ -96,6 +96,7 @@ export const buttonTheme: ComponentSingleStyleConfig = { return defaultStyles }, ghost: { + color: "inherit", _hover: { bg: "transparent", }, From 610fcfd336e4fb665a5c119890faa5c340b1fba8 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Wed, 14 Aug 2024 17:16:46 +0200 Subject: [PATCH 06/61] Implement `AcrePointsRewardEstimation` component --- .../AcrePointsRewardEstimation.tsx | 111 ++++++++++++++++++ .../StakeFormModal/index.tsx | 2 + dapp/src/constants/acrePoints.ts | 16 +++ dapp/src/constants/index.ts | 1 + dapp/src/types/acrePoints.ts | 5 + dapp/src/types/index.ts | 1 + dapp/src/utils/acrePoints.ts | 14 +++ dapp/src/utils/index.ts | 1 + 8 files changed, 151 insertions(+) create mode 100644 dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/AcrePointsRewardEstimation.tsx create mode 100644 dapp/src/constants/acrePoints.ts create mode 100644 dapp/src/types/acrePoints.ts create mode 100644 dapp/src/utils/acrePoints.ts diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/AcrePointsRewardEstimation.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/AcrePointsRewardEstimation.tsx new file mode 100644 index 000000000..8d05a7b7c --- /dev/null +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/AcrePointsRewardEstimation.tsx @@ -0,0 +1,111 @@ +import React, { useMemo, useState } from "react" +import { + HStack, + Icon, + Menu, + MenuButton, + MenuItem, + MenuList, + StackProps, + VStack, +} from "@chakra-ui/react" +import { H4, TextMd } from "#/components/shared/Typography" +import { AcrePointsClaimTier } from "#/types" +import { useAcrePoints } from "#/hooks" +import { acrePoints, numberToLocaleString } from "#/utils" +import { IconChevronDown } from "@tabler/icons-react" +import { ACRE_POINTS_TIER_LABELS } from "#/constants" + +const { estimateRewardAmountPerTier } = acrePoints + +function AcrePointsRewardEstimation(props: StackProps) { + const [selectedTierItem, setSelectedTierItem] = useState( + AcrePointsClaimTier.Weekly, + ) + const selectedTierItemLabel = useMemo( + () => ACRE_POINTS_TIER_LABELS[selectedTierItem], + [selectedTierItem], + ) + + const tierItems = [ + selectedTierItem, + ...Object.entries(AcrePointsClaimTier) + .filter(([, tierValue]) => tierValue !== selectedTierItem) + .map(([, tierLabel]) => tierLabel), + ] + + const { data } = useAcrePoints() + + const estimatedReward = useMemo( + () => estimateRewardAmountPerTier(data.dailyPointsAmount, selectedTierItem), + [data, selectedTierItem], + ) + + return ( + + + Acre points you'll earn + + + {({ isOpen }) => ( + <> + + + {selectedTierItemLabel} + + + + + + {tierItems.map((tierItem) => ( + setSelectedTierItem(tierItem)} + fontWeight="semibold" + > + {ACRE_POINTS_TIER_LABELS[tierItem]} + + ))} + + + )} + + + +

+{numberToLocaleString(estimatedReward, 0)} PTS

+
+ ) +} + +export default AcrePointsRewardEstimation diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx index 1c5037a76..f39c30e34 100644 --- a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx @@ -5,6 +5,7 @@ import { useMinDepositAmount, useWallet } from "#/hooks" import { FormSubmitButton } from "#/components/shared/Form" import { BaseFormProps } from "#/types" import StakeDetails from "./StakeDetails" +import AcrePointsRewardEstimation from "./AcrePointsRewardEstimation" function StakeFormModal({ onSubmitForm, @@ -22,6 +23,7 @@ function StakeFormModal({ onSubmitForm={onSubmitForm} withMaxButton={false} > + Deposit diff --git a/dapp/src/constants/acrePoints.ts b/dapp/src/constants/acrePoints.ts new file mode 100644 index 000000000..3fdf5a6cf --- /dev/null +++ b/dapp/src/constants/acrePoints.ts @@ -0,0 +1,16 @@ +import { AcrePointsClaimTier } from "#/types" + +export const ACRE_POINTS_REWARDS_MULTIPLERS: Record< + AcrePointsClaimTier, + number +> = { + WEEKLY: 7, + MONTHLY: 30, + YEARLY: 365, +} + +export const ACRE_POINTS_TIER_LABELS: Record = { + WEEKLY: "Per week", + MONTHLY: "Per month", + YEARLY: "Per year", +} diff --git a/dapp/src/constants/index.ts b/dapp/src/constants/index.ts index 5ab47427b..4e42b43d4 100644 --- a/dapp/src/constants/index.ts +++ b/dapp/src/constants/index.ts @@ -12,3 +12,4 @@ export * from "./staking" export { default as tbtc } from "./tbtc" export * from "./time" export { default as wallets } from "./wallets" +export * from "./acrePoints" diff --git a/dapp/src/types/acrePoints.ts b/dapp/src/types/acrePoints.ts new file mode 100644 index 000000000..4c3b50cc2 --- /dev/null +++ b/dapp/src/types/acrePoints.ts @@ -0,0 +1,5 @@ +export enum AcrePointsClaimTier { + Weekly = "WEEKLY", + Monthly = "MONTHLY", + Yearly = "YEARLY", +} diff --git a/dapp/src/types/index.ts b/dapp/src/types/index.ts index ec4b743ac..1cc2c4503 100644 --- a/dapp/src/types/index.ts +++ b/dapp/src/types/index.ts @@ -15,3 +15,4 @@ export * from "./eip1193" export * from "./error" export * from "./status" export * from "./orangekit" +export * from "./acrePoints" diff --git a/dapp/src/utils/acrePoints.ts b/dapp/src/utils/acrePoints.ts new file mode 100644 index 000000000..799c68e95 --- /dev/null +++ b/dapp/src/utils/acrePoints.ts @@ -0,0 +1,14 @@ +import { ACRE_POINTS_REWARDS_MULTIPLERS } from "#/constants" +import { AcrePointsClaimTier } from "#/types" + +const estimateRewardAmountPerTier = ( + baseReward: number, + tier: AcrePointsClaimTier, +) => { + const multipler = ACRE_POINTS_REWARDS_MULTIPLERS[tier] + return baseReward * multipler +} + +export default { + estimateRewardAmountPerTier, +} diff --git a/dapp/src/utils/index.ts b/dapp/src/utils/index.ts index 43b73bfed..9b1c5919b 100644 --- a/dapp/src/utils/index.ts +++ b/dapp/src/utils/index.ts @@ -16,3 +16,4 @@ export { default as orangeKit } from "./orangeKit" export { default as userAgent } from "./userAgent" export { default as referralProgram } from "./referralProgram" export { default as mezoPortalAPI } from "./mezoPortalApi" +export { default as acrePoints } from "./acrePoints" From 2e0bbedf04aa281e6bc4c3ec3cff86e97b60ea3f Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Wed, 14 Aug 2024 17:18:34 +0200 Subject: [PATCH 07/61] Minor style adjustments --- dapp/src/pages/DashboardPage/AcrePointsCard.tsx | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx index 436ea9a2e..4cdca1d10 100644 --- a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx +++ b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx @@ -1,7 +1,6 @@ import React from "react" import { H4, TextMd } from "#/components/shared/Typography" import { - Box, Button, Card, CardBody, @@ -30,12 +29,7 @@ export default function AcrePointsCard(props: CardProps) { -

- {formatted.totalPointsAmount} - -  PTS - -

+

{formatted.totalPointsAmount} PTS

+ {formatted.dailyPointsAmount} PTS/day From b45f6b0ad118430e20876c2fdd763efb52ecfa96 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Tue, 20 Aug 2024 15:48:24 +0200 Subject: [PATCH 08/61] Implement `AcrePointsClaimModal` component --- .../components/AcrePointsClaimModal/index.tsx | 131 ++++++++++++++++++ dapp/src/components/ModalRoot/index.tsx | 2 + .../pages/DashboardPage/AcrePointsCard.tsx | 9 ++ dapp/src/theme/Modal.ts | 10 ++ dapp/src/theme/utils/colors.ts | 5 + dapp/src/types/modal.ts | 1 + dapp/src/utils/numbers.ts | 5 + 7 files changed, 163 insertions(+) create mode 100644 dapp/src/components/AcrePointsClaimModal/index.tsx diff --git a/dapp/src/components/AcrePointsClaimModal/index.tsx b/dapp/src/components/AcrePointsClaimModal/index.tsx new file mode 100644 index 000000000..e10493a93 --- /dev/null +++ b/dapp/src/components/AcrePointsClaimModal/index.tsx @@ -0,0 +1,131 @@ +import React, { useEffect, useMemo } from "react" +import { useAcrePoints } from "#/hooks" +import { Box, ModalBody, Text, VStack } from "@chakra-ui/react" +import { + AnimationSequence, + motion, + Transition, + useAnimate, + useMotionValue, +} from "framer-motion" +import { getNumberWithSign } from "#/utils" +import withBaseModal from "../ModalRoot/withBaseModal" +import { TextXl } from "../shared/Typography" + +const MotionVStack = motion(VStack) + +const INITIAL_CONTAINER_HEIGHT = 214 +const CONTAINER_HEIGHT = 288 +const VALUE_SCALE = 0.375 +const STEP_HEIGHT = CONTAINER_HEIGHT * (1 - VALUE_SCALE) +const STEP_SPACING = 32 +const TRANSITION: Transition = { + type: "spring", + damping: 14, + stiffness: 86, + delay: 4, // step duration +} + +const getStepOffsets = ( + stepCount: number, + stepHeight: number, + spacing: number, +) => + Array(stepCount - 1) + .fill(0) + .map((_, index) => + index === 0 + ? -stepHeight + : (index + 1) * -stepHeight - spacing * 2 ** index, + ) + +export function AcrePointsClaimModalBase() { + const { formatted, data } = useAcrePoints() + const rankDifference = getNumberWithSign( + data.estimatedRankPosition - data.rankPosition, + ) + + const steps = useMemo( + () => [ + ["You earned", `+${formatted.claimablePointsAmount} PTS`], + [ + "Updating points balance..." /* Staggered text component: current points + claimed */, + "2,749,993", + ], + ["Calculating rank...", rankDifference], + [ + "Updating rank..." /* Staggered text component: current rank + difference */, + "#4923", + ], + ], + [formatted, rankDifference], + ) + + const containerHeight = useMotionValue(INITIAL_CONTAINER_HEIGHT) + const [scope, animate] = useAnimate() + + useEffect(() => { + const offsets = getStepOffsets(steps.length, STEP_HEIGHT, STEP_SPACING) + const valueElements = [ + ...(scope.current as HTMLElement).querySelectorAll("[data-step-value]"), + ].slice(0, -1) + + const sequence = [ + ["[data-steps-list]", { y: offsets[0] }, TRANSITION], + [containerHeight, CONTAINER_HEIGHT, { at: "<", ...TRANSITION }], + [valueElements[0], { scale: VALUE_SCALE }, { at: "<", ...TRANSITION }], + ["[data-steps-list]", { y: offsets[1] }, TRANSITION], + [valueElements[1], { scale: VALUE_SCALE }, { at: "<", ...TRANSITION }], + ["[data-steps-list]", { y: offsets[2] }, TRANSITION], + [valueElements[2], { scale: VALUE_SCALE }, { at: "<", ...TRANSITION }], + ] as AnimationSequence + + const handleAnimation = async () => { + await animate(sequence) + } + + // eslint-disable-next-line no-void + void handleAnimation() + }, [scope, animate, containerHeight, steps]) + + return ( + + + 32px + data-steps-list + style={{ height: containerHeight }} + > + {steps.map(([currentStepLabel, currentStepValue]) => ( + + + {currentStepLabel} + + + + {currentStepValue} + + + ))} + + + + ) +} + +const AcrePointsClaimModal = withBaseModal(AcrePointsClaimModalBase, { + returnFocusOnClose: false, + variant: "unstyled", + size: "xl", +}) +export default AcrePointsClaimModal diff --git a/dapp/src/components/ModalRoot/index.tsx b/dapp/src/components/ModalRoot/index.tsx index 7ca8db6e0..fdf513531 100644 --- a/dapp/src/components/ModalRoot/index.tsx +++ b/dapp/src/components/ModalRoot/index.tsx @@ -6,6 +6,7 @@ import WelcomeModal from "../WelcomeModal" import MezoBeehiveModal from "../MezoBeehiveModal" import ConnectWalletModal from "../ConnectWalletModal" import UnexpectedErrorModal from "../UnexpectedErrorModal" +import AcrePointsClaimModal from "../AcrePointsClaimModal" const MODALS: Record = { STAKE: TransactionModal, @@ -14,6 +15,7 @@ const MODALS: Record = { MEZO_BEEHIVE: MezoBeehiveModal, CONNECT_WALLET: ConnectWalletModal, UNEXPECTED_ERROR: UnexpectedErrorModal, + ACRE_POINTS_CLAIM: AcrePointsClaimModal, } as const export default function ModalRoot() { diff --git a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx index 4cdca1d10..b93298ef2 100644 --- a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx +++ b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx @@ -11,9 +11,17 @@ import { import UserDataSkeleton from "#/components/shared/UserDataSkeleton" import Countdown from "#/components/shared/Countdown" import useAcrePoints from "#/hooks/useAcrePoints" +import { MODAL_TYPES } from "#/types" +import { useModal } from "#/hooks" export default function AcrePointsCard(props: CardProps) { const { data, formatted } = useAcrePoints() + const { openModal } = useModal() + + const handleClaim = () => { + // TODO: Call API endpoint to claim points + openModal(MODAL_TYPES.ACRE_POINTS_CLAIM) + } return ( export const getPercentValue = (value: number, maxValue: number) => (value * 100) / maxValue + +export const getNumberWithSign = (number: number) => + new Intl.NumberFormat("en-US", { + signDisplay: "exceptZero", + }).format(number) From e6f3f2ab559c947b15363420629bae48625845f3 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Wed, 21 Aug 2024 10:37:15 +0200 Subject: [PATCH 09/61] Implement `AnimatedNumber` component --- .../components/AcrePointsClaimModal/index.tsx | 75 +++++++++++++---- .../shared/AnimatedNumber/AnimatedNumber.tsx | 72 ++++++++++++++++ .../AnimatedNumber/AnimatedNumberColumn.tsx | 84 +++++++++++++++++++ .../AnimatedNumberSeparator.tsx | 14 ++++ .../components/shared/AnimatedNumber/index.ts | 1 + dapp/src/hooks/useAcrePoints.ts | 63 +++++--------- .../pages/DashboardPage/AcrePointsCard.tsx | 34 +++++--- dapp/src/pages/DashboardPage/AcreRankCard.tsx | 18 ++-- dapp/src/utils/acrePoints.ts | 4 + 9 files changed, 292 insertions(+), 73 deletions(-) create mode 100644 dapp/src/components/shared/AnimatedNumber/AnimatedNumber.tsx create mode 100644 dapp/src/components/shared/AnimatedNumber/AnimatedNumberColumn.tsx create mode 100644 dapp/src/components/shared/AnimatedNumber/AnimatedNumberSeparator.tsx create mode 100644 dapp/src/components/shared/AnimatedNumber/index.ts diff --git a/dapp/src/components/AcrePointsClaimModal/index.tsx b/dapp/src/components/AcrePointsClaimModal/index.tsx index e10493a93..a511a047f 100644 --- a/dapp/src/components/AcrePointsClaimModal/index.tsx +++ b/dapp/src/components/AcrePointsClaimModal/index.tsx @@ -1,4 +1,4 @@ -import React, { useEffect, useMemo } from "react" +import React, { ReactNode, useEffect, useMemo } from "react" import { useAcrePoints } from "#/hooks" import { Box, ModalBody, Text, VStack } from "@chakra-ui/react" import { @@ -8,9 +8,12 @@ import { useAnimate, useMotionValue, } from "framer-motion" -import { getNumberWithSign } from "#/utils" +import { acrePoints as acrePointsUtils } from "#/utils" import withBaseModal from "../ModalRoot/withBaseModal" import { TextXl } from "../shared/Typography" +import { AnimatedNumber } from "../shared/AnimatedNumber" + +const { getFormattedAmount } = acrePointsUtils const MotionVStack = motion(VStack) @@ -40,25 +43,67 @@ const getStepOffsets = ( ) export function AcrePointsClaimModalBase() { - const { formatted, data } = useAcrePoints() - const rankDifference = getNumberWithSign( - data.estimatedRankPosition - data.rankPosition, + const { + claimablePointsAmount, + totalPointsAmount, + rankPosition, + estimatedRankPosition, + } = useAcrePoints() + + const formattedClaimablePointsAmount = getFormattedAmount( + claimablePointsAmount, ) + const formattedUpdatedPointsAmount = getFormattedAmount( + claimablePointsAmount + totalPointsAmount, + ) + const rankPositionDifference = estimatedRankPosition - rankPosition - const steps = useMemo( + const steps = useMemo<[string, ReactNode][]>( () => [ - ["You earned", `+${formatted.claimablePointsAmount} PTS`], [ - "Updating points balance..." /* Staggered text component: current points + claimed */, - "2,749,993", + "You earned", + , + ], + [ + "Updating points balance...", + , + ], + [ + "Calculating rank...", + 0 ? "+" : "-"} + animateMode="whileInView" + color={rankPositionDifference > 0 ? "green.500" : "red.500"} + />, ], - ["Calculating rank...", rankDifference], [ - "Updating rank..." /* Staggered text component: current rank + difference */, - "#4923", + "Updating rank...", + , ], ], - [formatted, rankDifference], + [ + formattedClaimablePointsAmount, + estimatedRankPosition, + formattedUpdatedPointsAmount, + rankPositionDifference, + ], ) const containerHeight = useMotionValue(INITIAL_CONTAINER_HEIGHT) @@ -97,7 +142,7 @@ export function AcrePointsClaimModalBase() { style={{ height: containerHeight }} > {steps.map(([currentStepLabel, currentStepValue]) => ( - + {currentStepValue} diff --git a/dapp/src/components/shared/AnimatedNumber/AnimatedNumber.tsx b/dapp/src/components/shared/AnimatedNumber/AnimatedNumber.tsx new file mode 100644 index 000000000..2a61007a2 --- /dev/null +++ b/dapp/src/components/shared/AnimatedNumber/AnimatedNumber.tsx @@ -0,0 +1,72 @@ +import React, { useMemo } from "react" +import { FlexProps, Flex, Box } from "@chakra-ui/react" +import { motion } from "framer-motion" +import AnimatedNumberSeparator from "./AnimatedNumberSeparator" +import AnimatedNumberColumn from "./AnimatedNumberColumn" + +const MotionFlex = motion(Flex) + +const isSeparatorSegment = (value: string): value is "." | "," => + [",", "."].includes(value) + +type AnimatedNumberProps = FlexProps & { + value: number | string + desiredDecimals?: number + prefix?: string + suffix?: string + animateMode?: "whileInView" | "always" + indicationColor?: string +} + +function AnimatedNumber(props: AnimatedNumberProps) { + const { + value, + desiredDecimals = 0, + prefix = "", + suffix = "", + animateMode = "whileInView", + indicationColor, + ...restProps + } = props + const numberSegments = useMemo(() => { + const parsedValue = + typeof value === "string" + ? value + : Math.max(value, 0).toFixed(desiredDecimals) + + return parsedValue.split("").reverse() + }, [value, desiredDecimals]) + + return ( + + {suffix && {suffix}} + + {numberSegments.map((segment, index) => + isSeparatorSegment(segment) ? ( + {segment} + ) : ( + + ), + )} + + {prefix && {prefix}} + + ) +} + +export default AnimatedNumber diff --git a/dapp/src/components/shared/AnimatedNumber/AnimatedNumberColumn.tsx b/dapp/src/components/shared/AnimatedNumber/AnimatedNumberColumn.tsx new file mode 100644 index 000000000..11d67d982 --- /dev/null +++ b/dapp/src/components/shared/AnimatedNumber/AnimatedNumberColumn.tsx @@ -0,0 +1,84 @@ +import React, { useCallback, useEffect, useRef, useState } from "react" +import { Box, BoxProps, useToken } from "@chakra-ui/react" +import { motion } from "framer-motion" + +const MotionBox = motion(Box) + +type AnimatedNumberColumnProps = BoxProps & { + digit: string + animateWhileInView?: boolean + indicationColor?: string +} + +function AnimatedNumberColumn(props: AnimatedNumberColumnProps) { + const { + digit, + animateWhileInView = false, + indicationColor, + ...restProps + } = props + + const [position, setPosition] = useState(0) + const [isUpdateIndicated, setIsUpdateIndicated] = useState(true) + + const containerRef = useRef(null) + + const setDigitToPosition = useCallback((columnDigit: string) => { + setPosition((containerRef.current?.clientHeight ?? 0) * +columnDigit) + }, []) + + useEffect(() => setDigitToPosition(digit), [digit, setDigitToPosition]) + + const indicationColorValue = useToken( + "colors", + indicationColor ?? "brand.400", + ) + + return ( + + setIsUpdateIndicated(false)} + > + {[9, 8, 7, 6, 5, 4, 3, 2, 1, 0].map((columnDigit) => ( + + {columnDigit} + + ))} + + + + 0 + + + ) +} + +export default AnimatedNumberColumn diff --git a/dapp/src/components/shared/AnimatedNumber/AnimatedNumberSeparator.tsx b/dapp/src/components/shared/AnimatedNumber/AnimatedNumberSeparator.tsx new file mode 100644 index 000000000..92adcd0bc --- /dev/null +++ b/dapp/src/components/shared/AnimatedNumber/AnimatedNumberSeparator.tsx @@ -0,0 +1,14 @@ +import React from "react" +import { Box, BoxProps } from "@chakra-ui/react" + +type AnimatedNumberSeparatorProps = BoxProps & { + children: "," | "." +} + +function AnimatedNumberSeparator(props: AnimatedNumberSeparatorProps) { + const { children, ...restProps } = props + + return {children} +} + +export default AnimatedNumberSeparator diff --git a/dapp/src/components/shared/AnimatedNumber/index.ts b/dapp/src/components/shared/AnimatedNumber/index.ts new file mode 100644 index 000000000..70f801b32 --- /dev/null +++ b/dapp/src/components/shared/AnimatedNumber/index.ts @@ -0,0 +1 @@ +export { default as AnimatedNumber } from "./AnimatedNumber" diff --git a/dapp/src/hooks/useAcrePoints.ts b/dapp/src/hooks/useAcrePoints.ts index 1febc0a71..0f7d84f52 100644 --- a/dapp/src/hooks/useAcrePoints.ts +++ b/dapp/src/hooks/useAcrePoints.ts @@ -1,25 +1,15 @@ -import { numberToLocaleString } from "#/utils" - -const getFormattedAmount = (amount: number) => numberToLocaleString(amount, 0) - const DEFAULT_USERNAME = "Stacrer" type UseAcrePointsReturnType = { - data: { - totalPointsAmount: number - dailyPointsAmount: number - claimablePointsAmount: number - nextDropTimestamp: number - rankPosition: number - estimatedRankPosition: number - userName: string - lastClaimedTimestamp: number - } - formatted: { - totalPointsAmount: string - dailyPointsAmount: string - claimablePointsAmount: string - } + totalPointsAmount: number + dailyPointsAmount: number + claimablePointsAmount: number + nextDropTimestamp: number + rankPosition: number + estimatedRankPosition: number + userName: string + userId: number + lastClaimedTimestamp: number } export default function useAcrePoints(): UseAcrePointsReturnType { @@ -27,32 +17,21 @@ export default function useAcrePoints(): UseAcrePointsReturnType { const dailyPointsAmount = 1201 // TODO: Fetch from the API const claimablePointsAmount = 2402 // TODO: Fetch from the API const nextDropTimestamp = 1634160000000 // TODO: Fetch from the API - const rankPosition = 8724 // TODO: Fetch from the API - const estimatedRankPosition = 2082 // TODO: Fetch from the API + const rankPosition = 2082 // TODO: Fetch from the API + const estimatedRankPosition = 2085 // TODO: Fetch from the API const userName = DEFAULT_USERNAME // Eventually an entry point for future implementation + const userId = 8724 // TODO: Fetch from the API const lastClaimedTimestamp = 1634160000000 // TODO: Fetch from the API - const formattedTotalPointsAmount = getFormattedAmount(totalPointsAmount) - const formattedDailyPointsAmount = getFormattedAmount(dailyPointsAmount) - const formattedClaimablePointsAmount = getFormattedAmount( - claimablePointsAmount, - ) - return { - data: { - totalPointsAmount, - dailyPointsAmount, - claimablePointsAmount, - nextDropTimestamp, - rankPosition, - estimatedRankPosition, - userName, - lastClaimedTimestamp, - }, - formatted: { - totalPointsAmount: formattedTotalPointsAmount, - dailyPointsAmount: formattedDailyPointsAmount, - claimablePointsAmount: formattedClaimablePointsAmount, - }, + totalPointsAmount, + dailyPointsAmount, + claimablePointsAmount, + nextDropTimestamp, + rankPosition, + estimatedRankPosition, + userName, + userId, + lastClaimedTimestamp, } } diff --git a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx index b93298ef2..5fe30893d 100644 --- a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx +++ b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx @@ -13,9 +13,17 @@ import Countdown from "#/components/shared/Countdown" import useAcrePoints from "#/hooks/useAcrePoints" import { MODAL_TYPES } from "#/types" import { useModal } from "#/hooks" +import { acrePoints as acrePointsUtils } from "#/utils" + +const { getFormattedAmount } = acrePointsUtils export default function AcrePointsCard(props: CardProps) { - const { data, formatted } = useAcrePoints() + const { + claimablePointsAmount, + nextDropTimestamp, + totalPointsAmount, + dailyPointsAmount, + } = useAcrePoints() const { openModal } = useModal() const handleClaim = () => { @@ -23,6 +31,12 @@ export default function AcrePointsCard(props: CardProps) { openModal(MODAL_TYPES.ACRE_POINTS_CLAIM) } + const formattedTotalPointsAmount = getFormattedAmount(totalPointsAmount) + const formattedDailyPointsAmount = getFormattedAmount(dailyPointsAmount) + const formattedClaimablePointsAmount = getFormattedAmount( + claimablePointsAmount, + ) + return ( -

{formatted.totalPointsAmount} PTS

+

{formattedTotalPointsAmount} PTS

- + {formatted.dailyPointsAmount} PTS/day + + {formattedDailyPointsAmount} PTS/day Next drop in - {data.claimablePointsAmount && ( + {claimablePointsAmount && ( )} diff --git a/dapp/src/pages/DashboardPage/AcreRankCard.tsx b/dapp/src/pages/DashboardPage/AcreRankCard.tsx index 93d5b18aa..5f2655a32 100644 --- a/dapp/src/pages/DashboardPage/AcreRankCard.tsx +++ b/dapp/src/pages/DashboardPage/AcreRankCard.tsx @@ -25,13 +25,19 @@ type AcreRankCardProps = CardProps & { export default function AcreRankCard(props: AcreRankCardProps) { const { withTrendingIcon = true, ...restProps } = props - const { data } = useAcrePoints() + const { + lastClaimedTimestamp, + estimatedRankPosition, + rankPosition, + userId, + userName, + } = useAcrePoints() const hasClaimedLastWeek = isWithinPeriod( - data.lastClaimedTimestamp, + lastClaimedTimestamp, ONE_WEEK_IN_SECONDS * ONE_SEC_IN_MILLISECONDS, ) - const isRankTrendingUp = data.estimatedRankPosition > data.rankPosition + const isRankTrendingUp = estimatedRankPosition > rankPosition return ( - #{data.estimatedRankPosition} + #{rankPosition} {withTrendingIcon && hasClaimedLastWeek && ( - {data.userName} + {userName} - #{data.rankPosition} + #{userId} diff --git a/dapp/src/utils/acrePoints.ts b/dapp/src/utils/acrePoints.ts index 799c68e95..249ba9477 100644 --- a/dapp/src/utils/acrePoints.ts +++ b/dapp/src/utils/acrePoints.ts @@ -1,5 +1,6 @@ import { ACRE_POINTS_REWARDS_MULTIPLERS } from "#/constants" import { AcrePointsClaimTier } from "#/types" +import { numberToLocaleString } from "./numbers" const estimateRewardAmountPerTier = ( baseReward: number, @@ -9,6 +10,9 @@ const estimateRewardAmountPerTier = ( return baseReward * multipler } +const getFormattedAmount = (amount: number) => numberToLocaleString(amount, 0) + export default { estimateRewardAmountPerTier, + getFormattedAmount, } From ab5fe1eae7c7ffc3721565c8e9d5c945a341eecf Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Wed, 11 Sep 2024 22:07:17 +0200 Subject: [PATCH 10/61] Hide Acre Points behind feature flag Removed (temporarily) `AcreRankCard` component --- dapp/.env | 1 + .../ArrowAnimatedBackground.tsx | 236 ++++++++++++++++++ .../components/AcrePointsClaimModal/index.tsx | 56 ++--- dapp/src/constants/featureFlags.ts | 4 + dapp/src/hooks/useAcrePoints.ts | 10 +- .../pages/DashboardPage/AcrePointsCard.tsx | 1 + dapp/src/pages/DashboardPage/index.tsx | 8 +- dapp/src/vite-env.d.ts | 1 + 8 files changed, 274 insertions(+), 43 deletions(-) create mode 100644 dapp/src/components/AcrePointsClaimModal/ArrowAnimatedBackground.tsx diff --git a/dapp/.env b/dapp/.env index 686f286e9..c80deb3a1 100644 --- a/dapp/.env +++ b/dapp/.env @@ -25,4 +25,5 @@ VITE_FEATURE_FLAG_WITHDRAWALS_ENABLED="false" VITE_FEATURE_FLAG_OKX_WALLET_ENABLED="false" VITE_FEATURE_FLAG_XVERSE_WALLET_ENABLED="false" VITE_FEATURE_FLAG_BEEHIVE_COMPONENT_ENABLED="false" +VITE_FEATURE_FLAG_ACRE_POINTS_ENABLED="true" diff --git a/dapp/src/components/AcrePointsClaimModal/ArrowAnimatedBackground.tsx b/dapp/src/components/AcrePointsClaimModal/ArrowAnimatedBackground.tsx new file mode 100644 index 000000000..e1a029c89 --- /dev/null +++ b/dapp/src/components/AcrePointsClaimModal/ArrowAnimatedBackground.tsx @@ -0,0 +1,236 @@ +import React, { useEffect, useLayoutEffect, useRef, useState } from "react" +import { BoxProps, Box } from "@chakra-ui/react" + +type Point = { + x: number + y: number +} +type Arrow = Point & { + opacity: number + direction: number +} + +class ArrowBackgroundAnimation { + readonly ctx: CanvasRenderingContext2D + + readonly arrows: Arrow[] + + readonly points: Point[] + + readonly cols: number + + readonly rows: number + + readonly options: { + speedFactor: number + maxDeviation: number + arrowColor: string + arrowOrientation: "up" | "down" + } + + readonly gridWidth = 24 + + readonly gridHeight = 27 + + readonly arrowPath = new Path2D("M8 2V16M8 2L14 8M8 2L2 8") + + constructor( + ctx: CanvasRenderingContext2D, + options = { + speedFactor: 0.05, + maxDeviation: 50, + arrowColor: "#318401", + arrowOrientation: "up" as const, + }, + ) { + this.ctx = ctx + this.options = options + + this.cols = Math.floor(this.ctx.canvas.width / this.gridWidth) + this.rows = Math.floor(this.ctx.canvas.height / this.gridHeight) + + console.log({ + w: this.ctx.canvas.width, + h: this.ctx.canvas.height, + cols: this.cols, + rows: this.rows, + }) + + this.arrows = [] + for (let i = 0; i < this.cols; i++) { + for (let j = 0; j < this.rows; j++) { + this.arrows.push({ + x: i * this.gridWidth, + y: j * this.gridHeight, + opacity: Math.random(), + direction: Math.random() > 0.5 ? 1 : -1, + }) + } + } + + this.points = [] + let lastY = this.ctx.canvas.height / 3 + for (let k = 0; k <= this.cols; k++) { + const nextY = lastY + (Math.random() - 0.5) * this.options.maxDeviation + this.points.push({ + x: k * this.gridWidth, + y: Math.min(this.ctx.canvas.height / 2, Math.max(50, nextY)), + }) + lastY = nextY + } + } + + private clearFrame() { + this.ctx.clearRect(0, 0, this.ctx.canvas.width, this.ctx.canvas.height) + } + + private updateCurve() { + this.points.forEach((point) => { + point.y += (Math.random() - 0.5) * 2 + }) + } + + private drawCurve() { + this.ctx.beginPath() + this.ctx.moveTo(this.points[0].x, this.points[0].y) + for (let i = 1; i < this.points.length - 2; i++) { + const xc = (this.points[i].x + this.points[i + 1].x) / 2 + const yc = (this.points[i].y + this.points[i + 1].y) / 2 + this.ctx.quadraticCurveTo(this.points[i].x, this.points[i].y, xc, yc) + } + this.ctx.quadraticCurveTo( + this.points[this.points.length - 2].x, + this.points[this.points.length - 2].y, + this.points[this.points.length - 1].x, + this.points[this.points.length - 1].y, + ) + this.ctx.lineTo(this.ctx.canvas.width, this.ctx.canvas.height) + this.ctx.lineTo(0, this.ctx.canvas.height) + this.ctx.closePath() + } + + private updateArrows() { + this.arrows.forEach((arrow) => { + arrow.opacity += arrow.direction * this.options.speedFactor + + if (arrow.opacity <= 0) { + arrow.opacity = 0 + arrow.direction = 1 + } else if (arrow.opacity >= 1) { + arrow.opacity = 1 + arrow.direction = -1 + } + }) + } + + private isBelowCurve(x, y) { + for (let i = 0; i < this.points.length - 1; i++) { + if (x >= this.points[i].x && x <= this.points[i + 1].x) { + const t = + (x - this.points[i].x) / (this.points[i + 1].x - this.points[i].x) + const curveY = (1 - t) * this.points[i].y + t * this.points[i + 1].y + return y > curveY + } + } + return false + } + + private getArrowRotationAngle() { + const angleMap: Record<"up" | "down", number> = { + up: 0, + down: 180, + } + const angle = angleMap[this.options.arrowOrientation] + return (angle * Math.PI) / 180 + } + + private drawArrows() { + this.arrows.forEach((arrow) => { + if ( + this.isBelowCurve( + arrow.x + this.gridWidth / 2, + arrow.y + this.gridHeight / 2, + ) + ) { + this.ctx.save() + this.ctx.globalAlpha = arrow.opacity + this.ctx.translate( + arrow.x + this.gridWidth / 2, + arrow.y + this.gridHeight / 2, + ) + this.ctx.strokeStyle = this.options.arrowColor + this.ctx.lineWidth = 3 + this.ctx.lineCap = "round" + this.ctx.lineJoin = "round" + this.ctx.rotate(this.getArrowRotationAngle()) + this.ctx.stroke(this.arrowPath) + this.ctx.restore() + } + }) + } + + animate() { + this.clearFrame() + this.updateCurve() + this.drawCurve() + this.updateArrows() + this.drawArrows() + requestAnimationFrame(this.animate.bind(this)) + } +} + +type ArrowAnimatedBackgroundProps = BoxProps + +function ArrowAnimatedBackground(props: ArrowAnimatedBackgroundProps) { + const containerRef = useRef(null) + const canvasRef = useRef(null) + + const [canvasWidth, setCanvasWidth] = useState(0) + const [canvasHeight, setCanvasHeight] = useState(0) + + useEffect(() => { + const ctx = canvasRef.current?.getContext("2d") + const scale = window.devicePixelRatio + if (ctx) ctx.scale(scale, scale) + + const resizeObserver = new ResizeObserver(([entry]) => { + const { width, height } = entry.contentRect + + const displayWidth = width * scale + const displayHeight = height * scale + + setCanvasWidth(displayWidth) + setCanvasHeight(displayHeight) + }) + + resizeObserver.observe(containerRef.current!) + + return () => resizeObserver.disconnect() + }, []) + + useLayoutEffect(() => { + const canvasElement = canvasRef.current + const ctx = canvasElement?.getContext("2d") + + if (!ctx) return + + const animation = new ArrowBackgroundAnimation(ctx) + + animation.animate() + }, []) + + return ( + + + + ) +} + +export default ArrowAnimatedBackground diff --git a/dapp/src/components/AcrePointsClaimModal/index.tsx b/dapp/src/components/AcrePointsClaimModal/index.tsx index a511a047f..61c868ba2 100644 --- a/dapp/src/components/AcrePointsClaimModal/index.tsx +++ b/dapp/src/components/AcrePointsClaimModal/index.tsx @@ -12,6 +12,7 @@ import { acrePoints as acrePointsUtils } from "#/utils" import withBaseModal from "../ModalRoot/withBaseModal" import { TextXl } from "../shared/Typography" import { AnimatedNumber } from "../shared/AnimatedNumber" +import ArrowAnimatedBackground from "./ArrowAnimatedBackground" const { getFormattedAmount } = acrePointsUtils @@ -26,7 +27,7 @@ const TRANSITION: Transition = { type: "spring", damping: 14, stiffness: 86, - delay: 4, // step duration + delay: 2, // step duration } const getStepOffsets = ( @@ -43,12 +44,7 @@ const getStepOffsets = ( ) export function AcrePointsClaimModalBase() { - const { - claimablePointsAmount, - totalPointsAmount, - rankPosition, - estimatedRankPosition, - } = useAcrePoints() + const { claimablePointsAmount, totalPointsAmount } = useAcrePoints() const formattedClaimablePointsAmount = getFormattedAmount( claimablePointsAmount, @@ -56,7 +52,6 @@ export function AcrePointsClaimModalBase() { const formattedUpdatedPointsAmount = getFormattedAmount( claimablePointsAmount + totalPointsAmount, ) - const rankPositionDifference = estimatedRankPosition - rankPosition const steps = useMemo<[string, ReactNode][]>( () => [ @@ -79,31 +74,26 @@ export function AcrePointsClaimModalBase() { indicationColor="brand.400" />, ], - [ - "Calculating rank...", - 0 ? "+" : "-"} - animateMode="whileInView" - color={rankPositionDifference > 0 ? "green.500" : "red.500"} - />, - ], - [ - "Updating rank...", - , - ], - ], - [ - formattedClaimablePointsAmount, - estimatedRankPosition, - formattedUpdatedPointsAmount, - rankPositionDifference, + // [ + // "Calculating rank...", + // 0 ? "+" : "-"} + // animateMode="whileInView" + // color={rankPositionDifference > 0 ? "green.500" : "red.500"} + // />, + // ], + // [ + // "Updating rank...", + // , + // ], ], + [formattedClaimablePointsAmount, formattedUpdatedPointsAmount], ) const containerHeight = useMotionValue(INITIAL_CONTAINER_HEIGHT) @@ -164,6 +154,8 @@ export function AcrePointsClaimModalBase() { ))}
+ + ) } diff --git a/dapp/src/constants/featureFlags.ts b/dapp/src/constants/featureFlags.ts index ba55436f9..3fe8c7bdd 100644 --- a/dapp/src/constants/featureFlags.ts +++ b/dapp/src/constants/featureFlags.ts @@ -13,12 +13,16 @@ const WITHDRAWALS_ENABLED = const BEEHIVE_COMPONENT_ENABLED = import.meta.env.VITE_FEATURE_FLAG_BEEHIVE_COMPONENT_ENABLED === "true" +const ACRE_POINTS_ENABLED = + import.meta.env.VITE_FEATURE_FLAG_ACRE_POINTS_ENABLED === "true" + const featureFlags = { GAMIFICATION_ENABLED, OKX_WALLET_ENABLED, XVERSE_WALLET_ENABLED, WITHDRAWALS_ENABLED, BEEHIVE_COMPONENT_ENABLED, + ACRE_POINTS_ENABLED, } export default featureFlags diff --git a/dapp/src/hooks/useAcrePoints.ts b/dapp/src/hooks/useAcrePoints.ts index 0f7d84f52..674f639c5 100644 --- a/dapp/src/hooks/useAcrePoints.ts +++ b/dapp/src/hooks/useAcrePoints.ts @@ -5,33 +5,33 @@ type UseAcrePointsReturnType = { dailyPointsAmount: number claimablePointsAmount: number nextDropTimestamp: number + lastClaimedTimestamp: number rankPosition: number estimatedRankPosition: number userName: string userId: number - lastClaimedTimestamp: number } export default function useAcrePoints(): UseAcrePointsReturnType { const totalPointsAmount = 2749993 // TODO: Fetch from the API - const dailyPointsAmount = 1201 // TODO: Fetch from the API + const dailyPointsAmount = 1201 // From deposit form as user input const claimablePointsAmount = 2402 // TODO: Fetch from the API - const nextDropTimestamp = 1634160000000 // TODO: Fetch from the API + const nextDropTimestamp = 1634160000000 // To be established + const lastClaimedTimestamp = 1634160000000 // TODO: Fetch from the API const rankPosition = 2082 // TODO: Fetch from the API const estimatedRankPosition = 2085 // TODO: Fetch from the API const userName = DEFAULT_USERNAME // Eventually an entry point for future implementation const userId = 8724 // TODO: Fetch from the API - const lastClaimedTimestamp = 1634160000000 // TODO: Fetch from the API return { totalPointsAmount, dailyPointsAmount, claimablePointsAmount, nextDropTimestamp, + lastClaimedTimestamp, rankPosition, estimatedRankPosition, userName, userId, - lastClaimedTimestamp, } } diff --git a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx index 5fe30893d..0c1c59d1b 100644 --- a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx +++ b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx @@ -24,6 +24,7 @@ export default function AcrePointsCard(props: CardProps) { totalPointsAmount, dailyPointsAmount, } = useAcrePoints() + const { openModal } = useModal() const handleClaim = () => { diff --git a/dapp/src/pages/DashboardPage/index.tsx b/dapp/src/pages/DashboardPage/index.tsx index 56c256ffe..9a8664b53 100644 --- a/dapp/src/pages/DashboardPage/index.tsx +++ b/dapp/src/pages/DashboardPage/index.tsx @@ -11,11 +11,9 @@ import { VStack } from "@chakra-ui/react" import CardButton from "#/components/shared/CardButton" import DashboardCard from "./DashboardCard" import { PageLayout, PageLayoutColumn } from "./PageLayout" -// import GrantedSeasonPassCard from "./GrantedSeasonPassCard" import AcrePointsCard from "./AcrePointsCard" import { CurrentSeasonCard } from "./CurrentSeasonCard" import BeehiveCard from "./BeehiveCard" -import AcreRankCard from "./AcreRankCard" export default function DashboardPage() { const isMobileMode = useMobileMode() @@ -32,14 +30,12 @@ export default function DashboardPage() { - {/* TODO: Uncomment in post-launch phases */} - {/* */} {featureFlags.BEEHIVE_COMPONENT_ENABLED && } - - + {featureFlags.ACRE_POINTS_ENABLED && } + Documentation diff --git a/dapp/src/vite-env.d.ts b/dapp/src/vite-env.d.ts index fdc08aae7..949f97916 100644 --- a/dapp/src/vite-env.d.ts +++ b/dapp/src/vite-env.d.ts @@ -12,6 +12,7 @@ interface ImportMetaEnv { readonly VITE_FEATURE_FLAG_OKX_WALLET_ENABLED: string readonly VITE_FEATURE_FLAG_XVERSE_WALLET_ENABLED: string readonly VITE_FEATURE_FLAG_BEEHIVE_COMPONENT_ENABLED: string + readonly VITE_FEATURE_FLAG_ACRE_POINTS_ENABLED: string readonly VITE_SUBGRAPH_API_KEY: string readonly VITE_MEZO_PORTAL_API_KEY: string readonly VITE_LATEST_COMMIT_HASH: string From c7a06268574cd33eba105ad8de9e487b7fb93c42 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Wed, 11 Sep 2024 23:07:23 +0200 Subject: [PATCH 11/61] Fix modal animation Add close button --- .../components/AcrePointsClaimModal/index.tsx | 65 ++++++++++++------- 1 file changed, 41 insertions(+), 24 deletions(-) diff --git a/dapp/src/components/AcrePointsClaimModal/index.tsx b/dapp/src/components/AcrePointsClaimModal/index.tsx index 61c868ba2..da84ec7b4 100644 --- a/dapp/src/components/AcrePointsClaimModal/index.tsx +++ b/dapp/src/components/AcrePointsClaimModal/index.tsx @@ -1,22 +1,20 @@ import React, { ReactNode, useEffect, useMemo } from "react" -import { useAcrePoints } from "#/hooks" -import { Box, ModalBody, Text, VStack } from "@chakra-ui/react" +import { useAcrePoints, useModal } from "#/hooks" +import { Box, Button, ModalBody, Text, VStack } from "@chakra-ui/react" import { AnimationSequence, motion, Transition, useAnimate, - useMotionValue, } from "framer-motion" import { acrePoints as acrePointsUtils } from "#/utils" import withBaseModal from "../ModalRoot/withBaseModal" import { TextXl } from "../shared/Typography" import { AnimatedNumber } from "../shared/AnimatedNumber" -import ArrowAnimatedBackground from "./ArrowAnimatedBackground" const { getFormattedAmount } = acrePointsUtils -const MotionVStack = motion(VStack) +const MotionBox = motion(Box) const INITIAL_CONTAINER_HEIGHT = 214 const CONTAINER_HEIGHT = 288 @@ -27,7 +25,7 @@ const TRANSITION: Transition = { type: "spring", damping: 14, stiffness: 86, - delay: 2, // step duration + delay: 4, // step duration } const getStepOffsets = ( @@ -96,7 +94,6 @@ export function AcrePointsClaimModalBase() { [formattedClaimablePointsAmount, formattedUpdatedPointsAmount], ) - const containerHeight = useMotionValue(INITIAL_CONTAINER_HEIGHT) const [scope, animate] = useAnimate() useEffect(() => { @@ -107,12 +104,22 @@ export function AcrePointsClaimModalBase() { const sequence = [ ["[data-steps-list]", { y: offsets[0] }, TRANSITION], - [containerHeight, CONTAINER_HEIGHT, { at: "<", ...TRANSITION }], + [ + "[data-container]", + { + clipPath: `polygon(0 0, 100% 0, 100% ${CONTAINER_HEIGHT}px, 0 ${CONTAINER_HEIGHT}px)`, + }, + { at: "<", ...TRANSITION }, + ], + [valueElements[0], { scale: VALUE_SCALE }, { at: "<", ...TRANSITION }], - ["[data-steps-list]", { y: offsets[1] }, TRANSITION], - [valueElements[1], { scale: VALUE_SCALE }, { at: "<", ...TRANSITION }], - ["[data-steps-list]", { y: offsets[2] }, TRANSITION], - [valueElements[2], { scale: VALUE_SCALE }, { at: "<", ...TRANSITION }], + ["[data-close-button]", { opacity: 1 }, { at: "<", ...TRANSITION }], + + // ["[data-steps-list]", { y: offsets[1] }, TRANSITION], + // [valueElements[1], { scale: VALUE_SCALE }, { at: "<", ...TRANSITION }], + + // ["[data-steps-list]", { y: offsets[2] }, TRANSITION], + // [valueElements[2], { scale: VALUE_SCALE }, { at: "<", ...TRANSITION }], ] as AnimationSequence const handleAnimation = async () => { @@ -121,16 +128,18 @@ export function AcrePointsClaimModalBase() { // eslint-disable-next-line no-void void handleAnimation() - }, [scope, animate, containerHeight, steps]) + }, [scope, animate, steps]) + + const { closeModal } = useModal() return ( - - - 32px - data-steps-list - style={{ height: containerHeight }} - > + + + {steps.map(([currentStepLabel, currentStepValue]) => ( ))} - - - - + + + + ) } From 1259eb71735e6355e70ef1694bc3de008a255ce8 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Wed, 11 Sep 2024 23:36:08 +0200 Subject: [PATCH 12/61] Add autoclose --- dapp/src/components/AcrePointsClaimModal/index.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dapp/src/components/AcrePointsClaimModal/index.tsx b/dapp/src/components/AcrePointsClaimModal/index.tsx index da84ec7b4..b1f973825 100644 --- a/dapp/src/components/AcrePointsClaimModal/index.tsx +++ b/dapp/src/components/AcrePointsClaimModal/index.tsx @@ -1,5 +1,5 @@ import React, { ReactNode, useEffect, useMemo } from "react" -import { useAcrePoints, useModal } from "#/hooks" +import { useAcrePoints, useModal, useTimeout } from "#/hooks" import { Box, Button, ModalBody, Text, VStack } from "@chakra-ui/react" import { AnimationSequence, @@ -8,6 +8,7 @@ import { useAnimate, } from "framer-motion" import { acrePoints as acrePointsUtils } from "#/utils" +import { ONE_SEC_IN_MILLISECONDS } from "#/constants" import withBaseModal from "../ModalRoot/withBaseModal" import { TextXl } from "../shared/Typography" import { AnimatedNumber } from "../shared/AnimatedNumber" @@ -27,6 +28,7 @@ const TRANSITION: Transition = { stiffness: 86, delay: 4, // step duration } +const AUTOCLOSE_DELAY = 12 * ONE_SEC_IN_MILLISECONDS const getStepOffsets = ( stepCount: number, @@ -132,6 +134,8 @@ export function AcrePointsClaimModalBase() { const { closeModal } = useModal() + useTimeout(closeModal, AUTOCLOSE_DELAY) + return ( + + {!isCofettiExploded && ( + + setIsCofettiExploded(true)} + /> + + )} ) } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c7bd3a524..d360dd316 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -80,6 +80,9 @@ importers: react: specifier: ^18.2.0 version: 18.3.1 + react-confetti-explosion: + specifier: ^2.1.2 + version: 2.1.2(react-dom@18.3.1)(react@18.3.1) react-dom: specifier: ^18.2.0 version: 18.3.1(react@18.3.1) @@ -3281,6 +3284,12 @@ packages: resolution: {integrity: sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==} dev: false + /@emotion/is-prop-valid@0.7.3: + resolution: {integrity: sha512-uxJqm/sqwXw3YPA5GXX365OBcJGFtxUVkB6WyezqFHlNe9jqUWH5ur2O2M8dGBz61kn1g3ZBlzUunFQXQIClhA==} + dependencies: + '@emotion/memoize': 0.7.1 + dev: false + /@emotion/is-prop-valid@0.8.8: resolution: {integrity: sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA==} requiresBuild: true @@ -3295,6 +3304,10 @@ packages: '@emotion/memoize': 0.8.1 dev: false + /@emotion/memoize@0.7.1: + resolution: {integrity: sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg==} + dev: false + /@emotion/memoize@0.7.4: resolution: {integrity: sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw==} requiresBuild: true @@ -10988,6 +11001,21 @@ packages: tiny-invariant: 1.3.3 dev: false + /css-jss@10.10.0: + resolution: {integrity: sha512-YyMIS/LsSKEGXEaVJdjonWe18p4vXLo8CMA4FrW/kcaEyqdIGKCFXao31gbJddXEdIxSXFFURWrenBJPlKTgAA==} + dependencies: + '@babel/runtime': 7.24.7 + jss: 10.10.0 + jss-preset-default: 10.10.0 + dev: false + + /css-vendor@2.0.8: + resolution: {integrity: sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==} + dependencies: + '@babel/runtime': 7.24.7 + is-in-browser: 1.1.3 + dev: false + /css-what@6.1.0: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} @@ -14129,6 +14157,10 @@ packages: engines: {node: '>=4'} dev: false + /hyphenate-style-name@1.1.0: + resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==} + dev: false + /i18next-browser-languagedetector@7.1.0: resolution: {integrity: sha512-cr2k7u1XJJ4HTOjM9GyOMtbOA47RtUoWRAtt52z43r3AoMs2StYKyjS3URPhzHaf+mn10hY9dZWamga5WPQjhA==} dependencies: @@ -14548,6 +14580,10 @@ packages: resolution: {integrity: sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==} engines: {node: '>=6.5.0', npm: '>=3'} + /is-in-browser@1.1.3: + resolution: {integrity: sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==} + dev: false + /is-inside-container@1.0.0: resolution: {integrity: sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==} engines: {node: '>=14.16'} @@ -15576,6 +15612,126 @@ packages: verror: 1.10.0 dev: false + /jss-plugin-camel-case@10.10.0: + resolution: {integrity: sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw==} + dependencies: + '@babel/runtime': 7.24.7 + hyphenate-style-name: 1.1.0 + jss: 10.10.0 + dev: false + + /jss-plugin-compose@10.10.0: + resolution: {integrity: sha512-F5kgtWpI2XfZ3Z8eP78tZEYFdgTIbpA/TMuX3a8vwrNolYtN1N4qJR/Ob0LAsqIwCMLojtxN7c7Oo/+Vz6THow==} + dependencies: + '@babel/runtime': 7.24.7 + jss: 10.10.0 + tiny-warning: 1.0.3 + dev: false + + /jss-plugin-default-unit@10.10.0: + resolution: {integrity: sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ==} + dependencies: + '@babel/runtime': 7.24.7 + jss: 10.10.0 + dev: false + + /jss-plugin-expand@10.10.0: + resolution: {integrity: sha512-ymT62W2OyDxBxr7A6JR87vVX9vTq2ep5jZLIdUSusfBIEENLdkkc0lL/Xaq8W9s3opUq7R0sZQpzRWELrfVYzA==} + dependencies: + '@babel/runtime': 7.24.7 + jss: 10.10.0 + dev: false + + /jss-plugin-extend@10.10.0: + resolution: {integrity: sha512-sKYrcMfr4xxigmIwqTjxNcHwXJIfvhvjTNxF+Tbc1NmNdyspGW47Ey6sGH8BcQ4FFQhLXctpWCQSpDwdNmXSwg==} + dependencies: + '@babel/runtime': 7.24.7 + jss: 10.10.0 + tiny-warning: 1.0.3 + dev: false + + /jss-plugin-global@10.10.0: + resolution: {integrity: sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A==} + dependencies: + '@babel/runtime': 7.24.7 + jss: 10.10.0 + dev: false + + /jss-plugin-nested@10.10.0: + resolution: {integrity: sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA==} + dependencies: + '@babel/runtime': 7.24.7 + jss: 10.10.0 + tiny-warning: 1.0.3 + dev: false + + /jss-plugin-props-sort@10.10.0: + resolution: {integrity: sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg==} + dependencies: + '@babel/runtime': 7.24.7 + jss: 10.10.0 + dev: false + + /jss-plugin-rule-value-function@10.10.0: + resolution: {integrity: sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g==} + dependencies: + '@babel/runtime': 7.24.7 + jss: 10.10.0 + tiny-warning: 1.0.3 + dev: false + + /jss-plugin-rule-value-observable@10.10.0: + resolution: {integrity: sha512-ZLMaYrR3QE+vD7nl3oNXuj79VZl9Kp8/u6A1IbTPDcuOu8b56cFdWRZNZ0vNr8jHewooEeq2doy8Oxtymr2ZPA==} + dependencies: + '@babel/runtime': 7.24.7 + jss: 10.10.0 + symbol-observable: 1.2.0 + dev: false + + /jss-plugin-template@10.10.0: + resolution: {integrity: sha512-ocXZBIOJOA+jISPdsgkTs8wwpK6UbsvtZK5JI7VUggTD6LWKbtoxUzadd2TpfF+lEtlhUmMsCkTRNkITdPKa6w==} + dependencies: + '@babel/runtime': 7.24.7 + jss: 10.10.0 + tiny-warning: 1.0.3 + dev: false + + /jss-plugin-vendor-prefixer@10.10.0: + resolution: {integrity: sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg==} + dependencies: + '@babel/runtime': 7.24.7 + css-vendor: 2.0.8 + jss: 10.10.0 + dev: false + + /jss-preset-default@10.10.0: + resolution: {integrity: sha512-GL175Wt2FGhjE+f+Y3aWh+JioL06/QWFgZp53CbNNq6ZkVU0TDplD8Bxm9KnkotAYn3FlplNqoW5CjyLXcoJ7Q==} + dependencies: + '@babel/runtime': 7.24.7 + jss: 10.10.0 + jss-plugin-camel-case: 10.10.0 + jss-plugin-compose: 10.10.0 + jss-plugin-default-unit: 10.10.0 + jss-plugin-expand: 10.10.0 + jss-plugin-extend: 10.10.0 + jss-plugin-global: 10.10.0 + jss-plugin-nested: 10.10.0 + jss-plugin-props-sort: 10.10.0 + jss-plugin-rule-value-function: 10.10.0 + jss-plugin-rule-value-observable: 10.10.0 + jss-plugin-template: 10.10.0 + jss-plugin-vendor-prefixer: 10.10.0 + dev: false + + /jss@10.10.0: + resolution: {integrity: sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw==} + dependencies: + '@babel/runtime': 7.24.7 + csstype: 3.1.3 + is-in-browser: 1.1.3 + tiny-warning: 1.0.3 + dev: false + /jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} @@ -17981,6 +18137,18 @@ packages: react: 18.3.1 dev: false + /react-confetti-explosion@2.1.2(react-dom@18.3.1)(react@18.3.1): + resolution: {integrity: sha512-4UzDFBajAGXmF9TSJoRMO2QOBCIXc66idTxH8l7Mkul48HLGtk+tMzK9HYDYsy7Zmw5sEGchi2fbn4AJUuLrZw==} + peerDependencies: + react: ^18.x + react-dom: ^18.x + dependencies: + lodash: 4.17.21 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-jss: 10.10.0(react@18.3.1) + dev: false + /react-devtools-core@5.2.0: resolution: {integrity: sha512-vZK+/gvxxsieAoAyYaiRIVFxlajb7KXhgBDV7OsoMzaAE+IqGpoxusBjIgq5ibqA2IloKu0p9n7tE68z1xs18A==} dependencies: @@ -17991,6 +18159,10 @@ packages: - utf-8-validate dev: false + /react-display-name@0.2.5: + resolution: {integrity: sha512-I+vcaK9t4+kypiSgaiVWAipqHRXYmZIuAiS8vzFvXHHXVigg/sMKwlRgLy6LH2i3rmP+0Vzfl5lFsFRwF1r3pg==} + dev: false + /react-dom@18.3.1(react@18.3.1): resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} peerDependencies: @@ -18059,6 +18231,25 @@ packages: /react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + /react-jss@10.10.0(react@18.3.1): + resolution: {integrity: sha512-WLiq84UYWqNBF6579/uprcIUnM1TSywYq6AIjKTTTG5ziJl9Uy+pwuvpN3apuyVwflMbD60PraeTKT7uWH9XEQ==} + peerDependencies: + react: '>=16.8.6' + dependencies: + '@babel/runtime': 7.24.7 + '@emotion/is-prop-valid': 0.7.3 + css-jss: 10.10.0 + hoist-non-react-statics: 3.3.2 + is-in-browser: 1.1.3 + jss: 10.10.0 + jss-preset-default: 10.10.0 + prop-types: 15.8.1 + react: 18.3.1 + shallow-equal: 1.2.1 + theming: 3.3.0(react@18.3.1) + tiny-warning: 1.0.3 + dev: false + /react-native-fetch-api@3.0.0: resolution: {integrity: sha512-g2rtqPjdroaboDKTsJCTlcmtw54E25OjyaunUP0anOZn4Fuo2IKs8BVfe02zVggA/UysbmfSnRJIqtNkAgggNA==} dependencies: @@ -19008,6 +19199,10 @@ packages: kind-of: 6.0.3 dev: false + /shallow-equal@1.2.1: + resolution: {integrity: sha512-S4vJDjHHMBaiZuT9NPb616CSmLf618jawtv3sufLl6ivK8WocjAo58cXwbRV1cgqxH0Qbv+iUt6m05eqEa2IRA==} + dev: false + /shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -19631,6 +19826,11 @@ packages: - utf-8-validate dev: false + /symbol-observable@1.2.0: + resolution: {integrity: sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==} + engines: {node: '>=0.10.0'} + dev: false + /sync-request@6.1.0: resolution: {integrity: sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==} engines: {node: '>=8.0.0'} @@ -19785,6 +19985,19 @@ packages: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} dev: true + /theming@3.3.0(react@18.3.1): + resolution: {integrity: sha512-u6l4qTJRDaWZsqa8JugaNt7Xd8PPl9+gonZaIe28vAhqgHMIG/DOyFPqiKN/gQLQYj05tHv+YQdNILL4zoiAVA==} + engines: {node: '>=8'} + peerDependencies: + react: '>=16.3' + dependencies: + hoist-non-react-statics: 3.3.2 + prop-types: 15.8.1 + react: 18.3.1 + react-display-name: 0.2.5 + tiny-warning: 1.0.3 + dev: false + /then-request@6.0.2: resolution: {integrity: sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==} engines: {node: '>=6.0.0'} From 4a82f7179810434dea88c373ca2effe4ed31bc16 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Thu, 12 Sep 2024 00:04:06 +0200 Subject: [PATCH 14/61] Remove daily points displayed --- dapp/src/pages/DashboardPage/AcrePointsCard.tsx | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx index 0c1c59d1b..58d06e9f2 100644 --- a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx +++ b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx @@ -18,12 +18,8 @@ import { acrePoints as acrePointsUtils } from "#/utils" const { getFormattedAmount } = acrePointsUtils export default function AcrePointsCard(props: CardProps) { - const { - claimablePointsAmount, - nextDropTimestamp, - totalPointsAmount, - dailyPointsAmount, - } = useAcrePoints() + const { claimablePointsAmount, nextDropTimestamp, totalPointsAmount } = + useAcrePoints() const { openModal } = useModal() @@ -33,7 +29,6 @@ export default function AcrePointsCard(props: CardProps) { } const formattedTotalPointsAmount = getFormattedAmount(totalPointsAmount) - const formattedDailyPointsAmount = getFormattedAmount(dailyPointsAmount) const formattedClaimablePointsAmount = getFormattedAmount( claimablePointsAmount, ) @@ -53,9 +48,6 @@ export default function AcrePointsCard(props: CardProps) {

{formattedTotalPointsAmount} PTS

- - + {formattedDailyPointsAmount} PTS/day - From b42cd15c8cc787f8e7523d2c33a9fc7dc8afe239 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Thu, 12 Sep 2024 15:21:35 +0200 Subject: [PATCH 15/61] Integrate dApp with Acre Points API --- dapp/.env | 2 +- .../components/AcrePointsClaimModal/index.tsx | 32 +++++---- .../AcrePointsRewardEstimation.tsx | 13 ++-- dapp/src/components/shared/Countdown.tsx | 4 +- dapp/src/constants/queryKeysFactory.ts | 4 ++ dapp/src/hooks/useAcrePoints.ts | 72 ++++++++++++------- .../pages/DashboardPage/AcrePointsCard.tsx | 52 ++++++++------ dapp/src/theme/Modal.ts | 5 ++ dapp/src/utils/acrePoints.ts | 46 +++++++++++- dapp/src/utils/numbers.ts | 6 +- 10 files changed, 166 insertions(+), 70 deletions(-) diff --git a/dapp/.env b/dapp/.env index 9e65b4df4..e3556acda 100644 --- a/dapp/.env +++ b/dapp/.env @@ -10,7 +10,7 @@ VITE_ETH_HOSTNAME_HTTP="https://sepolia.infura.io/v3/c80e8ccdcc4c4a809bce4fc1653 VITE_REFERRAL=0 # TODO: Set this env variable in CI. -VITE_TBTC_API_ENDPOINT="" +VITE_TBTC_API_ENDPOINT="https://tbtc.api.test.acre.fi/tbtc-api/v1/" # API KEYS VITE_GELATO_RELAY_API_KEY="htaJCy_XHj8WsE3w53WBMurfySDtjLP_TrNPPa6IPIc_" # this key should not be used on production diff --git a/dapp/src/components/AcrePointsClaimModal/index.tsx b/dapp/src/components/AcrePointsClaimModal/index.tsx index cc753952f..0f09cf2b8 100644 --- a/dapp/src/components/AcrePointsClaimModal/index.tsx +++ b/dapp/src/components/AcrePointsClaimModal/index.tsx @@ -7,7 +7,7 @@ import { Transition, useAnimate, } from "framer-motion" -import { acrePoints as acrePointsUtils } from "#/utils" +import { acrePoints as acrePointsUtils, logPromiseFailure } from "#/utils" import { ONE_SEC_IN_MILLISECONDS } from "#/constants" import ConfettiExplosion from "react-confetti-explosion" import withBaseModal from "../ModalRoot/withBaseModal" @@ -46,14 +46,14 @@ const getStepOffsets = ( ) export function AcrePointsClaimModalBase() { - const { claimablePointsAmount, totalPointsAmount } = useAcrePoints() + const { + claimableBalance: claimedPointsAmount, + totalBalance, + updateBalance, + } = useAcrePoints() - const formattedClaimablePointsAmount = getFormattedAmount( - claimablePointsAmount, - ) - const formattedUpdatedPointsAmount = getFormattedAmount( - claimablePointsAmount + totalPointsAmount, - ) + const formattedClaimablePointsAmount = getFormattedAmount(claimedPointsAmount) + const formattedTotalPointsAmount = getFormattedAmount(totalBalance) const steps = useMemo<[string, ReactNode][]>( () => [ @@ -70,7 +70,7 @@ export function AcrePointsClaimModalBase() { [ "Updating points balance...", , // ], ], - [formattedClaimablePointsAmount, formattedUpdatedPointsAmount], + [formattedClaimablePointsAmount, formattedTotalPointsAmount], ) const [scope, animate] = useAnimate() @@ -136,9 +136,15 @@ export function AcrePointsClaimModalBase() { const { closeModal } = useModal() - useTimeout(closeModal, AUTOCLOSE_DELAY) const [isCofettiExploded, setIsCofettiExploded] = useState(false) + const handleClose = () => { + logPromiseFailure(updateBalance()) + closeModal() + } + + useTimeout(handleClose, AUTOCLOSE_DELAY) + return ( @@ -205,6 +211,6 @@ export function AcrePointsClaimModalBase() { const AcrePointsClaimModal = withBaseModal(AcrePointsClaimModalBase, { returnFocusOnClose: false, variant: "unstyled", - size: "xl", + size: "full", }) export default AcrePointsClaimModal diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/AcrePointsRewardEstimation.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/AcrePointsRewardEstimation.tsx index 8d05a7b7c..57616429c 100644 --- a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/AcrePointsRewardEstimation.tsx +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/AcrePointsRewardEstimation.tsx @@ -11,12 +11,12 @@ import { } from "@chakra-ui/react" import { H4, TextMd } from "#/components/shared/Typography" import { AcrePointsClaimTier } from "#/types" -import { useAcrePoints } from "#/hooks" import { acrePoints, numberToLocaleString } from "#/utils" import { IconChevronDown } from "@tabler/icons-react" import { ACRE_POINTS_TIER_LABELS } from "#/constants" +import { useTokenAmountField } from "#/components/shared/TokenAmountForm/TokenAmountFormBase" -const { estimateRewardAmountPerTier } = acrePoints +const { estimateRewardAmountPerTier, getPointsFromAmount } = acrePoints function AcrePointsRewardEstimation(props: StackProps) { const [selectedTierItem, setSelectedTierItem] = useState( @@ -34,11 +34,12 @@ function AcrePointsRewardEstimation(props: StackProps) { .map(([, tierLabel]) => tierLabel), ] - const { data } = useAcrePoints() + const { value = 0n } = useTokenAmountField() + const baseReward = getPointsFromAmount(value) const estimatedReward = useMemo( - () => estimateRewardAmountPerTier(data.dailyPointsAmount, selectedTierItem), - [data, selectedTierItem], + () => estimateRewardAmountPerTier(baseReward, selectedTierItem), + [baseReward, selectedTierItem], ) return ( @@ -50,6 +51,7 @@ function AcrePointsRewardEstimation(props: StackProps) { {({ isOpen }) => ( <> {tierItems.map((tierItem) => ( void } & BoxProps function Countdown(props: CountdownProps) { @@ -16,12 +17,13 @@ function Countdown(props: CountdownProps) { addLeadingZeros = true, units = ["hours", "minutes", "seconds"], size = "md", + onCountdownEnd, ...restProps } = props const styles = useMultiStyleConfig("Countdown", { size }) - const timeUnits = useCountdown(timestamp, addLeadingZeros) + const timeUnits = useCountdown(timestamp, addLeadingZeros, onCountdownEnd) return ( diff --git a/dapp/src/constants/queryKeysFactory.ts b/dapp/src/constants/queryKeysFactory.ts index b4aba9684..0b94d5119 100644 --- a/dapp/src/constants/queryKeysFactory.ts +++ b/dapp/src/constants/queryKeysFactory.ts @@ -8,6 +8,10 @@ const acreKeys = { all: ["acre"] as const, totalAssets: () => [...acreKeys.all, "total-assets"] as const, mats: () => [...acreKeys.all, "mats"] as const, + claimedAcrePoints: () => [...acreKeys.all, "claimed-acre-points"] as const, + claimEligibilityStatus: () => + [...acreKeys.all, "claim-eligibility-status"] as const, + pointsDropTime: () => [...acreKeys.all, "points-drop-time"] as const, } export default { diff --git a/dapp/src/hooks/useAcrePoints.ts b/dapp/src/hooks/useAcrePoints.ts index 674f639c5..a01d43621 100644 --- a/dapp/src/hooks/useAcrePoints.ts +++ b/dapp/src/hooks/useAcrePoints.ts @@ -1,37 +1,55 @@ -const DEFAULT_USERNAME = "Stacrer" +import { useMutation, useQuery } from "@tanstack/react-query" +import { + acrePoints as acrePointsUtils, + bigIntToUserAmount, + dateToUnixTimestamp, +} from "#/utils" +import { queryKeysFactory } from "#/constants" +import { useWallet } from "./useWallet" + +const { acreKeys } = queryKeysFactory type UseAcrePointsReturnType = { - totalPointsAmount: number - dailyPointsAmount: number - claimablePointsAmount: number - nextDropTimestamp: number - lastClaimedTimestamp: number - rankPosition: number - estimatedRankPosition: number - userName: string - userId: number + totalBalance: number + claimableBalance: number + nextDropTimestamp?: number + handleClaim: () => void + updateBalance: () => Promise } export default function useAcrePoints(): UseAcrePointsReturnType { - const totalPointsAmount = 2749993 // TODO: Fetch from the API - const dailyPointsAmount = 1201 // From deposit form as user input - const claimablePointsAmount = 2402 // TODO: Fetch from the API - const nextDropTimestamp = 1634160000000 // To be established - const lastClaimedTimestamp = 1634160000000 // TODO: Fetch from the API - const rankPosition = 2082 // TODO: Fetch from the API - const estimatedRankPosition = 2085 // TODO: Fetch from the API - const userName = DEFAULT_USERNAME // Eventually an entry point for future implementation - const userId = 8724 // TODO: Fetch from the API + const { address = "" } = useWallet() + + const acrePointsQuery = useQuery({ + queryKey: [...acreKeys.claimedAcrePoints(), address], + queryFn: async () => acrePointsUtils.getAcrePoints(address), + }) + + const { mutate: handleClaim } = useMutation({ + mutationFn: async () => acrePointsUtils.handleClaimAcrePoints(address), + onSettled: async () => { + await acrePointsQuery.refetch() + }, + }) + + const totalBalance = bigIntToUserAmount( + BigInt(acrePointsQuery.data?.claimed ?? 0), + 0, + ) + const claimableBalance = bigIntToUserAmount( + BigInt(acrePointsQuery.data?.unclaimed ?? 0), + 0, + ) + + const nextDropTimestamp = dateToUnixTimestamp( + new Date(acrePointsQuery.data?.dropAt ?? 0), + ) return { - totalPointsAmount, - dailyPointsAmount, - claimablePointsAmount, + totalBalance, + claimableBalance, nextDropTimestamp, - lastClaimedTimestamp, - rankPosition, - estimatedRankPosition, - userName, - userId, + handleClaim, + updateBalance: acrePointsQuery.refetch, } } diff --git a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx index 58d06e9f2..b3bdd87b1 100644 --- a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx +++ b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx @@ -13,25 +13,32 @@ import Countdown from "#/components/shared/Countdown" import useAcrePoints from "#/hooks/useAcrePoints" import { MODAL_TYPES } from "#/types" import { useModal } from "#/hooks" -import { acrePoints as acrePointsUtils } from "#/utils" +import { acrePoints as acrePointsUtils, logPromiseFailure } from "#/utils" const { getFormattedAmount } = acrePointsUtils export default function AcrePointsCard(props: CardProps) { - const { claimablePointsAmount, nextDropTimestamp, totalPointsAmount } = - useAcrePoints() + const { + claimableBalance, + nextDropTimestamp, + totalBalance, + handleClaim, + updateBalance, + } = useAcrePoints() const { openModal } = useModal() - const handleClaim = () => { - // TODO: Call API endpoint to claim points + const onClaimButtonClick = () => { + handleClaim() openModal(MODAL_TYPES.ACRE_POINTS_CLAIM) } - const formattedTotalPointsAmount = getFormattedAmount(totalPointsAmount) - const formattedClaimablePointsAmount = getFormattedAmount( - claimablePointsAmount, - ) + const formattedTotalPointsAmount = getFormattedAmount(totalBalance) + const formattedClaimablePointsAmount = getFormattedAmount(claimableBalance) + + const handleOnCountdownEnd = () => { + logPromiseFailure(updateBalance()) + } return ( {formattedTotalPointsAmount} PTS - - Next drop in - - + {nextDropTimestamp && ( + + Next drop in + + + )} - {claimablePointsAmount && ( + {claimableBalance && ( + +
+
+
+ ) +} diff --git a/dapp/src/pages/DashboardPage/index.tsx b/dapp/src/pages/DashboardPage/index.tsx index 57d7376cc..40a9f5925 100644 --- a/dapp/src/pages/DashboardPage/index.tsx +++ b/dapp/src/pages/DashboardPage/index.tsx @@ -8,6 +8,7 @@ import AcrePointsCard from "./AcrePointsCard" import { CurrentSeasonCard } from "./CurrentSeasonCard" import BeehiveCard from "./BeehiveCard" import UsefulLinks from "./UsefulLinks" +import AcrePointsTemplateCard from "./AcrePointsTemplateCard" export default function DashboardPage() { const isMobileMode = useMobileMode() @@ -29,8 +30,11 @@ export default function DashboardPage() { - {featureFlags.ACRE_POINTS_ENABLED && } - + {featureFlags.ACRE_POINTS_ENABLED ? ( + + ) : ( + + )} From c810a834c5fa29de27b9c2e8b1d7b9bef806d3f8 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 17 Sep 2024 11:06:52 +0200 Subject: [PATCH 34/61] Update query key names for the user --- dapp/src/constants/queryKeysFactory.ts | 2 +- dapp/src/hooks/useAcrePoints.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dapp/src/constants/queryKeysFactory.ts b/dapp/src/constants/queryKeysFactory.ts index e8d486248..e32ca67a5 100644 --- a/dapp/src/constants/queryKeysFactory.ts +++ b/dapp/src/constants/queryKeysFactory.ts @@ -2,7 +2,7 @@ const userKeys = { all: ["user"] as const, balance: () => [...userKeys.all, "balance"] as const, position: () => [...userKeys.all, "position"] as const, - claimedAcrePoints: () => [...userKeys.all, "claimed-acre-points"] as const, + pointsData: () => [...userKeys.all, "points-data"] as const, } const acreKeys = { diff --git a/dapp/src/hooks/useAcrePoints.ts b/dapp/src/hooks/useAcrePoints.ts index 2f1ba0781..1a7cb8a24 100644 --- a/dapp/src/hooks/useAcrePoints.ts +++ b/dapp/src/hooks/useAcrePoints.ts @@ -18,7 +18,7 @@ export default function useAcrePoints(): UseAcrePointsReturnType { const { address = "" } = useWallet() const userPointsDataQuery = useQuery({ - queryKey: [...userKeys.claimedAcrePoints(), address], + queryKey: [...userKeys.pointsData(), address], enabled: !!address, queryFn: async () => acreApi.getPointsDataByUser(address), }) From b945b43e4b6b61642f4069974084c40007be23d8 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 17 Sep 2024 11:08:59 +0200 Subject: [PATCH 35/61] Remove the empty line --- dapp/.env | 1 - 1 file changed, 1 deletion(-) diff --git a/dapp/.env b/dapp/.env index 052dedf5d..60e4f4a3b 100644 --- a/dapp/.env +++ b/dapp/.env @@ -20,7 +20,6 @@ VITE_SUBGRAPH_API_KEY="" # Get the API key from: 1Password. VITE_MEZO_PORTAL_API_KEY="" - # Feature flags VITE_FEATURE_FLAG_GAMIFICATION_ENABLED="false" VITE_FEATURE_FLAG_WITHDRAWALS_ENABLED="false" From 4306454fdda65d222436ed6c22e1eb72188a13f6 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 17 Sep 2024 12:52:52 +0200 Subject: [PATCH 36/61] Refactor for `AcrePointsRewardEstimation` component --- .../AcrePointsRewardEstimation.tsx | 51 ++++++++++--------- dapp/src/constants/acrePoints.ts | 16 ------ dapp/src/constants/index.ts | 1 - dapp/src/constants/time.ts | 4 ++ dapp/src/types/acrePoints.ts | 5 -- dapp/src/types/index.ts | 1 - 6 files changed, 32 insertions(+), 46 deletions(-) delete mode 100644 dapp/src/constants/acrePoints.ts delete mode 100644 dapp/src/types/acrePoints.ts diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/AcrePointsRewardEstimation.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/AcrePointsRewardEstimation.tsx index a33db6bb6..a4e1a05bb 100644 --- a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/AcrePointsRewardEstimation.tsx +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/AcrePointsRewardEstimation.tsx @@ -10,44 +10,49 @@ import { VStack, } from "@chakra-ui/react" import { H4, TextMd } from "#/components/shared/Typography" -import { AcrePointsClaimTier } from "#/types" import { numberToLocaleString } from "#/utils" import { IconChevronDown } from "@tabler/icons-react" +import { useTokenAmountField } from "#/components/shared/TokenAmountForm/TokenAmountFormBase" import { - ACRE_POINTS_REWARDS_MULTIPLERS, - ACRE_POINTS_TIER_LABELS, + ONE_MONTH_IN_DAYS, + ONE_WEEK_IN_DAYS, + ONE_YEAR_IN_DAYS, } from "#/constants" -import { useTokenAmountField } from "#/components/shared/TokenAmountForm/TokenAmountFormBase" -const estimateRewardAmountPerTier = ( - baseReward: number, - tier: AcrePointsClaimTier, -) => { - const multipler = ACRE_POINTS_REWARDS_MULTIPLERS[tier] - return baseReward * multipler +const ACRE_POINTS_DATA = { + week: { + label: "Per week", + multipler: ONE_WEEK_IN_DAYS, + }, + month: { + label: "Per month", + multipler: ONE_MONTH_IN_DAYS, + }, + year: { + label: "Per year", + multipler: ONE_YEAR_IN_DAYS, + }, } function AcrePointsRewardEstimation(props: StackProps) { - const [selectedTierItem, setSelectedTierItem] = useState( - AcrePointsClaimTier.Weekly, - ) - const selectedTierItemLabel = useMemo( - () => ACRE_POINTS_TIER_LABELS[selectedTierItem], - [selectedTierItem], + const [selectedTierItem, setSelectedTierItem] = useState( + ACRE_POINTS_DATA.week, ) const tierItems = [ selectedTierItem, - ...Object.entries(AcrePointsClaimTier) - .filter(([, tierValue]) => tierValue !== selectedTierItem) - .map(([, tierLabel]) => tierLabel), + ...Object.values(ACRE_POINTS_DATA).filter( + ({ label, multipler }) => + label !== selectedTierItem.label && + multipler !== selectedTierItem.multipler, + ), ] const { value = 0n } = useTokenAmountField() const baseReward = Number(value) const estimatedReward = useMemo( - () => estimateRewardAmountPerTier(baseReward, selectedTierItem), + () => selectedTierItem.multipler * baseReward, [baseReward, selectedTierItem], ) @@ -69,7 +74,7 @@ function AcrePointsRewardEstimation(props: StackProps) { _hover={{ bg: "gold.200" }} > - {selectedTierItemLabel} + {selectedTierItem.label} setSelectedTierItem(tierItem)} fontWeight="semibold" > - {ACRE_POINTS_TIER_LABELS[tierItem]} + {tierItem.label} ))} diff --git a/dapp/src/constants/acrePoints.ts b/dapp/src/constants/acrePoints.ts deleted file mode 100644 index 3fdf5a6cf..000000000 --- a/dapp/src/constants/acrePoints.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { AcrePointsClaimTier } from "#/types" - -export const ACRE_POINTS_REWARDS_MULTIPLERS: Record< - AcrePointsClaimTier, - number -> = { - WEEKLY: 7, - MONTHLY: 30, - YEARLY: 365, -} - -export const ACRE_POINTS_TIER_LABELS: Record = { - WEEKLY: "Per week", - MONTHLY: "Per month", - YEARLY: "Per year", -} diff --git a/dapp/src/constants/index.ts b/dapp/src/constants/index.ts index 56436f804..122333c6f 100644 --- a/dapp/src/constants/index.ts +++ b/dapp/src/constants/index.ts @@ -11,4 +11,3 @@ export * from "./staking" export { default as tbtc } from "./tbtc" export * from "./time" export { default as wallets } from "./wallets" -export * from "./acrePoints" diff --git a/dapp/src/constants/time.ts b/dapp/src/constants/time.ts index 0620f3abd..1fc712364 100644 --- a/dapp/src/constants/time.ts +++ b/dapp/src/constants/time.ts @@ -6,6 +6,10 @@ export const ONE_WEEK_IN_SECONDS = ONE_DAY_IN_SECONDS * 7 export const ONE_MONTH_IN_SECONDS = ONE_DAY_IN_SECONDS * 30 export const ONE_YEAR_IN_SECONDS = ONE_DAY_IN_SECONDS * 365 +export const ONE_WEEK_IN_DAYS = 7 +export const ONE_MONTH_IN_DAYS = 30 +export const ONE_YEAR_IN_DAYS = 365 + export const REFETCH_INTERVAL_IN_MILLISECONDS = ONE_SEC_IN_MILLISECONDS * ONE_MINUTE_IN_SECONDS * 5 diff --git a/dapp/src/types/acrePoints.ts b/dapp/src/types/acrePoints.ts deleted file mode 100644 index 4c3b50cc2..000000000 --- a/dapp/src/types/acrePoints.ts +++ /dev/null @@ -1,5 +0,0 @@ -export enum AcrePointsClaimTier { - Weekly = "WEEKLY", - Monthly = "MONTHLY", - Yearly = "YEARLY", -} diff --git a/dapp/src/types/index.ts b/dapp/src/types/index.ts index cc5ab571d..5411acd06 100644 --- a/dapp/src/types/index.ts +++ b/dapp/src/types/index.ts @@ -14,4 +14,3 @@ export * from "./eip1193" export * from "./error" export * from "./status" export * from "./orangekit" -export * from "./acrePoints" From fff6c021df9c69efa80a059d86203015dcf5a4e0 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 17 Sep 2024 12:57:16 +0200 Subject: [PATCH 37/61] Update a default `desiredDecimals` for `numberToLocaleString` --- dapp/src/components/AcrePointsClaimModal/index.tsx | 7 ++----- dapp/src/components/MezoBeehiveModal.tsx | 4 +--- .../StakeFormModal/AcrePointsRewardEstimation.tsx | 2 +- dapp/src/pages/DashboardPage/AcrePointsCard.tsx | 7 ++----- dapp/src/pages/DashboardPage/BeehiveCard.tsx | 2 +- dapp/src/pages/DashboardPage/CurrentSeasonCard.tsx | 2 +- dapp/src/utils/numbers.ts | 2 +- 7 files changed, 9 insertions(+), 17 deletions(-) diff --git a/dapp/src/components/AcrePointsClaimModal/index.tsx b/dapp/src/components/AcrePointsClaimModal/index.tsx index 283197da9..a6834bfcc 100644 --- a/dapp/src/components/AcrePointsClaimModal/index.tsx +++ b/dapp/src/components/AcrePointsClaimModal/index.tsx @@ -50,13 +50,10 @@ export function AcrePointsClaimModalBase() { updateUserPointsData, } = useAcrePoints() - const formattedClaimablePointsAmount = numberToLocaleString( - claimedPointsAmount, - 0, - ) + const formattedClaimablePointsAmount = + numberToLocaleString(claimedPointsAmount) const formattedTotalPointsAmount = numberToLocaleString( totalBalance + claimedPointsAmount, - 0, ) const steps = useMemo<[string, ReactNode][]>( diff --git a/dapp/src/components/MezoBeehiveModal.tsx b/dapp/src/components/MezoBeehiveModal.tsx index 645f8f7d5..2a3f50834 100644 --- a/dapp/src/components/MezoBeehiveModal.tsx +++ b/dapp/src/components/MezoBeehiveModal.tsx @@ -46,9 +46,7 @@ function MezoBeehiveModalBase() { {data && ( -
- {numberToLocaleString(data.totalMats, 0)} -
+
{numberToLocaleString(data.totalMats)}
MATS
)} diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/AcrePointsRewardEstimation.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/AcrePointsRewardEstimation.tsx index a4e1a05bb..6d6deeb00 100644 --- a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/AcrePointsRewardEstimation.tsx +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/AcrePointsRewardEstimation.tsx @@ -120,7 +120,7 @@ function AcrePointsRewardEstimation(props: StackProps) {
-

+{numberToLocaleString(estimatedReward, 0)} PTS

+

+{numberToLocaleString(estimatedReward)} PTS

) } diff --git a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx index b655be9d0..0cbec3b0f 100644 --- a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx +++ b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx @@ -32,11 +32,8 @@ export default function AcrePointsCard(props: CardProps) { openModal(MODAL_TYPES.ACRE_POINTS_CLAIM) } - const formattedTotalPointsAmount = numberToLocaleString(totalBalance, 0) - const formattedClaimablePointsAmount = numberToLocaleString( - claimableBalance, - 0, - ) + const formattedTotalPointsAmount = numberToLocaleString(totalBalance) + const formattedClaimablePointsAmount = numberToLocaleString(claimableBalance) const handleOnCountdownEnd = () => { logPromiseFailure(updatePointsData()) diff --git a/dapp/src/pages/DashboardPage/BeehiveCard.tsx b/dapp/src/pages/DashboardPage/BeehiveCard.tsx index 4704b1b00..34d7b6c6e 100644 --- a/dapp/src/pages/DashboardPage/BeehiveCard.tsx +++ b/dapp/src/pages/DashboardPage/BeehiveCard.tsx @@ -107,7 +107,7 @@ export default function BeehiveCard(props: CardProps) { {data && (
- {numberToLocaleString(data.totalMats, 0)} + {numberToLocaleString(data.totalMats)}
MATS
diff --git a/dapp/src/pages/DashboardPage/CurrentSeasonCard.tsx b/dapp/src/pages/DashboardPage/CurrentSeasonCard.tsx index 7f757b725..749c5dba0 100644 --- a/dapp/src/pages/DashboardPage/CurrentSeasonCard.tsx +++ b/dapp/src/pages/DashboardPage/CurrentSeasonCard.tsx @@ -58,7 +58,7 @@ export function CurrentSeasonCard(props: CurrentSeasonCardProps) { Total joined  - {numberToLocaleString(totalJoined)} + {numberToLocaleString(totalJoined, 2)} diff --git a/dapp/src/utils/numbers.ts b/dapp/src/utils/numbers.ts index f0c10d2f1..0de6c8d4a 100644 --- a/dapp/src/utils/numbers.ts +++ b/dapp/src/utils/numbers.ts @@ -4,7 +4,7 @@ export function roundUp(amount: number, desiredDecimals = 2): number { export const numberToLocaleString = ( value: string | number, - desiredDecimals = 2, + desiredDecimals = 0, ): string => { const number = typeof value === "number" ? value : parseFloat(value) From 0bec1554c0fd578f2fe1610b6046b2df8e020270 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 17 Sep 2024 13:12:26 +0200 Subject: [PATCH 38/61] Remove unneeded component - `AcreRankCard` --- dapp/src/pages/DashboardPage/AcreRankCard.tsx | 113 ------------------ dapp/src/utils/time.ts | 5 - 2 files changed, 118 deletions(-) delete mode 100644 dapp/src/pages/DashboardPage/AcreRankCard.tsx diff --git a/dapp/src/pages/DashboardPage/AcreRankCard.tsx b/dapp/src/pages/DashboardPage/AcreRankCard.tsx deleted file mode 100644 index 9047ba082..000000000 --- a/dapp/src/pages/DashboardPage/AcreRankCard.tsx +++ /dev/null @@ -1,113 +0,0 @@ -import React from "react" -import { TextMd, TextXl } from "#/components/shared/Typography" -import { - Card, - CardBody, - CardHeader, - CardProps, - Flex, - HStack, - Icon, - Tag, - TagLabel, -} from "@chakra-ui/react" -import UserDataSkeleton from "#/components/shared/UserDataSkeleton" -import ButtonLink from "#/components/shared/ButtonLink" -import { IconTrendingDown, IconTrendingUp } from "@tabler/icons-react" -import { isWithinPeriod } from "#/utils" -import { ONE_SEC_IN_MILLISECONDS, ONE_WEEK_IN_SECONDS } from "#/constants" - -type AcreRankCardProps = CardProps & { - withTrendingIcon?: boolean -} - -export default function AcreRankCard(props: AcreRankCardProps) { - const { withTrendingIcon = true, ...restProps } = props - - // TODO: To implement - const lastClaimedTimestamp = 0 - const estimatedRankPosition = 0 - const rankPosition = 0 - const userId = 0 - const userName = "" - - const hasClaimedLastWeek = isWithinPeriod( - lastClaimedTimestamp, - ONE_WEEK_IN_SECONDS * ONE_SEC_IN_MILLISECONDS, - ) - const isRankTrendingUp = estimatedRankPosition > rankPosition - - return ( - - - - Your rank - - - - Leaderboard - - - - - - - - #{rankPosition} - - {withTrendingIcon && hasClaimedLastWeek && ( - - )} - - - - {userName} - - - - #{userId} - - - - - - - - ) -} diff --git a/dapp/src/utils/time.ts b/dapp/src/utils/time.ts index fd1786440..bd40d351d 100644 --- a/dapp/src/utils/time.ts +++ b/dapp/src/utils/time.ts @@ -94,8 +94,3 @@ export const getExpirationTimestamp = (duration: number, startDate?: Date) => { return dateToUnixTimestamp(expirationDate) } - -export const isWithinPeriod = ( - timestamp: number, - periodInMilliseconds: number, -) => Date.now() - timestamp <= periodInMilliseconds && timestamp <= Date.now() From fd55e216c977ed94c72aeeebc61f3d35e4e43ebb Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 17 Sep 2024 13:52:49 +0200 Subject: [PATCH 39/61] Add missing `key` --- .../shared/AnimatedNumber/AnimatedNumber.tsx | 29 ++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/dapp/src/components/shared/AnimatedNumber/AnimatedNumber.tsx b/dapp/src/components/shared/AnimatedNumber/AnimatedNumber.tsx index 2a61007a2..e08595fe3 100644 --- a/dapp/src/components/shared/AnimatedNumber/AnimatedNumber.tsx +++ b/dapp/src/components/shared/AnimatedNumber/AnimatedNumber.tsx @@ -50,19 +50,22 @@ function AnimatedNumber(props: AnimatedNumberProps) { > {suffix && {suffix}} - {numberSegments.map((segment, index) => - isSeparatorSegment(segment) ? ( - {segment} - ) : ( - - ), - )} + {numberSegments.map((segment, index) => { + const key = `${segment}-${index}` + return ( + + {isSeparatorSegment(segment) ? ( + {segment} + ) : ( + + )} + + ) + })} {prefix && {prefix}} From 3c5eb761291e4c443bb04bbd9a0d5e26315743a3 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 17 Sep 2024 14:01:16 +0200 Subject: [PATCH 40/61] Fix an error of nested `
` in `

` --- dapp/src/components/AcrePointsClaimModal/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dapp/src/components/AcrePointsClaimModal/index.tsx b/dapp/src/components/AcrePointsClaimModal/index.tsx index a6834bfcc..adf8710f2 100644 --- a/dapp/src/components/AcrePointsClaimModal/index.tsx +++ b/dapp/src/components/AcrePointsClaimModal/index.tsx @@ -1,6 +1,6 @@ import React, { ReactNode, useEffect, useMemo, useState } from "react" import { useAcrePoints, useModal, useTimeout } from "#/hooks" -import { Box, Button, ModalBody, Text, VStack } from "@chakra-ui/react" +import { Box, Button, ModalBody, VStack } from "@chakra-ui/react" import { AnimationSequence, motion, @@ -163,7 +163,7 @@ export function AcrePointsClaimModalBase() { {currentStepLabel} - {currentStepValue} - + ))} From ccf775bcb427087a74439fbc70ff44def39b5b49 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 17 Sep 2024 14:01:58 +0200 Subject: [PATCH 41/61] Fix eslint issue --- dapp/src/components/AcrePointsClaimModal/index.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/dapp/src/components/AcrePointsClaimModal/index.tsx b/dapp/src/components/AcrePointsClaimModal/index.tsx index adf8710f2..027aef78d 100644 --- a/dapp/src/components/AcrePointsClaimModal/index.tsx +++ b/dapp/src/components/AcrePointsClaimModal/index.tsx @@ -131,8 +131,7 @@ export function AcrePointsClaimModalBase() { await animate(sequence) } - // eslint-disable-next-line no-void - void handleAnimation() + logPromiseFailure(handleAnimation()) }, [scope, animate, steps]) const { closeModal } = useModal() From 62b8f76085163b33a237313915e0c91546ccfcfa Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 17 Sep 2024 15:20:12 +0200 Subject: [PATCH 42/61] Use eth Address to fetch acre points data --- dapp/src/hooks/useAcrePoints.ts | 10 +++++----- dapp/src/hooks/useWallet.ts | 16 +++++++++++++++- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/dapp/src/hooks/useAcrePoints.ts b/dapp/src/hooks/useAcrePoints.ts index 1a7cb8a24..305d4cd63 100644 --- a/dapp/src/hooks/useAcrePoints.ts +++ b/dapp/src/hooks/useAcrePoints.ts @@ -15,12 +15,12 @@ type UseAcrePointsReturnType = { } export default function useAcrePoints(): UseAcrePointsReturnType { - const { address = "" } = useWallet() + const { address = "", ethAddress = "" } = useWallet() const userPointsDataQuery = useQuery({ - queryKey: [...userKeys.pointsData(), address], - enabled: !!address, - queryFn: async () => acreApi.getPointsDataByUser(address), + queryKey: [...userKeys.pointsData(), ethAddress], + enabled: !!ethAddress, + queryFn: async () => acreApi.getPointsDataByUser(ethAddress), }) const pointsDataQuery = useQuery({ @@ -37,7 +37,7 @@ export default function useAcrePoints(): UseAcrePointsReturnType { const { data } = userPointsDataQuery const totalBalance = bigIntToUserAmount(data?.claimed ?? 0n, 0) - const claimableBalance = bigIntToUserAmount(data?.claimed ?? 0n, 0) + const claimableBalance = bigIntToUserAmount(data?.unclaimed ?? 0n, 0) return { totalBalance, diff --git a/dapp/src/hooks/useWallet.ts b/dapp/src/hooks/useWallet.ts index 5d6c1da7e..366acf1a5 100644 --- a/dapp/src/hooks/useWallet.ts +++ b/dapp/src/hooks/useWallet.ts @@ -18,6 +18,7 @@ const { typeConversionToConnector, typeConversionToOrangeKitConnector } = type UseWalletReturn = { isConnected: boolean address?: string + ethAddress?: string balance?: bigint status: Status onConnect: ( @@ -41,6 +42,7 @@ export function useWallet(): UseWalletReturn { const resetWalletState = useResetWalletState() const [address, setAddress] = useState(undefined) + const [ethAddress, setEthAddress] = useState(undefined) // `isConnected` is variable derived from `status` but does not guarantee us a set `address`. // When `status` is 'connected' properties like `address` are guaranteed to be defined. @@ -89,10 +91,13 @@ export function useWallet(): UseWalletReturn { const fetchBitcoinAddress = async () => { if (connector) { const btcAddress = await connector.getBitcoinAddress() + const accounts = await connector.getAccounts() setAddress(btcAddress) + setEthAddress(accounts[0]) } else { setAddress(undefined) + setEthAddress(undefined) } } @@ -103,11 +108,20 @@ export function useWallet(): UseWalletReturn { () => ({ isConnected, address, + ethAddress, balance, status, onConnect, onDisconnect, }), - [address, balance, isConnected, onConnect, onDisconnect, status], + [ + address, + balance, + ethAddress, + isConnected, + onConnect, + onDisconnect, + status, + ], ) } From 68a5277597e9dd5de07d80c2781cb9b8bd89bc2b Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Tue, 17 Sep 2024 15:29:23 +0200 Subject: [PATCH 43/61] Add `TODO` comment --- dapp/src/pages/DashboardPage/AcrePointsCard.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx index 0cbec3b0f..e8cc0c4d0 100644 --- a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx +++ b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx @@ -29,6 +29,7 @@ export default function AcrePointsCard(props: CardProps) { const onClaimButtonClick = () => { claimPoints() + // TODO: The modal window should be opened when the claim action succeeds. openModal(MODAL_TYPES.ACRE_POINTS_CLAIM) } From b1bac459ad502ffc3d32400a221d57a62b9e53a7 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Wed, 18 Sep 2024 09:09:55 +0200 Subject: [PATCH 44/61] Fix incorrect path for acre API --- dapp/src/utils/acreApi.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dapp/src/utils/acreApi.ts b/dapp/src/utils/acreApi.ts index 8d5581031..1223458fb 100644 --- a/dapp/src/utils/acreApi.ts +++ b/dapp/src/utils/acreApi.ts @@ -8,7 +8,7 @@ type PointsDataResponse = { } const getPointsData = async () => { - const url = `${ACRE_API_ENDPOINT}/points` + const url = `${ACRE_API_ENDPOINT}points` const response = await axios.get(url) return { @@ -23,7 +23,7 @@ type PointsDataByUserResponse = { } const getPointsDataByUser = async (address: string) => { - const url = `${ACRE_API_ENDPOINT}/users/${address}/points` + const url = `${ACRE_API_ENDPOINT}users/${address}/points` const response = await axios.get(url) return { @@ -39,7 +39,7 @@ type ClaimAcrePointsResponse = { claimedAt: number } const claimPoints = async (address: string) => { - const url = `${ACRE_API_ENDPOINT}/users/points/${address}/claim` + const url = `${ACRE_API_ENDPOINT}users/points/${address}/claim` const response = await axios.post(url) return { From 6b4dca907594eac221695f70656ca2e660b4dbd4 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Wed, 18 Sep 2024 09:30:55 +0200 Subject: [PATCH 45/61] Use eth address to fetch acre points data --- dapp/src/hooks/useAcrePoints.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dapp/src/hooks/useAcrePoints.ts b/dapp/src/hooks/useAcrePoints.ts index 305d4cd63..9de3c27f7 100644 --- a/dapp/src/hooks/useAcrePoints.ts +++ b/dapp/src/hooks/useAcrePoints.ts @@ -15,7 +15,7 @@ type UseAcrePointsReturnType = { } export default function useAcrePoints(): UseAcrePointsReturnType { - const { address = "", ethAddress = "" } = useWallet() + const { ethAddress = "" } = useWallet() const userPointsDataQuery = useQuery({ queryKey: [...userKeys.pointsData(), ethAddress], @@ -29,7 +29,7 @@ export default function useAcrePoints(): UseAcrePointsReturnType { }) const { mutate: claimPoints } = useMutation({ - mutationFn: async () => acreApi.claimPoints(address), + mutationFn: async () => acreApi.claimPoints(ethAddress), onSettled: async () => { await userPointsDataQuery.refetch() }, From eb56b59941fe4f22448efc0c6d496e54fe398ae3 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Wed, 18 Sep 2024 09:38:12 +0200 Subject: [PATCH 46/61] Update acre points endpoints --- dapp/src/utils/acreApi.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/dapp/src/utils/acreApi.ts b/dapp/src/utils/acreApi.ts index 1223458fb..ee7bbadbe 100644 --- a/dapp/src/utils/acreApi.ts +++ b/dapp/src/utils/acreApi.ts @@ -1,14 +1,17 @@ import { env } from "#/constants" -import axios from "axios" +import axiosStatic from "axios" -const { ACRE_API_ENDPOINT } = env +const axios = axiosStatic.create({ + baseURL: env.ACRE_API_ENDPOINT, + withCredentials: true, +}) type PointsDataResponse = { dropAt: number } const getPointsData = async () => { - const url = `${ACRE_API_ENDPOINT}points` + const url = "points" const response = await axios.get(url) return { @@ -23,7 +26,7 @@ type PointsDataByUserResponse = { } const getPointsDataByUser = async (address: string) => { - const url = `${ACRE_API_ENDPOINT}users/${address}/points` + const url = `users/${address}/points` const response = await axios.get(url) return { @@ -39,7 +42,7 @@ type ClaimAcrePointsResponse = { claimedAt: number } const claimPoints = async (address: string) => { - const url = `${ACRE_API_ENDPOINT}users/points/${address}/claim` + const url = `users/${address}/points/claim` const response = await axios.post(url) return { From 5caed3580880a295aebaa84fa92a2dbb48f06ead Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Wed, 18 Sep 2024 10:26:44 +0200 Subject: [PATCH 47/61] Fix post-merge errors --- dapp/src/utils/acre-api.ts | 41 -------------------------------------- dapp/src/utils/acreApi.ts | 31 ++++++++++++++++++++++++++++ dapp/src/utils/index.ts | 1 - 3 files changed, 31 insertions(+), 42 deletions(-) delete mode 100644 dapp/src/utils/acre-api.ts diff --git a/dapp/src/utils/acre-api.ts b/dapp/src/utils/acre-api.ts deleted file mode 100644 index 8210f1d62..000000000 --- a/dapp/src/utils/acre-api.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { env } from "#/constants" -import axiosStatic from "axios" - -const axios = axiosStatic.create({ - baseURL: env.ACRE_API_ENDPOINT, - withCredentials: true, -}) - -async function getSession() { - const response = await axios.get<{ nonce: string } | { address: string }>( - "session", - ) - - return response.data -} - -async function createSession( - message: string, - signature: string, - publicKey: string, -) { - const response = await axios.post<{ success: boolean }>("session", { - message, - signature, - publicKey, - }) - - if (!response.data.success) { - throw new Error("Failed to create Acre session") - } -} - -async function deleteSession() { - await axios.delete("session") -} - -export default { - createSession, - getSession, - deleteSession, -} diff --git a/dapp/src/utils/acreApi.ts b/dapp/src/utils/acreApi.ts index ee7bbadbe..48fff0fd9 100644 --- a/dapp/src/utils/acreApi.ts +++ b/dapp/src/utils/acreApi.ts @@ -6,6 +6,34 @@ const axios = axiosStatic.create({ withCredentials: true, }) +async function getSession() { + const response = await axios.get<{ nonce: string } | { address: string }>( + "session", + ) + + return response.data +} + +async function createSession( + message: string, + signature: string, + publicKey: string, +) { + const response = await axios.post<{ success: boolean }>("session", { + message, + signature, + publicKey, + }) + + if (!response.data.success) { + throw new Error("Failed to create Acre session") + } +} + +async function deleteSession() { + await axios.delete("session") +} + type PointsDataResponse = { dropAt: number } @@ -53,6 +81,9 @@ const claimPoints = async (address: string) => { } export default { + createSession, + getSession, + deleteSession, getPointsData, getPointsDataByUser, claimPoints, diff --git a/dapp/src/utils/index.ts b/dapp/src/utils/index.ts index 3ca8c2e14..3154265d9 100644 --- a/dapp/src/utils/index.ts +++ b/dapp/src/utils/index.ts @@ -18,4 +18,3 @@ export { default as referralProgram } from "./referralProgram" export { default as mezoPortalAPI } from "./mezoPortalApi" export { default as acreApi } from "./acreApi" export { default as router } from "./router" -export { default as acreApi } from "./acre-api" From 4e1a0d00c34e31f42c00a218849956de5ac7a724 Mon Sep 17 00:00:00 2001 From: Karolina Kosiorowska Date: Wed, 18 Sep 2024 13:26:13 +0200 Subject: [PATCH 48/61] Fixes for claim action - Updates names - Open the model with confetti only when the claim action is successful - Show correct amounts --- .../components/AcrePointsClaimModal/index.tsx | 44 ++++++++----------- dapp/src/hooks/useAcrePoints.ts | 14 ++++++ .../pages/DashboardPage/AcrePointsCard.tsx | 12 +---- dapp/src/utils/acreApi.ts | 2 +- 4 files changed, 35 insertions(+), 37 deletions(-) diff --git a/dapp/src/components/AcrePointsClaimModal/index.tsx b/dapp/src/components/AcrePointsClaimModal/index.tsx index 027aef78d..e9e2e80be 100644 --- a/dapp/src/components/AcrePointsClaimModal/index.tsx +++ b/dapp/src/components/AcrePointsClaimModal/index.tsx @@ -1,5 +1,5 @@ import React, { ReactNode, useEffect, useMemo, useState } from "react" -import { useAcrePoints, useModal, useTimeout } from "#/hooks" +import { useTimeout } from "#/hooks" import { Box, Button, ModalBody, VStack } from "@chakra-ui/react" import { AnimationSequence, @@ -10,6 +10,7 @@ import { import { logPromiseFailure, numberToLocaleString } from "#/utils" import { ONE_SEC_IN_MILLISECONDS } from "#/constants" import ConfettiExplosion from "react-confetti-explosion" +import { BaseModalProps } from "#/types" import withBaseModal from "../ModalRoot/withBaseModal" import { TextXl } from "../shared/Typography" import { AnimatedNumber } from "../shared/AnimatedNumber" @@ -43,25 +44,25 @@ const getStepOffsets = ( : (index + 1) * -stepHeight - spacing * 2 ** index, ) -export function AcrePointsClaimModalBase() { - const { - claimableBalance: claimedPointsAmount, - totalBalance, - updateUserPointsData, - } = useAcrePoints() - - const formattedClaimablePointsAmount = - numberToLocaleString(claimedPointsAmount) - const formattedTotalPointsAmount = numberToLocaleString( - totalBalance + claimedPointsAmount, - ) +type AcrePointsClaimModalBaseProps = { + claimedAmount: number + totalAmount: number +} & BaseModalProps + +export function AcrePointsClaimModalBase({ + claimedAmount, + totalAmount, + closeModal, +}: AcrePointsClaimModalBaseProps) { + const formattedClaimedAmount = numberToLocaleString(claimedAmount) + const formattedTotalAmount = numberToLocaleString(totalAmount) const steps = useMemo<[string, ReactNode][]>( () => [ [ "You earned", , // ], ], - [formattedClaimablePointsAmount, formattedTotalPointsAmount], + [formattedClaimedAmount, formattedTotalAmount], ) const [scope, animate] = useAnimate() @@ -134,16 +135,9 @@ export function AcrePointsClaimModalBase() { logPromiseFailure(handleAnimation()) }, [scope, animate, steps]) - const { closeModal } = useModal() - const [isCofettiExploded, setIsCofettiExploded] = useState(false) - const handleClose = () => { - logPromiseFailure(updateUserPointsData()) - closeModal() - } - - useTimeout(handleClose, AUTOCLOSE_DELAY) + useTimeout(closeModal, AUTOCLOSE_DELAY) return ( @@ -179,7 +173,7 @@ export function AcrePointsClaimModalBase() { )} - + ) diff --git a/dapp/src/utils/acreApi.ts b/dapp/src/utils/acreApi.ts index 244994da0..7d7718108 100644 --- a/dapp/src/utils/acreApi.ts +++ b/dapp/src/utils/acreApi.ts @@ -36,6 +36,7 @@ async function deleteSession() { type PointsDataResponse = { dropAt: number + isPreparingDrop: boolean } const getPointsData = async () => { @@ -44,6 +45,7 @@ const getPointsData = async () => { return { dropAt: response.data.dropAt, + isPreparingDrop: response.data.isPreparingDrop, } } From 292d36e068393e116f1dd7b05d9d8d51c0f8735b Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Tue, 15 Oct 2024 10:36:56 +0200 Subject: [PATCH 56/61] Revert "Resolve modal button issue" This reverts commit 414a7532cb3154499d0072b016e80ce30fcc8bb3. --- .../TransactionModal/ActionFormModal.tsx | 2 +- .../ActiveStakingStep/StakeFormModal/index.tsx | 15 +-------------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/dapp/src/components/TransactionModal/ActionFormModal.tsx b/dapp/src/components/TransactionModal/ActionFormModal.tsx index 8b5f42e8b..53ab0e42b 100644 --- a/dapp/src/components/TransactionModal/ActionFormModal.tsx +++ b/dapp/src/components/TransactionModal/ActionFormModal.tsx @@ -108,7 +108,7 @@ function ActionFormModal({ type }: { type: ActionFlowType }) { {!isLoading && } {heading} - + diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx index 63816a5b4..6233134bd 100644 --- a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/index.tsx @@ -6,7 +6,6 @@ import { FormSubmitButton } from "#/components/shared/Form" import { ACTION_FLOW_TYPES, BaseFormProps } from "#/types" import { fixedPointNumberToString, getCurrencyByType } from "#/utils" import { featureFlags } from "#/constants" -import { Box } from "@chakra-ui/react" import StakeDetails from "./StakeDetails" import AcrePointsRewardEstimation from "./AcrePointsRewardEstimation" @@ -34,20 +33,8 @@ function StakeFormModal({ {featureFlags.ACRE_POINTS_ENABLED && ( )} - - - - Deposit - + Deposit ) } From 13ef302ec0ba76ad0afde2ddfcb02e9d9532178a Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Tue, 15 Oct 2024 14:52:56 +0200 Subject: [PATCH 57/61] Use existing skeleton component --- dapp/src/hooks/useAcrePoints.ts | 4 ---- dapp/src/pages/DashboardPage/AcrePointsCard.tsx | 13 +++++++------ 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/dapp/src/hooks/useAcrePoints.ts b/dapp/src/hooks/useAcrePoints.ts index 3b6d0c0c0..ca4a7808a 100644 --- a/dapp/src/hooks/useAcrePoints.ts +++ b/dapp/src/hooks/useAcrePoints.ts @@ -15,7 +15,6 @@ type UseAcrePointsReturnType = { claimPoints: () => void updateUserPointsData: () => Promise updatePointsData: () => Promise - isLoading: boolean } export default function useAcrePoints(): UseAcrePointsReturnType { @@ -55,8 +54,6 @@ export default function useAcrePoints(): UseAcrePointsReturnType { const totalBalance = bigIntToUserAmount(data?.claimed ?? 0n, 0) const claimableBalance = bigIntToUserAmount(data?.unclaimed ?? 0n, 0) - const isLoading = userPointsDataQuery.isLoading || pointsDataQuery.isLoading - return { totalBalance, claimableBalance, @@ -65,6 +62,5 @@ export default function useAcrePoints(): UseAcrePointsReturnType { claimPoints, updateUserPointsData: userPointsDataQuery.refetch, updatePointsData: pointsDataQuery.refetch, - isLoading, } } diff --git a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx index 19ea73648..78de5c02e 100644 --- a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx +++ b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx @@ -8,7 +8,6 @@ import { CardProps, HStack, Icon, - Skeleton, Stack, Tooltip, VStack, @@ -18,6 +17,7 @@ import { logPromiseFailure, numberToLocaleString } from "#/utils" import { useAcrePoints } from "#/hooks" import Spinner from "#/components/shared/Spinner" import { IconInfoCircle } from "@tabler/icons-react" +import UserDataSkeleton from "#/components/shared/UserDataSkeleton" export default function AcrePointsCard(props: CardProps) { const { @@ -28,7 +28,6 @@ export default function AcrePointsCard(props: CardProps) { updateUserPointsData, updatePointsData, isPreparingDrop, - isLoading, } = useAcrePoints() const formattedTotalPointsAmount = numberToLocaleString(totalBalance) @@ -52,11 +51,11 @@ export default function AcrePointsCard(props: CardProps) { - +

{formattedTotalPointsAmount} PTS

- {!isPreparingDrop && nextDropTimestamp ? ( + {nextDropTimestamp && ( - ) : ( + )} + + {isPreparingDrop && ( {!claimableBalance && ( Please wait... @@ -109,7 +110,7 @@ export default function AcrePointsCard(props: CardProps) { )} - + ) From f8ea2b13107bb554fe8eac9ec36d7f7676a4512a Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Tue, 15 Oct 2024 15:39:04 +0200 Subject: [PATCH 58/61] Reduce content layout shift --- .../pages/DashboardPage/AcrePointsCard.tsx | 105 +++++++++--------- 1 file changed, 55 insertions(+), 50 deletions(-) diff --git a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx index 78de5c02e..7d17958dd 100644 --- a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx +++ b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx @@ -38,6 +38,9 @@ export default function AcrePointsCard(props: CardProps) { logPromiseFailure(updateUserPointsData()) } + const isDataReady = + !!nextDropTimestamp || isPreparingDrop || !!claimableBalance + return (

{formattedTotalPointsAmount} PTS

- - {nextDropTimestamp && ( - - - Next drop in - - - - )} + {isDataReady && ( + + {nextDropTimestamp && ( + + + Next drop in + + + + )} - {isPreparingDrop && ( - - {!claimableBalance && ( - Please wait... - )} + {isPreparingDrop && ( + + {!claimableBalance && ( + Please wait... + )} - - - Your drop is being prepared. - + + Your drop is being prepared. + - - - - - )} + maxW={72} + > + + + + + )} - {claimableBalance && ( - - )} - + {claimableBalance && ( + + )} + + )}
From 1ad292f37d9dcc2f33b1e59306da33da9afc944c Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Tue, 15 Oct 2024 16:09:16 +0200 Subject: [PATCH 59/61] Update api handler and render conditions --- dapp/src/hooks/useAcrePoints.ts | 4 +- .../pages/DashboardPage/AcrePointsCard.tsx | 42 +++++++++---------- dapp/src/utils/acreApi.ts | 4 +- 3 files changed, 24 insertions(+), 26 deletions(-) diff --git a/dapp/src/hooks/useAcrePoints.ts b/dapp/src/hooks/useAcrePoints.ts index ca4a7808a..f1445b655 100644 --- a/dapp/src/hooks/useAcrePoints.ts +++ b/dapp/src/hooks/useAcrePoints.ts @@ -11,7 +11,7 @@ type UseAcrePointsReturnType = { totalBalance: number claimableBalance: number nextDropTimestamp?: number - isPreparingDrop?: boolean + isCalculationInProgress?: boolean claimPoints: () => void updateUserPointsData: () => Promise updatePointsData: () => Promise @@ -58,7 +58,7 @@ export default function useAcrePoints(): UseAcrePointsReturnType { totalBalance, claimableBalance, nextDropTimestamp: pointsDataQuery.data?.dropAt, - isPreparingDrop: pointsDataQuery.data?.isPreparingDrop, + isCalculationInProgress: pointsDataQuery.data?.isCalculationInProgress, claimPoints, updateUserPointsData: userPointsDataQuery.refetch, updatePointsData: pointsDataQuery.refetch, diff --git a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx index 7d17958dd..00b9cd985 100644 --- a/dapp/src/pages/DashboardPage/AcrePointsCard.tsx +++ b/dapp/src/pages/DashboardPage/AcrePointsCard.tsx @@ -27,7 +27,7 @@ export default function AcrePointsCard(props: CardProps) { claimPoints, updateUserPointsData, updatePointsData, - isPreparingDrop, + isCalculationInProgress, } = useAcrePoints() const formattedTotalPointsAmount = numberToLocaleString(totalBalance) @@ -39,7 +39,7 @@ export default function AcrePointsCard(props: CardProps) { } const isDataReady = - !!nextDropTimestamp || isPreparingDrop || !!claimableBalance + isCalculationInProgress || !!nextDropTimestamp || !!claimableBalance return ( - {nextDropTimestamp && ( - - - Next drop in - - - - )} - - {isPreparingDrop && ( + {isCalculationInProgress ? ( {!claimableBalance && ( Please wait... @@ -98,6 +79,23 @@ export default function AcrePointsCard(props: CardProps) { + ) : ( + + + Next drop in + + + )} {claimableBalance && ( diff --git a/dapp/src/utils/acreApi.ts b/dapp/src/utils/acreApi.ts index 7d7718108..a5836d9b3 100644 --- a/dapp/src/utils/acreApi.ts +++ b/dapp/src/utils/acreApi.ts @@ -36,7 +36,7 @@ async function deleteSession() { type PointsDataResponse = { dropAt: number - isPreparingDrop: boolean + isCalculationInProgress: boolean } const getPointsData = async () => { @@ -45,7 +45,7 @@ const getPointsData = async () => { return { dropAt: response.data.dropAt, - isPreparingDrop: response.data.isPreparingDrop, + isCalculationInProgress: response.data.isCalculationInProgress, } } From 74fe81869f20088a34c6e973034133242ae88971 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Tue, 15 Oct 2024 19:06:36 +0200 Subject: [PATCH 60/61] Update points estimation due to latest changes The daily points amount is capped from 100 000 000 pts to 10 000 pts (assuming the deposited amount is 1 BTC) --- .../StakeFormModal/AcrePointsRewardEstimation.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/AcrePointsRewardEstimation.tsx b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/AcrePointsRewardEstimation.tsx index 6d6deeb00..84fa4419c 100644 --- a/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/AcrePointsRewardEstimation.tsx +++ b/dapp/src/components/TransactionModal/ActiveStakingStep/StakeFormModal/AcrePointsRewardEstimation.tsx @@ -50,9 +50,10 @@ function AcrePointsRewardEstimation(props: StackProps) { const { value = 0n } = useTokenAmountField() const baseReward = Number(value) + const pointsRate = 10000 const estimatedReward = useMemo( - () => selectedTierItem.multipler * baseReward, + () => (selectedTierItem.multipler * baseReward) / pointsRate, [baseReward, selectedTierItem], ) From 1df501bf3b48af5cbb55ec748f0b78356cc0e913 Mon Sep 17 00:00:00 2001 From: Kamil Pyszkowski Date: Fri, 18 Oct 2024 13:25:18 +0200 Subject: [PATCH 61/61] Add TODO comments --- dapp/src/components/AcrePointsClaimModal.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dapp/src/components/AcrePointsClaimModal.tsx b/dapp/src/components/AcrePointsClaimModal.tsx index c33011886..6efa97ee0 100644 --- a/dapp/src/components/AcrePointsClaimModal.tsx +++ b/dapp/src/components/AcrePointsClaimModal.tsx @@ -78,6 +78,7 @@ function AcrePointsClaimModalBase({ indicationColor="brand.400" />, ], + // TODO: Uncomment when the leaderboard feature is ready // [ // "Calculating rank...", //