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

Sort token list update #571

Closed
wants to merge 3 commits into from
Closed
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
98 changes: 64 additions & 34 deletions src/components/SearchModal/CurrencyList/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { SorobanContextType, useSorobanReact } from '@soroban-react/core';
import BigNumber from 'bignumber.js';
import { CSSProperties, MutableRefObject, useCallback, useMemo } from 'react';
import { CSSProperties, MutableRefObject, useCallback, useEffect, useMemo, useState } from 'react';
import { Check } from 'react-feather';
import { FixedSizeList } from 'react-window';
import { AccountResponse } from '@stellar/stellar-sdk/lib/horizon';
import useSWRImmutable from 'swr/immutable';

import { CircularProgress, Typography, styled } from 'soroswap-ui';
import Column, { AutoColumn } from 'components/Column';
Expand Down Expand Up @@ -107,30 +106,17 @@ export function CurrencyRow({
otherSelected,
style,
showCurrencyAmount,
balance,
}: {
currency: TokenType;
onSelect: (hasWarning: boolean) => void;
isSelected: boolean;
otherSelected: boolean;
showCurrencyAmount?: boolean;
balance?: number;
eventProperties: Record<string, unknown>;
style?: CSSProperties;
}) {
const sorobanContext = useSorobanReact();

const { address } = sorobanContext;

const { tokenBalancesResponse } = useGetMyBalances();

const { account } = useHorizonLoadAccount();

const { data, isLoading } = useSWRImmutable(
sorobanContext.activeChain && sorobanContext.address && account
? ['currencyBalance', tokenBalancesResponse, currency, sorobanContext, account]
: null,
([_, tokenBalancesResponse, currency, sorobanContext, account]) =>
getCurrencyBalance(tokenBalancesResponse, currency, sorobanContext, account),
);
const shortenSorobanClassicAsset = (currency: TokenType) => {
if (!currency) return '';
if (currency?.name && currency.name.toString().length > 56) {
Expand Down Expand Up @@ -184,9 +170,9 @@ export function CurrencyRow({
{currency.domain ? currency.domain : formattedCurrencyName(currency as TokenType)}
</Typography>
</AutoColumn>
{showCurrencyAmount ? (
{showCurrencyAmount && balance ? (
<RowFixed style={{ justifySelf: 'flex-end' }}>
{isLoading ? <Loader /> : address ? <Balance balance={data || '0'} /> : null}
{<Balance balance={balance.toString() || '0'} />}
{isSelected && <CheckIcon />}
</RowFixed>
) : (
Expand Down Expand Up @@ -258,41 +244,85 @@ export default function CurrencyList({
isAddressSearch: string | false;
isLoading?: boolean;
}) {
const itemData: TokenType[] = useMemo(() => {
if (otherListTokens && otherListTokens?.length > 0) {
return [...currencies, ...otherListTokens];
}
return currencies;
const sorobanContext = useSorobanReact();
const { tokenBalancesResponse } = useGetMyBalances();
const { account } = useHorizonLoadAccount();

const [balances, setBalances] = useState<Record<string, number>>({});

const itemData = useMemo(() => {
return otherListTokens && otherListTokens.length > 0
? [...currencies, ...otherListTokens]
: currencies;
}, [currencies, otherListTokens]);

useEffect(() => {
const fetchBalances = async () => {
const newBalances: Record<string, number> = {};

if (account) {
for (const currency of itemData) {
const balance = await getCurrencyBalance(
tokenBalancesResponse,
currency,
sorobanContext,
account,
);
newBalances[currencyKey(currency)] = Number(balance ?? 0);
}
setBalances(newBalances);
}
};

if (sorobanContext.activeChain && sorobanContext.address && account && itemData.length > 0) {
fetchBalances();
}
}, [
sorobanContext.activeChain,
sorobanContext.address,
account,
itemData,
tokenBalancesResponse,
]);

const sortedItemData = useMemo(() => {
if (!itemData || !Array.isArray(itemData)) {
return [];
}
return [...itemData]
.map((currency) => ({
...currency,
balance: balances[currencyKey(currency)] || 0,
}))
.sort((a, b) => b.balance - a.balance);
}, [itemData, balances]);

const Row = useCallback(
function TokenRow({ data, index, style }: TokenRowProps) {
const row: TokenType = data[index];

const row = data[index];
const currency = row;

const isSelected = Boolean(
currency && selectedCurrency && selectedCurrency.contract == currency.contract,
currency && selectedCurrency && selectedCurrency.contract === currency.contract,
);
const otherSelected = Boolean(
currency && otherCurrency && otherCurrency.contract == currency.contract,
currency && otherCurrency && otherCurrency.contract === currency.contract,
);
const handleSelect = (hasWarning: boolean) =>
currency && onCurrencySelect(currency, hasWarning);

const token = currency;

if (currency) {
return (
<CurrencyRow
style={style}
balance={row.balance}
currency={currency}
isSelected={isSelected}
onSelect={handleSelect}
otherSelected={otherSelected}
showCurrencyAmount={showCurrencyAmount}
eventProperties={formatAnalyticsEventProperties(
token,
currency,
index,
data,
searchQuery,
Expand All @@ -314,7 +344,7 @@ export default function CurrencyList({
],
);

const itemKey = useCallback((index: number, data: typeof itemData) => {
const itemKey = useCallback((index: number, data: typeof sortedItemData) => {
const currency = data[index];
return currencyKey(currency);
}, []);
Expand All @@ -330,8 +360,8 @@ export default function CurrencyList({
height={height}
ref={fixedListRef as any}
width="100%"
itemData={itemData}
itemCount={itemData.length}
itemData={sortedItemData}
itemCount={sortedItemData.length}
itemSize={56}
itemKey={itemKey}
>
Expand Down
27 changes: 16 additions & 11 deletions src/hooks/tokens/useToken.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ export const findToken = async (
if (!tokenAddress || tokenAddress === '') return undefined;

const classicAssetSearch = getClassicAssetSorobanAddress(tokenAddress!, sorobanContext);

const formattedAddress = isAddress(classicAssetSearch ? classicAssetSearch : tokenAddress);
if (!formattedAddress) return undefined;

const fromMap = tokensAsMap[formattedAddress];

if (fromMap) return fromMap;

const token = await getToken(sorobanContext, formattedAddress);
// const token: TokenType = {
// contract: formattedAddress,
Expand All @@ -33,7 +33,7 @@ export const findToken = async (
// decimals,
// icon: logo,
// };

if (!token?.name || !token?.code) return undefined;
// Here from token.name we will try to understand if this is a classic asset (even if we got a soroban contracta as address).
const stellarAsset = getClassicStellarAsset(token.name);
Expand All @@ -44,17 +44,17 @@ export const findToken = async (
*/
if (stellarAsset && typeof stellarAsset !== 'boolean') {
return {
balance: 0,
issuer: stellarAsset.issuer,
contract: token.contract,
name: stellarAsset.asset,
code: stellarAsset.assetCode,
decimals: 7,
icon: '',
};
} else {
return token;
}
else {
return token
};
};

const revalidateKeysCondition = (key: any) => {
Expand Down Expand Up @@ -96,8 +96,12 @@ export function useToken(tokenAddress: string | undefined) {
const bothLoading = isLoading || isStellarClassicAssetLoading;

const needsWrapping = !data && isStellarClassicAsset;

const checkContractId = (contractId: string, code: string, issuer: string): boolean | undefined => {

const checkContractId = (
contractId: string,
code: string,
issuer: string,
): boolean | undefined => {
if (!issuer) {
return undefined;
}
Expand All @@ -107,7 +111,7 @@ export function useToken(tokenAddress: string | undefined) {
} else {
return false;
}
}
};
const isSafe = data ? checkContractId(data.contract, data.code, data.issuer!) : false;

const needsWrappingOnAddLiquidity = (!data && isStellarClassicAsset) || !name;
Expand All @@ -120,6 +124,7 @@ export function useToken(tokenAddress: string | undefined) {
if (sorobanAddress || (stellarAsset && typeof sorobanAddress === 'string')) {
if (stellarAsset && typeof stellarAsset !== 'boolean') {
newTokenData = {
balance: 0,
issuer: stellarAsset.issuer,
contract: sorobanAddress,
name: stellarAsset.asset,
Expand Down
1 change: 1 addition & 0 deletions src/hooks/tokens/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export async function getToken(
logo = await getTokenLogo(formattedAddress, sorobanContext);

const token: TokenType = {
balance: 0,
contract: formattedAddress,
name: name as string,
code: symbol as string,
Expand Down
1 change: 1 addition & 0 deletions src/interfaces/tokens.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export interface TokenType {
balance?: number;
code: string;
issuer?: string;
contract: string;
Expand Down
Loading