diff --git a/packages/common/src/api/tan-query/tracks/useStems.ts b/packages/common/src/api/tan-query/tracks/useStems.ts index 29701dabca5..3ebf6fc5761 100644 --- a/packages/common/src/api/tan-query/tracks/useStems.ts +++ b/packages/common/src/api/tan-query/tracks/useStems.ts @@ -3,13 +3,15 @@ import { useQuery, useQueryClient } from '@tanstack/react-query' import { stemTrackMetadataFromSDK, transformAndCleanList } from '~/adapters' import { useQueryContext } from '~/api/tan-query/utils' -import { ID } from '~/models/Identifiers' -import { StemTrack } from '~/models/Track' +import type { ID } from '~/models/Identifiers' +import type { Stem, StemTrack, Track } from '~/models/Track' import { QUERY_KEYS } from '../queryKeys' import { QueryKey, QueryOptions } from '../types' import { primeTrackData } from '../utils/primeTrackData' +import { getTrackQueryKey } from './useTrack' + export const getStemsQueryKey = (trackId: ID | null | undefined) => [QUERY_KEYS.stems, trackId] as unknown as QueryKey @@ -41,6 +43,24 @@ export const useStems = ( primeTrackData({ tracks: stems, queryClient }) } + queryClient.setQueryData( + getTrackQueryKey(trackId!), + (track) => { + if (!track) { + return track + } + + return { + ...track, + _stems: stems.map((stem) => ({ + track_id: stem.track_id, + category: stem.stem_of.category, + orig_filename: stem.orig_filename ?? '' + })) + } + } + ) + return stems }, ...options, diff --git a/packages/mobile/src/components/premium-content-purchase-drawer/PremiumContentPurchaseDrawer.tsx b/packages/mobile/src/components/premium-content-purchase-drawer/PremiumContentPurchaseDrawer.tsx index c9e8713d0b4..628ffaa3b36 100644 --- a/packages/mobile/src/components/premium-content-purchase-drawer/PremiumContentPurchaseDrawer.tsx +++ b/packages/mobile/src/components/premium-content-purchase-drawer/PremiumContentPurchaseDrawer.tsx @@ -2,6 +2,7 @@ import React, { useCallback, type ReactNode, useEffect } from 'react' import { useCollection, + useStems, useTrack, useUser, useUSDCBalance @@ -449,6 +450,9 @@ export const PremiumContentPurchaseDrawer = () => { } = usePremiumContentPurchaseModal() const isAlbum = contentType === PurchaseableContentType.ALBUM const { data: track, isPending: isTrackPending } = useTrack(contentId) + useStems(contentId, { + enabled: isOpen && !isAlbum && !!track?.is_download_gated + }) const { data: album } = useCollection(contentId, { enabled: isAlbum }) diff --git a/packages/mobile/src/screens/track-screen/DownloadSection.tsx b/packages/mobile/src/screens/track-screen/DownloadSection.tsx index b3efc2181c1..668e8358c55 100644 --- a/packages/mobile/src/screens/track-screen/DownloadSection.tsx +++ b/packages/mobile/src/screens/track-screen/DownloadSection.tsx @@ -153,6 +153,7 @@ export const DownloadSection = ({ trackId }: { trackId: ID }) => { const hasStems = stemTracks.length > 0 const downloadButtonText = hasStems ? messages.downloadAll : messages.download + const hasOriginalDownload = !!track?.is_downloadable const handleDownloadButtonPress = useCallback(() => { if (hasStems) { @@ -236,7 +237,7 @@ export const DownloadSection = ({ trackId }: { trackId: ID }) => { onToggleExpand={onToggleExpand} > - {track?.is_downloadable ? ( + {hasOriginalDownload ? ( <> { trackId={stemTrack.track_id} index={ i + - (track?.is_downloadable + (hasOriginalDownload ? STEM_INDEX_OFFSET_WITH_ORIGINAL_TRACK : STEM_INDEX_OFFSET_WITHOUT_ORIGINAL_TRACK) } diff --git a/packages/mobile/src/screens/track-screen/TrackScreenDetailsTile.tsx b/packages/mobile/src/screens/track-screen/TrackScreenDetailsTile.tsx index 07211025c35..e6e10cf7823 100644 --- a/packages/mobile/src/screens/track-screen/TrackScreenDetailsTile.tsx +++ b/packages/mobile/src/screens/track-screen/TrackScreenDetailsTile.tsx @@ -208,6 +208,7 @@ export const TrackScreenDetailsTile = ({ title, track_id: trackId, stream_conditions: streamConditions, + is_download_gated: isDownloadGated, ddex_app: ddexApp, is_delete: isDeleted, release_date: releaseDate, @@ -226,8 +227,11 @@ export const TrackScreenDetailsTile = ({ const remixParentTrackId = remixOf?.tracks?.[0]?.parent_track_id const isRemix = !!remixParentTrackId const { data: stems = [] } = useStems(track.track_id) + const hasGatedDownloadExtras = isDownloadGated && !isStreamGated const hasDownloadableAssets = - (track as Track)?.is_downloadable || stems.length > 0 + (track as Track)?.is_downloadable || + hasGatedDownloadExtras || + stems.length > 0 const { open: openCommentDrawer } = useCommentDrawer() diff --git a/packages/web/src/components/premium-content-purchase-modal/PremiumContentPurchaseModal.tsx b/packages/web/src/components/premium-content-purchase-modal/PremiumContentPurchaseModal.tsx index 3052a28023c..2d31e6ff930 100644 --- a/packages/web/src/components/premium-content-purchase-modal/PremiumContentPurchaseModal.tsx +++ b/packages/web/src/components/premium-content-purchase-modal/PremiumContentPurchaseModal.tsx @@ -5,6 +5,7 @@ import { useCollection, useCurrentAccount, useCurrentAccountUser, + useStems, useTrack, useUser } from '@audius/common/api' @@ -228,6 +229,9 @@ export const PremiumContentPurchaseModal = () => { const isAlbum = contentType === PurchaseableContentType.ALBUM const { data: track } = useTrack(contentId, { enabled: !isAlbum }) + useStems(contentId, { + enabled: isOpen && !isAlbum && !!track?.is_download_gated + }) const { data: album } = useCollection(contentId, { enabled: isAlbum diff --git a/packages/web/src/components/track/DownloadSection.tsx b/packages/web/src/components/track/DownloadSection.tsx index 8733305e479..3a207721735 100644 --- a/packages/web/src/components/track/DownloadSection.tsx +++ b/packages/web/src/components/track/DownloadSection.tsx @@ -192,10 +192,10 @@ export const DownloadSection = ({ trackId }: DownloadSectionProps) => { const hasStems = stemTracks.length > 0 || isUploadingStems const downloadButtonText = hasStems ? messages.downloadAll : messages.download - // No caret / no expandable list when there's a single downloadable track - // (download original ON, no stems). Tapping the row's download button - // should be the entire interaction — there's nothing to expand. - const isSingleTrackDownload = !!is_downloadable && !hasStems + // No caret / no expandable list when there's a single downloadable track. + // Tapping the row's download button should be the entire interaction. + const hasOriginalDownload = !!is_downloadable + const isSingleTrackDownload = hasOriginalDownload && !hasStems const handleDownloadButtonClick = useRequiresAccountCallback( (e: MouseEvent) => { @@ -341,7 +341,7 @@ export const DownloadSection = ({ trackId }: DownloadSectionProps) => { ) : null} - {is_downloadable ? ( + {hasOriginalDownload ? ( { size={fileSizes?.[stemTrack.track_id]?.[downloadQuality]} index={ i + - (is_downloadable + (hasOriginalDownload ? STEM_INDEX_OFFSET_WITH_ORIGINAL_TRACK : STEM_INDEX_OFFSET_WITHOUT_ORIGINAL_TRACK) } @@ -378,7 +378,7 @@ export const DownloadSection = ({ trackId }: DownloadSectionProps) => { index={ i + stemTracks.length + - (is_downloadable + (hasOriginalDownload ? STEM_INDEX_OFFSET_WITH_ORIGINAL_TRACK : STEM_INDEX_OFFSET_WITHOUT_ORIGINAL_TRACK) } diff --git a/packages/web/src/components/track/GiantTrackTile.tsx b/packages/web/src/components/track/GiantTrackTile.tsx index 3e00b7c7ee0..5eeddec5b2e 100644 --- a/packages/web/src/components/track/GiantTrackTile.tsx +++ b/packages/web/src/components/track/GiantTrackTile.tsx @@ -212,9 +212,17 @@ export const GiantTrackTile = ({ genre === Genre.Podcasts || genre === Genre.Audiobooks const isUSDCPurchaseGated = isContentUSDCPurchaseGated(streamConditions) const { data: track } = useTrack(trackId, { - select: (track) => pick(track, ['is_downloadable', 'preview_cid']) + select: (track) => + pick(track, [ + 'is_downloadable', + 'is_download_gated', + 'is_stream_gated', + 'preview_cid' + ]) }) - const shouldShowDownloadSection = !!track?.is_downloadable + const shouldShowDownloadSection = + !!track?.is_downloadable || + (!!track?.is_download_gated && !track?.is_stream_gated) // Preview button is shown for USDC-gated tracks if user does not have access // or is the owner const showPreview = diff --git a/packages/web/src/pages/track-page/components/mobile/TrackHeader.tsx b/packages/web/src/pages/track-page/components/mobile/TrackHeader.tsx index 2097804c424..f119631b9c6 100644 --- a/packages/web/src/pages/track-page/components/mobile/TrackHeader.tsx +++ b/packages/web/src/pages/track-page/components/mobile/TrackHeader.tsx @@ -184,6 +184,8 @@ const TrackHeader = ({ select: (track) => { return { is_downloadable: track?.is_downloadable, + is_download_gated: track?.is_download_gated, + is_stream_gated: track?.is_stream_gated, album_backlink: track?.album_backlink, release_date: track?.release_date, ddex_app: track?.ddex_app, @@ -194,6 +196,8 @@ const TrackHeader = ({ }) const { is_downloadable, + is_download_gated, + is_stream_gated, album_backlink, release_date, ddex_app, @@ -202,7 +206,9 @@ const TrackHeader = ({ } = partialTrack ?? {} const dispatch = useDispatch() - const hasDownloadableAssets = is_downloadable || (_stems?.length ?? 0) > 0 + const hasGatedDownloadExtras = !!is_download_gated && !is_stream_gated + const hasDownloadableAssets = + is_downloadable || hasGatedDownloadExtras || (_stems?.length ?? 0) > 0 const showSocials = !isUnlisted && hasStreamAccess const isUSDCPurchaseGated = isContentUSDCPurchaseGated(streamConditions)