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: create new token card and format mechanism #223

Merged
merged 6 commits into from
Feb 24, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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

- [#223](https://github.com/alleslabs/celatone-frontend/pull/223) Newer version of token card and format mechanism
- [#218](https://github.com/alleslabs/celatone-frontend/pull/218) Add instantiated and admin contracts of an account
- [#192](https://github.com/alleslabs/celatone-frontend/pull/192) Add alternative sidebar with only icons
- [#210](https://github.com/alleslabs/celatone-frontend/pull/210) New design for token card, currently support price
Expand Down
65 changes: 38 additions & 27 deletions src/lib/pages/contract-details/components/token/TokenCard.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Flex, Image, Text, Tooltip } from "@chakra-ui/react";
import { Badge, Flex, Image, Text, Tooltip } from "@chakra-ui/react";
import { useState } from "react";

import { Copier } from "lib/components/copy";
Expand All @@ -17,48 +17,53 @@ interface TokenCardProps {
export const TokenCard = ({ userBalance }: TokenCardProps) => {
const [logoError, setLogoError] = useState(false);
const { symbol, price, amount, precision, id } = userBalance.balance;
const tokenInfoText = price
? `1 ${symbol} = $${formatPrice(price as USD<number>)}
Token ID: ${id}`
: "No Price Data";

return (
<Tooltip
hasArrow
label={tokenInfoText}
label={`Token ID: ${id}`}
placement="top"
bg="honeydew.darker"
maxW="240px"
whiteSpace="pre-line"
textAlign="center"
>
<Flex
direction="column"
h="101px"
gap={2}
p={2}
p={3}
background="pebble.900"
borderRadius="8px"
alignItems="center"
_hover={{
bgColor: "pebble.800",
"& .copy-button": { display: "flex" },
}}
>
{!logoError ? (
<Image
boxSize={8}
src={userBalance.assetInfo?.logo}
alt={symbol}
onError={() => setLogoError(true)}
/>
) : (
<NAToken />
)}
<div>
<Flex gap={1} align="center">
<Text fontWeight="700" variant="body2">
{formatUTokenWithPrecision(amount as U<Token>, precision)}
<Flex
gap={1}
alignItems="center"
borderBottom="1px solid"
borderBottomColor="pebble.700"
pb={2}
>
{!logoError ? (
<Image
boxSize={6}
src={userBalance.assetInfo?.logo}
alt={symbol}
onError={() => setLogoError(true)}
/>
) : (
<NAToken />
)}
<Text variant="body2" className="ellipsis" maxW="91">
{symbol}
</Text>
<Text variant="body2">{symbol}</Text>
<Badge variant="gray" ml="6px">
{price ? formatPrice(price as USD<number>) : "N/A"}
</Badge>
<Copier
value={id}
copyLabel="Token ID Copied!"
Expand All @@ -67,14 +72,20 @@ export const TokenCard = ({ userBalance }: TokenCardProps) => {
className="copy-button"
/>
</Flex>
<Text variant="body3" color="text.dark">
</div>

<Flex direction="column">
<Text fontWeight="700" variant="body2">
{formatUTokenWithPrecision(amount as U<Token>, precision)}
</Text>
<Text variant="body3" color="grey.400">
{price
? `$${formatPrice(
? `(${formatPrice(
calAssetValueWithPrecision(userBalance.balance)
)}`
: "-"}
)})`
: "N/A"}
</Text>
</div>
</Flex>
</Flex>
</Tooltip>
);
Expand Down
50 changes: 42 additions & 8 deletions src/lib/utils/formatter/token.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import type { BigSource } from "big.js";
import type { Big, BigSource } from "big.js";
import big from "big.js";

import type { Token, U, USD } from "lib/types";

const B = 1000000000;

export const formatDemimal =
({
decimalPoints,
Expand Down Expand Up @@ -31,26 +33,58 @@ export const formatDemimal =
return (ii === "0" && num[0] === "-" ? "-" : "") + ii + dd;
};

const d2Formatter = formatDemimal({ decimalPoints: 2, delimiter: true });
const d6Formatter = formatDemimal({ decimalPoints: 6, delimiter: true });

export const toToken = (
uAmount: U<Token<BigSource>>,
precision: number
): Token<BigSource> =>
big(uAmount).div(big(10).pow(precision)) as Token<BigSource>;

/**
* @remarks
* If token is more than or equal to 1 billion, should add postfix M and format to 2 decimal point
*
*/
export const formatUTokenWithPrecision = (
amount: U<Token<BigSource>>,
precision: number
): string => d6Formatter(toToken(amount, precision), "0");
): string => {
const token = toToken(amount, precision);
const bToken = token as Big;
if (bToken.gte(B)) {
return `${formatDemimal({ decimalPoints: 2, delimiter: true })(
bToken.div(B),
"0.00"
)}M`;
}
return formatDemimal({ decimalPoints: precision, delimiter: true })(
token,
"0.00"
);
};

const d2Formatter = formatDemimal({ decimalPoints: 2, delimiter: true });
const d6Formatter = formatDemimal({ decimalPoints: 6, delimiter: true });

/**
* @remarks
* If the value is greater than or equal to 1, should return 2 decimal points else 6 decimal points
*
* Condition for price rendering
* $0 or >= $1 -> 2d
* < $1 -> 6d
* <$0.000001 -> <$0.000001
*/
export const formatPrice = (value: USD<BigSource>): string => {
const price = big(value);
return price.gte(1) ? d2Formatter(price, "0") : d6Formatter(price, "0");
const lowestThreshold = 0.000001;

const d2 = d2Formatter(price, "0.00");
const d6 = d6Formatter(price, "0.00");

if (price.eq(0) || price.gte(1)) {
return `$${d2}`;
}

if (price.lt(lowestThreshold)) {
return `<$${lowestThreshold}`;
}
return `$${d6}`;
};