Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Issue-1234] Sort the token by the balance on mobile app #1495

Open
wants to merge 2 commits into
base: upgrade-ui
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 46 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,22 @@ 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 useAccountBalance from 'hooks/screen/useAccountBalance';
import useChainAssets from 'hooks/chain/useChainAssets';
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';

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

interface Props {
Expand All @@ -32,8 +42,12 @@ interface Props {
acceptDefaultValue?: boolean;
onCloseAccountSelector?: () => void;
showAddBtn?: boolean;
isShowBalance?: boolean;
selectedAccount?: string;
}

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

export const TokenSelector = ({
items,
selectedValueMap,
Expand All @@ -50,19 +64,47 @@ export const TokenSelector = ({
acceptDefaultValue,
onCloseAccountSelector,
showAddBtn = true,
isShowBalance,
selectedAccount,
}: Props) => {
const navigation = useNavigation<RootNavigationProps>();
useEffect(() => {
setAdjustPan();
}, []);
const { chainStateMap } = useSelector((state: RootState) => state.chainStore);
const assetRegistry = useChainAssets({}).chainAssetRegistry;

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

const filteredItems = useMemo((): TokenItemType[] => {
const raw = items.filter(item => {
const chainAsset = assetRegistry[item.slug];

return chainAsset ? _isAssetFungibleToken(chainAsset) : false;
});

isShowBalance &&
raw.sort((a, b) => {
return sortTokenByValue(tokenBalanceMap[a.slug], tokenBalanceMap[b.slug]);
});

raw.sort((a, b) => {
return (
convertChainActivePriority(chainStateMap[b.originChain]?.active) -
convertChainActivePriority(chainStateMap[a.originChain]?.active)
);
});

return raw;
}, [assetRegistry, chainStateMap, isShowBalance, items, tokenBalanceMap]);

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

return (
<FullSizeSelectModal
items={items}
items={filteredItems}
selectedValueMap={selectedValueMap}
selectModalType={'single'}
selectModalItemType={'token'}
Expand All @@ -77,6 +119,8 @@ export const TokenSelector = ({
renderCustomItem={renderCustomItem}
defaultValue={defaultValue}
acceptDefaultValue={acceptDefaultValue}
isShowBalance={isShowBalance}
selectedAccount={selectedAccount}
renderListEmptyComponent={() => {
return (
<EmptyList
Expand Down
28 changes: 24 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 { BN_ZERO } from 'utils/chainBalances';
import { TokenBalanceItemType } from 'types/balance';

interface Props extends TouchableOpacityProps {
symbol: string;
Expand All @@ -19,6 +21,8 @@ interface Props extends TouchableOpacityProps {
onSelectNetwork: () => void;
defaultItemKey?: string;
iconSize?: number;
isShowBalance?: boolean;
tokenBalance?: TokenBalanceItemType;
}

export const TokenSelectItem = ({
Expand All @@ -31,6 +35,8 @@ export const TokenSelectItem = ({
onSelectNetwork,
defaultItemKey,
iconSize = 40,
isShowBalance = false,
tokenBalance,
}: Props) => {
const theme = useSubWalletTheme().swThemes;
const styles = useMemo(() => createStyle(theme), [theme]);
Expand All @@ -52,11 +58,24 @@ export const TokenSelectItem = ({
</View>
</View>

{isSelected && (
<View style={styles.selectedIconWrapper}>
<Icon phosphorIcon={CheckCircle} weight={'fill'} size={'sm'} iconColor={theme.colorSuccess} />
{!!isShowBalance && tokenBalance && tokenBalance.isChainEnabled && (
<View style={{ alignItems: 'flex-end' }}>
<Number size={16} value={tokenBalance.total.value || BN_ZERO} decimal={0} />
<Number
prefix={'$'}
size={12}
unitOpacity={0.45}
intOpacity={0.45}
decimalOpacity={0.45}
value={tokenBalance.total.convertedValue}
decimal={0}
/>
</View>
)}

<View style={styles.selectedIconWrapper}>
{isSelected && <Icon phosphorIcon={CheckCircle} weight={'fill'} size={'sm'} iconColor={theme.colorSuccess} />}
</View>
</View>
</TouchableOpacity>
);
Expand All @@ -81,6 +100,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
70 changes: 40 additions & 30 deletions src/components/common/SelectModal/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +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 useAccountBalance from 'hooks/screen/useAccountBalance';

interface Props<T> {
items: T[];
Expand Down Expand Up @@ -56,7 +57,9 @@ interface Props<T> {
onCloseModal?: () => void;
onModalOpened?: () => void;
rightIconOption?: RightIconOpt;
isShowBalance?: boolean;
level?: number;
selectedAccount?: string;
}
const LOADING_TIMEOUT = Platform.OS === 'ios' ? 20 : 100;

Expand Down Expand Up @@ -93,7 +96,9 @@ function _SelectModal<T>(selectModalProps: Props<T>, ref: ForwardedRef<any>) {
onCloseModal: _onCloseModal,
onModalOpened,
rightIconOption,
isShowBalance,
level,
selectedAccount,
} = selectModalProps;
const chainInfoMap = useSelector((root: RootState) => root.chainStore.chainInfoMap);
const [isOpen, setOpen] = useState<boolean>(false);
Expand All @@ -105,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 @@ -190,40 +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()}
/>
);
} 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
15 changes: 14 additions & 1 deletion src/components/common/SelectModal/parts/TokenSelectItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,28 @@ import { TokenItemType } from 'components/Modal/common/TokenSelector';
import { useSelector } from 'react-redux';
import { RootState } from 'stores/index';
import { TokenSelectItem } from 'components/TokenSelectItem';
import { TokenBalanceItemType } from 'types/balance';

interface Props<T> {
item: T;
selectedValueMap: Record<string, boolean>;
onSelectItem?: (item: T) => void;
onCloseModal?: () => void;
isShowBalance?: boolean;
tokenBalance?: TokenBalanceItemType;
}

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

return (
<TokenSelectItem
key={`${symbol}-${originChain}`}
Expand All @@ -23,10 +34,12 @@ export function _TokenSelectItem<T>({ item, selectedValueMap, onSelectItem, onCl
logoKey={slug.toLowerCase()}
subLogoKey={originChain}
isSelected={!!selectedValueMap[slug]}
isShowBalance={isShowBalance}
onSelectNetwork={() => {
onSelectItem && onSelectItem(item);
onCloseModal && onCloseModal();
}}
tokenBalance={tokenBalance}
/>
);
}
4 changes: 1 addition & 3 deletions src/hooks/earning/useYieldGroupInfo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { BN_ZERO } from '@polkadot/util';
import { calculateReward } from '@subwallet/extension-base/services/earning-service/utils';
import { useGetChainSlugs } from 'hooks/screen/Home/useGetChainSlugs';
import useAccountBalance from 'hooks/screen/useAccountBalance';
import useTokenGroup from 'hooks/screen/useTokenGroup';
import { useMemo } from 'react';
import { useSelector } from 'react-redux';
import { RootState } from 'stores/index';
Expand All @@ -17,8 +16,7 @@ const useYieldGroupInfo = (): YieldGroupInfo[] => {
const { assetRegistry, multiChainAssetMap } = useSelector((state: RootState) => state.assetRegistry);
const { chainInfoMap } = useSelector((state: RootState) => state.chainStore);
const chainsByAccountType = useGetChainSlugs();
const { tokenGroupMap } = useTokenGroup(chainsByAccountType);
const { tokenGroupBalanceMap, tokenBalanceMap } = useAccountBalance(tokenGroupMap, undefined, true);
const { tokenGroupBalanceMap, tokenBalanceMap } = useAccountBalance(true, undefined);

return useMemo(() => {
const result: Record<string, YieldGroupInfo> = {};
Expand Down
Loading
Loading