refactor(web): migrate playlists.ts to api.* wrapper (#375)
Drop 9 hand-rolled apiFetch calls with redundant Content-Type headers + manual JSON.stringify. The api.* wrapper in client.ts already handles both. Identical wire shape; existing tests cover all endpoints. Also makes api.del generic so DELETE endpoints with typed returns (playlist track removal) can use the wrapper instead of raw apiFetch. Default `T = null` preserves existing callers. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -58,6 +58,6 @@ export const api = {
|
||||
apiFetch(path, { method: 'PUT', body: JSON.stringify(body) }) as Promise<T>,
|
||||
patch: <T>(path: string, body: unknown): Promise<T> =>
|
||||
apiFetch(path, { method: 'PATCH', body: JSON.stringify(body) }) as Promise<T>,
|
||||
del: (path: string): Promise<null> =>
|
||||
apiFetch(path, { method: 'DELETE' }) as Promise<null>
|
||||
del: <T = null>(path: string): Promise<T> =>
|
||||
apiFetch(path, { method: 'DELETE' }) as Promise<T>
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { createQuery } from '@tanstack/svelte-query';
|
||||
import { apiFetch } from './client';
|
||||
import { api } from './client';
|
||||
import { qk } from './queries';
|
||||
import type { Playlist, PlaylistDetail } from './types';
|
||||
|
||||
@@ -12,13 +12,11 @@ export type ListPlaylistsResponse = {
|
||||
|
||||
export async function listPlaylists(kind?: PlaylistKind): Promise<ListPlaylistsResponse> {
|
||||
const k = kind ?? 'user';
|
||||
return (await apiFetch(`/api/playlists?kind=${k}`, { method: 'GET' })) as ListPlaylistsResponse;
|
||||
return api.get<ListPlaylistsResponse>(`/api/playlists?kind=${k}`);
|
||||
}
|
||||
|
||||
export async function getPlaylist(id: string): Promise<PlaylistDetail> {
|
||||
return (await apiFetch(`/api/playlists/${encodeURIComponent(id)}`, {
|
||||
method: 'GET'
|
||||
})) as PlaylistDetail;
|
||||
return api.get<PlaylistDetail>(`/api/playlists/${encodeURIComponent(id)}`);
|
||||
}
|
||||
|
||||
export type CreatePlaylistInput = {
|
||||
@@ -28,11 +26,7 @@ export type CreatePlaylistInput = {
|
||||
};
|
||||
|
||||
export async function createPlaylist(input: CreatePlaylistInput): Promise<Playlist> {
|
||||
return (await apiFetch('/api/playlists', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(input)
|
||||
})) as Playlist;
|
||||
return api.post<Playlist>('/api/playlists', input);
|
||||
}
|
||||
|
||||
export type UpdatePlaylistInput = {
|
||||
@@ -42,38 +36,31 @@ export type UpdatePlaylistInput = {
|
||||
};
|
||||
|
||||
export async function updatePlaylist(id: string, input: UpdatePlaylistInput): Promise<Playlist> {
|
||||
return (await apiFetch(`/api/playlists/${encodeURIComponent(id)}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(input)
|
||||
})) as Playlist;
|
||||
return api.patch<Playlist>(`/api/playlists/${encodeURIComponent(id)}`, input);
|
||||
}
|
||||
|
||||
export async function deletePlaylist(id: string): Promise<void> {
|
||||
await apiFetch(`/api/playlists/${encodeURIComponent(id)}`, { method: 'DELETE' });
|
||||
await api.del(`/api/playlists/${encodeURIComponent(id)}`);
|
||||
}
|
||||
|
||||
export async function appendTracks(playlistID: string, trackIDs: string[]): Promise<PlaylistDetail> {
|
||||
return (await apiFetch(`/api/playlists/${encodeURIComponent(playlistID)}/tracks`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ track_ids: trackIDs })
|
||||
})) as PlaylistDetail;
|
||||
return api.post<PlaylistDetail>(
|
||||
`/api/playlists/${encodeURIComponent(playlistID)}/tracks`,
|
||||
{ track_ids: trackIDs }
|
||||
);
|
||||
}
|
||||
|
||||
export async function removePlaylistTrack(playlistID: string, position: number): Promise<PlaylistDetail> {
|
||||
return (await apiFetch(
|
||||
`/api/playlists/${encodeURIComponent(playlistID)}/tracks/${position}`,
|
||||
{ method: 'DELETE' }
|
||||
)) as PlaylistDetail;
|
||||
return api.del<PlaylistDetail>(
|
||||
`/api/playlists/${encodeURIComponent(playlistID)}/tracks/${position}`
|
||||
);
|
||||
}
|
||||
|
||||
export async function reorderPlaylist(playlistID: string, orderedPositions: number[]): Promise<PlaylistDetail> {
|
||||
return (await apiFetch(`/api/playlists/${encodeURIComponent(playlistID)}/tracks`, {
|
||||
method: 'PUT',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ ordered_positions: orderedPositions })
|
||||
})) as PlaylistDetail;
|
||||
return api.put<PlaylistDetail>(
|
||||
`/api/playlists/${encodeURIComponent(playlistID)}/tracks`,
|
||||
{ ordered_positions: orderedPositions }
|
||||
);
|
||||
}
|
||||
|
||||
export function createPlaylistsQuery(kind?: PlaylistKind) {
|
||||
@@ -103,10 +90,7 @@ export type RefreshDiscoverResponse = {
|
||||
// returns the resulting Discover playlist's id + track count.
|
||||
// Used by the detail-page Refresh button and the home tile kebab.
|
||||
export async function refreshDiscover(): Promise<RefreshDiscoverResponse> {
|
||||
return (await apiFetch('/api/playlists/system/discover/refresh', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({})
|
||||
})) as RefreshDiscoverResponse;
|
||||
return api.post<RefreshDiscoverResponse>('/api/playlists/system/discover/refresh', {});
|
||||
}
|
||||
|
||||
// For-You refresh ------------------------------------------------------------
|
||||
@@ -125,7 +109,5 @@ export type RefreshForYouResponse = {
|
||||
// triggers refresh, the frontend then calls getPlaylist(new_id) to
|
||||
// get full TrackRef snapshots and enqueues for playback.
|
||||
export async function refreshForYou(): Promise<RefreshForYouResponse> {
|
||||
return (await apiFetch('/api/playlists/system/for-you/refresh', {
|
||||
method: 'POST'
|
||||
})) as RefreshForYouResponse;
|
||||
return api.post<RefreshForYouResponse>('/api/playlists/system/for-you/refresh', {});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user