-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
de6be39
commit 1d9b80f
Showing
6 changed files
with
369 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
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 { getReferralLeaderboardQuery } from '@/services/datahub/referral/query' | ||
import { useMyMainAddress } from '@/stores/my-account' | ||
import { cx, mutedTextColorStyles } from '@/utils/class-names' | ||
import { Transition } from '@headlessui/react' | ||
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' | ||
|
||
type LeaderboardModalProps = { | ||
isOpen: boolean | ||
close: () => void | ||
} | ||
|
||
const LeaderboardModal = ({ isOpen, close }: LeaderboardModalProps) => { | ||
const myAddress = useMyMainAddress() | ||
|
||
return createPortal( | ||
<> | ||
<Transition | ||
appear | ||
show={isOpen} | ||
className='fixed inset-0 z-10 h-full w-full bg-background transition duration-300' | ||
enterFrom={cx('opacity-0')} | ||
enterTo='opacity-100' | ||
leaveFrom='h-auto' | ||
leaveTo='opacity-0 !duration-150' | ||
/> | ||
<Transition | ||
appear | ||
show={isOpen} | ||
className='fixed inset-0 z-10 flex h-full w-full flex-col bg-background pb-20 transition duration-300' | ||
enterFrom={cx('opacity-0 -translate-y-48')} | ||
enterTo='opacity-100 translate-y-0' | ||
leaveFrom='h-auto' | ||
leaveTo='opacity-0 -translate-y-24 !duration-150' | ||
> | ||
<div className='mx-auto flex w-full max-w-screen-md flex-1 flex-col overflow-auto'> | ||
<Image | ||
src={BlueGradient} | ||
priority | ||
alt='' | ||
className='absolute -top-56 left-1/2 w-full -translate-x-1/2' | ||
/> | ||
<div className='flex flex-col gap-2'> | ||
<div className='relative mx-auto flex w-full items-center justify-between px-4 py-4'> | ||
<span className='text-xl font-bold'> | ||
Referrers Leaderboard 🏆 | ||
</span> | ||
<Button | ||
className='-mr-2' | ||
variant='transparent' | ||
size='circleSm' | ||
onClick={close} | ||
> | ||
<HiXMark className='text-3xl' /> | ||
</Button> | ||
</div> | ||
<span></span> | ||
</div> | ||
<div className='relative mx-auto flex h-full w-full flex-col items-center px-4'> | ||
<LeaderboardTable /> | ||
</div> | ||
</div> | ||
</Transition> | ||
</>, | ||
document.body | ||
) | ||
} | ||
|
||
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 '), | ||
}, | ||
] | ||
|
||
const LeaderboardTable = () => { | ||
const myAddress = useMyMainAddress() | ||
const { data: referrersData, isLoading } = | ||
getReferralLeaderboardQuery.useQuery(myAddress || '') | ||
|
||
const data = useMemo(() => { | ||
return ( | ||
referrersData?.map((item, i) => ({ | ||
id: i + 1, | ||
user: ( | ||
<UserPreview | ||
address={item.address} | ||
nameClassName='[&>span]:overflow-hidden [&>span]:whitespace-nowrap [&>span]:text-ellipsis' | ||
desc={ | ||
<span className='text-sm text-slate-400'> | ||
👋 {item.count} frens | ||
</span> | ||
} | ||
/> | ||
), | ||
})) || [] | ||
) | ||
}, [referrersData]) | ||
|
||
return ( | ||
<> | ||
{data.length === 0 && | ||
(isLoading ? ( | ||
<Loading title='Loading table data' className='p-7' /> | ||
) : ( | ||
<div | ||
className='flex flex-col items-center justify-center p-4 text-center' | ||
style={{ gridColumn: '1/4' }} | ||
> | ||
<Image | ||
src={MedalsImage} | ||
alt='' | ||
className='relative w-[70px] max-w-sm' | ||
/> | ||
<span className={cx(mutedTextColorStyles)}> | ||
Invite your frens to show up here! | ||
</span> | ||
</div> | ||
))} | ||
{!!data.length && ( | ||
<div className='flex w-full flex-col'> | ||
<table className='w-full table-fixed text-left'> | ||
<tbody> | ||
{data.map((item, i) => { | ||
return ( | ||
<TableRow | ||
key={i} | ||
columns={tableColumns()} | ||
item={item} | ||
withDivider={false} | ||
/> | ||
) | ||
})} | ||
</tbody> | ||
</table> | ||
</div> | ||
)} | ||
</> | ||
) | ||
} | ||
|
||
export default LeaderboardModal |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 ( | ||
<> | ||
<div className='flex w-full flex-col rounded-xl bg-slate-800 hover:cursor-pointer'> | ||
<div | ||
className={cx('border-b border-slate-700 p-4')} | ||
onClick={() => { | ||
sendEvent('open_leaderboard') | ||
setOpenLeaderboard(true) | ||
}} | ||
> | ||
<div className='flex items-center justify-between gap-2'> | ||
<div className='flex items-center gap-2'> | ||
<AddressAvatar address={myAddress ?? ''} className='h-16 w-16' /> | ||
<div className='flex flex-col gap-1'> | ||
<div className='flex items-center gap-3'> | ||
<Name | ||
address={myAddress ?? ''} | ||
clipText | ||
className='text-lg font-medium !text-text' | ||
/> | ||
<Button | ||
size='circleSm' | ||
variant='muted' | ||
className='inline flex-shrink-0' | ||
onClick={(e) => { | ||
e.preventDefault() | ||
e.stopPropagation() | ||
|
||
sendEvent('edit_profile_click') | ||
setOpenProfileModal(true) | ||
}} | ||
> | ||
<RiPencilFill /> | ||
</Button> | ||
</div> | ||
<LinkText | ||
variant='primary' | ||
className='flex items-center gap-2 hover:no-underline focus:no-underline' | ||
> | ||
<IoIosStats /> See the Leaderboard | ||
</LinkText> | ||
</div> | ||
</div> | ||
<IoIosArrowForward className={cx('fill-slate-400 text-2xl')} /> | ||
</div> | ||
</div> | ||
<div className='flex w-full items-center gap-4 px-4'> | ||
<div className='flex w-full flex-col gap-1 border-r border-slate-700 py-4'> | ||
<SkeletonFallback isLoading={isLoading} className='w-8'> | ||
<span className='flex items-center gap-2 text-2xl font-bold'> | ||
<Image src={Diamond} alt='' className='h-8 w-8' /> | ||
{formatNumber(refCount * pointsPerUser, { shorten: true })} | ||
</span> | ||
</SkeletonFallback> | ||
<span className='text-sm font-medium text-slate-400'> | ||
Points earned from {refCount} invited friends | ||
</span> | ||
</div> | ||
<div className='flex w-full flex-col gap-1 py-4'> | ||
<SkeletonFallback isLoading={isLoading} className='w-8'> | ||
<span className='flex items-center gap-2 text-2xl font-bold'> | ||
<Image src={Diamond} alt='' className='h-8 w-8' /> | ||
{formatNumber(pointsEarned, { shorten: true })} | ||
</span> | ||
</SkeletonFallback> | ||
<span className='text-sm font-medium text-slate-400'> | ||
Points earned from your friends activity | ||
</span> | ||
</div> | ||
</div> | ||
</div> | ||
<SubsocialProfileModal | ||
title='✏️ Edit Profile' | ||
closeModal={() => setOpenProfileModal(false)} | ||
isOpen={openProfileModal} | ||
/> | ||
<LeaderboardModal | ||
isOpen={openLeaderboard} | ||
close={() => setOpenLeaderboard(false)} | ||
/> | ||
</> | ||
) | ||
} | ||
|
||
export default ReferrerStats |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.