Enhance ChannelPlayer component: add volume control functionality with a slider, update mute logic to respect last volume level, and integrate volume translations for improved user experience.
This commit is contained in:
@@ -20,6 +20,7 @@ export function ChannelPlayer({
|
|||||||
const hlsRef = useRef<Hls | null>(null)
|
const hlsRef = useRef<Hls | null>(null)
|
||||||
const [playing, setPlaying] = useState(false)
|
const [playing, setPlaying] = useState(false)
|
||||||
const [muted, setMuted] = useState(true)
|
const [muted, setMuted] = useState(true)
|
||||||
|
const [volume, setVolume] = useState(1)
|
||||||
|
|
||||||
const seekToLiveEdge = useCallback(() => {
|
const seekToLiveEdge = useCallback(() => {
|
||||||
const video = videoRef.current
|
const video = videoRef.current
|
||||||
@@ -77,11 +78,26 @@ export function ChannelPlayer({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Установить громкость (0..1); 0 = mute, >0 запоминаем как последний уровень для «размьютить».
|
||||||
|
const applyVolume = (value: number) => {
|
||||||
|
const video = videoRef.current
|
||||||
|
if (!video) return
|
||||||
|
const clamped = Math.min(1, Math.max(0, value))
|
||||||
|
video.volume = clamped
|
||||||
|
video.muted = clamped === 0
|
||||||
|
setMuted(clamped === 0)
|
||||||
|
if (clamped > 0) setVolume(clamped)
|
||||||
|
}
|
||||||
|
|
||||||
const toggleMute = () => {
|
const toggleMute = () => {
|
||||||
const video = videoRef.current
|
const video = videoRef.current
|
||||||
if (!video) return
|
if (!video) return
|
||||||
video.muted = !video.muted
|
if (video.muted || video.volume === 0) {
|
||||||
setMuted(video.muted)
|
applyVolume(volume > 0 ? volume : 0.5)
|
||||||
|
} else {
|
||||||
|
video.muted = true
|
||||||
|
setMuted(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const toggleFullscreen = () => {
|
const toggleFullscreen = () => {
|
||||||
@@ -110,9 +126,21 @@ export function ChannelPlayer({
|
|||||||
<button type="button" onClick={togglePlay} aria-label="play/pause">
|
<button type="button" onClick={togglePlay} aria-label="play/pause">
|
||||||
{playing ? <Pause className="h-5 w-5" /> : <Play className="h-5 w-5" />}
|
{playing ? <Pause className="h-5 w-5" /> : <Play className="h-5 w-5" />}
|
||||||
</button>
|
</button>
|
||||||
<button type="button" onClick={toggleMute} aria-label="mute">
|
<div className="flex items-center gap-2">
|
||||||
{muted ? <VolumeX className="h-5 w-5" /> : <Volume2 className="h-5 w-5" />}
|
<button type="button" onClick={toggleMute} aria-label="mute">
|
||||||
</button>
|
{muted ? <VolumeX className="h-5 w-5" /> : <Volume2 className="h-5 w-5" />}
|
||||||
|
</button>
|
||||||
|
<input
|
||||||
|
type="range"
|
||||||
|
min={0}
|
||||||
|
max={1}
|
||||||
|
step={0.05}
|
||||||
|
value={muted ? 0 : volume}
|
||||||
|
onChange={(e) => applyVolume(Number(e.target.value))}
|
||||||
|
aria-label={t('air.volume')}
|
||||||
|
className="h-1 w-20 cursor-pointer accent-emerald-400"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<span className="ml-auto flex items-center gap-1.5 text-xs font-medium uppercase tracking-wide text-red-500">
|
<span className="ml-auto flex items-center gap-1.5 text-xs font-medium uppercase tracking-wide text-red-500">
|
||||||
<span className="h-2 w-2 animate-pulse rounded-full bg-red-500" />
|
<span className="h-2 w-2 animate-pulse rounded-full bg-red-500" />
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ const resources = {
|
|||||||
bumper: 'Заставка',
|
bumper: 'Заставка',
|
||||||
episode: 'Серия',
|
episode: 'Серия',
|
||||||
live: 'В эфире',
|
live: 'В эфире',
|
||||||
|
volume: 'Громкость',
|
||||||
noChannels: 'Пока нет доступных каналов. Загляните позже.',
|
noChannels: 'Пока нет доступных каналов. Загляните позже.',
|
||||||
offline: 'Канал сейчас не в эфире',
|
offline: 'Канал сейчас не в эфире',
|
||||||
offlineHint: 'Нет расписания или контента. Загляните позже.',
|
offlineHint: 'Нет расписания или контента. Загляните позже.',
|
||||||
@@ -292,6 +293,7 @@ const resources = {
|
|||||||
ad: 'Ad',
|
ad: 'Ad',
|
||||||
bumper: 'Bumper',
|
bumper: 'Bumper',
|
||||||
episode: 'Episode',
|
episode: 'Episode',
|
||||||
|
volume: 'Volume',
|
||||||
live: 'Live',
|
live: 'Live',
|
||||||
noChannels: 'No channels available yet. Check back later.',
|
noChannels: 'No channels available yet. Check back later.',
|
||||||
offline: 'This channel is off the air',
|
offline: 'This channel is off the air',
|
||||||
|
|||||||
Reference in New Issue
Block a user