import { Head, Link, router, useForm, usePage } from '@inertiajs/react'; import { Heart, LogIn, MessageSquare, Radio, Send, Users, Video, } from 'lucide-react'; import type { FormEvent } from 'react'; import { useState } from 'react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger, } from '@/components/ui/sheet'; import { VideoPlayer } from '@/components/video-player'; import { cn } from '@/lib/utils'; import { home, login } from '@/routes'; import { follow, unfollow } from '@/routes/channels'; import { store as storeChat } from '@/routes/channels/chat'; import type { BroadcastSummary, ChannelDetail, ChatMessage, VodSummary, } from '@/types'; type Props = { channel: ChannelDetail; currentBroadcast: BroadcastSummary | null; vods: VodSummary[]; chatMessages: ChatMessage[]; isFollowing: boolean; }; export default function ChannelShow({ channel, currentBroadcast, vods, chatMessages, isFollowing, }: Props) { const { auth } = usePage().props; const [chatOpen, setChatOpen] = useState(false); const chatForm = useForm({ body: '' }); function submitChat(event: FormEvent) { event.preventDefault(); chatForm.submit(storeChat(channel.slug), { preserveScroll: true, onSuccess: () => chatForm.reset('body'), }); } function toggleFollow() { if (isFollowing) { router.delete(unfollow(channel.slug), { preserveScroll: true, }); return; } router.post(follow(channel.slug), undefined, { preserveScroll: true, }); } return ( <>
{currentBroadcast?.hls_url ? ( ) : ( )}
{channel.category && ( {channel.category.name} )}

{currentBroadcast?.title ?? channel.display_name}

{channel.display_name} {channel.viewer_count} viewers {channel.followers_count} followers
{channel.description && (

{channel.description}

)}
Live chat {auth.user ? ( ) : ( )}
{vods.length} available
{vods.length > 0 ? (
{vods.map((vod) => ( ))}
) : (
No public recordings yet.
)}
); } ChannelShow.layout = { breadcrumbs: [ { title: 'Channel', href: home(), }, ], }; function OfflinePlayer({ channel }: { channel: ChannelDetail }) { return (
{channel.banner_url ? ( ) : null}
{channel.display_name} is offline

Recent recordings remain available below when the creator publishes VODs.

); } function ChatPanel({ channel, authUser, messages, form, onSubmit, compact = false, }: { channel: ChannelDetail; authUser: boolean; messages: ChatMessage[]; form: ReturnType>; onSubmit: (event: FormEvent) => void; compact?: boolean; }) { return (

Live chat

{messages.map((message) => (
{message.user.name} {message.body}
))} {messages.length === 0 && (
{channel.is_live ? 'No messages yet.' : 'Chat appears when the channel is live.'}
)}
{authUser ? (
form.setData('body', event.target.value) } placeholder={ channel.is_live ? 'Send a message' : 'Channel is offline' } disabled={!channel.is_live} />
) : ( )}
); } function VodCard({ vod }: { vod: VodSummary }) { return (
{vod.title}
Expires{' '} {vod.expires_at ? new Date(vod.expires_at).toLocaleDateString() : 'later'}
{vod.playback_url ? ( ) : (
Processing recording
)}
); } function LiveState({ channel, compact = false, }: { channel: ChannelDetail; compact?: boolean; }) { if (channel.is_live) { return ( LIVE ); } return ( Offline ); } function Avatar({ channel }: { channel: ChannelDetail }) { return (
{channel.avatar_url ? ( ) : ( )}
); }