mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-22 14:35:41 +00:00
463d3e0c5b
* refactor(fullscreen-player): H5 — extract FsLyricsApple + FsLyricsRail + useWordLyricsSync
The two lyrics views become own files under components/fullscreenPlayer/.
Their identical word-sync imperative DOM-update useEffect (only differing in
the .fsa-/.fsr- class prefix) collapses into the shared useWordLyricsSync
hook with a classPrefix arg.
FullscreenPlayer.tsx: 911 → 613 LOC.
* refactor(fullscreen-player): H5 — extract FsArt + FsPortrait + FsSeekbar
The three visual subcomponents move into own files. formatTime moves into
utils/fullscreenPlayerHelpers.ts so FsSeekbar keeps using it.
FullscreenPlayer.tsx: 613 → 421 LOC.
* refactor(fullscreen-player): H5 — extract FsLyricsMenu + FsPlayBtn
The lyrics-settings popover and the isolated play/pause button move into
own files. FullscreenPlayer drops the now-unused lucide-react icons
(Play/Pause/Moon/Sunrise/Music) and the PlaybackDelayModal +
PlaybackScheduleBadge imports — both used only by FsPlayBtn now.
FullscreenPlayer.tsx: 421 → 304 LOC.
* refactor(fullscreen-player): H5 — extract useFsDynamicAccent + useFsArtistPortrait + useFsIdleFade
Pulls three state+effect islands out of FullscreenPlayer:
- useFsDynamicAccent owns the cover-blob fetch + extractCoverColors call
plus the module-level artKey → accent cache that makes same-album song
switches instant.
- useFsArtistPortrait fetches getArtistInfo().largeImageUrl for the right-
side portrait, returning '' until resolved (or when no artistId).
- useFsIdleFade flips isIdle true after 3 s of inactivity, exposes a
throttled mousemove handler, and binds Escape to the provided callback.
FullscreenPlayer.tsx: 304 → 228 LOC.
44 lines
1.7 KiB
TypeScript
44 lines
1.7 KiB
TypeScript
import { useEffect, useState } from 'react';
|
|
import { extractCoverColors } from '../utils/dynamicColors';
|
|
|
|
// Module-level cache: artKey → accent color string.
|
|
// Survives track changes so same-album songs reuse the extracted color instantly.
|
|
const coverAccentCache = new Map<string, string>();
|
|
|
|
/** Extract a dominant accent color from the current cover art and cache it by
|
|
* artKey. Cache hits return instantly; cache misses fetch the cover blob,
|
|
* run extractCoverColors, then cache + apply the result. Keeps the previous
|
|
* color visible until extraction completes so the UI doesn't flash to default. */
|
|
export function useFsDynamicAccent(artUrl: string, artKey: string): string | null {
|
|
const [dynamicAccent, setDynamicAccent] = useState<string | null>(null);
|
|
|
|
useEffect(() => {
|
|
if (!artKey || !artUrl) { setDynamicAccent(null); return; }
|
|
const cached = coverAccentCache.get(artKey);
|
|
if (cached) { setDynamicAccent(cached); return; }
|
|
let cancelled = false;
|
|
let blobUrl = '';
|
|
(async () => {
|
|
try {
|
|
const resp = await fetch(artUrl);
|
|
if (cancelled) return;
|
|
const blob = await resp.blob();
|
|
if (cancelled) return;
|
|
blobUrl = URL.createObjectURL(blob);
|
|
const colors = await extractCoverColors(blobUrl);
|
|
if (cancelled) return;
|
|
if (colors.accent) {
|
|
coverAccentCache.set(artKey, colors.accent);
|
|
setDynamicAccent(colors.accent);
|
|
}
|
|
} catch { /* ignore */ } finally {
|
|
if (blobUrl) URL.revokeObjectURL(blobUrl);
|
|
}
|
|
})();
|
|
return () => { cancelled = true; };
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
}, [artKey]);
|
|
|
|
return dynamicAccent;
|
|
}
|