From 9689c19863c1ddfc1fce0f05767c98c68d3e5d61 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Tue, 30 Sep 2025 00:52:46 +0300 Subject: [PATCH] fix: state fix and remake checking for overlap --- .../game-details/modals/edit-game-modal.tsx | 144 +++++++----------- .../user-library-game-card.scss | 20 +++ .../user-library-game-card.tsx | 73 ++------- 3 files changed, 93 insertions(+), 144 deletions(-) diff --git a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx index 3979d051..2413cb9e 100644 --- a/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/edit-game-modal.tsx @@ -29,19 +29,24 @@ export function EditGameModal({ const { showSuccessToast, showErrorToast } = useToast(); const [gameName, setGameName] = useState(""); - const [iconPath, setIconPath] = useState(""); - const [logoPath, setLogoPath] = useState(""); - const [heroPath, setHeroPath] = useState(""); - const [iconDisplayPath, setIconDisplayPath] = useState(""); - const [logoDisplayPath, setLogoDisplayPath] = useState(""); - const [heroDisplayPath, setHeroDisplayPath] = useState(""); + const [assetPaths, setAssetPaths] = useState({ + icon: "", + logo: "", + hero: "", + }); + const [assetDisplayPaths, setAssetDisplayPaths] = useState({ + icon: "", + logo: "", + hero: "", + }); + const [defaultUrls, setDefaultUrls] = useState({ + icon: null as string | null, + logo: null as string | null, + hero: null as string | null, + }); const [isUpdating, setIsUpdating] = useState(false); const [selectedAssetType, setSelectedAssetType] = useState("icon"); - const [defaultIconUrl, setDefaultIconUrl] = useState(null); - const [defaultLogoUrl, setDefaultLogoUrl] = useState(null); - const [defaultHeroUrl, setDefaultHeroUrl] = useState(null); - const isCustomGame = (game: LibraryGame | Game): boolean => { return game.shop === "custom"; }; @@ -51,32 +56,36 @@ export function EditGameModal({ }; const setCustomGameAssets = useCallback((game: LibraryGame | Game) => { - setIconPath(extractLocalPath(game.iconUrl)); - setLogoPath(extractLocalPath(game.logoImageUrl)); - setHeroPath(extractLocalPath(game.libraryHeroImageUrl)); - setIconDisplayPath(extractLocalPath(game.iconUrl)); - setLogoDisplayPath(extractLocalPath(game.logoImageUrl)); - setHeroDisplayPath(extractLocalPath(game.libraryHeroImageUrl)); + setAssetPaths({ + icon: extractLocalPath(game.iconUrl), + logo: extractLocalPath(game.logoImageUrl), + hero: extractLocalPath(game.libraryHeroImageUrl), + }); + setAssetDisplayPaths({ + icon: extractLocalPath(game.iconUrl), + logo: extractLocalPath(game.logoImageUrl), + hero: extractLocalPath(game.libraryHeroImageUrl), + }); }, []); const setNonCustomGameAssets = useCallback( (game: LibraryGame) => { - setIconPath(extractLocalPath(game.customIconUrl)); - setLogoPath(extractLocalPath(game.customLogoImageUrl)); - setHeroPath(extractLocalPath(game.customHeroImageUrl)); - setIconDisplayPath(extractLocalPath(game.customIconUrl)); - setLogoDisplayPath(extractLocalPath(game.customLogoImageUrl)); - setHeroDisplayPath(extractLocalPath(game.customHeroImageUrl)); + setAssetPaths({ + icon: extractLocalPath(game.customIconUrl), + logo: extractLocalPath(game.customLogoImageUrl), + hero: extractLocalPath(game.customHeroImageUrl), + }); + setAssetDisplayPaths({ + icon: extractLocalPath(game.customIconUrl), + logo: extractLocalPath(game.customLogoImageUrl), + hero: extractLocalPath(game.customHeroImageUrl), + }); - setDefaultIconUrl(shopDetails?.assets?.iconUrl || game.iconUrl || null); - setDefaultLogoUrl( - shopDetails?.assets?.logoImageUrl || game.logoImageUrl || null - ); - setDefaultHeroUrl( - shopDetails?.assets?.libraryHeroImageUrl || - game.libraryHeroImageUrl || - null - ); + setDefaultUrls({ + icon: shopDetails?.assets?.iconUrl || game.iconUrl || null, + logo: shopDetails?.assets?.logoImageUrl || game.logoImageUrl || null, + hero: shopDetails?.assets?.libraryHeroImageUrl || game.libraryHeroImageUrl || null, + }); }, [shopDetails] ); @@ -102,64 +111,23 @@ export function EditGameModal({ }; const getAssetPath = (assetType: AssetType): string => { - switch (assetType) { - case "icon": - return iconPath; - case "logo": - return logoPath; - case "hero": - return heroPath; - } + return assetPaths[assetType]; }; const getAssetDisplayPath = (assetType: AssetType): string => { - switch (assetType) { - case "icon": - return iconDisplayPath; - case "logo": - return logoDisplayPath; - case "hero": - return heroDisplayPath; - } + return assetDisplayPaths[assetType]; }; const setAssetPath = (assetType: AssetType, path: string): void => { - switch (assetType) { - case "icon": - setIconPath(path); - break; - case "logo": - setLogoPath(path); - break; - case "hero": - setHeroPath(path); - break; - } + setAssetPaths(prev => ({ ...prev, [assetType]: path })); }; const setAssetDisplayPath = (assetType: AssetType, path: string): void => { - switch (assetType) { - case "icon": - setIconDisplayPath(path); - break; - case "logo": - setLogoDisplayPath(path); - break; - case "hero": - setHeroDisplayPath(path); - break; - } + setAssetDisplayPaths(prev => ({ ...prev, [assetType]: path })); }; const getDefaultUrl = (assetType: AssetType): string | null => { - switch (assetType) { - case "icon": - return defaultIconUrl; - case "logo": - return defaultLogoUrl; - case "hero": - return defaultHeroUrl; - } + return defaultUrls[assetType]; }; const handleSelectAsset = async (assetType: AssetType) => { @@ -324,10 +292,10 @@ export function EditGameModal({ // Helper function to prepare custom game assets const prepareCustomGameAssets = (game: LibraryGame | Game) => { - const iconUrl = iconPath ? `local:${iconPath}` : game.iconUrl; - const logoImageUrl = logoPath ? `local:${logoPath}` : game.logoImageUrl; - const libraryHeroImageUrl = heroPath - ? `local:${heroPath}` + const iconUrl = assetPaths.icon ? `local:${assetPaths.icon}` : game.iconUrl; + const logoImageUrl = assetPaths.logo ? `local:${assetPaths.logo}` : game.logoImageUrl; + const libraryHeroImageUrl = assetPaths.hero + ? `local:${assetPaths.hero}` : game.libraryHeroImageUrl; return { iconUrl, logoImageUrl, libraryHeroImageUrl }; @@ -336,9 +304,9 @@ export function EditGameModal({ // Helper function to prepare non-custom game assets const prepareNonCustomGameAssets = () => { return { - customIconUrl: iconPath ? `local:${iconPath}` : null, - customLogoImageUrl: logoPath ? `local:${logoPath}` : null, - customHeroImageUrl: heroPath ? `local:${heroPath}` : null, + customIconUrl: assetPaths.icon ? `local:${assetPaths.icon}` : null, + customLogoImageUrl: assetPaths.logo ? `local:${assetPaths.logo}` : null, + customHeroImageUrl: assetPaths.hero ? `local:${assetPaths.hero}` : null, }; }; @@ -407,9 +375,11 @@ export function EditGameModal({ if (isCustomGame(game)) { setCustomGameAssets(game); // Clear default URLs for custom games - setDefaultIconUrl(null); - setDefaultLogoUrl(null); - setDefaultHeroUrl(null); + setDefaultUrls({ + icon: null, + logo: null, + hero: null, + }); } else { setNonCustomGameAssets(game as LibraryGame); } diff --git a/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss b/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss index e40061de..dccd9dd1 100644 --- a/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss +++ b/src/renderer/src/pages/profile/profile-content/user-library-game-card.scss @@ -8,6 +8,7 @@ display: flex; transition: all ease 0.2s; cursor: grab; + container-type: inline-size; &:hover { transform: scale(1.05); @@ -160,6 +161,25 @@ transform: translateY(-1px); box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); } + + &-long { + display: inline; + } + + &-short { + display: none; + } + + // When the card is narrow (less than 180px), show short format + @container (max-width: 180px) { + &-long { + display: none; + } + + &-short { + display: inline; + } + } } &__manual-playtime { color: globals.$warning-color; diff --git a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx index d30dbf8a..ec7736e0 100644 --- a/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx +++ b/src/renderer/src/pages/profile/profile-content/user-library-game-card.tsx @@ -2,7 +2,7 @@ import { UserGame } from "@types"; import HydraIcon from "@renderer/assets/icons/hydra.svg?react"; import { useFormat, useToast } from "@renderer/hooks"; import { useNavigate } from "react-router-dom"; -import { useCallback, useContext, useState, useEffect, useRef } from "react"; +import { useCallback, useContext, useState } from "react"; import { buildGameAchievementPath, buildGameDetailsPath, @@ -43,9 +43,7 @@ export function UserLibraryGameCard({ const navigate = useNavigate(); const [isTooltipHovered, setIsTooltipHovered] = useState(false); const [isPinning, setIsPinning] = useState(false); - const [useShortFormat, setUseShortFormat] = useState(false); - const cardRef = useRef(null); - const playtimeRef = useRef(null); + const getStatsItemCount = useCallback(() => { let statsCount = 1; @@ -82,64 +80,25 @@ export function UserLibraryGameCard({ }; const formatPlayTime = useCallback( - (playTimeInSeconds = 0) => { + (playTimeInSeconds = 0, isShort = false) => { const minutes = playTimeInSeconds / 60; if (minutes < MAX_MINUTES_TO_SHOW_IN_PLAYTIME) { - return t("amount_minutes", { + return t(isShort ? "amount_minutes_short" : "amount_minutes", { amount: minutes.toFixed(0), }); } const hours = minutes / 60; - return t("amount_hours", { amount: numberFormatter.format(hours) }); + 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 formatPlayTimeShort = useCallback( - (playTimeInSeconds = 0) => { - const minutes = playTimeInSeconds / 60; - if (minutes < MAX_MINUTES_TO_SHOW_IN_PLAYTIME) { - return t("amount_minutes_short", { - amount: minutes.toFixed(0), - }); - } - - const hours = minutes / 60; - return t("amount_hours_short", { amount: Math.floor(hours) }); - }, - [t] - ); - - const checkForOverlap = useCallback(() => { - if (!cardRef.current || !playtimeRef.current) return; - - const cardWidth = cardRef.current.offsetWidth; - const hasButtons = game.isFavorite || isMe; - - if (hasButtons && cardWidth < 180) { - setUseShortFormat(true); - } else { - setUseShortFormat(false); - } - }, [game.isFavorite, isMe]); - - useEffect(() => { - checkForOverlap(); - - const handleResize = () => { - checkForOverlap(); - }; - - window.addEventListener('resize', handleResize); - return () => window.removeEventListener('resize', handleResize); - }, [checkForOverlap]); - - useEffect(() => { - checkForOverlap(); - }, [game.isFavorite, isMe, checkForOverlap]); const toggleGamePinned = async () => { setIsPinning(true); @@ -166,7 +125,6 @@ export function UserLibraryGameCard({ return ( <>
  • )} - )} - {useShortFormat - ? formatPlayTimeShort(game.playTimeInSeconds) - : formatPlayTime(game.playTimeInSeconds) - } - + + {formatPlayTime(game.playTimeInSeconds)} + + + {formatPlayTime(game.playTimeInSeconds, true)} + + {userProfile?.hasActiveSubscription && game.achievementCount > 0 && (