mirror of
https://github.com/p-stream/p-stream.git
synced 2026-04-05 09:49:44 +00:00
fix lazy carousels
This commit is contained in:
parent
b8ca66b4cb
commit
bab39e2580
2 changed files with 96 additions and 78 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
import { get } from "@/backend/metadata/tmdb";
|
||||
import { useIntersectionObserver } from "@/pages/discover/hooks/useIntersectionObserver";
|
||||
|
|
@ -8,7 +8,6 @@ import { MediaItem } from "@/utils/mediaTypes";
|
|||
import { MediaCarousel } from "./MediaCarousel";
|
||||
import {
|
||||
Category,
|
||||
Genre,
|
||||
Media,
|
||||
Movie,
|
||||
TVShow,
|
||||
|
|
@ -17,39 +16,48 @@ import {
|
|||
} from "../common";
|
||||
|
||||
interface LazyMediaCarouselProps {
|
||||
category?: Category;
|
||||
genre?: Genre;
|
||||
mediaType: "movie" | "tv";
|
||||
medias?: Media[];
|
||||
category?: string;
|
||||
isTVShow: boolean;
|
||||
carouselRefs: React.MutableRefObject<{
|
||||
[key: string]: HTMLDivElement | null;
|
||||
}>;
|
||||
onShowDetails?: (media: MediaItem) => void;
|
||||
preloadedMedia?: Movie[] | TVShow[];
|
||||
genreId?: number;
|
||||
title?: string;
|
||||
moreContent?: boolean;
|
||||
moreLink?: string;
|
||||
relatedButtons?: Array<{ name: string; id: string }>;
|
||||
onButtonClick?: (id: string, name: string) => void;
|
||||
moreContent?: boolean;
|
||||
recommendationSources?: Array<{ id: string; title: string }>;
|
||||
selectedRecommendationSource?: string;
|
||||
onRecommendationSourceChange?: (id: string) => void;
|
||||
preloadedMedia?: Movie[] | TVShow[];
|
||||
title?: string;
|
||||
}
|
||||
|
||||
export function LazyMediaCarousel({
|
||||
medias: propMedias,
|
||||
category,
|
||||
genre,
|
||||
mediaType,
|
||||
isTVShow,
|
||||
carouselRefs,
|
||||
onShowDetails,
|
||||
preloadedMedia,
|
||||
genreId,
|
||||
title,
|
||||
moreContent,
|
||||
moreLink,
|
||||
relatedButtons,
|
||||
onButtonClick,
|
||||
moreContent,
|
||||
recommendationSources,
|
||||
selectedRecommendationSource,
|
||||
onRecommendationSourceChange,
|
||||
preloadedMedia,
|
||||
title,
|
||||
}: LazyMediaCarouselProps) {
|
||||
const [medias, setMedias] = useState<Media[]>([]);
|
||||
const [loading, setLoading] = useState(!preloadedMedia);
|
||||
const [medias, setMedias] = useState<Media[]>(propMedias || []);
|
||||
const [loading, setLoading] = useState(!preloadedMedia && !propMedias);
|
||||
const hasLoaded = useRef(false);
|
||||
|
||||
const categoryData = (mediaType === "movie" ? categories : tvCategories).find(
|
||||
(c: Category) => c.name === (category?.name || genre?.name || title || ""),
|
||||
const categoryData = (isTVShow ? tvCategories : categories).find(
|
||||
(c: Category) => c.name === (category || title || ""),
|
||||
);
|
||||
|
||||
// Use intersection observer to detect when this component is visible
|
||||
|
|
@ -57,12 +65,15 @@ export function LazyMediaCarousel({
|
|||
{ rootMargin: "200px" }, // Load when within 200px of viewport
|
||||
);
|
||||
|
||||
// Use the lazy loading hook only if we don't have preloaded media
|
||||
// Use the lazy loading hook only if we don't have preloaded media or prop medias
|
||||
// and haven't loaded yet
|
||||
const { media } = useLazyTMDBData(
|
||||
!preloadedMedia ? genre || null : null,
|
||||
!preloadedMedia ? category || null : null,
|
||||
mediaType,
|
||||
isIntersecting,
|
||||
null, // We don't use genre anymore since we're using category directly
|
||||
!preloadedMedia && !propMedias && !hasLoaded.current
|
||||
? categoryData || null
|
||||
: null,
|
||||
isTVShow ? "tv" : "movie",
|
||||
isIntersecting && !hasLoaded.current,
|
||||
);
|
||||
|
||||
// Update medias when data is loaded or preloaded
|
||||
|
|
@ -70,15 +81,23 @@ export function LazyMediaCarousel({
|
|||
if (preloadedMedia) {
|
||||
setMedias(preloadedMedia);
|
||||
setLoading(false);
|
||||
hasLoaded.current = true;
|
||||
} else if (propMedias) {
|
||||
setMedias(propMedias);
|
||||
setLoading(false);
|
||||
hasLoaded.current = true;
|
||||
} else if (media.length > 0) {
|
||||
setMedias(media);
|
||||
setLoading(false);
|
||||
hasLoaded.current = true;
|
||||
}
|
||||
}, [media, preloadedMedia]);
|
||||
}, [media, preloadedMedia, propMedias]);
|
||||
|
||||
// Only fetch category content if we don't have preloaded media
|
||||
// Only fetch category content if we don't have preloaded media or prop medias
|
||||
// and haven't loaded yet
|
||||
useEffect(() => {
|
||||
if (preloadedMedia || !categoryData) return;
|
||||
if (preloadedMedia || propMedias || !categoryData || hasLoaded.current)
|
||||
return;
|
||||
|
||||
const fetchContent = async () => {
|
||||
try {
|
||||
|
|
@ -87,6 +106,7 @@ export function LazyMediaCarousel({
|
|||
language: "en-US",
|
||||
});
|
||||
setMedias(data.results);
|
||||
hasLoaded.current = true;
|
||||
} catch (error) {
|
||||
console.error("Error fetching content:", error);
|
||||
} finally {
|
||||
|
|
@ -95,10 +115,10 @@ export function LazyMediaCarousel({
|
|||
};
|
||||
|
||||
fetchContent();
|
||||
}, [categoryData, preloadedMedia]);
|
||||
}, [categoryData, preloadedMedia, propMedias]);
|
||||
|
||||
const categoryName = category?.name || genre?.name || title || "";
|
||||
const categorySlug = `${categoryName.toLowerCase().replace(/[^a-z0-9]+/g, "-")}-${mediaType}`;
|
||||
const categoryName = category || title || "";
|
||||
const categorySlug = `${categoryName.toLowerCase().replace(/[^a-z0-9]+/g, "-")}-${isTVShow ? "tv" : "movie"}`;
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
|
|
@ -118,24 +138,23 @@ export function LazyMediaCarousel({
|
|||
<MediaCarousel
|
||||
medias={medias}
|
||||
category={categoryName}
|
||||
isTVShow={mediaType === "tv"}
|
||||
isTVShow={isTVShow}
|
||||
carouselRefs={carouselRefs}
|
||||
onShowDetails={onShowDetails}
|
||||
genreId={genreId}
|
||||
relatedButtons={relatedButtons}
|
||||
onButtonClick={onButtonClick}
|
||||
moreContent={moreContent}
|
||||
moreLink={
|
||||
categoryData
|
||||
? `/discover/more/category/${categoryData.urlPath}/${categoryData.mediaType}`
|
||||
: undefined
|
||||
}
|
||||
moreLink={moreLink}
|
||||
recommendationSources={recommendationSources}
|
||||
selectedRecommendationSource={selectedRecommendationSource}
|
||||
onRecommendationSourceChange={onRecommendationSourceChange}
|
||||
/>
|
||||
) : (
|
||||
<div className="relative overflow-hidden carousel-container">
|
||||
<div id={`carousel-${categorySlug}`}>
|
||||
<h2 className="ml-2 md:ml-8 mt-2 text-2xl cursor-default font-bold text-white md:text-2xl mx-auto pl-5 text-balance">
|
||||
{categoryName} {mediaType === "tv" ? "Shows" : "Movies"}
|
||||
{categoryName} {isTVShow ? "Shows" : "Movies"}
|
||||
</h2>
|
||||
<div className="flex whitespace-nowrap pt-0 pb-4 overflow-auto scrollbar rounded-xl overflow-y-hidden h-[300px] animate-pulse bg-background-secondary/20">
|
||||
<div className="w-full text-center flex items-center justify-center">
|
||||
|
|
|
|||
|
|
@ -23,7 +23,6 @@ import { DiscoverNavigation } from "./components/DiscoverNavigation";
|
|||
import type { FeaturedMedia } from "./components/FeaturedCarousel";
|
||||
import { LazyMediaCarousel } from "./components/LazyMediaCarousel";
|
||||
import { LazyTabContent } from "./components/LazyTabContent";
|
||||
import { MediaCarousel } from "./components/MediaCarousel";
|
||||
import { ScrollToTopButton } from "./components/ScrollToTopButton";
|
||||
|
||||
// Provider constants moved from DiscoverNavigation
|
||||
|
|
@ -494,37 +493,13 @@ export function DiscoverContent() {
|
|||
detailsModal.show();
|
||||
};
|
||||
|
||||
// Render Editor Picks content
|
||||
const renderEditorPicksContent = () => {
|
||||
return (
|
||||
<>
|
||||
<LazyMediaCarousel
|
||||
preloadedMedia={filteredGenreMovies}
|
||||
title="Editor Picks"
|
||||
mediaType="movie"
|
||||
carouselRefs={carouselRefs}
|
||||
onShowDetails={handleShowDetails}
|
||||
moreContent
|
||||
/>
|
||||
<LazyMediaCarousel
|
||||
preloadedMedia={filteredGenreTVShows}
|
||||
title="Editor Picks"
|
||||
mediaType="tv"
|
||||
carouselRefs={carouselRefs}
|
||||
onShowDetails={handleShowDetails}
|
||||
moreContent
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
// Render Movies content with lazy loading
|
||||
const renderMoviesContent = () => {
|
||||
return (
|
||||
<>
|
||||
{/* Movie Recommendations */}
|
||||
{movieRecommendations.length > 0 && (
|
||||
<MediaCarousel
|
||||
<LazyMediaCarousel
|
||||
medias={movieRecommendations}
|
||||
category={movieRecommendationTitle}
|
||||
isTVShow={false}
|
||||
|
|
@ -540,8 +515,8 @@ export function DiscoverContent() {
|
|||
|
||||
{/* In Cinemas */}
|
||||
<LazyMediaCarousel
|
||||
category={categories[0]}
|
||||
mediaType="movie"
|
||||
category={categories[0].name}
|
||||
isTVShow={false}
|
||||
carouselRefs={carouselRefs}
|
||||
onShowDetails={handleShowDetails}
|
||||
moreContent
|
||||
|
|
@ -549,8 +524,8 @@ export function DiscoverContent() {
|
|||
|
||||
{/* Top Rated */}
|
||||
<LazyMediaCarousel
|
||||
category={categories[1]}
|
||||
mediaType="movie"
|
||||
category={categories[1].name}
|
||||
isTVShow={false}
|
||||
carouselRefs={carouselRefs}
|
||||
onShowDetails={handleShowDetails}
|
||||
moreContent
|
||||
|
|
@ -558,15 +533,15 @@ export function DiscoverContent() {
|
|||
|
||||
{/* Popular */}
|
||||
<LazyMediaCarousel
|
||||
category={categories[2]}
|
||||
mediaType="movie"
|
||||
category={categories[2].name}
|
||||
isTVShow={false}
|
||||
carouselRefs={carouselRefs}
|
||||
onShowDetails={handleShowDetails}
|
||||
moreContent
|
||||
/>
|
||||
|
||||
{/* Provider Movies */}
|
||||
<MediaCarousel
|
||||
<LazyMediaCarousel
|
||||
medias={providerMovies}
|
||||
category={`Movies on ${selectedProvider.name || ""}`}
|
||||
isTVShow={false}
|
||||
|
|
@ -582,7 +557,7 @@ export function DiscoverContent() {
|
|||
/>
|
||||
|
||||
{/* Genre Movies */}
|
||||
<MediaCarousel
|
||||
<LazyMediaCarousel
|
||||
medias={filteredGenreMovies}
|
||||
category={`${selectedGenre.name || ""}`}
|
||||
isTVShow={false}
|
||||
|
|
@ -606,7 +581,7 @@ export function DiscoverContent() {
|
|||
<>
|
||||
{/* TV Show Recommendations */}
|
||||
{tvRecommendations.length > 0 && (
|
||||
<MediaCarousel
|
||||
<LazyMediaCarousel
|
||||
medias={tvRecommendations}
|
||||
category={tvRecommendationTitle}
|
||||
isTVShow
|
||||
|
|
@ -622,8 +597,8 @@ export function DiscoverContent() {
|
|||
|
||||
{/* On Air */}
|
||||
<LazyMediaCarousel
|
||||
category={tvCategories[0]}
|
||||
mediaType="tv"
|
||||
category={tvCategories[0].name}
|
||||
isTVShow
|
||||
carouselRefs={carouselRefs}
|
||||
onShowDetails={handleShowDetails}
|
||||
moreContent
|
||||
|
|
@ -631,8 +606,8 @@ export function DiscoverContent() {
|
|||
|
||||
{/* Top Rated */}
|
||||
<LazyMediaCarousel
|
||||
category={tvCategories[1]}
|
||||
mediaType="tv"
|
||||
category={tvCategories[1].name}
|
||||
isTVShow
|
||||
carouselRefs={carouselRefs}
|
||||
onShowDetails={handleShowDetails}
|
||||
moreContent
|
||||
|
|
@ -640,15 +615,15 @@ export function DiscoverContent() {
|
|||
|
||||
{/* Popular */}
|
||||
<LazyMediaCarousel
|
||||
category={tvCategories[2]}
|
||||
mediaType="tv"
|
||||
category={tvCategories[2].name}
|
||||
isTVShow
|
||||
carouselRefs={carouselRefs}
|
||||
onShowDetails={handleShowDetails}
|
||||
moreContent
|
||||
/>
|
||||
|
||||
{/* Provider TV Shows */}
|
||||
<MediaCarousel
|
||||
<LazyMediaCarousel
|
||||
medias={providerTVShows}
|
||||
category={`Shows on ${selectedProvider.name || ""}`}
|
||||
isTVShow
|
||||
|
|
@ -664,7 +639,7 @@ export function DiscoverContent() {
|
|||
/>
|
||||
|
||||
{/* Genre TV Shows */}
|
||||
<MediaCarousel
|
||||
<LazyMediaCarousel
|
||||
medias={filteredGenreTVShows}
|
||||
category={`${selectedGenre.name || ""}`}
|
||||
isTVShow
|
||||
|
|
@ -682,6 +657,30 @@ export function DiscoverContent() {
|
|||
);
|
||||
};
|
||||
|
||||
// Render Editor Picks content
|
||||
const renderEditorPicksContent = () => {
|
||||
return (
|
||||
<>
|
||||
<LazyMediaCarousel
|
||||
preloadedMedia={filteredGenreMovies}
|
||||
title="Editor Picks"
|
||||
isTVShow={false}
|
||||
carouselRefs={carouselRefs}
|
||||
onShowDetails={handleShowDetails}
|
||||
moreContent
|
||||
/>
|
||||
<LazyMediaCarousel
|
||||
preloadedMedia={filteredGenreTVShows}
|
||||
title="Editor Picks"
|
||||
isTVShow
|
||||
carouselRefs={carouselRefs}
|
||||
onShowDetails={handleShowDetails}
|
||||
moreContent
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative min-h-screen">
|
||||
<DiscoverNavigation
|
||||
|
|
|
|||
Loading…
Reference in a new issue