mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 06:25:41 +00:00
d08875dc70
Four-cut cluster. 572 → 471 LOC (−101). Each handler on the page
collapses to a one-line delegate; pure code move otherwise.
runPlaylistSaveMeta — the meta save flow (updatePlaylistMeta then
optional uploadPlaylistCoverArt + getPlaylist refresh + cover toast,
or coverRemoved → null, then metaSaved toast + closes the modal).
Takes the deps separately from the opts object so the call-site
just passes through the modal's payload.
usePlaylistSongSearch — searchOpen + searchQuery driven debounced
search against subsonic. Owns the 350 ms timeout, the filter-out
of songs already in the playlist, and the searching state. Returns
{ searchResults, setSearchResults, searching } so addSong on the
page can still drop a just-added song out of the result list.
usePlaylistSongMutations — addSong / removeSong. removeSong is
trivial (filter + setSongs + savePlaylist with prevCount).
addSong preserves the .main-content scrollTop save / requestAnimationFrame
restore trick + drops the song out of both suggestions and search
results + fires the add toast with playlist name interpolation.
usePlaylistStarRating — handleRate (local override + playerStore
userRatingOverride + setRating API) + handleToggleStar (e.stopPropagation,
local set + playerStore starredOverride + star/unstar API). Reads
starredOverrides / setStarredOverride from playerStore directly so
the page doesn't have to thread them through.
PlaylistDetail drops these direct imports (now consumed in the
hooks/utility): updatePlaylistMeta, uploadPlaylistCoverArt, search,
setRating, star, unstar, showToast, useRef. Pure code move.
47 lines
1.6 KiB
TypeScript
47 lines
1.6 KiB
TypeScript
import type { TFunction } from 'i18next';
|
|
import { getPlaylist, updatePlaylistMeta, uploadPlaylistCoverArt } from '../api/subsonicPlaylists';
|
|
import type { SubsonicPlaylist } from '../api/subsonicTypes';
|
|
import { showToast } from './toast';
|
|
|
|
export interface RunPlaylistSaveMetaDeps {
|
|
id: string;
|
|
playlist: SubsonicPlaylist;
|
|
t: TFunction;
|
|
setPlaylist: (updater: (p: SubsonicPlaylist | null) => SubsonicPlaylist | null) => void;
|
|
setCustomCoverId: (id: string | null) => void;
|
|
setEditingMeta: (v: boolean) => void;
|
|
}
|
|
|
|
export async function runPlaylistSaveMeta(
|
|
deps: RunPlaylistSaveMetaDeps,
|
|
opts: {
|
|
name: string;
|
|
comment: string;
|
|
isPublic: boolean;
|
|
coverFile: File | null;
|
|
coverRemoved: boolean;
|
|
},
|
|
): Promise<void> {
|
|
const { id, playlist, t, setPlaylist, setCustomCoverId, setEditingMeta } = deps;
|
|
await updatePlaylistMeta(id, opts.name.trim() || playlist.name, opts.comment, opts.isPublic);
|
|
setPlaylist(p => p
|
|
? { ...p, name: opts.name.trim() || p.name, comment: opts.comment, public: opts.isPublic }
|
|
: p
|
|
);
|
|
if (opts.coverFile) {
|
|
try {
|
|
await uploadPlaylistCoverArt(id, opts.coverFile);
|
|
const { playlist: refreshed } = await getPlaylist(id);
|
|
setPlaylist(prev => prev ? { ...prev, coverArt: refreshed.coverArt } : prev);
|
|
if (refreshed.coverArt) setCustomCoverId(refreshed.coverArt);
|
|
showToast(t('playlists.coverUpdated'));
|
|
} catch (err) {
|
|
showToast(err instanceof Error ? err.message : t('playlists.coverUpdated'), 3000, 'error');
|
|
}
|
|
} else if (opts.coverRemoved) {
|
|
setCustomCoverId(null);
|
|
}
|
|
showToast(t('playlists.metaSaved'));
|
|
setEditingMeta(false);
|
|
}
|