mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
ef5eda263d
* refactor(context-menu): H.1 — extract helpers + constants * refactor(context-menu): H.2 — extract AddToPlaylistSubmenu component * refactor(context-menu): H.3 — extract AlbumToPlaylistSubmenu + ArtistToPlaylistSubmenu * refactor(context-menu): H.4 — extract MultiAlbumToPlaylistSubmenu * refactor(context-menu): H.5 — extract MultiArtistToPlaylistSubmenu * refactor(context-menu): H.6 — extract SinglePlaylist + MultiPlaylist submenus * refactor(context-menu): H.7 — extract startRadio/startInstantMix/downloadAlbum/copyShareLink actions * refactor(context-menu): H.8 — extract useContextMenuKeyboardNav hook * refactor(context-menu): H.9 — extract useContextMenuRating hook * refactor(context-menu): H.10 — extract ContextMenuItems (all 9 type branches) * refactor(context-menu): H.11 — split ContextMenuItems into 5 type-branch files ContextMenuItems.tsx (800 LOC) was just a moved 400-LOC-cap violation. Now ContextMenuItems is a 30-LOC switch that dispatches to: - SongContextItems (song + album-song + favorite-song) - QueueItemContextItems (queue-item) - AlbumContextItems (album + multi-album) - ArtistContextItems (artist + multi-artist) - PlaylistContextItems (playlist + multi-playlist) All five branch files are now under 330 LOC; ContextMenu.tsx itself stays at 194 LOC. Shared Props interface lives in contextMenuItemTypes.ts. * refactor(context-menu): H.12 — strip unused imports from branch components
59 lines
1.9 KiB
TypeScript
59 lines
1.9 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { getAlbum } from '../../api/subsonicLibrary';
|
|
import { getArtist } from '../../api/subsonicArtists';
|
|
import { AddToPlaylistSubmenu } from './AddToPlaylistSubmenu';
|
|
|
|
interface AlbumProps {
|
|
albumId: string;
|
|
onDone: () => void;
|
|
triggerId?: string;
|
|
}
|
|
|
|
export function AlbumToPlaylistSubmenu({ albumId, onDone, triggerId }: AlbumProps) {
|
|
const [resolvedIds, setResolvedIds] = useState<string[] | null>(null);
|
|
|
|
useEffect(() => {
|
|
getAlbum(albumId).then((data) => {
|
|
setResolvedIds(data.songs.map((s) => s.id));
|
|
}).catch(() => setResolvedIds([]));
|
|
}, [albumId]);
|
|
|
|
if (resolvedIds === null) {
|
|
return (
|
|
<div className="context-submenu" style={{ display: 'flex', justifyContent: 'center', padding: '0.75rem' }}>
|
|
<div className="spinner" style={{ width: 16, height: 16 }} />
|
|
</div>
|
|
);
|
|
}
|
|
if (resolvedIds.length === 0) return null;
|
|
return <AddToPlaylistSubmenu songIds={resolvedIds} onDone={onDone} triggerId={triggerId} />;
|
|
}
|
|
|
|
interface ArtistProps {
|
|
artistId: string;
|
|
onDone: () => void;
|
|
triggerId?: string;
|
|
}
|
|
|
|
export function ArtistToPlaylistSubmenu({ artistId, onDone, triggerId }: ArtistProps) {
|
|
const [resolvedIds, setResolvedIds] = useState<string[] | null>(null);
|
|
|
|
useEffect(() => {
|
|
(async () => {
|
|
const { albums } = await getArtist(artistId);
|
|
const albumSongs = await Promise.all(albums.map(a => getAlbum(a.id).then(r => r.songs)));
|
|
setResolvedIds(albumSongs.flat().map(s => s.id));
|
|
})().catch(() => setResolvedIds([]));
|
|
}, [artistId]);
|
|
|
|
if (resolvedIds === null) {
|
|
return (
|
|
<div className="context-submenu" style={{ display: 'flex', justifyContent: 'center', padding: '0.75rem' }}>
|
|
<div className="spinner" style={{ width: 16, height: 16 }} />
|
|
</div>
|
|
);
|
|
}
|
|
if (resolvedIds.length === 0) return null;
|
|
return <AddToPlaylistSubmenu songIds={resolvedIds} onDone={onDone} triggerId={triggerId} />;
|
|
}
|