Merge pull request #200 from Mkdantas/main

Added screenshots and trailers to game details
This commit is contained in:
Hydra 2024-05-07 05:55:54 +01:00 committed by GitHub
commit 748f3457f2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 12672 additions and 2 deletions

12433
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@
<title>Hydra</title>
<meta
http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://steamcdn-a.akamaihd.net https://cdn.cloudflare.steamstatic.com https://cdn2.steamgriddb.com https://cdn.akamai.steamstatic.com;"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https://steamcdn-a.akamaihd.net https://cdn.cloudflare.steamstatic.com https://cdn2.steamgriddb.com https://cdn.akamai.steamstatic.com; media-src 'self' data: https://steamcdn-a.akamaihd.net https://cdn.cloudflare.steamstatic.com https://cdn2.steamgriddb.com https://cdn.akamai.steamstatic.com;"
/>
</head>
<body style="background-color: #1c1c1c">

View File

@ -0,0 +1,132 @@
import { RefObject, useEffect, useRef, useState } from "react";
import { ShopDetails, SteamMovies, SteamScreenshot } from "@types";
import { ChevronRightIcon, ChevronLeftIcon } from "@primer/octicons-react";
import * as styles from "./game-details.css";
export interface GallerySliderProps {
gameDetails: ShopDetails | null;
}
export function GallerySlider({ gameDetails }: GallerySliderProps) {
const scrollContainerRef: RefObject<HTMLDivElement> =
useRef<HTMLDivElement>(null);
const [mediaCount] = useState<number>(() => {
if (gameDetails) {
if (gameDetails.screenshots && gameDetails.movies) {
return gameDetails.screenshots.length + gameDetails.movies.length;
} else if (gameDetails.movies) {
return gameDetails.movies.length;
} else if (gameDetails.screenshots) {
return gameDetails.screenshots.length;
}
}
return 0;
});
const [mediaIndex, setMediaIndex] = useState<number>(0);
const [arrowShow, setArrowShow] = useState(false);
const scrollHorizontallyToPercentage = () => {
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;
}
};
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(() => {
scrollHorizontallyToPercentage();
}, [mediaIndex]);
return (
<>
{gameDetails?.screenshots && (
<div className={styles.gallerySliderContainer}>
<div
onMouseEnter={() => setArrowShow(true)}
onMouseLeave={() => setArrowShow(false)}
className={styles.gallerySliderAnimationContainer}
>
{gameDetails.movies &&
gameDetails.movies.map((video: SteamMovies) => (
<video
controls
className={styles.gallerySliderMedia}
poster={video.thumbnail}
style={{ translate: `${-100 * mediaIndex}%` }}
>
<source src={video.webm.max.replace("http", "https")} />
</video>
))}
{gameDetails.screenshots &&
gameDetails.screenshots.map((image: SteamScreenshot) => (
<img
className={styles.gallerySliderMedia}
src={image.path_full}
style={{ translate: `${-100 * mediaIndex}%` }}
/>
))}
{arrowShow && (
<>
<button
onClick={showPrevImage}
className={styles.gallerySliderButton}
style={{ left: 0 }}
>
<ChevronLeftIcon className={styles.gallerySliderIcons} />
</button>
<button
onClick={showNextImage}
className={styles.gallerySliderButton}
style={{ right: 0 }}
>
<ChevronRightIcon className={styles.gallerySliderIcons} />
</button>
</>
)}
</div>
<div className={styles.gallerySliderPreview} ref={scrollContainerRef}>
{gameDetails.movies &&
gameDetails.movies.map((video: SteamMovies, i: number) => (
<img
onClick={() => setMediaIndex(i)}
src={video.thumbnail}
className={`${styles.gallerySliderMediaPreview} ${mediaIndex === i ? styles.gallerySliderMediaPreviewActive : ""}`}
/>
))}
{gameDetails.screenshots &&
gameDetails.screenshots.map(
(image: SteamScreenshot, i: number) => (
<img
onClick={() =>
setMediaIndex(
i + (gameDetails.movies ? gameDetails.movies.length : 0)
)
}
className={`${styles.gallerySliderMediaPreview} ${mediaIndex === i + (gameDetails.movies ? gameDetails.movies.length : 0) ? styles.gallerySliderMediaPreviewActive : ""}`}
src={image.path_full}
/>
)
)}
</div>
</div>
)}
</>
);
}

