diff --git a/packages/mobile/src/assets/images/iconCart.svg b/packages/mobile/src/assets/images/iconCart.svg new file mode 100644 index 0000000000..ddae1070e1 --- /dev/null +++ b/packages/mobile/src/assets/images/iconCart.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/mobile/src/components/core/DogEar.tsx b/packages/mobile/src/components/core/DogEar.tsx index 2f773b53bd..d8f1c689c2 100644 --- a/packages/mobile/src/components/core/DogEar.tsx +++ b/packages/mobile/src/components/core/DogEar.tsx @@ -2,9 +2,9 @@ import type { ViewStyle } from 'react-native' import { View } from 'react-native' import LinearGradient from 'react-native-linear-gradient' +import IconCart from 'app/assets/images/iconCart.svg' import IconCollectible from 'app/assets/images/iconCollectible.svg' import IconHidden from 'app/assets/images/iconHidden.svg' -import IconLock from 'app/assets/images/iconLock.svg' import IconSpecialAccess from 'app/assets/images/iconSpecialAccess.svg' import IconStar from 'app/assets/images/iconStar.svg' import { makeStyles } from 'app/styles' @@ -39,9 +39,9 @@ const useStyles = makeStyles(({ spacing }) => ({ export enum DogEarType { STAR = 'star', HIDDEN = 'hidden', - LOCKED = 'locked', COLLECTIBLE_GATED = 'collectible gated', - SPECIAL_ACCESS = 'special access' + SPECIAL_ACCESS = 'special access', + USDC_PURCHASE = 'usdc purchase' } type DogEarProps = { @@ -58,7 +58,8 @@ export const DogEar = (props: DogEarProps) => { pageHeaderGradientColor2, secondary, staticWhite, - accentBlue + accentBlue, + specialLightGreen1 } = useThemeColors() const { icon: Icon, colors } = { @@ -70,10 +71,6 @@ export const DogEar = (props: DogEarProps) => { icon: IconHidden, colors: [neutral, neutralLight3] }, - [DogEarType.LOCKED]: { - icon: IconLock, - colors: [accentBlue, accentBlue] - }, [DogEarType.COLLECTIBLE_GATED]: { icon: IconCollectible, colors: [accentBlue, accentBlue] @@ -81,6 +78,10 @@ export const DogEar = (props: DogEarProps) => { [DogEarType.SPECIAL_ACCESS]: { icon: IconSpecialAccess, colors: [accentBlue, accentBlue] + }, + [DogEarType.USDC_PURCHASE]: { + icon: IconCart, + colors: [specialLightGreen1, specialLightGreen1] } }[type] diff --git a/packages/mobile/src/components/details-tile/DetailsTile.tsx b/packages/mobile/src/components/details-tile/DetailsTile.tsx index b0144fb31c..cb4d72855e 100644 --- a/packages/mobile/src/components/details-tile/DetailsTile.tsx +++ b/packages/mobile/src/components/details-tile/DetailsTile.tsx @@ -28,6 +28,7 @@ import { import Text from 'app/components/text' import UserBadges from 'app/components/user-badges' import { light } from 'app/haptics' +import { useIsUSDCEnabled } from 'app/hooks/useIsUSDCEnabled' import { useNavigation } from 'app/hooks/useNavigation' import { useFeatureFlag } from 'app/hooks/useRemoteConfig' import { flexRowCentered, makeStyles } from 'app/styles' @@ -228,6 +229,7 @@ export const DetailsTile = ({ const { isEnabled: isAiGeneratedTracksEnabled } = useFeatureFlag( FeatureFlags.AI_ATTRIBUTION ) + const isUSDCEnabled = useIsUSDCEnabled() const { track_id: trackId, premium_conditions: premiumConditions } = track ?? {} @@ -266,10 +268,12 @@ export const DetailsTile = ({ const dogEarType = showPremiumDogEar ? isOwner - ? premiumConditions.nft_collection + ? isUSDCEnabled && premiumConditions.usdc_purchase + ? DogEarType.USDC_PURCHASE + : premiumConditions.nft_collection ? DogEarType.COLLECTIBLE_GATED : DogEarType.SPECIAL_ACCESS - : DogEarType.LOCKED + : null : null if (showPremiumDogEar && dogEarType) { diff --git a/packages/mobile/src/components/lineup-tile/LineupTile.tsx b/packages/mobile/src/components/lineup-tile/LineupTile.tsx index f9657267e6..0199f0de56 100644 --- a/packages/mobile/src/components/lineup-tile/LineupTile.tsx +++ b/packages/mobile/src/components/lineup-tile/LineupTile.tsx @@ -11,6 +11,7 @@ import { useSelector, useDispatch } from 'react-redux' import { DogEar, DogEarType } from 'app/components/core' import type { LineupTileProps } from 'app/components/lineup-tile/types' +import { useIsUSDCEnabled } from 'app/hooks/useIsUSDCEnabled' import { setVisibility } from 'app/store/drawers/slice' import { LineupTileActionButtons } from './LineupTileActionButtons' @@ -59,6 +60,8 @@ export const LineupTile = ({ repost_count, save_count } = item + const dispatch = useDispatch() + const isUSDCEnabled = useIsUSDCEnabled() const { artist_pick_track_id, name, user_id } = user const currentUserId = useSelector(getUserId) const isOwner = user_id === currentUserId @@ -68,7 +71,6 @@ export const LineupTile = ({ const premiumConditions = isTrack ? item.premium_conditions : null const isArtistPick = artist_pick_track_id === id const { doesUserHaveAccess } = usePremiumContentAccess(isTrack ? item : null) - const dispatch = useDispatch() const showPremiumDogEar = premiumConditions && @@ -76,7 +78,9 @@ export const LineupTile = ({ !(showArtistPick && isArtistPick) const dogEarType = showPremiumDogEar - ? premiumConditions.nft_collection + ? isUSDCEnabled && premiumConditions.usdc_purchase + ? DogEarType.USDC_PURCHASE + : premiumConditions.nft_collection ? DogEarType.COLLECTIBLE_GATED : DogEarType.SPECIAL_ACCESS : null @@ -159,6 +163,7 @@ export const LineupTile = ({ isShareHidden={hideShare} isUnlisted={isUnlisted} trackId={trackId} + premiumConditions={premiumConditions} doesUserHaveAccess={doesUserHaveAccess} onPressOverflow={onPressOverflow} onPressRepost={onPressRepost} diff --git a/packages/mobile/src/components/lineup-tile/LineupTileAccessStatus.tsx b/packages/mobile/src/components/lineup-tile/LineupTileAccessStatus.tsx index 23b32cd3e1..1fa767da8a 100644 --- a/packages/mobile/src/components/lineup-tile/LineupTileAccessStatus.tsx +++ b/packages/mobile/src/components/lineup-tile/LineupTileAccessStatus.tsx @@ -1,4 +1,4 @@ -import type { ID } from '@audius/common' +import type { ID, PremiumConditions } from '@audius/common' import { premiumContentSelectors } from '@audius/common' import { View } from 'react-native' import { useSelector } from 'react-redux' @@ -6,6 +6,7 @@ import { useSelector } from 'react-redux' import IconLock from 'app/assets/images/iconLock.svg' import { Text } from 'app/components/core' import LoadingSpinner from 'app/components/loading-spinner' +import { useIsUSDCEnabled } from 'app/hooks/useIsUSDCEnabled' import { flexRowCentered, makeStyles } from 'app/styles' import { useColor } from 'app/utils/theme' @@ -33,17 +34,34 @@ const useStyles = makeStyles(({ palette, spacing, typography }) => ({ loadingSpinner: { width: spacing(4), height: spacing(4) + }, + usdcPurchase: { + backgroundColor: palette.specialLightGreen1 } })) -export const LineupTileAccessStatus = ({ trackId }: { trackId: ID }) => { +export const LineupTileAccessStatus = ({ + trackId, + premiumConditions +}: { + trackId: ID + premiumConditions: PremiumConditions +}) => { const styles = useStyles() + const isUSDCEnabled = useIsUSDCEnabled() const premiumTrackStatusMap = useSelector(getPremiumTrackStatusMap) const premiumTrackStatus = premiumTrackStatusMap[trackId] const staticWhite = useColor('staticWhite') return ( - + {premiumTrackStatus === 'UNLOCKING' ? ( ) : ( diff --git a/packages/mobile/src/components/lineup-tile/LineupTileActionButtons.tsx b/packages/mobile/src/components/lineup-tile/LineupTileActionButtons.tsx index 4efd382c79..bd8f9752bc 100644 --- a/packages/mobile/src/components/lineup-tile/LineupTileActionButtons.tsx +++ b/packages/mobile/src/components/lineup-tile/LineupTileActionButtons.tsx @@ -1,4 +1,4 @@ -import type { ID } from '@audius/common' +import type { ID, Nullable, PremiumConditions } from '@audius/common' import { View } from 'react-native' import IconKebabHorizontal from 'app/assets/images/iconKebabHorizontal.svg' @@ -20,6 +20,7 @@ type Props = { isShareHidden?: boolean isUnlisted?: boolean trackId?: ID + premiumConditions?: Nullable doesUserHaveAccess?: boolean onPressOverflow?: GestureResponderHandler onPressRepost?: GestureResponderHandler @@ -57,6 +58,7 @@ export const LineupTileActionButtons = ({ isUnlisted, trackId, doesUserHaveAccess = false, + premiumConditions, onPressOverflow, onPressRepost, onPressSave, @@ -111,9 +113,12 @@ export const LineupTileActionButtons = ({ return ( - {showPremiumAccessStatus && ( - - )} + {showPremiumAccessStatus && premiumConditions != null ? ( + + ) : null} {showLeftButtons && ( <> {repostButton} diff --git a/packages/mobile/src/components/lineup-tile/LineupTilePremiumContentTypeTag.tsx b/packages/mobile/src/components/lineup-tile/LineupTilePremiumContentTypeTag.tsx index 6648f020f6..1916add6aa 100644 --- a/packages/mobile/src/components/lineup-tile/LineupTilePremiumContentTypeTag.tsx +++ b/packages/mobile/src/components/lineup-tile/LineupTilePremiumContentTypeTag.tsx @@ -3,16 +3,19 @@ import { useMemo } from 'react' import type { PremiumConditions } from '@audius/common' import { View } from 'react-native' +import IconCart from 'app/assets/images/iconCart.svg' import IconCollectible from 'app/assets/images/iconCollectible.svg' import IconSpecialAccess from 'app/assets/images/iconSpecialAccess.svg' import Text from 'app/components/text' +import { useIsUSDCEnabled } from 'app/hooks/useIsUSDCEnabled' import { makeStyles, flexRowCentered } from 'app/styles' import { spacing } from 'app/styles/spacing' import { useThemeColors } from 'app/utils/theme' const messages = { collectibleGated: 'Collectible Gated', - specialAccess: 'Special Access' + specialAccess: 'Special Access', + premium: 'Premium' } const useStyles = makeStyles(({ spacing, palette, typography }) => ({ @@ -28,7 +31,8 @@ const useStyles = makeStyles(({ spacing, palette, typography }) => ({ enum PremiumContentType { COLLECTIBLE_GATED = 'collectible gated', - SPECIAL_ACCESS = 'special access' + SPECIAL_ACCESS = 'special access', + USDC_PURCHASE = 'usdc purchase' } type LineupTilePremiumContentTypeTagProps = { @@ -49,11 +53,15 @@ export const LineupTilePremiumContentTypeTag = ({ isOwner }: LineupTilePremiumContentTypeTagProps) => { const styles = useStyles() - const { accentBlue, neutralLight4 } = useThemeColors() + const { accentBlue, neutralLight4, specialLightGreen1 } = useThemeColors() + const isUSDCEnabled = useIsUSDCEnabled() - const type = premiumConditions?.nft_collection - ? PremiumContentType.COLLECTIBLE_GATED - : PremiumContentType.SPECIAL_ACCESS + const type = + isUSDCEnabled && premiumConditions?.usdc_purchase + ? PremiumContentType.USDC_PURCHASE + : premiumConditions?.nft_collection + ? PremiumContentType.COLLECTIBLE_GATED + : PremiumContentType.SPECIAL_ACCESS const premiumContentTypeMap = useMemo(() => { return { @@ -66,9 +74,21 @@ export const LineupTilePremiumContentTypeTag = ({ icon: IconSpecialAccess, color: doesUserHaveAccess && !isOwner ? neutralLight4 : accentBlue, text: messages.specialAccess + }, + [PremiumContentType.USDC_PURCHASE]: { + icon: IconCart, + color: + doesUserHaveAccess && !isOwner ? neutralLight4 : specialLightGreen1, + text: messages.premium } } - }, [accentBlue, doesUserHaveAccess, isOwner, neutralLight4]) + }, [ + accentBlue, + doesUserHaveAccess, + isOwner, + neutralLight4, + specialLightGreen1 + ]) const { icon: Icon, color, text } = premiumContentTypeMap[type] diff --git a/packages/mobile/src/components/lineup-tile/LineupTileStats.tsx b/packages/mobile/src/components/lineup-tile/LineupTileStats.tsx index d9d81f65aa..43d6f751b0 100644 --- a/packages/mobile/src/components/lineup-tile/LineupTileStats.tsx +++ b/packages/mobile/src/components/lineup-tile/LineupTileStats.tsx @@ -22,6 +22,7 @@ import IconUnlocked from 'app/assets/images/iconUnlocked.svg' import { CollectionDownloadStatusIndicator } from 'app/components/offline-downloads/CollectionDownloadStatusIndicator' import { TrackDownloadStatusIndicator } from 'app/components/offline-downloads/TrackDownloadStatusIndicator' import Text from 'app/components/text' +import { useIsUSDCEnabled } from 'app/hooks/useIsUSDCEnabled' import { useNavigation } from 'app/hooks/useNavigation' import { makeStyles, flexRowCentered } from 'app/styles' import { useThemeColors } from 'app/utils/theme' @@ -77,6 +78,9 @@ const useStyles = makeStyles(({ spacing, palette }) => ({ }, iconUnlocked: { backgroundColor: palette.accentBlue + }, + iconUSDC: { + backgroundColor: palette.specialLightGreen1 } })) @@ -122,6 +126,7 @@ export const LineupTileStats = ({ const { neutralLight4, staticWhite } = useThemeColors() const dispatch = useDispatch() const navigation = useNavigation() + const isUSDCEnabled = useIsUSDCEnabled() const hasEngagement = Boolean(repostCount || saveCount) @@ -203,7 +208,10 @@ export const LineupTileStats = ({ style={[ styles.iconLocked, styles.listenCount, - doesUserHaveAccess ? styles.iconUnlocked : null + doesUserHaveAccess ? styles.iconUnlocked : null, + isUSDCEnabled && premiumConditions.usdc_purchase + ? styles.iconUSDC + : null ]} > {doesUserHaveAccess ? ( diff --git a/packages/mobile/src/utils/theme.ts b/packages/mobile/src/utils/theme.ts index 550431c121..f56126534f 100644 --- a/packages/mobile/src/utils/theme.ts +++ b/packages/mobile/src/utils/theme.ts @@ -57,6 +57,7 @@ export const defaultTheme = { staticNeutralLight2: '#AAA7B8', staticNeutralLight8: '#F2F2F4', staticAccentGreenLight1: '#23AD1A', + specialLightGreen1: '#13C65A', staticPrimary: '#CC0FE0', staticSecondary: '#7E1BCC', pageHeaderGradientColor1: '#5B23E1', @@ -115,6 +116,7 @@ export const darkTheme = { staticNeutralLight2: '#AAA7B8', staticNeutralLight8: '#F2F2F4', staticAccentGreenLight1: '#23AD1A', + specialLightGreen1: '#13C65A', staticPrimary: '#CC0FE0', staticSecondary: '#7E1BCC', pageHeaderGradientColor1: '#7652CC', @@ -162,6 +164,7 @@ export const matrixTheme = { staticNeutralLight2: '#AAA7B8', staticNeutralLight8: '#F2F2F4', staticAccentGreenLight1: '#23AD1A', + specialLightGreen1: '#13C65A', staticPrimary: '#CC0FE0', staticSecondary: '#7E1BCC', pageHeaderGradientColor1: '#4FF069', @@ -223,6 +226,7 @@ export type ThemeColors = { staticNeutralLight2: string staticNeutralLight8: string staticAccentGreenLight1: string + specialLightGreen1: string staticPrimary: string staticSecondary: string pageHeaderGradientColor1: string