-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Skip fetching events for UserGrants page.
- When means we skip displaying Recent Activity. That will save 200 requests
- Loading branch information
Showing
4 changed files
with
589 additions
and
292 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 |
---|---|---|
@@ -1,84 +1,160 @@ | ||
import { useState } from 'react'; | ||
import { Heading, Input, Button, FormControl, Flex, Box, FormLabel, Text, Checkbox, Grid, LightMode } from '@chakra-ui/react' | ||
import { Icon } from '@chakra-ui/icons' | ||
import { BsCash } from 'react-icons/bs' | ||
import { ethers } from 'ethers' | ||
import { useRecoilState } from 'recoil' | ||
import { getGrant, redeemGrant } from '../../../lib/store/grants' | ||
import { getEventsByTokenId } from '../../../lib/store/events' | ||
import { Icon } from "@chakra-ui/icons"; | ||
import { | ||
Box, | ||
Button, | ||
Checkbox, | ||
Flex, | ||
FormControl, | ||
FormLabel, | ||
Grid, | ||
Heading, | ||
Input, | ||
LightMode, | ||
Text, | ||
} from "@chakra-ui/react"; | ||
import { ethers } from "ethers"; | ||
import { useState } from "react"; | ||
import { BsCash } from "react-icons/bs"; | ||
import { useRecoilState } from "recoil"; | ||
import { getGrant, redeemGrant } from "../../../lib/store/grants"; | ||
|
||
export default function RedeemTokens({ tokenId }) { | ||
const [grant, setGrant] = useRecoilState(getGrant(tokenId)); | ||
const [events, setEvents] = useRecoilState(getEventsByTokenId(tokenId)); | ||
|
||
const [exchangeMode, setExchangeMode] = useState(false); | ||
const [exchangeTokenAmount, setExchangeTokenAmount] = useState(0); | ||
const [exchangeTokenAddress, setExchangeTokenAddress] = useState(""); | ||
const [loadingRedemption, setLoadingRedemption] = useState(false); | ||
|
||
const vested = parseFloat(ethers.utils.formatUnits(grant.amountVested, 18)) | ||
const redeemed = parseFloat(ethers.utils.formatUnits(grant.amountRedeemed, 18)); | ||
const available = parseFloat(ethers.utils.formatUnits(grant.amountAvailable, 18)); | ||
const vested = parseFloat(ethers.utils.formatUnits(grant.amountVested, 18)); | ||
const redeemed = parseFloat( | ||
ethers.utils.formatUnits(grant.amountRedeemed, 18) | ||
); | ||
const available = parseFloat( | ||
ethers.utils.formatUnits(grant.amountAvailable, 18) | ||
); | ||
|
||
const redeem = () => { | ||
setLoadingRedemption(true) | ||
redeemGrant(grant.tokenId, exchangeMode && exchangeTokenAmount, exchangeMode && exchangeTokenAddress, setGrant, setEvents) | ||
.finally(() => { | ||
setLoadingRedemption(false) | ||
}) | ||
} | ||
setLoadingRedemption(true); | ||
redeemGrant( | ||
grant.tokenId, | ||
exchangeMode && exchangeTokenAmount, | ||
exchangeMode && exchangeTokenAddress, | ||
setGrant | ||
).finally(() => { | ||
setLoadingRedemption(false); | ||
}); | ||
}; | ||
|
||
return ( | ||
<Box | ||
mb={5} | ||
borderRadius="md" | ||
background="gray.900" | ||
py={5} | ||
px={6}> | ||
<Heading size="lg" fontWeight="light"><Icon as={BsCash} boxSize={5} mr={2} />Redeem {grant.tokenSymbol}</Heading> | ||
<Box mb={5} borderRadius="md" background="gray.900" py={5} px={6}> | ||
<Heading size="lg" fontWeight="light"> | ||
<Icon as={BsCash} boxSize={5} mr={2} /> | ||
Redeem {grant.tokenSymbol} | ||
</Heading> | ||
|
||
<Flex align="center" mb={6}> | ||
<Box width="50%" pr={4} pt={2}> | ||
<Text>You can redeem available {grant.tokenSymbol}. Available tokens do not expire.</Text> | ||
<Text> | ||
You can redeem available {grant.tokenSymbol}. Available tokens do | ||
not expire. | ||
</Text> | ||
</Box> | ||
<Box width="50%" pr={4}> | ||
<Flex> | ||
<Box> | ||
<Text fontSize='2xl' fontWeight="medium">{vested.toLocaleString()}</Text> | ||
<Text fontSize='sm' lineHeight={1} textTransform="uppercase" letterSpacing={1.5} opacity={0.8}>Vested</Text> | ||
<Text fontSize="2xl" fontWeight="medium"> | ||
{vested.toLocaleString()} | ||
</Text> | ||
<Text | ||
fontSize="sm" | ||
lineHeight={1} | ||
textTransform="uppercase" | ||
letterSpacing={1.5} | ||
opacity={0.8} | ||
> | ||
Vested | ||
</Text> | ||
</Box> | ||
<Box px={4}> | ||
<Text fontSize='2xl' fontWeight="medium">-</Text> | ||
<Text fontSize="2xl" fontWeight="medium"> | ||
- | ||
</Text> | ||
</Box> | ||
<Box> | ||
<Text fontSize='2xl' fontWeight="medium">{redeemed.toLocaleString()}</Text> | ||
<Text fontSize='sm' lineHeight={1} textTransform="uppercase" letterSpacing={1.5} opacity={0.8}>Redeemed</Text> | ||
<Text fontSize="2xl" fontWeight="medium"> | ||
{redeemed.toLocaleString()} | ||
</Text> | ||
<Text | ||
fontSize="sm" | ||
lineHeight={1} | ||
textTransform="uppercase" | ||
letterSpacing={1.5} | ||
opacity={0.8} | ||
> | ||
Redeemed | ||
</Text> | ||
</Box> | ||
<Box px={4}> | ||
<Text fontSize='2xl' fontWeight="medium">=</Text> | ||
<Text fontSize="2xl" fontWeight="medium"> | ||
= | ||
</Text> | ||
</Box> | ||
<Box> | ||
<Text fontSize='2xl' fontWeight="medium">{available.toLocaleString()}</Text> | ||
<Text fontSize='sm' lineHeight={1} textTransform="uppercase" letterSpacing={1.5} opacity={0.8}>Available</Text> | ||
<Text fontSize="2xl" fontWeight="medium"> | ||
{available.toLocaleString()} | ||
</Text> | ||
<Text | ||
fontSize="sm" | ||
lineHeight={1} | ||
textTransform="uppercase" | ||
letterSpacing={1.5} | ||
opacity={0.8} | ||
> | ||
Available | ||
</Text> | ||
</Box> | ||
</Flex> | ||
</Box> | ||
</Flex> | ||
|
||
<Checkbox mb={4} checked={exchangeMode} onChange={(e) => setExchangeMode(e.target.checked)}>I would like to purchase this {grant.tokenSymbol}.</Checkbox> | ||
<Checkbox | ||
mb={4} | ||
checked={exchangeMode} | ||
onChange={(e) => setExchangeMode(e.target.checked)} | ||
> | ||
I would like to purchase this {grant.tokenSymbol}. | ||
</Checkbox> | ||
|
||
<FormControl d={!exchangeMode && "none"} mb={6}> | ||
<FormLabel>Optional Purchase Price</FormLabel> | ||
|
||
<Grid templateColumns='repeat(2, 1fr)' gap={6}> | ||
<Input type="number" value={exchangeTokenAmount} onChange={(e) => setExchangeTokenAmount(e.target.value)} placeholder='Enter Amount' /> | ||
<Input value={exchangeTokenAddress} onChange={(e) => setExchangeTokenAddress(e.target.value)} placeholder='Enter Token Address' /> | ||
<Grid templateColumns="repeat(2, 1fr)" gap={6}> | ||
<Input | ||
type="number" | ||
value={exchangeTokenAmount} | ||
onChange={(e) => setExchangeTokenAmount(e.target.value)} | ||
placeholder="Enter Amount" | ||
/> | ||
<Input | ||
value={exchangeTokenAddress} | ||
onChange={(e) => setExchangeTokenAddress(e.target.value)} | ||
placeholder="Enter Token Address" | ||
/> | ||
</Grid> | ||
</FormControl> | ||
<LightMode> | ||
<Button onClick={redeem} isLoading={loadingRedemption} isFullWidth size="lg" isDisabled={available == 0} colorScheme="blue">Redeem {available.toLocaleString()} {grant.tokenSymbol}</Button> | ||
<Button | ||
onClick={redeem} | ||
isLoading={loadingRedemption} | ||
isFullWidth | ||
size="lg" | ||
isDisabled={available == 0} | ||
colorScheme="blue" | ||
> | ||
Redeem {available.toLocaleString()} {grant.tokenSymbol} | ||
</Button> | ||
</LightMode> | ||
</Box> | ||
) | ||
); | ||
} | ||
|
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 |
---|---|---|
@@ -1,56 +1,103 @@ | ||
import { useState, useEffect } from 'react' | ||
import { Box, Text, Spinner } from '@chakra-ui/react' | ||
import { InfoOutlineIcon } from '@chakra-ui/icons' | ||
import GrantStatus from './GrantStatus' | ||
import RedeemTokens from './RedeemTokens' | ||
import RedeemAll from './RedeemAll' | ||
import RecentActivity from '../../shared/RecentActivity' | ||
import EtherscanLink from '../../shared/EtherscanLink' | ||
import { useRecoilState } from 'recoil' | ||
import { getGrantsByUser, fetchGrantsByUser } from '../../../lib/store/grants' | ||
import { eventsState, fetchEvents } from '../../../lib/store/events' | ||
import { useAccount } from 'wagmi' | ||
import { useState, useEffect } from "react"; | ||
import { Box, Text, Spinner } from "@chakra-ui/react"; | ||
import { InfoOutlineIcon } from "@chakra-ui/icons"; | ||
import GrantStatus from "./GrantStatus"; | ||
import RedeemTokens from "./RedeemTokens"; | ||
import RedeemAll from "./RedeemAll"; | ||
import EtherscanLink from "../../shared/EtherscanLink"; | ||
import { useRecoilState } from "recoil"; | ||
import { getGrantsByUser, fetchGrantsByUser } from "../../../lib/store/grants"; | ||
import { useAccount, useNetwork } from "wagmi"; | ||
|
||
export default function UserGrants() { | ||
const { address } = useAccount() | ||
const { address } = useAccount(); | ||
const { chain } = useNetwork(); | ||
|
||
const networkId = chain.id; | ||
|
||
const [grants, setGrant] = useRecoilState(getGrantsByUser(address)); | ||
const [events, setEvents] = useRecoilState(eventsState); | ||
const [loadingData, setLoadingData] = useState(true); | ||
|
||
useEffect(() => { | ||
if (address) { | ||
Promise.all([fetchEvents(setEvents), fetchGrantsByUser(setGrant, address)]).finally(() => { | ||
setLoadingData(false) | ||
}) | ||
if (address && networkId) { | ||
fetchGrantsByUser(setGrant, address, networkId).finally(() => { | ||
setLoadingData(false); | ||
}); | ||
} | ||
}, [address]) | ||
}, [address, networkId]); | ||
|
||
const makeGrantElement = (grant, ind) => { | ||
return (<Box key={ind} mb={12}> | ||
<Text fontSize='xs' fontWeight="semibold" lineHeight={1} textTransform="uppercase" letterSpacing={1} mb={4}>Grant #{grant.tokenId.toNumber()}</Text> | ||
<GrantStatus tokenId={grant.tokenId} cancelled={grant.cancelled} /> | ||
{grant.cancelled ? "" : <RedeemTokens tokenId={grant.tokenId} />} | ||
<RecentActivity tokenId={grant.tokenId} /> | ||
<Text fontSize="sm" my={6}><InfoOutlineIcon style={{ transform: 'translateY(-1px)' }} mr={1} /> Each grant is an NFT at the contract address <EtherscanLink | ||
d="inline" | ||
borderBottom="1px rgba(255,255,255,0.66) dotted" | ||
borderRadius={1} | ||
_hover={{ | ||
textDecoration: "none", | ||
borderBottom: "1px rgba(255,255,255,0.9) dotted", | ||
}} path={`/token/${process.env.NEXT_PUBLIC_VESTER_CONTRACT_ADDRESS}`} isExternal>{process.env.NEXT_PUBLIC_VESTER_CONTRACT_ADDRESS}</EtherscanLink></Text> | ||
</Box>) | ||
} | ||
|
||
const noGrantsText = <Text textAlign="center" py={16} fontWeight="thin" fontSize="2xl" letterSpacing={1.5}>There are no grants associated with this wallet.</Text> | ||
|
||
const numNonZeroAvailable = grants.length ? grants.filter(g => g.amountAvailable.gt(0)).length : 0; | ||
|
||
return loadingData ? (address ? <Spinner d="block" mx="auto" my={6} /> : <Text textAlign="center" py={16} fontWeight="thin" fontSize="2xl" letterSpacing={1.5}>Connect a wallet to view grants</Text>) : | ||
(grants.length ? <div> | ||
return ( | ||
<Box key={ind} mb={12}> | ||
<Text | ||
fontSize="xs" | ||
fontWeight="semibold" | ||
lineHeight={1} | ||
textTransform="uppercase" | ||
letterSpacing={1} | ||
mb={4} | ||
> | ||
Grant #{grant.tokenId.toNumber()} | ||
</Text> | ||
<GrantStatus tokenId={grant.tokenId} cancelled={grant.cancelled} /> | ||
{grant.cancelled ? "" : <RedeemTokens tokenId={grant.tokenId} />} | ||
<Text fontSize="sm" my={6}> | ||
<InfoOutlineIcon style={{ transform: "translateY(-1px)" }} mr={1} />{" "} | ||
Each grant is an NFT at the contract address{" "} | ||
<EtherscanLink | ||
d="inline" | ||
borderBottom="1px rgba(255,255,255,0.66) dotted" | ||
borderRadius={1} | ||
_hover={{ | ||
textDecoration: "none", | ||
borderBottom: "1px rgba(255,255,255,0.9) dotted", | ||
}} | ||
path={`/token/${process.env.NEXT_PUBLIC_VESTER_CONTRACT_ADDRESS}`} | ||
isExternal | ||
> | ||
{process.env.NEXT_PUBLIC_VESTER_CONTRACT_ADDRESS} | ||
</EtherscanLink> | ||
</Text> | ||
</Box> | ||
); | ||
}; | ||
|
||
const noGrantsText = ( | ||
<Text | ||
textAlign="center" | ||
py={16} | ||
fontWeight="thin" | ||
fontSize="2xl" | ||
letterSpacing={1.5} | ||
> | ||
There are no grants associated with this wallet. | ||
</Text> | ||
); | ||
|
||
const numNonZeroAvailable = grants.length | ||
? grants.filter((g) => g.amountAvailable.gt(0)).length | ||
: 0; | ||
|
||
return loadingData ? ( | ||
address ? ( | ||
<Spinner d="block" mx="auto" my={6} /> | ||
) : ( | ||
<Text | ||
textAlign="center" | ||
py={16} | ||
fontWeight="thin" | ||
fontSize="2xl" | ||
letterSpacing={1.5} | ||
> | ||
Connect a wallet to view grants | ||
</Text> | ||
) | ||
) : grants.length ? ( | ||
<div> | ||
{numNonZeroAvailable > 1 ? <RedeemAll /> : ""} | ||
{[...grants].reverse().map(makeGrantElement)} | ||
</div> : noGrantsText) | ||
|
||
</div> | ||
) : ( | ||
noGrantsText | ||
); | ||
} |
Oops, something went wrong.