import { useEffect, useState, type MouseEvent } from "react"; import { formatDuration, formatLength } from "../api"; import type { LoopMode, PlayerState, VoiceChannel } from "../types"; interface Props { state: PlayerState; position: number; canControl: boolean; voiceChannels: VoiceChannel[]; yourVoiceChannel: VoiceChannel | null; requireListener: boolean; /** Hidden entirely when the bot was built without video support. */ videoAvailable: boolean; onAction(action: string, payload?: Record): void; onSeek(seconds: number): void; onJoin(channelId: string | null): void; } const STATUS_LABEL: Record = { idle: "ожидание", connecting: "подключение", buffering: "буферизация", playing: "играет", paused: "пауза", }; const LOOP_LABEL: Record = { off: "🔁 повтор", track: "🔂 трек", queue: "🔁 очередь", }; export function NowPlaying({ state, position, canControl, voiceChannels, yourVoiceChannel, requireListener, videoAvailable, onAction, onSeek, onJoin, }: Props) { const [volume, setVolume] = useState(state.volume); const [channelId, setChannelId] = useState( state.voiceChannelId ?? yourVoiceChannel?.id ?? voiceChannels[0]?.id ?? "", ); useEffect(() => setVolume(state.volume), [state.volume]); useEffect(() => { if (state.voiceChannelId) setChannelId(state.voiceChannelId); }, [state.voiceChannelId]); const track = state.current; const duration = track?.duration ?? 0; const ratio = duration > 0 ? Math.min(1, position / duration) : 0; const isPlaying = state.status === "playing" || state.status === "buffering"; function seekFromClick(event: MouseEvent) { if (!track || duration <= 0 || !canControl) return; const rect = event.currentTarget.getBoundingClientRect(); const fraction = (event.clientX - rect.left) / rect.width; onSeek(Math.max(0, Math.min(duration - 1, fraction * duration))); } const nextLoop: LoopMode = state.loop === "off" ? "track" : state.loop === "track" ? "queue" : "off"; return (

Сейчас играет{" "} {STATUS_LABEL[state.status]}

{track?.thumbnail ? ( ) : (
🎵
)}
{track ? ( /^https?:/.test(track.url) ? ( {track.title} ) : ( track.title ) ) : ( "Тишина" )}
{track ? [track.author, `запросил ${track.requestedBy.username}`].filter(Boolean).join(" · ") : "Очередь пуста — найдите что-нибудь слева"}
{formatDuration(track ? position : 0)} {track ? formatLength(track) : "0:00"}
{videoAvailable && ( )}
🔊 setVolume(Number(event.target.value))} onMouseUp={() => onAction("volume", { volume })} onTouchEnd={() => onAction("volume", { volume })} /> {volume}%
{requireListener ? ( // Playback follows the listener, so there is nothing to choose here: // the bot joins the channel you are sitting in. {yourVoiceChannel ? ( <> Вы в канале {yourVoiceChannel.name} ) : ( "Вы не в голосовом канале" )} {state.voiceChannelName ? ` · бот в «${state.voiceChannelName}»` : " · бот не в канале"} ) : ( )}
); }