diff --git a/.github/workflows/build-deploy.yml b/.github/workflows/build-deploy.yml index 9de4ac6dc..f49d011f0 100644 --- a/.github/workflows/build-deploy.yml +++ b/.github/workflows/build-deploy.yml @@ -61,6 +61,7 @@ jobs: GH_NEXT_PUBLIC_CONTEST_NAME=MEMECOIN CONTEST GH_NEXT_PUBLIC_CONTEST_END_TIME=1720796400739 GH_NEXT_PUBLIC_TIME_CONSTRAINT=300000 + GH_NEXT_PUBLIC_CONTEST_RANGE_KEY=ReferrersRankedListByReferralsCountCp_20240722_20240728 GH_NEXT_PUBLIC_AMP_ID=40d4174295c7edf657fc3bedf2748549 GH_NEXT_PUBLIC_COMMUNITY_HUB_ID=12455 GH_NEXT_PUBLIC_GA_ID=G-TP1XEFNHQD @@ -115,6 +116,7 @@ jobs: GH_NEXT_PUBLIC_CONTEST_NAME=MEMECOIN CONTEST GH_NEXT_PUBLIC_CONTEST_END_TIME=1720796400739 GH_NEXT_PUBLIC_TIME_CONSTRAINT=5000 + GH_NEXT_PUBLIC_CONTEST_RANGE_KEY=ReferrersRankedListByReferralsCountCp_20240722_20240728 GH_NEXT_PUBLIC_TELEGRAM_NOTIFICATION_BOT=https://t.me/g_notif_staging_bot/ GH_TELEGRAM_BOT_TOKEN="7038999347:AAGBgXTWcXpR4vZPW9A8_ia9PkWOpeyDeWA" # without base path diff --git a/ci.env b/ci.env index a9f4e29ef..69fb1bc35 100644 --- a/ci.env +++ b/ci.env @@ -8,6 +8,7 @@ NEXT_PUBLIC_CONTEST_CHAT_ID='$GH_NEXT_PUBLIC_CONTEST_CHAT_ID' NEXT_PUBLIC_CONTEST_NAME='$GH_NEXT_PUBLIC_CONTEST_NAME' NEXT_PUBLIC_CONTEST_END_TIME='$GH_NEXT_PUBLIC_CONTEST_END_TIME' NEXT_PUBLIC_TIME_CONSTRAINT='$GH_NEXT_PUBLIC_TIME_CONSTRAINT' +NEXT_PUBLIC_CONTEST_RANGE_KEY='$GH_NEXT_PUBLIC_CONTEST_RANGE_KEY' NEXT_PUBLIC_BASE_PATH='$GH_NEXT_PUBLIC_BASE_PATH' NEXT_PUBLIC_NEYNAR_CLIENT_ID='$GH_NEXT_PUBLIC_NEYNAR_CLIENT_ID' NEXT_PUBLIC_TELEGRAM_BOT_ID='$GH_NEXT_PUBLIC_TELEGRAM_BOT_ID' diff --git a/docker/Dockerfile b/docker/Dockerfile index c9dfe4402..0b6480987 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -15,6 +15,7 @@ ARG GH_NEXT_PUBLIC_CONTEST_CHAT_ID ARG GH_NEXT_PUBLIC_CONTEST_NAME ARG GH_NEXT_PUBLIC_CONTEST_END_TIME ARG GH_NEXT_PUBLIC_TIME_CONSTRAINT +ARG GH_NEXT_PUBLIC_CONTEST_RANGE_KEY ARG GH_NEXT_PUBLIC_BASE_PATH ARG GH_NEXT_PUBLIC_NEYNAR_CLIENT_ID ARG GH_NEXT_PUBLIC_TELEGRAM_BOT_ID @@ -44,6 +45,7 @@ ENV NEXTAUTH_URL=${GH_NEXTAUTH_URL} \ NEXT_PUBLIC_CONTEST_NAME=${GH_NEXT_PUBLIC_CONTEST_NAME} \ NEXT_PUBLIC_CONTEST_END_TIME=${GH_NEXT_PUBLIC_CONTEST_END_TIME} \ NEXT_PUBLIC_TIME_CONSTRAINT=${GH_NEXT_PUBLIC_TIME_CONSTRAINT} \ + NEXT_PUBLIC_CONTEST_RANGE_KEY=${GH_NEXT_PUBLIC_CONTEST_RANGE_KEY} \ NEXT_PUBLIC_BASE_PATH=${GH_NEXT_PUBLIC_BASE_PATH} \ NEXT_PUBLIC_NEYNAR_CLIENT_ID=${GH_NEXT_PUBLIC_NEYNAR_CLIENT_ID} \ NEXT_PUBLIC_TELEGRAM_BOT_ID=${GH_NEXT_PUBLIC_TELEGRAM_BOT_ID} \ @@ -103,6 +105,7 @@ ARG GH_NEXT_PUBLIC_CONTEST_CHAT_ID ARG GH_NEXT_PUBLIC_CONTEST_NAME ARG GH_NEXT_PUBLIC_CONTEST_END_TIME ARG GH_NEXT_PUBLIC_TIME_CONSTRAINT +ARG GH_NEXT_PUBLIC_CONTEST_RANGE_KEY ARG GH_NEXT_PUBLIC_BASE_PATH ARG GH_NEXT_PUBLIC_NEYNAR_CLIENT_ID ARG GH_NEXT_PUBLIC_TELEGRAM_BOT_ID @@ -132,6 +135,7 @@ ENV NEXTAUTH_URL=${GH_NEXTAUTH_URL} \ NEXT_PUBLIC_CONTEST_NAME=${GH_NEXT_PUBLIC_CONTEST_NAME} \ NEXT_PUBLIC_CONTEST_END_TIME=${GH_NEXT_PUBLIC_CONTEST_END_TIME} \ NEXT_PUBLIC_TIME_CONSTRAINT=${GH_NEXT_PUBLIC_TIME_CONSTRAINT} \ + NEXT_PUBLIC_CONTEST_RANGE_KEY=${GH_NEXT_PUBLIC_CONTEST_RANGE_KEY} \ NEXT_PUBLIC_BASE_PATH=${GH_NEXT_PUBLIC_BASE_PATH} \ NEXT_PUBLIC_NEYNAR_CLIENT_ID=${GH_NEXT_PUBLIC_NEYNAR_CLIENT_ID} \ NEXT_PUBLIC_TELEGRAM_BOT_ID=${GH_NEXT_PUBLIC_TELEGRAM_BOT_ID} \ diff --git a/src/assets/graphics/friends/friends-animals.png b/src/assets/graphics/friends/friends-animals.png new file mode 100644 index 000000000..bd4d00067 Binary files /dev/null and b/src/assets/graphics/friends/friends-animals.png differ diff --git a/src/assets/graphics/tasks/referral-task.png b/src/assets/graphics/tasks/referral-task.png new file mode 100644 index 000000000..0b5522255 Binary files /dev/null and b/src/assets/graphics/tasks/referral-task.png differ diff --git a/src/components/Tabs.tsx b/src/components/Tabs.tsx index 242db527a..b0b5a7022 100644 --- a/src/components/Tabs.tsx +++ b/src/components/Tabs.tsx @@ -19,6 +19,7 @@ export type TabsProps = ComponentProps<'div'> & { tabClassName?: ((selected: boolean) => string) | string defaultTab?: number withHashIntegration?: boolean + containerClassName?: string hideBeforeHashLoaded?: boolean manualTabControl?: { selectedTab: number @@ -33,6 +34,7 @@ export default function Tabs({ panelClassName, tabsRightElement, tabClassName, + containerClassName, defaultTab = 0, hideBeforeHashLoaded, withHashIntegration = true, @@ -140,7 +142,9 @@ export default function Tabs({ {tabs.map(({ id, content }) => ( - {content(changeTab)} + + {content(changeTab)} + ))} {usedSelectedTab === -1 && } diff --git a/src/env.mjs b/src/env.mjs index 449bea3c7..5a26505dc 100644 --- a/src/env.mjs +++ b/src/env.mjs @@ -45,6 +45,7 @@ export const env = createEnv({ NEXT_PUBLIC_CONTEST_NAME: z.string().default(''), NEXT_PUBLIC_CONTEST_END_TIME: z.string().default('').transform(Number), NEXT_PUBLIC_TIME_CONSTRAINT: z.string().default('').transform(Number), + NEXT_PUBLIC_CONTEST_RANGE_KEY: z.string().default(''), NEXT_PUBLIC_BASE_PATH: z.string().default(''), NEXT_PUBLIC_TELEGRAM_BOT_ID: z.string().default(''), NEXT_PUBLIC_TELEGRAM_BOT_USERNAME: z.string().default(''), @@ -87,6 +88,7 @@ export const env = createEnv({ NEXT_PUBLIC_CONTEST_NAME: process.env.NEXT_PUBLIC_CONTEST_NAME, NEXT_PUBLIC_CONTEST_END_TIME: process.env.NEXT_PUBLIC_CONTEST_END_TIME, NEXT_PUBLIC_TIME_CONSTRAINT: process.env.NEXT_PUBLIC_TIME_CONSTRAINT, + NEXT_PUBLIC_CONTEST_RANGE_KEY: process.env.NEXT_PUBLIC_CONTEST_RANGE_KEY, NEXT_PUBLIC_TELEGRAM_BOT_ID: process.env.NEXT_PUBLIC_TELEGRAM_BOT_ID, NEXT_PUBLIC_TELEGRAM_BOT_USERNAME: process.env.NEXT_PUBLIC_TELEGRAM_BOT_USERNAME, diff --git a/src/modules/points/PointsWidget.tsx b/src/modules/points/PointsWidget.tsx index ec9b740ad..077e6ce12 100644 --- a/src/modules/points/PointsWidget.tsx +++ b/src/modules/points/PointsWidget.tsx @@ -297,20 +297,10 @@ const UserStatsSection = ({ - {/* { - sendEvent('edit_evm_address_click') - setOpenEvmLinkModal(true) - }} - > - Edit - */} )} -
-
+
+
LIKES LEFT TODAY:
@@ -325,7 +315,7 @@ const UserStatsSection = ({
-
+
POINTS EARNED:
{ - sendEvent('ref_copied') - copyToClipboard(text) - - setIsCopied(true) - setTimeout(() => { - setIsCopied(false) - }, 1000) - } - - return ( - - -
-
- - Earn 10% of Friends Rewards - - - You receive 10% of the points your first-line friends earn, and 1% - from their friends’ earnings. - -
- - {isMounted && myAddress ? ( - - {referralLink} - - ) : ( - - )} - - - - - {referralData ? ( - {formatNumber(referralData?.refCount)} - ) : ( - - )} - - - Friends Joined - - - - - {referralData ? ( - {formatNumber(referralData?.pointsEarned)} - ) : ( - - )} - - - Points Earned - - -
-
- ) -} diff --git a/src/modules/telegram/FriendsPage/LeaderboardModal.tsx b/src/modules/telegram/FriendsPage/LeaderboardModal.tsx new file mode 100644 index 000000000..7bd1204ae --- /dev/null +++ b/src/modules/telegram/FriendsPage/LeaderboardModal.tsx @@ -0,0 +1,278 @@ +import BlueGradient from '@/assets/graphics/landing/gradients/blue.png' +import MedalsImage from '@/assets/graphics/medals.png' +import Button from '@/components/Button' +import Loading from '@/components/Loading' +import { Column, TableRow } from '@/components/Table' +import Tabs from '@/components/Tabs' +import { + getReferralLeaderboardQuery, + getReferrerRankQuery, +} from '@/services/datahub/referral/query' +import { useMyMainAddress } from '@/stores/my-account' +import { cx, mutedTextColorStyles } from '@/utils/class-names' +import { Transition } from '@headlessui/react' +import { isDef } from '@subsocial/utils' +import dayjs from 'dayjs' +import Image from 'next/image' +import { useMemo } from 'react' +import { createPortal } from 'react-dom' +import { HiXMark } from 'react-icons/hi2' +import { UserPreview } from '../StatsPage/LeaderboardTable' + +const DISTRIBUTION_DAY = 0 +const DEFAULT_LIST_COUNT = 100 + +const getDistributionDaysLeft = () => { + // monday: 7 days, tuesday: 6 days, ..., sunday: 1 day + return ((DISTRIBUTION_DAY + 7 - dayjs.utc().get('day')) % 7) + 1 +} + +type LeaderboardModalProps = { + isOpen: boolean + close: () => void +} + +const LeaderboardModal = ({ isOpen, close }: LeaderboardModalProps) => { + return createPortal( + <> + + +
+ +
+ Referrers Leaderboard 🏆 + +
+
+ +
+
+
+ , + document.body + ) +} + +const LeaderboardModalContent = () => { + const tabs = [ + { + id: 'contest', + text: 'Contest', + content: () => ( +
+ + Top 10 users who invite the most friends during the week will be + rewarded.{' '} + + ⏳ {getDistributionDaysLeft()} days left + + + +
+ ), + }, + { + id: 'allTime', + text: 'All-Time', + content: () => ( +
+ + All users who invite the most friends. + + +
+ ), + }, + ] + + return ( +
+ + cx( + { + ['bg-background-primary/50 rounded-full [&>span]:!text-text']: + selected, + }, + '[&>span]:text-slate-300 leading-6 font-medium p-[6px] [&>span]:text-sm border-none' + ) + } + asContainer + tabStyle='buttons' + defaultTab={0} + tabs={tabs} + /> +
+ ) +} + +export const tableColumns = (): Column[] => [ + { + index: 'user', + align: 'left', + className: cx('p-0 py-2 pr-2'), + }, + { + index: 'index', + align: 'right', + className: cx('p-0 py-2 pl-2 w-[15%] text-slate-400 '), + }, +] + +type LeaderboardTableProps = { + isContest?: boolean +} + +const LeaderboardTable = ({ isContest }: LeaderboardTableProps) => { + const myAddress = useMyMainAddress() + const { data: referrersData, isLoading } = getReferralLeaderboardQuery( + isContest || false + ).useQuery({}) + + const { leaderboardData: items, totalCount } = referrersData || {} + + const { data: referrerData } = getReferrerRankQuery( + isContest || false + ).useQuery(myAddress || '') + + const data = useMemo(() => { + const currentUserRankItem = + referrerData?.rankIndex !== undefined && myAddress + ? { + index: (referrerData?.rankIndex || 0) + 1, + className: cx('bg-slate-800 sticky bottom-0 z-[11]'), + user: ( + + 👋 {referrerData.count} frens + + } + /> + ), + } + : undefined + + const leaderboardData = + items?.map((item, i) => ({ + index: i + 1, + className: item.address === myAddress ? 'bg-slate-800' : '', + user: ( + + 👋 {item.count} frens + + } + /> + ), + })) || [] + + return [...leaderboardData, currentUserRankItem] + }, [referrerData?.rankIndex, referrerData?.count, myAddress, items]).filter( + isDef + ) + + return ( + <> + {data.length === 0 && + (isLoading ? ( + + ) : ( +
+ + + Invite your frens to show up here! + +
+ ))} + {!!data.length && ( +
+ + + {data.map((item, i) => { + return ( + <> + + {isContest && i === 9 && ( + + + + )} + + ) + })} + +
+
+ + + OTHER MEMBERS + + +
+
+ {totalCount && totalCount > DEFAULT_LIST_COUNT && ( +
+ + + AND {totalCount - DEFAULT_LIST_COUNT} MORE MEMBERS + + +
+ )} +
+ )} + + ) +} + +export default LeaderboardModal diff --git a/src/modules/telegram/FriendsPage/ReferralTable.tsx b/src/modules/telegram/FriendsPage/ReferralTable.tsx new file mode 100644 index 000000000..cfa3d585b --- /dev/null +++ b/src/modules/telegram/FriendsPage/ReferralTable.tsx @@ -0,0 +1,97 @@ +import Card from '@/components/Card' +import SkeletonFallback from '@/components/SkeletonFallback' +import { Column, TableRow } from '@/components/Table' +import { cx } from '@/utils/class-names' +import { isEmptyArray } from '@subsocial/utils' +import dayjs from 'dayjs' +import { useMemo } from 'react' +import { UserPreview } from '../StatsPage/LeaderboardTable' + +export const tableColumns = (): Column[] => [ + { + index: 'user', + align: 'left', + className: cx('p-0 py-2 pr-2'), + }, + { + index: 'id', + align: 'right', + className: cx('p-0 py-2 pl-2 w-[15%] text-slate-400 '), + }, +] + +type ReferralTableProps = { + referrals?: { + timestamp: string + socialProfileId: string + }[] + isLoading?: boolean + refCount: number +} + +const ReferralTable = ({ + referrals, + isLoading, + refCount, +}: ReferralTableProps) => { + const data = useMemo(() => { + return ( + referrals?.map((item, i) => ({ + id: i + 1, + user: ( + + {dayjs(item.timestamp).format('DD MMM')} + + } + /> + ), + })) || [] + ) + }, [referrals]) + + if (!data || isEmptyArray(data)) return null + + return ( + +
+ + + {refCount} + {' '} + referrals + +
+ + + {data.map((item, i) => { + return ( + + ) + })} + +
+ {refCount && refCount > 100 && ( +
+ + + AND {refCount - 100} MORE MEMBERS + + +
+ )} +
+
+
+ ) +} + +export default ReferralTable diff --git a/src/modules/telegram/FriendsPage/ReferrerStats.tsx b/src/modules/telegram/FriendsPage/ReferrerStats.tsx new file mode 100644 index 000000000..5fef43782 --- /dev/null +++ b/src/modules/telegram/FriendsPage/ReferrerStats.tsx @@ -0,0 +1,120 @@ +import Diamond from '@/assets/emojis/diamond.png' +import AddressAvatar from '@/components/AddressAvatar' +import Button from '@/components/Button' +import LinkText from '@/components/LinkText' +import Name from '@/components/Name' +import SkeletonFallback from '@/components/SkeletonFallback' +import SubsocialProfileModal from '@/components/subsocial-profile/SubsocialProfileModal' +import { useSendEvent } from '@/stores/analytics' +import { useMyMainAddress } from '@/stores/my-account' +import { cx } from '@/utils/class-names' +import { formatNumber } from '@/utils/strings' +import Image from 'next/image' +import { useState } from 'react' +import { IoIosArrowForward, IoIosStats } from 'react-icons/io' +import { RiPencilFill } from 'react-icons/ri' +import LeaderboardModal from './LeaderboardModal' + +type ReferrerStatsProps = { + refCount: number + pointsEarned: string + isLoading?: boolean +} + +const pointsPerUser = 200_000 + +const ReferrerStats = ({ + refCount, + pointsEarned, + isLoading, +}: ReferrerStatsProps) => { + const [openProfileModal, setOpenProfileModal] = useState(false) + const [openLeaderboard, setOpenLeaderboard] = useState(false) + const myAddress = useMyMainAddress() + const sendEvent = useSendEvent() + + return ( + <> +
+
{ + sendEvent('open_leaderboard') + setOpenLeaderboard(true) + }} + > +
+
+ +
+
+ + +
+ + See the Leaderboard + +
+
+ +
+
+
+
+ + + + {formatNumber(refCount * pointsPerUser, { shorten: true })} + + + + Points earned from {refCount} invited friends + +
+
+ + + + {formatNumber(pointsEarned, { shorten: true })} + + + + Points earned from your friends activity + +
+
+
+ setOpenProfileModal(false)} + isOpen={openProfileModal} + /> + setOpenLeaderboard(false)} + /> + + ) +} + +export default ReferrerStats diff --git a/src/modules/telegram/FriendsPage/index.tsx b/src/modules/telegram/FriendsPage/index.tsx new file mode 100644 index 000000000..78af654cb --- /dev/null +++ b/src/modules/telegram/FriendsPage/index.tsx @@ -0,0 +1,191 @@ +import FriendsAnimals from '@/assets/graphics/friends/friends-animals.png' +import BlueGradient from '@/assets/graphics/landing/gradients/blue.png' +import Button from '@/components/Button' +import Card from '@/components/Card' +import LayoutWithBottomNavigation from '@/components/layouts/LayoutWithBottomNavigation' +import { getReferralLink } from '@/components/referral/utils' +import useTgNoScroll from '@/hooks/useTgNoScroll' +import PointsWidget from '@/modules/points/PointsWidget' +import { getUserReferralStatsQuery } from '@/services/datahub/leaderboard/query' +import { useSendEvent } from '@/stores/analytics' +import { useMyMainAddress } from '@/stores/my-account' +import { isTouchDevice } from '@/utils/device' +import { copyToClipboard, formatNumber } from '@/utils/strings' +import { initUtils } from '@tma.js/sdk-react' +import Image from 'next/image' +import { useState } from 'react' +import { MdCheck, MdOutlineContentCopy } from 'react-icons/md' +import SkeletonFallback from '../../../components/SkeletonFallback' +import ReferralTable from './ReferralTable' +import ReferrerStats from './ReferrerStats' + +export default function FriendsPage() { + useTgNoScroll() + + return ( + + + + + + ) +} + +const FriendsPageContent = () => { + const [isCopied, setIsCopied] = useState(false) + const myAddress = useMyMainAddress() + const referralLink = getReferralLink(myAddress) + const sendEvent = useSendEvent() + + const { data, isLoading } = getUserReferralStatsQuery.useQuery( + myAddress || '' + ) + + const { refCount, pointsEarned, referrals } = data || {} + + const onCopyClick = (text: string) => { + sendEvent('ref_copied') + copyToClipboard(text) + + setIsCopied(true) + setTimeout(() => { + setIsCopied(false) + }, 1000) + } + + return ( +
+
+ + +
+ + INVITE TO GET BONUS + + + You receive 10% of + the points your first-line friends earn, and{' '} + 1% from their + friends’ earnings. + +
+
+
+ + + +
+
+ + +
+
+ ) +} + +type ReferralCardsProps = { + refCount: number + pointsEarned: string + isLoading?: boolean +} + +const ReferralCards = ({ + refCount, + pointsEarned, + isLoading, +}: ReferralCardsProps) => { + return ( +
+ + + {formatNumber(refCount)} + + + Points earned from your friends activity + + + + + + {formatNumber(pointsEarned)} + + + + Points earned from {formatNumber(refCount)} invited friends + + +
+ ) +} + +const EarnInfoSection = () => { + return ( + +
+ + 💎 +200,000{' '} + when your friend joined + + + You’ll get 200,000 points for every invite. Complete tasks to earn + even more. + +
+ +
+ ) +} diff --git a/src/modules/telegram/StatsPage/LeaderboardSection.tsx b/src/modules/telegram/StatsPage/LeaderboardSection.tsx index 2ffe62a83..250f2dfd0 100644 --- a/src/modules/telegram/StatsPage/LeaderboardSection.tsx +++ b/src/modules/telegram/StatsPage/LeaderboardSection.tsx @@ -36,7 +36,7 @@ export const LeaderboardContent = () => { return (
cx( @@ -44,7 +44,7 @@ export const LeaderboardContent = () => { ['bg-background-primary/50 rounded-full [&>span]:!text-text']: selected, }, - '[&>span]:text-slate-300 leading-6 font-medium p-[10px] [&>span]:text-sm border-none' + '[&>span]:text-slate-300 leading-6 font-medium p-[6px] [&>span]:text-sm border-none' ) } asContainer diff --git a/src/modules/telegram/StatsPage/LeaderboardTable.tsx b/src/modules/telegram/StatsPage/LeaderboardTable.tsx index c1e8a7000..25e367a4b 100644 --- a/src/modules/telegram/StatsPage/LeaderboardTable.tsx +++ b/src/modules/telegram/StatsPage/LeaderboardTable.tsx @@ -84,6 +84,8 @@ const LeaderboardTable = ({ refetchOnMount: refetchTab[period] ? 'always' : false, }) + const { leaderboardData, totalCount } = leaderboardDataResult || {} + const { data: userStats } = userDataQueryByPeriod[period].useQuery( myAddress || '', { refetchOnMount: refetchTab[period] ? 'always' : false } @@ -114,10 +116,10 @@ const LeaderboardTable = ({ : undefined return [ - ...parseTableRows(leaderboardDataResult || [], TABLE_LIMIT, userStats), + ...parseTableRows(leaderboardData || [], TABLE_LIMIT, userStats), currentUserRankItem, ].filter(Boolean) - }, [userStats, leaderboardDataResult]) + }, [userStats, leaderboardData]) return ( <> @@ -156,6 +158,15 @@ const LeaderboardTable = ({ })} + {totalCount && totalCount > 100 && ( +
+ + + AND {totalCount - 100} MORE MEMBERS + + +
+ )}
)} @@ -184,24 +195,31 @@ export const UserReward = ({ reward }: UserRewardProps) => { type UserPreviewProps = { address: string desc?: React.ReactNode + className?: string + nameClassName?: string } export const UserPreview = ({ address, desc, loading, + className, + nameClassName, }: UserPreviewProps & { loading?: ImageProps['loading'] }) => { return ( -
+
-
+
{desc && (
'claim-tasks-error') +const getReferralTasksObj = (aimRefCount: number) => ({ + image: ReferralTask, + tag: `INVITE_REFERRALS_${aimRefCount}_referrals_v1`, + title: `Invite ${aimRefCount} Friends`, + event: `tasks_referral_${aimRefCount}_open`, + aim: aimRefCount, + steps: [ + + + Invite {aimRefCount} friends to join + {' '} + + , + + Click the button below to earn your reward. + , + ], +}) + +type ReferralTaskModalProps = { + aimRefCount: number +} + +const ReferralTaskModalStepPart = ({ aimRefCount }: ReferralTaskModalProps) => { + const myAddress = useMyMainAddress() + const { data, isLoading } = getUserReferralStatsQuery.useQuery( + myAddress || '' + ) + + const { refCount } = data || {} + + return ( + + ( + + {refCount && refCount > aimRefCount ? aimRefCount : refCount} + + /{aimRefCount}) + + ) +} + type ModalConfig = { image: StaticImageData - title: string + title: React.ReactNode stepsWithOrangeText?: number[] steps: React.ReactNode[] event: string tag: string + aim?: number } export const modalConfigByVariant: Record< @@ -101,18 +157,23 @@ export const modalConfigByVariant: Record< , ], }, + INVITE_REFERRALS_5_referrals_v1: getReferralTasksObj(5), + INVITE_REFERRALS_10_referrals_v1: getReferralTasksObj(10), + INVITE_REFERRALS_15_referrals_v1: getReferralTasksObj(15), } type ClaimTasksTokensModalProps = { modalVariant: ClaimModalVariant close: () => void data: GamificationTask[] + disableButton?: boolean } const ClaimTasksTokensModal = ({ modalVariant, close, data, + disableButton, }: ClaimTasksTokensModalProps) => { const [isOpenAnimation, setIsOpenAnimation] = useState(false) const client = useQueryClient() @@ -259,6 +320,7 @@ const ClaimTasksTokensModal = ({