diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 293a898b..3e5072c7 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -1,6 +1,6 @@ name: Lint -on: [pull_request, push] +on: [push] jobs: lint: diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 0674d1b5..6855100e 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -96,7 +96,11 @@ "dont_show_it_again": "Don't show it again", "copy_to_clipboard": "Copy", "copied_to_clipboard": "Copied", - "got_it": "Got it" + "got_it": "Got it", + "previous_screenshot": "Previous screenshot", + "next_screenshot": "Next screenshot", + "screenshot": "Screenshot {{number}}", + "open_screenshot": "Open screenshot {{number}}" }, "activation": { "title": "Activate Hydra", diff --git a/src/locales/pt/translation.json b/src/locales/pt/translation.json index dda53065..5ad27c07 100644 --- a/src/locales/pt/translation.json +++ b/src/locales/pt/translation.json @@ -92,7 +92,11 @@ "dont_show_it_again": "Não mostrar novamente", "copy_to_clipboard": "Copiar", "copied_to_clipboard": "Copiado", - "got_it": "Entendi" + "got_it": "Entendi", + "previous_screenshot": "Captura de tela anterior", + "next_screenshot": "Próxima captura de tela", + "screenshot": "Captura de tela {{number}}", + "open_screenshot": "Ver captura de tela {{number}}" }, "activation": { "title": "Ativação", diff --git a/src/renderer/src/pages/game-details/gallery-slider.css.ts b/src/renderer/src/pages/game-details/gallery-slider.css.ts index c7e0b7c2..801c3a5b 100644 --- a/src/renderer/src/pages/game-details/gallery-slider.css.ts +++ b/src/renderer/src/pages/game-details/gallery-slider.css.ts @@ -1,3 +1,4 @@ +import { recipe } from "@vanilla-extract/recipes"; import { SPACING_UNIT, vars } from "../../theme.css"; import { style } from "@vanilla-extract/css"; @@ -13,7 +14,7 @@ export const gallerySliderMedia = style({ width: "100%", height: "100%", display: "block", - flexShrink: 0, + flexShrink: "0", flexGrow: "0", transition: "translate 0.3s ease-in-out", borderRadius: "4px", @@ -54,42 +55,77 @@ export const gallerySliderPreview = style({ }, }); -export const gallerySliderMediaPreview = style({ - cursor: "pointer", - width: "20%", - height: "20%", - display: "block", - flexShrink: 0, - flexGrow: 0, - opacity: 0.3, - transition: "translate 0.3s ease-in-out, opacity 0.2s ease", - borderRadius: "4px", - border: `solid 1px ${vars.color.border}`, - ":hover": { - opacity: "1", +export const mediaPreviewButton = recipe({ + base: { + cursor: "pointer", + width: "20%", + height: "20%", + display: "block", + flexShrink: "0", + flexGrow: "0", + opacity: "0.3", + transition: "translate 0.3s ease-in-out, opacity 0.2s ease", + borderRadius: "4px", + border: `solid 1px ${vars.color.border}`, + ":hover": { + opacity: "0.8", + }, + }, + variants: { + active: { + true: { + opacity: "1", + }, + }, }, }); -export const gallerySliderMediaPreviewActive = style({ - opacity: 1, +export const mediaPreview = style({ + width: "100%", + height: "100%", + display: "flex", + flex: "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 gallerySliderButton = recipe({ + base: { + position: "absolute", + alignSelf: "center", + cursor: "pointer", + backgroundColor: "rgba(0, 0, 0, 0.4)", + transition: "all 0.2s ease-in-out", + borderRadius: "50%", + color: vars.color.muted, + width: "48px", + height: "48px", + ":hover": { + backgroundColor: "rgba(0, 0, 0, 0.6)", + }, + ":active": { + transform: "scale(0.95)", + }, + }, + variants: { + direction: { + left: { + left: "0", + marginLeft: `${SPACING_UNIT}px`, + transform: `translateX(${-(48 + SPACING_UNIT)}px)`, + }, + right: { + right: "0", + marginRight: `${SPACING_UNIT}px`, + transform: `translateX(${48 + SPACING_UNIT}px)`, + }, + }, + visible: { + true: { + transform: "translateX(0)", + opacity: "1", + }, + false: { + opacity: "0", + }, + }, }, }); - -export const gallerySliderIcons = style({ - fill: vars.color.muted, - width: "2rem", - height: "2rem", -}); diff --git a/src/renderer/src/pages/game-details/gallery-slider.tsx b/src/renderer/src/pages/game-details/gallery-slider.tsx index 1baca359..81bbf72d 100644 --- a/src/renderer/src/pages/game-details/gallery-slider.tsx +++ b/src/renderer/src/pages/game-details/gallery-slider.tsx @@ -1,7 +1,10 @@ -import { useEffect, useRef, useState } from "react"; -import { ShopDetails, SteamMovies, SteamScreenshot } from "@types"; +import { useEffect, useMemo, useRef, useState } from "react"; import { ChevronRightIcon, ChevronLeftIcon } from "@primer/octicons-react"; + +import type { ShopDetails } from "@types"; + import * as styles from "./gallery-slider.css"; +import { useTranslation } from "react-i18next"; export interface GallerySliderProps { gameDetails: ShopDetails | null; @@ -9,6 +12,12 @@ export interface GallerySliderProps { export function GallerySlider({ gameDetails }: GallerySliderProps) { const scrollContainerRef = useRef(null); + const mediaContainerRef = useRef(null); + + const { t } = useTranslation("game_details"); + + const hasScreenshots = gameDetails && gameDetails.screenshots.length; + const hasMovies = gameDetails && gameDetails.movies?.length; const [mediaCount] = useState(() => { if (gameDetails) { @@ -25,7 +34,7 @@ export function GallerySlider({ gameDetails }: GallerySliderProps) { }); const [mediaIndex, setMediaIndex] = useState(0); - const [arrowShow, setArrowShow] = useState(false); + const [showArrows, setShowArrows] = useState(false); const showNextImage = () => { setMediaIndex((index: number) => { @@ -47,6 +56,20 @@ export function GallerySlider({ gameDetails }: GallerySliderProps) { setMediaIndex(0); }, [gameDetails]); + useEffect(() => { + if (hasMovies && mediaContainerRef.current) { + mediaContainerRef.current.childNodes.forEach((node, index) => { + if (node instanceof HTMLVideoElement) { + if (index == mediaIndex) { + node.play(); + } else { + node.pause(); + } + } + }); + } + }, [hasMovies, mediaContainerRef, mediaIndex]); + useEffect(() => { if (scrollContainerRef.current) { const container = scrollContainerRef.current; @@ -57,92 +80,107 @@ export function GallerySlider({ gameDetails }: GallerySliderProps) { } }, [gameDetails, mediaIndex, mediaCount]); - const hasScreenshots = gameDetails && gameDetails.screenshots.length; - const hasMovies = gameDetails && gameDetails.movies?.length; + const previews = useMemo(() => { + const screenshotPreviews = + gameDetails?.screenshots.map(({ id, path_thumbnail }) => ({ + id, + thumbnail: path_thumbnail, + })) ?? []; + + if (gameDetails?.movies) { + const moviePreviews = gameDetails.movies.map(({ id, thumbnail }) => ({ + id, + thumbnail, + })); + + return [...moviePreviews, ...screenshotPreviews]; + } + + return screenshotPreviews; + }, [gameDetails]); return ( <> {hasScreenshots && (
setArrowShow(true)} - onMouseLeave={() => setArrowShow(false)} + onMouseEnter={() => setShowArrows(true)} + onMouseLeave={() => setShowArrows(false)} className={styles.gallerySliderAnimationContainer} + ref={mediaContainerRef} > {gameDetails.movies && - gameDetails.movies.map((video: SteamMovies) => ( + gameDetails.movies.map((video) => ( ))} - {gameDetails.screenshots && - gameDetails.screenshots.map( - (image: SteamScreenshot, i: number) => ( - - ) - )} - {arrowShow && ( - <> - - - - )} + {hasScreenshots && + gameDetails.screenshots.map((image, i) => ( + {t("screenshot", + ))} + + + +
- {hasMovies && - gameDetails.movies?.map((video: SteamMovies, i: number) => ( + {previews.map((media, i) => ( + + ))}
)}