Skip to content

Commit

Permalink
[PAY-2376] Stems & Downloads section UI visibility according to access (
Browse files Browse the repository at this point in the history
  • Loading branch information
dharit-tan authored Jan 24, 2024
1 parent e732d23 commit 0f96753
Show file tree
Hide file tree
Showing 5 changed files with 356 additions and 80 deletions.
40 changes: 39 additions & 1 deletion packages/common/src/hooks/useGatedContent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import {
Track,
isContentCollectibleGated,
isContentFollowGated,
isContentTipGated
isContentTipGated,
isContentUSDCPurchaseGated
} from 'models/Track'
import { useGetCurrentUserId } from 'src/api'
import { getAccountUser } from 'store/account/selectors'
import { cacheTracksSelectors, cacheUsersSelectors } from 'store/cache'
import { gatedContentSelectors } from 'store/gated-content'
Expand Down Expand Up @@ -162,3 +164,39 @@ export const useLockedContent = () => {

return { id, track, owner }
}

export const useDownloadableContentAccess = ({ trackId }: { trackId: ID }) => {
const track = useSelector((state: CommonState) =>
getTrack(state, { id: trackId })
)
const { data: currentUserId } = useGetCurrentUserId({})
const isOwner = track?.owner_id === currentUserId
// Only display downloadable-content-specific gated UI if the track is not
// stream-gated
const isDownloadGatedOnly =
!track?.is_stream_gated && track?.is_download_gated
const shouldDisplayDownloadFollowGated =
isDownloadGatedOnly &&
isContentFollowGated(track?.download_conditions) &&
track?.access?.download === false &&
!isOwner
const isOnlyDownloadableContentPurchaseGated =
isDownloadGatedOnly &&
isContentUSDCPurchaseGated(track?.download_conditions)
const price = isContentUSDCPurchaseGated(track?.download_conditions)
? track?.download_conditions.usdc_purchase.price
: undefined

return {
price,
shouldDisplayPremiumDownloadLocked:
isOnlyDownloadableContentPurchaseGated &&
track?.access?.download === false &&
!isOwner,
shouldDisplayPremiumDownloadUnlocked:
isOnlyDownloadableContentPurchaseGated &&
track?.access?.download === true &&
!isOwner,
shouldDisplayDownloadFollowGated
}
}
44 changes: 33 additions & 11 deletions packages/mobile/src/screens/track-screen/DownloadRow.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,53 @@
import { useCallback } from 'react'

import { cacheTracksSelectors } from '@audius/common'
import {
cacheTracksSelectors,
useDownloadableContentAccess
} from '@audius/common'
import type { DownloadQuality, CommonState, ID } from '@audius/common'
import { css } from '@emotion/native'
import { useSelector } from 'react-redux'

import { Flex, Text, IconReceive, Box } from '@audius/harmony-native'
import { PlainButton } from 'app/harmony-native/components/Button/PlainButton/PlainButton'
import { useToast } from 'app/hooks/useToast'

const { getTrack } = cacheTracksSelectors

const messages = {
fullTrack: 'Full Track'
fullTrack: 'Full Track',
followToDownload: 'Must follow artist to download.'
}

type DownloadRowProps = {
trackId: ID
quality: DownloadQuality
// onDownload: (trackId: ID, category?: string, parentTrackId?: ID) => void
hideDownload?: boolean
index: number
}

export const DownloadRow = ({
trackId,
// onDownload,
hideDownload,
index
}: DownloadRowProps) => {
const { toast } = useToast()
const track = useSelector((state: CommonState) =>
getTrack(state, { id: trackId })
)
const { shouldDisplayDownloadFollowGated } = useDownloadableContentAccess({
trackId
})

const handlePress = useCallback(() => {
if (track) {
if (shouldDisplayDownloadFollowGated) {
toast({ content: messages.followToDownload })
} else if (track && track.access.download) {
// onDownload(trackId, track.stem_of?.category, trackId)
}
}, [track])
}, [shouldDisplayDownloadFollowGated, toast, track])

return (
<Flex
Expand All @@ -49,24 +63,32 @@ export const DownloadRow = ({
gap='xl'
alignItems='center'
justifyContent='space-between'
style={css({ flexShrink: 1 })}
>
<Text variant='body' color='subdued'>
{index}
</Text>
<Flex gap='xs'>
<Flex gap='xs' style={css({ flexShrink: 1 })}>
<Text variant='body'>
{track?.stem_of?.category ?? messages.fullTrack}
</Text>
<Text variant='body' color='subdued'>
<Text
variant='body'
color='subdued'
ellipsizeMode='tail'
numberOfLines={1}
>
{track?.orig_filename}
</Text>
</Flex>
</Flex>
<PlainButton onPress={handlePress}>
<Box ph='s' pv='m'>
<IconReceive color='subdued' size='s' />
</Box>
</PlainButton>
{hideDownload ? null : (
<PlainButton onPress={handlePress}>
<Box ph='s' pv='m'>
<IconReceive color='subdued' size='s' />
</Box>
</PlainButton>
)}
</Flex>
)
}
130 changes: 111 additions & 19 deletions packages/mobile/src/screens/track-screen/DownloadSection.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,26 @@ import { useCallback, useState } from 'react'

import {
DownloadQuality,
ModalSource,
cacheTracksSelectors,
useCurrentStems
useCurrentStems,
useDownloadableContentAccess,
usePremiumContentPurchaseModal
} from '@audius/common'
import type { ID, CommonState } from '@audius/common'
import { USDC } from '@audius/fixed-decimal'
import { css } from '@emotion/native'
import { LayoutAnimation } from 'react-native'
import { useSelector } from 'react-redux'

import { Flex, IconReceive, Text, Button } from '@audius/harmony-native'
import {
Flex,
IconReceive,
Text,
Button,
IconLockUnlocked,
useTheme
} from '@audius/harmony-native'
import { SegmentedControl } from 'app/components/core'
import { Expandable, ExpandableArrowIcon } from 'app/components/expandable'

Expand All @@ -26,16 +38,30 @@ const messages = {
choose: 'Choose File Quality',
mp3: 'MP3',
original: 'Original',
downloadAll: 'Download All'
downloadAll: 'Download All',
unlockAll: (price: string) => `Unlock All $${price}`,
purchased: 'purchased'
}

export const DownloadSection = ({ trackId }: { trackId: ID }) => {
const { color } = useTheme()
const { onOpen: openPremiumContentPurchaseModal } =
usePremiumContentPurchaseModal()
const [quality, setQuality] = useState(DownloadQuality.MP3)
const [isExpanded, setIsExpanded] = useState(false)
const { stemTracks } = useCurrentStems({ trackId })
const shouldDisplayDownloadAll = stemTracks.length > 1
const {
price,
shouldDisplayPremiumDownloadLocked,
shouldDisplayPremiumDownloadUnlocked,
shouldDisplayDownloadFollowGated
} = useDownloadableContentAccess({ trackId })
const track = useSelector((state: CommonState) =>
getTrack(state, { id: trackId })
)
const shouldHideDownload =
!track?.access.download && !shouldDisplayDownloadFollowGated

const onToggleExpand = useCallback(() => {
LayoutAnimation.configureNext(
Expand All @@ -44,6 +70,13 @@ export const DownloadSection = ({ trackId }: { trackId: ID }) => {
setIsExpanded((expanded) => !expanded)
}, [])

const handlePurchasePress = useCallback(() => {
openPremiumContentPurchaseModal(
{ contentId: trackId },
{ source: ModalSource.TrackDetails }
)
}, [trackId, openPremiumContentPurchaseModal])

const renderHeader = () => {
return (
<Flex
Expand All @@ -52,11 +85,62 @@ export const DownloadSection = ({ trackId }: { trackId: ID }) => {
justifyContent='space-between'
alignItems='center'
>
<Flex direction='row' alignItems='center' gap='s'>
<IconReceive color='default' />
<Text variant='label' size='l' strength='strong'>
{messages.title}
</Text>
<Flex gap='m'>
<Flex direction='row' alignItems='center' gap='s'>
<IconReceive color='default' />
<Text variant='label' size='l' strength='strong'>
{messages.title}
</Text>
</Flex>
{shouldDisplayPremiumDownloadLocked && price !== undefined ? (
<Button
variant='primary'
size='small'
color='lightGreen'
onPress={handlePurchasePress}
>
{messages.unlockAll(
USDC(price / 100).toLocaleString('en-us', {
roundingMode: 'floor',
minimumFractionDigits: 2,
maximumFractionDigits: 2
})
)}
</Button>
) : null}
{shouldDisplayPremiumDownloadUnlocked ? (
<>
<Flex
gap='s'
direction='row'
alignItems='center'
style={css({
backgroundColor: color.special.blue
})}
>
<Flex
borderRadius='3xl'
ph='s'
style={css({
backgroundColor: color.special.lightGreen,
paddingTop: 1,
paddingBottom: 1
})}
>
<IconLockUnlocked color='staticWhite' size='xs' />
</Flex>
<Text
variant='label'
// TODO: size other than m causes misalignment C-3709
size='l'
strength='strong'
color='subdued'
>
{messages.purchased}
</Text>
</Flex>
</>
) : null}
</Flex>
<ExpandableArrowIcon expanded={isExpanded} />
</Flex>
Expand All @@ -81,22 +165,22 @@ export const DownloadSection = ({ trackId }: { trackId: ID }) => {
expanded={isExpanded}
onToggleExpand={onToggleExpand}
>
<Flex p='l' borderTop='default' gap='l' alignItems='flex-start'>
<Text variant='title'>{messages.choose}</Text>
<SegmentedControl
options={options}
selected={quality}
onSelectOption={(quality) => setQuality(quality)}
/>
<Button variant='secondary' iconLeft={IconReceive} size='small'>
{messages.downloadAll}
</Button>
</Flex>
{track?.is_original_available ? (
<Flex p='l' borderTop='default' gap='l' alignItems='flex-start'>
<Text variant='title'>{messages.choose}</Text>
<SegmentedControl
options={options}
selected={quality}
onSelectOption={(quality) => setQuality(quality)}
/>
</Flex>
) : null}
{track?.is_downloadable ? (
<DownloadRow
trackId={trackId}
quality={quality}
index={ORIGINAL_TRACK_INDEX}
hideDownload={shouldHideDownload}
/>
) : null}
{stemTracks?.map((s, i) => (
Expand All @@ -110,8 +194,16 @@ export const DownloadSection = ({ trackId }: { trackId: ID }) => {
: STEM_INDEX_OFFSET_WITHOUT_ORIGINAL_TRACK)
}
quality={quality}
hideDownload={shouldHideDownload}
/>
))}
{shouldDisplayDownloadAll ? (
<Flex p='l' borderTop='default' justifyContent='center'>
<Button variant='secondary' iconLeft={IconReceive} size='small'>
{messages.downloadAll}
</Button>
</Flex>
) : null}
</Expandable>
</Flex>
)
Expand Down
Loading

0 comments on commit 0f96753

Please sign in to comment.