Skip to content

Commit 03f79f8

Browse files
committed
feat(home): rotate hero through trending movies every 15s, clamp
overview to 3 lines, and fix error boundary retry prop
1 parent c2fcfac commit 03f79f8

3 files changed

Lines changed: 57 additions & 40 deletions

File tree

app/page.tsx

Lines changed: 8 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -48,39 +48,21 @@ export default async function HomePage() {
4848
getOnTheAirTV(),
4949
]);
5050

51-
const featured = movieToMedia(trending[0]);
51+
const featuredItems = trending.map(movieToMedia);
5252

5353
return (
5454
<div className="flex flex-col">
55-
{featured && <Hero item={featured} />}
55+
{featuredItems.length > 0 && <Hero items={featuredItems} />}
5656

5757
<div className="-mt-16 relative z-10 flex flex-col gap-10 pb-16 max-w-7xl mx-auto w-full">
58-
<Carousel
59-
title="Trending Movies"
60-
items={trending.map(movieToMedia)}
61-
/>
62-
<Carousel
63-
title="Trending TV Shows"
64-
items={trendingTV.map(tvToMedia)}
65-
/>
58+
<Carousel title="Trending Movies" items={trending.map(movieToMedia)} />
59+
<Carousel title="Trending TV Shows" items={trendingTV.map(tvToMedia)} />
6660
<Carousel title="Popular Movies" items={popular.map(movieToMedia)} />
67-
<Carousel
68-
title="Popular TV Shows"
69-
items={popularTV.map(tvToMedia)}
70-
/>
71-
<Carousel
72-
title="Top Rated Movies"
73-
items={topRated.map(movieToMedia)}
74-
/>
75-
<Carousel
76-
title="Top Rated TV"
77-
items={topRatedTV.map(tvToMedia)}
78-
/>
61+
<Carousel title="Popular TV Shows" items={popularTV.map(tvToMedia)} />
62+
<Carousel title="Top Rated Movies" items={topRated.map(movieToMedia)} />
63+
<Carousel title="Top Rated TV" items={topRatedTV.map(tvToMedia)} />
7964
<Carousel title="Now Playing" items={nowPlaying.map(movieToMedia)} />
80-
<Carousel
81-
title="On The Air"
82-
items={onTheAirTV.map(tvToMedia)}
83-
/>
65+
<Carousel title="On The Air" items={onTheAirTV.map(tvToMedia)} />
8466
<Carousel title="Upcoming" items={upcoming.map(movieToMedia)} />
8567
<Carousel title="Action" items={action.map(movieToMedia)} />
8668
<Carousel title="Comedy" items={comedy.map(movieToMedia)} />

components/ExpandableOverview.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,15 @@ export default function ExpandableOverview({
2727
const [isTruncated, setIsTruncated] = useState(false);
2828

2929
useEffect(() => {
30-
setIsTruncated(wouldTruncate(text, fontKey, fontSize, containerWidth, lineHeight, maxLines));
31-
}, [text, fontKey, fontSize, containerWidth, lineHeight, maxLines]);
30+
setIsTruncated(
31+
wouldTruncate(text, fontKey, fontSize, containerWidth, lineHeight, 3),
32+
);
33+
}, [text, fontKey, fontSize, containerWidth, lineHeight]);
3234

3335
return (
3436
<div>
3537
<p
36-
className={`${className} ${!expanded && isTruncated ? `line-clamp-${maxLines}` : ""}`}
38+
className={`${className} ${!expanded && isTruncated ? "line-clamp-3" : ""}`}
3739
>
3840
{text}
3941
</p>

components/Hero.tsx

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
"use client";
2+
3+
import { useEffect, useMemo, useState } from "react";
14
import Image from "next/image";
25
import Link from "next/link";
36
import { Play, Star, Info } from "lucide-react";
@@ -7,19 +10,49 @@ import type { MediaItem } from "@/types/tmdb";
710
import { backdropUrl } from "@/lib/tmdb";
811
import ExpandableOverview from "@/components/ExpandableOverview";
912

10-
export default function Hero({ item }: { item: MediaItem }) {
11-
const backdrop = backdropUrl(item.backdrop_path);
13+
interface HeroProps {
14+
items: MediaItem[];
15+
}
16+
17+
export default function Hero({ items }: HeroProps) {
18+
const [activeIndex, setActiveIndex] = useState(0);
19+
20+
const safeItems = useMemo(
21+
() => items.filter((item) => Boolean(item?.id)),
22+
[items],
23+
);
24+
25+
useEffect(() => {
26+
if (safeItems.length <= 1) return;
27+
28+
const intervalId = window.setInterval(() => {
29+
setActiveIndex((previousIndex) => (previousIndex + 1) % safeItems.length);
30+
}, 15000);
31+
32+
return () => window.clearInterval(intervalId);
33+
}, [safeItems.length]);
34+
35+
const normalizedIndex =
36+
safeItems.length > 0 ? activeIndex % safeItems.length : 0;
37+
const activeItem = safeItems[normalizedIndex];
38+
if (!activeItem) return null;
39+
40+
const backdrop = backdropUrl(activeItem.backdrop_path);
1241
const detailHref =
13-
item.media_type === "tv" ? `/tv/${item.id}` : `/movie/${item.id}`;
42+
activeItem.media_type === "tv"
43+
? `/tv/${activeItem.id}`
44+
: `/movie/${activeItem.id}`;
1445
const watchHref =
15-
item.media_type === "tv" ? `/watch/tv/${item.id}` : `/watch/${item.id}`;
46+
activeItem.media_type === "tv"
47+
? `/watch/tv/${activeItem.id}`
48+
: `/watch/${activeItem.id}`;
1649

1750
return (
1851
<section className="relative w-full h-[70vh] sm:h-[80vh] overflow-hidden">
1952
{backdrop && (
2053
<Image
2154
src={backdrop}
22-
alt={item.title}
55+
alt={activeItem.title}
2356
fill
2457
priority
2558
className="object-cover object-top"
@@ -40,20 +73,20 @@ export default function Hero({ item }: { item: MediaItem }) {
4073
>
4174
Trending
4275
</Badge>
43-
{item.media_type === "tv" && (
76+
{activeItem.media_type === "tv" && (
4477
<Badge className="bg-accent-red/90 text-white text-xs uppercase tracking-wider hover:bg-accent-red/80">
4578
TV Series
4679
</Badge>
4780
)}
4881
<div className="flex items-center gap-1 text-accent-red">
4982
<Star className="h-4 w-4 fill-accent-red" />
5083
<span className="text-sm font-semibold">
51-
{item.vote_average.toFixed(1)}
84+
{activeItem.vote_average.toFixed(1)}
5285
</span>
5386
</div>
54-
{item.date && (
87+
{activeItem.date && (
5588
<span className="text-sm text-muted-foreground">
56-
{new Date(item.date).getFullYear()}
89+
{new Date(activeItem.date).getFullYear()}
5790
</span>
5891
)}
5992
</div>
@@ -62,11 +95,11 @@ export default function Hero({ item }: { item: MediaItem }) {
6295
className="text-4xl sm:text-5xl lg:text-6xl font-bold tracking-tight leading-none"
6396
style={{ fontFamily: "var(--font-heading)" }}
6497
>
65-
{item.title}
98+
{activeItem.title}
6699
</h1>
67100

68101
<ExpandableOverview
69-
text={item.overview}
102+
text={activeItem.overview}
70103
fontKey="body"
71104
fontSize={16}
72105
containerWidth={576}

0 commit comments

Comments
 (0)