diff --git a/packages/common/src/api/tan-query/coins/useArtistCoinByTicker.ts b/packages/common/src/api/tan-query/coins/useArtistCoinByTicker.ts index 472b53e8c97..2b84906916e 100644 --- a/packages/common/src/api/tan-query/coins/useArtistCoinByTicker.ts +++ b/packages/common/src/api/tan-query/coins/useArtistCoinByTicker.ts @@ -10,6 +10,7 @@ import { useQueryContext, type QueryContextType } from '~/api/tan-query/utils/QueryContext' +import { formatTicker } from '~/utils' import { QUERY_KEYS } from '../queryKeys' import { combineQueryStatuses } from '../utils' @@ -32,7 +33,7 @@ export const fetchCoinTickerAvailability = async ( const sdk = await audiusSdk() try { // Use getCoinByTicker - if it returns a coin, the ticker is taken - await sdk.coins.getCoinByTicker({ ticker }) + await sdk.coins.getCoinByTicker({ ticker: formatTicker(ticker) }) // If we get a coin back, the ticker is not available return { available: false } } catch (error: any) { @@ -66,8 +67,9 @@ const getArtistCoinByTickerQueryFn = const [_ignored, ticker] = queryKey const { audiusSdk } = context const sdk = await audiusSdk() + // NOTE: Might not need to format the ticker here, but being safe const response = await sdk.coins.getCoinByTicker({ - ticker + ticker: formatTicker(ticker) }) const coin = coinFromSdk(response.data) @@ -88,7 +90,7 @@ export const getArtistCoinByTickerOptions = ( { ticker }: UseArtistCoinByTickerParams ) => { return queryOptions({ - queryKey: getArtistCoinByTickerQueryKey(ticker), + queryKey: getArtistCoinByTickerQueryKey(formatTicker(ticker)), queryFn: getArtistCoinByTickerQueryFn(context), enabled: !!ticker }) @@ -103,7 +105,10 @@ export const useArtistCoinByTicker = ( const mintQuery = useQuery({ ...options, - ...getArtistCoinByTickerOptions({ ...context, queryClient }, params) + ...getArtistCoinByTickerOptions( + { ...context, queryClient }, + { ...params, ticker: formatTicker(params.ticker) } + ) }) const coinQuery = useArtistCoin(mintQuery.data!) diff --git a/packages/common/src/utils/formatUtil.ts b/packages/common/src/utils/formatUtil.ts index 308c8ce2a64..da01390e575 100644 --- a/packages/common/src/utils/formatUtil.ts +++ b/packages/common/src/utils/formatUtil.ts @@ -190,8 +190,18 @@ export const getHash = (str: string) => export const formatDoubleDigit = (value: number) => value.toString().padStart(2, '0') +/** + * Formats a ticker to be uppercase and trimmed + */ +export const formatTicker = (ticker?: string) => + ticker?.trim().toUpperCase() ?? '' + /** * Formats a ticker to be url friendly */ -export const formatTickerForUrl = (ticker: string) => - ticker.startsWith('$') ? ticker.slice(1) : ticker +export const formatTickerForUrl = (ticker: string) => { + const formattedTicker = formatTicker(ticker) + return formattedTicker.startsWith('$') + ? formattedTicker.slice(1) + : formattedTicker +} diff --git a/packages/common/src/utils/route.ts b/packages/common/src/utils/route.ts index 1525fb7ae75..2f6dc2ac11a 100644 --- a/packages/common/src/utils/route.ts +++ b/packages/common/src/utils/route.ts @@ -3,7 +3,7 @@ import { matchPath, generatePath } from 'react-router' import { ID, SearchCategory, SearchFilters } from '~/models' -import { encodeUrlName } from './formatUtil' +import { encodeUrlName, formatTickerForUrl } from './formatUtil' import { convertGenreLabelToValue, Genre } from './genres' // External Routes @@ -473,4 +473,7 @@ export const searchPage = (searchOptions: SearchOptions) => { } export const coinPage = (ticker: string) => - `/coins/${ticker.startsWith('$') ? ticker.slice(1) : ticker}` + `/coins/${formatTickerForUrl(ticker)}` + +export const coinRedeemPage = (ticker: string, code?: string) => + `/coins/${formatTickerForUrl(ticker)}/redeem${code ? `/${code}` : ''}` diff --git a/packages/web/src/pages/coin-detail-page/CoinDetailPage.tsx b/packages/web/src/pages/coin-detail-page/CoinDetailPage.tsx index 07b39483422..de60bdfbfd4 100644 --- a/packages/web/src/pages/coin-detail-page/CoinDetailPage.tsx +++ b/packages/web/src/pages/coin-detail-page/CoinDetailPage.tsx @@ -4,7 +4,8 @@ import { useUser } from '@audius/common/api' import { coinDetailsMessages } from '@audius/common/messages' -import { route } from '@audius/common/utils' +import { coinPage } from '@audius/common/src/utils/route' +import { formatTickerForUrl, route } from '@audius/common/utils' import { Flex, LoadingSpinner } from '@audius/harmony' import { Redirect, useParams } from 'react-router-dom' @@ -91,13 +92,14 @@ export const CoinDetailPage = () => { const { ticker } = useParams<{ ticker: string }>() const isMobile = useIsMobile() const { data: currentUserId } = useCurrentUserId() + const formattedTicker = formatTickerForUrl(ticker) const { data: coin, isPending: coinPending, isError, isSuccess - } = useArtistCoinByTicker({ ticker }) + } = useArtistCoinByTicker({ ticker: formattedTicker }) const { data: owner } = useUser(coin?.ownerId, { enabled: !!coin?.ownerId @@ -107,6 +109,10 @@ export const CoinDetailPage = () => { return } + if (ticker !== formattedTicker) { + return + } + if (isError || (isSuccess && !coin)) { return } diff --git a/packages/web/src/pages/coin-redeem-page/CoinRedeemPage.tsx b/packages/web/src/pages/coin-redeem-page/CoinRedeemPage.tsx index 037c3f76d17..3599a129af8 100644 --- a/packages/web/src/pages/coin-redeem-page/CoinRedeemPage.tsx +++ b/packages/web/src/pages/coin-redeem-page/CoinRedeemPage.tsx @@ -12,9 +12,11 @@ import { import { toast } from '@audius/common/src/store/ui/toast/slice' import { coinPage, + coinRedeemPage, COINS_EXPLORE_PAGE, NOT_FOUND_PAGE } from '@audius/common/src/utils/route' +import { formatTickerForUrl } from '@audius/common/utils' import { Button, Flex, @@ -173,6 +175,7 @@ const PageContent = ({ export const CoinRedeemPage = () => { const { ticker, code } = useParams<{ ticker: string; code?: string }>() const isMobile = useIsMobile() + const formattedTicker = formatTickerForUrl(ticker) const { data: coin, @@ -203,6 +206,10 @@ export const CoinRedeemPage = () => { return } + if (ticker !== formattedTicker) { + return + } + if (isError || (isSuccess && !coin)) { return }