From 2812689e5937bb754633277ea5abbee68b38c030 Mon Sep 17 00:00:00 2001 From: averel10 Date: Fri, 3 Apr 2026 10:23:19 +0200 Subject: [PATCH] feat: added spacebar navigation to audioplayer --- src/components/AudioProvider.tsx | 24 ++++- src/components/WaveformPlayer.tsx | 141 +++++++++++++++++++++--------- 2 files changed, 121 insertions(+), 44 deletions(-) diff --git a/src/components/AudioProvider.tsx b/src/components/AudioProvider.tsx index 25f2e94..c152d4e 100644 --- a/src/components/AudioProvider.tsx +++ b/src/components/AudioProvider.tsx @@ -1,19 +1,39 @@ 'use client'; -import React, { createContext, useContext, useState, ReactNode } from 'react'; +import React, { createContext, useContext, useState, ReactNode, useCallback } from 'react'; interface AudioContextType { currentAudioId: string | null; setCurrentAudio: (id: string | null) => void; + playerCount: number; + registerPlayer: (id: string) => void; + unregisterPlayer: (id: string) => void; } const AudioContext = createContext(undefined); export function AudioProvider({ children }: { children: ReactNode }) { const [currentAudioId, setCurrentAudioId] = useState(null); + const [playerCount, setPlayerCount] = useState(0); + + const registerPlayer = useCallback((id: string) => { + setPlayerCount((prev) => prev + 1); + }, []); + + const unregisterPlayer = useCallback((id: string) => { + setPlayerCount((prev) => Math.max(0, prev - 1)); + }, []); return ( - + {children} ); diff --git a/src/components/WaveformPlayer.tsx b/src/components/WaveformPlayer.tsx index 8dcdca6..1d4e26e 100644 --- a/src/components/WaveformPlayer.tsx +++ b/src/components/WaveformPlayer.tsx @@ -25,11 +25,12 @@ export default function WaveformPlayer({ showWaveform = true, playMode = 'pause', }: WaveformPlayerProps) { - const { currentAudioId, setCurrentAudio } = useAudio(); + const { currentAudioId, setCurrentAudio, playerCount, registerPlayer, unregisterPlayer } = useAudio(); // Generate unique ID for this player instance const playerId = useRef(Math.random().toString(36).slice(2)).current; const isActive = currentAudioId === playerId; + const isOnlyPlayer = playerCount === 1; const audioRef = useRef(null); const canvasRef = useRef(null); @@ -41,6 +42,14 @@ export default function WaveformPlayer({ const [duration, setDuration] = useState(0); const [peaks, setPeaks] = useState([]); + // Register/unregister player on mount/unmount + useEffect(() => { + registerPlayer(playerId); + return () => { + unregisterPlayer(playerId); + }; + }, [playerId, registerPlayer, unregisterPlayer]); + // Stop if another player became active useEffect(() => { if (!isActive && isPlaying) { @@ -112,10 +121,37 @@ export default function WaveformPlayer({ } }, [onFullyPlayed]); + const handleKeyDown = useCallback( + (e: KeyboardEvent) => { + if (e.code === 'Space' && isOnlyPlayer) { + e.preventDefault(); + if (isPlaying) { + if (playMode === 'stop') { + handleStop(); + } else { + handlePause(); + } + } else { + handlePlay(); + } + } + }, + [isOnlyPlayer, isPlaying, playMode, handlePlay, handlePause, handleStop] + ); + const handleLoadedMetadata = useCallback(() => { if (audioRef.current) setDuration(audioRef.current.duration); }, []); + // Add spacebar listener when only one player exists + useEffect(() => { + if (!isOnlyPlayer) return; + window.addEventListener('keydown', handleKeyDown); + return () => { + window.removeEventListener('keydown', handleKeyDown); + }; + }, [isOnlyPlayer, handleKeyDown]); + // Decode audio and generate waveform peaks (only if showWaveform is true) useEffect(() => { if (!showWaveform) return; @@ -188,38 +224,50 @@ export default function WaveformPlayer({ // Simple player button (no waveform) if (!showWaveform) { return ( -
-
+ ); } @@ -235,16 +283,17 @@ export default function WaveformPlayer({ preload="metadata" />
- {/* Play / Pause button */} - - + + {isOnlyPlayer && ( +
+ ? +
+ )} +
{/* Waveform canvas - hidden on small screens */}