veza/apps/web/src/components/player/audio-player/AudioPlayer.tsx
senke 0a29c544af fix(web): resolve all 568 TypeScript errors — tsc --noEmit now passes with zero errors
Major categories fixed:
- TS6133 (188): Remove unused imports (React, icons, types) and variables
- TS2322 (222): Fix type mismatches in stories (satisfies Meta -> const meta: Meta),
  add nullish coalescing for optional values, fix component prop types
- TS2345 (43): Fix argument type mismatches with proper null checks and type narrowing
- TS2741 (21): Add missing required properties to mock/story data
- TS2339 (19): Fix property access on incorrect types, add type guards
- TS2353 (13): Remove extra properties from object literals or extend interfaces
- TS2352 (11): Fix type conversion chains
- TS2307 (9): Fix import paths and module references
- Other (42): Fix implicit any, possibly undefined, export declarations

Vite build and tsc --noEmit both pass cleanly.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-13 00:32:08 +01:00

128 lines
4.1 KiB
TypeScript

import { useRef, useState, useCallback } from 'react';
import { usePlayerStore } from '@/features/player/store/playerStore';
import { Button } from '@/components/ui/button';
import { Tooltip } from '@/components/ui/tooltip';
import { useTranslation } from '@/hooks/useTranslation';
import { List } from 'lucide-react';
import { QueuePanel } from '../QueuePanel';
import { useAudioPlayerEffects } from './useAudioPlayerEffects';
import { AudioPlayerTrackInfo } from './AudioPlayerTrackInfo';
import { AudioPlayerControls } from './AudioPlayerControls';
import { AudioPlayerProgress } from './AudioPlayerProgress';
import { AudioPlayerVolume } from './AudioPlayerVolume';
import type { AudioPlayerProps as Props } from './types';
export function AudioPlayer(_props: Props = {}) {
const audioRef = useRef<HTMLAudioElement>(null);
const { handlePlayPause } = useAudioPlayerEffects(audioRef);
const {
currentTrack,
isPlaying,
currentTime,
duration,
volume,
muted,
repeat,
shuffle,
setCurrentTime,
setVolume,
toggleMute,
toggleShuffle,
setRepeat,
next,
previous,
} = usePlayerStore();
const { t } = useTranslation();
const [showQueue, setShowQueue] = useState(false);
const handleSeek = useCallback(
(value: number[]) => {
const audio = audioRef.current;
const seekTime = value[0] ?? 0;
if (audio) {
audio.currentTime = seekTime;
setCurrentTime(seekTime);
}
},
[setCurrentTime],
);
const handleVolumeChange = useCallback((value: number[]) => setVolume(value[0] ?? 0), [setVolume]);
const handleRepeatCycle = useCallback(() => {
const modes: Array<'off' | 'track' | 'playlist'> = ['off', 'track', 'playlist'];
const currentIndex = modes.indexOf(repeat);
setRepeat(modes[(currentIndex + 1) % modes.length] ?? 'off');
}, [repeat, setRepeat]);
if (!currentTrack) {
return null;
}
return (
<>
<audio ref={audioRef} preload="metadata" />
<div className="fixed bottom-0 left-0 right-0 bg-background/95 backdrop-blur-md border-t border-border z-50">
{/* Full-width progress bar at the very top of the player */}
<AudioPlayerProgress
currentTime={currentTime}
duration={duration}
onSeek={handleSeek}
variant="minimal"
/>
<div className="container mx-auto px-4 py-2">
<div className="grid grid-cols-3 items-center gap-4">
{/* Left: Track info */}
<AudioPlayerTrackInfo track={currentTrack} />
{/* Center: Playback controls */}
<AudioPlayerControls
isPlaying={isPlaying}
shuffle={shuffle}
repeat={repeat}
onPlayPause={handlePlayPause}
onPrevious={previous}
onNext={next}
onToggleShuffle={toggleShuffle}
onRepeatCycle={handleRepeatCycle}
/>
{/* Right: Volume + Queue */}
<div className="flex items-center justify-end gap-2">
<AudioPlayerVolume
volume={volume}
muted={muted}
onVolumeChange={handleVolumeChange}
onToggleMute={toggleMute}
/>
<Tooltip
content={
showQueue
? t('player.hideQueue', 'Hide queue')
: t('player.showQueue', 'Show queue')
}
>
<Button
variant="ghost"
size="icon"
onClick={() => setShowQueue(!showQueue)}
className={showQueue ? 'text-primary' : ''}
aria-label={
showQueue
? t('player.hideQueue', 'Hide queue')
: t('player.showQueue', 'Show queue')
}
>
<List className="h-4 w-4" />
</Button>
</Tooltip>
</div>
</div>
</div>
</div>
{showQueue && <QueuePanel onClose={() => setShowQueue(false)} />}
</>
);
}