Skip to content

Commit 74f3c41

Browse files
dylanjeffersclaude
andcommitted
feat(web): add duplicate playlist flow under the New nav button
Adds a "Duplicate Playlist" secondary action to the sidebar's New (+) popup menu. Opens a modal where the user pastes any public Audius playlist URL, sees a preview of the source playlist (title, description, cover art), and toggles per-field switches to customize what should differ in the copy. The duplicated playlist is created as private by default (enforced by the existing optimisticallySavePlaylist saga). Scope: - Metadata-only duplication for now. Tracks are not copied — a follow-up PR will support full duplicate including track contents. The modal surfaces this with a helper line so users know they need to add tracks separately. - Reuses the existing createPlaylist saga: when artwork is not customized, we pass the source playlist's cover_art_sizes CID through so the saga reuses the cover instead of treating it as a new upload. Implementation: - New `DuplicatePlaylistModal` Redux modal slice (createModal helper). - New `DuplicatePlaylistModal` component (Harmony Modal + TextInput + Switch + TextArea + Artwork + UploadArtwork). - Resolves pasted URL → permalink via `getPathFromPlaylistUrl`, then loads the source via `useCollectionByPermalink`. - Wires "Duplicate Playlist" into `CreatePlaylistLibraryItemButton`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 0d699e5 commit 74f3c41

8 files changed

Lines changed: 395 additions & 5 deletions

File tree

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { createModal } from '../createModal'
2+
3+
export type DuplicatePlaylistModalState = {
4+
isAlbum?: boolean
5+
}
6+
7+
const duplicatePlaylistModal = createModal<DuplicatePlaylistModalState>({
8+
reducerPath: 'DuplicatePlaylistModal',
9+
initialState: {
10+
isOpen: false,
11+
isAlbum: false
12+
},
13+
sliceSelector: (state) => state.ui.modals
14+
})
15+
16+
export const {
17+
hook: useDuplicatePlaylistModal,
18+
reducer: duplicatePlaylistModalReducer,
19+
actions: duplicatePlaylistModalActions
20+
} = duplicatePlaylistModal

packages/common/src/store/ui/modals/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,4 @@ export * from './send-tokens-modal'
4242
export * from './coin-success-modal'
4343
export * from './fan-club-details-modal'
4444
export * from './create-playlist-modal'
45+
export * from './duplicate-playlist-modal'

packages/common/src/store/ui/modals/parentSlice.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ export const initialState: BasicModalsState = {
8484
FanClubDetailsModal: { isOpen: false },
8585
VerificationSuccess: { isOpen: false },
8686
VerificationError: { isOpen: false },
87-
CreatePlaylistModal: { isOpen: false }
87+
CreatePlaylistModal: { isOpen: false },
88+
DuplicatePlaylistModal: { isOpen: false }
8889
}
8990

9091
const slice = createSlice({

packages/common/src/store/ui/modals/reducers.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import { createChatModalReducer } from './create-chat-modal'
1515
import { createPlaylistModalReducer } from './create-playlist-modal'
1616
import { deleteTrackConfirmationModalReducer } from './delete-track-confirmation-modal'
1717
import { downloadTrackArchiveModalReducer } from './download-track-archive-modal'
18+
import { duplicatePlaylistModalReducer } from './duplicate-playlist-modal'
1819
import { earlyReleaseConfirmationModalReducer } from './early-release-confirmation-modal'
1920
import { editAccessConfirmationModalReducer } from './edit-access-confirmation-modal'
2021
import { externalWalletSignUpModalReducer } from './external-wallet-sign-up-modal'
@@ -94,7 +95,8 @@ const combinedReducers = combineReducers({
9495
SendTokensModal: sendTokensModalReducer,
9596
CoinSuccessModal: coinSuccessModalReducer,
9697
FanClubDetailsModal: fanClubDetailsModalReducer,
97-
CreatePlaylistModal: createPlaylistModalReducer
98+
CreatePlaylistModal: createPlaylistModalReducer,
99+
DuplicatePlaylistModal: duplicatePlaylistModalReducer
98100
})
99101

100102
/**

packages/common/src/store/ui/modals/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ export type Modals =
120120
| 'VerificationSuccess'
121121
| 'VerificationError'
122122
| 'CreatePlaylistModal'
123+
| 'DuplicatePlaylistModal'
123124

124125
export type BasicModalsState = {
125126
[modal in Modals]: BaseModalState

0 commit comments

Comments
 (0)