From b927eca951f233c6cd2cc336fccdf67794c70fc4 Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Mon, 16 Sep 2024 17:17:13 +0300 Subject: [PATCH 01/18] added native token routes --- src/helpers/urlBuilder.ts | 3 ++ src/redux/slices/economics.ts | 2 +- src/redux/slices/stats.ts | 3 ++ src/routes/layouts/index.ts | 1 + src/routes/layouts/nativeTokenLayout.ts | 45 +++++++++++++++++++++++++ src/routes/routes.tsx | 4 ++- src/types/stats.types.ts | 2 ++ 7 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 src/routes/layouts/nativeTokenLayout.ts diff --git a/src/helpers/urlBuilder.ts b/src/helpers/urlBuilder.ts index 7744c15af..5ebd32c40 100644 --- a/src/helpers/urlBuilder.ts +++ b/src/helpers/urlBuilder.ts @@ -67,6 +67,9 @@ export const urlBuilder = { tokenDetailsLockedAccounts: (tokenId: string) => `/tokens/${tokenId}/locked-accounts`, tokenDetailsRoles: (tokenId: string) => `/tokens/${tokenId}/roles`, + nativeTokenDetails: (egldLabel: string) => `/${egldLabel.toLowerCase()}`, + nativeTokenDetailsAccounts: (egldLabel: string) => + `/${egldLabel.toLowerCase()}/accounts`, collections: (params?: GetCollectionsType) => { const urlSearch = params ? new URLSearchParams(params as Record).toString() diff --git a/src/redux/slices/economics.ts b/src/redux/slices/economics.ts index e888ef7e9..2218263d8 100644 --- a/src/redux/slices/economics.ts +++ b/src/redux/slices/economics.ts @@ -28,7 +28,7 @@ export const getInitialEconomicsState = (): EconomicsSliceType => { baseApr: 0, tokenMarketCap: 0 }, - isFetched: false + isFetched: undefined }; }; diff --git a/src/redux/slices/stats.ts b/src/redux/slices/stats.ts index 60f32f86a..342f66459 100644 --- a/src/redux/slices/stats.ts +++ b/src/redux/slices/stats.ts @@ -10,6 +10,7 @@ export const getInitialStatsState = (): StatsSliceType => { blocks: 0, accounts: 0, transactions: 0, + scResults: 0, refreshRate: 0, epoch: 0, roundsPassed: 0, @@ -25,6 +26,7 @@ export const getInitialStatsState = (): StatsSliceType => { blocks: ELLIPSIS, accounts: ELLIPSIS, transactions: ELLIPSIS, + scResults: ELLIPSIS, refreshRate: 0, epoch: 0, roundsPassed: 0, @@ -52,6 +54,7 @@ export const statsSlice = createSlice({ state.blocks = action.payload.blocks; state.accounts = action.payload.accounts; state.transactions = action.payload.transactions; + state.scResults = action.payload.scResults; state.refreshRate = action.payload.refreshRate; state.epoch = action.payload.epoch; state.roundsPassed = action.payload.roundsPassed; diff --git a/src/routes/layouts/index.ts b/src/routes/layouts/index.ts index df1e5020f..94dc4c9b5 100644 --- a/src/routes/layouts/index.ts +++ b/src/routes/layouts/index.ts @@ -1,6 +1,7 @@ export * from './accountLayout'; export * from './blockLayout'; export * from './collectionLayout'; +export * from './nativeTokenLayout'; export * from './nftLayout'; export * from './tokenLayout'; export * from './transactionsLayout'; diff --git a/src/routes/layouts/nativeTokenLayout.ts b/src/routes/layouts/nativeTokenLayout.ts new file mode 100644 index 000000000..c63db58a9 --- /dev/null +++ b/src/routes/layouts/nativeTokenLayout.ts @@ -0,0 +1,45 @@ +import { networks } from 'config'; + +import { NativeTokenLayout } from 'layouts/NativeTokenLayout'; +import { NativeTokenAccounts } from 'pages/NativeToken/NativeTokenAccounts'; +import { NativeTokenTransactions } from 'pages/NativeToken/NativeTokenTransactions'; + +import { TitledRouteObject } from '../routes'; + +export const nativeTokenLayout: TitledRouteObject[] = []; + +networks.forEach((network) => { + if (!network.egldLabel) { + return; + } + + const networkPath = `/${network.egldLabel?.toLowerCase()}`; + + const routeExists = nativeTokenLayout.find( + (route) => route.path === networkPath + ); + + if (routeExists) { + return; + } + + nativeTokenLayout.push({ + path: networkPath, + preventScroll: true, + Component: NativeTokenLayout, + children: [ + { + path: networkPath, + title: network.egldLabel, + preventScroll: true, + Component: NativeTokenTransactions + }, + { + path: `${networkPath}/accounts`, + title: 'Holders', + preventScroll: true, + Component: NativeTokenAccounts + } + ] + }); +}); diff --git a/src/routes/routes.tsx b/src/routes/routes.tsx index d4e1149eb..6216761ec 100644 --- a/src/routes/routes.tsx +++ b/src/routes/routes.tsx @@ -20,6 +20,7 @@ import { blocksRoutes, collectionLayout, collectionRoutes, + nativeTokenLayout, nftLayout, nftRoutes, tokenLayout, @@ -107,7 +108,8 @@ const mainRoutes: TitledRouteObject[] = [ ...nftLayout, ...tokenLayout, ...transactionsLayout, - ...validatorLayout + ...validatorLayout, + ...nativeTokenLayout ] } ]; diff --git a/src/types/stats.types.ts b/src/types/stats.types.ts index 40b557911..cdf1e2698 100644 --- a/src/types/stats.types.ts +++ b/src/types/stats.types.ts @@ -5,6 +5,7 @@ export interface StatsType { blocks: number; accounts: number; transactions: number; + scResults: number; refreshRate: number; epoch: number; roundsPassed: number; @@ -25,6 +26,7 @@ export interface StatsSliceType extends SliceType { blocks: string; accounts: string; transactions: string; + scResults: string; refreshRate: number; epoch: number; roundsPassed: number; From 401f42242dfdb684062789976536f90d8d9a9468 Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Mon, 16 Sep 2024 17:18:12 +0300 Subject: [PATCH 02/18] use AccountTable component on TokenAccounts and Token LockedAccounts --- .../AccountsTable/AccountsTable.tsx | 170 +++++++++++++++++ src/components/AccountsTable/index.ts | 1 + src/components/index.ts | 1 + src/helpers/parseAmount.ts | 9 +- src/pages/TokenDetails/TokenAccounts.tsx | 173 +++--------------- .../TokenDetails/TokenLockedAccounts.tsx | 164 +++-------------- 6 files changed, 231 insertions(+), 287 deletions(-) create mode 100644 src/components/AccountsTable/AccountsTable.tsx create mode 100644 src/components/AccountsTable/index.ts diff --git a/src/components/AccountsTable/AccountsTable.tsx b/src/components/AccountsTable/AccountsTable.tsx new file mode 100644 index 000000000..3bd432321 --- /dev/null +++ b/src/components/AccountsTable/AccountsTable.tsx @@ -0,0 +1,170 @@ +import BigNumber from 'bignumber.js'; +import classNames from 'classnames'; + +import { + FormatAmount, + AccountLink, + FormatNumber, + FormatUSD, + PercentageBar, + Pager, + PageSize, + Loader, + PageState +} from 'components'; +import { DECIMALS } from 'config'; +import { formatBigNumber, parseAmount } from 'helpers'; +import { faUser } from 'icons/regular'; +import { AccountType, TokenLockedAccountType, WithClassnameType } from 'types'; + +export interface AccountsTableUIType extends WithClassnameType { + accounts: AccountType[] | TokenLockedAccountType[]; + accountsCount: number; + title?: React.ReactNode; + message?: string; + decimals?: number; + showValue?: boolean; + supply?: string | number; + price?: number; + isDataReady?: boolean; + hasNameColumn?: boolean; + isNativeToken?: boolean; +} + +export const AccountsTable = ({ + accounts, + accountsCount, + title, + message = 'Accounts', + decimals = DECIMALS, + showValue, + price, + supply, + isNativeToken = false, + isDataReady, + hasNameColumn +}: AccountsTableUIType) => { + const hasSupply = new BigNumber(supply ?? 0).isGreaterThan(0); + const showAccounts = isDataReady === true && accounts.length > 0; + + return ( +
+
+
+ {title} + 0} + className='d-flex ms-auto me-auto me-sm-0' + /> +
+
+ {showAccounts ? ( + <> +
+
+ + + + + {hasNameColumn && } + + {hasSupply && ( + + )} + {showValue && } + + + + {accounts.map((account) => { + const holdingsPercentage = + hasSupply && supply + ? new BigNumber(account.balance) + .times(100) + .dividedBy(parseAmount(supply, decimals)) + : new BigNumber(0); + const hasAccountName = + hasNameColumn && (account as TokenLockedAccountType).name; + + return ( + + + {hasAccountName && ( + + )} + + {hasSupply && ( + + )} + {showValue && ( + + )} + + ); + })} + +
AddressNameBalancePercentageValue
+
+ +
+
{(account as TokenLockedAccountType).name} + + +
+ +
+ +
+ +
+
+
+
+ + 0} /> +
+ + ) : ( + <> + {isDataReady === undefined && } + {isDataReady === false && ( + + )} + {isDataReady === true && accounts.length === 0 && ( + + )} + + )} +
+ ); +}; diff --git a/src/components/AccountsTable/index.ts b/src/components/AccountsTable/index.ts new file mode 100644 index 000000000..20e7f94d1 --- /dev/null +++ b/src/components/AccountsTable/index.ts @@ -0,0 +1 @@ +export * from './AccountsTable'; diff --git a/src/components/index.ts b/src/components/index.ts index 0b65de469..55283003b 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -4,6 +4,7 @@ import SharedIdentity from './SharedIdentity'; import TransactionActionBlock from './TransactionActionBlock'; export * from './AccountName'; +export * from './AccountsTable'; export * from './BlockGasUsed'; export * from './BlocksTable'; export * from './Cards'; diff --git a/src/helpers/parseAmount.ts b/src/helpers/parseAmount.ts index 135575d4c..8562d1c9b 100644 --- a/src/helpers/parseAmount.ts +++ b/src/helpers/parseAmount.ts @@ -2,8 +2,13 @@ import BigNumber from 'bignumber.js'; import { ELLIPSIS } from 'appConstants'; import { DECIMALS } from 'config'; -export function parseAmount(amount: BigNumber.Value, numDecimals?: number) { - const amountAsBigInteger = new BigNumber(amount); +export function parseAmount( + amount: string | number | BigNumber, + numDecimals?: number +) { + const amountAsBigInteger = BigNumber.isBigNumber(amount) + ? amount + : new BigNumber(amount); if (!amountAsBigInteger.isInteger() || amountAsBigInteger.isNegative()) { return ELLIPSIS; } diff --git a/src/pages/TokenDetails/TokenAccounts.tsx b/src/pages/TokenDetails/TokenAccounts.tsx index 7a7706f75..6724e6600 100644 --- a/src/pages/TokenDetails/TokenAccounts.tsx +++ b/src/pages/TokenDetails/TokenAccounts.tsx @@ -1,30 +1,16 @@ -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useState } from 'react'; import BigNumber from 'bignumber.js'; -import classNames from 'classnames'; import { useSelector } from 'react-redux'; import { useSearchParams } from 'react-router-dom'; import { LOW_LIQUIDITY_MARKET_CAP_DISPLAY_TRESHOLD } from 'appConstants'; -import { - Loader, - Pager, - PageSize, - FormatAmount, - PageState, - AccountLink, - FormatNumber, - FormatUSD, - PercentageBar -} from 'components'; -import { formatBigNumber, parseAmount } from 'helpers'; +import { AccountsTable } from 'components'; import { useAdapter, useGetPage } from 'hooks'; -import { faUser } from 'icons/regular'; import { TokenTabs } from 'layouts/TokenLayout/TokenTabs'; import { activeNetworkSelector, tokenSelector } from 'redux/selectors'; import { AccountType } from 'types'; export const TokenDetailsAccounts = () => { - const ref = useRef(null); const [searchParams] = useSearchParams(); const { token } = useSelector(tokenSelector); const { id: activeNetworkId } = useSelector(activeNetworkSelector); @@ -44,20 +30,20 @@ export const TokenDetailsAccounts = () => { const [accounts, setAccounts] = useState([]); const [accountsCount, setAccountsCount] = useState(0); - const [dataReady, setDataReady] = useState(); + const [isDataReady, setIsDataReady] = useState(); const fetchAccounts = () => { Promise.all([ getTokenAccounts({ tokenId: identifier, page, size }), getTokenAccountsCount({ tokenId: identifier }) ]).then(([tokenAccountsData, tokenAccountsCountData]) => { - if (ref.current !== null) { - if (tokenAccountsData.success && tokenAccountsCountData.success) { - setAccounts(tokenAccountsData.data); - setAccountsCount(tokenAccountsCountData.data); - setDataReady(true); - } + if (tokenAccountsData.success && tokenAccountsCountData.success) { + setAccounts(tokenAccountsData.data); + setAccountsCount(tokenAccountsCountData.data); } + setIsDataReady( + tokenAccountsData.success && tokenAccountsCountData.success + ); }); }; @@ -65,131 +51,26 @@ export const TokenDetailsAccounts = () => { fetchAccounts(); }, [activeNetworkId, totalAccounts, searchParams, identifier]); - const hasSupply = new BigNumber(supply ?? 0).isGreaterThan(0); - const showAccounts = dataReady === true && accounts.length > 0; - const showValue = + const showValue = Boolean( price && - marketCap && - (!isLowLiquidity || - new BigNumber(marketCap).isLessThan( - LOW_LIQUIDITY_MARKET_CAP_DISPLAY_TRESHOLD - )); + marketCap && + (!isLowLiquidity || + new BigNumber(marketCap).isLessThan( + LOW_LIQUIDITY_MARKET_CAP_DISPLAY_TRESHOLD + )) + ); return ( -
-
-
-
- - 0} - className='d-flex ms-auto me-auto me-sm-0' - /> -
-
- {showAccounts ? ( - <> -
-
- - - - - - {hasSupply && ( - - )} - {showValue && } - - - - {accounts.map((account) => { - const holdingsPercentage = new BigNumber(account.balance) - .times(100) - .dividedBy(parseAmount(supply, decimals)); - - return ( - - - - {hasSupply && ( - - )} - {showValue && ( - - )} - - ); - })} - -
- Address - BalancePercentageValue
-
- -
-
- - -
- -
- -
- -
-
-
-
- - 0} /> -
- - ) : ( - <> - {dataReady === undefined && ( - - )} - {dataReady === false && ( - - )} - {dataReady === true && accounts.length === 0 && ( - - )} - - )} -
-
+ } + accounts={accounts} + accountsCount={accountsCount} + price={price} + supply={supply} + decimals={decimals} + showValue={showValue} + isDataReady={isDataReady} + message='Token Holders' + /> ); }; diff --git a/src/pages/TokenDetails/TokenLockedAccounts.tsx b/src/pages/TokenDetails/TokenLockedAccounts.tsx index abbe041d8..9ac8bfc0f 100644 --- a/src/pages/TokenDetails/TokenLockedAccounts.tsx +++ b/src/pages/TokenDetails/TokenLockedAccounts.tsx @@ -1,27 +1,15 @@ -import { useEffect, useRef, useState } from 'react'; +import { useEffect, useState } from 'react'; import BigNumber from 'bignumber.js'; import { useSelector } from 'react-redux'; import { LOW_LIQUIDITY_MARKET_CAP_DISPLAY_TRESHOLD } from 'appConstants'; -import { - Loader, - AccountLink, - PageState, - FormatAmount, - FormatNumber, - FormatUSD, - PercentageBar -} from 'components'; -import { formatBigNumber, parseAmount } from 'helpers'; +import { AccountsTable } from 'components'; import { useAdapter } from 'hooks'; -import { faUser } from 'icons/regular'; import { TokenTabs } from 'layouts/TokenLayout/TokenTabs'; import { activeNetworkSelector, tokenSelector } from 'redux/selectors'; import { TokenLockedAccountType } from 'types'; export const TokenDetailsLockedAccounts = () => { - const ref = useRef(null); - const { id: activeNetworkId } = useSelector(activeNetworkSelector); const { token } = useSelector(tokenSelector); const { identifier, price, marketCap, supply, isLowLiquidity, decimals } = @@ -31,16 +19,14 @@ export const TokenDetailsLockedAccounts = () => { const [tokenLockedAccounts, setTokenLockedAccounts] = useState< TokenLockedAccountType[] >([]); - const [dataReady, setDataReady] = useState(); + const [isDataReady, setIsDataReady] = useState(); const fetchTokenLockedAccounts = () => { getTokenSupply({ tokenId: identifier }).then(({ data, success }) => { - if (ref.current !== null) { - if (success && data?.lockedAccounts) { - setTokenLockedAccounts(data.lockedAccounts); - } - setDataReady(success); + if (success && data?.lockedAccounts) { + setTokenLockedAccounts(data.lockedAccounts); } + setIsDataReady(success && data?.lockedAccounts); }); }; @@ -48,127 +34,27 @@ export const TokenDetailsLockedAccounts = () => { fetchTokenLockedAccounts(); }, [activeNetworkId, identifier]); - const showLockedAccounts = - dataReady === true && tokenLockedAccounts.length > 0; - const hasSupply = new BigNumber(supply ?? 0).isGreaterThan(0); - const showValue = + const showValue = Boolean( price && - marketCap && - (!isLowLiquidity || - new BigNumber(marketCap).isLessThan( - LOW_LIQUIDITY_MARKET_CAP_DISPLAY_TRESHOLD - )); + marketCap && + (!isLowLiquidity || + new BigNumber(marketCap).isLessThan( + LOW_LIQUIDITY_MARKET_CAP_DISPLAY_TRESHOLD + )) + ); return ( -
-
-
-
- -
-
- {showLockedAccounts ? ( - <> -
-
- - - - - - - {hasSupply && ( - - )} - {showValue && } - - - - {tokenLockedAccounts.map((lockedAccount) => { - const holdingsPercentage = new BigNumber( - lockedAccount.balance - ) - .times(100) - .dividedBy(parseAmount(supply, decimals)); - - return ( - - - - - {hasSupply && ( - - )} - {showValue && ( - - )} - - ); - })} - -
AddressNameBalancePercentageValue
- - {lockedAccount.name} - - -
- -
- -
- -
-
-
-
- - ) : ( - <> - {dataReady === undefined && ( - - )} - {dataReady === false && ( - - )} - {dataReady === true && tokenLockedAccounts.length === 0 && ( - - )} - - )} -
-
+ } + accounts={tokenLockedAccounts} + accountsCount={tokenLockedAccounts.length} + price={price} + supply={supply} + decimals={decimals} + showValue={showValue} + isDataReady={isDataReady} + hasNameColumn={true} + message='Token Locked Accounts' + /> ); }; From c4241bae60d7e657948b44a50a2ffc5aecaa61b0 Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Mon, 16 Sep 2024 17:20:07 +0300 Subject: [PATCH 03/18] added useGetNativeTokenDetails hook to be used in token details display --- src/hooks/useGetNativeTokenDetails.ts | 83 +++++++++++++++++++ .../components/TokensTable/NativeTokenRow.tsx | 48 +++++------ 2 files changed, 103 insertions(+), 28 deletions(-) create mode 100644 src/hooks/useGetNativeTokenDetails.ts diff --git a/src/hooks/useGetNativeTokenDetails.ts b/src/hooks/useGetNativeTokenDetails.ts new file mode 100644 index 000000000..e2642822f --- /dev/null +++ b/src/hooks/useGetNativeTokenDetails.ts @@ -0,0 +1,83 @@ +import BigNumber from 'bignumber.js'; +import { useSelector } from 'react-redux'; + +import { BRAND_NAME } from 'appConstants'; +import { DECIMALS } from 'config'; +import { useIsSovereign } from 'hooks'; +import { + economicsSelector, + statsSelector, + activeNetworkSelector +} from 'redux/selectors'; +import { TokenAssetType } from 'types'; + +export interface NativeTokenType { + identifier: string; + ticker?: string; + name: string; + balance?: string; + decimals?: number; + supply: string | number; + circulatingSupply: string | number; + transactions: number; + accounts: number; + price?: number; + marketCap?: number; + valueUsd?: number; + assets?: TokenAssetType; + transfersCount?: number; +} + +export const useGetNativeTokenDetails = () => { + const { egldLabel, name, decimals } = useSelector(activeNetworkSelector); + const { + unprocessed: { price, marketCap, totalSupply, circulatingSupply } + } = useSelector(economicsSelector); + const { + unprocessed: { accounts, transactions, scResults } + } = useSelector(statsSelector); + + const isSovereign = useIsSovereign(); + + const displayName = `${isSovereign ? name : BRAND_NAME} ${egldLabel}`; + const description = isSovereign + ? `${egldLabel} Token is native to ${name ?? BRAND_NAME}` + : `The ${BRAND_NAME} eGold (${egldLabel}) Token is native to the ${BRAND_NAME} Network and will be used for everything from staking, governance, transactions, smart contracts and validator rewards.`; + + const nativeTokenTransactions = new BigNumber(transactions || 0).minus( + scResults || 0 + ); + const assets = { + name: displayName, + description, + ...(!isSovereign + ? { + website: 'https://multiversx.com/', + social: { + blog: 'https://multiversx.com/blog', + x: 'https://x.com/MultiversX', + telegram: 'https://t.me/MultiversX', + discord: 'https://discord.com/invite/multiversxbuilders', + facebook: 'https://www.facebook.com/MultiversX/', + linkedin: 'https://www.linkedin.com/company/multiversx' + } + } + : {}) + }; + + return { + identifier: egldLabel, + ticker: egldLabel, + name: egldLabel, + decimals: Number(decimals ?? DECIMALS), + supply: totalSupply, + circulatingSupply, + transactions: nativeTokenTransactions.isGreaterThanOrEqualTo(0) + ? nativeTokenTransactions.toNumber() + : 0, + accounts, + price, + marketCap, + assets + }; +}; diff --git a/src/pages/Tokens/components/TokensTable/NativeTokenRow.tsx b/src/pages/Tokens/components/TokensTable/NativeTokenRow.tsx index 97cb37f03..1931be66e 100644 --- a/src/pages/Tokens/components/TokensTable/NativeTokenRow.tsx +++ b/src/pages/Tokens/components/TokensTable/NativeTokenRow.tsx @@ -1,15 +1,15 @@ import BigNumber from 'bignumber.js'; import { useSelector } from 'react-redux'; -import { BRAND_NAME } from 'appConstants'; import { NativeTokenLogo } from 'components'; import { pagerHelper } from 'components/Pager/helpers/pagerHelper'; +import { formatBigNumber } from 'helpers'; import { useGetPage, useGetSearch, useGetSort, - useIsSovereign, - useIsNativeTokenSearched + useIsNativeTokenSearched, + useGetNativeTokenDetails } from 'hooks'; import { economicsSelector, @@ -35,7 +35,7 @@ export const NativeTokenRow = ({ index: number; totalTokens: number; }) => { - const { egldLabel, name } = useSelector(activeNetworkSelector); + const { egldLabel } = useSelector(activeNetworkSelector); const { isFetched: isEconomicsFetched, price, @@ -43,21 +43,13 @@ export const NativeTokenRow = ({ circulatingSupply, unprocessed: unProcessedEconomics } = useSelector(economicsSelector); - const { - isFetched: isStatsFetched, - accounts, - transactions, - unprocessed: unProcessedStats - } = useSelector(statsSelector); + const { isFetched: isStatsFetched } = useSelector(statsSelector); + + const { assets, accounts, transactions } = useGetNativeTokenDetails(); const { page, size } = useGetPage(); const { search } = useGetSearch(); const { sort, order } = useGetSort(); - const isSovereign = useIsSovereign(); - - const description = isSovereign - ? `${egldLabel} Token is native to ${name ?? BRAND_NAME}` - : `The ${BRAND_NAME} eGold (${egldLabel}) Token is native to the ${BRAND_NAME} Network and will be used for everything from staking, governance, transactions, smart contracts and validator rewards.`; const showOnSearch = useIsNativeTokenSearched(); let showOnFilter = (!page || page === 1) && index === 0; @@ -85,8 +77,8 @@ export const NativeTokenRow = ({ price: unProcessedEconomics.price, marketCap: unProcessedEconomics.marketCap, circulatingSupply: unProcessedEconomics.circulatingSupply, - accounts: unProcessedStats.accounts, - transactions: unProcessedStats.transactions + accounts, + transactions }; const tokenValue = currentToken[sort as keyof TokenType]; @@ -159,23 +151,23 @@ export const NativeTokenRow = ({
{egldLabel} -
- {description} -
+ {assets?.description && ( +
+ {assets.description} +
+ )}
- - {isSovereign ? name : BRAND_NAME} {egldLabel} - + {assets?.name ?? egldLabel} {price} {circulatingSupply} {marketCap} - {accounts} - {transactions} + {formatBigNumber({ value: accounts })} + {formatBigNumber({ value: transactions })} ); }; From 7c86f1b635ccf212edbdbcb668388033d0819073 Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Mon, 16 Sep 2024 17:20:41 +0300 Subject: [PATCH 04/18] use custom icon component on hero details card --- .../HeroDetailsCard/HeroDetailsCard.tsx | 68 +++++-------------- .../heroDetailsCard.styles.scss | 16 +++-- src/hooks/index.ts | 1 + 3 files changed, 31 insertions(+), 54 deletions(-) diff --git a/src/components/HeroDetailsCard/HeroDetailsCard.tsx b/src/components/HeroDetailsCard/HeroDetailsCard.tsx index 190f259a1..9cd033c3f 100644 --- a/src/components/HeroDetailsCard/HeroDetailsCard.tsx +++ b/src/components/HeroDetailsCard/HeroDetailsCard.tsx @@ -24,6 +24,7 @@ export interface HeroDetailsCardUIType extends WithClassnameType { title?: React.ReactNode; titleContent?: React.ReactNode; icon?: string; + iconComponent?: React.ReactNode; iconPlaceholder?: React.ReactNode; isVerified?: boolean; verifiedComponent?: React.ReactNode; @@ -40,6 +41,7 @@ export const HeroDetailsCard = ({ title, titleContent, icon, + iconComponent, iconPlaceholder, isVerified, verifiedComponent, @@ -55,7 +57,7 @@ export const HeroDetailsCard = ({ const dispatch = useDispatch(); const isMainnet = useIsMainnet(); const hasStatCards = statsCards.length > 0 || smallStatsCards.length > 0; - const hasIcon = Boolean(icon || iconPlaceholder); + const hasIcon = Boolean(icon || iconPlaceholder || iconComponent); useEffect(() => { if (seoDetails?.completeDetails && isMainnet) { @@ -70,6 +72,18 @@ export const HeroDetailsCard = ({ } }, [seoDetails, isMainnet]); + const Icon = ({ className }: WithClassnameType) => ( + + {hasIcon && <>{iconComponent}} + + ); + return (
- - {hasIcon && ( - <> - {icon ? ( - - ) : ( - iconPlaceholder - )} - - )} - + +
{title && (
- - {hasIcon && ( - <> - {icon ? ( - - ) : ( - iconPlaceholder - )} - - )} - +

Date: Mon, 16 Sep 2024 17:22:17 +0300 Subject: [PATCH 05/18] added base Native Token Layout --- .../NativeTokenDetailsCard.tsx | 135 ++++++++++++++++++ .../NativeTokenLayout/NativeTokenLayout.tsx | 52 +++++++ .../NativeTokenLayout/NativeTokenTabs.tsx | 24 ++++ src/layouts/NativeTokenLayout/index.ts | 1 + 4 files changed, 212 insertions(+) create mode 100644 src/layouts/NativeTokenLayout/NativeTokenDetailsCard.tsx create mode 100644 src/layouts/NativeTokenLayout/NativeTokenLayout.tsx create mode 100644 src/layouts/NativeTokenLayout/NativeTokenTabs.tsx create mode 100644 src/layouts/NativeTokenLayout/index.ts diff --git a/src/layouts/NativeTokenLayout/NativeTokenDetailsCard.tsx b/src/layouts/NativeTokenLayout/NativeTokenDetailsCard.tsx new file mode 100644 index 000000000..021850706 --- /dev/null +++ b/src/layouts/NativeTokenLayout/NativeTokenDetailsCard.tsx @@ -0,0 +1,135 @@ +import { useMemo } from 'react'; +import BigNumber from 'bignumber.js'; + +import { + SocialIcons, + SocialWebsite, + HeroDetailsCard, + FormatUSD, + NativeTokenLogo +} from 'components'; +import { useGetNativeTokenDetails } from 'hooks'; + +export const NativeTokenDetailsCard = () => { + const { + name, + ticker, + decimals, + assets, + price, + marketCap, + accounts, + transactions, + supply, + circulatingSupply + } = useGetNativeTokenDetails(); + + const title = assets?.name ?? name; + + const detailItems = useMemo( + () => [ + assets?.description + ? { + title: 'Description', + value: ( +
+ {assets.description} +
+ ) + } + : {}, + assets?.website + ? { + title: 'Website', + value: + } + : {}, + assets?.social && Object.keys(assets.social).length > 0 + ? { + title: 'Other Links', + value: + } + : {}, + !assets && ticker !== name ? { title: 'Name', value: name } : {}, + { title: 'Decimals', value: decimals } + ], + [assets] + ); + + const statsCards = useMemo( + () => [ + ...(price && marketCap + ? [ + { + title: 'Price', + value: ( + + ) + }, + { + title: 'Market Cap', + value: ( + + ) + } + ] + : []), + { title: 'Holders', value: new BigNumber(accounts).toFormat() }, + { + title: 'Transactions', + value: new BigNumber(transactions || 0).toFormat() + } + ], + [price, marketCap, transactions, accounts] + ); + + const smallStatsCards = useMemo( + () => [ + supply && new BigNumber(supply).isGreaterThanOrEqualTo(0) + ? { + title: 'Supply', + value: new BigNumber(supply).toFormat(0) + } + : {}, + circulatingSupply && + new BigNumber(circulatingSupply).isGreaterThanOrEqualTo(0) + ? { + title: 'Circulating', + value: new BigNumber(circulatingSupply).toFormat(0) + } + : {} + ], + [supply, circulatingSupply] + ); + + return ( + + } + seoDetails={{ + title, + description: assets?.description, + completeDetails: Boolean(assets) + }} + className='token-details' + detailItems={detailItems} + statsCards={statsCards} + smallStatsCards={smallStatsCards} + /> + ); +}; diff --git a/src/layouts/NativeTokenLayout/NativeTokenLayout.tsx b/src/layouts/NativeTokenLayout/NativeTokenLayout.tsx new file mode 100644 index 000000000..3ba29f0c4 --- /dev/null +++ b/src/layouts/NativeTokenLayout/NativeTokenLayout.tsx @@ -0,0 +1,52 @@ +import { useSelector } from 'react-redux'; +import { Outlet } from 'react-router-dom'; + +import { Loader, PageState } from 'components'; +import { useHasGrowthWidgets } from 'hooks'; +import { faCoins } from 'icons/regular'; +import { ChartPrice } from 'pages/Home/components/ChartPrice'; +import { ChartStake } from 'pages/Home/components/ChartStake'; +import { activeNetworkSelector, economicsSelector } from 'redux/selectors'; + +import { NativeTokenDetailsCard } from './NativeTokenDetailsCard'; + +export const NativeTokenLayout = () => { + const hasGrowthWidgets = useHasGrowthWidgets(); + const { egldLabel } = useSelector(activeNetworkSelector); + const { isFetched } = useSelector(economicsSelector); + + const loading = isFetched === undefined; + const failed = isFetched === false; + + if (loading) { + return ; + } + + if (failed) { + return ( + + {egldLabel} +

+ } + isError + /> + ); + } + + return ( +
+ + {hasGrowthWidgets && ( +
+ + +
+ )} + +
+ ); +}; diff --git a/src/layouts/NativeTokenLayout/NativeTokenTabs.tsx b/src/layouts/NativeTokenLayout/NativeTokenTabs.tsx new file mode 100644 index 000000000..ae87a0c37 --- /dev/null +++ b/src/layouts/NativeTokenLayout/NativeTokenTabs.tsx @@ -0,0 +1,24 @@ +import { useSelector } from 'react-redux'; + +import { Tabs } from 'components/Tabs'; +import { urlBuilder } from 'helpers'; +import { activeNetworkSelector } from 'redux/selectors'; + +export const NativeTokenTabs = () => { + const { egldLabel = 'egld' } = useSelector(activeNetworkSelector); + + const tabs = [ + { + tabLabel: 'Transactions', + tabTo: urlBuilder.nativeTokenDetails(egldLabel), + activationRoutes: [urlBuilder.nativeTokenDetails(egldLabel)] + }, + { + tabLabel: 'Holders', + tabTo: urlBuilder.nativeTokenDetailsAccounts(egldLabel), + activationRoutes: [urlBuilder.nativeTokenDetailsAccounts(egldLabel)] + } + ]; + + return ; +}; diff --git a/src/layouts/NativeTokenLayout/index.ts b/src/layouts/NativeTokenLayout/index.ts new file mode 100644 index 000000000..c14abb02b --- /dev/null +++ b/src/layouts/NativeTokenLayout/index.ts @@ -0,0 +1 @@ +export * from './NativeTokenLayout'; From 94fc831994a42ff92911515803f9f52d3290d08b Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Mon, 16 Sep 2024 17:22:35 +0300 Subject: [PATCH 06/18] added Native Token Accounts and Transactions Tabs --- src/pages/NativeToken/NativeTokenAccounts.tsx | 56 +++++++++++++++++++ .../NativeToken/NativeTokenTransactions.tsx | 55 ++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 src/pages/NativeToken/NativeTokenAccounts.tsx create mode 100644 src/pages/NativeToken/NativeTokenTransactions.tsx diff --git a/src/pages/NativeToken/NativeTokenAccounts.tsx b/src/pages/NativeToken/NativeTokenAccounts.tsx new file mode 100644 index 000000000..2f2276b6e --- /dev/null +++ b/src/pages/NativeToken/NativeTokenAccounts.tsx @@ -0,0 +1,56 @@ +import { useEffect, useState } from 'react'; +import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom'; + +import { AccountsTable } from 'components'; +import { useAdapter, useGetNativeTokenDetails, useGetPage } from 'hooks'; +import { NativeTokenTabs } from 'layouts/NativeTokenLayout/NativeTokenTabs'; +import { activeNetworkSelector } from 'redux/selectors'; +import { AccountType } from 'types'; + +export const NativeTokenAccounts = () => { + const [searchParams] = useSearchParams(); + const { id: activeNetworkId } = useSelector(activeNetworkSelector); + + const { price, marketCap, supply, decimals } = useGetNativeTokenDetails(); + const { page, size } = useGetPage(); + const { getAccounts, getAccountsCount } = useAdapter(); + + const [accounts, setAccounts] = useState([]); + const [accountsCount, setAccountsCount] = useState(0); + const [isDataReady, setIsDataReady] = useState(); + + const fetchAccounts = () => { + Promise.all([getAccounts({ page, size }), getAccountsCount({})]).then( + ([tokenAccountsData, tokenAccountsCountData]) => { + if (tokenAccountsData.success && tokenAccountsCountData.success) { + setAccounts(tokenAccountsData.data); + setAccountsCount(tokenAccountsCountData.data); + } + setIsDataReady( + tokenAccountsData.success && tokenAccountsCountData.success + ); + } + ); + }; + + useEffect(() => { + fetchAccounts(); + }, [activeNetworkId, searchParams]); + + const showValue = Boolean(price && marketCap); + + return ( + } + accounts={accounts} + accountsCount={accountsCount} + price={price} + supply={supply} + decimals={decimals} + showValue={showValue} + isNativeToken={true} + isDataReady={isDataReady} + /> + ); +}; diff --git a/src/pages/NativeToken/NativeTokenTransactions.tsx b/src/pages/NativeToken/NativeTokenTransactions.tsx new file mode 100644 index 000000000..df388071e --- /dev/null +++ b/src/pages/NativeToken/NativeTokenTransactions.tsx @@ -0,0 +1,55 @@ +import { useEffect, useRef } from 'react'; +import { useSelector } from 'react-redux'; +import { useSearchParams } from 'react-router-dom'; + +import { TransactionsTable } from 'components'; +import { useAdapter, useFetchTransactions } from 'hooks'; +import { NativeTokenTabs } from 'layouts/NativeTokenLayout/NativeTokenTabs'; +import { activeNetworkSelector } from 'redux/selectors'; +import { TransactionFiltersEnum } from 'types'; + +export const NativeTokenTransactions = () => { + const ref = useRef(null); + const [searchParams] = useSearchParams(); + const { id: activeNetworkId, egldLabel } = useSelector(activeNetworkSelector); + const { getTransfers, getTransfersCount } = useAdapter(); + + const { + fetchTransactions, + transactions, + totalTransactions, + isDataReady, + dataChanged + } = useFetchTransactions(getTransfers, getTransfersCount, { + token: egldLabel + }); + + useEffect(() => { + if (ref.current !== null) { + fetchTransactions(); + } + }, [activeNetworkId]); + + useEffect(() => { + fetchTransactions(Boolean(searchParams.toString())); + }, [searchParams]); + + return ( +
+
+
+ } + dataChanged={dataChanged} + isDataReady={isDataReady} + inactiveFilters={[TransactionFiltersEnum.token]} + showLockedAccounts + /> +
+
+
+ ); +}; From 83def99f4eee1869da21f622593fc7ad2399d5e2 Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Mon, 16 Sep 2024 18:02:45 +0300 Subject: [PATCH 07/18] move ChartContractsTransactions to widgets --- src/assets/scss/components/_widgets.scss | 2 +- src/layouts/Layout/components/Hero/Hero.tsx | 2 +- src/pages/Analytics/Analytics.tsx | 3 +- src/pages/Home/Home.tsx | 3 +- .../components/ChartArea/styles.module.scss | 33 ------------------- .../ChartArea/ChartArea.tsx | 0 .../ChartArea/index.ts | 0 .../ChartArea/types.ts | 0 .../ChartContractsTransactions.tsx | 4 +-- .../chartContractsTransactions.styles.scss | 0 .../ChartContractsTransactions/index.ts | 0 .../ChartContractsTransactions/types.ts | 0 .../HeroApplications/HeroApplications.tsx | 2 +- src/widgets/index.ts | 1 + 14 files changed, 8 insertions(+), 42 deletions(-) delete mode 100644 src/pages/Home/components/ChartArea/styles.module.scss rename src/{pages/Home/components => widgets/ChartContractsTransactions}/ChartArea/ChartArea.tsx (100%) rename src/{pages/Home/components => widgets/ChartContractsTransactions}/ChartArea/index.ts (100%) rename src/{pages/Home/components => widgets/ChartContractsTransactions}/ChartArea/types.ts (100%) rename src/{pages/Home/components => widgets}/ChartContractsTransactions/ChartContractsTransactions.tsx (98%) rename src/{pages/Home/components => widgets}/ChartContractsTransactions/chartContractsTransactions.styles.scss (100%) rename src/{pages/Home/components => widgets}/ChartContractsTransactions/index.ts (100%) rename src/{pages/Home/components => widgets}/ChartContractsTransactions/types.ts (100%) diff --git a/src/assets/scss/components/_widgets.scss b/src/assets/scss/components/_widgets.scss index c1855ed0f..745a03f16 100644 --- a/src/assets/scss/components/_widgets.scss +++ b/src/assets/scss/components/_widgets.scss @@ -6,4 +6,4 @@ @import '../../../widgets/StatsCard/statsCard.styles.scss'; // temp -@import '../../../pages/Home/components/ChartContractsTransactions/chartContractsTransactions.styles.scss'; +@import '../../../widgets/ChartContractsTransactions/chartContractsTransactions.styles.scss'; diff --git a/src/layouts/Layout/components/Hero/Hero.tsx b/src/layouts/Layout/components/Hero/Hero.tsx index c2ff64721..8c97ade8f 100644 --- a/src/layouts/Layout/components/Hero/Hero.tsx +++ b/src/layouts/Layout/components/Hero/Hero.tsx @@ -10,12 +10,12 @@ import { useHasGrowthWidgets, usePageStats } from 'hooks'; -import { ChartContractsTransactions } from 'pages/Home/components/ChartContractsTransactions'; import { activeNetworkSelector, defaultNetworkSelector } from 'redux/selectors'; import { analyticsRoutes } from 'routes'; import { AccountsStatsCard, BlockHeightStatsCard, + ChartContractsTransactions, TransactionsStatsCard, ValidatorsStatusCard, HeroPills, diff --git a/src/pages/Analytics/Analytics.tsx b/src/pages/Analytics/Analytics.tsx index d2db6dbd5..f8cb67160 100644 --- a/src/pages/Analytics/Analytics.tsx +++ b/src/pages/Analytics/Analytics.tsx @@ -9,10 +9,9 @@ import { useAdapter, useHasGrowthWidgets, useNetworkRoute } from 'hooks'; import { AnalyticsChart } from 'pages/AnalyticsCompare/AnalyticsChart'; import { FailedAnalytics } from 'pages/AnalyticsCompare/components/FailedAnalytics'; import { NoAnalytics } from 'pages/AnalyticsCompare/components/NoAnalytics'; -import { ChartContractsTransactions } from 'pages/Home/components/ChartContractsTransactions'; import { activeNetworkSelector } from 'redux/selectors'; import { analyticsRoutes } from 'routes'; -import { MostUsed } from 'widgets'; +import { ChartContractsTransactions, MostUsed } from 'widgets'; import { ChartWrapper } from './components/ChartWrapper'; diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index bc9ee634f..7ba6f4de9 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -1,7 +1,6 @@ import { useHasGrowthWidgets } from 'hooks'; -import { MostUsed } from 'widgets'; +import { ChartContractsTransactions, MostUsed } from 'widgets'; -import { ChartContractsTransactions } from './components/ChartContractsTransactions'; import { ChartPrice } from './components/ChartPrice'; import { ChartStake } from './components/ChartStake'; import { EconomicsCard } from './components/EconomicsCard'; diff --git a/src/pages/Home/components/ChartArea/styles.module.scss b/src/pages/Home/components/ChartArea/styles.module.scss deleted file mode 100644 index d0794f80e..000000000 --- a/src/pages/Home/components/ChartArea/styles.module.scss +++ /dev/null @@ -1,33 +0,0 @@ -.tooltip { - border-radius: 0.5rem; - border: 1px solid var(--black); - overflow: hidden; - font-size: 0.75rem; - background-color: var(--neutral-900); - text-align: left; - opacity: 0.8; - - .date { - line-height: 1; - color: var(--neutral-400); - background-color: var(--black); - padding: 0.25rem 0.5rem; - } - - .items { - padding: 0 0.5rem; - - .item { - margin-bottom: 0.25rem; - padding: 0.25rem 0; - - &:last-child { - margin-bottom: 0; - } - - .label { - display: block; - } - } - } -} diff --git a/src/pages/Home/components/ChartArea/ChartArea.tsx b/src/widgets/ChartContractsTransactions/ChartArea/ChartArea.tsx similarity index 100% rename from src/pages/Home/components/ChartArea/ChartArea.tsx rename to src/widgets/ChartContractsTransactions/ChartArea/ChartArea.tsx diff --git a/src/pages/Home/components/ChartArea/index.ts b/src/widgets/ChartContractsTransactions/ChartArea/index.ts similarity index 100% rename from src/pages/Home/components/ChartArea/index.ts rename to src/widgets/ChartContractsTransactions/ChartArea/index.ts diff --git a/src/pages/Home/components/ChartArea/types.ts b/src/widgets/ChartContractsTransactions/ChartArea/types.ts similarity index 100% rename from src/pages/Home/components/ChartArea/types.ts rename to src/widgets/ChartContractsTransactions/ChartArea/types.ts diff --git a/src/pages/Home/components/ChartContractsTransactions/ChartContractsTransactions.tsx b/src/widgets/ChartContractsTransactions/ChartContractsTransactions.tsx similarity index 98% rename from src/pages/Home/components/ChartContractsTransactions/ChartContractsTransactions.tsx rename to src/widgets/ChartContractsTransactions/ChartContractsTransactions.tsx index 3ccb616b3..81d7275fd 100644 --- a/src/pages/Home/components/ChartContractsTransactions/ChartContractsTransactions.tsx +++ b/src/widgets/ChartContractsTransactions/ChartContractsTransactions.tsx @@ -14,9 +14,9 @@ import { TransactionsStatisticsLabelEnum } from 'types'; +import { ChartArea } from './ChartArea'; +import { PayloadType } from './ChartArea/types'; import { ChartContractsTransactionsUIType } from './types'; -import { ChartArea } from '../ChartArea'; -import { PayloadType } from '../ChartArea/types'; const getSum = ( first: GrowthChartDataType[], diff --git a/src/pages/Home/components/ChartContractsTransactions/chartContractsTransactions.styles.scss b/src/widgets/ChartContractsTransactions/chartContractsTransactions.styles.scss similarity index 100% rename from src/pages/Home/components/ChartContractsTransactions/chartContractsTransactions.styles.scss rename to src/widgets/ChartContractsTransactions/chartContractsTransactions.styles.scss diff --git a/src/pages/Home/components/ChartContractsTransactions/index.ts b/src/widgets/ChartContractsTransactions/index.ts similarity index 100% rename from src/pages/Home/components/ChartContractsTransactions/index.ts rename to src/widgets/ChartContractsTransactions/index.ts diff --git a/src/pages/Home/components/ChartContractsTransactions/types.ts b/src/widgets/ChartContractsTransactions/types.ts similarity index 100% rename from src/pages/Home/components/ChartContractsTransactions/types.ts rename to src/widgets/ChartContractsTransactions/types.ts diff --git a/src/widgets/HeroApplications/HeroApplications.tsx b/src/widgets/HeroApplications/HeroApplications.tsx index 824d1b662..8969654c9 100644 --- a/src/widgets/HeroApplications/HeroApplications.tsx +++ b/src/widgets/HeroApplications/HeroApplications.tsx @@ -2,8 +2,8 @@ import { useSelector } from 'react-redux'; import { FormatEGLD } from 'components'; import { useFetchGrowthEconomics } from 'hooks'; -import { ChartContractsTransactions } from 'pages/Home/components/ChartContractsTransactions'; import { growthEconomicsSelector } from 'redux/selectors'; +import { ChartContractsTransactions } from 'widgets'; export const HeroApplications = () => { const { applicationsDeployed, unprocessed } = useSelector( diff --git a/src/widgets/index.ts b/src/widgets/index.ts index 9a8404106..3b2393514 100644 --- a/src/widgets/index.ts +++ b/src/widgets/index.ts @@ -1,6 +1,7 @@ export * from './AccountsStatsCard'; export * from './BlockHeightStatsCard'; export * from './BlockProgressRing'; +export * from './ChartContractsTransactions'; export * from './EpochProgressRing'; export * from './HeroApplications'; export * from './HeroHome'; From 22ca194ae7b88d69c018c7ba14895d7f5987a98d Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Mon, 16 Sep 2024 18:07:23 +0300 Subject: [PATCH 08/18] - move ChartPrice, ChartStake, EconomicsCard to widgets - cleanup duplicate .tooltip css --- .../NativeTokenLayout/NativeTokenLayout.tsx | 3 +- src/pages/Home/Home.tsx | 11 +++-- .../ChartPrice/ChartPrice.tsx | 0 .../ChartPrice/index.ts | 0 .../ChartPrice/styles.module.scss | 41 ------------------- .../ChartRoot/ChartRoot.tsx | 0 .../components => widgets}/ChartRoot/index.ts | 0 .../components => widgets}/ChartRoot/types.ts | 0 .../ChartStake/ChartStake.tsx | 0 .../ChartStake/index.ts | 0 .../ChartStake/styles.module.scss | 41 ------------------- .../EconomicsCard/EconomicsCard.tsx | 0 .../EconomicsCard/index.ts | 0 .../EconomicsCard/styles.module.scss | 0 src/widgets/HeroNodes/HeroNodes.tsx | 3 +- src/widgets/index.ts | 3 ++ 16 files changed, 12 insertions(+), 90 deletions(-) rename src/{pages/Home/components => widgets}/ChartPrice/ChartPrice.tsx (100%) rename src/{pages/Home/components => widgets}/ChartPrice/index.ts (100%) rename src/{pages/Home/components => widgets}/ChartPrice/styles.module.scss (73%) rename src/{pages/Home/components => widgets}/ChartRoot/ChartRoot.tsx (100%) rename src/{pages/Home/components => widgets}/ChartRoot/index.ts (100%) rename src/{pages/Home/components => widgets}/ChartRoot/types.ts (100%) rename src/{pages/Home/components => widgets}/ChartStake/ChartStake.tsx (100%) rename src/{pages/Home/components => widgets}/ChartStake/index.ts (100%) rename src/{pages/Home/components => widgets}/ChartStake/styles.module.scss (73%) rename src/{pages/Home/components => widgets}/EconomicsCard/EconomicsCard.tsx (100%) rename src/{pages/Home/components => widgets}/EconomicsCard/index.ts (100%) rename src/{pages/Home/components => widgets}/EconomicsCard/styles.module.scss (100%) diff --git a/src/layouts/NativeTokenLayout/NativeTokenLayout.tsx b/src/layouts/NativeTokenLayout/NativeTokenLayout.tsx index 3ba29f0c4..5670fcbc3 100644 --- a/src/layouts/NativeTokenLayout/NativeTokenLayout.tsx +++ b/src/layouts/NativeTokenLayout/NativeTokenLayout.tsx @@ -4,9 +4,8 @@ import { Outlet } from 'react-router-dom'; import { Loader, PageState } from 'components'; import { useHasGrowthWidgets } from 'hooks'; import { faCoins } from 'icons/regular'; -import { ChartPrice } from 'pages/Home/components/ChartPrice'; -import { ChartStake } from 'pages/Home/components/ChartStake'; import { activeNetworkSelector, economicsSelector } from 'redux/selectors'; +import { ChartPrice, ChartStake } from 'widgets'; import { NativeTokenDetailsCard } from './NativeTokenDetailsCard'; diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index 7ba6f4de9..c206e3912 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -1,9 +1,12 @@ import { useHasGrowthWidgets } from 'hooks'; -import { ChartContractsTransactions, MostUsed } from 'widgets'; +import { + MostUsed, + ChartContractsTransactions, + ChartPrice, + ChartStake, + EconomicsCard +} from 'widgets'; -import { ChartPrice } from './components/ChartPrice'; -import { ChartStake } from './components/ChartStake'; -import { EconomicsCard } from './components/EconomicsCard'; import { LatestBlocks } from './components/LatestBlocks'; import { LatestTransactions } from './components/LatestTransactions'; diff --git a/src/pages/Home/components/ChartPrice/ChartPrice.tsx b/src/widgets/ChartPrice/ChartPrice.tsx similarity index 100% rename from src/pages/Home/components/ChartPrice/ChartPrice.tsx rename to src/widgets/ChartPrice/ChartPrice.tsx diff --git a/src/pages/Home/components/ChartPrice/index.ts b/src/widgets/ChartPrice/index.ts similarity index 100% rename from src/pages/Home/components/ChartPrice/index.ts rename to src/widgets/ChartPrice/index.ts diff --git a/src/pages/Home/components/ChartPrice/styles.module.scss b/src/widgets/ChartPrice/styles.module.scss similarity index 73% rename from src/pages/Home/components/ChartPrice/styles.module.scss rename to src/widgets/ChartPrice/styles.module.scss index 9f21d469d..784778030 100644 --- a/src/pages/Home/components/ChartPrice/styles.module.scss +++ b/src/widgets/ChartPrice/styles.module.scss @@ -122,44 +122,3 @@ } } } - -.tooltip { - border: 2px solid #24f7dd33; - border-radius: 1rem; - padding: 0.625rem; - padding-left: 1.75rem; - line-height: 1; - color: var(--primary); - font-family: 'Roobert SemiBold'; - font-weight: 600; - position: relative; - overflow: hidden; - - &:before { - content: ''; - left: 0.625rem; - width: 0.625rem; - top: 50%; - -webkit-transform: translateY(-50%); - transform: translateY(-50%); - position: absolute; - border-radius: 50%; - background-color: var(--primary); - height: 0.625rem; - } - - &:after { - content: ''; - left: 0; - right: 0; - top: 0; - bottom: 0; - position: absolute; - background: linear-gradient( - to bottom, - black 0%, - rgba(var(--primary-rgb), 0.15) 100% - ); - z-index: -1; - } -} diff --git a/src/pages/Home/components/ChartRoot/ChartRoot.tsx b/src/widgets/ChartRoot/ChartRoot.tsx similarity index 100% rename from src/pages/Home/components/ChartRoot/ChartRoot.tsx rename to src/widgets/ChartRoot/ChartRoot.tsx diff --git a/src/pages/Home/components/ChartRoot/index.ts b/src/widgets/ChartRoot/index.ts similarity index 100% rename from src/pages/Home/components/ChartRoot/index.ts rename to src/widgets/ChartRoot/index.ts diff --git a/src/pages/Home/components/ChartRoot/types.ts b/src/widgets/ChartRoot/types.ts similarity index 100% rename from src/pages/Home/components/ChartRoot/types.ts rename to src/widgets/ChartRoot/types.ts diff --git a/src/pages/Home/components/ChartStake/ChartStake.tsx b/src/widgets/ChartStake/ChartStake.tsx similarity index 100% rename from src/pages/Home/components/ChartStake/ChartStake.tsx rename to src/widgets/ChartStake/ChartStake.tsx diff --git a/src/pages/Home/components/ChartStake/index.ts b/src/widgets/ChartStake/index.ts similarity index 100% rename from src/pages/Home/components/ChartStake/index.ts rename to src/widgets/ChartStake/index.ts diff --git a/src/pages/Home/components/ChartStake/styles.module.scss b/src/widgets/ChartStake/styles.module.scss similarity index 73% rename from src/pages/Home/components/ChartStake/styles.module.scss rename to src/widgets/ChartStake/styles.module.scss index ee7f03d84..2b26360a1 100644 --- a/src/pages/Home/components/ChartStake/styles.module.scss +++ b/src/widgets/ChartStake/styles.module.scss @@ -118,44 +118,3 @@ } } } - -.tooltip { - border: 2px solid #24f7dd33; - border-radius: 1rem; - padding: 0.625rem; - padding-left: 1.75rem; - line-height: 1; - color: var(--primary); - font-family: 'Roobert SemiBold'; - font-weight: 600; - position: relative; - overflow: hidden; - - &:before { - content: ''; - left: 0.625rem; - width: 0.625rem; - top: 50%; - -webkit-transform: translateY(-50%); - transform: translateY(-50%); - position: absolute; - border-radius: 50%; - background-color: var(--primary); - height: 0.625rem; - } - - &:after { - content: ''; - left: 0; - right: 0; - top: 0; - bottom: 0; - position: absolute; - background: linear-gradient( - to bottom, - black 0%, - rgba(var(--primary-rgb), 0.15) 100% - ); - z-index: -1; - } -} diff --git a/src/pages/Home/components/EconomicsCard/EconomicsCard.tsx b/src/widgets/EconomicsCard/EconomicsCard.tsx similarity index 100% rename from src/pages/Home/components/EconomicsCard/EconomicsCard.tsx rename to src/widgets/EconomicsCard/EconomicsCard.tsx diff --git a/src/pages/Home/components/EconomicsCard/index.ts b/src/widgets/EconomicsCard/index.ts similarity index 100% rename from src/pages/Home/components/EconomicsCard/index.ts rename to src/widgets/EconomicsCard/index.ts diff --git a/src/pages/Home/components/EconomicsCard/styles.module.scss b/src/widgets/EconomicsCard/styles.module.scss similarity index 100% rename from src/pages/Home/components/EconomicsCard/styles.module.scss rename to src/widgets/EconomicsCard/styles.module.scss diff --git a/src/widgets/HeroNodes/HeroNodes.tsx b/src/widgets/HeroNodes/HeroNodes.tsx index 2aedcc9f9..df7a06dc8 100644 --- a/src/widgets/HeroNodes/HeroNodes.tsx +++ b/src/widgets/HeroNodes/HeroNodes.tsx @@ -2,9 +2,8 @@ import { useSelector } from 'react-redux'; import { MultilayerPercentageRing } from 'components'; import { useHasGrowthWidgets } from 'hooks'; -import { ChartStake } from 'pages/Home/components/ChartStake'; import { nodesVersionsSelector } from 'redux/selectors'; -import { ValidatorsStatusCard } from 'widgets'; +import { ChartStake, ValidatorsStatusCard } from 'widgets'; export const HeroNodes = () => { const { nodesVersions } = useSelector(nodesVersionsSelector); diff --git a/src/widgets/index.ts b/src/widgets/index.ts index 3b2393514..195003c34 100644 --- a/src/widgets/index.ts +++ b/src/widgets/index.ts @@ -2,6 +2,9 @@ export * from './AccountsStatsCard'; export * from './BlockHeightStatsCard'; export * from './BlockProgressRing'; export * from './ChartContractsTransactions'; +export * from './ChartPrice'; +export * from './ChartStake'; +export * from './EconomicsCard'; export * from './EpochProgressRing'; export * from './HeroApplications'; export * from './HeroHome'; From eadd5202d71678b69767e642a96b6cb8dcb3562e Mon Sep 17 00:00:00 2001 From: Radu Mojic Date: Tue, 17 Sep 2024 18:39:32 +0300 Subject: [PATCH 09/18] - moved ChartPrice and ChartStake into /widgets - use common component ChartCard --- src/assets/scss/components/_components.scss | 2 +- src/assets/scss/components/_pages.scss | 1 + src/assets/scss/components/_widgets.scss | 8 +- src/pages/Home/Home.tsx | 4 +- src/pages/Home/home.styles.scss | 10 ++ src/widgets/ChartCard/ChartCard.tsx | 59 +++++++++ .../{ => ChartCard}/ChartRoot/ChartRoot.tsx | 0 .../{ => ChartCard}/ChartRoot/index.ts | 0 .../{ => ChartCard}/ChartRoot/types.ts | 0 .../chartCard.styles.scss} | 48 +++---- src/widgets/ChartCard/index.ts | 2 + src/widgets/ChartPrice/ChartPrice.tsx | 89 +++++-------- src/widgets/ChartStake/ChartStake.tsx | 70 ++++------ src/widgets/ChartStake/styles.module.scss | 120 ------------------ 14 files changed, 164 insertions(+), 249 deletions(-) create mode 100644 src/pages/Home/home.styles.scss create mode 100644 src/widgets/ChartCard/ChartCard.tsx rename src/widgets/{ => ChartCard}/ChartRoot/ChartRoot.tsx (100%) rename src/widgets/{ => ChartCard}/ChartRoot/index.ts (100%) rename src/widgets/{ => ChartCard}/ChartRoot/types.ts (100%) rename src/widgets/{ChartPrice/styles.module.scss => ChartCard/chartCard.styles.scss} (75%) create mode 100644 src/widgets/ChartCard/index.ts delete mode 100644 src/widgets/ChartStake/styles.module.scss diff --git a/src/assets/scss/components/_components.scss b/src/assets/scss/components/_components.scss index b8c464f60..2f3349383 100644 --- a/src/assets/scss/components/_components.scss +++ b/src/assets/scss/components/_components.scss @@ -15,10 +15,10 @@ @import '../../../components/Marquee/marquee.styles.scss'; @import '../../../components/MultilayerPercentage/multilayerPercentage.styles.scss'; @import '../../../components/NftPreview/nftPreview.styles.scss'; -@import '../../../components/NodesTable/nodesTable.styles.scss'; @import '../../../components/Nodes/AuctionListTable/auctionListTables.styles.scss'; @import '../../../components/Nodes/NodesOverview/nodesOverview.styles.scss'; @import '../../../components/Nodes/NodesStatusPreviewCards/nodesStatusPreviewCards.styles.scss'; +@import '../../../components/NodesTable/nodesTable.styles.scss'; @import '../../../components/NotificationsBar/notificationsBar.styles.scss'; @import '../../../components/Pager/pager.styles.scss'; @import '../../../components/PageState/components/IconState/iconState.styles.scss'; diff --git a/src/assets/scss/components/_pages.scss b/src/assets/scss/components/_pages.scss index f71135710..5b35c80a5 100644 --- a/src/assets/scss/components/_pages.scss +++ b/src/assets/scss/components/_pages.scss @@ -2,6 +2,7 @@ @import '../../../pages/AccountDetails/AccountTokensTable/accountTokensTable.styles.scss'; @import '../../../pages/Analytics/analytics.styles.scss'; @import '../../../pages/BlockDetails/blockDetails.styles.scss'; +@import '../../../pages/Home/home.styles.scss'; @import '../../../pages/Identities/identities.styles.scss'; @import '../../../pages/NftDetails/nftDetails.styles.scss'; @import '../../../pages/NodeDetails/components/NetworkMetrics/networkMetrics.styles.scss'; diff --git a/src/assets/scss/components/_widgets.scss b/src/assets/scss/components/_widgets.scss index 745a03f16..e05afff04 100644 --- a/src/assets/scss/components/_widgets.scss +++ b/src/assets/scss/components/_widgets.scss @@ -1,9 +1,9 @@ +@import '../../../widgets/ChartCard/chartCard.styles.scss'; +@import '../../../widgets/ChartContractsTransactions/chartContractsTransactions.styles.scss'; +@import '../../../widgets/EconomicsCard/economicsCard.styles.scss'; @import '../../../widgets/HeroHome/heroHome.styles.scss'; @import '../../../widgets/HeroNodes/heroNodes.styles.scss'; @import '../../../widgets/HeroPills/heroPills.styles.scss'; @import '../../../widgets/MostUsed/mostUsed.styles.scss'; -@import '../../../widgets/ValidatorsStatusCard/validatorsStatusCard.styles.scss'; @import '../../../widgets/StatsCard/statsCard.styles.scss'; - -// temp -@import '../../../widgets/ChartContractsTransactions/chartContractsTransactions.styles.scss'; +@import '../../../widgets/ValidatorsStatusCard/validatorsStatusCard.styles.scss'; diff --git a/src/pages/Home/Home.tsx b/src/pages/Home/Home.tsx index c206e3912..b9ccefc2a 100644 --- a/src/pages/Home/Home.tsx +++ b/src/pages/Home/Home.tsx @@ -17,9 +17,9 @@ export const Home = () => {
{hasGrowthWidgets && ( <> -
+
- +
diff --git a/src/pages/Home/home.styles.scss b/src/pages/Home/home.styles.scss new file mode 100644 index 000000000..328669f88 --- /dev/null +++ b/src/pages/Home/home.styles.scss @@ -0,0 +1,10 @@ +.home { + @include media-breakpoint-up(xl) { + .chart-price { + width: 37.5%; + } + .chart-stake { + width: 41.85%; + } + } +} diff --git a/src/widgets/ChartCard/ChartCard.tsx b/src/widgets/ChartCard/ChartCard.tsx new file mode 100644 index 000000000..8d9974e85 --- /dev/null +++ b/src/widgets/ChartCard/ChartCard.tsx @@ -0,0 +1,59 @@ +import { ReactNode } from 'react'; +import classNames from 'classnames'; +import { SingleValue } from 'react-select'; + +import { Select, SelectOptionType } from 'components'; +import { StatisticType, WithClassnameType } from 'types'; + +export interface ChartCardUIType extends WithClassnameType { + title?: ReactNode; + value?: ReactNode; + subtitle?: ReactNode; + children?: ReactNode; + filters?: SelectOptionType[]; + defaultFilterValue?: SelectOptionType; + statistics?: StatisticType[]; + onChange?: (option: SingleValue) => void; +} + +export const ChartCard = ({ + title, + value, + subtitle, + children, + filters = [], + defaultFilterValue, + statistics = [], + onChange, + className +}: ChartCardUIType) => { + return ( +
+
+
+ {title &&
{title}
} + {value &&
{value}
} + {subtitle &&
{subtitle}
} +
+ {onChange && ( +
+ + -
-
- -
- - new Intl.NumberFormat('en-US', { - style: 'currency', - currency: 'USD', - maximumFractionDigits: 2, - minimumFractionDigits: 2 - }).format(option.value) - } - /> -
-
- {statistics.map((statistic) => ( -
-
{statistic.label}
-
{statistic.value}
-
- ))} -
-
+ {priceChange24h} today + + } + > + + new Intl.NumberFormat('en-US', { + style: 'currency', + currency: 'USD', + maximumFractionDigits: 2, + minimumFractionDigits: 2 + }).format(option.value) + } + /> + ); }; diff --git a/src/widgets/ChartStake/ChartStake.tsx b/src/widgets/ChartStake/ChartStake.tsx index 0b88ef59c..e4370f5a1 100644 --- a/src/widgets/ChartStake/ChartStake.tsx +++ b/src/widgets/ChartStake/ChartStake.tsx @@ -4,7 +4,7 @@ import classNames from 'classnames'; import { useSelector } from 'react-redux'; import { SingleValue } from 'react-select'; -import { Select, SelectOptionType } from 'components'; +import { SelectOptionType } from 'components'; import { getPrimaryColor } from 'helpers'; import { useFetchGrowthStaking } from 'hooks'; import { growthStakingSelector, activeNetworkSelector } from 'redux/selectors'; @@ -14,8 +14,7 @@ import { WithClassnameType } from 'types'; -import styles from './styles.module.scss'; -import { ChartRoot } from '../ChartRoot'; +import { ChartCard, ChartRoot } from '../ChartCard'; export const ChartStake = ({ className }: WithClassnameType) => { const { @@ -83,7 +82,7 @@ export const ChartStake = ({ className }: WithClassnameType) => { const defaultValue = filters.find((filter) => filter.value === initialFilter); const [data, setData] = useState(dataMap.get(initialFilter)); - const onChange = useCallback( + const handleChange = useCallback( (option: SingleValue) => { if (option && option.value && isFetched) { setData(dataMap.get(String(option.value))); @@ -102,45 +101,28 @@ export const ChartStake = ({ className }: WithClassnameType) => { useEffect(onInitialLoad, [onInitialLoad]); return ( -
-
-
-
Total Staked
-
- {totalStaked} EGLD ({stakingPercentage}) -
-
- -
-