Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(components): implement unsupported assets in contract details page #94

Merged
merged 11 commits into from
Jan 20, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Features

- [#94](https://github.com/alleslabs/celatone-frontend/pull/94) Add unsupported assets in contract details page
- [#72](https://github.com/alleslabs/celatone-frontend/pull/72) Fix general wording and grammar
- [#110](https://github.com/alleslabs/celatone-frontend/pull/110) Fix proposal detail rendering
- [#109](https://github.com/alleslabs/celatone-frontend/pull/109) Fix incorrect rendering of zero value badges
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,6 @@
"next": "^13.0.0",
"next-pwa": "^5.6.0",
"next-seo": "^5.8.0",
"numeral": "^2.0.6",
"react": "^18.2.0",
"react-ace": "^10.1.0",
"react-dom": "^18.2.0",
Expand Down
131 changes: 131 additions & 0 deletions src/lib/components/modal/UnsupportedTokensModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import {
Modal,
ModalHeader,
Flex,
Icon,
Text,
ModalOverlay,
ModalContent,
ModalCloseButton,
useDisclosure,
ModalBody,
Button,
Heading,
Box,
} from "@chakra-ui/react";
import router from "next/router";
import { useMemo } from "react";
import { MdAttachMoney } from "react-icons/md";

import { Copier } from "../Copier";
import { ExplorerLink } from "../ExplorerLink";
import type { BalanceWithAssetInfo, Balance } from "lib/types";
import {
getFirstQueryParam,
getTokenType,
truncate,
formatToken,
} from "lib/utils";

interface UnsupportedTokensModalProps {
unsupportedAssets: BalanceWithAssetInfo[];
}

interface UnsupportedTokenProps {
balance: Balance;
}

const UnsupportedToken = ({ balance }: UnsupportedTokenProps) => {
// TODO - Move this to utils
const [tokenLabel, tokenType] = useMemo(() => {
const splitId = balance.id.split("/");
bkioshn marked this conversation as resolved.
Show resolved Hide resolved
const type = !balance.id.includes("/")
? getTokenType(balance.type)
: getTokenType(splitId[0]);
if (splitId[1]) {
splitId[1] = truncate(splitId[1]);
}
const label = splitId.length === 1 ? balance.id : splitId.join("/");
return [label, type];
}, [balance]);

return (
<Flex
borderRadius="8px"
bg="gray.900"
justify="space-between"
p={4}
role="group"
>
<Flex direction="column" maxW="70%">
<Flex direction="row" alignItems="center">
<Text variant="body2" className="ellipsis">
{tokenLabel}
</Text>
<Box _groupHover={{ display: "flex" }} display="none">
<Copier value={balance.id} />
</Box>
</Flex>
<Text variant="body3" color="text.dark">
{`${tokenType} Token`}
</Text>
</Flex>
<Text variant="body2" fontWeight="900">
{formatToken(balance.amount, balance.precision)}
</Text>
</Flex>
);
};

export const UnsupportedTokensModal = ({
unsupportedAssets,
}: UnsupportedTokensModalProps) => {
const contractAddress = getFirstQueryParam(router.query.contractAddress);

const { isOpen, onOpen, onClose } = useDisclosure();

if (unsupportedAssets.length === 0) return null;

return (
<>
<Flex onClick={onOpen}>
<Button variant="ghost" color="text.dark" mb={1} fontWeight={500}>
{`View ${unsupportedAssets.length} Unsupported Assets`}
</Button>
</Flex>
<Modal isOpen={isOpen} onClose={onClose} isCentered>
<ModalOverlay />
<ModalContent w="700px">
<ModalHeader>
<Flex w="full" direction="row" alignItems="center" gap={2} pt={1}>
<Icon as={MdAttachMoney} boxSize={5} color="gray.600" />
<Heading variant="h5" as="h5">
Unsupported Assets
</Heading>
</Flex>
</ModalHeader>

<ModalCloseButton color="gray.600" />
<ModalBody maxH="400px" overflow="overlay">
<Flex direction="column" gap={5}>
<Flex direction="row" gap={4}>
<Text variant="body2" fontWeight="700">
Contract Address
</Text>
<ExplorerLink value={contractAddress} type="contract_address" />
</Flex>
<Flex gap={2} direction="column">
{unsupportedAssets.map((asset) => (
<UnsupportedToken
balance={asset.balance}
key={asset.balance.id}
/>
))}
</Flex>
</Flex>
</ModalBody>
</ModalContent>
</Modal>
</>
);
};
63 changes: 41 additions & 22 deletions src/lib/pages/contract-details/components/token/TokenSection.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Grid, Text } from "@chakra-ui/react";
import { Flex, Grid, Text } from "@chakra-ui/react";
import { useMemo, useState } from "react";

import { ShowMoreButton } from "lib/components/button";
import { UnsupportedTokensModal } from "lib/components/modal/UnsupportedTokensModal";
import type { BalanceWithAssetInfo, Option } from "lib/types";

import { TokenCard } from "./TokenCard";
Expand All @@ -11,37 +12,55 @@ interface TokenSectionProps {
}
export const TokenSection = ({ balances }: TokenSectionProps) => {
const [showMore, setShowMore] = useState(false);
const unsupportedAssets = useMemo(
() => balances?.filter((balance) => !balance.assetInfo) ?? [],
[balances]
);

const supportedAssets = useMemo(
() => balances?.filter((balance) => balance.assetInfo) ?? [],
[balances]
);

if (!balances?.length)
const renderContext = () => {
bkioshn marked this conversation as resolved.
Show resolved Hide resolved
if (!balances?.length) {
return (
<Text variant="body2" color="text.dark" mb={1} fontWeight={500}>
This contract does not hold any assets
</Text>
);
}
return (
<Text variant="body2" color="text.dark" mb={1} fontWeight={500}>
This contract does not hold any assets
</Text>
<>
<Grid gridGap={4} gridTemplateColumns="repeat(4, 1fr)">
{supportedAssets.map((balance, index) => {
if (!showMore && index >= 4) {
return null;
}
return <TokenCard key={balance.balance.id} userBalance={balance} />;
})}
</Grid>
{supportedAssets.length > 4 && (
<ShowMoreButton
showMoreText="View All Assets"
showLessText="View Less Assets"
toggleShowMore={showMore}
setToggleShowMore={() => setShowMore(!showMore)}
/>
)}
</>
);
};

return (
<>
{/* TODO - Implement unsupported assets */}
<Grid gridGap={4} gridTemplateColumns="repeat(4, 1fr)">
{supportedAssets.map((balance, index) => {
if (!showMore && index >= 4) {
return null;
}
return <TokenCard key={balance.balance.id} userBalance={balance} />;
})}
</Grid>
{supportedAssets.length > 4 && (
<ShowMoreButton
showMoreText="View All Assets"
showLessText="View Less Assets"
toggleShowMore={showMore}
setToggleShowMore={() => setShowMore(!showMore)}
/>
)}
<Flex justify="space-between">
<Text variant="body2" color="text.dark" mb={1} fontWeight={500}>
Assets
</Text>
<UnsupportedTokensModal unsupportedAssets={unsupportedAssets} />
</Flex>
{renderContext()}
</>
);
};
4 changes: 0 additions & 4 deletions src/lib/pages/contract-details/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
Tabs,
TabPanels,
TabPanel,
Text,
} from "@chakra-ui/react";
import { observer } from "mobx-react-lite";
import { useRouter } from "next/router";
Expand Down Expand Up @@ -58,9 +57,6 @@ const ContractDetailsBody = observer(
<ContractTop contractData={contractData} />
{/* Tokens Section */}
<Flex direction="column">
<Text variant="body2" color="text.dark" mb={1} fontWeight={500}>
Assets
</Text>
<TokenSection balances={contractData.balances} />
</Flex>
{/* Contract Description Section */}
Expand Down
2 changes: 2 additions & 0 deletions src/lib/utils/formatter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ export * from "./denom";
export * from "./camelToSnake";
export * from "./snakeToCamel";
export * from "./formatBalanceWithDenom";
export * from "./tokenType";
export * from "./token";
4 changes: 2 additions & 2 deletions src/lib/utils/formatter/token.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import type { BigSource } from "big.js";
import big from "big.js";
import numeral from "numeral";

export const formatDemimal =
({
Expand All @@ -23,8 +22,9 @@ export const formatDemimal =
if (num === "NaN") return fallbackValue;

const [i, d] = num.split(".");
const thousands = /\B(?=(\d{3})+(?!\d))/g;

const ii = delimiter ? numeral(i).format("0,0") : i;
const ii = delimiter ? i.replace(thousands, ",") : i;
const dd = d ? `.${d}` : "";

return (ii === "0" && num[0] === "-" ? "-" : "") + ii + dd;
Expand Down
9 changes: 9 additions & 0 deletions src/lib/utils/formatter/tokenType.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export const getTokenType = (type: string) => {
switch (type.toLowerCase()) {
case "ibc":
case "cw20":
return type.toUpperCase();
default:
return type.charAt(0).toUpperCase() + type.slice(1).toLowerCase();
}
};
5 changes: 0 additions & 5 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8331,11 +8331,6 @@ nullthrows@^1.1.1:
resolved "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz"
integrity sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==

numeral@^2.0.6:
version "2.0.6"
resolved "https://registry.yarnpkg.com/numeral/-/numeral-2.0.6.tgz#4ad080936d443c2561aed9f2197efffe25f4e506"
integrity sha512-qaKRmtYPZ5qdw4jWJD6bxEf1FJEqllJrwxCLIm0sQU/A7v2/czigzOb+C2uSiFsa9lBUzeH7M1oK+Q+OLxL3kA==

object-assign@^4.0.1, object-assign@^4.1.0, object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
Expand Down