diff --git a/CHANGELOG.md b/CHANGELOG.md index 85623c166..2cd827c8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Improvements +- [#725](https://github.com/alleslabs/celatone-frontend/pull/725) Adjust unsupported asset display in account and contract detail - [#728](https://github.com/alleslabs/celatone-frontend/pull/728) Support tx events for cosmos sdk 0.50 - [#729](https://github.com/alleslabs/celatone-frontend/pull/729) Add missing title for block transactions - [#726](https://github.com/alleslabs/celatone-frontend/pull/726) Add missing title for instantiate permission and adjust contract menu in nav bar diff --git a/src/lib/components/CustomTab.tsx b/src/lib/components/CustomTab.tsx index 1ebfbbdde..3c7666d87 100644 --- a/src/lib/components/CustomTab.tsx +++ b/src/lib/components/CustomTab.tsx @@ -33,6 +33,7 @@ export const CustomTab = ({ lineHeight="24px" letterSpacing="0.4px" variant="ghost-gray" + minW="fit-content" mb={0} sx={{ "&[aria-selected=true]": { diff --git a/src/lib/components/asset/AssetSectionOverview.tsx b/src/lib/components/asset/AssetSectionOverview.tsx new file mode 100644 index 000000000..ce8a8f469 --- /dev/null +++ b/src/lib/components/asset/AssetSectionOverview.tsx @@ -0,0 +1,84 @@ +import { Flex, Grid, GridItem, Text } from "@chakra-ui/react"; + +import { ViewMore } from "../table"; +import type { Option, TokenWithValue, USD } from "lib/types"; + +import { SupportedAssetSectionContent } from "./SupportedAssetSectionContent"; +import { SupportedAssetTitle } from "./SupportedAssetTitle"; +import { UnsupportedAssetSectionContent } from "./UnsupportedAssetSectionContent"; +import { UnsupportedAssetTitle } from "./UnsupportedAssetTitle"; + +const MAX_SUPPORTED_ASSETS_SHOW = 6; +const MAX_UNSUPPORTED_ASSETS_SHOW = 3; + +interface AssetSectionOverviewProps { + isAccount: boolean; + supportedAssets: TokenWithValue[]; + totalSupportedAssetsValue: Option>; + unsupportedAssets: TokenWithValue[]; + onViewMore: () => void; +} + +export const AssetSectionOverview = ({ + isAccount, + supportedAssets, + totalSupportedAssetsValue, + unsupportedAssets, + onViewMore, +}: AssetSectionOverviewProps) => { + if (!supportedAssets.length && !unsupportedAssets.length) + return ( + + This {isAccount ? "address" : "contract"} does not hold any assets + + ); + + return ( + + + + + + + + {onViewMore && supportedAssets.length > MAX_SUPPORTED_ASSETS_SHOW && ( + + )} + + + + + + {onViewMore && + unsupportedAssets.length > MAX_UNSUPPORTED_ASSETS_SHOW && ( + + )} + + + ); +}; diff --git a/src/lib/components/asset/SupportedAssetSectionContent.tsx b/src/lib/components/asset/SupportedAssetSectionContent.tsx new file mode 100644 index 000000000..c7e4ea0bb --- /dev/null +++ b/src/lib/components/asset/SupportedAssetSectionContent.tsx @@ -0,0 +1,47 @@ +import { Flex, Grid, Text } from "@chakra-ui/react"; + +import { TokenCard } from "lib/components/token"; +import type { TokenWithValue } from "lib/types"; + +interface SupportedAssetSectionContentProps { + supportedAssets: TokenWithValue[]; + isAccount?: boolean; + onViewMore?: () => void; +} + +export const SupportedAssetSectionContent = ({ + supportedAssets, + isAccount = false, + onViewMore, +}: SupportedAssetSectionContentProps) => { + if (!supportedAssets.length) + return ( + + + This {isAccount ? "address" : "contract"} does not hold any supported + assets + + + ); + + return ( + + {supportedAssets.map((asset) => ( + + ))} + + ); +}; diff --git a/src/lib/components/asset/SupportedAssetTitle.tsx b/src/lib/components/asset/SupportedAssetTitle.tsx new file mode 100644 index 000000000..d04b8bd9c --- /dev/null +++ b/src/lib/components/asset/SupportedAssetTitle.tsx @@ -0,0 +1,49 @@ +import { Flex, Heading } from "@chakra-ui/react"; + +import { TableTitle } from "../table"; +import { useMobile } from "lib/app-provider"; +import type { Option, TokenWithValue, USD } from "lib/types"; +import { formatPrice } from "lib/utils"; + +interface SupportedAssetTitleProps { + supportedAssets: TokenWithValue[]; + totalSupportedAssetsValue: Option>; +} + +export const SupportedAssetTitle = ({ + supportedAssets, + totalSupportedAssetsValue, +}: SupportedAssetTitleProps) => { + const isMobile = useMobile(); + const isZeroValue = + !totalSupportedAssetsValue || totalSupportedAssetsValue.eq(0); + + return ( + + + {!isMobile && ( + + {totalSupportedAssetsValue + ? formatPrice(totalSupportedAssetsValue) + : "N/A"} + + )} + + ); +}; diff --git a/src/lib/components/asset/UnsupportedAssetSectionContent.tsx b/src/lib/components/asset/UnsupportedAssetSectionContent.tsx new file mode 100644 index 000000000..bc86dd08c --- /dev/null +++ b/src/lib/components/asset/UnsupportedAssetSectionContent.tsx @@ -0,0 +1,48 @@ +import { Flex, Grid, Text } from "@chakra-ui/react"; + +import { UnsupportedToken } from "../token"; +import type { TokenWithValue } from "lib/types"; + +interface UnsupportedAssetSectionContentProps { + unsupportedAssets: TokenWithValue[]; + isAccount?: boolean; + onViewMore?: () => void; +} + +export const UnsupportedAssetSectionContent = ({ + unsupportedAssets, + onViewMore, + isAccount = false, +}: UnsupportedAssetSectionContentProps) => { + if (!unsupportedAssets.length) + return ( + + + This {isAccount ? "address" : "contract"} does not hold any + unsupported assets + + + ); + + return ( + + + {unsupportedAssets.map((asset) => ( + + ))} + + + ); +}; diff --git a/src/lib/components/asset/UnsupportedAssetTitle.tsx b/src/lib/components/asset/UnsupportedAssetTitle.tsx new file mode 100644 index 000000000..8ea72996f --- /dev/null +++ b/src/lib/components/asset/UnsupportedAssetTitle.tsx @@ -0,0 +1,20 @@ +import { Flex } from "@chakra-ui/react"; + +import { TableTitle } from "../table"; +import type { TokenWithValue } from "lib/types"; + +interface UnsupportedAssetTitleProps { + unsupportedAssets: TokenWithValue[]; +} + +export const UnsupportedAssetTitle = ({ + unsupportedAssets, +}: UnsupportedAssetTitleProps) => ( + + + +); diff --git a/src/lib/pages/account-details/components/asset/UserAssetInfoCard.tsx b/src/lib/components/asset/UserAssetInfoCard.tsx similarity index 100% rename from src/lib/pages/account-details/components/asset/UserAssetInfoCard.tsx rename to src/lib/components/asset/UserAssetInfoCard.tsx diff --git a/src/lib/components/asset/index.tsx b/src/lib/components/asset/index.tsx new file mode 100644 index 000000000..3ff7dedb4 --- /dev/null +++ b/src/lib/components/asset/index.tsx @@ -0,0 +1,153 @@ +import { Button, Flex, Heading, Text } from "@chakra-ui/react"; + +import { ErrorFetching } from "../state"; +import { trackUseViewJSON } from "lib/amplitude"; +import { useMobile } from "lib/app-provider"; +import { CustomIcon } from "lib/components/icon"; +import { Loading } from "lib/components/Loading"; +import { TableTitle } from "lib/components/table"; +import { useOpenAssetTab } from "lib/hooks"; +import { useBalanceInfos } from "lib/services/balanceService"; +import type { BechAddr } from "lib/types"; +import { formatPrice } from "lib/utils"; + +import { AssetSectionOverview } from "./AssetSectionOverview"; +import { SupportedAssetSectionContent } from "./SupportedAssetSectionContent"; +import { SupportedAssetTitle } from "./SupportedAssetTitle"; +import { UnsupportedAssetSectionContent } from "./UnsupportedAssetSectionContent"; +import { UnsupportedAssetTitle } from "./UnsupportedAssetTitle"; +import { UserAssetInfoCard } from "./UserAssetInfoCard"; + +interface AssetsSectionProps { + address: BechAddr; + onViewMore?: () => void; + isAccount?: boolean; +} + +export const AssetsSection = ({ + address, + onViewMore, + isAccount = false, +}: AssetsSectionProps) => { + const isMobile = useMobile(); + const openAssetTab = useOpenAssetTab(); + const isMobileOverview = isMobile && !!onViewMore; + + const { + supportedAssets, + totalSupportedAssetsValue, + unsupportedAssets, + totalData = 0, + isLoading, + error, + } = useBalanceInfos(address); + + if (isLoading) return ; + if (error) return ; + + return ( + + {isMobileOverview ? ( + + + + + + + + ) : ( + <> + + + + + {onViewMore ? ( + + ) : ( + <> + + + {isMobile && ( + + + Total Asset Value + + + {totalSupportedAssetsValue + ? formatPrice(totalSupportedAssetsValue) + : "N/A"} + + + )} + + + + + + + + )} + + )} + + ); +}; diff --git a/src/lib/components/delegations/DelegationInfo.tsx b/src/lib/components/delegations/DelegationInfo.tsx index 73ca25443..164eebf00 100644 --- a/src/lib/components/delegations/DelegationInfo.tsx +++ b/src/lib/components/delegations/DelegationInfo.tsx @@ -51,6 +51,7 @@ export const DelegationInfo = ({ alignItems={{ base: "start", md: "center" }} justify="space-between" overflowX="scroll" + overflowY="hidden" > - addrType === "contract_address" - ? getTokenType("cw20") - : getTokenType("native"); - -const UnsupportedToken = ({ token }: { token: TokenWithValue }) => { - const getAddressType = useGetAddressType(); - const tokenType = !token.denom.includes("/") - ? getTokenTypeWithAddress(getAddressType(token.denom)) - : getTokenType(token.denom.split("/")[0]); - - const isMobile = useMobile(); - return ( - - - - - {getTokenLabel(token.denom, token.symbol, !isMobile)} - - {!isMobile && ( - - - - - - )} - - - - {`${tokenType} Token`} - - - - {formatUTokenWithPrecision(token.amount, token.precision ?? 0, false)} - - - ); -}; const unsupportedTokensContent = ( addressType: AddressReturnType diff --git a/src/lib/components/table/ViewMore.tsx b/src/lib/components/table/ViewMore.tsx index ad7775133..f549aaabb 100644 --- a/src/lib/components/table/ViewMore.tsx +++ b/src/lib/components/table/ViewMore.tsx @@ -1,3 +1,4 @@ +import type { BorderProps, LayoutProps } from "@chakra-ui/react"; import { Button, Flex } from "@chakra-ui/react"; import { CustomIcon } from "../icon"; @@ -5,14 +6,20 @@ import { AmpEvent, track } from "lib/amplitude"; interface ViewMoreProps { onClick: () => void; + borderRadius?: BorderProps["borderRadius"]; + minH?: LayoutProps["minH"]; } -export const ViewMore = ({ onClick }: ViewMoreProps) => ( +export const ViewMore = ({ + onClick, + borderRadius = "0", + minH = "64px", +}: ViewMoreProps) => ( - )} - - - ); -}; diff --git a/src/lib/pages/account-details/components/asset/AssetSectionContent.tsx b/src/lib/pages/account-details/components/asset/AssetSectionContent.tsx deleted file mode 100644 index 4a7164746..000000000 --- a/src/lib/pages/account-details/components/asset/AssetSectionContent.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Grid, Text } from "@chakra-ui/react"; - -import { ErrorFetching } from "lib/components/state"; -import { TokenCard } from "lib/components/token"; -import type { TokenWithValue } from "lib/types"; - -interface AssetSectionContentProps { - supportedAssets: TokenWithValue[]; - error: Error; - isAccount?: boolean; -} - -export const AssetSectionContent = ({ - supportedAssets, - error, - isAccount = false, -}: AssetSectionContentProps) => { - if (error) return ; - - return supportedAssets.length ? ( - - {supportedAssets.map((asset) => ( - - ))} - - ) : ( - - This {isAccount ? "address" : "contract"} does not hold any supported - assets - - ); -}; diff --git a/src/lib/pages/account-details/components/asset/index.tsx b/src/lib/pages/account-details/components/asset/index.tsx deleted file mode 100644 index 7e4505cd6..000000000 --- a/src/lib/pages/account-details/components/asset/index.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import { Flex } from "@chakra-ui/react"; - -import { useMobile } from "lib/app-provider"; -import { CustomIcon } from "lib/components/icon"; -import { Loading } from "lib/components/Loading"; -import { TableTitle, ViewMore } from "lib/components/table"; -import { useBalanceInfos } from "lib/services/balanceService"; -import type { BechAddr } from "lib/types"; - -import { AssetCta } from "./AssetCta"; -import { AssetSectionContent } from "./AssetSectionContent"; -import { UserAssetInfoCard } from "./UserAssetInfoCard"; - -const MAX_ASSETS_SHOW = 8; - -interface AssetsSectionProps { - address: BechAddr; - onViewMore?: () => void; - isAccount?: boolean; -} - -export const AssetsSection = ({ - address, - onViewMore, - isAccount = false, -}: AssetsSectionProps) => { - const isMobile = useMobile(); - const isMobileOverview = isMobile && !!onViewMore; - - const { - supportedAssets, - totalSupportedAssetsValue, - unsupportedAssets, - isLoading, - totalData = 0, - error, - } = useBalanceInfos(address); - - if (isLoading) return ; - - const tableTitle = ; - const totalAssetValueInfo = ( - - ); - return ( - - {isMobileOverview ? ( - - - {tableTitle} - {totalAssetValueInfo} - - - - ) : ( - <> - {tableTitle} - - {totalAssetValueInfo} - {!isMobile && ( - - )} - - - {isMobile && ( - - )} - {onViewMore && supportedAssets.length > MAX_ASSETS_SHOW && ( - - )} - - )} - - ); -}; diff --git a/src/lib/pages/account-details/index.tsx b/src/lib/pages/account-details/index.tsx index 5224002d1..da1f1b43c 100644 --- a/src/lib/pages/account-details/index.tsx +++ b/src/lib/pages/account-details/index.tsx @@ -21,6 +21,7 @@ import { useValidateAddress, useWasmConfig, } from "lib/app-provider"; +import { AssetsSection } from "lib/components/asset"; import { Breadcrumb } from "lib/components/Breadcrumb"; import { CustomTab } from "lib/components/CustomTab"; import { CustomIcon } from "lib/components/icon"; @@ -35,7 +36,6 @@ import type { Addr, BechAddr, HexAddr, Option } from "lib/types"; import { truncate } from "lib/utils"; import { AccountHeader } from "./components/AccountHeader"; -import { AssetsSection } from "./components/asset"; import { ModuleLists } from "./components/modules"; import { NftsOverview, NftsSection } from "./components/nfts"; import { ResourceOverview, ResourceSection } from "./components/resources"; @@ -264,7 +264,7 @@ const AccountDetailsBody = ({ - + + @@ -162,7 +162,7 @@ export const ContractTop = ({ {displayName} - + diff --git a/src/lib/pages/contract-details/index.tsx b/src/lib/pages/contract-details/index.tsx index 89200bbcf..cf5a7e1ab 100644 --- a/src/lib/pages/contract-details/index.tsx +++ b/src/lib/pages/contract-details/index.tsx @@ -11,8 +11,6 @@ import { observer } from "mobx-react-lite"; import { useRouter } from "next/router"; import { useCallback, useEffect } from "react"; -import { DelegationsSection } from "../../components/delegations"; -import { AssetsSection } from "../account-details/components/asset"; import { AmpEvent, track, trackUseTab } from "lib/amplitude"; import { useValidateAddress, @@ -20,7 +18,9 @@ import { useMobile, useInternalNavigate, } from "lib/app-provider"; +import { AssetsSection } from "lib/components/asset"; import { CustomTab } from "lib/components/CustomTab"; +import { DelegationsSection } from "lib/components/delegations"; import { CustomIcon } from "lib/components/icon"; import { Loading } from "lib/components/Loading"; import PageContainer from "lib/components/PageContainer"; @@ -41,6 +41,8 @@ import { TabIndex, zContractDetailsQueryParams } from "./types"; const InvalidContract = () => ; +const tableHeaderId = "contractDetailsTab"; + interface ContractDetailsBodyProps { contractAddress: BechAddr32; tab: TabIndex; @@ -105,11 +107,10 @@ const ContractDetailsBody = observer( lazyBehavior="keepMounted" > Overview @@ -133,13 +134,9 @@ const ContractDetailsBody = observer( - + - + - + - + - +