-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #268 from Official-MoonDao/jade/de-prize-join
Join DePrize
- Loading branch information
Showing
12 changed files
with
404 additions
and
34 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
import { NFT, ThirdwebNftMedia } from '@thirdweb-dev/react' | ||
import { useEffect, useState } from 'react' | ||
|
||
type CompetitorPreviewProps = { | ||
teamId: any | ||
teamContract?: any | ||
} | ||
|
||
export function CompetitorPreview({ | ||
teamId, | ||
teamContract, | ||
}: CompetitorPreviewProps) { | ||
const [teamNFT, setTeamNFT] = useState<NFT>() | ||
|
||
useEffect(() => { | ||
async function getTeamNFT() { | ||
const nft = await teamContract.erc721.get(teamId) | ||
setTeamNFT(nft) | ||
} | ||
|
||
if (teamContract?.erc721?.get && teamId) { | ||
getTeamNFT() | ||
} | ||
}, [teamId, teamContract]) | ||
|
||
return ( | ||
<div className="flex items-center gap-5"> | ||
{teamNFT && ( | ||
<div className="flex items-center"> | ||
<ThirdwebNftMedia | ||
metadata={teamNFT.metadata} | ||
width="66px" | ||
height="66px" | ||
style={{ borderRadius: '50%' }} | ||
/> | ||
<div>{teamNFT.metadata.name}</div> | ||
</div> | ||
)} | ||
</div> | ||
) | ||
} |
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,146 @@ | ||
import { Arbitrum, Sepolia } from '@thirdweb-dev/chains' | ||
import { useAddress, useContract } from '@thirdweb-dev/react' | ||
import CompetitorABI from 'const/abis/Competitor.json' | ||
import ERC20 from 'const/abis/ERC20.json' | ||
import REVDeployer from 'const/abis/REVDeployer.json' | ||
import { | ||
DEPRIZE_ID, | ||
COMPETITOR_TABLE_ADDRESSES, | ||
REVNET_ADDRESSES, | ||
} from 'const/config' | ||
import { TEAM_ADDRESSES } from 'const/config' | ||
import { useState, useContext } from 'react' | ||
import toast from 'react-hot-toast' | ||
import { useTeamWearer } from '@/lib/hats/useTeamWearer' | ||
import toastStyle from '@/lib/marketplace/marketplace-utils/toastConfig' | ||
import useWindowSize from '@/lib/team/use-window-size' | ||
import ChainContext from '@/lib/thirdweb/chain-context' | ||
import useTokenSupply from '@/lib/tokens/hooks/useTokenSupply' | ||
import useWatchTokenBalance from '@/lib/tokens/hooks/useWatchTokenBalance' | ||
import Asset from '@/components/dashboard/treasury/balance/Asset' | ||
import Container from '@/components/layout/Container' | ||
import ContentLayout from '@/components/layout/ContentLayout' | ||
import Head from '@/components/layout/Head' | ||
import { NoticeFooter } from '@/components/layout/NoticeFooter' | ||
import { CompetitorPreview } from '@/components/nance/CompetitorPreview' | ||
import { JoinDePrizeModal } from '@/components/nance/JoinDePrizeModal' | ||
import StandardButton from '../layout/StandardButton' | ||
|
||
export type Metadata = {} | ||
export type Competitor = { | ||
id: string | ||
deprize: number | ||
teamId: number | ||
metadata: Metadata | ||
} | ||
|
||
export type DePrizeProps = { | ||
competitors: Competitor[] | ||
refreshRewards: () => void | ||
} | ||
|
||
export function DePrize({ competitors, refreshRewards }: DePrizeProps) { | ||
const { selectedChain } = useContext(ChainContext) | ||
const [joinModalOpen, setJoinModalOpen] = useState(false) | ||
const userAddress = useAddress() | ||
|
||
const { contract: competitorContract } = useContract( | ||
COMPETITOR_TABLE_ADDRESSES[selectedChain.slug], | ||
CompetitorABI | ||
) | ||
const { contract: teamContract } = useContract( | ||
TEAM_ADDRESSES[selectedChain.slug] | ||
) | ||
|
||
const userTeams = useTeamWearer(teamContract, selectedChain, userAddress) | ||
|
||
const isCompetitor = userTeams.some((team: any) => | ||
competitors.some( | ||
(competitor) => competitor.teamId.toString() === team.teamId | ||
) | ||
) | ||
const handleJoinWithTeam = async (teamId: string) => { | ||
try { | ||
await competitorContract?.call('insertIntoTable', [ | ||
DEPRIZE_ID, | ||
teamId, | ||
'{}', | ||
]) | ||
toast.success('Joined as a competitor!', { | ||
style: toastStyle, | ||
}) | ||
setJoinModalOpen(false) | ||
setTimeout(() => { | ||
refreshRewards() | ||
}, 5000) | ||
} catch (error) { | ||
console.error('Error joining as a competitor:', error) | ||
toast.error('Error joining as a competitor. Please try again.', { | ||
style: toastStyle, | ||
}) | ||
} | ||
} | ||
return ( | ||
<section id="rewards-container" className="overflow-hidden"> | ||
<Head | ||
title="DePrize" | ||
description="Distribute rewards to contributors based on their contributions." | ||
/> | ||
<Container> | ||
<ContentLayout | ||
header={'DePrize'} | ||
description="Distribute rewards to contributors based on their contributions." | ||
headerSize="max(20px, 3vw)" | ||
preFooter={<NoticeFooter />} | ||
mainPadding | ||
mode="compact" | ||
popOverEffect={false} | ||
isProfile | ||
> | ||
{!isCompetitor && ( | ||
<> | ||
<StandardButton | ||
onClick={() => setJoinModalOpen(true)} | ||
className="gradient-2 rounded-full" | ||
> | ||
Join | ||
</StandardButton> | ||
{joinModalOpen && ( | ||
<JoinDePrizeModal | ||
userTeams={userTeams} | ||
setJoinModalOpen={setJoinModalOpen} | ||
teamContract={teamContract} | ||
handleJoinWithTeam={handleJoinWithTeam} | ||
/> | ||
)} | ||
</> | ||
)} | ||
<div className="pb-32 w-full flex flex-col gap-4 py-2"> | ||
<div className="flex justify-between items-center"> | ||
<h3 className="title-text-colors text-2xl font-GoodTimes"> | ||
Competitors | ||
</h3> | ||
</div> | ||
<div> | ||
{competitors && | ||
competitors.length > 0 && | ||
competitors.map((competitor, i: number) => ( | ||
<div | ||
key={i} | ||
className="flex items-center w-full py-1 text-[17px]" | ||
> | ||
<div className="flex-1 px-8"> | ||
<CompetitorPreview | ||
teamId={competitor.teamId} | ||
teamContract={teamContract} | ||
/> | ||
</div> | ||
</div> | ||
))} | ||
</div> | ||
</div> | ||
</ContentLayout> | ||
</Container> | ||
</section> | ||
) | ||
} |
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,66 @@ | ||
import { XMarkIcon } from '@heroicons/react/24/outline' | ||
import Modal from '@/components/layout/Modal' | ||
import { CompetitorPreview } from '@/components/nance/CompetitorPreview' | ||
import StandardButton from '../layout/StandardButton' | ||
|
||
type JoinDePrizeModalProps = { | ||
userTeams: any[] | ||
setJoinModalOpen: (open: boolean) => void | ||
teamContract: any | ||
handleJoinWithTeam: (teamId: string) => void | ||
} | ||
|
||
export function JoinDePrizeModal({ | ||
userTeams, | ||
setJoinModalOpen, | ||
teamContract, | ||
handleJoinWithTeam, | ||
}: JoinDePrizeModalProps) { | ||
return ( | ||
<Modal id="join-deprize-modal" setEnabled={setJoinModalOpen}> | ||
<button | ||
id="close-modal" | ||
type="button" | ||
className="flex h-10 w-10 border-2 items-center justify-center rounded-full focus:outline-none focus:ring-2 focus:ring-inset focus:ring-white" | ||
onClick={() => setJoinModalOpen(false)} | ||
> | ||
<XMarkIcon className="h-6 w-6 text-white" aria-hidden="true" /> | ||
</button> | ||
<div className="p-6"> | ||
<h3 className="text-xl mb-4">Select a Team or Create a New One</h3> | ||
|
||
{/* Existing Teams */} | ||
{userTeams && userTeams.length > 0 && ( | ||
<div className="mb-6"> | ||
<h4 className="text-lg mb-2">Your Teams</h4> | ||
<div className="space-y-2"> | ||
{userTeams.map((team: any) => ( | ||
<button | ||
key={team.teamId} | ||
onClick={() => handleJoinWithTeam(team.teamId)} | ||
className="w-full p-3 text-left hover:bg-gray-100 rounded-lg transition-colors" | ||
> | ||
<CompetitorPreview | ||
teamId={team.teamId} | ||
teamContract={teamContract} | ||
/> | ||
</button> | ||
))} | ||
</div> | ||
</div> | ||
)} | ||
|
||
{/* Create New Team */} | ||
<div className="mt-4"> | ||
<StandardButton | ||
className="gradient-2 rounded-full w-full" | ||
hoverEffect={false} | ||
link="/team" | ||
> | ||
Create New Team | ||
</StandardButton> | ||
</div> | ||
</div> | ||
</Modal> | ||
) | ||
} |
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 @@ | ||
[{"type":"constructor","inputs":[{"name":"_table_prefix","type":"string","internalType":"string"}],"stateMutability":"nonpayable"},{"type":"function","name":"currId","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getPolicy","inputs":[{"name":"","type":"address","internalType":"address"}],"outputs":[{"name":"","type":"tuple","internalType":"struct TablelandPolicy","components":[{"name":"allowInsert","type":"bool","internalType":"bool"},{"name":"allowUpdate","type":"bool","internalType":"bool"},{"name":"allowDelete","type":"bool","internalType":"bool"},{"name":"whereClause","type":"string","internalType":"string"},{"name":"withCheck","type":"string","internalType":"string"},{"name":"updatableColumns","type":"string[]","internalType":"string[]"}]}],"stateMutability":"payable"},{"type":"function","name":"getPolicy","inputs":[{"name":"caller","type":"address","internalType":"address"},{"name":"","type":"uint256","internalType":"uint256"}],"outputs":[{"name":"","type":"tuple","internalType":"struct TablelandPolicy","components":[{"name":"allowInsert","type":"bool","internalType":"bool"},{"name":"allowUpdate","type":"bool","internalType":"bool"},{"name":"allowDelete","type":"bool","internalType":"bool"},{"name":"whereClause","type":"string","internalType":"string"},{"name":"withCheck","type":"string","internalType":"string"},{"name":"updatableColumns","type":"string[]","internalType":"string[]"}]}],"stateMutability":"payable"},{"type":"function","name":"getTableId","inputs":[],"outputs":[{"name":"","type":"uint256","internalType":"uint256"}],"stateMutability":"view"},{"type":"function","name":"getTableName","inputs":[],"outputs":[{"name":"","type":"string","internalType":"string"}],"stateMutability":"view"},{"type":"function","name":"insertIntoTable","inputs":[{"name":"deprize","type":"uint256","internalType":"uint256"},{"name":"teamId","type":"uint256","internalType":"uint256"},{"name":"metadata","type":"string","internalType":"string"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"owner","inputs":[],"outputs":[{"name":"","type":"address","internalType":"address"}],"stateMutability":"view"},{"type":"function","name":"renounceOwnership","inputs":[],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"setAccessControl","inputs":[{"name":"controller","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"function","name":"transferOwnership","inputs":[{"name":"newOwner","type":"address","internalType":"address"}],"outputs":[],"stateMutability":"nonpayable"},{"type":"event","name":"OwnershipTransferred","inputs":[{"name":"previousOwner","type":"address","indexed":true,"internalType":"address"},{"name":"newOwner","type":"address","indexed":true,"internalType":"address"}],"anonymous":false},{"type":"error","name":"ChainNotSupported","inputs":[{"name":"chainid","type":"uint256","internalType":"uint256"}]},{"type":"error","name":"OwnableInvalidOwner","inputs":[{"name":"owner","type":"address","internalType":"address"}]},{"type":"error","name":"OwnableUnauthorizedAccount","inputs":[{"name":"account","type":"address","internalType":"address"}]}] |
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 @@ | ||
[{"inputs":[{"internalType":"string","name":"_table_prefix","type":"string"},{"internalType":"address","name":"_prize_contract","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"chainid","type":"uint256"}],"name":"ChainNotSupported","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"length","type":"uint256"}],"name":"StringsInsufficientHexLength","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"inputs":[],"name":"_prizeContract","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTableId","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTableName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"deprize","type":"uint256"},{"internalType":"string","name":"distribution","type":"string"}],"name":"insertIntoTable","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"controller","type":"address"}],"name":"setAccessControl","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}] |
Large diffs are not rendered by default.
Oops, something went wrong.
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,39 @@ | ||
import { useEffect, useState } from 'react' | ||
|
||
export default function useTokenBalances( | ||
tokenContract: any, | ||
decimals: number, | ||
addresses: string[] | ||
) { | ||
const [tokenBalances, setTokenBalances] = useState<number[]>([]) | ||
|
||
useEffect(() => { | ||
async function getBalances() { | ||
if (!tokenContract || !addresses || addresses.length === 0) return | ||
|
||
try { | ||
const balances = await Promise.all( | ||
addresses.map(async (address) => { | ||
try { | ||
const balance = await tokenContract.balanceOf(address) // Adjust this for your library | ||
return +balance.toString() / 10 ** decimals | ||
} catch (error) { | ||
console.error( | ||
`Failed to fetch balance for address ${address}:`, | ||
error | ||
) | ||
return 0 | ||
} | ||
}) | ||
) | ||
setTokenBalances(balances) | ||
} catch (error) { | ||
console.error('Error fetching balances:', error) | ||
} | ||
} | ||
|
||
getBalances() | ||
}, [tokenContract, addresses, decimals]) | ||
|
||
return tokenBalances | ||
} |
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,16 @@ | ||
import { useEffect, useState } from 'react' | ||
|
||
export default function useTokenSupply(tokenContract: any, decimals: number) { | ||
const [tokenSupply, setTokenSupply] = useState<number>(0) | ||
|
||
useEffect(() => { | ||
async function getSupply() { | ||
if (!tokenContract) return | ||
const supply = await tokenContract.call('totalSupply') | ||
setTokenSupply(+supply.toString() / 10 ** decimals) | ||
} | ||
|
||
getSupply() | ||
}, [tokenContract]) | ||
return tokenSupply | ||
} |
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.