mirror of
https://github.com/Psychotoxical/psysonic.git
synced 2026-07-22 15:25:46 +00:00
6f8dd73448
Four-cut cluster closing out the NowPlaying decomposition. 715 → 391 LOC (−324). TourCard — Bandsintown tour-dates card with the privacy-prompt gate (shown before the user opts in), loading state, empty state, 5-item show-more list with date / venue / place, and the "Tour data via Bandsintown" credit footer. DiscographyCard — chronological album grid (10×2 = 20 tiles initial, show-more for the rest), each tile a cached cover-art thumbnail with a tooltip and click-through to the album page. useNowPlayingFetchers — the eight cached fetch effects that drove the page: song meta / artist info / album / top songs / Bandsintown tour events (+ loading state) / discography / Last.fm track stats / Last.fm artist stats. Each effect follows the same pattern (cache hit → seed state, cache miss → fetch + setCache + setState, cancellation flag on cleanup). All eight makeCache<…> instances move into the hook module too. useNowPlayingStarLove — local starred + lfmLoved booleans seeded from songMeta.starred and lfmTrack.userLoved respectively, plus toggleStar (star/unstar Subsonic API) and toggleLfmLove (lastfmLove/Unlove with the session key). NowPlaying drops the now-unused imports: star/unstar (Subsonic), getArtist/getArtistInfo/getTopSongs/getSong/getAlbum, the entire '../api/lastfm' line, fetchBandsintownEvents + BandsintownEvent, makeCache (was passing through to the page only for cache instantiation), plus SubsonicAlbum (only the type is still used inside the hook). The radio + dashboard JSX in the body still references everything via the hook returns. Pure code move otherwise.
48 lines
1.9 KiB
TypeScript
48 lines
1.9 KiB
TypeScript
import { useCallback, useEffect, useState } from 'react';
|
|
import { star, unstar } from '../api/subsonicStarRating';
|
|
import type { SubsonicSong } from '../api/subsonicTypes';
|
|
import {
|
|
lastfmLoveTrack, lastfmUnloveTrack,
|
|
type LastfmTrackInfo,
|
|
} from '../api/lastfm';
|
|
|
|
export interface NowPlayingStarLoveDeps {
|
|
currentTrack: { id: string; title: string; artist: string } | null;
|
|
songMeta: SubsonicSong | null;
|
|
lfmTrack: LastfmTrackInfo | null;
|
|
lfmLoveEnabled: boolean;
|
|
lastfmSessionKey: string;
|
|
}
|
|
|
|
export interface NowPlayingStarLoveResult {
|
|
starred: boolean;
|
|
lfmLoved: boolean;
|
|
toggleStar: () => Promise<void>;
|
|
toggleLfmLove: () => Promise<void>;
|
|
}
|
|
|
|
export function useNowPlayingStarLove(deps: NowPlayingStarLoveDeps): NowPlayingStarLoveResult {
|
|
const { currentTrack, songMeta, lfmTrack, lfmLoveEnabled, lastfmSessionKey } = deps;
|
|
|
|
// Star
|
|
const [starred, setStarred] = useState(false);
|
|
useEffect(() => { setStarred(!!songMeta?.starred); }, [songMeta]);
|
|
const toggleStar = useCallback(async () => {
|
|
if (!currentTrack) return;
|
|
if (starred) { await unstar(currentTrack.id, 'song'); setStarred(false); }
|
|
else { await star(currentTrack.id, 'song'); setStarred(true); }
|
|
}, [currentTrack, starred]);
|
|
|
|
// Last.fm love (seeded from track.getInfo, toggle via love/unlove)
|
|
const [lfmLoved, setLfmLoved] = useState(false);
|
|
useEffect(() => { setLfmLoved(!!lfmTrack?.userLoved); }, [lfmTrack]);
|
|
const toggleLfmLove = useCallback(async () => {
|
|
if (!currentTrack || !lfmLoveEnabled) return;
|
|
const track = { title: currentTrack.title, artist: currentTrack.artist };
|
|
if (lfmLoved) { await lastfmUnloveTrack(track, lastfmSessionKey); setLfmLoved(false); }
|
|
else { await lastfmLoveTrack (track, lastfmSessionKey); setLfmLoved(true); }
|
|
}, [currentTrack, lfmLoved, lfmLoveEnabled, lastfmSessionKey]);
|
|
|
|
return { starred, lfmLoved, toggleStar, toggleLfmLove };
|
|
}
|