From 9366dbca1473f770605b4ecf53931f9f848815af Mon Sep 17 00:00:00 2001 From: JSC Date: Mon, 22 Sep 2025 20:10:37 +0200 Subject: [PATCH] feat: integrate volume control into PlayerControls and remove PlayerVolume component --- src/components/player/Player.tsx | 18 ++---- src/components/player/PlayerControls.tsx | 61 ++++++++++++++++++- src/components/player/PlayerProgress.tsx | 6 +- src/components/player/PlayerVolume.tsx | 77 ------------------------ src/hooks/useRenderFlash.ts | 4 +- 5 files changed, 71 insertions(+), 95 deletions(-) delete mode 100644 src/components/player/PlayerVolume.tsx diff --git a/src/components/player/Player.tsx b/src/components/player/Player.tsx index c428328..a7c0642 100644 --- a/src/components/player/Player.tsx +++ b/src/components/player/Player.tsx @@ -21,7 +21,6 @@ import { Playlist } from './Playlist' import { PlayerControls } from './PlayerControls' import { PlayerProgress } from './PlayerProgress' import { PlayerTrackInfo } from './PlayerTrackInfo' -import { PlayerVolume } from './PlayerVolume' import { useRenderFlash } from '@/hooks/useRenderFlash' export type PlayerDisplayMode = 'normal' | 'minimized' | 'maximized' | 'sidebar' @@ -347,22 +346,17 @@ export function Player({ className, onPlayerModeChange }: PlayerProps) { mode={state.mode} isLoading={isLoading} showPlaylistButton={true} + volume={state.volume} onPlayPause={handlePlayPause} onStop={handleStop} onPrevious={handlePrevious} onNext={handleNext} onModeChange={handleModeChange} onTogglePlaylist={() => setShowPlaylist(!showPlaylist)} + onVolumeChange={handleVolumeChange} + onMute={handleMute} /> -
- -
- {/* Playlist */} {showPlaylist && state.playlist && (
@@ -429,16 +423,12 @@ export function Player({ className, onPlayerModeChange }: PlayerProps) { status={state.status} mode={state.mode} isLoading={isLoading} + volume={state.volume} onPlayPause={handlePlayPause} onStop={handleStop} onPrevious={handlePrevious} onNext={handleNext} onModeChange={handleModeChange} - variant="maximized" - /> - - void onStop: () => void onPrevious: () => void onNext: () => void onModeChange: () => void onTogglePlaylist?: () => void + onVolumeChange?: (volume: number[]) => void + onMute?: () => void variant?: 'normal' | 'maximized' | 'minimized' } @@ -36,17 +42,20 @@ export const PlayerControls = memo(function PlayerControls({ mode, isLoading, showPlaylistButton = false, + volume, onPlayPause, onStop, onPrevious, onNext, onModeChange, onTogglePlaylist, + onVolumeChange, + onMute, variant = 'normal', }: PlayerControlsProps) { const isMinimized = variant === 'minimized' const isMaximized = variant === 'maximized' - const flashClass = useRenderFlash([status, mode], 'green') + const flashClass = useRenderFlash([status, mode, volume], 'green') const modeIcon = useMemo(() => { switch (mode) { @@ -168,6 +177,30 @@ export const PlayerControls = memo(function PlayerControls({ {modeLabel}
+ + {volume !== undefined && onVolumeChange && onMute && ( +
+ +
+ +
+ + {Math.round(volume)}% + +
+ )} ) @@ -242,6 +275,32 @@ export const PlayerControls = memo(function PlayerControls({ {modeLabel} + + {volume !== undefined && onVolumeChange && onMute && ( +
+ +
+ +
+
+ )} ) diff --git a/src/components/player/PlayerProgress.tsx b/src/components/player/PlayerProgress.tsx index 1d6dfe1..88a0a6b 100644 --- a/src/components/player/PlayerProgress.tsx +++ b/src/components/player/PlayerProgress.tsx @@ -18,7 +18,11 @@ export const PlayerProgress = memo(function PlayerProgress({ variant = 'normal', }: PlayerProgressProps) { const isMaximized = variant === 'maximized' - const flashClass = useRenderFlash([position, duration], 'blue') + + // Only flash when seconds actually change to avoid NumberFlow timing issues + const positionSeconds = Math.floor(position / 1000) + const durationSeconds = Math.floor(duration / 1000) + const flashClass = useRenderFlash([positionSeconds, durationSeconds], 'blue') const progressPercentage = useMemo(() => (position / (duration || 1)) * 100, diff --git a/src/components/player/PlayerVolume.tsx b/src/components/player/PlayerVolume.tsx deleted file mode 100644 index 80cfb5e..0000000 --- a/src/components/player/PlayerVolume.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import { Button } from '@/components/ui/button' -import { Slider } from '@/components/ui/slider' -import { Volume2, VolumeX } from 'lucide-react' -import { memo } from 'react' -import { useRenderFlash } from '@/hooks/useRenderFlash' - -interface PlayerVolumeProps { - volume: number - onVolumeChange: (volume: number[]) => void - onMute: () => void - variant?: 'normal' | 'maximized' -} - -export const PlayerVolume = memo(function PlayerVolume({ - volume, - onVolumeChange, - onMute, - variant = 'normal', -}: PlayerVolumeProps) { - const isMaximized = variant === 'maximized' - const flashClass = useRenderFlash([volume], 'yellow') - - if (isMaximized) { - return ( -
- {/* DEBUG: PlayerVolume Maximized - YELLOW FLASH */} - -
- -
- - {Math.round(volume)}% - -
- ) - } - - // Normal variant - return ( -
- {/* DEBUG: PlayerVolume Normal - YELLOW FLASH */} - -
- -
-
- ) -}) \ No newline at end of file diff --git a/src/hooks/useRenderFlash.ts b/src/hooks/useRenderFlash.ts index 6f34138..6fd0bf1 100644 --- a/src/hooks/useRenderFlash.ts +++ b/src/hooks/useRenderFlash.ts @@ -2,7 +2,7 @@ import { useEffect, useRef, useState } from 'react' export function useRenderFlash(deps: any[], color: string = 'red', duration: number = 300) { const [isFlashing, setIsFlashing] = useState(false) - const prevDepsRef = useRef() + const prevDepsRef = useRef(undefined) useEffect(() => { // Check if this is the first render @@ -25,7 +25,7 @@ export function useRenderFlash(deps: any[], color: string = 'red', duration: num } prevDepsRef.current = deps - }, deps) + }, [...deps]) const flashClass = isFlashing ? `border-2 border-${color}-500 border-dashed animate-pulse`