From c7dec59670ea246de8997c227d626f28bb84e04e Mon Sep 17 00:00:00 2001 From: Farid Salau Date: Sat, 28 Jun 2025 10:10:55 -0500 Subject: [PATCH 01/10] Add slug based asset detail page --- .../common/src/messages/walletMessages.ts | 5 +- .../common/src/store/ui/buy-sell/constants.ts | 6 +- packages/common/src/utils/route.ts | 2 + packages/web/src/app/web-player/WebPlayer.jsx | 15 ++- .../asset-detail-page/AssetDetailContent.tsx | 33 ++++++ .../asset-detail-page/AssetDetailPage.tsx | 32 +++++ .../asset-detail-page/AssetDetailTabs.tsx | 61 ++++++++++ .../components/BalanceSection.tsx | 111 ++++++++++++++++++ .../src/pages/asset-detail-page/constants.ts | 11 ++ .../web/src/pages/asset-detail-page/types.ts | 7 ++ .../audio-page/AudioWalletTransactions.tsx | 2 +- 11 files changed, 274 insertions(+), 11 deletions(-) create mode 100644 packages/web/src/pages/asset-detail-page/AssetDetailContent.tsx create mode 100644 packages/web/src/pages/asset-detail-page/AssetDetailPage.tsx create mode 100644 packages/web/src/pages/asset-detail-page/AssetDetailTabs.tsx create mode 100644 packages/web/src/pages/asset-detail-page/components/BalanceSection.tsx create mode 100644 packages/web/src/pages/asset-detail-page/constants.ts create mode 100644 packages/web/src/pages/asset-detail-page/types.ts diff --git a/packages/common/src/messages/walletMessages.ts b/packages/common/src/messages/walletMessages.ts index abe3726a571..24b2b25c548 100644 --- a/packages/common/src/messages/walletMessages.ts +++ b/packages/common/src/messages/walletMessages.ts @@ -70,5 +70,8 @@ export const walletMessages = { // YourCoins messages yourCoins: 'Your Coins', - buySell: 'Buy/Sell' + buySell: 'Buy/Sell', + buy: 'Buy', + send: 'Send', + receive: 'Receive' } diff --git a/packages/common/src/store/ui/buy-sell/constants.ts b/packages/common/src/store/ui/buy-sell/constants.ts index 3b0b03a0d9f..7105da9a4e0 100644 --- a/packages/common/src/store/ui/buy-sell/constants.ts +++ b/packages/common/src/store/ui/buy-sell/constants.ts @@ -9,7 +9,7 @@ export const MAX_SWAP_AMOUNT_USD = 10000 // $10,000 // Token metadata without icons (to avoid circular dependency with harmony) export const TOKENS: Record = { AUDIO: { - symbol: 'AUDIO', + symbol: TOKEN_LISTING_MAP.AUDIO.symbol, name: 'Audius', decimals: TOKEN_LISTING_MAP.AUDIO.decimals, balance: null, @@ -17,8 +17,8 @@ export const TOKENS: Record = { address: TOKEN_LISTING_MAP.AUDIO.address }, USDC: { - symbol: 'USDC', - name: 'USD Coin', + symbol: TOKEN_LISTING_MAP.USDC.symbol, + name: TOKEN_LISTING_MAP.USDC.name, decimals: TOKEN_LISTING_MAP.USDC.decimals, balance: null, isStablecoin: true, diff --git a/packages/common/src/utils/route.ts b/packages/common/src/utils/route.ts index cb0cc4e792c..acfbe881d0f 100644 --- a/packages/common/src/utils/route.ts +++ b/packages/common/src/utils/route.ts @@ -80,6 +80,7 @@ export const PURCHASES_PAGE = '/payments/purchases' export const SALES_PAGE = '/payments/sales' export const WITHDRAWALS_PAGE = '/payments/withdrawals' export const TRANSACTION_HISTORY_PAGE = '/wallet/transaction-history' +export const WALLET_SLUG_PAGE = '/wallet/:slug' export const WALLET_PAGE = '/wallet' export const PRIVATE_KEY_EXPORTER_SETTINGS_PAGE = '/settings/export-private-key' export const DEV_TOOLS_PAGE = '/dev-tools' @@ -279,6 +280,7 @@ export const orderedRoutes = [ PAYMENTS_PAGE, AUDIO_PAGE, WALLET_AUDIO_PAGE, + WALLET_SLUG_PAGE, WALLET_PAGE, REWARDS_PAGE, SETTINGS_PAGE, diff --git a/packages/web/src/app/web-player/WebPlayer.jsx b/packages/web/src/app/web-player/WebPlayer.jsx index 5ed835f575f..e21ae6c66a0 100644 --- a/packages/web/src/app/web-player/WebPlayer.jsx +++ b/packages/web/src/app/web-player/WebPlayer.jsx @@ -51,6 +51,7 @@ import { USDCBalanceFetcher } from 'components/usdc-balance-fetcher/USDCBalanceF import { useEnvironment } from 'hooks/useEnvironment' import { MAIN_CONTENT_ID, MainContentContext } from 'pages/MainContentContext' import { AiAttributedTracksPage } from 'pages/ai-attributed-tracks-page' +import { AssetDetailPage } from 'pages/asset-detail-page/AssetDetailPage' import { AudioPage } from 'pages/audio-page/AudioPage' import { ChatPageProvider } from 'pages/chat-page/ChatPageProvider' import { CollectiblesPlaylistPage } from 'pages/collectibles-playlist-page' @@ -129,7 +130,7 @@ const { HISTORY_PAGE, DASHBOARD_PAGE, AUDIO_PAGE, - WALLET_AUDIO_PAGE, + WALLET_SLUG_PAGE, REWARDS_PAGE, UPLOAD_PAGE, UPLOAD_ALBUM_PAGE, @@ -790,19 +791,21 @@ const WebPlayer = (props) => { /> { + return + }} /> diff --git a/packages/web/src/pages/asset-detail-page/AssetDetailContent.tsx b/packages/web/src/pages/asset-detail-page/AssetDetailContent.tsx new file mode 100644 index 00000000000..babdd4ae302 --- /dev/null +++ b/packages/web/src/pages/asset-detail-page/AssetDetailContent.tsx @@ -0,0 +1,33 @@ +import { Flex } from '@audius/harmony' + +import { BalanceSection } from './components/BalanceSection' +import { AssetDetailProps } from './types' + +const LEFT_SECTION_WIDTH = '704px' +const RIGHT_SECTION_WIDTH = '360px' + +export const AssetDetailContent = ({ slug }: AssetDetailProps) => { + return ( + + + + + + + <> + + + ) +} diff --git a/packages/web/src/pages/asset-detail-page/AssetDetailPage.tsx b/packages/web/src/pages/asset-detail-page/AssetDetailPage.tsx new file mode 100644 index 00000000000..29865698153 --- /dev/null +++ b/packages/web/src/pages/asset-detail-page/AssetDetailPage.tsx @@ -0,0 +1,32 @@ +import { Redirect, useParams } from 'react-router-dom' + +import { Header } from 'components/header/desktop/Header' +import Page from 'components/page/Page' + +import { AssetDetailTabs } from './AssetDetailTabs' +import { ACCEPTED_ROUTES } from './constants' +import { AcceptedRouteKey } from './types' + +export const AssetDetailPage = () => { + const { slug } = useParams<{ slug: string }>() + + if (!slug || !(slug in ACCEPTED_ROUTES)) { + return + } + + // At this point, we know slug is a valid key + const typedSlug = slug as AcceptedRouteKey + const routeConfig = ACCEPTED_ROUTES[typedSlug] + const title = routeConfig.title + + const { tabs, body } = AssetDetailTabs({ slug: typedSlug }) + + const header = ( +
+ ) + return ( + + {body} + + ) +} diff --git a/packages/web/src/pages/asset-detail-page/AssetDetailTabs.tsx b/packages/web/src/pages/asset-detail-page/AssetDetailTabs.tsx new file mode 100644 index 00000000000..6411574a0b6 --- /dev/null +++ b/packages/web/src/pages/asset-detail-page/AssetDetailTabs.tsx @@ -0,0 +1,61 @@ +import { useCallback, useState } from 'react' + +import useTabs from 'hooks/useTabs/useTabs' +import { AudioWalletTransactions } from 'pages/audio-page/AudioWalletTransactions' +import Tiers from 'pages/rewards-page/Tiers' + +import { AssetDetailContent } from './AssetDetailContent' +import { AcceptedRouteKey } from './types' + +export enum AssetDetailTabType { + HOME = 'home', + PERKS = 'perks', + TRANSACTIONS = 'transactions' +} + +const messages = { + home: 'Home', + perks: 'Perks', + transactions: 'Transactions' +} + +type AssetDetailTabsProps = { + slug: AcceptedRouteKey +} + +export const AssetDetailTabs = ({ slug }: AssetDetailTabsProps) => { + const [selectedTab, setSelectedTab] = useState(AssetDetailTabType.HOME) + + const tabElements = [ + , + , + + ] + + const handleTabChange = useCallback((from: string, to: string) => { + setSelectedTab(to as AssetDetailTabType) + }, []) + + const { tabs, body } = useTabs({ + isMobile: false, + tabs: [ + { + text: messages.home, + label: AssetDetailTabType.HOME + }, + { + text: messages.perks, + label: AssetDetailTabType.PERKS + }, + { + text: messages.transactions, + label: AssetDetailTabType.TRANSACTIONS + } + ], + selectedTabLabel: selectedTab, + elements: tabElements, + didChangeTabsFrom: handleTabChange + }) + + return { tabs, body } +} diff --git a/packages/web/src/pages/asset-detail-page/components/BalanceSection.tsx b/packages/web/src/pages/asset-detail-page/components/BalanceSection.tsx new file mode 100644 index 00000000000..2bfa9fc3af4 --- /dev/null +++ b/packages/web/src/pages/asset-detail-page/components/BalanceSection.tsx @@ -0,0 +1,111 @@ +import { ComponentType } from 'react' + +import { useAudioBalance } from '@audius/common/api' +import { useFormattedAudioBalance } from '@audius/common/hooks' +import { walletMessages } from '@audius/common/messages' +import { Button, Flex, Paper, Text, useTheme } from '@audius/harmony' + +import { ACCEPTED_ROUTES } from '../constants' +import { AssetDetailProps } from '../types' + +type BalanceStateProps = { + title: string + icon?: ComponentType +} + +const TokenIcon = ({ icon: Icon }: { icon?: ComponentType }) => { + const { cornerRadius } = useTheme() + if (!Icon) return null + return +} + +const ZeroBalanceState = ({ title, icon }: BalanceStateProps) => { + return ( + <> + + + + {title} + + + + + + + + ) +} + +const HasBalanceState = ({ title, icon }: BalanceStateProps) => { + const { motion } = useTheme() + const { + audioBalanceFormatted, + audioDollarValue, + isAudioBalanceLoading, + isAudioPriceLoading + } = useFormattedAudioBalance() + + const isLoading = isAudioBalanceLoading || isAudioPriceLoading + + return ( + <> + + + + + + {audioBalanceFormatted} + + + {title} + + + + {audioDollarValue} + + + + + + + + + + + + ) +} + +export const BalanceSection = ({ slug }: AssetDetailProps) => { + const { totalBalance } = useAudioBalance() + const { title, icon } = ACCEPTED_ROUTES[slug] + + return ( + + + {!totalBalance ? ( + + ) : ( + + )} + + + ) +} diff --git a/packages/web/src/pages/asset-detail-page/constants.ts b/packages/web/src/pages/asset-detail-page/constants.ts new file mode 100644 index 00000000000..08c9810b0fc --- /dev/null +++ b/packages/web/src/pages/asset-detail-page/constants.ts @@ -0,0 +1,11 @@ +import { TOKEN_LISTING_MAP } from '@audius/common/store' + +import { TOKENS } from 'components/buy-sell-modal/constants' + +export const ACCEPTED_ROUTES = { + audio: { + title: '$AUDIO', + symbol: TOKEN_LISTING_MAP.AUDIO.symbol, + icon: TOKENS.AUDIO.icon + } +} diff --git a/packages/web/src/pages/asset-detail-page/types.ts b/packages/web/src/pages/asset-detail-page/types.ts new file mode 100644 index 00000000000..a486b15ec27 --- /dev/null +++ b/packages/web/src/pages/asset-detail-page/types.ts @@ -0,0 +1,7 @@ +import { ACCEPTED_ROUTES } from './constants' + +export type AcceptedRouteKey = keyof typeof ACCEPTED_ROUTES + +export type AssetDetailProps = { + slug: AcceptedRouteKey +} diff --git a/packages/web/src/pages/audio-page/AudioWalletTransactions.tsx b/packages/web/src/pages/audio-page/AudioWalletTransactions.tsx index f2104923aa1..04dd7178acc 100644 --- a/packages/web/src/pages/audio-page/AudioWalletTransactions.tsx +++ b/packages/web/src/pages/audio-page/AudioWalletTransactions.tsx @@ -124,7 +124,7 @@ export const AudioWalletTransactions = () => { const isEmpty = audioTransactions.length === 0 return ( - + {isEmpty && !tableLoading ? ( Date: Mon, 30 Jun 2025 02:30:48 -0500 Subject: [PATCH 02/10] Add 'AssetInfoSection' --- .../asset-detail-page/AssetDetailContent.tsx | 2 + .../components/AssetInfoSection.tsx | 113 ++++++++++++++++++ .../src/pages/asset-detail-page/constants.ts | 20 +++- 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 packages/web/src/pages/asset-detail-page/components/AssetInfoSection.tsx diff --git a/packages/web/src/pages/asset-detail-page/AssetDetailContent.tsx b/packages/web/src/pages/asset-detail-page/AssetDetailContent.tsx index babdd4ae302..0d0a1d325fd 100644 --- a/packages/web/src/pages/asset-detail-page/AssetDetailContent.tsx +++ b/packages/web/src/pages/asset-detail-page/AssetDetailContent.tsx @@ -1,5 +1,6 @@ import { Flex } from '@audius/harmony' +import { AssetInfoSection } from './components/AssetInfoSection' import { BalanceSection } from './components/BalanceSection' import { AssetDetailProps } from './types' @@ -17,6 +18,7 @@ export const AssetDetailContent = ({ slug }: AssetDetailProps) => { gap='m' > + { + const { image: coverPhoto } = useCoverPhoto({ + userId: ACCEPTED_ROUTES[slug].userId, + size: WidthSizes.SIZE_640 + }) + + return ( + + + + {ASSET_INFO_SECTION_MESSAGES.default.createdBy} + + + + + + {ACCEPTED_ROUTES[slug].name} + + + + + + + ) +} + +export const AssetInfoSection = ({ slug }: AssetDetailProps) => { + const { title } = ACCEPTED_ROUTES[slug] + const CTAIcon = ASSET_INFO_SECTION_MESSAGES[slug].ctaIcon + + return ( + + + + + + + {ASSET_INFO_SECTION_MESSAGES.default.whatIs(title)} + + + + + {ASSET_INFO_SECTION_MESSAGES[slug].description.map((text, index) => ( + + {text} + + ))} + + + + + + + + {ASSET_INFO_SECTION_MESSAGES[slug].cta} + + + + + ) +} diff --git a/packages/web/src/pages/asset-detail-page/constants.ts b/packages/web/src/pages/asset-detail-page/constants.ts index 08c9810b0fc..e2dd8b72463 100644 --- a/packages/web/src/pages/asset-detail-page/constants.ts +++ b/packages/web/src/pages/asset-detail-page/constants.ts @@ -1,4 +1,5 @@ import { TOKEN_LISTING_MAP } from '@audius/common/store' +import { IconGift } from '@audius/harmony' import { TOKENS } from 'components/buy-sell-modal/constants' @@ -6,6 +7,23 @@ export const ACCEPTED_ROUTES = { audio: { title: '$AUDIO', symbol: TOKEN_LISTING_MAP.AUDIO.symbol, - icon: TOKENS.AUDIO.icon + icon: TOKENS.AUDIO.icon, + name: TOKENS.AUDIO.name, + userId: 51 + } +} + +export const ASSET_INFO_SECTION_MESSAGES = { + default: { + createdBy: 'Created By', + whatIs: (asset: string) => `What is ${asset}?` + }, + audio: { + description: [ + '$AUDIO is the token that powers Audius. Earn $AUDIO for free by completing in-app challenges and winning weekly trending competitions.', + "You can use $AUDIO for tipping, and as your balance grows, you'll move up rewards tiers that unlock perks like flair, custom themes, and more. Holding $AUDIO also gives you a voice in community votes, helping shape Audius and the future of the music industry." + ], + cta: 'Browse Rewards', + ctaIcon: IconGift } } From 2ff1ae32c15d62cbffe922d47322edca8b0eb83f Mon Sep 17 00:00:00 2001 From: Farid Salau Date: Mon, 30 Jun 2025 02:34:14 -0500 Subject: [PATCH 03/10] Add TokenIcon --- .../components/AssetInfoSection.tsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/packages/web/src/pages/asset-detail-page/components/AssetInfoSection.tsx b/packages/web/src/pages/asset-detail-page/components/AssetInfoSection.tsx index 6f93e107b95..4009e157ba3 100644 --- a/packages/web/src/pages/asset-detail-page/components/AssetInfoSection.tsx +++ b/packages/web/src/pages/asset-detail-page/components/AssetInfoSection.tsx @@ -1,5 +1,5 @@ import { WidthSizes } from '@audius/common/models' -import { Flex, Paper, Text } from '@audius/harmony' +import { Flex, Paper, Text, useTheme } from '@audius/harmony' import UserBadges from 'components/user-badges/UserBadgesV2' import { useCoverPhoto } from 'hooks/useCoverPhoto' @@ -10,8 +10,12 @@ import { AssetDetailProps } from '../types' const BANNER_HEIGHT = 120 const BannerSection = ({ slug }: AssetDetailProps) => { + const { name, userId, icon: TokenIcon } = ACCEPTED_ROUTES[slug] + + const { cornerRadius } = useTheme() + const { image: coverPhoto } = useCoverPhoto({ - userId: ACCEPTED_ROUTES[slug].userId, + userId, size: WidthSizes.SIZE_640 }) @@ -48,11 +52,14 @@ const BannerSection = ({ slug }: AssetDetailProps) => { borderRadius='circle' border='default' > + {TokenIcon ? ( + + ) : null} - {ACCEPTED_ROUTES[slug].name} + {name} - + From 0885ae205d88b23850ee30d40a3348aca034c1d5 Mon Sep 17 00:00:00 2001 From: Farid Salau Date: Mon, 30 Jun 2025 02:55:53 -0500 Subject: [PATCH 04/10] Scaffold asset insights --- .../asset-detail-page/AssetDetailContent.tsx | 3 +- .../components/AssetInsights.tsx | 131 ++++++++++++++++++ 2 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 packages/web/src/pages/asset-detail-page/components/AssetInsights.tsx diff --git a/packages/web/src/pages/asset-detail-page/AssetDetailContent.tsx b/packages/web/src/pages/asset-detail-page/AssetDetailContent.tsx index 0d0a1d325fd..48950c9c364 100644 --- a/packages/web/src/pages/asset-detail-page/AssetDetailContent.tsx +++ b/packages/web/src/pages/asset-detail-page/AssetDetailContent.tsx @@ -1,6 +1,7 @@ import { Flex } from '@audius/harmony' import { AssetInfoSection } from './components/AssetInfoSection' +import { AssetInsights } from './components/AssetInsights' import { BalanceSection } from './components/BalanceSection' import { AssetDetailProps } from './types' @@ -28,7 +29,7 @@ export const AssetDetailContent = ({ slug }: AssetDetailProps) => { direction='column' gap='m' > - <> + ) diff --git a/packages/web/src/pages/asset-detail-page/components/AssetInsights.tsx b/packages/web/src/pages/asset-detail-page/components/AssetInsights.tsx new file mode 100644 index 00000000000..af009309c46 --- /dev/null +++ b/packages/web/src/pages/asset-detail-page/components/AssetInsights.tsx @@ -0,0 +1,131 @@ +import { Flex, IconCaretDown, IconCaretUp, Paper, Text } from '@audius/harmony' + +import { AssetDetailProps } from '../types' + +const messages = { + title: 'Insights', + pricePerCoin: 'Price per coin', + holdersOnAudius: 'Holders on Audius', + uniqueHolders: 'Unique Holders', + volume24hr: 'Volume (24hr)', + totalTransfers: 'Total Transfers' +} + +type MetricData = { + value: string + label: string + change?: { + value: string + isPositive: boolean + } +} + +const MOCK_METRICS: MetricData[] = [ + { + value: '$0.082', + label: messages.pricePerCoin, + change: { + value: '0.005', + isPositive: false + } + }, + { + value: '12.6K', + label: messages.holdersOnAudius, + change: { + value: '0.5%', + isPositive: true + } + }, + { + value: '37.7K', + label: messages.uniqueHolders, + change: { + value: '0.01%', + isPositive: true + } + }, + { + value: '5.9M', + label: messages.volume24hr, + change: { + value: '1.68%', + isPositive: true + } + }, + { + value: '514K', + label: messages.totalTransfers + } +] + +const MetricRow = ({ metric }: { metric: MetricData }) => { + const changeColor = metric.change?.isPositive ? 'premium' : 'danger' + + return ( + + + + {metric.value} + + + {metric.label} + + + + {metric.change ? ( + + + {metric.change.value} + + + {metric.change.isPositive ? ( + + ) : ( + + )} + + + ) : null} + + ) +} + +export const AssetInsights = ({ slug }: AssetDetailProps) => { + return ( + + + + {messages.title} + + + + {MOCK_METRICS.map((metric, index) => ( + + ))} + + ) +} From 054274dc6ea50c54d2fdec167d7d298a25ec9991 Mon Sep 17 00:00:00 2001 From: Farid Salau Date: Wed, 16 Jul 2025 09:15:45 -0500 Subject: [PATCH 05/10] Use proper import --- .../src/pages/asset-detail-page/components/AssetInfoSection.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/web/src/pages/asset-detail-page/components/AssetInfoSection.tsx b/packages/web/src/pages/asset-detail-page/components/AssetInfoSection.tsx index 4009e157ba3..afc520139d8 100644 --- a/packages/web/src/pages/asset-detail-page/components/AssetInfoSection.tsx +++ b/packages/web/src/pages/asset-detail-page/components/AssetInfoSection.tsx @@ -1,7 +1,7 @@ import { WidthSizes } from '@audius/common/models' import { Flex, Paper, Text, useTheme } from '@audius/harmony' -import UserBadges from 'components/user-badges/UserBadgesV2' +import UserBadges from 'components/user-badges/UserBadges' import { useCoverPhoto } from 'hooks/useCoverPhoto' import { ACCEPTED_ROUTES, ASSET_INFO_SECTION_MESSAGES } from '../constants' From 4d77ca559f0aa9e2e9e8a0185c734798ef38d82b Mon Sep 17 00:00:00 2001 From: Farid Salau Date: Wed, 16 Jul 2025 09:36:20 -0500 Subject: [PATCH 06/10] Add modals --- .../asset-detail-page/AssetDetailPage.tsx | 2 + .../asset-detail-page/AssetDetailTabs.tsx | 7 +- .../components/BalanceSection.tsx | 91 +++++++++++++++++-- 3 files changed, 85 insertions(+), 15 deletions(-) diff --git a/packages/web/src/pages/asset-detail-page/AssetDetailPage.tsx b/packages/web/src/pages/asset-detail-page/AssetDetailPage.tsx index 29865698153..bad35763be8 100644 --- a/packages/web/src/pages/asset-detail-page/AssetDetailPage.tsx +++ b/packages/web/src/pages/asset-detail-page/AssetDetailPage.tsx @@ -2,6 +2,7 @@ import { Redirect, useParams } from 'react-router-dom' import { Header } from 'components/header/desktop/Header' import Page from 'components/page/Page' +import WalletModal from 'pages/audio-page/WalletModal' import { AssetDetailTabs } from './AssetDetailTabs' import { ACCEPTED_ROUTES } from './constants' @@ -26,6 +27,7 @@ export const AssetDetailPage = () => { ) return ( + {body} ) diff --git a/packages/web/src/pages/asset-detail-page/AssetDetailTabs.tsx b/packages/web/src/pages/asset-detail-page/AssetDetailTabs.tsx index 6411574a0b6..e467c356ecc 100644 --- a/packages/web/src/pages/asset-detail-page/AssetDetailTabs.tsx +++ b/packages/web/src/pages/asset-detail-page/AssetDetailTabs.tsx @@ -2,7 +2,6 @@ import { useCallback, useState } from 'react' import useTabs from 'hooks/useTabs/useTabs' import { AudioWalletTransactions } from 'pages/audio-page/AudioWalletTransactions' -import Tiers from 'pages/rewards-page/Tiers' import { AssetDetailContent } from './AssetDetailContent' import { AcceptedRouteKey } from './types' @@ -28,7 +27,6 @@ export const AssetDetailTabs = ({ slug }: AssetDetailTabsProps) => { const tabElements = [ , - , ] @@ -43,10 +41,7 @@ export const AssetDetailTabs = ({ slug }: AssetDetailTabsProps) => { text: messages.home, label: AssetDetailTabType.HOME }, - { - text: messages.perks, - label: AssetDetailTabType.PERKS - }, + { text: messages.transactions, label: AssetDetailTabType.TRANSACTIONS diff --git a/packages/web/src/pages/asset-detail-page/components/BalanceSection.tsx b/packages/web/src/pages/asset-detail-page/components/BalanceSection.tsx index 2bfa9fc3af4..ea77d7a9c96 100644 --- a/packages/web/src/pages/asset-detail-page/components/BalanceSection.tsx +++ b/packages/web/src/pages/asset-detail-page/components/BalanceSection.tsx @@ -3,7 +3,16 @@ import { ComponentType } from 'react' import { useAudioBalance } from '@audius/common/api' import { useFormattedAudioBalance } from '@audius/common/hooks' import { walletMessages } from '@audius/common/messages' +import { + useBuySellModal, + useAddCashModal, + tokenDashboardPageActions +} from '@audius/common/store' import { Button, Flex, Paper, Text, useTheme } from '@audius/harmony' +import { useDispatch } from 'react-redux' + +import { useModalState } from 'common/hooks/useModalState' +import { useIsMobile } from 'hooks/useIsMobile' import { ACCEPTED_ROUTES } from '../constants' import { AssetDetailProps } from '../types' @@ -11,6 +20,9 @@ import { AssetDetailProps } from '../types' type BalanceStateProps = { title: string icon?: ComponentType + onBuy?: () => void + onReceive?: () => void + onSend?: () => void } const TokenIcon = ({ icon: Icon }: { icon?: ComponentType }) => { @@ -19,7 +31,12 @@ const TokenIcon = ({ icon: Icon }: { icon?: ComponentType }) => { return } -const ZeroBalanceState = ({ title, icon }: BalanceStateProps) => { +const ZeroBalanceState = ({ + title, + icon, + onBuy, + onReceive +}: BalanceStateProps) => { return ( <> @@ -29,10 +46,10 @@ const ZeroBalanceState = ({ title, icon }: BalanceStateProps) => { - - @@ -40,7 +57,13 @@ const ZeroBalanceState = ({ title, icon }: BalanceStateProps) => { ) } -const HasBalanceState = ({ title, icon }: BalanceStateProps) => { +const HasBalanceState = ({ + title, + icon, + onBuy, + onSend, + onReceive +}: BalanceStateProps) => { const { motion } = useTheme() const { audioBalanceFormatted, @@ -77,14 +100,14 @@ const HasBalanceState = ({ title, icon }: BalanceStateProps) => { - - - @@ -97,13 +120,63 @@ export const BalanceSection = ({ slug }: AssetDetailProps) => { const { totalBalance } = useAudioBalance() const { title, icon } = ACCEPTED_ROUTES[slug] + // Modal hooks + const { onOpen: openBuySellModal } = useBuySellModal() + const { onOpen: openAddCashModal } = useAddCashModal() + const [, openTransferDrawer] = useModalState('TransferAudioMobileWarning') + + // Redux and mobile detection + const dispatch = useDispatch() + const isMobile = useIsMobile() + + // Action destructuring + const { pressReceive, pressSend } = tokenDashboardPageActions + + // Handler functions + const handleBuySell = () => { + // Has balance - show buy/sell modal + openBuySellModal() + } + + const handleAddCash = () => { + // No balance - show add cash modal (uses Coinflow) + openAddCashModal() + } + + const handleReceive = () => { + if (isMobile) { + openTransferDrawer(true) + } else { + dispatch(pressReceive()) + } + } + + const handleSend = () => { + if (isMobile) { + openTransferDrawer(true) + } else { + dispatch(pressSend()) + } + } + return ( {!totalBalance ? ( - + ) : ( - + )} From df25d3c304198b7e5aa683bf7f210e2123e2aabc Mon Sep 17 00:00:00 2001 From: Farid Salau Date: Wed, 16 Jul 2025 09:37:20 -0500 Subject: [PATCH 07/10] Use hex --- .../pages/asset-detail-page/components/BalanceSection.tsx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/web/src/pages/asset-detail-page/components/BalanceSection.tsx b/packages/web/src/pages/asset-detail-page/components/BalanceSection.tsx index ea77d7a9c96..f9235b116cc 100644 --- a/packages/web/src/pages/asset-detail-page/components/BalanceSection.tsx +++ b/packages/web/src/pages/asset-detail-page/components/BalanceSection.tsx @@ -4,9 +4,9 @@ import { useAudioBalance } from '@audius/common/api' import { useFormattedAudioBalance } from '@audius/common/hooks' import { walletMessages } from '@audius/common/messages' import { - useBuySellModal, + tokenDashboardPageActions, useAddCashModal, - tokenDashboardPageActions + useBuySellModal } from '@audius/common/store' import { Button, Flex, Paper, Text, useTheme } from '@audius/harmony' import { useDispatch } from 'react-redux' @@ -26,9 +26,8 @@ type BalanceStateProps = { } const TokenIcon = ({ icon: Icon }: { icon?: ComponentType }) => { - const { cornerRadius } = useTheme() if (!Icon) return null - return + return } const ZeroBalanceState = ({ From 10225eb4ad21129e8de4c11827466fecead61c33 Mon Sep 17 00:00:00 2001 From: Farid Salau Date: Wed, 16 Jul 2025 09:45:08 -0500 Subject: [PATCH 08/10] Address comment --- packages/common/src/utils/route.ts | 4 ++-- packages/web/src/app/web-player/WebPlayer.jsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/common/src/utils/route.ts b/packages/common/src/utils/route.ts index ad800fcf4c3..d941b46af36 100644 --- a/packages/common/src/utils/route.ts +++ b/packages/common/src/utils/route.ts @@ -69,7 +69,7 @@ export const PURCHASES_PAGE = '/payments/purchases' export const SALES_PAGE = '/payments/sales' export const WITHDRAWALS_PAGE = '/payments/withdrawals' export const TRANSACTION_HISTORY_PAGE = '/wallet/transaction-history' -export const WALLET_SLUG_PAGE = '/wallet/:slug' +export const ASSET_DETAIL_PAGE = '/wallet/:slug' export const WALLET_PAGE = '/wallet' export const PRIVATE_KEY_EXPORTER_SETTINGS_PAGE = '/settings/export-private-key' export const DEV_TOOLS_PAGE = '/dev-tools' @@ -273,7 +273,7 @@ export const orderedRoutes = [ PAYMENTS_PAGE, AUDIO_PAGE, WALLET_AUDIO_PAGE, - WALLET_SLUG_PAGE, + ASSET_DETAIL_PAGE, WALLET_PAGE, REWARDS_PAGE, SETTINGS_PAGE, diff --git a/packages/web/src/app/web-player/WebPlayer.jsx b/packages/web/src/app/web-player/WebPlayer.jsx index 567db4fb276..2af68afd625 100644 --- a/packages/web/src/app/web-player/WebPlayer.jsx +++ b/packages/web/src/app/web-player/WebPlayer.jsx @@ -118,7 +118,7 @@ const { HISTORY_PAGE, DASHBOARD_PAGE, AUDIO_PAGE, - WALLET_SLUG_PAGE, + ASSET_DETAIL_PAGE, REWARDS_PAGE, UPLOAD_PAGE, UPLOAD_ALBUM_PAGE, @@ -693,7 +693,7 @@ const WebPlayer = (props) => { /> { return From 3befefdcd3b4cbac83edb46ac5c7970bf27ee81e Mon Sep 17 00:00:00 2001 From: Farid Salau Date: Thu, 17 Jul 2025 09:48:57 -0500 Subject: [PATCH 09/10] Address comments --- .../asset-detail-page/AssetDetailContent.tsx | 26 +++++-------------- .../asset-detail-page/AssetDetailPage.tsx | 17 ++++++++++-- .../asset-detail-page/AssetDetailTabs.tsx | 17 +++++------- .../components/AssetInfoSection.tsx | 18 ++++++------- .../components/AssetInsights.tsx | 6 ++--- .../components/BalanceSection.tsx | 4 +-- .../web/src/pages/asset-detail-page/types.ts | 2 +- 7 files changed, 43 insertions(+), 47 deletions(-) diff --git a/packages/web/src/pages/asset-detail-page/AssetDetailContent.tsx b/packages/web/src/pages/asset-detail-page/AssetDetailContent.tsx index 48950c9c364..88c2cb60326 100644 --- a/packages/web/src/pages/asset-detail-page/AssetDetailContent.tsx +++ b/packages/web/src/pages/asset-detail-page/AssetDetailContent.tsx @@ -8,28 +8,16 @@ import { AssetDetailProps } from './types' const LEFT_SECTION_WIDTH = '704px' const RIGHT_SECTION_WIDTH = '360px' -export const AssetDetailContent = ({ slug }: AssetDetailProps) => { +export const AssetDetailContent = ({ mint }: AssetDetailProps) => { return ( - - - - + + + + - - + + ) diff --git a/packages/web/src/pages/asset-detail-page/AssetDetailPage.tsx b/packages/web/src/pages/asset-detail-page/AssetDetailPage.tsx index bad35763be8..15b6f210ba5 100644 --- a/packages/web/src/pages/asset-detail-page/AssetDetailPage.tsx +++ b/packages/web/src/pages/asset-detail-page/AssetDetailPage.tsx @@ -4,7 +4,7 @@ import { Header } from 'components/header/desktop/Header' import Page from 'components/page/Page' import WalletModal from 'pages/audio-page/WalletModal' -import { AssetDetailTabs } from './AssetDetailTabs' +import { useAssetDetailTabs } from './AssetDetailTabs' import { ACCEPTED_ROUTES } from './constants' import { AcceptedRouteKey } from './types' @@ -20,11 +20,24 @@ export const AssetDetailPage = () => { const routeConfig = ACCEPTED_ROUTES[typedSlug] const title = routeConfig.title - const { tabs, body } = AssetDetailTabs({ slug: typedSlug }) + return +} + +type AssetDetailPageContentProps = { + mint: AcceptedRouteKey + title: string +} + +const AssetDetailPageContent = ({ + mint, + title +}: AssetDetailPageContentProps) => { + const { tabs, body } = useAssetDetailTabs({ mint }) const header = (
) + return ( diff --git a/packages/web/src/pages/asset-detail-page/AssetDetailTabs.tsx b/packages/web/src/pages/asset-detail-page/AssetDetailTabs.tsx index e467c356ecc..8b372091fed 100644 --- a/packages/web/src/pages/asset-detail-page/AssetDetailTabs.tsx +++ b/packages/web/src/pages/asset-detail-page/AssetDetailTabs.tsx @@ -8,40 +8,37 @@ import { AcceptedRouteKey } from './types' export enum AssetDetailTabType { HOME = 'home', - PERKS = 'perks', TRANSACTIONS = 'transactions' } const messages = { home: 'Home', - perks: 'Perks', transactions: 'Transactions' } -type AssetDetailTabsProps = { - slug: AcceptedRouteKey +type UseAssetDetailTabsProps = { + mint: AcceptedRouteKey } -export const AssetDetailTabs = ({ slug }: AssetDetailTabsProps) => { +export const useAssetDetailTabs = ({ mint }: UseAssetDetailTabsProps) => { const [selectedTab, setSelectedTab] = useState(AssetDetailTabType.HOME) const tabElements = [ - , + , ] - const handleTabChange = useCallback((from: string, to: string) => { + const handleTabChange = useCallback((_from: string, to: string) => { setSelectedTab(to as AssetDetailTabType) }, []) - const { tabs, body } = useTabs({ + return useTabs({ isMobile: false, tabs: [ { text: messages.home, label: AssetDetailTabType.HOME }, - { text: messages.transactions, label: AssetDetailTabType.TRANSACTIONS @@ -51,6 +48,4 @@ export const AssetDetailTabs = ({ slug }: AssetDetailTabsProps) => { elements: tabElements, didChangeTabsFrom: handleTabChange }) - - return { tabs, body } } diff --git a/packages/web/src/pages/asset-detail-page/components/AssetInfoSection.tsx b/packages/web/src/pages/asset-detail-page/components/AssetInfoSection.tsx index afc520139d8..8cf9ea59a2d 100644 --- a/packages/web/src/pages/asset-detail-page/components/AssetInfoSection.tsx +++ b/packages/web/src/pages/asset-detail-page/components/AssetInfoSection.tsx @@ -9,8 +9,8 @@ import { AssetDetailProps } from '../types' const BANNER_HEIGHT = 120 -const BannerSection = ({ slug }: AssetDetailProps) => { - const { name, userId, icon: TokenIcon } = ACCEPTED_ROUTES[slug] +const BannerSection = ({ mint }: AssetDetailProps) => { + const { name, userId, icon: TokenIcon } = ACCEPTED_ROUTES[mint] const { cornerRadius } = useTheme() @@ -67,9 +67,9 @@ const BannerSection = ({ slug }: AssetDetailProps) => { ) } -export const AssetInfoSection = ({ slug }: AssetDetailProps) => { - const { title } = ACCEPTED_ROUTES[slug] - const CTAIcon = ASSET_INFO_SECTION_MESSAGES[slug].ctaIcon +export const AssetInfoSection = ({ mint }: AssetDetailProps) => { + const { title } = ACCEPTED_ROUTES[mint] + const CTAIcon = ASSET_INFO_SECTION_MESSAGES[mint].ctaIcon return ( { direction='column' alignItems='flex-start' > - + { - {ASSET_INFO_SECTION_MESSAGES[slug].description.map((text, index) => ( + {ASSET_INFO_SECTION_MESSAGES[mint].description.map((text, index) => ( { - {ASSET_INFO_SECTION_MESSAGES[slug].cta} + {ASSET_INFO_SECTION_MESSAGES[mint].cta} diff --git a/packages/web/src/pages/asset-detail-page/components/AssetInsights.tsx b/packages/web/src/pages/asset-detail-page/components/AssetInsights.tsx index af009309c46..14abcbf7ea8 100644 --- a/packages/web/src/pages/asset-detail-page/components/AssetInsights.tsx +++ b/packages/web/src/pages/asset-detail-page/components/AssetInsights.tsx @@ -108,7 +108,7 @@ const MetricRow = ({ metric }: { metric: MetricData }) => { ) } -export const AssetInsights = ({ slug }: AssetDetailProps) => { +export const AssetInsights = ({ mint: _mint }: AssetDetailProps) => { return ( { - {MOCK_METRICS.map((metric, index) => ( - + {MOCK_METRICS.map((metric) => ( + ))} ) diff --git a/packages/web/src/pages/asset-detail-page/components/BalanceSection.tsx b/packages/web/src/pages/asset-detail-page/components/BalanceSection.tsx index f9235b116cc..f1ad64b17d9 100644 --- a/packages/web/src/pages/asset-detail-page/components/BalanceSection.tsx +++ b/packages/web/src/pages/asset-detail-page/components/BalanceSection.tsx @@ -115,9 +115,9 @@ const HasBalanceState = ({ ) } -export const BalanceSection = ({ slug }: AssetDetailProps) => { +export const BalanceSection = ({ mint }: AssetDetailProps) => { const { totalBalance } = useAudioBalance() - const { title, icon } = ACCEPTED_ROUTES[slug] + const { title, icon } = ACCEPTED_ROUTES[mint] // Modal hooks const { onOpen: openBuySellModal } = useBuySellModal() diff --git a/packages/web/src/pages/asset-detail-page/types.ts b/packages/web/src/pages/asset-detail-page/types.ts index a486b15ec27..91edbbc4435 100644 --- a/packages/web/src/pages/asset-detail-page/types.ts +++ b/packages/web/src/pages/asset-detail-page/types.ts @@ -3,5 +3,5 @@ import { ACCEPTED_ROUTES } from './constants' export type AcceptedRouteKey = keyof typeof ACCEPTED_ROUTES export type AssetDetailProps = { - slug: AcceptedRouteKey + mint: AcceptedRouteKey } From aef2171580fd3a99b1b2a1e14904ab3c0a2ed621 Mon Sep 17 00:00:00 2001 From: Farid Salau Date: Thu, 17 Jul 2025 10:47:05 -0500 Subject: [PATCH 10/10] Add feature flag --- packages/web/src/app/web-player/WebPlayer.jsx | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/web/src/app/web-player/WebPlayer.jsx b/packages/web/src/app/web-player/WebPlayer.jsx index 2af68afd625..e89ac767083 100644 --- a/packages/web/src/app/web-player/WebPlayer.jsx +++ b/packages/web/src/app/web-player/WebPlayer.jsx @@ -213,8 +213,14 @@ const validSearchCategories = [ initializeSentry() const WebPlayer = (props) => { - const { isProduction, history, location, mainContentRef, setMainContentRef } = - props + const { + isProduction, + history, + location, + mainContentRef, + setMainContentRef, + isArtistCoinsEnabled + } = props const dispatch = useDispatch() @@ -696,7 +702,11 @@ const WebPlayer = (props) => { path={ASSET_DETAIL_PAGE} isMobile={isMobile} render={(props) => { - return + return isArtistCoinsEnabled ? ( + + ) : ( + + ) }} /> { const { isEnabled: isSearchExploreEnabled } = useFeatureFlag( FeatureFlags.SEARCH_EXPLORE ) + const { isEnabled: isArtistCoinsEnabled } = useFeatureFlag( + FeatureFlags.ARTIST_COINS + ) const { isProduction } = useEnvironment() return ( )