mirror of
https://github.com/kilyabin/psysonic.git
synced 2026-07-28 09:26:41 +00:00
refactor(fullscreen-player): H5 — split FullscreenPlayer.tsx 911 → 228 LOC across 11 files (#668)
* 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.
This commit is contained in:
committed by
GitHub
parent
8ff630cb5c
commit
463d3e0c5b
@@ -0,0 +1,43 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user