Skip to content

Commit

Permalink
Skip fetching events for UserGrants page.
Browse files Browse the repository at this point in the history
- When means we skip displaying Recent Activity. That will save 200 requests
  • Loading branch information
0xjocke committed May 27, 2024
1 parent d8144b8 commit 7f75734
Show file tree
Hide file tree
Showing 4 changed files with 589 additions and 292 deletions.
158 changes: 117 additions & 41 deletions frontend/components/Home/UserGrants/RedeemTokens.js
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>
)
);
}

133 changes: 90 additions & 43 deletions frontend/components/Home/UserGrants/index.js
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
);
}
Loading

0 comments on commit 7f75734

Please sign in to comment.