From 9800956d224e087b8bcadef82df23276409025b8 Mon Sep 17 00:00:00 2001 From: pandablue0809 Date: Tue, 2 Sep 2025 01:34:25 +0900 Subject: [PATCH 01/13] Add a search bar on the /token page --- components/Layout/SearchBlock.js | 244 +++++++++++++++++++++++-------- pages/token/[[...id]].js | 7 + 2 files changed, 188 insertions(+), 63 deletions(-) diff --git a/components/Layout/SearchBlock.js b/components/Layout/SearchBlock.js index 728ca9301..d94054826 100644 --- a/components/Layout/SearchBlock.js +++ b/components/Layout/SearchBlock.js @@ -17,9 +17,10 @@ import { networksIds, isValidNftXls20, isCurrencyHashValid, - server + server, + validateCurrencyCode } from '../../utils' -import { userOrServiceName, amountFormat } from '../../utils/format' +import { userOrServiceName, amountFormat, shortAddress, shortNiceNumber } from '../../utils/format' import { IoSearch } from 'react-icons/io5' @@ -73,7 +74,9 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat const windowWidth = useWidth() const { id } = router.query - const [searchItem, setSearchItem] = useState(id || userData?.address || '') + // Don't pre-populate search field on token pages to avoid showing current token + const initialSearchItem = tab === 'token' ? '' : (id || userData?.address || '') + const [searchItem, setSearchItem] = useState(initialSearchItem) const [searching, setSearching] = useState(false) const [searchSuggestions, setSearchSuggestions] = useState([]) const [searchingSuggestions, setSearchingSuggestions] = useState(false) @@ -96,10 +99,10 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat }, [id, searchInput]) useEffect(() => { - if (userData?.address) { + if (userData?.address && tab !== 'token') { setSearchItem(userData.address) } - }, [userData]) + }, [userData, tab]) const requestSuggestions = (value) => { if (isValidCTID(value)) { @@ -113,19 +116,39 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat typingTimer = setTimeout(async () => { if (value && value.length > 2) { setSearchingSuggestions(true) - const suggestionsResponse = await axios('v2/address/search/' + value).catch((error) => { - setSearchingSuggestions(false) - console.log(error.message) - }) - if (suggestionsResponse) { - const suggestions = suggestionsResponse.data - if (suggestions?.addresses?.length > 0) { - setSearchSuggestions(suggestions.addresses) + + try { + let suggestionsResponse + if (tab === 'token') { + // Use the same search logic as TokenSelector + suggestionsResponse = await axios(`v2/trustlines/tokens/search/${value}?limit=20¤cyDetails=true`) + } else { + // Default address search + suggestionsResponse = await axios('v2/address/search/' + value) + } + + if (suggestionsResponse) { + const suggestions = suggestionsResponse.data + if (tab === 'token' && suggestions?.tokens?.length > 0) { + // Format token suggestions with enhanced issuer information + const tokenSuggestions = await suggestions.tokens.map(token => ({ + ...token, + // Create a display label for tokens + displayLabel: `${token.currency} (${token.issuerDetails?.username || token.issuerDetails?.service || shortAddress(token.issuer, 8)})`, + // For routing purposes, we'll use the token identifier + tokenId: `${token.issuer}:${token.currency}` + })) + setSearchSuggestions(tokenSuggestions) + } else if (suggestions?.addresses?.length > 0) { + setSearchSuggestions(suggestions.addresses) + } } + } catch (error) { + setSearchingSuggestions(false) } setSearchingSuggestions(false) } - }, 500) // 0.5 sec + }, 300) // 0.3 sec - same as TokenSelector } } @@ -150,7 +173,14 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat const searchOnChange = (option) => { if (!option) return - if (option.username && !option.username.includes('-')) { + + // Update the search input to show the selected value + if (tab === 'token' && option.tokenId) { + // For tokens, show the token identifier in the input + setSearchItem(option.tokenId) + // Then search + onSearch(option.tokenId) + } else if (option.username && !option.username.includes('-')) { onSearch(option.username) } else { onSearch(option.address) @@ -190,6 +220,46 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat if (!searchFor) return + // Handle token search + if (tab === 'token') { + // Check if it's a token identifier (issuer:currency format) + if (searchFor.includes(':')) { + const [issuer, currency] = searchFor.split(':') + if (isAddressOrUsername(issuer) && validateCurrencyCode(currency).valid) { + router.push('/token/' + encodeURIComponent(issuer) + '/' + encodeURIComponent(currency)) + return + } + } + + // Check if it's a valid currency code + const { valid: isCurrencyValid } = validateCurrencyCode(searchFor) + + if (isCurrencyValid) { + // If it's just a currency code, redirect to tokens page with currency filter + router.push('/tokens?currency=' + encodeURIComponent(searchFor)) + return + } + + // Check if it's an issuer address or username + if (isAddressOrUsername(searchFor)) { + // Redirect to tokens page with issuer filter + router.push('/tokens?issuer=' + encodeURIComponent(searchFor)) + return + } + + // For any other search term, try to search for tokens and redirect to tokens page + if (searchFor.length >= 3) { + // Since tokens page doesn't support general search, redirect to tokens page + // The user can then use the filters on the tokens page + router.push('/tokens') + return + } + + // If none of the above, show error + setErrorMessage(t('explorer.invalid-token-input') || 'Please enter a valid currency code, issuer address, or token identifier') + return + } + if (tab === 'nft' && isValidNftXls20(searchFor)) { router.push('/nft/' + encodeURI(searchFor)) return @@ -339,7 +409,7 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat const explorerHeader = (tab) => { if ( - ['amm', 'account', 'nft', 'nfts', 'nft-offer', 'nft-offers', 'transaction', 'nft-volumes', 'object'].includes(tab) + ['amm', 'account', 'nft', 'nfts', 'nft-offer', 'nft-offers', 'transaction', 'nft-volumes', 'object', 'token'].includes(tab) ) { return t('explorer.header.' + tab) } else if (tab === 'transactions') { @@ -373,64 +443,112 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat onChange={searchOnChange} spellCheck="false" options={searchSuggestions} - getOptionLabel={(option) => ( - <> - {option.address} - {option.username || option.service || option.xaman ? (windowWidth > 400 ? ' - ' : ' ') : ''} - {option.username} - {option.service && ( - <> - {option.username ? ' (' : ''} - {option.service} - {option.username ? ')' : ''} - - )} - {(option.username || option.service) && (option.verifiedDomain || option.serviceDomain) && <>, } - {option.verifiedDomain ? ( - {option.verifiedDomain} - ) : ( - option.serviceDomain && {option.serviceDomain} - )} - {(option.username || option.service || option.verifiedDomain || option.serviceDomain) && - option.xaman && <>, } - {option.xaman && ( + getOptionLabel={(option) => { + if (tab === 'token' && option.tokenId) { + // Display token information similar to TokenSelector + return ( <> - Xaman{' '} - - {option.xaman.includes('+') ? option.xaman.replace(/\+/g, ' (') + ')' : option.xaman} + + {option.currency} + {option.issuerDetails?.username || option.issuerDetails?.service ? ( + <> + {' - '} + + {option.issuerDetails.username || option.issuerDetails.service} + + + ) : ( + <> + {' - '} + {shortAddress(option.issuer, 8)} + + )} - {option.xamanVerified && <> ✅} + {option.holders !== undefined && ( + <> + {' '} + + {shortNiceNumber(option.holders, 0)} holders + + + )} + {option.supply && ( + <> + {' '} + [{amountFormat(option.supply, { maxFractionDigits: 2, noSpace: true })}] + + )} - )} - {(option.username || - option.service || - option.verifiedDomain || - option.serviceDomain || - option.xaman) && <>, } - {option.balance && ( + ) + } else { + // Default address display + return ( <> - {' '} - [{amountFormat(option.balance, { maxFractionDigits: 2, noSpace: true })}] + {option.address} + {option.username || option.service || option.xaman ? (windowWidth > 400 ? ' - ' : ' ') : ''} + {option.username} + {option.service && ( + <> + {option.username ? ' (' : ''} + {option.service} + {option.username ? ')' : ''} + + )} + {(option.username || option.service) && (option.verifiedDomain || option.serviceDomain) && <>, } + {option.verifiedDomain ? ( + {option.verifiedDomain} + ) : ( + option.serviceDomain && {option.serviceDomain} + )} + {(option.username || + option.service || + option.verifiedDomain || + option.serviceDomain) && + option.xaman && <>, } + {option.xaman && ( + <> + Xaman{' '} + + {option.xaman.includes('+') ? option.xaman.replace(/\+/g, ' (') + ')' : option.xaman} + + {option.xamanVerified && <> ✅} + + )} + {(option.username || + option.service || + option.verifiedDomain || + option.serviceDomain || + option.xaman) && <>, } + {option.balance && ( + <> + {' '} + [{amountFormat(option.balance, { maxFractionDigits: 2, noSpace: true })}] + + )} - )} - - )} - getOptionValue={(option) => - option.address + - option.username + - option.service + - option.xaman + - option.verifiedDomain + - option.serviceDomain - } + ) + } + }} + getOptionValue={(option) => { + if (tab === 'token' && option.tokenId) { + return option.tokenId+option.currency+option.issuerDetails?.username+option.issuerDetails?.service+option.issuer + } + return option.address + + option.username + + option.service + + option.xaman + + option.verifiedDomain + + option.serviceDomain + }} inputValue={searchItem} onInputChange={searchOnInputChange} isSearchable={true} classNamePrefix="react-select" instanceId="search-select" noOptionsMessage={ - () => (searchingSuggestions ? t('explorer.searching-for-addresses') : null) - //({ inputValue }) => inputValue.length > 3 + () => (searchingSuggestions ? + (tab === 'token' ? t('explorer.searching-for-tokens') || 'Searching for tokens...' : t('explorer.searching-for-addresses')) + : null) } aria-label="Search" components={{ IndicatorsContainer: CustomIndicatorsContainer }} diff --git a/pages/token/[[...id]].js b/pages/token/[[...id]].js index 76ee162e6..2631a7ddb 100644 --- a/pages/token/[[...id]].js +++ b/pages/token/[[...id]].js @@ -4,6 +4,7 @@ import Link from 'next/link' import { useRouter } from 'next/router' import SEO from '../../components/SEO' +import SearchBlock from '../../components/Layout/SearchBlock' import { tokenClass } from '../../styles/pages/token.module.scss' import { niceNumber, shortNiceNumber, fullNiceNumber, AddressWithIconFilled } from '../../utils/format' import { axiosServer, getFiatRateServer, passHeaders } from '../../utils/axios' @@ -264,6 +265,12 @@ export default function TokenPage({ }`} /> + +
From 29e6cc2f9f0c1397ff4d5be3bfef709c4f2f1d46 Mon Sep 17 00:00:00 2001 From: pandablue0809 Date: Tue, 2 Sep 2025 01:43:52 +0900 Subject: [PATCH 02/13] reset delay in setTimeout --- components/Layout/SearchBlock.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/Layout/SearchBlock.js b/components/Layout/SearchBlock.js index d94054826..100844286 100644 --- a/components/Layout/SearchBlock.js +++ b/components/Layout/SearchBlock.js @@ -148,7 +148,7 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat } setSearchingSuggestions(false) } - }, 300) // 0.3 sec - same as TokenSelector + }, 500) // 0.5 sec } } From 3eca6f1ca6a9ff97472c76895439f749386f156a Mon Sep 17 00:00:00 2001 From: pandablue0809 Date: Tue, 2 Sep 2025 09:33:14 +0900 Subject: [PATCH 03/13] fix search block --- components/Layout/SearchBlock.js | 28 ++++++++++++++++++++++------ pages/token/[[...id]].js | 12 ++++++------ 2 files changed, 28 insertions(+), 12 deletions(-) diff --git a/components/Layout/SearchBlock.js b/components/Layout/SearchBlock.js index 100844286..7d9b29d51 100644 --- a/components/Layout/SearchBlock.js +++ b/components/Layout/SearchBlock.js @@ -20,7 +20,7 @@ import { server, validateCurrencyCode } from '../../utils' -import { userOrServiceName, amountFormat, shortAddress, shortNiceNumber } from '../../utils/format' +import { userOrServiceName, amountFormat, shortAddress, shortNiceNumber, niceCurrency } from '../../utils/format' import { IoSearch } from 'react-icons/io5' @@ -74,8 +74,10 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat const windowWidth = useWidth() const { id } = router.query - // Don't pre-populate search field on token pages to avoid showing current token - const initialSearchItem = tab === 'token' ? '' : (id || userData?.address || '') + // Set initial search item based on tab type + const initialSearchItem = tab === 'token' + ? (id && Array.isArray(id) && id.length >= 2 ? `${id[0]}:${id[1]}` : '') + : (id || userData?.address || '') const [searchItem, setSearchItem] = useState(initialSearchItem) const [searching, setSearching] = useState(false) const [searchSuggestions, setSearchSuggestions] = useState([]) @@ -409,15 +411,29 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat const explorerHeader = (tab) => { if ( - ['amm', 'account', 'nft', 'nfts', 'nft-offer', 'nft-offers', 'transaction', 'nft-volumes', 'object', 'token'].includes(tab) + ['amm', 'account', 'nft', 'nfts', 'nft-offer', 'nft-offers', 'transaction', 'nft-volumes', 'object'].includes(tab) ) { return t('explorer.header.' + tab) } else if (tab === 'transactions') { return t('explorer.menu.transactions') + } else if (tab === 'token') { + return 'Token information' } return '' } + const getTokenDisplayName = () => { + if (tab !== 'token') return '' + + const { id } = router.query + if (id && Array.isArray(id) && id.length >= 2) { + const currency = id[1] + return {niceCurrency(currency)} + } + + return '' + } + return ( <>
@@ -430,7 +446,7 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat ) : (
- {explorerHeader(tab)} {userOrServiceName(userData)} + {explorerHeader(tab)} {tab === 'token' ? getTokenDisplayName() : userOrServiceName(userData)}
)}
@@ -547,7 +563,7 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat instanceId="search-select" noOptionsMessage={ () => (searchingSuggestions ? - (tab === 'token' ? t('explorer.searching-for-tokens') || 'Searching for tokens...' : t('explorer.searching-for-addresses')) + (tab === 'token' ? 'Searching for tokens...' : t('explorer.searching-for-addresses')) : null) } aria-label="Search" diff --git a/pages/token/[[...id]].js b/pages/token/[[...id]].js index 2631a7ddb..c5abd7e83 100644 --- a/pages/token/[[...id]].js +++ b/pages/token/[[...id]].js @@ -394,18 +394,18 @@ export default function TokenPage({ {priceLine({ price: statistics?.priceNativeCurrencySpot, key: 'spot' })} )} - {statistics?.priceNativeCurrency1h && ( - - 1 Hour Ago - {priceLine({ price: statistics?.priceNativeCurrency1h, key: '1h' })} - - )} {statistics?.priceNativeCurrency5m && ( 5 Minutes Ago {priceLine({ price: statistics?.priceNativeCurrency5m, key: '5m' })} )} + {statistics?.priceNativeCurrency1h && ( + + 1 Hour Ago + {priceLine({ price: statistics?.priceNativeCurrency1h, key: '1h' })} + + )} {statistics?.priceNativeCurrency24h && ( 24 Hours Ago From 93edc3f151b2251c36c02270fa2b5de704ce9dea Mon Sep 17 00:00:00 2001 From: pandablue0809 Date: Fri, 10 Oct 2025 22:52:40 +0900 Subject: [PATCH 04/13] reset searchblock --- components/Layout/SearchBlock.js | 254 ++++++++----------------------- 1 file changed, 60 insertions(+), 194 deletions(-) diff --git a/components/Layout/SearchBlock.js b/components/Layout/SearchBlock.js index 21696ab52..08579167b 100644 --- a/components/Layout/SearchBlock.js +++ b/components/Layout/SearchBlock.js @@ -18,11 +18,10 @@ import { isValidNftXls20, isCurrencyHashValid, server, - validateCurrencyCode, isValidPayString, isValidXAddress } from '../../utils' -import { userOrServiceName, amountFormat, shortAddress, shortNiceNumber, niceCurrency } from '../../utils/format' +import { userOrServiceName, amountFormat } from '../../utils/format' import { IoSearch } from 'react-icons/io5' @@ -76,11 +75,7 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat const windowWidth = useWidth() const { id } = router.query - // Set initial search item based on tab type - const initialSearchItem = tab === 'token' - ? (id && Array.isArray(id) && id.length >= 2 ? `${id[0]}:${id[1]}` : '') - : (id || userData?.address || '') - const [searchItem, setSearchItem] = useState(initialSearchItem) + const [searchItem, setSearchItem] = useState(id || userData?.address || '') const [searching, setSearching] = useState(false) const [searchSuggestions, setSearchSuggestions] = useState([]) const [searchingSuggestions, setSearchingSuggestions] = useState(false) @@ -103,10 +98,10 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat }, [id, searchInput]) useEffect(() => { - if (userData?.address && tab !== 'token') { + if (userData?.address) { setSearchItem(userData.address) } - }, [userData, tab]) + }, [userData]) const requestSuggestions = (value) => { if (isValidCTID(value)) { @@ -120,35 +115,15 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat typingTimer = setTimeout(async () => { if (value && value.length > 2) { setSearchingSuggestions(true) - - try { - let suggestionsResponse - if (tab === 'token') { - // Use the same search logic as TokenSelector - suggestionsResponse = await axios(`v2/trustlines/tokens/search/${value}?limit=20¤cyDetails=true`) - } else { - // Default address search - suggestionsResponse = await axios('v2/address/search/' + value) - } - - if (suggestionsResponse) { - const suggestions = suggestionsResponse.data - if (tab === 'token' && suggestions?.tokens?.length > 0) { - // Format token suggestions with enhanced issuer information - const tokenSuggestions = await suggestions.tokens.map(token => ({ - ...token, - // Create a display label for tokens - displayLabel: `${token.currency} (${token.issuerDetails?.username || token.issuerDetails?.service || shortAddress(token.issuer, 8)})`, - // For routing purposes, we'll use the token identifier - tokenId: `${token.issuer}:${token.currency}` - })) - setSearchSuggestions(tokenSuggestions) - } else if (suggestions?.addresses?.length > 0) { - setSearchSuggestions(suggestions.addresses) - } - } - } catch (error) { + const suggestionsResponse = await axios('v2/address/search/' + value).catch((error) => { setSearchingSuggestions(false) + console.log(error.message) + }) + if (suggestionsResponse) { + const suggestions = suggestionsResponse.data + if (suggestions?.addresses?.length > 0) { + setSearchSuggestions(suggestions.addresses) + } } setSearchingSuggestions(false) } @@ -177,14 +152,7 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat const searchOnChange = (option) => { if (!option) return - - // Update the search input to show the selected value - if (tab === 'token' && option.tokenId) { - // For tokens, show the token identifier in the input - setSearchItem(option.tokenId) - // Then search - onSearch(option.tokenId) - } else if (option.username && !option.username.includes('-')) { + if (option.username && !option.username.includes('-')) { onSearch(option.username) } else { onSearch(option.address) @@ -224,46 +192,6 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat if (!searchFor) return - // Handle token search - if (tab === 'token') { - // Check if it's a token identifier (issuer:currency format) - if (searchFor.includes(':')) { - const [issuer, currency] = searchFor.split(':') - if (isAddressOrUsername(issuer) && validateCurrencyCode(currency).valid) { - router.push('/token/' + encodeURIComponent(issuer) + '/' + encodeURIComponent(currency)) - return - } - } - - // Check if it's a valid currency code - const { valid: isCurrencyValid } = validateCurrencyCode(searchFor) - - if (isCurrencyValid) { - // If it's just a currency code, redirect to tokens page with currency filter - router.push('/tokens?currency=' + encodeURIComponent(searchFor)) - return - } - - // Check if it's an issuer address or username - if (isAddressOrUsername(searchFor)) { - // Redirect to tokens page with issuer filter - router.push('/tokens?issuer=' + encodeURIComponent(searchFor)) - return - } - - // For any other search term, try to search for tokens and redirect to tokens page - if (searchFor.length >= 3) { - // Since tokens page doesn't support general search, redirect to tokens page - // The user can then use the filters on the tokens page - router.push('/tokens') - return - } - - // If none of the above, show error - setErrorMessage(t('explorer.invalid-token-input') || 'Please enter a valid currency code, issuer address, or token identifier') - return - } - if (tab === 'nft' && isValidNftXls20(searchFor)) { router.push('/nft/' + encodeURI(searchFor)) return @@ -434,26 +362,12 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat return t('explorer.header.' + tab) } else if (tab === 'transactions') { return t('explorer.menu.transactions') - } else if (tab === 'token') { - return 'Token information' } else if (tab === 'dex') { return 'DEX Orders' } return '' } - const getTokenDisplayName = () => { - if (tab !== 'token') return '' - - const { id } = router.query - if (id && Array.isArray(id) && id.length >= 2) { - const currency = id[1] - return {niceCurrency(currency)} - } - - return '' - } - return ( <>
@@ -466,7 +380,7 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat ) : (
- {explorerHeader(tab)} {tab === 'token' ? getTokenDisplayName() : userOrServiceName(userData)} + {explorerHeader(tab)} {userOrServiceName(userData)}
)}
@@ -479,105 +393,58 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat onChange={searchOnChange} spellCheck="false" options={searchSuggestions} - getOptionLabel={(option) => { - if (tab === 'token' && option.tokenId) { - // Display token information similar to TokenSelector - return ( + getOptionLabel={(option) => ( + <> + {option.address} + {option.username || option.service || option.xaman ? (windowWidth > 400 ? ' - ' : ' ') : ''} + {option.username} + {option.service && ( + <> + {option.username ? ' (' : ''} + {option.service} + {option.username ? ')' : ''} + + )} + {(option.username || option.service) && (option.verifiedDomain || option.serviceDomain) && <>, } + {option.verifiedDomain ? ( + {option.verifiedDomain} + ) : ( + option.serviceDomain && {option.serviceDomain} + )} + {(option.username || option.service || option.verifiedDomain || option.serviceDomain) && + option.xaman && <>, } + {option.xaman && ( <> - - {option.currency} - {option.issuerDetails?.username || option.issuerDetails?.service ? ( - <> - {' - '} - - {option.issuerDetails.username || option.issuerDetails.service} - - - ) : ( - <> - {' - '} - {shortAddress(option.issuer, 8)} - - )} + Xaman{' '} + + {option.xaman.includes('+') ? option.xaman.replace(/\+/g, ' (') + ')' : option.xaman} - {option.holders !== undefined && ( - <> - {' '} - - {shortNiceNumber(option.holders, 0)} holders - - - )} - {option.supply && ( - <> - {' '} - [{amountFormat(option.supply, { maxFractionDigits: 2, noSpace: true })}] - - )} + {option.xamanVerified && <> ✅} - ) - } else { - // Default address display - return ( + )} + {option.tag ? ( + + {' '} + [TAG: {option.tag}] + + ) : ( <> - {option.address} - {option.username || option.service || option.xaman ? (windowWidth > 400 ? ' - ' : ' ') : ''} - {option.username} - {option.service && ( - <> - {option.username ? ' (' : ''} - {option.service} - {option.username ? ')' : ''} - - )} - {(option.username || option.service) && (option.verifiedDomain || option.serviceDomain) && <>, } - {option.verifiedDomain ? ( - {option.verifiedDomain} - ) : ( - option.serviceDomain && {option.serviceDomain} - )} - {(option.username || - option.service || - option.verifiedDomain || - option.serviceDomain) && - option.xaman && <>, } - {option.xaman && ( + {option.balance !== null && ( <> - Xaman{' '} - - {option.xaman.includes('+') ? option.xaman.replace(/\+/g, ' (') + ')' : option.xaman} - - {option.xamanVerified && <> ✅} - - )} - {option.tag ? ( - {' '} - [TAG: {option.tag}] - - ) : ( - <> - {option.balance !== null && ( - <> - {' '} - [ - - {amountFormat(option.balance, { maxFractionDigits: 2, noSpace: true }) || 'Not activated'} - - ] - - )} + [ + + {amountFormat(option.balance, { maxFractionDigits: 2, noSpace: true }) || 'Not activated'} + + ] )} - ) - } - }} - getOptionValue={(option) => { - if (tab === 'token' && option.tokenId) { - return option.tokenId+option.currency+option.issuerDetails?.username+option.issuerDetails?.service+option.issuer - } - return option.address + + )} + + )} + getOptionValue={(option) => + option.address + option.username + option.service + option.payString + @@ -585,16 +452,15 @@ export default function SearchBlock({ searchPlaceholderText, tab = null, userDat option.verifiedDomain + option.serviceDomain + option.xAddress - }} + } inputValue={searchItem} onInputChange={searchOnInputChange} isSearchable={true} classNamePrefix="react-select" instanceId="search-select" noOptionsMessage={ - () => (searchingSuggestions ? - (tab === 'token' ? 'Searching for tokens...' : t('explorer.searching-for-addresses')) - : null) + () => (searchingSuggestions ? t('explorer.searching-for-addresses') : null) + //({ inputValue }) => inputValue.length > 3 } aria-label="Search" components={{ IndicatorsContainer: CustomIndicatorsContainer }} From 9cccb12438ce9b8321246c7c6b8cfd5f190c2cc7 Mon Sep 17 00:00:00 2001 From: pandablue0809 Date: Sat, 11 Oct 2025 03:36:21 +0900 Subject: [PATCH 05/13] fix using the popup --- components/UI/TokenSelector.js | 13 +++++++++--- pages/token/[[...id]].js | 38 +++++++++++++++++++++++++--------- 2 files changed, 38 insertions(+), 13 deletions(-) diff --git a/components/UI/TokenSelector.js b/components/UI/TokenSelector.js index 6f5cce018..0c3c1f400 100644 --- a/components/UI/TokenSelector.js +++ b/components/UI/TokenSelector.js @@ -61,7 +61,8 @@ export default function TokenSelector({ excludeNative = false, destinationAddress = null, allOrOne, - currencyQueryName + currencyQueryName, + addParams = true }) { const { t } = useTranslation() const router = useRouter() @@ -81,6 +82,12 @@ export default function TokenSelector({ useEffect(() => { if (!currencyQueryName) return + if(!addParams) { + const {pathname, query} = router + query.id = [value?.issuer, value?.currency] + router.replace({ pathname, query }, null, { shallow: true }) + return + } let queryAddList = [] let queryRemoveList = [] if (value?.currency) { @@ -142,7 +149,7 @@ export default function TokenSelector({ tokens = await fetchTrustlinesForDestination(destinationAddress) } else { // Fallback to original behavior if no destination address - const response = await axios('v2/trustlines/tokens?limit=' + limit + '¤cyDetails=true') + const response = await axios('v2/trustlines/tokens?limit=' + limit + '¤cyDetails=true&statistics=true') tokens = response.data?.tokens || [] if (!excludeNative) { const defaultTokens = [{ currency: nativeCurrency }, ...tokens] @@ -199,7 +206,7 @@ export default function TokenSelector({ setCachedSearchResults(tokensWithNative) } else { // Fallback to original search behavior - const response = await axios(`v2/trustlines/tokens/search/${searchQuery}?limit=${limit}¤cyDetails=true`) + const response = await axios(`v2/trustlines/tokens/search/${searchQuery}?limit=${limit}¤cyDetails=true&statistics=true`) const tokens = response.data?.tokens || [] const tokensWithNative = addNativeCurrencyIfNeeded(tokens, excludeNative, searchQuery) setSearchResults(tokensWithNative) diff --git a/pages/token/[[...id]].js b/pages/token/[[...id]].js index 111c7d50d..0dac5edf4 100644 --- a/pages/token/[[...id]].js +++ b/pages/token/[[...id]].js @@ -4,9 +4,9 @@ import Link from 'next/link' import { useRouter } from 'next/router' import SEO from '../../components/SEO' -import SearchBlock from '../../components/Layout/SearchBlock' +import TokenSelector from '../../components/UI/TokenSelector' import { tokenClass } from '../../styles/pages/token.module.scss' -import { niceNumber, shortNiceNumber, fullNiceNumber, AddressWithIconFilled } from '../../utils/format' +import { niceNumber, shortNiceNumber, fullNiceNumber, AddressWithIconFilled, amountFormat } from '../../utils/format' import { axiosServer, getFiatRateServer, passHeaders } from '../../utils/axios' import { fetchHistoricalRate } from '../../utils/common' import { getIsSsrMobile } from '../../utils/mobile' @@ -98,7 +98,7 @@ export default function TokenPage({ isSsrMobile }) { const router = useRouter() - const token = initialData + const [token, setToken] = useState(initialData) const errorMessage = initialErrorMessage || '' let selectedCurrency = selectedCurrencyServer @@ -270,15 +270,33 @@ export default function TokenPage({ token.issuerDetails?.service || token.issuerDetails?.username || 'Token Details' }`} /> - - -
+
+ + Token + {token.supply && token.currency && ( + + {' '} + - the Limit will be set to the total supply:{' '} + {amountFormat({ value: token.supply, currency: token.currency })} + + )} + +
+ +
+
{/* Big Token Icon */} Date: Wed, 15 Oct 2025 05:45:00 +0200 Subject: [PATCH 06/13] prettify --- components/UI/TokenSelector.js | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/components/UI/TokenSelector.js b/components/UI/TokenSelector.js index 677764a60..e2a5eff40 100644 --- a/components/UI/TokenSelector.js +++ b/components/UI/TokenSelector.js @@ -83,8 +83,8 @@ export default function TokenSelector({ useEffect(() => { if (!currencyQueryName) return - if(!addParams) { - const {pathname, query} = router + if (!addParams) { + const { pathname, query } = router query.id = [value?.issuer, value?.currency] router.replace({ pathname, query }, null, { shallow: true }) return @@ -150,7 +150,9 @@ export default function TokenSelector({ tokens = await fetchTrustlinesForDestination(destinationAddress) } else { // Fallback to original behavior if no destination address - const response = await axios('v2/trustlines/tokens?limit=' + limit + '¤cyDetails=true&statistics=true') + const response = await axios( + 'v2/trustlines/tokens?limit=' + limit + '¤cyDetails=true&statistics=true' + ) tokens = response.data?.tokens || [] if (!excludeNative) { const defaultTokens = [{ currency: nativeCurrency }, ...tokens] @@ -207,7 +209,9 @@ export default function TokenSelector({ setCachedSearchResults(tokensWithNative) } else { // Fallback to original search behavior - const response = await axios(`v2/trustlines/tokens/search/${searchQuery}?limit=${limit}¤cyDetails=true&statistics=true`) + const response = await axios( + `v2/trustlines/tokens/search/${searchQuery}?limit=${limit}¤cyDetails=true&statistics=true` + ) const tokens = response.data?.tokens || [] const tokensWithNative = addNativeCurrencyIfNeeded(tokens, excludeNative, searchQuery) setSearchResults(tokensWithNative) @@ -241,7 +245,9 @@ export default function TokenSelector({ } const fetchToken = async (token) => { - const response = await axios(`v2/trustlines/token/${token?.issuer}/${token?.currency}?statistics=true¤cyDetails=true&convertCurrencies=${selectedCurrency}`) + const response = await axios( + `v2/trustlines/token/${token?.issuer}/${token?.currency}?statistics=true¤cyDetails=true&convertCurrencies=${selectedCurrency}` + ) return response.data || null } From 471e3488fefc22175957655af517c1b5a3fb3c6a Mon Sep 17 00:00:00 2001 From: Viacheslav Bakshaev Date: Wed, 15 Oct 2025 05:47:40 +0200 Subject: [PATCH 07/13] prettify, remove currencyQueryName --- pages/token/[[...id]].js | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/pages/token/[[...id]].js b/pages/token/[[...id]].js index 43d20159e..6bee99090 100644 --- a/pages/token/[[...id]].js +++ b/pages/token/[[...id]].js @@ -275,17 +275,18 @@ export default function TokenPage({ />
-
-
+
+
From f3f847858782e372e2dfda92ccc400c9ecc09677 Mon Sep 17 00:00:00 2001 From: Viacheslav Bakshaev Date: Wed, 15 Oct 2025 05:56:21 +0200 Subject: [PATCH 08/13] move token page specific code to the token page --- components/UI/TokenSelector.js | 7 ------- pages/token/[[...id]].js | 8 +++++++- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/components/UI/TokenSelector.js b/components/UI/TokenSelector.js index e2a5eff40..8593909d2 100644 --- a/components/UI/TokenSelector.js +++ b/components/UI/TokenSelector.js @@ -62,7 +62,6 @@ export default function TokenSelector({ destinationAddress = null, allOrOne, currencyQueryName, - addParams = true, selectedCurrency = null }) { const { t } = useTranslation() @@ -83,12 +82,6 @@ export default function TokenSelector({ useEffect(() => { if (!currencyQueryName) return - if (!addParams) { - const { pathname, query } = router - query.id = [value?.issuer, value?.currency] - router.replace({ pathname, query }, null, { shallow: true }) - return - } let queryAddList = [] let queryRemoveList = [] if (value?.currency && value.currency !== nativeCurrency) { diff --git a/pages/token/[[...id]].js b/pages/token/[[...id]].js index 6bee99090..27784d607 100644 --- a/pages/token/[[...id]].js +++ b/pages/token/[[...id]].js @@ -140,8 +140,14 @@ export default function TokenPage({ getHistoricalRates() // eslint-disable-next-line react-hooks/exhaustive-deps }, [selectedCurrency]) - // Helper: price line as "fiat (XRP)" using historical rate when available + useEffect(() => { + const { pathname, query } = router + query.id = [token?.issuer, token?.currency] + router.replace({ pathname, query }, null, { shallow: true }) + }, [router, token]) + + // Helper: price line as "fiat (XRP)" using historical rate when available const priceLine = ({ priceNative, priceFiat }) => { const price = priceNative return ( From f17876d25570cad66ff7c02a118d034b2f2efb1e Mon Sep 17 00:00:00 2001 From: Viacheslav Bakshaev Date: Wed, 15 Oct 2025 06:04:10 +0200 Subject: [PATCH 09/13] Remove not needed flag for api endpoint --- components/UI/TokenSelector.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/UI/TokenSelector.js b/components/UI/TokenSelector.js index 8593909d2..1251f7e18 100644 --- a/components/UI/TokenSelector.js +++ b/components/UI/TokenSelector.js @@ -144,7 +144,7 @@ export default function TokenSelector({ } else { // Fallback to original behavior if no destination address const response = await axios( - 'v2/trustlines/tokens?limit=' + limit + '¤cyDetails=true&statistics=true' + 'v2/trustlines/tokens?limit=' + limit + '¤cyDetails=true' // &statistics=true - shall we get USD prices and show them ) tokens = response.data?.tokens || [] if (!excludeNative) { From 37b60f8e113a63868f9d8f78a675700ab977979e Mon Sep 17 00:00:00 2001 From: Viacheslav Bakshaev Date: Wed, 15 Oct 2025 06:13:24 +0200 Subject: [PATCH 10/13] remove not needed api flags - part 2 --- components/UI/TokenSelector.js | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/components/UI/TokenSelector.js b/components/UI/TokenSelector.js index 1251f7e18..ef5cf2414 100644 --- a/components/UI/TokenSelector.js +++ b/components/UI/TokenSelector.js @@ -62,7 +62,7 @@ export default function TokenSelector({ destinationAddress = null, allOrOne, currencyQueryName, - selectedCurrency = null + selectedCurrency }) { const { t } = useTranslation() const router = useRouter() @@ -143,9 +143,8 @@ export default function TokenSelector({ tokens = await fetchTrustlinesForDestination(destinationAddress) } else { // Fallback to original behavior if no destination address - const response = await axios( - 'v2/trustlines/tokens?limit=' + limit + '¤cyDetails=true' // &statistics=true - shall we get USD prices and show them - ) + // &statistics=true - shall we get USD prices and show them? + const response = await axios('v2/trustlines/tokens?limit=' + limit + '¤cyDetails=true') tokens = response.data?.tokens || [] if (!excludeNative) { const defaultTokens = [{ currency: nativeCurrency }, ...tokens] @@ -202,9 +201,8 @@ export default function TokenSelector({ setCachedSearchResults(tokensWithNative) } else { // Fallback to original search behavior - const response = await axios( - `v2/trustlines/tokens/search/${searchQuery}?limit=${limit}¤cyDetails=true&statistics=true` - ) + // &statistics=true - shall we get USD prices and show them? + const response = await axios(`v2/trustlines/tokens/search/${searchQuery}?limit=${limit}¤cyDetails=true`) const tokens = response.data?.tokens || [] const tokensWithNative = addNativeCurrencyIfNeeded(tokens, excludeNative, searchQuery) setSearchResults(tokensWithNative) From 7a29d8a1ca6df2ed35c4eaa113efe2555366369e Mon Sep 17 00:00:00 2001 From: Viacheslav Bakshaev Date: Wed, 15 Oct 2025 06:35:22 +0200 Subject: [PATCH 11/13] remove added fetch from the token selector component, rename function --- components/UI/TokenSelector.js | 13 ++----------- pages/token/[[...id]].js | 22 +++++++++------------- 2 files changed, 11 insertions(+), 24 deletions(-) diff --git a/components/UI/TokenSelector.js b/components/UI/TokenSelector.js index ef5cf2414..83c917020 100644 --- a/components/UI/TokenSelector.js +++ b/components/UI/TokenSelector.js @@ -61,8 +61,7 @@ export default function TokenSelector({ excludeNative = false, destinationAddress = null, allOrOne, - currencyQueryName, - selectedCurrency + currencyQueryName }) { const { t } = useTranslation() const router = useRouter() @@ -230,18 +229,10 @@ export default function TokenSelector({ }, [searchQuery, isOpen, destinationAddress]) const handleSelect = async (token) => { - const newToken = await fetchToken(token) - onChange(newToken) + onChange(token) setIsOpen(false) } - const fetchToken = async (token) => { - const response = await axios( - `v2/trustlines/token/${token?.issuer}/${token?.currency}?statistics=true¤cyDetails=true&convertCurrencies=${selectedCurrency}` - ) - return response.data || null - } - // Helper to get icon url if available const getTokenIcon = (token) => { let imageUrl = tokenImageSrc(token) diff --git a/pages/token/[[...id]].js b/pages/token/[[...id]].js index 27784d607..29c255f69 100644 --- a/pages/token/[[...id]].js +++ b/pages/token/[[...id]].js @@ -98,6 +98,7 @@ export default function TokenPage({ const router = useRouter() const [token, setToken] = useState(initialData) const [loading, setLoading] = useState(false) + const [selectedToken, setSelectedToken] = useState(initialData) const errorMessage = initialErrorMessage || '' const firstRenderRef = useRef(true) @@ -116,11 +117,11 @@ export default function TokenPage({ } }, [initialData, initialErrorMessage, router]) - const getHistoricalRates = async () => { + const getData = async () => { setLoading(true) const cur = selectedCurrency?.toLowerCase() if (!cur) return - const url = `v2/trustlines/token/${initialData.issuer}/${initialData.currency}?statistics=true¤cyDetails=true&convertCurrencies=${cur}` + const url = `v2/trustlines/token/${selectedToken.issuer}/${selectedToken.currency}?statistics=true¤cyDetails=true&convertCurrencies=${cur}` const res = await axiosServer({ method: 'get', url @@ -137,15 +138,16 @@ export default function TokenPage({ firstRenderRef.current = false return } - getHistoricalRates() + getData() // eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedCurrency]) + }, [selectedCurrency, selectedToken]) useEffect(() => { const { pathname, query } = router - query.id = [token?.issuer, token?.currency] + query.id = [selectedToken?.issuer, selectedToken?.currency] router.replace({ pathname, query }, null, { shallow: true }) - }, [router, token]) + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [selectedToken]) // Helper: price line as "fiat (XRP)" using historical rate when available const priceLine = ({ priceNative, priceFiat }) => { @@ -289,13 +291,7 @@ export default function TokenPage({ }} >
- +
From 2376374f5597819af6c82e4c74414bd3a41de9aa Mon Sep 17 00:00:00 2001 From: Viacheslav Bakshaev Date: Wed, 15 Oct 2025 06:36:26 +0200 Subject: [PATCH 12/13] clean up --- components/UI/TokenSelector.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/UI/TokenSelector.js b/components/UI/TokenSelector.js index 83c917020..069a36dc4 100644 --- a/components/UI/TokenSelector.js +++ b/components/UI/TokenSelector.js @@ -228,7 +228,7 @@ export default function TokenSelector({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [searchQuery, isOpen, destinationAddress]) - const handleSelect = async (token) => { + const handleSelect = (token) => { onChange(token) setIsOpen(false) } From 5dcbf1f43d25a22c1482ab20630730aee288e62c Mon Sep 17 00:00:00 2001 From: Viacheslav Bakshaev Date: Wed, 15 Oct 2025 06:40:48 +0200 Subject: [PATCH 13/13] image fix on mobile --- pages/token/[[...id]].js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/token/[[...id]].js b/pages/token/[[...id]].js index 29c255f69..1a9281f85 100644 --- a/pages/token/[[...id]].js +++ b/pages/token/[[...id]].js @@ -300,7 +300,7 @@ export default function TokenPage({ alt="token" src={tokenImageSrc(token)} className="token-image" - style={{ width: '100%', height: 'auto' }} + style={{ width: 'calc(100% - 4px)', height: 'auto' }} />

{token?.currencyDetails?.currency}