fix: play/pause and play/stop behviour

This commit is contained in:
averel10
2026-04-03 10:13:08 +02:00
parent 3e54d2c1ee
commit 6986e88560
8 changed files with 37 additions and 19 deletions

View File

@@ -117,7 +117,7 @@ export default async function DatasetEntryPage({ params }: DatasetEntryPageProps
{(() => { {(() => {
const fileExtension = entry.fileName.substring(entry.fileName.lastIndexOf('.')); const fileExtension = entry.fileName.substring(entry.fileName.lastIndexOf('.'));
const src = `/public/datasets/${datasetIdNum}/${entry.externalId}${fileExtension}`; const src = `/public/datasets/${datasetIdNum}/${entry.externalId}${fileExtension}`;
return <WaveformPlayer src={src} showWaveform={false} />; return <WaveformPlayer src={src} showWaveform={false} playMode='stop'/>;
})()} })()}
</div> </div>

View File

@@ -3,7 +3,6 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { getAnnotationDistribution, DistributionDimension, DistributionData, DistributionItem, getAnnotatedSamples, AnnotatedSample } from '@/app/actions/annotation-stats'; import { getAnnotationDistribution, DistributionDimension, DistributionData, DistributionItem, getAnnotatedSamples, AnnotatedSample } from '@/app/actions/annotation-stats';
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts'; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
import WaveformPlayer from './WaveformPlayer';
interface AnnotationDistributionProps { interface AnnotationDistributionProps {
experimentId: number; experimentId: number;

View File

@@ -58,8 +58,8 @@ export default function SingleChoiceBinaryEntryView({
{/* Waveform player */} {/* Waveform player */}
<WaveformPlayer <WaveformPlayer
src={audioSrc} src={audioSrc}
durationMs={entry.durationMs}
onFullyPlayed={() => setFullyPlayed(true)} onFullyPlayed={() => setFullyPlayed(true)}
playMode='pause'
/> />
{/* Must-listen hint */} {/* Must-listen hint */}

View File

@@ -60,6 +60,7 @@ export default function SingleChoiceEntryView({
<WaveformPlayer <WaveformPlayer
src={audioSrc} src={audioSrc}
onFullyPlayed={() => setFullyPlayed(true)} onFullyPlayed={() => setFullyPlayed(true)}
playMode='pause'
/> />
{/* Must-listen hint */} {/* Must-listen hint */}

View File

@@ -83,6 +83,7 @@ export default function CalibrationEntryView({
<WaveformPlayer <WaveformPlayer
src={audioSrc} src={audioSrc}
onFullyPlayed={() => setFullyPlayed(true)} onFullyPlayed={() => setFullyPlayed(true)}
playMode='pause'
/> />
{/* Must-listen hint */} {/* Must-listen hint */}

View File

@@ -517,7 +517,7 @@ export default function DatasetEntriesList({
{(() => { {(() => {
const fileExtension = entry.fileName.substring(entry.fileName.lastIndexOf('.')); const fileExtension = entry.fileName.substring(entry.fileName.lastIndexOf('.'));
const src = `/public/datasets/${datasetId}/${entry.externalId}${fileExtension}`; const src = `/public/datasets/${datasetId}/${entry.externalId}${fileExtension}`;
return <WaveformPlayer src={src} showWaveform={false} />; return <WaveformPlayer src={src} showWaveform={false} playMode='stop' />;
})()} })()}
</td> </td>
<td className="py-3 px-4"> <td className="py-3 px-4">

View File

@@ -168,7 +168,7 @@ export default function ParticipantDetailView({ participant, experimentId }: Par
{(() => { {(() => {
const fileExtension = selectedAnnotation.fileName.substring(selectedAnnotation.fileName.lastIndexOf('.')); const fileExtension = selectedAnnotation.fileName.substring(selectedAnnotation.fileName.lastIndexOf('.'));
const src = `/public/datasets/${selectedAnnotation.datasetId}/${selectedAnnotation.externalId}${fileExtension}`; const src = `/public/datasets/${selectedAnnotation.datasetId}/${selectedAnnotation.externalId}${fileExtension}`;
return <WaveformPlayer src={src} showWaveform={false} />; return <WaveformPlayer src={src} showWaveform={false} playMode='stop' />;
})()} })()}
</div> </div>
</div> </div>

View File

@@ -13,6 +13,7 @@ interface WaveformPlayerProps {
// Display options // Display options
showWaveform?: boolean; // defaults to true; if false, shows simple player button showWaveform?: boolean; // defaults to true; if false, shows simple player button
playMode?: 'pause' | 'stop'; // defaults to 'pause'; determines play/pause vs play/stop behavior
} }
const BAR_COUNT = 120; const BAR_COUNT = 120;
@@ -22,6 +23,7 @@ export default function WaveformPlayer({
onPlay, onPlay,
onFullyPlayed, onFullyPlayed,
showWaveform = true, showWaveform = true,
playMode = 'pause',
}: WaveformPlayerProps) { }: WaveformPlayerProps) {
const { currentAudioId, setCurrentAudio } = useAudio(); const { currentAudioId, setCurrentAudio } = useAudio();
@@ -74,11 +76,14 @@ export default function WaveformPlayer({
if (!audio) return; if (!audio) return;
setCurrentAudio(playerId); setCurrentAudio(playerId);
onPlay?.(); onPlay?.();
// Only reset to start if in stop mode; in pause mode, resume from paused position
if (playMode === 'stop') {
audio.currentTime = 0; audio.currentTime = 0;
}
audio.play(); audio.play();
setIsPlaying(true); setIsPlaying(true);
rafRef.current = requestAnimationFrame(tick); rafRef.current = requestAnimationFrame(tick);
}, [playerId, onPlay, tick]); }, [playerId, onPlay, tick, playMode]);
const handlePause = useCallback(() => { const handlePause = useCallback(() => {
const audio = audioRef.current; const audio = audioRef.current;
@@ -190,19 +195,25 @@ export default function WaveformPlayer({
onEnded={handleEnded} onEnded={handleEnded}
/> />
<button <button
onClick={isPlaying ? handleStop : handlePlay} onClick={isPlaying ? (playMode === 'stop' ? handleStop : handlePause) : handlePlay}
className={`inline-flex items-center justify-center w-8 h-8 rounded-full transition-colors ${ className={`inline-flex items-center justify-center w-8 h-8 rounded-full transition-colors ${
isPlaying isPlaying
? 'bg-red-500 hover:bg-red-600 text-white' ? 'bg-red-500 hover:bg-red-600 text-white'
: 'bg-blue-500 hover:bg-blue-600 text-white' : 'bg-blue-500 hover:bg-blue-600 text-white'
}`} }`}
title={isPlaying ? 'Stop' : 'Play'} title={isPlaying ? (playMode === 'stop' ? 'Stop' : 'Pause') : 'Play'}
> >
{isPlaying ? ( {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"> <svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<rect x="6" y="4" width="2" height="12" /> <rect x="6" y="4" width="2" height="12" />
<rect x="12" y="4" width="2" height="12" /> <rect x="12" y="4" width="2" height="12" />
</svg> </svg>
)
) : ( ) : (
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20"> <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" /> <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" />
@@ -226,19 +237,25 @@ export default function WaveformPlayer({
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
{/* Play / Pause button */} {/* Play / Pause button */}
<button <button
onClick={isPlaying ? handlePause : handlePlay} 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 ${ className={`flex-shrink-0 inline-flex items-center justify-center w-9 h-9 rounded-full transition-colors ${
isPlaying isPlaying
? 'bg-red-500 hover:bg-red-600 text-white' ? 'bg-red-500 hover:bg-red-600 text-white'
: 'bg-blue-500 hover:bg-blue-600 text-white' : 'bg-blue-500 hover:bg-blue-600 text-white'
}`} }`}
title={isPlaying ? 'Pause' : 'Abspielen'} title={isPlaying ? (playMode === 'stop' ? 'Stop' : 'Pause') : 'Abspielen'}
> >
{isPlaying ? ( {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"> <svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
<rect x="5" y="4" width="3" height="12" rx="1" /> <rect x="5" y="4" width="3" height="12" rx="1" />
<rect x="12" y="4" width="3" height="12" rx="1" /> <rect x="12" y="4" width="3" height="12" rx="1" />
</svg> </svg>
)
) : ( ) : (
<svg className="w-4 h-4" fill="currentColor" viewBox="0 0 20 20"> <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" /> <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" />