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

Display token infos in Send Payment view #346

Merged
merged 6 commits into from
Jan 17, 2024
Merged
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
6 changes: 6 additions & 0 deletions packages/extension/cypress/e2e/receive.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ describe('ReceivePayment View', () => {
set(obj, cb) {
if (cb) cb();
}
},
session: {
get(key, cb) {},
set(obj, cb) {
if (cb) cb();
}
}
};

Expand Down
6 changes: 6 additions & 0 deletions packages/extension/cypress/e2e/send_token.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ describe('Send Token', () => {
set(obj, cb) {
if (cb) cb();
}
},
session: {
get(key, cb) {},
set(obj, cb) {
if (cb) cb();
}
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import InfoOutlinedIcon from '@mui/icons-material/InfoOutlined';
import { Paper, Typography } from '@mui/material';

import { SECONDARY_GRAY } from '../../../constants';
import { XRPL_META_URL } from '../../../constants/xrplmeta';
import { XRPLMetaTokenAPIResponse } from '../../../types';
import { TokenDisplayData, getTrustLineData } from '../../../utils';
import { IconTextButton } from '../../atoms/IconTextButton';
import { TokenInfo } from './TokenInfo';

Expand All @@ -22,13 +21,6 @@ export interface TokenDisplayProps {
style?: CSSProperties;
}

interface TokenDisplayData {
tokenName: string;
tokenIconUrl?: string;
issuerName?: string;
issuerIconUrl?: string;
}

export const TokenDisplay: FC<TokenDisplayProps> = ({
balance,
token,
Expand All @@ -42,32 +34,18 @@ export const TokenDisplay: FC<TokenDisplayProps> = ({
}) => {
const [tokenData, setTokenData] = useState<TokenDisplayData | undefined>(undefined);

/* We use the token and issuer to get the logo from XRPL Meta */
useEffect(() => {
async function getTrustLineLogo() {
if (issuer) {
try {
// API Reference: https://xrplmeta.org/api
const res: Response = await fetch(`${XRPL_META_URL}/token/${token}:${issuer}`);
const json: XRPLMetaTokenAPIResponse = await res.json(); // Make sure this JSON structure conforms to XRPLMetaTokenAPIResponse
const tokenName: string | undefined = json?.meta?.token?.name ?? token;
const tokenIconUrl: string | undefined =
json?.meta?.token?.icon ?? json?.meta?.issuer?.icon;
const issuerName: string | undefined = json?.meta?.issuer?.name;
const issuerIconUrl: string | undefined = json?.meta?.issuer?.icon;
setTokenData({
tokenName,
tokenIconUrl,
issuerName,
issuerIconUrl
});
} catch (error) {
console.log(`An error occurred: ${error}`);
}
}
if (!issuer) {
return;
}

getTrustLineLogo();
getTrustLineData({ token, issuer })
.then((data) => {
setTokenData(data);
})
.catch((error) => {
console.error(error);
});
}, [issuer, token]);

/* We a warning if trustline's limit is 0 or if the noRipple flag is set to false */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ import {
} from '../../../../constants';
import { useLedger, useNetwork, useServer, useWallet } from '../../../../contexts';
import { useMainToken } from '../../../../hooks';
import { buildDefaultMemos, convertHexCurrencyString } from '../../../../utils';
import {
buildDefaultMemos,
convertHexCurrencyString,
formatToken,
getTrustLineData
} from '../../../../utils';
import { NumericInput } from '../../../atoms';
import { InformationMessage } from '../../../molecules';
import { PageWithNavMenu, PageWithReturn, PageWithSpinner } from '../../../templates';
Expand Down Expand Up @@ -78,6 +83,15 @@ export const PreparePayment: FC<PreparePaymentProps> = ({ onSendPaymentClick })
}[]
| undefined
>();
const [tokensWithMetadata, setTokensWithMetadata] = useState<
| {
value: string;
currency: string;
issuer?: string;
issuerName?: string;
}[]
| undefined
>();
const [errorTokens, setErrorTokens] = useState<string>('');
const [isWalletActivated, setIsWalletActivated] = useState<boolean>(true);
const [ownerCount, setOwnerCount] = useState<number>(0);
Expand Down Expand Up @@ -141,6 +155,38 @@ export const PreparePayment: FC<PreparePaymentProps> = ({ onSendPaymentClick })
fetchBalances();
}, [client, getCurrentWallet]);

useEffect(() => {
setTokensWithMetadata(tokens);

const fetchTokensData = async () => {
if (!tokens) {
return;
}

const tokensListDataPromises = tokens.map(async (token) => {
const { currency, issuer } = token;
if (!issuer) {
return { ...token };
}

try {
const res = await getTrustLineData({ token: currency, issuer });
return {
...token,
issuerName: res.issuerName
};
} catch (error) {
return { ...token };
}
});

const tokensListData = await Promise.all(tokensListDataPromises);
setTokensWithMetadata(tokensListData);
};

fetchTokensData();
}, [tokens]);

useEffect(() => {
async function checkWalletActivated() {
try {
Expand Down Expand Up @@ -340,7 +386,7 @@ export const PreparePayment: FC<PreparePaymentProps> = ({ onSendPaymentClick })
);
}

if (!tokens) {
if (!tokensWithMetadata) {
return <PageWithSpinner />;
}

Expand Down Expand Up @@ -382,22 +428,51 @@ export const PreparePayment: FC<PreparePaymentProps> = ({ onSendPaymentClick })
labelId="token-label"
id="token-select"
inputRef={tokenRef}
defaultValue={`${tokens[0].currency}-${tokens[0].issuer}`}
defaultValue={`${tokensWithMetadata[0].currency}-${tokensWithMetadata[0].issuer}`}
label="Token"
onChange={handleTokenChange}
>
{tokens.map((token) => (
<MenuItem
key={`${token.currency}-${token.issuer}`}
value={`${token.currency}-${token.issuer}`}
>
{convertHexCurrencyString(
token.issuer === undefined && token.currency !== mainToken
? mainToken
: token.currency
)}
</MenuItem>
))}
{tokensWithMetadata.map((token) => {
const displayCurrency =
token.issuer === undefined && token.currency !== mainToken
? mainToken
: token.currency;

return (
<MenuItem
key={`${token.currency}-${token.issuer}`}
value={`${token.currency}-${token.issuer}`}
>
{convertHexCurrencyString(displayCurrency)}
{token.value ? (
<Typography
variant="caption"
component="span"
style={{ marginLeft: '8px', fontStyle: 'italic', fontSize: '0.8rem' }}
>
- Available:{' '}
{formatToken(Number(token.value), convertHexCurrencyString(displayCurrency))}
</Typography>
) : null}
{token.issuer ? (
<Typography
variant="caption"
component="span"
style={{
marginLeft: '8px',
fontStyle: 'italic',
fontSize: '0.8rem',
overflow: 'hidden',
whiteSpace: 'nowrap',
textOverflow: 'ellipsis'
}}
>
- {token.issuerName ?? token.issuer}
</Typography>
) : null}
</MenuItem>
);
})}
</Select>
</FormControl>
<NumericInput
Expand Down
55 changes: 55 additions & 0 deletions packages/extension/src/utils/fetchTokenData.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { XRPL_META_URL } from '../constants';
import { XRPLMetaTokenAPIResponse } from '../types';
import { loadFromChromeSessionStorage, saveInChromeSessionStorage } from './storageChromeSession';

export interface TokenDisplayData {
tokenName: string;
tokenIconUrl?: string;
issuerName?: string;
issuerIconUrl?: string;
}

// API Reference: https://xrplmeta.org/api
export const getTrustLineData = async (params: {
token: string;
issuer: string;
}): Promise<TokenDisplayData> => {
const { token, issuer } = params;

try {
const cachedData = await loadFromChromeSessionStorage(`tokenData-${token}-${issuer}`);
if (cachedData) {
return cachedData;
}
} catch (e) {
} finally {
const fetchedData = fetchDataFromAPI(token, issuer);
if (fetchedData) {
saveInChromeSessionStorage(`tokenData-${token}-${issuer}`, fetchedData);
return fetchedData;
}
}

return {
tokenName: token
};
};

const fetchDataFromAPI = async (token: string, issuer: string): Promise<TokenDisplayData> => {
const res: Response = await fetch(`${XRPL_META_URL}/token/${token}:${issuer}`);
const json: XRPLMetaTokenAPIResponse = await res.json();

const tokenName: string | undefined = json?.meta?.token?.name ?? token;
const tokenIconUrl: string | undefined = json?.meta?.token?.icon ?? json?.meta?.issuer?.icon;
const issuerName: string | undefined = json?.meta?.issuer?.name;
const issuerIconUrl: string | undefined = json?.meta?.issuer?.icon;

const tokenData: TokenDisplayData = {
tokenName,
tokenIconUrl,
issuerName,
issuerIconUrl
};

return tokenData;
};
1 change: 1 addition & 0 deletions packages/extension/src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export * from './breakStringByLine';
export * from './convertHexCurrencyString';
export * from './crypto';
export * from './fetchTokenData';
export * from './format';
export * from './getLastItemFromArray';
export * from './hexConverter';
Expand Down
Loading