hydra/src/renderer/src/pages/game-details/gallery-slider/gallery-slider.tsx
2024-06-11 23:59:58 +01:00

184 lines
5.5 KiB
TypeScript

import { useContext, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { ChevronRightIcon, ChevronLeftIcon } from "@primer/octicons-react";
import * as styles from "./gallery-slider.css";
import { gameDetailsContext } from "@renderer/context";
export function GallerySlider() {
const { shopDetails } = useContext(gameDetailsContext);
const scrollContainerRef = useRef<HTMLDivElement>(null);
const mediaContainerRef = useRef<HTMLDivElement>(null);
const { t } = useTranslation("game_details");
const hasScreenshots = shopDetails && shopDetails.screenshots?.length;
const hasMovies = shopDetails && shopDetails.movies?.length;
const mediaCount = useMemo(() => {
if (!shopDetails) return 0;
if (shopDetails.screenshots && shopDetails.movies) {
return shopDetails.screenshots.length + shopDetails.movies.length;
} else if (shopDetails.movies) {
return shopDetails.movies.length;
} else if (shopDetails.screenshots) {
return shopDetails.screenshots.length;
}
return 0;
}, [shopDetails]);
const [mediaIndex, setMediaIndex] = useState(0);
const [showArrows, setShowArrows] = useState(false);
const showNextImage = () => {
setMediaIndex((index: number) => {
if (index === mediaCount - 1) return 0;
return index + 1;
});
};
const showPrevImage = () => {
setMediaIndex((index: number) => {
if (index === 0) return mediaCount - 1;
return index - 1;
});
};
useEffect(() => {
setMediaIndex(0);
}, [shopDetails]);
useEffect(() => {
if (hasMovies && mediaContainerRef.current) {
mediaContainerRef.current.childNodes.forEach((node, index) => {
if (node instanceof HTMLVideoElement) {
if (index !== mediaIndex) {
node.pause();
}
}
});
}
}, [hasMovies, mediaContainerRef, mediaIndex]);
useEffect(() => {
if (scrollContainerRef.current) {
const container = scrollContainerRef.current;
const totalWidth = container.scrollWidth - container.clientWidth;
const itemWidth = totalWidth / (mediaCount - 1);
const scrollLeft = mediaIndex * itemWidth;
container.scrollLeft = scrollLeft;
}
}, [shopDetails, mediaIndex, mediaCount]);
const previews = useMemo(() => {
const screenshotPreviews =
shopDetails?.screenshots?.map(({ id, path_thumbnail }) => ({
id,
thumbnail: path_thumbnail,
})) ?? [];
if (shopDetails?.movies) {
const moviePreviews = shopDetails.movies.map(({ id, thumbnail }) => ({
id,
thumbnail,
}));
return [...moviePreviews, ...screenshotPreviews];
}
return screenshotPreviews;
}, [shopDetails]);
return (
<>
{hasScreenshots && (
<div className={styles.gallerySliderContainer}>
<div
onMouseEnter={() => setShowArrows(true)}
onMouseLeave={() => setShowArrows(false)}
className={styles.gallerySliderAnimationContainer}
ref={mediaContainerRef}
>
{shopDetails.movies &&
shopDetails.movies.map((video) => (
<video
key={video.id}
controls
className={styles.gallerySliderMedia}
poster={video.thumbnail}
style={{ translate: `${-100 * mediaIndex}%` }}
loop
muted
tabIndex={-1}
>
<source src={video.mp4.max.replace("http", "https")} />
</video>
))}
{hasScreenshots &&
shopDetails.screenshots?.map((image, i) => (
<img
key={image.id}
className={styles.gallerySliderMedia}
src={image.path_full}
style={{ translate: `${-100 * mediaIndex}%` }}
alt={t("screenshot", { number: i + 1 })}
/>
))}
<button
onClick={showPrevImage}
type="button"
className={styles.gallerySliderButton({
visible: showArrows,
direction: "left",
})}
aria-label={t("previous_screenshot")}
tabIndex={0}
>
<ChevronLeftIcon size={36} />
</button>
<button
onClick={showNextImage}
type="button"
className={styles.gallerySliderButton({
visible: showArrows,
direction: "right",
})}
aria-label={t("next_screenshot")}
tabIndex={0}
>
<ChevronRightIcon size={36} />
</button>
</div>
<div className={styles.gallerySliderPreview} ref={scrollContainerRef}>
{previews.map((media, i) => (
<button
key={media.id}
type="button"
className={styles.mediaPreviewButton({
active: mediaIndex === i,
})}
onClick={() => setMediaIndex(i)}
aria-label={t("open_screenshot", { number: i + 1 })}
>
<img
src={media.thumbnail}
className={styles.mediaPreview}
alt={t("screenshot", { number: i + 1 })}
/>
</button>
))}
</div>
</div>
)}
</>
);
}