diff --git a/mediorum/.version.json b/mediorum/.version.json index b460792dd95..dc176d3658d 100644 --- a/mediorum/.version.json +++ b/mediorum/.version.json @@ -1,4 +1,4 @@ { - "version": "0.6.81", + "version": "0.6.82", "service": "content-node" } diff --git a/packages/common/src/models/Search.ts b/packages/common/src/models/Search.ts index 20027784f2e..5d00aeb48c8 100644 --- a/packages/common/src/models/Search.ts +++ b/packages/common/src/models/Search.ts @@ -86,7 +86,7 @@ export type SearchTrack = TrackImage & { followee_favorites: null user_id: number permalink: string - ddex_app?: Nullable + ddex_app: null _remixes: undefined _remixes_count: undefined } @@ -121,5 +121,6 @@ export type SearchPlaylist = CollectionImage & { time: number }[] } - ddex_app?: Nullable + stream_conditions: null + ddex_app: null } diff --git a/packages/common/src/store/cache/collections/actions.ts b/packages/common/src/store/cache/collections/actions.ts index 111255c84cc..c4d50f54f99 100644 --- a/packages/common/src/store/cache/collections/actions.ts +++ b/packages/common/src/store/cache/collections/actions.ts @@ -122,8 +122,8 @@ export function addTrackToPlaylistFailed( } export function removeTrackFromPlaylist( - trackId: number, - playlistId: number, + trackId: ID, + playlistId: ID, timestamp: number ) { return { type: REMOVE_TRACK_FROM_PLAYLIST, trackId, playlistId, timestamp } diff --git a/packages/common/src/store/ui/modals/album-track-remove-confirmation-modal.ts b/packages/common/src/store/ui/modals/album-track-remove-confirmation-modal.ts new file mode 100644 index 00000000000..9369922d107 --- /dev/null +++ b/packages/common/src/store/ui/modals/album-track-remove-confirmation-modal.ts @@ -0,0 +1,29 @@ +import { ID } from '~/models' + +import { createModal } from './createModal' + +export type AlbumTrackRemoveConfirmationModalState = { + trackId: ID | undefined + playlistId: ID | undefined + uid: string | undefined + timestamp: number | undefined +} + +const albumTrackRemoveConfirmationModal = + createModal({ + reducerPath: 'AlbumTrackRemoveConfirmation', + initialState: { + isOpen: false, + trackId: undefined, + playlistId: undefined, + uid: undefined, + timestamp: undefined + }, + sliceSelector: (state) => state.ui.modals + }) + +export const { + hook: useAlbumTrackRemoveConfirmationModal, + reducer: albumTrackRemoveConfirmationModalReducer, + actions: albumTrackRemoveConfirmationModalActions +} = albumTrackRemoveConfirmationModal diff --git a/packages/common/src/store/ui/modals/index.ts b/packages/common/src/store/ui/modals/index.ts index 01fffcb697b..33833787572 100644 --- a/packages/common/src/store/ui/modals/index.ts +++ b/packages/common/src/store/ui/modals/index.ts @@ -19,3 +19,4 @@ export * from './usdc-manual-transfer-modal' export * from './add-funds-modal' export * from './wait-for-download-modal' export * from './artist-pick-modal' +export * from './album-track-remove-confirmation-modal' diff --git a/packages/common/src/store/ui/modals/parentSlice.ts b/packages/common/src/store/ui/modals/parentSlice.ts index d933115858f..ab2ef02b9e4 100644 --- a/packages/common/src/store/ui/modals/parentSlice.ts +++ b/packages/common/src/store/ui/modals/parentSlice.ts @@ -47,6 +47,7 @@ export const initialState: BasicModalsState = { PlaybackRate: { isOpen: false }, ProfileActions: { isOpen: false }, PublishPlaylistConfirmation: { isOpen: false }, + AlbumTrackRemoveConfirmation: { isOpen: false }, AiAttributionSettings: { isOpen: false }, PremiumContentPurchaseModal: { isOpen: false }, CreateChatModal: { isOpen: false }, diff --git a/packages/common/src/store/ui/modals/reducers.ts b/packages/common/src/store/ui/modals/reducers.ts index a8e799fd5a2..031512dcc71 100644 --- a/packages/common/src/store/ui/modals/reducers.ts +++ b/packages/common/src/store/ui/modals/reducers.ts @@ -1,6 +1,7 @@ import { Action, combineReducers, Reducer } from '@reduxjs/toolkit' import { addFundsModalReducer } from './add-funds-modal' +import { albumTrackRemoveConfirmationModalReducer } from './album-track-remove-confirmation-modal' import { artistPickModalReducer } from './artist-pick-modal' import { coinflowOnrampModalReducer } from './coinflow-onramp-modal' import { coinflowWithdrawModalReducer } from './coinflow-withdraw-modal' @@ -47,7 +48,8 @@ const combinedReducers = combineReducers({ CoinflowOnramp: coinflowOnrampModalReducer, CoinflowWithdraw: coinflowWithdrawModalReducer, WaitForDownloadModal: waitForDownloadModalReducer, - ArtistPick: artistPickModalReducer + ArtistPick: artistPickModalReducer, + AlbumTrackRemoveConfirmation: albumTrackRemoveConfirmationModalReducer }) /** diff --git a/packages/common/src/store/ui/modals/types.ts b/packages/common/src/store/ui/modals/types.ts index 449741a2d78..81f4d48d02a 100644 --- a/packages/common/src/store/ui/modals/types.ts +++ b/packages/common/src/store/ui/modals/types.ts @@ -3,6 +3,7 @@ import { Action } from '@reduxjs/toolkit' import { ModalSource } from '~/models/Analytics' import { AddFundsModalState } from './add-funds-modal' +import { AlbumTrackRemoveConfirmationModalState } from './album-track-remove-confirmation-modal' import { ArtistPickModalState } from './artist-pick-modal' import { CoinflowOnrampModalState } from './coinflow-onramp-modal' import { CoinflowWithdrawModalState } from './coinflow-withdraw-modal' @@ -84,6 +85,7 @@ export type Modals = | 'CoinflowWithdraw' | 'WaitForDownloadModal' | 'ArtistPick' + | 'AlbumTrackRemoveConfirmation' export type BasicModalsState = { [modal in Modals]: BaseModalState @@ -105,6 +107,7 @@ export type StatefulModalsState = { CoinflowWithdraw: CoinflowWithdrawModalState WaitForDownloadModal: WaitForDownloadModalState ArtistPick: ArtistPickModalState + AlbumTrackRemoveConfirmation: AlbumTrackRemoveConfirmationModalState } export type ModalsState = BasicModalsState & StatefulModalsState diff --git a/packages/discovery-provider/.version.json b/packages/discovery-provider/.version.json index 284fb3e9ddf..d9af77348d2 100644 --- a/packages/discovery-provider/.version.json +++ b/packages/discovery-provider/.version.json @@ -1,4 +1,4 @@ { - "version": "0.6.81", + "version": "0.6.82", "service": "discovery-node" } diff --git a/packages/discovery-provider/src/tasks/index_nethermind.py b/packages/discovery-provider/src/tasks/index_nethermind.py index 6358eef11a9..36600563696 100644 --- a/packages/discovery-provider/src/tasks/index_nethermind.py +++ b/packages/discovery-provider/src/tasks/index_nethermind.py @@ -354,7 +354,7 @@ def index_next_block( ) if should_update: celery.send_task( - "calculate_trending_challenges", kwargs={"date": date} + "calculate_trending_challenges", queue="index_nethermind", kwargs={"date": date} ) except Exception as e: # Do not throw error, as this should not stop indexing diff --git a/packages/mobile/metro.config.js b/packages/mobile/metro.config.js index bbc5d6f285a..6eeb2df2ae7 100644 --- a/packages/mobile/metro.config.js +++ b/packages/mobile/metro.config.js @@ -67,7 +67,6 @@ const config = { splPath ], resolver: { - unstable_enablePackageExports: true, assetExts: assetExts.filter((ext) => ext !== 'svg'), sourceExts: [...sourceExts, 'svg', 'cjs', 'workerscript'], extraNodeModules: { @@ -108,6 +107,14 @@ const config = { type: 'sourceFile' } } + + if (moduleName === '@metaplex-foundation/umi/serializers') { + return { + filePath: `${resolveModule('@metaplex-foundation/umi')}/dist/cjs/serializers.cjs`, + type: 'sourceFile' + } + } + return context.resolveRequest(context, moduleName, platform) } }, diff --git a/packages/mobile/src/screens/collection-screen/CollectionScreenDetailsTile.tsx b/packages/mobile/src/screens/collection-screen/CollectionScreenDetailsTile.tsx index 70920f07a79..8cb16cb5fae 100644 --- a/packages/mobile/src/screens/collection-screen/CollectionScreenDetailsTile.tsx +++ b/packages/mobile/src/screens/collection-screen/CollectionScreenDetailsTile.tsx @@ -17,7 +17,7 @@ import { PurchaseableContentType } from '@audius/common/store' import { formatSecondsAsText, removeNullable } from '@audius/common/utils' -import type { Maybe } from '@audius/common/utils' +import type { Maybe, Nullable } from '@audius/common/utils' import { useDispatch, useSelector } from 'react-redux' import { usePrevious } from 'react-use' import { createSelector } from 'reselect' @@ -104,7 +104,7 @@ const getMessages = (collectionType: 'album' | 'playlist') => ({ detailsPlaceholder: '---' }) -const useStyles = makeStyles(({ palette, spacing, typography }) => ({ +const useStyles = makeStyles(({ palette, spacing }) => ({ trackListDivider: { marginHorizontal: spacing(6), borderTopWidth: 1, @@ -127,7 +127,7 @@ type CollectionScreenDetailsTileProps = { extraDetails?: DetailsTileDetail[] collectionId: number | SmartCollectionVariant hasStreamAccess?: boolean - streamConditions?: AccessConditions + streamConditions?: Nullable } & Omit< DetailsTileProps, | 'descriptionLinkPressSource' @@ -163,6 +163,7 @@ export const CollectionScreenDetailsTile = ({ hideRepost, hasStreamAccess, streamConditions, + ddexApp, ...detailsTileProps }: CollectionScreenDetailsTileProps) => { const styles = useStyles() @@ -288,7 +289,7 @@ export const CollectionScreenDetailsTile = ({ {...detailsTileProps} contentId={numericCollectionId} contentType={PurchaseableContentType.ALBUM} - ddexApp={(collection as Collection | null)?.ddex_app} + ddexApp={ddexApp} description={description} descriptionLinkPressSource='collection page' details={details} diff --git a/packages/web/src/components/album-track-remove-confirmation-modal/AlbumTrackRemoveConfirmationModal.tsx b/packages/web/src/components/album-track-remove-confirmation-modal/AlbumTrackRemoveConfirmationModal.tsx new file mode 100644 index 00000000000..bf7ca617784 --- /dev/null +++ b/packages/web/src/components/album-track-remove-confirmation-modal/AlbumTrackRemoveConfirmationModal.tsx @@ -0,0 +1,71 @@ +import { useCallback } from 'react' + +import { Kind } from '@audius/common/models' +import { + useAlbumTrackRemoveConfirmationModal, + collectionPageLineupActions, + cacheCollectionsActions +} from '@audius/common/store' +import { + Button, + Modal, + ModalContent, + ModalContentText, + ModalHeader, + ModalTitle, + ModalFooter +} from '@audius/harmony' +import { useDispatch } from 'react-redux' + +const messages = { + title: 'Remove Track', + description1: 'Are you sure you want to remove this track from your album?', + description2: + 'By default, fans who have purchased your album will still have access to your track.', + cancel: 'Cancel', + release: 'Remove Track From Album' +} + +export const AlbumTrackRemoveConfirmationModal = () => { + const { + isOpen, + onClose, + data: { trackId, playlistId, uid, timestamp } + } = useAlbumTrackRemoveConfirmationModal() + + const dispatch = useDispatch() + + const handleConfirm = useCallback(() => { + if (trackId && playlistId && uid && timestamp) { + dispatch( + cacheCollectionsActions.removeTrackFromPlaylist( + trackId, + playlistId, + timestamp + ) + ) + dispatch(collectionPageLineupActions.remove(Kind.TRACKS, uid)) + } + onClose() + }, [dispatch, onClose, playlistId, timestamp, trackId, uid]) + + return ( + + + + + + {messages.description1} + {messages.description2} + + + + + + + ) +} diff --git a/packages/web/src/pages/collection-page/CollectionPageProvider.tsx b/packages/web/src/pages/collection-page/CollectionPageProvider.tsx index 9a4c56b96f8..ddb61d05597 100644 --- a/packages/web/src/pages/collection-page/CollectionPageProvider.tsx +++ b/packages/web/src/pages/collection-page/CollectionPageProvider.tsx @@ -14,7 +14,8 @@ import { Collection, SmartCollection, ID, - UID + UID, + isContentUSDCPurchaseGated } from '@audius/common/models' import { accountSelectors, @@ -40,7 +41,9 @@ import { playlistUpdatesSelectors, CollectionTrack, CollectionsPageType, - CollectionPageTrackRecord + CollectionPageTrackRecord, + albumTrackRemoveConfirmationModalActions, + AlbumTrackRemoveConfirmationModalState } from '@audius/common/store' import { formatUrlName, Uid, Nullable } from '@audius/common/utils' import { push as pushRoute, replace } from 'connected-react-router' @@ -74,6 +77,7 @@ import { getCollectionPageSEOFields } from 'utils/seo' import { CollectionPageProps as DesktopCollectionPageProps } from './components/desktop/CollectionPage' import { CollectionPageProps as MobileCollectionPageProps } from './components/mobile/CollectionPage' + const { selectAllPlaylistUpdateIds } = playlistUpdatesSelectors const { makeGetCurrent } = queueSelectors const { getPlaying, getBuffering } = playerSelectors @@ -202,6 +206,7 @@ class CollectionPage extends Component< // if the uids of the tracks in the lineup are changing with this // update (initialOrder should contain ALL of the uids, so it suffices to check the first one). const newInitialOrder = tracks.entries.map((track) => track.uid) + const noInitialOrder = !initialOrder && tracks.entries.length > 0 const entryIds = new Set(newInitialOrder) const newUids = @@ -489,27 +494,24 @@ class CollectionPage extends Component< onClickRemove = ( trackId: number, - index: number, + _index: number, uid: string, timestamp: number ) => { - const { playlistId } = this.props - this.props.removeTrackFromPlaylist( - trackId, - playlistId as number, - uid, - timestamp - ) - - // Remove the track from the initial order, - // because reorder uses initial order as a starting point - const initialOrder = this.state.initialOrder - ? [ - ...this.state.initialOrder.slice(0, index), - ...this.state.initialOrder.slice(index + 1) - ] - : null - this.setState({ initialOrder }) + const { + playlistId, + collection: { stream_conditions } + } = this.props + if (isContentUSDCPurchaseGated(stream_conditions)) { + this.props.openConfirmationModal({ + trackId, + playlistId, + uid, + timestamp + }) + } else { + this.props.removeTrackFromPlaylist(trackId, playlistId, uid, timestamp) + } } onPlay = () => { @@ -988,6 +990,8 @@ function mapDispatchToProps(dispatch: Dispatch) { }) ), setModalVisibility: () => dispatch(setVisibility(true)), + openConfirmationModal: (args: AlbumTrackRemoveConfirmationModalState) => + dispatch(albumTrackRemoveConfirmationModalActions.open(args)), onEditCollection: (collectionId: ID) => dispatch( editPlaylistModalActions.open({ diff --git a/packages/web/src/pages/modals/Modals.tsx b/packages/web/src/pages/modals/Modals.tsx index c1f54b4500c..66a3e222a31 100644 --- a/packages/web/src/pages/modals/Modals.tsx +++ b/packages/web/src/pages/modals/Modals.tsx @@ -5,6 +5,7 @@ import { Modals as ModalTypes } from '@audius/common/store' import { AddFundsModal } from 'components/add-funds-modal/AddFundsModal' import AddToCollectionModal from 'components/add-to-collection/desktop/AddToCollectionModal' import { AiAttributionSettingsModal } from 'components/ai-attribution-settings-modal' +import { AlbumTrackRemoveConfirmationModal } from 'components/album-track-remove-confirmation-modal/AlbumTrackRemoveConfirmationModal' import AppCTAModal from 'components/app-cta-modal/AppCTAModal' import { ArtistPickModal } from 'components/artist-pick-modal/ArtistPickModal' import BrowserPushConfirmationModal from 'components/browser-push-confirmation-modal/BrowserPushConfirmationModal' @@ -88,6 +89,7 @@ const commonModalsMap: { [Modal in ModalTypes]?: ComponentType } = { AudioBreakdown: AudioBreakdownModal, UploadConfirmation: UploadConfirmationModal, PublishTrackConfirmation: PublishTrackConfirmationModal, + AlbumTrackRemoveConfirmation: AlbumTrackRemoveConfirmationModal, BuyAudio: BuyAudioModal, BuyAudioRecovery: BuyAudioRecoveryModal, TransactionDetails: TransactionDetailsModal,