feat: add playlist song picker

This commit is contained in:
Dominik
2026-08-09 22:11:21 +02:00
parent cf0a70d834
commit 4f6f84bdb5
6 changed files with 416 additions and 60 deletions
+53
View File
@@ -10,6 +10,9 @@ import {
} from 'vitest'
import type { MusicTrack } from '@/types/music'
import { nuiCall } from '@/utils/nui'
vi.mock('@/utils/nui', () => ({ nuiCall: vi.fn() }))
class FakeAudio extends EventTarget {
static latest: FakeAudio | null = null
@@ -117,6 +120,7 @@ beforeEach(() => {
createObjectUrlMock.mockReset()
createObjectUrlMock.mockReturnValue('blob:sky-music')
revokeObjectUrlMock.mockReset()
vi.mocked(nuiCall).mockReset()
})
afterEach(() => {
@@ -198,3 +202,52 @@ describe('music server playback', () => {
expect(createObjectUrlMock).toHaveBeenCalledOnce()
})
})
describe('music playlists', () => {
it('adds a track with the expected payload and applies the refreshed playlist', async () => {
const serverTrack = track('night-drive')
vi.mocked(nuiCall).mockResolvedValueOnce({
data: {
playlists: [
{
createdAt: 1,
entries: [{ songId: serverTrack.id, source: serverTrack.source }],
id: 'playlist-1',
name: 'Night Ride',
},
],
serverTracks: [serverTrack],
youtubeTracks: [],
},
success: true,
})
const music = useMusicStore()
const success = await music.addToPlaylist('playlist-1', serverTrack)
expect(success).toBe(true)
expect(nuiCall).toHaveBeenCalledWith('music:add-to-playlist', {
playlistId: 'playlist-1',
songId: 'night-drive',
source: 'server',
})
expect(music.playlists[0]?.entries).toEqual([
{ songId: 'night-drive', source: 'server' },
])
})
it('surfaces a duplicate-track error and releases the loading state', async () => {
const serverTrack = track('night-drive')
vi.mocked(nuiCall).mockResolvedValueOnce({
error: 'song_already_in_playlist',
success: false,
})
const music = useMusicStore()
const success = await music.addToPlaylist('playlist-1', serverTrack)
expect(success).toBe(false)
expect(music.error).toBe('song_already_in_playlist')
expect(music.isLoading).toBe(false)
})
})
+8
View File
@@ -1866,10 +1866,16 @@ const defaultLocales: LocaleTree = {
songRemoved: 'Song removed from your library.',
newPlaylist: 'New Playlist',
renamePlaylist: 'Rename Playlist',
addSongs: 'Add Songs',
addSongsBody: 'Choose songs from your library for this playlist.',
allSongsAdded: 'All Songs Added',
allSongsAddedBody:
'Every song in your library is already in this playlist.',
playlistBody: 'Give this playlist a name you will recognize.',
playlistName: 'Playlist Name',
playlistPlaceholder: 'My Playlist',
playlistCreated: 'Playlist created.',
playlistCreatedWithSong: 'Playlist created and song added.',
playlistRenamed: 'Playlist renamed.',
playlistDeleted: 'Playlist deleted.',
playlistActions: 'Playlist actions',
@@ -1880,6 +1886,7 @@ const defaultLocales: LocaleTree = {
choosePlaylist: 'Choose a Playlist',
addToPlaylist: 'Add to Playlist',
addedToPlaylist: 'Added to playlist.',
alreadyAdded: 'Added',
removeFromPlaylist: 'Remove from Playlist',
removedFromPlaylist: 'Removed from playlist.',
createPlaylistFirst: 'Create a playlist before adding this song.',
@@ -1910,6 +1917,7 @@ const defaultLocales: LocaleTree = {
playlist_not_found: 'That playlist no longer exists.',
playlist_song_limit: 'That playlist has reached its song limit.',
invalid_song: 'That song cannot be added to this playlist.',
song_already_in_playlist: 'That song is already in this playlist.',
rate_limited: 'Too many music changes. Try again shortly.',
playback_failed: 'This song could not be played.',
request_failed: 'Music is temporarily unavailable.',