View File

@ -79,6 +79,94 @@ export const descriptionContent = style({
height: "100%",
});
export const gallerySliderContainer = style({
padding: `${SPACING_UNIT * 3}px ${SPACING_UNIT * 2}px`,
width: "100%",
display: "flex",
flexDirection: "column",
alignItems: "center",
});
export const gallerySliderMedia = style({
width: "100%",
height: "100%",
display: "block",
flexShrink: 0,
flexGrow: 0,
transition: "translate 300ms ease-in-out",
});
export const gallerySliderAnimationContainer = style({
width: "100%",
height: "100%",
display: "flex",
position: "relative",
overflow: "hidden",
"@media": {
"(min-width: 1280px)": {
width: "60%",
},
},
});
export const gallerySliderPreview = style({
width: "100%",
paddingTop: "0.5rem",
height: "100%",
display: "flex",
position: "relative",
overflowX: "auto",
overflowY: "hidden",
"@media": {
"(min-width: 1280px)": {
width: "60%",
},
},
"::-webkit-scrollbar-thumb": {
width: "20%"
}
});
export const gallerySliderMediaPreview = style({
cursor: "pointer",
width: "20%",
height: "20%",
display: "block",
flexShrink: 0,
flexGrow: 0,
opacity: 0.3,
paddingRight: "5px",
transition: "translate 300ms ease-in-out",
":hover": {
opacity: 1,
},
});
export const gallerySliderMediaPreviewActive = style({
opacity: 1,
});
export const gallerySliderButton = style({
all: "unset",
display: "block",
position: "absolute",
top: 0,
bottom: 0,
padding: "1rem",
cursor: "pointer",
transition: "background-color 100ms ease-in-out",
":hover": {
backgroundColor: "rgb(0,0,0, 0.2)",
},
});
export const gallerySliderIcons = style({
stroke: "white",
fill: "black",
width: "2rem",
height: "2rem",
});
export const contentSidebar = style({
borderLeft: `solid 1px ${vars.color.border};`,
width: "100%",

View File

@ -36,6 +36,7 @@ import {
DONT_SHOW_ONLINE_FIX_INSTRUCTIONS_KEY,
OnlineFixInstallationGuide,
} from "./installation-guides";
import { GallerySlider } from "./gallery-slider";
export function GameDetails() {
const { objectID, shop } = useParams();
@ -89,7 +90,6 @@ export function GameDetails() {
useEffect(() => {
getGame();
}, [getGame, gameDownloading?.id]);
useEffect(() => {
setGame(null);
setIsLoading(true);
@ -249,6 +249,8 @@ export function GameDetails() {
<div className={styles.descriptionContent}>
<DescriptionHeader gameDetails={gameDetails} />
<GallerySlider gameDetails={gameDetails} />
<div
dangerouslySetInnerHTML={{
__html: gameDetails?.about_the_game ?? "",

View File

@ -12,6 +12,20 @@ export interface SteamScreenshot {
path_full: string;
}
export interface SteamVideoSource {
max: string;
'480': string;
}
export interface SteamMovies {
id: number;
mp4: SteamVideoSource;
webm: SteamVideoSource;
thumbnail: string;
name: string;
highlight: boolean;
}
export interface SteamAppDetails {
name: string;
detailed_description: string;
@ -19,6 +33,7 @@ export interface SteamAppDetails {
short_description: string;
publishers: string[];
genres: SteamGenre[];
movies: SteamMovies[];
screenshots: SteamScreenshot[];
pc_requirements: {
minimum: string;