optimize trakt loading for carousel view

This commit is contained in:
Pas 2025-06-25 08:53:43 -06:00
parent 0603c8cb7c
commit 8cbe8f986a
3 changed files with 58 additions and 28 deletions

View file

@ -84,6 +84,7 @@ export function MoreContent({ onShowDetails }: MoreContentProps) {
mediaTitle: recommendationSources.find(
(s) => s.id === selectedRecommendationId,
)?.title,
isCarouselView: false,
});
// Handle content visibility
@ -184,17 +185,19 @@ export function MoreContent({ onShowDetails }: MoreContentProps) {
<div className="animate-pulse">
<div className="h-8 bg-mediaCard-hoverBackground rounded w-1/4 mb-8" />
<MediaGrid>
{Array.from({ length: 20 }).map((_, _i) => (
<div
key={`loading-skeleton-${Math.random().toString(36).substring(7)}`}
className="relative group cursor-default user-select-none rounded-xl p-2 bg-transparent"
>
<div className="animate-pulse">
<div className="w-full aspect-[2/3] bg-mediaCard-hoverBackground rounded-lg" />
<div className="mt-2 h-4 bg-mediaCard-hoverBackground rounded w-3/4" />
{Array(20)
.fill(null)
.map(() => (
<div
key={`loading-skeleton-${Math.random().toString(36).substring(2)}`}
className="relative group cursor-default user-select-none rounded-xl p-2 bg-transparent"
>
<div className="animate-pulse">
<div className="w-full aspect-[2/3] bg-mediaCard-hoverBackground rounded-lg" />
<div className="mt-2 h-4 bg-mediaCard-hoverBackground rounded w-3/4" />
</div>
</div>
</div>
))}
))}
</MediaGrid>
</div>
</WideContainer>

View file

@ -213,6 +213,7 @@ export function MediaCarousel({
genreName: selectedGenreName,
providerName: selectedProviderName,
mediaTitle: selectedRecommendationTitle,
isCarouselView: true,
});
// Find active button
@ -313,11 +314,13 @@ export function MediaCarousel({
<div className="relative overflow-hidden carousel-container md:pb-4">
<div className="grid grid-flow-col auto-cols-max gap-4 pt-0 overflow-x-scroll scrollbar-none rounded-xl overflow-y-hidden md:pl-8 md:pr-8">
<div className="md:w-12" />
{Array.from({ length: 10 }).map(() => (
<MediaCardSkeleton
key={`loading-skeleton-${Math.random().toString(36).substring(7)}`}
/>
))}
{Array(10)
.fill(null)
.map(() => (
<MediaCardSkeleton
key={`skeleton-loading-${Math.random().toString(36).substring(2)}`}
/>
))}
<div className="md:w-12" />
</div>
</div>
@ -533,11 +536,13 @@ export function MediaCarousel({
/>
</div>
))
: Array.from({ length: 10 }).map(() => (
<MediaCardSkeleton
key={`loading-skeleton-${Math.random().toString(36).substring(7)}`}
/>
))}
: Array(10)
.fill(null)
.map((_, i) => (
<MediaCardSkeleton
key={`skeleton-${categorySlug}-${Math.random().toString(36).substring(2)}`}
/>
))}
{moreContent && generatedMoreLink && (
<MoreCard link={generatedMoreLink} />

View file

@ -129,6 +129,8 @@ export interface UseDiscoverMediaProps {
providerName?: string;
/** Media title for recommendations display */
mediaTitle?: string;
/** Whether this is for a carousel view (limits results) */
isCarouselView?: boolean;
}
/**
@ -284,6 +286,7 @@ export function useDiscoverMedia({
genreName,
providerName,
mediaTitle,
isCarouselView = false,
}: UseDiscoverMediaProps): UseDiscoverMediaReturn {
const [media, setMedia] = useState<DiscoverMedia[]>([]);
const [isLoading, setIsLoading] = useState(false);
@ -308,15 +311,26 @@ export function useDiscoverMedia({
const fetchTMDBMedia = useCallback(
async (endpoint: string, params: Record<string, any> = {}) => {
try {
// For carousel views, we only need one page of results
if (isCarouselView) {
params.page = "1"; // Always use first page for carousels
} else {
params.page = page.toString(); // Use the requested page for "view more" pages
}
const data = await get<any>(endpoint, {
api_key: conf().TMDB_READ_API_KEY,
language: formattedLanguage,
page: page.toString(),
...params,
});
// For carousel views, we might want to limit the number of results
const results = isCarouselView
? data.results.slice(0, 20)
: data.results;
return {
results: data.results.map((item: any) => ({
results: results.map((item: any) => ({
...item,
type: mediaType === "movie" ? "movie" : "show",
})),
@ -327,7 +341,7 @@ export function useDiscoverMedia({
throw err;
}
},
[formattedLanguage, page, mediaType],
[formattedLanguage, page, mediaType, isCarouselView],
);
const fetchTraktMedia = useCallback(
@ -342,13 +356,18 @@ export function useDiscoverMedia({
const response = await Promise.race([traktFunction(), timeoutPromise]);
// Paginate the results
const pageSize = isCarouselView ? 20 : 100; // Limit to 20 items for carousels, get more for detailed views
const { tmdb_ids: tmdbIds, hasMore: hasMoreResults } = paginateResults(
response,
page,
pageSize,
);
// For carousel views, we only need to fetch details for displayed items
const idsToFetch = isCarouselView ? tmdbIds.slice(0, 20) : tmdbIds;
// Fetch details for each TMDB ID
const mediaPromises = tmdbIds.map(async (tmdbId: number) => {
const mediaPromises = idsToFetch.map(async (tmdbId: number) => {
const endpoint = `/${mediaType}/${tmdbId}`;
try {
const data = await get<any>(endpoint, {
@ -385,7 +404,7 @@ export function useDiscoverMedia({
throw err;
}
},
[mediaType, formattedLanguage, page],
[mediaType, formattedLanguage, page, isCarouselView],
);
// Get Trakt function for provider
@ -442,8 +461,11 @@ export function useDiscoverMedia({
const picks =
mediaType === "movie" ? EDITOR_PICKS_MOVIES : EDITOR_PICKS_TV_SHOWS;
// For carousel views, limit the number of picks to fetch
const picksToFetch = isCarouselView ? picks.slice(0, 20) : picks;
try {
const mediaPromises = picks.map(async (item) => {
const mediaPromises = picksToFetch.map(async (item) => {
const endpoint = `/${mediaType}/${item.id}`;
const data = await get<any>(endpoint, {
api_key: conf().TMDB_READ_API_KEY,
@ -459,13 +481,13 @@ export function useDiscoverMedia({
const results = await Promise.all(mediaPromises);
return {
results,
hasMore: false,
hasMore: picks.length > picksToFetch.length,
};
} catch (err) {
console.error("Error fetching editor picks:", err);
throw err;
}
}, [mediaType, formattedLanguage]);
}, [mediaType, formattedLanguage, isCarouselView]);
const fetchMedia = useCallback(async () => {
setIsLoading(true);