Skip to content
Open
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
25 changes: 25 additions & 0 deletions packages/common/src/store/cache/collections/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ export const ORDER_PLAYLIST_FAILED = 'ORDER_PLAYLIST_FAILED'
export const PUBLISH_PLAYLIST = 'PUBLISH_PLAYLIST'
export const PUBLISH_PLAYLIST_FAILED = 'PUBLISH_PLAYLIST_FAILED'

export const DUPLICATE_PLAYLIST = 'DUPLICATE_PLAYLIST'

/**
* @param initTrackId optional track id to pull artwork from.
*/
Expand Down Expand Up @@ -175,3 +177,26 @@ export function publishPlaylistFailed(
) {
return { type: PUBLISH_PLAYLIST_FAILED, error, params, metadata }
}

export function duplicatePlaylist({
sourcePlaylistId,
formFields,
trackIds,
source,
isAlbum
}: {
sourcePlaylistId: ID
formFields: Partial<Collection>
trackIds: ID[]
source: string
isAlbum?: boolean
}) {
return {
type: DUPLICATE_PLAYLIST,
sourcePlaylistId,
formFields,
trackIds,
source,
isAlbum: !!isAlbum
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import { cacheCollectionsActions, toastActions } from '@audius/common/store'
import { call, delay, put, take, takeEvery } from 'typed-redux-saga'

const { toast } = toastActions

const INTER_DISPATCH_DELAY_MS = 30

const messages = {
duplicated: (count: number, isAlbum: boolean) =>
`Duplicated ${isAlbum ? 'album' : 'playlist'} with ${count} ${
count === 1 ? 'track' : 'tracks'
}`,
duplicatedNoTracks: (isAlbum: boolean) =>
`Duplicated ${isAlbum ? 'album' : 'playlist'}`
}

export function* duplicatePlaylistSaga() {
yield* takeEvery(
cacheCollectionsActions.DUPLICATE_PLAYLIST,
duplicatePlaylistWorker
)
}

function* duplicatePlaylistWorker(
action: ReturnType<typeof cacheCollectionsActions.duplicatePlaylist>
) {
const { formFields, trackIds, source, isAlbum } = action
const initTrackId = trackIds.length > 0 ? trackIds[0] : null

const createAction = isAlbum
? cacheCollectionsActions.createAlbum
: cacheCollectionsActions.createPlaylist

yield* put(createAction(formFields, source, initTrackId, 'route'))

if (trackIds.length <= 1) {
if (trackIds.length === 0) {
yield* put(toast({ content: messages.duplicatedNoTracks(isAlbum) }))
} else {
yield* put(toast({ content: messages.duplicated(1, isAlbum) }))
}
return
}

const requestedAction = yield* take(
cacheCollectionsActions.CREATE_PLAYLIST_REQUESTED
)
const newPlaylistId = (requestedAction as unknown as { playlistId: number })
.playlistId

for (let i = 1; i < trackIds.length; i += 1) {
yield* put(
cacheCollectionsActions.addTrackToPlaylist(trackIds[i], newPlaylistId, {
silent: true
})
)
yield* delay(INTER_DISPATCH_DELAY_MS)
}

yield* call(function* () {
yield* put(
toast({ content: messages.duplicated(trackIds.length, isAlbum) })
)
})
}
3 changes: 2 additions & 1 deletion packages/web/src/common/store/cache/collections/webSagas.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import commonSagas from './commonSagas'
import { createPlaylistRequestedSaga } from './createPlaylistRequestedSaga'
import { duplicatePlaylistSaga } from './duplicatePlaylistSaga'

export default function sagas() {
return [...commonSagas(), createPlaylistRequestedSaga]
return [...commonSagas(), createPlaylistRequestedSaga, duplicatePlaylistSaga]
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import UploadArtwork from 'components/upload/UploadArtwork'
import { useCollectionCoverArt } from 'hooks/useCollectionCoverArt'
import { resizeImage } from 'utils/imageProcessingUtil'

const { createPlaylist, createAlbum } = cacheCollectionsActions
const { duplicatePlaylist } = cacheCollectionsActions

const messages = {
title: 'Duplicate Playlist',
Expand All @@ -53,8 +53,10 @@ const messages = {
newDescriptionPlaceholder: 'Describe what makes this special',
cancel: 'Cancel',
duplicate: 'Duplicate',
trackCopyNote:
'Tracks are not copied automatically — you can add them after duplicating.',
trackCopyNote: (count: number) =>
count === 0
? 'No tracks to copy from this playlist.'
: `All ${count} ${count === 1 ? 'track' : 'tracks'} will be copied to the new playlist.`,
copySuffix: ' (Copy)'
}

Expand Down Expand Up @@ -179,8 +181,18 @@ export const DuplicatePlaylistModal = () => {
sourceCollection.is_image_autogenerated ?? false
}

const action = isAlbum ? createAlbum : createPlaylist
dispatch(action(formFields, CreatePlaylistSource.NAV, undefined, 'route'))
const sourceTrackIds =
sourceCollection.playlist_contents?.track_ids.map((t) => t.track) ?? []

dispatch(
duplicatePlaylist({
sourcePlaylistId: sourceCollection.playlist_id,
formFields,
trackIds: sourceTrackIds,
source: CreatePlaylistSource.NAV,
isAlbum
})
)
onClose()
}, [
customArtwork,
Expand Down Expand Up @@ -321,7 +333,9 @@ export const DuplicatePlaylistModal = () => {
) : null}
</Flex>
<Text variant='body' size='s' color='subdued'>
{messages.trackCopyNote}
{messages.trackCopyNote(
sourceCollection.playlist_contents?.track_ids.length ?? 0
)}
</Text>
</Flex>
) : null}
Expand Down
Loading