update media card skeleton and on recomented carousel

This commit is contained in:
Pas 2025-12-02 10:13:08 -07:00
parent 02291a3e37
commit ac722880c4
2 changed files with 42 additions and 20 deletions

View file

@ -49,10 +49,16 @@ function useIntersectionObserver(options: IntersectionObserverInit = {}) {
}
// Skeleton Component
function MediaCardSkeleton() {
export function MediaCardSkeleton() {
return (
<div className="group -m-[0.705em] rounded-xl bg-background-main transition-colors duration-300">
<div className="pointer-events-auto relative mb-2 p-[0.4em] transition-transform duration-300">
<Flare.Base className="group -m-[0.705em] rounded-xl bg-background-main transition-colors duration-300">
<Flare.Light
flareSize={300}
cssColorVar="--colors-mediaCard-hoverAccent"
backgroundClass="bg-mediaCard-hoverBackground duration-100"
className="rounded-xl bg-background-main group-hover:opacity-100"
/>
<Flare.Child className="pointer-events-auto relative mb-2 p-[0.4em] transition-transform duration-300 opacity-60">
<div className="animate-pulse">
{/* Poster skeleton - matches MediaCard poster dimensions exactly */}
<div className="relative mb-4 pb-[150%] w-full overflow-hidden rounded-xl bg-mediaCard-hoverBackground" />
@ -71,8 +77,8 @@ function MediaCardSkeleton() {
<div className="h-3 bg-mediaCard-hoverBackground rounded w-8" />
</div>
</div>
</div>
</div>
</Flare.Child>
</Flare.Base>
);
}

View file

@ -3,7 +3,7 @@ import { useTranslation } from "react-i18next";
import { getMediaPoster, getRelatedMedia } from "@/backend/metadata/tmdb";
import { TMDBContentTypes } from "@/backend/metadata/types/tmdb";
import { MediaCard } from "@/components/media/MediaCard";
import { MediaCard, MediaCardSkeleton } from "@/components/media/MediaCard";
import { useIsMobile } from "@/hooks/useIsMobile";
import { CarouselNavButtons } from "@/pages/discover/components/CarouselNavButtons";
import { useOverlayStack } from "@/stores/interface/overlayStack";
@ -22,6 +22,7 @@ export function SimilarMediaCarousel({
const { isMobile } = useIsMobile();
const { showModal } = useOverlayStack();
const [similarMedia, setSimilarMedia] = useState<MediaItem[]>([]);
const [isLoading, setIsLoading] = useState(true);
const carouselRef = useRef<HTMLDivElement>(null);
const carouselRefs = useRef<{ [key: string]: HTMLDivElement | null }>({
similar: null,
@ -29,6 +30,7 @@ export function SimilarMediaCarousel({
useEffect(() => {
const loadSimilarMedia = async () => {
setIsLoading(true);
try {
const results = await getRelatedMedia(mediaId, mediaType, 12);
const mediaItems: MediaItem[] = results.map((result) => {
@ -57,6 +59,8 @@ export function SimilarMediaCarousel({
setSimilarMedia(mediaItems);
} catch (err) {
console.error("Failed to load similar media:", err);
} finally {
setIsLoading(false);
}
};
@ -76,7 +80,7 @@ export function SimilarMediaCarousel({
});
};
if (similarMedia.length === 0) return null;
if (!isLoading && similarMedia.length === 0) return null;
return (
<div className="space-y-4 pt-8">
@ -96,19 +100,31 @@ export function SimilarMediaCarousel({
>
<div className="md:w-12" />
{similarMedia.map((media) => (
<div
key={media.id}
className="relative mt-4 group cursor-pointer user-select-none rounded-xl p-2 bg-transparent transition-colors duration-300 w-[10rem] md:w-[11.5rem] h-auto"
style={{ scrollSnapAlign: "start" }}
>
<MediaCard
media={media}
linkable
onShowDetails={handleShowDetails}
/>
</div>
))}
{isLoading
? // Show skeleton cards while loading
Array.from({ length: 12 }, (_, i) => (
<div
key={`skeleton-${i}`}
className="relative mt-4 group cursor-pointer user-select-none rounded-xl p-2 bg-transparent transition-colors duration-300 w-[10rem] md:w-[11.5rem] h-auto"
style={{ scrollSnapAlign: "start" }}
>
<MediaCardSkeleton />
</div>
))
: // Show actual media cards when loaded
similarMedia.map((media) => (
<div
key={media.id}
className="relative mt-4 group cursor-pointer user-select-none rounded-xl p-2 bg-transparent transition-colors duration-300 w-[10rem] md:w-[11.5rem] h-auto"
style={{ scrollSnapAlign: "start" }}
>
<MediaCard
media={media}
linkable
onShowDetails={handleShowDetails}
/>
</div>
))}
<div className="md:w-12" />
</div>