Files
Psychotoxical-psysonic/src/hooks/useFsIdleFade.ts
T
Frank Stellmacher 463d3e0c5b 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.
2026-05-13 22:52:08 +02:00

41 lines
1.4 KiB
TypeScript

import { useCallback, useEffect, useRef, useState } from 'react';
/** Idle-fade system — flips `isIdle` true after 3 s of no user activity.
* Returns the boolean plus a mousemove handler that's throttled to one reset
* per ~200 ms so cleanup/start timers don't fire on every mouse pixel.
* Also resets on key presses and triggers `onEscape` when the user hits Esc. */
export function useFsIdleFade(onEscape: () => void) {
const [isIdle, setIsIdle] = useState(false);
const idleTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
const resetIdle = useCallback(() => {
setIsIdle(false);
if (idleTimer.current) clearTimeout(idleTimer.current);
idleTimer.current = setTimeout(() => setIsIdle(true), 3000);
}, []);
const lastMoveTime = useRef(0);
const handleMouseMove = useCallback(() => {
const now = Date.now();
if (now - lastMoveTime.current < 200) return;
lastMoveTime.current = now;
resetIdle();
}, [resetIdle]);
useEffect(() => {
resetIdle();
return () => { if (idleTimer.current) clearTimeout(idleTimer.current); };
}, [resetIdle]);
useEffect(() => {
const onKey = (e: KeyboardEvent) => {
resetIdle();
if (e.key === 'Escape') onEscape();
};
window.addEventListener('keydown', onKey);
return () => window.removeEventListener('keydown', onKey);
}, [onEscape, resetIdle]);
return { isIdle, handleMouseMove };
}