Skip to content

Commit

Permalink
[issue-1234] Sort the token by the balance on mobile app
Browse files Browse the repository at this point in the history
  • Loading branch information
dominhquang committed Feb 19, 2024
1 parent 99f2c05 commit 44dc455
Show file tree
Hide file tree
Showing 5 changed files with 93 additions and 70 deletions.
21 changes: 3 additions & 18 deletions src/components/Modal/common/TokenSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { RootNavigationProps } from 'routes/index';
import BigN from 'bignumber.js';
import useAccountBalance from 'hooks/screen/useAccountBalance';
import useChainAssets from 'hooks/chain/useChainAssets';
import { _getMultiChainAsset, _isAssetFungibleToken } from '@subwallet/extension-base/services/chain-service/utils';
import { _isAssetFungibleToken } from '@subwallet/extension-base/services/chain-service/utils';
import { sortTokenByValue } from 'utils/sort/token';
import { useSelector } from 'react-redux';
import { RootState } from 'stores/index';
Expand Down Expand Up @@ -74,22 +74,7 @@ export const TokenSelector = ({
const { chainStateMap } = useSelector((state: RootState) => state.chainStore);
const assetRegistry = useChainAssets({}).chainAssetRegistry;

const tokenGroupMap = useMemo(() => {
return Object.values(assetRegistry).reduce((_tokenGroupMap: Record<string, string[]>, chainAsset) => {
const multiChainAsset = _getMultiChainAsset(chainAsset);
const tokenGroupKey = multiChainAsset || chainAsset.slug;

if (_tokenGroupMap[tokenGroupKey]) {
_tokenGroupMap[tokenGroupKey].push(chainAsset.slug);
} else {
_tokenGroupMap[tokenGroupKey] = [chainAsset.slug];
}

return _tokenGroupMap;
}, {});
}, [assetRegistry]);

const { tokenBalanceMap } = useAccountBalance(tokenGroupMap, true, true, selectedAccount);
const { tokenBalanceMap } = useAccountBalance(true, true, selectedAccount);

const filteredItems = useMemo((): TokenItemType[] => {
const raw = items.filter(item => {
Expand Down Expand Up @@ -136,7 +121,7 @@ export const TokenSelector = ({
defaultValue={defaultValue}
acceptDefaultValue={acceptDefaultValue}
isShowBalance={isShowBalance}
tokenBalanceMap={tokenBalanceMap}
selectedAccount={selectedAccount}
renderListEmptyComponent={() => {
return (
<EmptyList
Expand Down
2 changes: 1 addition & 1 deletion src/components/TokenSelectItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export const TokenSelectItem = ({
</View>
</View>

{!!isShowBalance && tokenBalance && tokenBalance.isReady && (
{!!isShowBalance && tokenBalance && tokenBalance.isChainEnabled && (
<View style={{ alignItems: 'flex-end' }}>
<Number size={16} value={tokenBalance.total.value || BN_ZERO} decimal={0} />
<Number
Expand Down
73 changes: 38 additions & 35 deletions src/components/common/SelectModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { RootState } from 'stores/index';
import { ChainInfo } from 'types/index';
import { useSubWalletTheme } from 'hooks/useSubWalletTheme';
import { SWModalRefProps } from 'components/design-system-ui/modal/ModalBaseV2';
import { TokenBalanceItemType } from 'types/balance';
import useAccountBalance from 'hooks/screen/useAccountBalance';

interface Props<T> {
items: T[];
Expand Down Expand Up @@ -59,7 +59,7 @@ interface Props<T> {
rightIconOption?: RightIconOpt;
isShowBalance?: boolean;
level?: number;
tokenBalanceMap?: Record<string, TokenBalanceItemType>;
selectedAccount?: string;
}
const LOADING_TIMEOUT = Platform.OS === 'ios' ? 20 : 100;

Expand Down Expand Up @@ -98,7 +98,7 @@ function _SelectModal<T>(selectModalProps: Props<T>, ref: ForwardedRef<any>) {
rightIconOption,
isShowBalance,
level,
tokenBalanceMap,
selectedAccount,
} = selectModalProps;
const chainInfoMap = useSelector((root: RootState) => root.chainStore.chainInfoMap);
const [isOpen, setOpen] = useState<boolean>(false);
Expand All @@ -110,7 +110,7 @@ function _SelectModal<T>(selectModalProps: Props<T>, ref: ForwardedRef<any>) {
_onCloseModal && _onCloseModal();
}, [_onCloseModal]);
const defaultItem = items[0];

const { tokenBalanceMap } = useAccountBalance(true, true, selectedAccount);
const theme = useSubWalletTheme().swThemes;

useEffect(() => {
Expand Down Expand Up @@ -195,42 +195,45 @@ function _SelectModal<T>(selectModalProps: Props<T>, ref: ForwardedRef<any>) {
}
};

const renderItem = ({ item }: ListRenderItemInfo<T>) => {
if (selectModalItemType === 'account') {
return (
<>
<AccountSelectItem
const renderItem = useCallback(
({ item }: ListRenderItemInfo<T>) => {
if (selectModalItemType === 'account') {
return (
<>
<AccountSelectItem
item={item}
selectedValueMap={selectedValueMap}
onSelectItem={_onSelectItem}
onCloseModal={() => closeModalAfterSelect && modalBaseV2Ref?.current?.close()}
/>
</>
);
} else if (selectModalItemType === 'token') {
return (
<_TokenSelectItem
item={item}
selectedValueMap={selectedValueMap}
onSelectItem={_onSelectItem}
onCloseModal={() => closeModalAfterSelect && modalBaseV2Ref?.current?.close()}
isShowBalance={isShowBalance}
tokenBalance={tokenBalanceMap ? tokenBalanceMap[(item as TokenItemType).slug] : undefined}
/>
</>
);
} else if (selectModalItemType === 'token') {
return (
<_TokenSelectItem
item={item}
selectedValueMap={selectedValueMap}
onSelectItem={_onSelectItem}
onCloseModal={() => closeModalAfterSelect && modalBaseV2Ref?.current?.close()}
isShowBalance={isShowBalance}
tokenBalance={tokenBalanceMap ? tokenBalanceMap[(item as TokenItemType).slug] : undefined}
/>
);
} else if (selectModalItemType === 'chain') {
return (
<ChainSelectItem
item={item}
selectedValueMap={selectedValueMap}
onSelectItem={_onSelectItem}
onCloseModal={() => closeModalAfterSelect && modalBaseV2Ref?.current?.close()}
/>
);
} else {
return <></>;
}
};
);
} else if (selectModalItemType === 'chain') {
return (
<ChainSelectItem
item={item}
selectedValueMap={selectedValueMap}
onSelectItem={_onSelectItem}
onCloseModal={() => closeModalAfterSelect && modalBaseV2Ref?.current?.close()}
/>
);
} else {
return <></>;
}
},
[_onSelectItem, closeModalAfterSelect, isShowBalance, selectModalItemType, selectedValueMap, tokenBalanceMap],
);

const _renderListEmptyComponent = () => {
return (
Expand Down
66 changes: 50 additions & 16 deletions src/hooks/screen/useAccountBalance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import {
_getAssetPriceId,
_getAssetSymbol,
_getChainName,
_getMultiChainAsset,
_getMultiChainAssetPriceId,
_getMultiChainAssetSymbol,
_getOriginChainOfAsset,
_isAssetValuable,
} from '@subwallet/extension-base/services/chain-service/utils';
import BigN from 'bignumber.js';
Expand Down Expand Up @@ -59,6 +61,7 @@ function getDefaultBalanceItem(slug: string, symbol: string, logoKey: string): T
logoKey,
slug,
symbol,
isChainEnabled: false,
};
}

Expand Down Expand Up @@ -95,6 +98,7 @@ function getAccountBalance(
assetRegistryMap: AssetRegistryStore['assetRegistry'],
multiChainAssetMap: AssetRegistryStore['multiChainAssetMap'],
chainInfoMap: ChainStore['chainInfoMap'],
chainStateMap: ChainStore['chainStateMap'],
isShowZeroBalance: boolean,
): AccountBalanceHookType {
const totalBalanceInfo: AccountBalanceHookType['totalBalanceInfo'] = {
Expand All @@ -108,6 +112,17 @@ function getAccountBalance(
const tokenBalanceMap: Record<string, TokenBalanceItemType> = {};
const tokenGroupBalanceMap: Record<string, TokenBalanceItemType> = {};

const checkChainConnected = (chain: string) => {
const chainState = chainStateMap[chain];

if (!chainState) {
// Couldn't get chain state
return false;
}

return chainState.active;
};

Object.keys(tokenGroupMap).forEach(tokenGroupKey => {
const tokenGroupBalanceReady: boolean[] = [];
const tokenGroupNotSupport: boolean[] = [];
Expand All @@ -124,18 +139,15 @@ function getAccountBalance(
return;
}

const balanceItem = balanceMap[address]?.[tokenSlug];

if (!balanceItem) {
return;
}

const tokenBalance = getDefaultTokenBalance(tokenSlug, chainAsset);
const originChain = _getAssetOriginChain(chainAsset);
const balanceItem = balanceMap[address]?.[tokenSlug];
const decimals = _getAssetDecimals(chainAsset);
const chainSlug = _getOriginChainOfAsset(tokenSlug);

const isTokenBalanceReady = balanceItem.state !== APIItemState.PENDING;
const isTokenNotSupport = balanceItem.state === APIItemState.NOT_SUPPORT;
const isTokenBalanceReady = !!balanceItem && balanceItem.state !== APIItemState.PENDING;
const isTokenNotSupport = !!balanceItem && balanceItem.state === APIItemState.NOT_SUPPORT;
const isChainEnable = checkChainConnected(chainSlug);

tokenGroupNotSupport.push(isTokenNotSupport);
tokenGroupBalanceReady.push(isTokenBalanceReady);
Expand All @@ -146,6 +158,7 @@ function getAccountBalance(

tokenBalance.isReady = isTokenBalanceReady;
tokenBalance.isNotSupport = isTokenNotSupport;
tokenBalance.isChainEnabled = isChainEnable;

tokenBalance.chain = originChain;
tokenBalance.chainDisplayName = _getChainName(chainInfoMap[originChain]);
Expand Down Expand Up @@ -266,7 +279,9 @@ function getAccountBalance(
// make sure priceId exists and token group has monetary value
// todo: check if multiChainAsset has monetary value too (after Nampc updates the background)
if (!tokenGroupPriceId || (assetRegistryMap[tokenGroupKey] && !_isAssetValuable(assetRegistryMap[tokenGroupKey]))) {
tokenGroupBalanceMap[tokenGroupKey] = tokenGroupBalance;
if (!tokenGroupBalance.isNotSupport) {
tokenGroupBalanceMap[tokenGroupKey] = tokenGroupBalance;
}

return;
}
Expand All @@ -283,11 +298,13 @@ function getAccountBalance(
tokenGroupBalance.priceChangeStatus = 'decrease';
}

tokenGroupBalanceMap[tokenGroupKey] = tokenGroupBalance;
totalBalanceInfo.convertedValue = totalBalanceInfo.convertedValue.plus(tokenGroupBalance.total.convertedValue);
totalBalanceInfo.converted24hValue = totalBalanceInfo.converted24hValue.plus(
tokenGroupBalance.total.pastConvertedValue,
);
if (!tokenGroupBalance.isNotSupport) {
tokenGroupBalanceMap[tokenGroupKey] = tokenGroupBalance;
totalBalanceInfo.convertedValue = totalBalanceInfo.convertedValue.plus(tokenGroupBalance.total.convertedValue);
totalBalanceInfo.converted24hValue = totalBalanceInfo.converted24hValue.plus(
tokenGroupBalance.total.pastConvertedValue,
);
}
});

// Compute total balance change
Expand Down Expand Up @@ -327,20 +344,34 @@ const DEFAULT_RESULT = {
} as AccountBalanceHookType;

export default function useAccountBalance(
tokenGroupMap: Record<string, string[]>,
lazy?: boolean,
showZero?: boolean,
selectedAccount?: string,
): AccountBalanceHookType {
const currentAccount = useSelector((state: RootState) => state.accountState.currentAccount);
const balanceMap = useSelector((state: RootState) => state.balance.balanceMap);
const chainInfoMap = useSelector((state: RootState) => state.chainStore.chainInfoMap);
const { chainInfoMap, chainStateMap } = useSelector((state: RootState) => state.chainStore);
const priceMap = useSelector((state: RootState) => state.price.priceMap);
const price24hMap = useSelector((state: RootState) => state.price.price24hMap);
const assetRegistryMap = useSelector((state: RootState) => state.assetRegistry.assetRegistry);
const multiChainAssetMap = useSelector((state: RootState) => state.assetRegistry.multiChainAssetMap);
const isShowZeroBalanceSetting = useSelector((state: RootState) => state.settings.isShowZeroBalance);

const tokenGroupMap = useMemo(() => {
return Object.values(assetRegistryMap).reduce((_tokenGroupMap: Record<string, string[]>, chainAsset) => {
const multiChainAsset = _getMultiChainAsset(chainAsset);
const tokenGroupKey = multiChainAsset || chainAsset.slug;

if (_tokenGroupMap[tokenGroupKey]) {
_tokenGroupMap[tokenGroupKey].push(chainAsset.slug);
} else {
_tokenGroupMap[tokenGroupKey] = [chainAsset.slug];
}

return _tokenGroupMap;
}, {});
}, [assetRegistryMap]);

const isShowZeroBalance = useMemo(() => {
return showZero || isShowZeroBalanceSetting;
}, [isShowZeroBalanceSetting, showZero]);
Expand All @@ -357,6 +388,7 @@ export default function useAccountBalance(
assetRegistryMap,
multiChainAssetMap,
chainInfoMap,
chainStateMap,
isShowZeroBalance,
),
);
Expand All @@ -373,6 +405,7 @@ export default function useAccountBalance(
assetRegistryMap,
multiChainAssetMap,
chainInfoMap,
chainStateMap,
isShowZeroBalance,
),
);
Expand All @@ -382,6 +415,7 @@ export default function useAccountBalance(
assetRegistryMap,
balanceMap,
chainInfoMap,
chainStateMap,
currentAccount,
isShowZeroBalance,
multiChainAssetMap,
Expand Down
1 change: 1 addition & 0 deletions src/types/balance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,5 @@ export interface TokenBalanceItemType extends SubstrateBalance {
locked: BalanceValueInfo;
total: BalanceValueInfo;
isReady: boolean;
isChainEnabled: boolean;
}

0 comments on commit 44dc455

Please sign in to comment.