diff --git a/src/renderer/src/hooks/index.ts b/src/renderer/src/hooks/index.ts index 73733e2b..23190def 100644 --- a/src/renderer/src/hooks/index.ts +++ b/src/renderer/src/hooks/index.ts @@ -6,3 +6,4 @@ export * from "./redux"; export * from "./use-user-details"; export * from "./use-format"; export * from "./use-feature"; +export * from "./use-game-card"; diff --git a/src/renderer/src/hooks/use-game-card.ts b/src/renderer/src/hooks/use-game-card.ts new file mode 100644 index 00000000..98987189 --- /dev/null +++ b/src/renderer/src/hooks/use-game-card.ts @@ -0,0 +1,66 @@ +import { useCallback } from "react"; +import { useNavigate } from "react-router-dom"; +import { useFormat } from "./use-format"; +import { useTranslation } from "react-i18next"; +import { buildGameDetailsPath } from "@renderer/helpers"; +import { MAX_MINUTES_TO_SHOW_IN_PLAYTIME } from "@renderer/constants"; +import { LibraryGame } from "@types"; + +export function useGameCard( + game: LibraryGame, + onContextMenu: (game: LibraryGame, position: { x: number; y: number }) => void +) { + const { t } = useTranslation("library"); + const { numberFormatter } = useFormat(); + const navigate = useNavigate(); + + const formatPlayTime = useCallback( + (playTimeInMilliseconds = 0, isShort = false) => { + const minutes = playTimeInMilliseconds / 60000; + + if (minutes < MAX_MINUTES_TO_SHOW_IN_PLAYTIME) { + return t(isShort ? "amount_minutes_short" : "amount_minutes", { + amount: minutes.toFixed(0), + }); + } + + const hours = minutes / 60; + const hoursKey = isShort ? "amount_hours_short" : "amount_hours"; + const hoursAmount = isShort + ? Math.floor(hours) + : numberFormatter.format(hours); + + return t(hoursKey, { amount: hoursAmount }); + }, + [numberFormatter, t] + ); + + const handleCardClick = useCallback(() => { + navigate(buildGameDetailsPath(game)); + }, [navigate, game]); + + const handleContextMenuClick = useCallback( + (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + onContextMenu(game, { x: e.clientX, y: e.clientY }); + }, + [game, onContextMenu] + ); + + const handleMenuButtonClick = useCallback( + (e: React.MouseEvent) => { + e.stopPropagation(); + const rect = e.currentTarget.getBoundingClientRect(); + onContextMenu(game, { x: rect.right, y: rect.bottom }); + }, + [game, onContextMenu] + ); + + return { + formatPlayTime, + handleCardClick, + handleContextMenuClick, + handleMenuButtonClick, + }; +} diff --git a/src/renderer/src/pages/library/library-game-card-large.tsx b/src/renderer/src/pages/library/library-game-card-large.tsx index 4cb54977..5c0e54c4 100644 --- a/src/renderer/src/pages/library/library-game-card-large.tsx +++ b/src/renderer/src/pages/library/library-game-card-large.tsx @@ -1,7 +1,5 @@ import { LibraryGame } from "@types"; -import { useDownload, useFormat } from "@renderer/hooks"; -import { useNavigate } from "react-router-dom"; -import { buildGameDetailsPath } from "@renderer/helpers"; +import { useDownload, useGameCard } from "@renderer/hooks"; import { PlayIcon, DownloadIcon, @@ -12,9 +10,8 @@ import { XIcon, } from "@primer/octicons-react"; import { useTranslation } from "react-i18next"; -import { useCallback, memo, useMemo } from "react"; +import { memo, useMemo } from "react"; import { useGameActions } from "@renderer/components/game-context-menu/use-game-actions"; -import { MAX_MINUTES_TO_SHOW_IN_PLAYTIME } from "@renderer/constants"; import { logger } from "@renderer/logger"; import "./library-game-card-large.scss"; @@ -39,38 +36,17 @@ export const LibraryGameCardLarge = memo(function LibraryGameCardLarge({ onContextMenu, }: Readonly) { const { t } = useTranslation("library"); - const { numberFormatter } = useFormat(); - const navigate = useNavigate(); const { lastPacket } = useDownload(); + const { + formatPlayTime, + handleCardClick, + handleContextMenuClick, + handleMenuButtonClick, + } = useGameCard(game, onContextMenu); const isGameDownloading = game?.download?.status === "active" && lastPacket?.gameId === game?.id; - const formatPlayTime = useCallback( - (playTimeInMilliseconds = 0, isShort = false) => { - const minutes = playTimeInMilliseconds / 60000; - - if (minutes < MAX_MINUTES_TO_SHOW_IN_PLAYTIME) { - return t(isShort ? "amount_minutes_short" : "amount_minutes", { - amount: minutes.toFixed(0), - }); - } - - const hours = minutes / 60; - const hoursKey = isShort ? "amount_hours_short" : "amount_hours"; - const hoursAmount = isShort - ? Math.floor(hours) - : numberFormatter.format(hours); - - return t(hoursKey, { amount: hoursAmount }); - }, - [numberFormatter, t] - ); - - const handleCardClick = () => { - navigate(buildGameDetailsPath(game)); - }; - const { handlePlayGame, handleOpenDownloadOptions, @@ -101,24 +77,6 @@ export const LibraryGameCardLarge = memo(function LibraryGameCardLarge({ } }; - const handleContextMenuClick = useCallback( - (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - onContextMenu(game, { x: e.clientX, y: e.clientY }); - }, - [game, onContextMenu] - ); - - const handleMenuButtonClick = useCallback( - (e: React.MouseEvent) => { - e.stopPropagation(); - const rect = e.currentTarget.getBoundingClientRect(); - onContextMenu(game, { x: rect.right, y: rect.bottom }); - }, - [game, onContextMenu] - ); - const backgroundImage = useMemo( () => getImageWithCustomPriority( diff --git a/src/renderer/src/pages/library/library-game-card.tsx b/src/renderer/src/pages/library/library-game-card.tsx index 1b5b7afa..64053155 100644 --- a/src/renderer/src/pages/library/library-game-card.tsx +++ b/src/renderer/src/pages/library/library-game-card.tsx @@ -1,16 +1,12 @@ import { LibraryGame } from "@types"; -import { useFormat } from "@renderer/hooks"; -import { useNavigate } from "react-router-dom"; -import { useCallback, memo } from "react"; -import { buildGameDetailsPath } from "@renderer/helpers"; +import { useGameCard } from "@renderer/hooks"; +import { memo } from "react"; import { ClockIcon, AlertFillIcon, ThreeBarsIcon, TrophyIcon, } from "@primer/octicons-react"; -import { MAX_MINUTES_TO_SHOW_IN_PLAYTIME } from "@renderer/constants"; -import { useTranslation } from "react-i18next"; import "./library-game-card.scss"; interface LibraryGameCardProps { @@ -31,52 +27,12 @@ export const LibraryGameCard = memo(function LibraryGameCard({ onMouseLeave, onContextMenu, }: Readonly) { - const { t } = useTranslation("library"); - const { numberFormatter } = useFormat(); - const navigate = useNavigate(); - - const formatPlayTime = useCallback( - (playTimeInMilliseconds = 0, isShort = false) => { - const minutes = playTimeInMilliseconds / 60000; - - if (minutes < MAX_MINUTES_TO_SHOW_IN_PLAYTIME) { - return t(isShort ? "amount_minutes_short" : "amount_minutes", { - amount: minutes.toFixed(0), - }); - } - - const hours = minutes / 60; - const hoursKey = isShort ? "amount_hours_short" : "amount_hours"; - const hoursAmount = isShort - ? Math.floor(hours) - : numberFormatter.format(hours); - - return t(hoursKey, { amount: hoursAmount }); - }, - [numberFormatter, t] - ); - - const handleCardClick = () => { - navigate(buildGameDetailsPath(game)); - }; - - const handleContextMenuClick = useCallback( - (e: React.MouseEvent) => { - e.preventDefault(); - e.stopPropagation(); - onContextMenu(game, { x: e.clientX, y: e.clientY }); - }, - [game, onContextMenu] - ); - - const handleMenuButtonClick = useCallback( - (e: React.MouseEvent) => { - e.stopPropagation(); - const rect = e.currentTarget.getBoundingClientRect(); - onContextMenu(game, { x: rect.right, y: rect.bottom }); - }, - [game, onContextMenu] - ); + const { + formatPlayTime, + handleCardClick, + handleContextMenuClick, + handleMenuButtonClick, + } = useGameCard(game, onContextMenu); const coverImage = game.coverImageUrl ??