import { Head, Link, usePage } from '@inertiajs/react';
import {
ArrowRight,
Compass,
LayoutDashboard,
Radio,
Search,
UserPlus,
Users,
Video,
} from 'lucide-react';
import { useMemo, useState } from 'react';
import { LanguageSwitcher } from '@/components/language-switcher';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { useTranslation } from '@/lib/translations';
import { cn } from '@/lib/utils';
import { dashboard, home, login, register } from '@/routes';
import { show as showChannel } from '@/routes/channels';
import type { ChannelCard, ChannelCategory } from '@/types';
type Props = {
canRegister: boolean;
liveChannels: ChannelCard[];
categories: ChannelCategory[];
};
export default function Welcome({
canRegister,
liveChannels,
categories,
}: Props) {
const { auth } = usePage().props;
const { t } = useTranslation();
const [query, setQuery] = useState('');
const [category, setCategory] = useState('all');
const featuredChannel = liveChannels[0] ?? null;
const filteredChannels = useMemo(() => {
const normalizedQuery = query.trim().toLowerCase();
return liveChannels.filter((channel) => {
const matchesCategory =
category === 'all' || channel.category?.value === category;
const matchesQuery =
normalizedQuery === '' ||
channel.display_name.toLowerCase().includes(normalizedQuery) ||
channel.broadcast?.title
.toLowerCase()
.includes(normalizedQuery) ||
channel.owner.name.toLowerCase().includes(normalizedQuery);
return matchesCategory && matchesQuery;
});
}, [category, liveChannels, query]);
return (
<>
{featuredChannel ? (
{featuredChannel.broadcast
?.title ??
featuredChannel.display_name}
{featuredChannel.description ??
t(
':channel is live now with :count viewers.',
{
channel:
featuredChannel.display_name,
count: featuredChannel.viewer_count,
},
)}
{featuredChannel.display_name}
{featuredChannel.category
?.label ??
t('Uncategorized')}
{t(':count viewers', {
count: featuredChannel.viewer_count,
})}
{t('Watch')}
) : (
{t('The live floor is quiet')}
{t(
'Creators can prepare a broadcast in the studio and go live from OBS when ready.',
)}
)}
{t('Live signal')}
{t('Public channels broadcasting now')}
{liveChannels.length}
total + item.viewer_count,
0,
),
)}
/>
{auth.user
? t('Open studio')
: t('Start a channel')}
{t('Live directory')}
{t(
'Browse active channels by category or title.',
)}
{filteredChannels.length > 0 ? (
{filteredChannels.map((channel) => (
))}
) : (
{t('No streams match this view')}
{t(
'Clear search or switch categories.',
)}
)}
>
);
}
function StreamCard({ channel }: { channel: ChannelCard }) {
const { t } = useTranslation();
return (
{channel.broadcast?.title ?? channel.display_name}
{t(':channel by :owner', {
channel: channel.display_name,
owner: channel.owner.name,
})}
{channel.category?.label ?? t('Uncategorized')}
{t(':count viewers', {
count: channel.viewer_count,
})}
);
}
function Thumbnail({
channel,
className,
label = 'Live preview',
}: {
channel: ChannelCard;
className?: string;
label?: string;
}) {
const { t } = useTranslation();
return (
{channel.thumbnail_url ? (
) : (
)}
);
}
function LiveBadge({ compact = false }: { compact?: boolean }) {
const { t } = useTranslation();
return (
{t('LIVE')}
);
}
function Metric({
icon: Icon,
label,
value,
}: {
icon: typeof Radio;
label: string;
value: string;
}) {
return (
);
}