- Connect MiniPlayer play/pause buttons to audioService - Improve audio context management with fallback creation - Fix state synchronization with interval-based readiness checks - Add error handling and user feedback for playback issues - Enhance mobile browser support with better audio context handling Fixes playback issues in SongView where controls were not working and state synchronization between UI and player was unreliable.
123 lines
3.1 KiB
TypeScript
Executable File
123 lines
3.1 KiB
TypeScript
Executable File
import { usePlayerStore } from "../stores/playerStore";
|
|
import { useNavigate } from "react-router-dom";
|
|
import { audioService } from "../services/audioService";
|
|
|
|
export function MiniPlayer() {
|
|
const { currentSongId, currentBandId, isPlaying, currentTime, duration } = usePlayerStore();
|
|
const navigate = useNavigate();
|
|
|
|
if (!currentSongId || !currentBandId) {
|
|
return null;
|
|
}
|
|
|
|
const formatTime = (seconds: number) => {
|
|
const m = Math.floor(seconds / 60);
|
|
const s = Math.floor(seconds % 60);
|
|
return `${m}:${String(s).padStart(2, "0")}`;
|
|
};
|
|
|
|
const progress = duration > 0 ? (currentTime / duration) * 100 : 0;
|
|
|
|
return (
|
|
<div
|
|
style={
|
|
{
|
|
position: "fixed",
|
|
bottom: 0,
|
|
left: 0,
|
|
right: 0,
|
|
background: "#18181e",
|
|
borderTop: "1px solid rgba(255,255,255,0.06)",
|
|
padding: "8px 16px",
|
|
zIndex: 999,
|
|
display: "flex",
|
|
alignItems: "center",
|
|
gap: 12,
|
|
}
|
|
}
|
|
>
|
|
<button
|
|
onClick={() => navigate(`/bands/${currentBandId}/songs/${currentSongId}`)}
|
|
style={
|
|
{
|
|
background: "transparent",
|
|
border: "none",
|
|
color: "white",
|
|
cursor: "pointer",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
gap: 8,
|
|
padding: "4px 8px",
|
|
borderRadius: 4,
|
|
}
|
|
}
|
|
title="Go to song"
|
|
>
|
|
<svg width="16" height="16" viewBox="0 0 14 14" fill="currentColor">
|
|
<path d="M3 2l9 5-9 5V2z" />
|
|
</svg>
|
|
<span style={{ fontSize: 12, color: "rgba(255,255,255,0.8)" }}>
|
|
Now Playing
|
|
</span>
|
|
</button>
|
|
|
|
<div
|
|
style={
|
|
{
|
|
flex: 1,
|
|
height: 4,
|
|
background: "rgba(255,255,255,0.1)",
|
|
borderRadius: 2,
|
|
overflow: "hidden",
|
|
cursor: "pointer",
|
|
}
|
|
}
|
|
>
|
|
<div
|
|
style={
|
|
{
|
|
width: `${progress}%`,
|
|
height: "100%",
|
|
background: "#e8a22a",
|
|
transition: "width 0.1s linear",
|
|
}
|
|
}
|
|
/>
|
|
</div>
|
|
|
|
<div style={{ fontSize: 11, color: "rgba(255,255,255,0.6)", minWidth: 60, textAlign: "right" }}>
|
|
{formatTime(currentTime)} / {formatTime(duration)}
|
|
</div>
|
|
|
|
<button
|
|
onClick={() => {
|
|
if (isPlaying) {
|
|
audioService.pause();
|
|
} else {
|
|
audioService.play();
|
|
}
|
|
}}
|
|
style={
|
|
{
|
|
background: "transparent",
|
|
border: "none",
|
|
color: "white",
|
|
cursor: "pointer",
|
|
padding: "4px",
|
|
}
|
|
}
|
|
title={isPlaying ? "Pause" : "Play"}
|
|
>
|
|
{isPlaying ? (
|
|
<svg width="16" height="16" viewBox="0 0 14 14" fill="currentColor">
|
|
<path d="M4 2h2v10H4zm4 0h2v10h-2z" />
|
|
</svg>
|
|
) : (
|
|
<svg width="16" height="16" viewBox="0 0 14 14" fill="currentColor">
|
|
<path d="M3 2l9 5-9 5V2z" />
|
|
</svg>
|
|
)}
|
|
</button>
|
|
</div>
|
|
);
|
|
} |