Enhance AirPage and ChannelPlayer components: add error handling for unavailable streams, implement retry functionality, and update translations for offline states. Refactor ChannelPlayer to accept an onUnavailable callback for better error management.
This commit is contained in:
@@ -1,10 +1,11 @@
|
||||
import { useQuery } from '@tanstack/react-query'
|
||||
import { useEffect, useMemo, useState } from 'react'
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { Radio } from 'lucide-react'
|
||||
import { Radio, RotateCw } from 'lucide-react'
|
||||
import type { ScheduleEntryDto } from '@/shared/api/types'
|
||||
import { cn } from '@/shared/lib/cn'
|
||||
import { Badge } from '@/shared/ui/badge'
|
||||
import { Button } from '@/shared/ui/button'
|
||||
import { ChannelPlayer } from './ChannelPlayer'
|
||||
import { getEpg, listChannels, watchChannel } from './api'
|
||||
|
||||
@@ -16,6 +17,14 @@ export function AirPage() {
|
||||
const { t } = useTranslation()
|
||||
const [selected, setSelected] = useState<string | null>(null)
|
||||
const [watchReady, setWatchReady] = useState(false)
|
||||
const [playerError, setPlayerError] = useState(false)
|
||||
const [attempt, setAttempt] = useState(0)
|
||||
|
||||
const handleUnavailable = useCallback(() => setPlayerError(true), [])
|
||||
const retry = () => {
|
||||
setPlayerError(false)
|
||||
setAttempt((a) => a + 1)
|
||||
}
|
||||
|
||||
const { data: channels, isLoading } = useQuery({
|
||||
queryKey: ['air', 'channels'],
|
||||
@@ -29,6 +38,7 @@ export function AirPage() {
|
||||
useEffect(() => {
|
||||
if (!selected) return
|
||||
setWatchReady(false)
|
||||
setPlayerError(false)
|
||||
let cancelled = false
|
||||
void watchChannel(selected)
|
||||
.then(() => {
|
||||
@@ -87,7 +97,25 @@ export function AirPage() {
|
||||
|
||||
<div className="flex flex-col gap-4">
|
||||
{selected && watchReady ? (
|
||||
<ChannelPlayer slug={selected} />
|
||||
playerError ? (
|
||||
<div className="crt-panel flex aspect-video w-full flex-col items-center justify-center gap-3 rounded-md text-center">
|
||||
<Radio className="h-10 w-10 text-muted-foreground" strokeWidth={1} />
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="font-medium">{t('air.offline')}</p>
|
||||
<p className="text-sm text-muted-foreground">{t('air.offlineHint')}</p>
|
||||
</div>
|
||||
<Button size="sm" variant="outline" onClick={retry}>
|
||||
<RotateCw className="h-4 w-4" />
|
||||
{t('air.retry')}
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<ChannelPlayer
|
||||
key={`${selected}-${attempt}`}
|
||||
slug={selected}
|
||||
onUnavailable={handleUnavailable}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<div className="aspect-video w-full animate-pulse rounded-md border border-border bg-black" />
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user