Skip to content

Add collections list route and support playlist/track collection items#59

Open
yajathb wants to merge 1 commit intoswingmx:masterfrom
yajathb:feat/collections-list-and-item-support
Open

Add collections list route and support playlist/track collection items#59
yajathb wants to merge 1 commit intoswingmx:masterfrom
yajathb:feat/collections-list-and-item-support

Conversation

@yajathb
Copy link
Copy Markdown

@yajathb yajathb commented Mar 30, 2026

No description provided.

Copilot AI review requested due to automatic review settings March 30, 2026 02:18
@CLAassistant
Copy link
Copy Markdown

CLAassistant commented Mar 30, 2026

CLA assistant check
All committers have signed the CLA.

@yajathb
Copy link
Copy Markdown
Author

yajathb commented Mar 30, 2026

Original SwingMusic Server-side Git Issue with which I have added the UI elements.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a first-class Collections list page/route and extends collection item support to include playlists and tracks, integrating these into navigation and context menus.

Changes:

  • Introduces /collections list route + new Collections list view UI and navigation entries.
  • Extends collection item handling to support playlists and tracks (requests, store updates, card rendering).
  • Adds playlist context menu wiring and collection add/remove actions for playlists/tracks.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
src/views/Collections/List.vue New collections list page fetching and rendering all collections.
src/stores/pages/collections.ts Expands local collection item removal to handle playlist/track items.
src/router/index.ts Adds CollectionList route and adjusts route constants/aliases for collections.
src/requests/collections.ts Adds Track support and improves payload handling for collection requests.
src/helpers/contextMenuHandler.ts Adds playlist context-menu entry point.
src/context_menus/track.ts Adds track “Add to Collection” and “Remove item” behavior.
src/context_menus/playlist.ts Implements playlist collection add/remove context menu options.
src/components/shared/CardRow.vue Adds rendering support for playlist and track items.
src/components/modals/CrudPage.vue Allows creating an empty collection (no initial item required).
src/components/PlaylistsList/PlaylistCard.vue Adds playlist context menu + minor accessibility improvements.
src/components/LeftSidebar/navitems.ts Adds Collections to sidebar menus/top nav items.
src/components/HomeView/Browse.vue Adds Collections entry to “Browse Library”.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +13 to 34
async removeLocalItem(
item: Album | Artist | Playlist | Track,
type: 'album' | 'artist' | 'playlist' | 'track'
) {
if (!this.collection) return

if (type == 'album') {
this.collection.items = this.collection.items.filter(i => {
return (i as Album).albumhash != (item as Album).albumhash
return !('albumhash' in i) || i.albumhash != (item as Album).albumhash
})
} else if (type == 'artist') {
this.collection.items = this.collection.items.filter(i => {
return !('artisthash' in i) || i.artisthash != (item as Artist).artisthash
})
} else if (type == 'playlist') {
this.collection.items = this.collection.items.filter(i => {
return !('id' in i && 'trackhashes' in i) || i.id != (item as Playlist).id
})
} else {
this.collection.items = this.collection.items.filter(i => {
return (i as Artist).artisthash != (item as Artist).artisthash
return !('trackhash' in i) || i.trackhash != (item as Track).trackhash
})
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removeLocalItem now handles 'track', but this.collection.items is typed from Collection and currently doesn't include track items; under strict TS this makes the 'track' branch (i.trackhash) fail type-checking. Update the Collection/collection-item typing to include Track (and any playlist shape used here), then remove the casts so this remains type-safe.

Copilot uses AI. Check for mistakes.
})
} else if (type == 'playlist') {
this.collection.items = this.collection.items.filter(i => {
return !('id' in i && 'trackhashes' in i) || i.id != (item as Playlist).id
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The playlist removal filter checks ('id' in i && 'trackhashes' in i), but Playlist in @/interfaces doesn't define trackhashes (it uses tracks). If collection playlist items don't have trackhashes, this branch will never match and the playlist won't be removed locally. Prefer a guard that matches the actual playlist shape used in collections (e.g., by checking typeof i.id === 'number' or the presence of tracks/name).

Suggested change
return !('id' in i && 'trackhashes' in i) || i.id != (item as Playlist).id
return !('id' in i && 'tracks' in i) || i.id != (item as Playlist).id

Copilot uses AI. Check for mistakes.
Comment on lines 22 to +64
return props.items.map((item: any) => {
const i = {
component: <any>null,
props: {},
key: '',
}

switch (item['type']) {
case 'album':
i.component = AlbumCard
i.key = item.albumhash
i.props = {
album: item,
}
break
case 'artist':
i.component = ArtistCard
i.key = item.artisthash
i.props = {
artist: item,
}
break
case 'mix':
i.component = MixCard
i.key = item.sourcehash
i.props = {
mix: item,
}
break
case 'playlist':
i.component = PlaylistCard
i.key = item.id
i.props = {
playlist: item,
}
break
case 'track':
i.component = TrackCard
i.key = item.trackhash
i.props = {
track: item,
playSource: playSources.track,
}
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i.key is inferred as a string from its initializer (key: ''), but the new playlist case assigns item.id (a number). With strict: true this will be a TS error. Define key as string | number (or initialize it in a way that matches both) so the computed mapping type-checks.

Copilot uses AI. Check for mistakes.
)

if (success) {
useCollection().removeLocalItem(track as any, 'track' as any)
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that removeLocalItem accepts (track, 'track'), the as any casts here are no longer needed and hide real typing issues. Remove the casts and let TypeScript enforce the correct item/type pairing.

Suggested change
useCollection().removeLocalItem(track as any, 'track' as any)
useCollection().removeLocalItem(track, 'track')

Copilot uses AI. Check for mistakes.
)

if (success) {
useCollection().removeLocalItem(playlist as any, 'playlist' as any)
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removeLocalItem has been updated to accept (playlist, 'playlist'), so the as any casts are unnecessary here and can mask type mismatches. Call removeLocalItem(playlist, 'playlist') directly.

Suggested change
useCollection().removeLocalItem(playlist as any, 'playlist' as any)
useCollection().removeLocalItem(playlist, 'playlist')

Copilot uses AI. Check for mistakes.
Comment on lines 71 to 75
export async function addOrRemoveItemFromCollection(
collection_id: number,
item: Album | Artist | Mix | Playlist,
item: Album | Artist | Mix | Playlist | Track,
type: string,
command: 'add' | 'remove'
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addOrRemoveItemFromCollection takes type: string, but only a fixed set of values is valid and the function logic depends on it. Consider typing this as a string-literal union (e.g. album/artist/mix/playlist/track) to prevent invalid calls at compile time, especially now that more item types are supported.

Copilot uses AI. Check for mistakes.
Comment on lines 37 to 70
@@ -60,6 +66,7 @@ export const menus = [
useDialog().showSettingsModal()
}
},
collections,
];
Copy link

Copilot AI Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

collections is appended as the last menus entry. In LeftSidebar/NavButtons.vue the CSS hides .circular.nav-item:last-child on phones, so this will likely hide the new Collections nav item instead of the Settings item. Reorder menus (or adjust the selector) so the intended item is hidden on mobile.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants