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 Jan 17, 2024
1 parent f849f4f commit d251eec
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 57 deletions.
45 changes: 43 additions & 2 deletions src/components/Modal/common/TokenSelector.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useEffect, useMemo } from 'react';
import { ListRenderItemInfo } from 'react-native';
import i18n from 'utils/i18n/i18n';
import { FullSizeSelectModal } from 'components/common/SelectModal';
Expand All @@ -8,12 +8,20 @@ import { EmptyList } from 'components/EmptyList';
import { MagnifyingGlass } from 'phosphor-react-native';
import { useNavigation } from '@react-navigation/native';
import { RootNavigationProps } from 'routes/index';
import BigN from 'bignumber.js';
import { useSelector } from 'react-redux';
import { RootState } from 'stores/index';
import { BN_ZERO } from 'utils/chainBalances';
import { getInputValuesFromString } from 'screens/Transaction/SendFund/Amount';

export type TokenItemType = {
name: string;
slug: string;
symbol: string;
originChain: string;
free?: BigN;
price?: number;
decimals?: number;
};

interface Props {
Expand All @@ -34,6 +42,29 @@ interface Props {
showAddBtn?: boolean;
}

const convertChainActivePriority = (active?: boolean) => (active ? 1 : 0);

const convertTokenBalance = (a: TokenItemType, b: TokenItemType) => {
const aFree = new BigN(a.free || BN_ZERO);
const bFree = new BigN(b.free || BN_ZERO);
const aResult = new BigN(a.free || BN_ZERO).multipliedBy(new BigN(a.price || BN_ZERO));
const bResult = new BigN(b.free || BN_ZERO).multipliedBy(new BigN(b.price || BN_ZERO));

if (aResult.eq(bResult)) {
return Number(getInputValuesFromString(aFree.toFixed(), a.decimals || 0)) -
Number(getInputValuesFromString(bFree.toFixed(), b.decimals || 0)) >
0
? -1
: 1;
}

return Number(getInputValuesFromString(aResult.toFixed(), a.decimals || 0)) -
Number(getInputValuesFromString(bResult.toFixed(), b.decimals || 0)) >
0
? -1
: 1;
};

export const TokenSelector = ({
items,
selectedValueMap,
Expand All @@ -52,17 +83,27 @@ export const TokenSelector = ({
showAddBtn = true,
}: Props) => {
const navigation = useNavigation<RootNavigationProps>();
const { chainStateMap } = useSelector((state: RootState) => state.chainStore);
useEffect(() => {
setAdjustPan();
}, []);

const filteredItems = useMemo((): TokenItemType[] => {
return items.sort((a, b) => {
return (
convertChainActivePriority(chainStateMap[b.originChain]?.active) -
convertChainActivePriority(chainStateMap[a.originChain]?.active) || convertTokenBalance(a, b)
);
});
}, [chainStateMap, items]);

const _onSelectItem = (item: TokenItemType) => {
onSelectItem && onSelectItem(item);
};

return (
<FullSizeSelectModal
items={items}
items={filteredItems}
selectedValueMap={selectedValueMap}
onBackButtonPress={() => tokenSelectorRef?.current?.onCloseModal()}
selectModalType={'single'}
Expand Down
30 changes: 26 additions & 4 deletions src/components/TokenSelectItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ import Text from 'components/Text';
import { ColorMap } from 'styles/color';
import { FontMedium, FontSemiBold } from 'styles/sharedStyles';
import { CheckCircle } from 'phosphor-react-native';
import { Icon, Typography } from 'components/design-system-ui';
import { Icon, Typography, Number } from 'components/design-system-ui';
import { useSubWalletTheme } from 'hooks/useSubWalletTheme';
import { ThemeTypes } from 'styles/themes';
import BigN from 'bignumber.js';
import { BN_ZERO } from 'utils/chainBalances';

interface Props extends TouchableOpacityProps {
symbol: string;
Expand All @@ -19,6 +21,9 @@ interface Props extends TouchableOpacityProps {
onSelectNetwork: () => void;
defaultItemKey?: string;
iconSize?: number;
free?: BigN;
decimals?: number;
price?: number;
}

export const TokenSelectItem = ({
Expand All @@ -31,6 +36,9 @@ export const TokenSelectItem = ({
onSelectNetwork,
defaultItemKey,
iconSize = 40,
free,
decimals,
price,
}: Props) => {
const theme = useSubWalletTheme().swThemes;
const styles = useMemo(() => createStyle(theme), [theme]);
Expand All @@ -52,11 +60,24 @@ export const TokenSelectItem = ({
</View>
</View>

{isSelected && (
<View style={styles.selectedIconWrapper}>
<Icon phosphorIcon={CheckCircle} weight={'fill'} size={'sm'} iconColor={theme.colorSuccess} />
{!!decimals && !new BigN(free || 0).eq(BN_ZERO) && (
<View style={{ alignItems: 'flex-end' }}>
<Number size={16} value={free || BN_ZERO} decimal={decimals} />
<Number
prefix={'$'}
size={12}
unitOpacity={0.45}
intOpacity={0.45}
decimalOpacity={0.45}
value={new BigN(free || 0).multipliedBy(new BigN(price || 0))}
decimal={decimals}
/>
</View>
)}

<View style={styles.selectedIconWrapper}>
{isSelected && <Icon phosphorIcon={CheckCircle} weight={'fill'} size={'sm'} iconColor={theme.colorSuccess} />}
</View>
</View>
</TouchableOpacity>
);
Expand All @@ -81,6 +102,7 @@ function createStyle(theme: ThemeTypes) {
flexDirection: 'row',
alignItems: 'center',
flex: 1,
paddingRight: theme.paddingXS,
},

itemTextStyle: {
Expand Down
6 changes: 5 additions & 1 deletion src/components/common/CancelUnstakeItem/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,11 @@ export const CancelUnstakeItem = ({ item, isSelected, onPress }: Props) => {
size={theme.fontSize}
textStyle={{ ...FontSemiBold }}
/>
{isSelected && <Icon phosphorIcon={CheckCircle} weight={'fill'} size={'sm'} iconColor={theme.colorSuccess} />}
{isSelected ? (
<Icon phosphorIcon={CheckCircle} weight={'fill'} size={'sm'} iconColor={theme.colorSuccess} />
) : (
<View style={{ width: 20 }} />
)}
</View>
</TouchableOpacity>
);
Expand Down
6 changes: 5 additions & 1 deletion src/components/common/SelectModal/parts/TokenSelectItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ interface Props<T> {

export function _TokenSelectItem<T>({ item, selectedValueMap, onSelectItem, onCloseModal }: Props<T>) {
const chainInfoMap = useSelector((state: RootState) => state.chainStore.chainInfoMap);
const { symbol, originChain, slug, name } = item as TokenItemType;
const { symbol, originChain, slug, name, free, price, decimals } = item as TokenItemType;

return (
<TokenSelectItem
key={`${symbol}-${originChain}`}
Expand All @@ -23,10 +24,13 @@ export function _TokenSelectItem<T>({ item, selectedValueMap, onSelectItem, onCl
logoKey={slug.toLowerCase()}
subLogoKey={originChain}
isSelected={!!selectedValueMap[slug]}
free={free}
onSelectNetwork={() => {
onSelectItem && onSelectItem(item);
onCloseModal && onCloseModal();
}}
decimals={decimals}
price={price}
/>
);
}
51 changes: 27 additions & 24 deletions src/hooks/screen/Staking/useGetSupportedStakingTokens.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { _ChainAsset, _ChainInfo } from '@subwallet/chain-list/types';
import { _ChainInfo } from '@subwallet/chain-list/types';
import { StakingType } from '@subwallet/extension-base/background/KoniTypes';
import { AccountJson } from '@subwallet/extension-base/background/types';
import { _STAKING_CHAIN_GROUP } from '@subwallet/extension-base/services/chain-service/constants';
Expand All @@ -15,7 +15,8 @@ import { ALL_KEY } from 'constants/index';
import { AccountAddressType } from 'types/index';
import { findAccountByAddress, getAccountAddressType } from 'utils/account';
import useChainAssets from 'hooks/chain/useChainAssets';
import useChainChecker from 'hooks/chain/useChainChecker';
import { BN_ZERO } from '@polkadot/util';
import { TokenItemType } from 'components/Modal/common/TokenSelector';

const isChainTypeValid = (chainInfo: _ChainInfo, accounts: AccountJson[], address?: string): boolean => {
const addressType = getAccountAddressType(address);
Expand Down Expand Up @@ -43,13 +44,15 @@ export default function useGetSupportedStakingTokens(
type: StakingType,
address?: string,
chain?: string,
): _ChainAsset[] {
): TokenItemType[] {
const chainInfoMap = useSelector((state: RootState) => state.chainStore.chainInfoMap);
const assetRegistryMap = useChainAssets().chainAssetRegistry;
const accounts = useSelector((state: RootState) => state.accountState.accounts);
const { checkChainConnected } = useChainChecker();
const priceMap = useSelector((state: RootState) => state.price.priceMap);
const { balanceMap } = useSelector((root: RootState) => root.balance);
const { accounts, currentAccount } = useSelector((state: RootState) => state.accountState);
return useMemo(() => {
const result: _ChainAsset[] = [];
const result: TokenItemType[] = [];
const accBalanceMap = currentAccount ? balanceMap[currentAccount.address] : undefined;

if (type === StakingType.NOMINATED) {
Object.values(chainInfoMap).forEach(chainInfo => {
Expand All @@ -61,7 +64,14 @@ export default function useGetSupportedStakingTokens(
isChainTypeValid(chainInfo, accounts, address) &&
(!chain || chain === ALL_KEY || chain === chainInfo.slug)
) {
result.push(assetRegistryMap[nativeTokenSlug]);
const item = assetRegistryMap[nativeTokenSlug];
const freeBalance = accBalanceMap[item.slug]?.free || BN_ZERO;
result.push({
...item,
price: item.priceId ? priceMap[item.priceId] : 0,
free: accBalanceMap ? freeBalance : BN_ZERO,
decimals: item.decimals || undefined,
});
}
}
});
Expand All @@ -78,26 +88,19 @@ export default function useGetSupportedStakingTokens(
isChainTypeValid(chainInfo, accounts, address) &&
(!chain || chain === ALL_KEY || chain === chainInfo.slug)
) {
result.push(assetRegistryMap[nativeTokenSlug]);
const item = assetRegistryMap[nativeTokenSlug];
const freeBalance = accBalanceMap[item.slug]?.free || BN_ZERO;
result.push({
...item,
price: item.priceId ? priceMap[item.priceId] : 0,
free: accBalanceMap ? freeBalance : BN_ZERO,
decimals: item.decimals || undefined,
});
}
}
});
}

return result.sort((a, b) => {
if (checkChainConnected(a.originChain)) {
if (checkChainConnected(b.originChain)) {
return 0;
} else {
return -1;
}
} else {
if (checkChainConnected(b.originChain)) {
return 1;
} else {
return 0;
}
}
});
}, [type, chainInfoMap, assetRegistryMap, accounts, address, chain, checkChainConnected]);
return result;
}, [currentAccount, balanceMap, type, chainInfoMap, assetRegistryMap, accounts, address, chain, priceMap]);
}
Loading

0 comments on commit d251eec

Please sign in to comment.