mirror of
https://github.com/SrIzan10/hctv.git
synced 2026-06-06 00:56:56 +00:00
refactor(ui): mobile friendly landing
This commit is contained in:
@@ -48,7 +48,7 @@ export default async function Home() {
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-4 md:p-6">
|
||||
<div className="p-3 md:p-6">
|
||||
<StreamGrid liveStreams={liveStreams} offlineStreams={offlineStreams} />
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -25,7 +25,7 @@ export default function StreamGrid({ liveStreams, offlineStreams }: StreamGridPr
|
||||
const [featured, ...rest] = sorted;
|
||||
|
||||
return (
|
||||
<div className="space-y-10">
|
||||
<div className="space-y-8 md:space-y-10">
|
||||
{!featured && (
|
||||
<div className="flex flex-col items-center gap-4 py-10 text-center">
|
||||
<ConfusedDino className="h-24 w-24 opacity-70" />
|
||||
@@ -45,7 +45,7 @@ export default function StreamGrid({ liveStreams, offlineStreams }: StreamGridPr
|
||||
{featured && (
|
||||
<section>
|
||||
<SectionHeading label="Featured" />
|
||||
<Link href={`/${featured.username}`} className="group block max-w-2xl">
|
||||
<Link href={`/${featured.username}`} className="group block w-full md:max-w-2xl">
|
||||
<div className="overflow-hidden rounded-xl border border-border bg-card shadow-sm transition-shadow duration-200 group-hover:shadow-md">
|
||||
<div className="relative aspect-video overflow-hidden bg-muted">
|
||||
<img
|
||||
@@ -54,28 +54,32 @@ export default function StreamGrid({ liveStreams, offlineStreams }: StreamGridPr
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/10 to-transparent" />
|
||||
<div className="absolute bottom-3 left-3 flex items-center gap-2">
|
||||
<div className="absolute bottom-2 left-2 flex items-center gap-1.5 md:bottom-3 md:left-3 md:gap-2">
|
||||
<LiveBadge />
|
||||
{featured.category && (
|
||||
<span className="rounded-full bg-black/60 px-2.5 py-0.5 text-xs font-medium text-white backdrop-blur-sm">
|
||||
<span className="rounded-full bg-black/60 px-2 py-0.5 text-[10px] font-medium text-white backdrop-blur-sm md:px-2.5 md:text-xs">
|
||||
{featured.category}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="absolute bottom-3 right-3">
|
||||
<div className="absolute bottom-2 right-2 md:bottom-3 md:right-3">
|
||||
<ViewerCount count={featured.viewers} />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-4 p-4">
|
||||
<Avatar className="h-10 w-10 shrink-0 ring-2 ring-primary/30">
|
||||
<div className="flex items-start gap-3 p-3 md:gap-4 md:p-4">
|
||||
<Avatar className="h-9 w-9 shrink-0 ring-2 ring-primary/30 md:h-10 md:w-10">
|
||||
<AvatarImage src={featured.channel.pfpUrl} alt={featured.channel.name} />
|
||||
<AvatarFallback className="text-sm font-semibold">
|
||||
{featured.channel.name.slice(0, 2).toUpperCase()}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate font-semibold leading-snug">{featured.title}</p>
|
||||
<p className="mt-0.5 text-sm text-muted-foreground">{featured.channel.name}</p>
|
||||
<p className="truncate text-sm font-semibold leading-snug md:text-base">
|
||||
{featured.title}
|
||||
</p>
|
||||
<p className="mt-0.5 text-xs text-muted-foreground md:text-sm">
|
||||
{featured.channel.name}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -86,7 +90,7 @@ export default function StreamGrid({ liveStreams, offlineStreams }: StreamGridPr
|
||||
{rest.length > 0 && (
|
||||
<section>
|
||||
<SectionHeading label="Live now" count={rest.length} />
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||
<div className="grid grid-cols-2 gap-3 md:gap-4 lg:grid-cols-3 xl:grid-cols-4">
|
||||
{rest.map((stream) => (
|
||||
<StreamCard key={stream.id} stream={stream} />
|
||||
))}
|
||||
@@ -97,20 +101,20 @@ export default function StreamGrid({ liveStreams, offlineStreams }: StreamGridPr
|
||||
{offlineStreams.length > 0 && (
|
||||
<section>
|
||||
<SectionHeading label="Offline channels" count={offlineStreams.length} />
|
||||
<div className="px-8">
|
||||
<div className="relative">
|
||||
<Carousel opts={{ align: 'start', dragFree: true }}>
|
||||
<CarouselContent>
|
||||
{offlineStreams.map((stream) => (
|
||||
<CarouselItem
|
||||
key={stream.id}
|
||||
className="basis-1/2 sm:basis-1/3 md:basis-1/4 lg:basis-1/5 xl:basis-1/6"
|
||||
className="basis-1/3 sm:basis-1/4 md:basis-1/5 lg:basis-1/6 xl:basis-1/8"
|
||||
>
|
||||
<OfflineCard stream={stream} />
|
||||
</CarouselItem>
|
||||
))}
|
||||
</CarouselContent>
|
||||
<CarouselPrevious />
|
||||
<CarouselNext />
|
||||
<CarouselPrevious className="hidden md:flex" />
|
||||
<CarouselNext className="hidden md:flex" />
|
||||
</Carousel>
|
||||
</div>
|
||||
</section>
|
||||
@@ -130,27 +134,29 @@ function StreamCard({ stream }: { stream: StreamWithChannel }) {
|
||||
className="h-full w-full object-cover"
|
||||
/>
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent" />
|
||||
<div className="absolute bottom-2 left-2">
|
||||
<div className="absolute bottom-1.5 left-1.5 md:bottom-2 md:left-2">
|
||||
<LiveBadge small />
|
||||
</div>
|
||||
<div className="absolute bottom-2 right-2">
|
||||
<div className="absolute bottom-1.5 right-1.5 md:bottom-2 md:right-2">
|
||||
<ViewerCount count={stream.viewers} small />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-3 p-3">
|
||||
<Avatar className="h-8 w-8 shrink-0 ring-1 ring-primary/20">
|
||||
<div className="flex items-start gap-2 p-2 md:gap-3 md:p-3">
|
||||
<Avatar className="h-7 w-7 shrink-0 ring-1 ring-primary/20 md:h-8 md:w-8">
|
||||
<AvatarImage src={stream.channel.pfpUrl} alt={stream.channel.name} />
|
||||
<AvatarFallback className="text-xs">
|
||||
<AvatarFallback className="text-[10px]">
|
||||
{stream.channel.name.slice(0, 2).toUpperCase()}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-sm font-medium leading-snug">{stream.title}</p>
|
||||
<p className="truncate text-xs text-muted-foreground">{stream.channel.name}</p>
|
||||
<p className="truncate text-xs font-medium leading-snug md:text-sm">{stream.title}</p>
|
||||
<p className="truncate text-[10px] text-muted-foreground md:text-xs">
|
||||
{stream.channel.name}
|
||||
</p>
|
||||
{stream.category && (
|
||||
<Badge
|
||||
variant="secondary"
|
||||
className="mt-1.5 rounded-full px-2 py-0 text-[10px] font-medium"
|
||||
className="mt-1 rounded-full px-1.5 py-0 text-[9px] font-medium md:mt-1.5 md:px-2 md:text-[10px]"
|
||||
>
|
||||
{stream.category}
|
||||
</Badge>
|
||||
@@ -165,17 +171,19 @@ function StreamCard({ stream }: { stream: StreamWithChannel }) {
|
||||
function OfflineCard({ stream }: { stream: StreamWithChannel }) {
|
||||
return (
|
||||
<Link href={`/${stream.username}`} className="group block">
|
||||
<div className="flex flex-col items-center gap-2 rounded-lg p-3 transition-colors duration-150 hover:bg-muted/50">
|
||||
<div className="flex flex-col items-center gap-1.5 rounded-lg p-2 transition-colors duration-150 hover:bg-muted/50 md:gap-2 md:p-3">
|
||||
<div className="relative">
|
||||
<Avatar className="h-16 w-16 ring-2 ring-border transition-colors duration-150 group-hover:ring-border/60">
|
||||
<Avatar className="h-12 w-12 ring-2 ring-border transition-colors duration-150 group-hover:ring-border/60 md:h-16 md:w-16">
|
||||
<AvatarImage src={stream.channel.pfpUrl} alt={stream.channel.name} />
|
||||
<AvatarFallback className="text-lg font-semibold">
|
||||
<AvatarFallback className="text-base font-semibold md:text-lg">
|
||||
{stream.channel.name.slice(0, 2).toUpperCase()}
|
||||
</AvatarFallback>
|
||||
</Avatar>
|
||||
<span className="absolute -bottom-0.5 -right-0.5 h-3.5 w-3.5 rounded-full border-2 border-background bg-muted-foreground/40" />
|
||||
<span className="absolute -bottom-0.5 -right-0.5 h-3 w-3 rounded-full border-2 border-background bg-muted-foreground/40 md:h-3.5 md:w-3.5" />
|
||||
</div>
|
||||
<p className="w-full truncate text-center text-xs font-medium">{stream.channel.name}</p>
|
||||
<p className="w-full truncate text-center text-[10px] font-medium md:text-xs">
|
||||
{stream.channel.name}
|
||||
</p>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
@@ -206,7 +214,7 @@ function ViewerCount({ count, small }: { count: number; small?: boolean }) {
|
||||
function SectionHeading({ label, count }: { label: string; count?: number }) {
|
||||
return (
|
||||
<div className="mb-3 flex items-center gap-2">
|
||||
<h2 className="pb-0 text-base font-semibold tracking-tight">{label}</h2>
|
||||
<h2 className="pb-0 text-sm font-semibold tracking-tight md:text-base">{label}</h2>
|
||||
{count !== undefined && (
|
||||
<span className="rounded-full bg-primary/10 px-2 py-0.5 text-xs font-medium text-primary">
|
||||
{count}
|
||||
|
||||
Reference in New Issue
Block a user