From cb0fc826443fae60005f7b8614863f0a6eaf0c76 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Tue, 30 Sep 2025 14:24:00 +0300 Subject: [PATCH 1/3] feat: added ability to reset assets for custom game --- .../game-details/modals/edit-game-modal.tsx | 69 ++++++++++++++++--- 1 file changed, 58 insertions(+), 11 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 0f6df95d..93a8ff6f 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 @@ -4,6 +4,7 @@ import { ImageIcon, XIcon } from "@primer/octicons-react"; import { Modal, TextField, Button } from "@renderer/components"; import { useToast } from "@renderer/hooks"; +import { generateRandomGradient } from "@renderer/helpers"; import type { LibraryGame, Game, ShopDetailsWithAssets } from "@types"; import "./edit-game-modal.scss"; @@ -44,6 +45,11 @@ export function EditGameModal({ logo: "", hero: "", }); + const [removedAssets, setRemovedAssets] = useState({ + icon: false, + logo: false, + hero: false, + }); const [defaultUrls, setDefaultUrls] = useState({ icon: null as string | null, logo: null as string | null, @@ -158,6 +164,21 @@ export function EditGameModal({ return defaultUrls[assetType]; }; + const getOriginalAssetUrl = (assetType: AssetType): string | null => { + if (!game || !isCustomGame(game)) return null; + + switch (assetType) { + case "icon": + return game.iconUrl; + case "logo": + return game.logoImageUrl; + case "hero": + return game.libraryHeroImageUrl; + default: + return null; + } + }; + const handleSelectAsset = async (assetType: AssetType) => { const { filePaths } = await window.electron.showOpenDialog({ properties: ["openFile"], @@ -183,6 +204,8 @@ export function EditGameModal({ ...prev, [assetType]: originalPath, })); + // Clear the removed flag when a new asset is selected + setRemovedAssets((prev) => ({ ...prev, [assetType]: false })); } catch (error) { console.error(`Failed to copy ${assetType} asset:`, error); setAssetPath(assetType, originalPath); @@ -191,14 +214,25 @@ export function EditGameModal({ ...prev, [assetType]: originalPath, })); + // Clear the removed flag when a new asset is selected + setRemovedAssets((prev) => ({ ...prev, [assetType]: false })); } } }; const handleRestoreDefault = (assetType: AssetType) => { - setAssetPath(assetType, ""); - setAssetDisplayPath(assetType, ""); - setOriginalAssetPaths((prev) => ({ ...prev, [assetType]: "" })); + if (game && isCustomGame(game)) { + // For custom games, mark asset as removed and clear paths + setRemovedAssets((prev) => ({ ...prev, [assetType]: true })); + setAssetPath(assetType, ""); + setAssetDisplayPath(assetType, ""); + setOriginalAssetPaths((prev) => ({ ...prev, [assetType]: "" })); + } else { + // For non-custom games, clear custom assets (restore to shop defaults) + setAssetPath(assetType, ""); + setAssetDisplayPath(assetType, ""); + setOriginalAssetPaths((prev) => ({ ...prev, [assetType]: "" })); + } }; const getOriginalTitle = (): string => { @@ -330,13 +364,19 @@ export function EditGameModal({ // Helper function to prepare custom game assets const prepareCustomGameAssets = (game: LibraryGame | Game) => { - 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; + // For custom games, check if asset was explicitly removed + const iconUrl = removedAssets.icon ? null : (assetPaths.icon ? `local:${assetPaths.icon}` : game.iconUrl); + const logoImageUrl = removedAssets.logo ? null : (assetPaths.logo ? `local:${assetPaths.logo}` : game.logoImageUrl); + + // For hero image, if removed, restore to the original gradient or keep the original + let libraryHeroImageUrl; + if (removedAssets.hero) { + // If the original hero was a gradient (data URL), keep it, otherwise generate a new one + const originalHero = game.libraryHeroImageUrl; + libraryHeroImageUrl = originalHero?.startsWith('data:image/svg+xml') ? originalHero : generateRandomGradient(); + } else { + libraryHeroImageUrl = assetPaths.hero ? `local:${assetPaths.hero}` : game.libraryHeroImageUrl; + } return { iconUrl, logoImageUrl, libraryHeroImageUrl }; }; @@ -418,6 +458,13 @@ export function EditGameModal({ (game: LibraryGame | Game) => { setGameName(game.title || ""); + // Reset removed assets state + setRemovedAssets({ + icon: false, + logo: false, + hero: false, + }); + if (isCustomGame(game)) { setCustomGameAssets(game); // Clear default URLs for custom games @@ -481,7 +528,7 @@ export function EditGameModal({ {t("edit_game_modal_browse")} - {game && !isCustomGame(game) && assetPath && ( + {game && (assetPath || (isCustomGame(game) && getOriginalAssetUrl(assetType))) && ( - {game && (assetPath || (isCustomGame(game) && getOriginalAssetUrl(assetType))) && ( - - )} + {game && + (assetPath || + (isCustomGame(game) && getOriginalAssetUrl(assetType))) && ( + + )} } /> From 9c87964e1674bf89f8a9347b1967ff5899e14329 Mon Sep 17 00:00:00 2001 From: Moyasee Date: Tue, 30 Sep 2025 19:19:39 +0300 Subject: [PATCH 3/3] fix: added missing translations --- src/locales/en/translation.json | 8 +++++++- src/locales/ru/translation.json | 9 ++++++++- .../src/pages/game-details/modals/edit-game-modal.tsx | 4 ++-- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 93fd5b0a..327499d0 100755 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -70,7 +70,13 @@ "edit_game_modal_icon_resolution": "Recommended resolution: 256x256px", "edit_game_modal_logo_resolution": "Recommended resolution: 640x360px", "edit_game_modal_hero_resolution": "Recommended resolution: 1920x620px", - "edit_game_modal_assets": "Assets" + "edit_game_modal_assets": "Assets", + "edit_game_modal_drop_icon_image_here": "Drop icon image here", + "edit_game_modal_drop_logo_image_here": "Drop logo image here", + "edit_game_modal_drop_hero_image_here": "Drop hero image here", + "edit_game_modal_drop_to_replace_icon": "Drop to replace icon", + "edit_game_modal_drop_to_replace_logo": "Drop to replace logo", + "edit_game_modal_drop_to_replace_hero": "Drop to replace hero" }, "header": { "search": "Search games", diff --git a/src/locales/ru/translation.json b/src/locales/ru/translation.json index 03413554..8992a4a0 100644 --- a/src/locales/ru/translation.json +++ b/src/locales/ru/translation.json @@ -67,7 +67,14 @@ "edit_game_modal_image_filter": "Изображение", "edit_game_modal_icon_resolution": "Рекомендуемое разрешение: 256x256px", "edit_game_modal_logo_resolution": "Рекомендуемое разрешение: 640x360px", - "edit_game_modal_hero_resolution": "Рекомендуемое разрешение: 1920x620px" + "edit_game_modal_hero_resolution": "Рекомендуемое разрешение: 1920x620px", + "edit_game_modal_assets": "Ресурсы", + "edit_game_modal_drop_icon_image_here": "Перетащите изображение иконки сюда", + "edit_game_modal_drop_logo_image_here": "Перетащите изображение логотипа сюда", + "edit_game_modal_drop_hero_image_here": "Перетащите изображение обложки сюда", + "edit_game_modal_drop_to_replace_icon": "Перетащите для замены иконки", + "edit_game_modal_drop_to_replace_logo": "Перетащите для замены логотипа", + "edit_game_modal_drop_to_replace_hero": "Перетащите для замены обложки" }, "header": { "search": "Поиск", 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 691dfa7c..a31ce400 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 @@ -587,7 +587,7 @@ export function EditGameModal({ /> {isDragOver && (
- Drop to replace {assetType} + {t(`edit_game_modal_drop_to_replace_${assetType}`)}
)} @@ -610,7 +610,7 @@ export function EditGameModal({ >
- Drop {assetType} image here + {t(`edit_game_modal_drop_${assetType}_image_here`)}
)}