Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
import { useGetPlaylistById } from '~/api'
import { useSelector } from 'react-redux'

import { ID, isContentUSDCPurchaseGated } from '~/models'
import { CommonState } from '~/store'
import { getCollection } from '~/store/cache/collections/selectors'
import { Nullable } from '~/utils'

import { LockedStatusVariant } from './types'

export const useCollectionLockedStatusVariant = (collectionId: ID) => {
const { data: collection } = useGetPlaylistById(
{ playlistId: collectionId },
{ disabled: !collectionId }
)

if (!collection) return null
const { stream_conditions } = collection
const streamConditions = useSelector((state: CommonState) => {
return getCollection(state, { id: collectionId })?.stream_conditions
})

const isPurchaseable = isContentUSDCPurchaseGated(stream_conditions)
const isPurchaseable = isContentUSDCPurchaseGated(streamConditions)

let variant: Nullable<LockedStatusVariant> = null
if (isPurchaseable) {
Expand Down
19 changes: 10 additions & 9 deletions packages/common/src/hooks/content/useIsCollectionUnlockable.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { useGetPlaylistById } from '~/api'
import { useSelector } from 'react-redux'

import { ID, isContentUSDCPurchaseGated } from '~/models'
import { CommonState } from '~/store'
import { getCollection } from '~/store/cache/collections/selectors'

export const useIsCollectionUnlockable = (collectionId: ID) => {
const { data: collection } = useGetPlaylistById({
playlistId: collectionId
})
return useSelector((state: CommonState) => {
const streamConditions = getCollection(state, {
id: collectionId
})?.stream_conditions

if (!collection) return false
const { stream_conditions } = collection
const isPurchaseable = isContentUSDCPurchaseGated(stream_conditions)

return isPurchaseable
return isContentUSDCPurchaseGated(streamConditions)
})
}
19 changes: 11 additions & 8 deletions packages/common/src/hooks/content/useIsTrackUnlockable.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import { useGetTrackById } from '~/api'
import { useSelector } from 'react-redux'

import {
isContentSpecialAccess,
isContentCollectibleGated,
isContentUSDCPurchaseGated,
ID
} from '~/models'
import { CommonState } from '~/store'
import { getTrack } from '~/store/cache/tracks/selectors'

export const useIsTrackUnlockable = (trackId: ID) => {
const { data: track } = useGetTrackById({ id: trackId })
return useSelector((state: CommonState) => {
const streamConditions = getTrack(state, { id: trackId })?.stream_conditions

if (!track) return false
const { stream_conditions } = track
const isPurchaseable = isContentUSDCPurchaseGated(stream_conditions)
const isCollectibleGated = isContentCollectibleGated(stream_conditions)
const isSpecialAccess = isContentSpecialAccess(stream_conditions)
const isPurchaseable = isContentUSDCPurchaseGated(streamConditions)
const isCollectibleGated = isContentCollectibleGated(streamConditions)
const isSpecialAccess = isContentSpecialAccess(streamConditions)

return isPurchaseable || isCollectibleGated || isSpecialAccess
return isPurchaseable || isCollectibleGated || isSpecialAccess
})
}
21 changes: 10 additions & 11 deletions packages/common/src/hooks/content/useTrackLockedStatusVariant.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
import { useGetTrackById } from '~/api'
import { useSelector } from 'react-redux'

import {
ID,
isContentCollectibleGated,
isContentSpecialAccess,
isContentUSDCPurchaseGated
} from '~/models'
import { CommonState } from '~/store'
import { getTrack } from '~/store/cache/tracks/selectors'
import { Nullable } from '~/utils'

import { LockedStatusVariant } from './types'

export const useTrackLockedStatusVariant = (trackId: ID) => {
const { data: track } = useGetTrackById(
{ id: trackId },
{ disabled: !trackId }
)

if (!track) return null
const { stream_conditions } = track
const streamConditions = useSelector((state: CommonState) => {
return getTrack(state, { id: trackId })?.stream_conditions
})

const isPurchaseable = isContentUSDCPurchaseGated(stream_conditions)
const isCollectibleGated = isContentCollectibleGated(stream_conditions)
const isSpecialAccess = isContentSpecialAccess(stream_conditions)
const isPurchaseable = isContentUSDCPurchaseGated(streamConditions)
const isCollectibleGated = isContentCollectibleGated(streamConditions)
const isSpecialAccess = isContentSpecialAccess(streamConditions)

let variant: Nullable<LockedStatusVariant> = null
if (isPurchaseable) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { VanityMetric } from './VanityMetrics'
const { setFavorite } = favoritesUserListActions
const { setRepost } = repostsUserListActions
const { getTrack } = cacheTracksSelectors

type RepostsMetricProps = {
trackId: ID
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { useGetPlaylistById } from '@audius/common/api'
import {
useCollectionLockedStatusVariant,
useGatedContentAccess
useGatedCollectionAccess
} from '@audius/common/hooks'
import { Collection, ID } from '@audius/common/models'
import { Nullable } from '@audius/common/utils'
import { ID } from '@audius/common/models'

import { LockedStatusBadge } from 'components/locked-status-badge'

Expand All @@ -16,14 +14,7 @@ export const CollectionLockedStatusBadge = (
props: CollectionLockedStatusBadgeProps
) => {
const { collectionId } = props
const { data: collection } = useGetPlaylistById(
{ playlistId: collectionId },
{ disabled: !collectionId }
)

const { hasStreamAccess } = useGatedContentAccess(
collection as Nullable<Collection>
)
const { hasStreamAccess } = useGatedCollectionAccess(collectionId)

const variant = useCollectionLockedStatusVariant(collectionId)
if (!variant) return null
Expand Down
67 changes: 40 additions & 27 deletions packages/web/src/components/collection/CollectionTileMetrics.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
import { useCallback } from 'react'

import { useGetPlaylistById } from '@audius/common/api'
import { ID } from '@audius/common/models'
import { repostsUserListActions, RepostType } from '@audius/common/store'
import { FavoriteType, ID } from '@audius/common/models'
import {
cacheCollectionsSelectors,
favoritesUserListActions,
repostsUserListActions,
RepostType
} from '@audius/common/store'
import { formatCount, route } from '@audius/common/utils'
import { Text, Flex, IconRepost, IconHeart } from '@audius/harmony'
import { push } from 'connected-react-router'
Expand All @@ -20,22 +24,35 @@ import {
UserListEntityType,
UserListType
} from 'store/application/ui/userListModal/types'
import { useSelector } from 'utils/reducer'
import { pluralize } from 'utils/stringUtils'

const { REPOSTING_USERS_ROUTE, FAVORITING_USERS_ROUTE } = route

const { setFavorite } = favoritesUserListActions
const { setRepost } = repostsUserListActions

const { getCollection } = cacheCollectionsSelectors

type RepostsMetricProps = {
collectionId: ID
size?: TrackTileSize
}

export const RepostsMetric = (props: RepostsMetricProps) => {
const { collectionId, size } = props
const { data: playlist } = useGetPlaylistById(
{ playlistId: collectionId },
{ disabled: !collectionId }
)
const repostCount = useSelector((state) => {
return getCollection(state, { id: collectionId })?.repost_count
})

const followeeReposts = useSelector((state) => {
return getCollection(state, { id: collectionId })?.followee_reposts
})

const isAlbum = useSelector((state) => {
return getCollection(state, { id: collectionId })?.is_album
})

const isMobile = useIsMobile()
const dispatch = useDispatch()

Expand All @@ -55,23 +72,22 @@ export const RepostsMetric = (props: RepostsMetricProps) => {
}
}, [dispatch, isMobile, collectionId])

if (!playlist) return null
const { repost_count = 0, followee_reposts = [], is_album } = playlist
if (repostCount === undefined || followeeReposts === undefined) return null

if (repost_count === 0)
if (repostCount === 0)
return (
<VanityMetric disabled>
<IconRepost size='s' color='subdued' />
<Text>
Be the first to repost this {is_album ? 'album' : 'playlist'}
Be the first to repost this {isAlbum ? 'album' : 'playlist'}
</Text>
</VanityMetric>
)

const renderName = () => {
const [{ user_id }] = followee_reposts
const [{ user_id }] = followeeReposts

const remainingCount = repost_count - 1
const remainingCount = repostCount - 1
const remainingText =
remainingCount > 0
? ` + ${formatCount(remainingCount)} ${pluralize(
Expand All @@ -95,14 +111,14 @@ export const RepostsMetric = (props: RepostsMetricProps) => {
css={(theme) => ({ gap: theme.spacing.l })}
onClick={handleClick}
>
{isLargeSize && followee_reposts.length >= 3 ? (
<AvatarList users={followee_reposts.map(({ user_id }) => user_id)} />
{isLargeSize && followeeReposts.length >= 3 ? (
<AvatarList users={followeeReposts.map(({ user_id }) => user_id)} />
) : null}
<Flex gap='xs'>
<IconRepost size='s' color='subdued' />
{isLargeSize && followee_reposts.length > 0
{isLargeSize && followeeReposts.length > 0
? renderName()
: formatCount(repost_count)}
: formatCount(repostCount)}
</Flex>
</VanityMetric>
)
Expand All @@ -114,16 +130,16 @@ type SavesMetricProps = {

export const SavesMetric = (props: SavesMetricProps) => {
const { collectionId } = props
const { data: playlist } = useGetPlaylistById(
{ playlistId: collectionId },
{ disabled: !collectionId }
)
const saveCount = useSelector((state) => {
return getCollection(state, { id: collectionId })?.save_count
})

const isMobile = useIsMobile()
const dispatch = useDispatch()

const handleClick = useCallback(() => {
if (isMobile) {
dispatch(setRepost(collectionId, RepostType.COLLECTION))
dispatch(setFavorite(collectionId, FavoriteType.PLAYLIST))
dispatch(push(FAVORITING_USERS_ROUTE))
} else {
dispatch(
Expand All @@ -137,15 +153,12 @@ export const SavesMetric = (props: SavesMetricProps) => {
}
}, [dispatch, isMobile, collectionId])

if (!playlist) return null
const { save_count = 0 } = playlist

if (save_count === 0) return null
if (!saveCount) return null

return (
<VanityMetric onClick={handleClick}>
<IconHeart size='s' color='subdued' />
{formatCount(save_count)}
{formatCount(saveCount)}
</VanityMetric>
)
}
18 changes: 9 additions & 9 deletions packages/web/src/components/collection/CollectionTileStats.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { useGetPlaylistById } from '@audius/common/api'
import { useIsCollectionUnlockable } from '@audius/common/hooks'
import { ID } from '@audius/common/models'
import { cacheCollectionsSelectors } from '@audius/common/store'
import { Flex, Skeleton } from '@audius/harmony'

import { EntityRank } from 'components/lineup/EntityRank'
import { TrackTileSize } from 'components/track/types'
import { useIsMobile } from 'hooks/useIsMobile'
import { useSelector } from 'utils/reducer'

import { CollectionAccessTypeLabel } from './CollectionAccessTypeLabel'
import { CollectionLockedStatusBadge } from './CollectionLockedStatusBadge'
import { RepostsMetric, SavesMetric } from './CollectionTileMetrics'

const { getCollection } = cacheCollectionsSelectors

type CollectionTileStatsProps = {
collectionId: ID
isTrending?: boolean
Expand All @@ -25,17 +28,14 @@ export const CollectionTileStats = (props: CollectionTileStatsProps) => {
const isMobile = useIsMobile()
const isUnlockable = useIsCollectionUnlockable(collectionId)

const { data: collection } = useGetPlaylistById(
{ playlistId: collectionId },
{ disabled: !!collectionId }
)
const isPrivate = useSelector((state) => {
return getCollection(state, { id: collectionId })?.is_private
})

if (isLoading || !collection) {
if (isLoading) {
return <Skeleton w='30%' h={isMobile ? 16 : 20} />
}

const { is_private } = collection

return (
<Flex
justifyContent='space-between'
Expand All @@ -47,7 +47,7 @@ export const CollectionTileStats = (props: CollectionTileStatsProps) => {
<EntityRank index={rankIndex} />
) : null}
<CollectionAccessTypeLabel collectionId={collectionId} />
{is_private ? null : (
{isPrivate ? null : (
<>
<RepostsMetric collectionId={collectionId} size={size} />
<SavesMetric collectionId={collectionId} />
Expand Down
12 changes: 3 additions & 9 deletions packages/web/src/components/track/TrackLockedStatusBadge.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { useGetTrackById } from '@audius/common/api'
import {
useGatedContentAccess,
useGatedTrackAccess,
useTrackLockedStatusVariant
} from '@audius/common/hooks'
import { ID, Track } from '@audius/common/models'
import { Nullable } from '@audius/common/utils'
import { ID } from '@audius/common/models'

import { LockedStatusBadge } from 'components/locked-status-badge'

Expand All @@ -14,12 +12,8 @@ type TrackLockedStatusBadgeProps = {

export const TrackLockedStatusBadge = (props: TrackLockedStatusBadgeProps) => {
const { trackId } = props
const { data: track } = useGetTrackById(
{ id: trackId },
{ disabled: !trackId }
)

const { hasStreamAccess } = useGatedContentAccess(track as Nullable<Track>)
const { hasStreamAccess } = useGatedTrackAccess(trackId)
const variant = useTrackLockedStatusVariant(trackId)

if (!variant) return null
Expand Down
Loading