mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-03-09 15:40:26 +00:00
184 lines
5.5 KiB
TypeScript
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>
|
|
)}
|
|
</>
|
|
);
|
|
}
|