diff --git a/packages/common/src/utils/wallet.ts b/packages/common/src/utils/wallet.ts index fc691972d9..57c11795b9 100644 --- a/packages/common/src/utils/wallet.ts +++ b/packages/common/src/utils/wallet.ts @@ -1,7 +1,14 @@ import BN from 'bn.js' import JSBI from 'jsbi' -import { BNAudio, BNWei, StringAudio, StringWei } from 'models/Wallet' +import { + BNAudio, + BNUSDC, + BNWei, + StringAudio, + StringUSDC, + StringWei +} from 'models/Wallet' import { WEI, trimRightZeros, @@ -12,6 +19,7 @@ import { } from 'utils/formatUtil' import { Nullable } from 'utils/typeUtils' +/** AUDIO utils */ const WEI_DECIMALS = 18 // 18 decimals on ETH AUDIO const SPL_DECIMALS = 8 // 8 decimals on SPL AUDIO @@ -102,14 +110,6 @@ export const formatWei = ( return formatNumberCommas(trimmed) as StringAudio } -export const shortenSPLAddress = (addr: string) => { - return `${addr.substring(0, 4)}...${addr.substr(addr.length - 5)}` -} - -export const shortenEthAddress = (addr: string) => { - return `0x${addr.substring(2, 4)}...${addr.substr(addr.length - 5)}` -} - export const convertJSBIToAmountObject = (amount: JSBI, decimals: number) => { const divisor = JSBI.BigInt(10 ** decimals) const quotient = JSBI.divide(amount, divisor) @@ -134,3 +134,39 @@ export const convertWeiToWAudio = (amount: BN) => { const decimals = WEI_DECIMALS - SPL_DECIMALS return amount.div(new BN('1'.padEnd(decimals + 1, '0'))) } + +/** USDC Utils */ +export const BN_USDC_WEI = new BN('1000000') +export const BN_USDC_CENT_WEI = new BN('10000') +const BN_USDC_WEI_ROUNDING_FRACTION = new BN('9999') + +/** Round a USDC value as a BN up to the nearest cent and return as a BN */ +export const ceilingBNUSDCToNearestCent = (value: BNUSDC): BNUSDC => { + return value + .add(BN_USDC_WEI_ROUNDING_FRACTION) + .div(BN_USDC_CENT_WEI) + .mul(BN_USDC_CENT_WEI) as BNUSDC +} + +/** Formats a USDC wei string (full precision) to a fixed string suitable for +display as a dollar amount. Note: will lose precision by rounding _up_ to nearest cent */ +export const formatUSDCWeiToUSDString = (amount: StringUSDC, precision = 2) => { + // Since we only need two digits of precision, we will multiply up by 1000 + // with BN, divide by $1 Wei, ceiling up to the nearest cent, + // and then convert to JS number and divide back down before formatting to + // two decimal places. + const cents = + ceilingBNUSDCToNearestCent(new BN(amount) as BNUSDC) + .div(BN_USDC_CENT_WEI) + .toNumber() / 100 + return formatNumberCommas(cents.toFixed(precision)) +} + +/** General Wallet Utils */ +export const shortenSPLAddress = (addr: string) => { + return `${addr.substring(0, 4)}...${addr.substr(addr.length - 5)}` +} + +export const shortenEthAddress = (addr: string) => { + return `0x${addr.substring(2, 4)}...${addr.substr(addr.length - 5)}` +} diff --git a/packages/stems/src/assets/icons/iconCart.svg b/packages/stems/src/assets/icons/iconCart.svg new file mode 100644 index 0000000000..79504308b1 --- /dev/null +++ b/packages/stems/src/assets/icons/iconCart.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/stems/src/assets/styles/colors.css b/packages/stems/src/assets/styles/colors.css index 8384c6cd7f..5b4aaf3402 100644 --- a/packages/stems/src/assets/styles/colors.css +++ b/packages/stems/src/assets/styles/colors.css @@ -62,6 +62,8 @@ root { --accent-blue: #1ba1f1; --accent-purple: #8e51cf; + --special-light-green: #13c65a; + --primary-gradient: linear-gradient(135deg, #ee00ed 0%, #c514e0 100%); --secondary-gradient: linear-gradient(315deg, #6b0fb3 0%, #7e1bcc 100%); --page-header-gradient-color-1: #5b23e1; diff --git a/packages/stems/src/components/Icons/index.ts b/packages/stems/src/components/Icons/index.ts index bb687848af..7ca2b1af98 100644 --- a/packages/stems/src/components/Icons/index.ts +++ b/packages/stems/src/components/Icons/index.ts @@ -16,6 +16,7 @@ export { ReactComponent as IconBigSearch } from '../../assets/icons/iconBigSearc export { ReactComponent as IconCalendar } from '../../assets/icons/iconCalendar.svg' export { ReactComponent as IconCamera } from '../../assets/icons/iconCamera.svg' export { ReactComponent as IconCareers } from '../../assets/icons/iconCareers.svg' +export { ReactComponent as IconCart } from '../../assets/icons/iconCart.svg' export { ReactComponent as IconCaretDown } from '../../assets/icons/iconCaretDown.svg' export { ReactComponent as IconCaretRight } from '../../assets/icons/iconCaretRight.svg' export { ReactComponent as IconCheck } from '../../assets/icons/iconCheck.svg' diff --git a/packages/stems/src/styles/colors.ts b/packages/stems/src/styles/colors.ts index 5b83514f1d..d64553fa09 100644 --- a/packages/stems/src/styles/colors.ts +++ b/packages/stems/src/styles/colors.ts @@ -37,3 +37,4 @@ export type ColorValue = | 'accentOrangeLight1' | 'accentPurple' | 'accentBlue' + | 'specialLightGreen' diff --git a/packages/web/src/assets/styles/colors.css b/packages/web/src/assets/styles/colors.css index 92b0753938..beed8b1d86 100644 --- a/packages/web/src/assets/styles/colors.css +++ b/packages/web/src/assets/styles/colors.css @@ -59,6 +59,8 @@ --accent-purple: #8e51cf; + --special-light-green: #13c65a; + --ai-primary: #1fd187; --ai-secondary: #0fc578; diff --git a/packages/web/src/components/dog-ear/DogEar.module.css b/packages/web/src/components/dog-ear/DogEar.module.css index 007c4eab41..6679d6818e 100644 --- a/packages/web/src/components/dog-ear/DogEar.module.css +++ b/packages/web/src/components/dog-ear/DogEar.module.css @@ -20,6 +20,10 @@ background: var(--accent-blue); } +.purchase { + background: var(--special-light-green); +} + .star { background: linear-gradient(314.61deg, #7e1bcc 0%, #a22feb 100%); } diff --git a/packages/web/src/components/dog-ear/DogEar.tsx b/packages/web/src/components/dog-ear/DogEar.tsx index d4415b6e23..684295c966 100644 --- a/packages/web/src/components/dog-ear/DogEar.tsx +++ b/packages/web/src/components/dog-ear/DogEar.tsx @@ -1,5 +1,10 @@ import { DogEarType } from '@audius/common' -import { IconCollectible, IconLock, IconSpecialAccess } from '@audius/stems' +import { + IconCart, + IconCollectible, + IconLock, + IconSpecialAccess +} from '@audius/stems' import cn from 'classnames' import { ReactComponent as IconHidden } from 'assets/img/iconHidden.svg' @@ -29,6 +34,8 @@ export const DogEar = (props: DogEarProps) => { return case DogEarType.COLLECTIBLE_GATED: return + case DogEarType.USDC_PURCHASE: + return case DogEarType.SPECIAL_ACCESS: return } @@ -48,6 +55,7 @@ export const DogEar = (props: DogEarProps) => { DogEarType.SPECIAL_ACCESS, DogEarType.LOCKED ].includes(type), + [styles.purchase]: type === DogEarType.USDC_PURCHASE, [styles.star]: type === DogEarType.STAR, [styles.hidden]: type === DogEarType.HIDDEN })} diff --git a/packages/web/src/components/track/LockedStatusBadge.module.css b/packages/web/src/components/track/LockedStatusBadge.module.css index 72ef278a0f..dbebae4248 100644 --- a/packages/web/src/components/track/LockedStatusBadge.module.css +++ b/packages/web/src/components/track/LockedStatusBadge.module.css @@ -6,10 +6,15 @@ height: var(--unit-4); padding: var(--unit-half) var(--unit-2); } -.unlocked { + +.unlocked.gated { background-color: var(--accent-blue); } +.unlocked.premium { + background-color: var(--special-light-green); +} + .icon { width: 14px; height: 14px; diff --git a/packages/web/src/components/track/LockedStatusBadge.tsx b/packages/web/src/components/track/LockedStatusBadge.tsx index 497503511c..364100d377 100644 --- a/packages/web/src/components/track/LockedStatusBadge.tsx +++ b/packages/web/src/components/track/LockedStatusBadge.tsx @@ -5,14 +5,22 @@ import styles from './LockedStatusBadge.module.css' export type LockedStatusBadgeProps = { locked: boolean + variant: 'premium' | 'gated' } /** Renders a small badge with locked or unlocked icon */ -export const LockedStatusBadge = ({ locked }: LockedStatusBadgeProps) => { +export const LockedStatusBadge = ({ + locked, + variant +}: LockedStatusBadgeProps) => { const LockComponent = locked ? IconLock : IconLockUnlocked return (
diff --git a/packages/web/src/components/track/PremiumConditionsPill.module.css b/packages/web/src/components/track/PremiumConditionsPill.module.css new file mode 100644 index 0000000000..2653cb1e62 --- /dev/null +++ b/packages/web/src/components/track/PremiumConditionsPill.module.css @@ -0,0 +1,38 @@ +.container { + align-items: center; + background-color: var(--accent-blue); + border-radius: var(--unit-1); + color: var(--static-white); + display: flex; + gap: var(--unit-1); + justify-content: center; + height: var(--unit-6); + min-width: var(--unit-16); + padding: var(--unit-2) var(--unit-3); +} + +.container svg { + width: var(--unit-4); + height: var(--unit-4); +} + +.container path { + fill: var(--static-white); +} + +.gatedContent { + background-color: var(--accent-blue); +} + +.premiumContent { + background-color: var(--special-light-green); +} + +.spinner { + height: var(--unit-4); + width: var(--unit-4); +} + +.spinner g path { + stroke: var(--static-white); +} diff --git a/packages/web/src/components/track/PremiumConditionsPill.tsx b/packages/web/src/components/track/PremiumConditionsPill.tsx new file mode 100644 index 0000000000..e6ad37e6a3 --- /dev/null +++ b/packages/web/src/components/track/PremiumConditionsPill.tsx @@ -0,0 +1,50 @@ +import { + PremiumConditions, + formatUSDCWeiToUSDString, + isPremiumContentUSDCPurchaseGated +} from '@audius/common' +import { IconLock } from '@audius/stems' +import cn from 'classnames' + +import LoadingSpinner from 'components/loading-spinner/LoadingSpinner' + +import styles from './PremiumConditionsPill.module.css' + +const messages = { + unlocking: 'Unlocking', + locked: 'Locked' +} + +export const PremiumConditionsPill = ({ + premiumConditions, + unlocking +}: { + premiumConditions: PremiumConditions + unlocking: boolean +}) => { + const isPurchase = isPremiumContentUSDCPurchaseGated(premiumConditions) + const icon = unlocking ? ( + + ) : isPurchase ? null : ( + + ) + + let message = null + if (unlocking) { + // Show only spinner when unlocking a purchase + message = isPurchase ? null : messages.unlocking + } else { + message = isPurchase + ? `$${formatUSDCWeiToUSDString(premiumConditions.usdc_purchase.price)}` + : messages.locked + } + + const colorStyle = isPurchase ? styles.premiumContent : styles.gatedContent + + return ( +
+ {icon} + {message} +
+ ) +} diff --git a/packages/web/src/components/track/PremiumContentLabel.module.css b/packages/web/src/components/track/PremiumContentLabel.module.css index 4dacffcd69..45c43a2fac 100644 --- a/packages/web/src/components/track/PremiumContentLabel.module.css +++ b/packages/web/src/components/track/PremiumContentLabel.module.css @@ -21,3 +21,11 @@ .gatedContent .icon path { fill: var(--accent-blue); } + +.premiumContent { + color: var(--special-light-green); +} + +.premiumContent .icon path { + fill: var(--special-light-green); +} diff --git a/packages/web/src/components/track/PremiumContentLabel.tsx b/packages/web/src/components/track/PremiumContentLabel.tsx index 805bc8dd0a..7cd9f292dd 100644 --- a/packages/web/src/components/track/PremiumContentLabel.tsx +++ b/packages/web/src/components/track/PremiumContentLabel.tsx @@ -1,16 +1,18 @@ import { PremiumConditions, Nullable, - isPremiumContentCollectibleGated + isPremiumContentCollectibleGated, + isPremiumContentUSDCPurchaseGated } from '@audius/common' -import { IconCollectible, IconSpecialAccess } from '@audius/stems' +import { IconCart, IconCollectible, IconSpecialAccess } from '@audius/stems' import cn from 'classnames' import styles from './PremiumContentLabel.module.css' const messages = { collectibleGated: 'Collectible Gated', - specialAccess: 'Special Access' + specialAccess: 'Special Access', + premium: 'Premium' } /** Renders a label indicating a premium content type. If the user does @@ -28,12 +30,17 @@ export const PremiumContentLabel = ({ const showColor = isOwner || !doesUserHaveAccess let message = messages.specialAccess let IconComponent = IconSpecialAccess - const colorStyle = styles.gatedContent + let colorStyle = styles.gatedContent if (isPremiumContentCollectibleGated(premiumConditions)) { message = messages.collectibleGated IconComponent = IconCollectible } + if (isPremiumContentUSDCPurchaseGated(premiumConditions)) { + message = messages.premium + IconComponent = IconCart + colorStyle = styles.premiumContent + } return (
diff --git a/packages/web/src/components/track/PremiumTrackSection.tsx b/packages/web/src/components/track/PremiumTrackSection.tsx index bed838862f..c59ea35a6e 100644 --- a/packages/web/src/components/track/PremiumTrackSection.tsx +++ b/packages/web/src/components/track/PremiumTrackSection.tsx @@ -15,7 +15,8 @@ import { removeNullable, isPremiumContentFollowGated, isPremiumContentCollectibleGated, - isPremiumContentTipGated + isPremiumContentTipGated, + isPremiumContentUSDCPurchaseGated } from '@audius/common' import { Button, @@ -303,7 +304,14 @@ const LockedPremiumTrackSection = ({ styles.premiumContentSectionTitle )} > - + {messages.howToUnlock}
{renderLockedDescription()} @@ -484,7 +492,14 @@ const UnlockedPremiumTrackSection = ({ ) ) : ( - + )} {isOwner ? isPremiumContentCollectibleGated(premiumConditions) diff --git a/packages/web/src/components/track/desktop/BottomRow.tsx b/packages/web/src/components/track/desktop/BottomRow.tsx index 4c16baf729..96725bd34e 100644 --- a/packages/web/src/components/track/desktop/BottomRow.tsx +++ b/packages/web/src/components/track/desktop/BottomRow.tsx @@ -1,17 +1,23 @@ import { ReactNode, useCallback } from 'react' -import { FieldVisibility, premiumContentSelectors, ID } from '@audius/common' -import { IconLock } from '@audius/stems' +import { + FieldVisibility, + premiumContentSelectors, + ID, + PremiumConditions, + Nullable +} from '@audius/common' import cn from 'classnames' import { useSelector } from 'react-redux' import FavoriteButton from 'components/alt-button/FavoriteButton' import RepostButton from 'components/alt-button/RepostButton' import ShareButton from 'components/alt-button/ShareButton' -import LoadingSpinner from 'components/loading-spinner/LoadingSpinner' import Tooltip from 'components/tooltip/Tooltip' import typeStyles from 'components/typography/typography.module.css' +import { PremiumConditionsPill } from '../PremiumConditionsPill' + import styles from './TrackTile.module.css' const { getPremiumTrackStatusMap } = premiumContentSelectors @@ -39,6 +45,7 @@ type BottomRowProps = { showIconButtons?: boolean isTrack?: boolean trackId?: ID + premiumConditions?: Nullable onClickRepost: (e?: any) => void onClickFavorite: (e?: any) => void onClickShare: (e?: any) => void @@ -60,6 +67,7 @@ export const BottomRow = ({ showIconButtons, isTrack, trackId, + premiumConditions, onClickRepost, onClickFavorite, onClickShare @@ -101,21 +109,14 @@ export const BottomRow = ({ ) } - if (isTrack && !isLoading && !doesUserHaveAccess) { + if (isTrack && premiumConditions && !isLoading && !doesUserHaveAccess) { return (
- {premiumTrackStatus === 'UNLOCKING' ? ( -
- - {messages.unlocking} -
- ) : ( -
- - {messages.locked} -
- )} - {!isLoading ?
{rightActions}
: null} + +
{rightActions}
) } diff --git a/packages/web/src/components/track/desktop/TrackTile.module.css b/packages/web/src/components/track/desktop/TrackTile.module.css index 5f354df29c..38b663f559 100644 --- a/packages/web/src/components/track/desktop/TrackTile.module.css +++ b/packages/web/src/components/track/desktop/TrackTile.module.css @@ -366,35 +366,6 @@ width: 100%; } -.bottomRow .premiumContent { - align-items: center; - background-color: var(--accent-blue); - border-radius: var(--unit-1); - color: var(--static-white); - display: flex; - gap: var(--unit-1); - height: var(--unit-6); - padding: var(--unit-2) var(--unit-3); -} - -.bottomRow .premiumContent svg { - width: var(--unit-4); - height: var(--unit-4); -} - -.bottomRow .premiumContent path { - fill: var(--static-white); -} - -.spinner { - height: var(--unit-4); - width: var(--unit-4); -} - -.spinner g path { - stroke: var(--static-white); -} - .bottomRow .iconButtons { display: flex; gap: var(--unit-8); diff --git a/packages/web/src/components/track/desktop/TrackTile.tsx b/packages/web/src/components/track/desktop/TrackTile.tsx index cc9ece9d92..028760e43c 100644 --- a/packages/web/src/components/track/desktop/TrackTile.tsx +++ b/packages/web/src/components/track/desktop/TrackTile.tsx @@ -8,7 +8,8 @@ import { formatLineupTileDuration, Genre, CommonState, - getDogEarType + getDogEarType, + isPremiumContentUSDCPurchaseGated } from '@audius/common' import { IconCheck, IconCrown, IconHidden, ProgressBar } from '@audius/stems' import cn from 'classnames' @@ -21,7 +22,7 @@ import Skeleton from 'components/skeleton/Skeleton' import typeStyles from 'components/typography/typography.module.css' import { useFlag } from 'hooks/useRemoteConfig' -import { LockedStatusBadge } from '../LockedStatusBadge' +import { LockedStatusBadge, LockedStatusBadgeProps } from '../LockedStatusBadge' import { PremiumContentLabel } from '../PremiumContentLabel' import { messages } from '../trackTileMessages' import { @@ -67,7 +68,8 @@ const renderLockedOrPlaysContent = ({ fieldVisibility, isOwner, isPremium, - listenCount + listenCount, + variant }: Pick< TrackTileProps, | 'doesUserHaveAccess' @@ -75,9 +77,10 @@ const renderLockedOrPlaysContent = ({ | 'isOwner' | 'isPremium' | 'listenCount' ->) => { +> & + Pick) => { if (isPremium && !isOwner) { - return + return } const hidePlays = fieldVisibility @@ -153,6 +156,8 @@ const TrackTile = ({ const isLongFormContent = genre === Genre.PODCASTS || genre === Genre.AUDIOBOOKS + const isPurchase = isPremiumContentUSDCPurchaseGated(premiumConditions) + const getDurationText = () => { if (!duration) { return '' @@ -332,7 +337,8 @@ const TrackTile = ({ fieldVisibility, isOwner, isPremium, - listenCount + listenCount, + variant: isPurchase ? 'premium' : 'gated' }) : null} @@ -355,6 +361,7 @@ const TrackTile = ({ onClickRepost={onClickRepost} onClickFavorite={onClickFavorite} onClickShare={onClickShare} + premiumConditions={premiumConditions} isTrack={isTrack} trackId={trackId} /> diff --git a/packages/web/src/components/track/mobile/BottomButtons.tsx b/packages/web/src/components/track/mobile/BottomButtons.tsx index a7c3a13d69..355738a79c 100644 --- a/packages/web/src/components/track/mobile/BottomButtons.tsx +++ b/packages/web/src/components/track/mobile/BottomButtons.tsx @@ -1,16 +1,16 @@ import { memo } from 'react' -import { PremiumTrackStatus } from '@audius/common' -import { IconLock } from '@audius/stems' +import { Nullable, PremiumConditions, PremiumTrackStatus } from '@audius/common' import cn from 'classnames' import FavoriteButton from 'components/alt-button/FavoriteButton' import MoreButton from 'components/alt-button/MoreButton' import RepostButton from 'components/alt-button/RepostButton' import ShareButton from 'components/alt-button/ShareButton' -import LoadingSpinner from 'components/loading-spinner/LoadingSpinner' import typeStyles from 'components/typography/typography.module.css' +import { PremiumConditionsPill } from '../PremiumConditionsPill' + import styles from './BottomButtons.module.css' type BottomButtonsProps = { @@ -20,21 +20,18 @@ type BottomButtonsProps = { toggleRepost: () => void onClickOverflow: () => void onShare: () => void + isLoading: boolean isOwner: boolean isDarkMode: boolean isUnlisted?: boolean isShareHidden?: boolean isTrack?: boolean doesUserHaveAccess?: boolean + premiumConditions?: Nullable premiumTrackStatus?: PremiumTrackStatus isMatrixMode: boolean } -const messages = { - locked: 'LOCKED', - unlocking: 'UNLOCKING' -} - const BottomButtons = (props: BottomButtonsProps) => { const moreButton = ( { ) // Premium condition without access - if (props.isTrack && !props.doesUserHaveAccess) { + if ( + props.isTrack && + !props.isLoading && + props.premiumConditions && + !props.doesUserHaveAccess + ) { return (
- {props.premiumTrackStatus === 'UNLOCKING' ? ( -
- - {messages.unlocking} -
- ) : ( -
- - {messages.locked} -
- )} +
{moreButton}
diff --git a/packages/web/src/components/track/mobile/PlaylistTile.tsx b/packages/web/src/components/track/mobile/PlaylistTile.tsx index bab1298d96..2f76ba11a4 100644 --- a/packages/web/src/components/track/mobile/PlaylistTile.tsx +++ b/packages/web/src/components/track/mobile/PlaylistTile.tsx @@ -278,6 +278,7 @@ const PlaylistTile = (props: PlaylistTileProps & ExtraProps) => { toggleRepost={props.toggleRepost} onShare={props.onShare} onClickOverflow={props.onClickOverflow} + isLoading={props.isLoading} isOwner={props.isOwner} isDarkMode={props.darkMode} isMatrixMode={props.isMatrix} diff --git a/packages/web/src/components/track/mobile/TrackTile.tsx b/packages/web/src/components/track/mobile/TrackTile.tsx index f1c6d731ef..1ec11675c8 100644 --- a/packages/web/src/components/track/mobile/TrackTile.tsx +++ b/packages/web/src/components/track/mobile/TrackTile.tsx @@ -9,7 +9,8 @@ import { premiumContentActions, formatLineupTileDuration, Genre, - getDogEarType + getDogEarType, + isPremiumContentUSDCPurchaseGated } from '@audius/common' import { IconCrown, IconHidden, IconTrending } from '@audius/stems' import cn from 'classnames' @@ -29,7 +30,7 @@ import typeStyles from 'components/typography/typography.module.css' import UserBadges from 'components/user-badges/UserBadges' import { profilePage } from 'utils/route' -import { LockedStatusBadge } from '../LockedStatusBadge' +import { LockedStatusBadge, LockedStatusBadgeProps } from '../LockedStatusBadge' import { messages } from '../trackTileMessages' import BottomButtons from './BottomButtons' @@ -52,7 +53,7 @@ type ExtraProps = { darkMode: boolean isMatrix: boolean isPremium: boolean - premiumConditions: Nullable + premiumConditions?: Nullable doesUserHaveAccess: boolean } @@ -63,7 +64,8 @@ const renderLockedOrPlaysContent = ({ fieldVisibility, isOwner, isPremium, - listenCount + listenCount, + variant }: Pick< CombinedProps, | 'doesUserHaveAccess' @@ -71,9 +73,10 @@ const renderLockedOrPlaysContent = ({ | 'isOwner' | 'isPremium' | 'listenCount' ->) => { +> & + Pick) => { if (isPremium && !isOwner) { - return + return } const hidePlays = fieldVisibility @@ -178,6 +181,7 @@ const TrackTile = (props: CombinedProps) => { const premiumTrackStatus = trackId ? premiumTrackStatusMap[trackId] : undefined + const isPurchase = isPremiumContentUSDCPurchaseGated(premiumConditions) const DogEarIconType = isLoading ? undefined @@ -425,7 +429,8 @@ const TrackTile = (props: CombinedProps) => { fieldVisibility, isOwner, isPremium, - listenCount + listenCount, + variant: isPurchase ? 'premium' : 'gated' }) : null} @@ -439,8 +444,10 @@ const TrackTile = (props: CombinedProps) => { onShare={onClickShare} onClickOverflow={onClickOverflowMenu} isOwner={isOwner} + isLoading={isLoading} isUnlisted={isUnlisted} doesUserHaveAccess={doesUserHaveAccess} + premiumConditions={premiumConditions} premiumTrackStatus={premiumTrackStatus} isShareHidden={hideShare} isDarkMode={darkMode} diff --git a/packages/web/src/utils/theme/dark.ts b/packages/web/src/utils/theme/dark.ts index 159818a6b6..94bc3a0f63 100644 --- a/packages/web/src/utils/theme/dark.ts +++ b/packages/web/src/utils/theme/dark.ts @@ -35,6 +35,8 @@ const theme = { '--accent-red-dark-1': '#C43047', + '--special-light-green': '#13c65a', + /* Semantic text */ '--text-default': 'var(--neutral)', '--text-subdued': 'var(--neutral-light-4)', diff --git a/packages/web/src/utils/theme/default.ts b/packages/web/src/utils/theme/default.ts index 7b7e387955..870f69703a 100644 --- a/packages/web/src/utils/theme/default.ts +++ b/packages/web/src/utils/theme/default.ts @@ -46,6 +46,7 @@ const theme = { '--accent-orange-light-1': '#FFA70F', '--accent-purple': '#8E51CF', + '--special-light-green': '#13c65a', /* Semantic text */ '--text-default': 'var(--neutral)',