feat: added spacebar navigation to audioplayer
This commit is contained in:
@@ -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<HTMLAudioElement>(null);
|
||||
const canvasRef = useRef<HTMLCanvasElement>(null);
|
||||
@@ -41,6 +42,14 @@ export default function WaveformPlayer({
|
||||
const [duration, setDuration] = useState(0);
|
||||
const [peaks, setPeaks] = useState<number[]>([]);
|
||||
|
||||
// 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 (
|
||||
<div className="flex items-center gap-2">
|
||||
<audio
|
||||
ref={audioRef}
|
||||
src={src}
|
||||
onEnded={handleEnded}
|
||||
/>
|
||||
<button
|
||||
onClick={isPlaying ? (playMode === 'stop' ? handleStop : handlePause) : handlePlay}
|
||||
className={`inline-flex items-center justify-center w-8 h-8 rounded-full transition-colors ${
|
||||
isPlaying
|
||||
? 'bg-red-500 hover:bg-red-600 text-white'
|
||||
: 'bg-blue-500 hover:bg-blue-600 text-white'
|
||||
}`}
|
||||
title={isPlaying ? (playMode === 'stop' ? 'Stop' : 'Pause') : 'Play'}
|
||||
>
|
||||
{isPlaying ? (
|
||||
playMode === 'stop' ? (
|
||||
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
||||
<rect x="5" y="5" width="10" height="10" />
|
||||
</svg>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<audio
|
||||
ref={audioRef}
|
||||
src={src}
|
||||
onEnded={handleEnded}
|
||||
/>
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={isPlaying ? (playMode === 'stop' ? handleStop : handlePause) : handlePlay}
|
||||
className={`inline-flex items-center justify-center w-8 h-8 rounded-full transition-colors ${
|
||||
isPlaying
|
||||
? 'bg-red-500 hover:bg-red-600 text-white'
|
||||
: 'bg-blue-500 hover:bg-blue-600 text-white'
|
||||
}`}
|
||||
title={`${isPlaying ? (playMode === 'stop' ? 'Stop' : 'Pause') : 'Play'}`}
|
||||
>
|
||||
{isPlaying ? (
|
||||
playMode === 'stop' ? (
|
||||
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
||||
<rect x="5" y="5" width="10" height="10" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
||||
<rect x="6" y="4" width="2" height="12" />
|
||||
<rect x="12" y="4" width="2" height="12" />
|
||||
</svg>
|
||||
)
|
||||
) : (
|
||||
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
||||
<rect x="6" y="4" width="2" height="12" />
|
||||
<rect x="12" y="4" width="2" height="12" />
|
||||
<path d="M6.3 2.841A1.5 1.5 0 004 4.11v11.78a1.5 1.5 0 002.3 1.269l9.344-5.89a1.5 1.5 0 000-2.538L6.3 2.84z" />
|
||||
</svg>
|
||||
)
|
||||
) : (
|
||||
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path d="M6.3 2.841A1.5 1.5 0 004 4.11v11.78a1.5 1.5 0 002.3 1.269l9.344-5.89a1.5 1.5 0 000-2.538L6.3 2.84z" />
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
</button>
|
||||
{isOnlyPlayer && (
|
||||
<div
|
||||
className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-gray-300 text-gray-700 text-xs font-bold cursor-help"
|
||||
title="Leertaste zum Abspielen/Pausieren verwenden"
|
||||
>
|
||||
?
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -235,16 +283,17 @@ export default function WaveformPlayer({
|
||||
preload="metadata"
|
||||
/>
|
||||
<div className="flex items-center gap-3">
|
||||
{/* Play / Pause button */}
|
||||
<button
|
||||
onClick={isPlaying ? (playMode === 'stop' ? handleStop : handlePause) : handlePlay}
|
||||
className={`flex-shrink-0 inline-flex items-center justify-center w-9 h-9 rounded-full transition-colors ${
|
||||
isPlaying
|
||||
? 'bg-red-500 hover:bg-red-600 text-white'
|
||||
: 'bg-blue-500 hover:bg-blue-600 text-white'
|
||||
}`}
|
||||
title={isPlaying ? (playMode === 'stop' ? 'Stop' : 'Pause') : 'Abspielen'}
|
||||
>
|
||||
{/* Play / Pause button with optional info icon */}
|
||||
<div className="flex items-center gap-1">
|
||||
<button
|
||||
onClick={isPlaying ? (playMode === 'stop' ? handleStop : handlePause) : handlePlay}
|
||||
className={`flex-shrink-0 inline-flex items-center justify-center w-9 h-9 rounded-full transition-colors ${
|
||||
isPlaying
|
||||
? 'bg-red-500 hover:bg-red-600 text-white'
|
||||
: 'bg-blue-500 hover:bg-blue-600 text-white'
|
||||
}`}
|
||||
title={isPlaying ? (playMode === 'stop' ? 'Stop' : 'Pause') : 'Abspielen'}
|
||||
>
|
||||
{isPlaying ? (
|
||||
playMode === 'stop' ? (
|
||||
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
||||
@@ -261,8 +310,16 @@ export default function WaveformPlayer({
|
||||
<path d="M6.3 2.841A1.5 1.5 0 004 4.11v11.78a1.5 1.5 0 002.3 1.269l9.344-5.89a1.5 1.5 0 000-2.538L6.3 2.84z" />
|
||||
</svg>
|
||||
)}
|
||||
</button>
|
||||
|
||||
</button>
|
||||
{isOnlyPlayer && (
|
||||
<div
|
||||
className="inline-flex items-center justify-center w-5 h-5 rounded-full bg-gray-300 text-gray-700 text-xs font-bold cursor-help"
|
||||
title="Leertaste zum Abspielen/Pausieren verwenden"
|
||||
>
|
||||
?
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{/* Waveform canvas - hidden on small screens */}
|
||||
<canvas
|
||||
ref={canvasRef}
|
||||
|
||||
Reference in New Issue
Block a user