diff --git a/src/main/events/library/add-custom-game-to-library.ts b/src/main/events/library/add-custom-game-to-library.ts index 382328a5..47fd3436 100644 --- a/src/main/events/library/add-custom-game-to-library.ts +++ b/src/main/events/library/add-custom-game-to-library.ts @@ -1,13 +1,8 @@ import { registerEvent } from "../register-event"; -import { - gamesSublevel, - gamesShopAssetsSublevel, - levelKeys, -} from "@main/level"; +import { gamesSublevel, gamesShopAssetsSublevel, levelKeys } from "@main/level"; import { randomUUID } from "crypto"; import type { GameShop } from "@types"; - const addCustomGameToLibrary = async ( _event: Electron.IpcMainInvokeEvent, title: string, @@ -21,12 +16,14 @@ const addCustomGameToLibrary = async ( const gameKey = levelKeys.game(shop, objectId); const existingGames = await gamesSublevel.iterator().all(); - const existingGame = existingGames.find(([_key, game]) => - game.executablePath === executablePath && !game.isDeleted + const existingGame = existingGames.find( + ([_key, game]) => game.executablePath === executablePath && !game.isDeleted ); if (existingGame) { - throw new Error("A game with this executable path already exists in your library"); + throw new Error( + "A game with this executable path already exists in your library" + ); } const assets = { @@ -65,4 +62,4 @@ const addCustomGameToLibrary = async ( return game; }; -registerEvent("addCustomGameToLibrary", addCustomGameToLibrary); \ No newline at end of file +registerEvent("addCustomGameToLibrary", addCustomGameToLibrary); diff --git a/src/main/events/library/add-game-to-library.ts b/src/main/events/library/add-game-to-library.ts index 01495a39..2fa6d20e 100644 --- a/src/main/events/library/add-game-to-library.ts +++ b/src/main/events/library/add-game-to-library.ts @@ -30,6 +30,8 @@ const addGameToLibrary = async ( game = { title, iconUrl: gameAssets?.iconUrl ?? null, + libraryHeroImageUrl: gameAssets?.libraryHeroImageUrl ?? null, + logoImageUrl: gameAssets?.logoImageUrl ?? null, objectId, shop, remoteId: null, diff --git a/src/main/events/library/change-game-playtime.ts b/src/main/events/library/change-game-playtime.ts index 20da70ec..8ad252bd 100644 --- a/src/main/events/library/change-game-playtime.ts +++ b/src/main/events/library/change-game-playtime.ts @@ -13,13 +13,13 @@ const changeGamePlaytime = async ( const gameKey = levelKeys.game(shop, objectId); const game = await gamesSublevel.get(gameKey); if (!game) return; - + if (game.remoteId) { await HydraApi.put(`/profile/games/${shop}/${objectId}/playtime`, { playTimeInSeconds, }); } - + await gamesSublevel.put(gameKey, { ...game, playTimeInMilliseconds: playTimeInSeconds * 1000, diff --git a/src/main/events/library/get-library.ts b/src/main/events/library/get-library.ts index ce859908..547045ae 100644 --- a/src/main/events/library/get-library.ts +++ b/src/main/events/library/get-library.ts @@ -18,11 +18,17 @@ const getLibrary = async (): Promise => { const download = await downloadsSublevel.get(key); const gameAssets = await gamesShopAssetsSublevel.get(key); + // 确保返回的对象符合 LibraryGame 类型 return { id: key, ...game, download: download ?? null, - ...gameAssets, + // 确保 gameAssets 中的可能为 null 的字段转换为 undefined + libraryHeroImageUrl: gameAssets?.libraryHeroImageUrl ?? undefined, + libraryImageUrl: gameAssets?.libraryImageUrl ?? undefined, + logoImageUrl: gameAssets?.logoImageUrl ?? undefined, + logoPosition: gameAssets?.logoPosition ?? undefined, + coverImageUrl: gameAssets?.coverImageUrl ?? undefined, }; }) ); diff --git a/src/main/events/library/update-custom-game.ts b/src/main/events/library/update-custom-game.ts index 4ff858a2..6152c0df 100644 --- a/src/main/events/library/update-custom-game.ts +++ b/src/main/events/library/update-custom-game.ts @@ -1,9 +1,5 @@ import { registerEvent } from "../register-event"; -import { - gamesSublevel, - gamesShopAssetsSublevel, - levelKeys, -} from "@main/level"; +import { gamesSublevel, gamesShopAssetsSublevel, levelKeys } from "@main/level"; import type { GameShop } from "@types"; const updateCustomGame = async ( @@ -16,7 +12,7 @@ const updateCustomGame = async ( libraryHeroImageUrl?: string ) => { const gameKey = levelKeys.game(shop, objectId); - + const existingGame = await gamesSublevel.get(gameKey); if (!existingGame) { throw new Error("Game not found"); @@ -50,4 +46,4 @@ const updateCustomGame = async ( return updatedGame; }; -registerEvent("updateCustomGame", updateCustomGame); \ No newline at end of file +registerEvent("updateCustomGame", updateCustomGame); diff --git a/src/main/events/torrenting/start-game-download.ts b/src/main/events/torrenting/start-game-download.ts index 00281973..8216b519 100644 --- a/src/main/events/torrenting/start-game-download.ts +++ b/src/main/events/torrenting/start-game-download.ts @@ -53,6 +53,8 @@ const startGameDownload = async ( await gamesSublevel.put(gameKey, { title, iconUrl: gameAssets?.iconUrl ?? null, + libraryHeroImageUrl: gameAssets?.libraryHeroImageUrl ?? null, + logoImageUrl: gameAssets?.logoImageUrl ?? null, objectId, shop, remoteId: null, diff --git a/src/main/index.ts b/src/main/index.ts index af9aa676..ab6980f9 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -65,36 +65,61 @@ app.whenReady().then(async () => { }); protocol.handle("gradient", (request) => { - const gradientCss = decodeURIComponent(request.url.slice("gradient:".length)); - - const match = gradientCss.match(/linear-gradient\(([^,]+),\s*([^,]+),\s*([^)]+)\)/); - + const gradientCss = decodeURIComponent( + request.url.slice("gradient:".length) + ); + + const match = gradientCss.match( + /linear-gradient\(([^,]+),\s*([^,]+),\s*([^)]+)\)/ + ); + let direction = "45deg"; - let color1 = '#4a90e2'; - let color2 = '#7b68ee'; - + let color1 = "#4a90e2"; + let color2 = "#7b68ee"; + if (match) { direction = match[1].trim(); color1 = match[2].trim(); color2 = match[3].trim(); } - - let x1 = "0%", y1 = "0%", x2 = "100%", y2 = "100%"; - + + let x1 = "0%", + y1 = "0%", + x2 = "100%", + y2 = "100%"; + if (direction === "to right") { - x1 = "0%"; y1 = "0%"; x2 = "100%"; y2 = "0%"; + x1 = "0%"; + y1 = "0%"; + x2 = "100%"; + y2 = "0%"; } else if (direction === "to bottom") { - x1 = "0%"; y1 = "0%"; x2 = "0%"; y2 = "100%"; + x1 = "0%"; + y1 = "0%"; + x2 = "0%"; + y2 = "100%"; } else if (direction === "45deg") { - x1 = "0%"; y1 = "100%"; x2 = "100%"; y2 = "0%"; + x1 = "0%"; + y1 = "100%"; + x2 = "100%"; + y2 = "0%"; } else if (direction === "135deg") { - x1 = "0%"; y1 = "0%"; x2 = "100%"; y2 = "100%"; + x1 = "0%"; + y1 = "0%"; + x2 = "100%"; + y2 = "100%"; } else if (direction === "225deg") { - x1 = "100%"; y1 = "0%"; x2 = "0%"; y2 = "100%"; + x1 = "100%"; + y1 = "0%"; + x2 = "0%"; + y2 = "100%"; } else if (direction === "315deg") { - x1 = "100%"; y1 = "100%"; x2 = "0%"; y2 = "0%"; + x1 = "100%"; + y1 = "100%"; + x2 = "0%"; + y2 = "0%"; } - + const svgContent = ` @@ -106,9 +131,9 @@ app.whenReady().then(async () => { `; - + return new Response(svgContent, { - headers: { 'Content-Type': 'image/svg+xml' } + headers: { "Content-Type": "image/svg+xml" }, }); }); diff --git a/src/main/services/library-sync/upload-games-batch.ts b/src/main/services/library-sync/upload-games-batch.ts index 10d2c8dd..653b5f40 100644 --- a/src/main/services/library-sync/upload-games-batch.ts +++ b/src/main/services/library-sync/upload-games-batch.ts @@ -11,7 +11,8 @@ export const uploadGamesBatch = async () => { .all() .then((results) => { return results.filter( - (game) => !game.isDeleted && game.remoteId === null && game.shop !== "custom" + (game) => + !game.isDeleted && game.remoteId === null && game.shop !== "custom" ); }); diff --git a/src/preload/index.ts b/src/preload/index.ts index b9ad8d98..8d297051 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -135,7 +135,14 @@ contextBridge.exposeInMainWorld("electron", { logoImageUrl?: string, libraryHeroImageUrl?: string ) => - ipcRenderer.invoke("addCustomGameToLibrary", title, executablePath, iconUrl, logoImageUrl, libraryHeroImageUrl), + ipcRenderer.invoke( + "addCustomGameToLibrary", + title, + executablePath, + iconUrl, + logoImageUrl, + libraryHeroImageUrl + ), updateCustomGame: ( shop: GameShop, objectId: string, @@ -144,7 +151,15 @@ contextBridge.exposeInMainWorld("electron", { logoImageUrl?: string, libraryHeroImageUrl?: string ) => - ipcRenderer.invoke("updateCustomGame", shop, objectId, title, iconUrl, logoImageUrl, libraryHeroImageUrl), + ipcRenderer.invoke( + "updateCustomGame", + shop, + objectId, + title, + iconUrl, + logoImageUrl, + libraryHeroImageUrl + ), createGameShortcut: ( shop: GameShop, objectId: string, @@ -493,6 +508,4 @@ contextBridge.exposeInMainWorld("electron", { }, closeEditorWindow: (themeId?: string) => ipcRenderer.invoke("closeEditorWindow", themeId), - - }); diff --git a/src/renderer/src/components/sidebar/sidebar-adding-custom-game-modal.scss b/src/renderer/src/components/sidebar/sidebar-adding-custom-game-modal.scss index 48b84da3..942384fe 100644 --- a/src/renderer/src/components/sidebar/sidebar-adding-custom-game-modal.scss +++ b/src/renderer/src/components/sidebar/sidebar-adding-custom-game-modal.scss @@ -49,4 +49,4 @@ justify-content: flex-end; gap: calc(globals.$spacing-unit * 2); } -} \ No newline at end of file +} diff --git a/src/renderer/src/components/sidebar/sidebar-adding-custom-game-modal.tsx b/src/renderer/src/components/sidebar/sidebar-adding-custom-game-modal.tsx index 771c5464..4b18a211 100644 --- a/src/renderer/src/components/sidebar/sidebar-adding-custom-game-modal.tsx +++ b/src/renderer/src/components/sidebar/sidebar-adding-custom-game-modal.tsx @@ -5,7 +5,10 @@ import { FileDirectoryIcon } from "@primer/octicons-react"; import { Modal, TextField, Button } from "@renderer/components"; import { useLibrary, useToast } from "@renderer/hooks"; -import { buildGameDetailsPath, generateRandomGradient } from "@renderer/helpers"; +import { + buildGameDetailsPath, + generateRandomGradient, +} from "@renderer/helpers"; import "./sidebar-adding-custom-game-modal.scss"; @@ -41,7 +44,7 @@ export function SidebarAddingCustomGameModal({ if (filePaths && filePaths.length > 0) { const selectedPath = filePaths[0]; setExecutablePath(selectedPath); - + if (!gameName.trim()) { const fileName = selectedPath.split(/[\\/]/).pop() || ""; const gameNameFromFile = fileName.replace(/\.[^/.]+$/, ""); @@ -54,8 +57,6 @@ export function SidebarAddingCustomGameModal({ setGameName(event.target.value); }; - - const handleAddGame = async () => { if (!gameName.trim() || !executablePath.trim()) { showErrorToast(t("custom_game_modal_fill_required")); @@ -70,7 +71,7 @@ export function SidebarAddingCustomGameModal({ const iconUrl = ""; // Don't use gradient for icon const logoImageUrl = ""; // Don't use gradient for logo const libraryHeroImageUrl = generateRandomGradient(); // Only use gradient for hero - + const newGame = await window.electron.addCustomGameToLibrary( gameNameForSeed, executablePath, @@ -81,24 +82,22 @@ export function SidebarAddingCustomGameModal({ showSuccessToast(t("custom_game_modal_success")); updateLibrary(); - + const gameDetailsPath = buildGameDetailsPath({ shop: "custom", objectId: newGame.objectId, - title: newGame.title + title: newGame.title, }); - + navigate(gameDetailsPath); - + setGameName(""); setExecutablePath(""); onClose(); } catch (error) { console.error("Failed to add custom game:", error); showErrorToast( - error instanceof Error - ? error.message - : t("custom_game_modal_failed") + error instanceof Error ? error.message : t("custom_game_modal_failed") ); } finally { setIsAdding(false); @@ -115,8 +114,6 @@ export function SidebarAddingCustomGameModal({ const isFormValid = gameName.trim() && executablePath.trim(); - - return ( - - - -
- -
); -} \ No newline at end of file +} diff --git a/src/renderer/src/components/sidebar/sidebar.tsx b/src/renderer/src/components/sidebar/sidebar.tsx index a7d946a2..f77066a2 100644 --- a/src/renderer/src/components/sidebar/sidebar.tsx +++ b/src/renderer/src/components/sidebar/sidebar.tsx @@ -22,7 +22,11 @@ import { buildGameDetailsPath } from "@renderer/helpers"; import { SidebarProfile } from "./sidebar-profile"; import { sortBy } from "lodash-es"; import cn from "classnames"; -import { CommentDiscussionIcon, PlayIcon, PlusIcon } from "@primer/octicons-react"; +import { + CommentDiscussionIcon, + PlayIcon, + PlusIcon, +} from "@primer/octicons-react"; import { SidebarGameItem } from "./sidebar-game-item"; import { SidebarAddingCustomGameModal } from "./sidebar-adding-custom-game-modal"; import { setFriendRequestCount } from "@renderer/features/user-details-slice"; @@ -265,7 +269,9 @@ export function Sidebar() { {t("my_library")} -
+
} /> - + {iconPath && (
} /> - + {logoPath && (
} /> - + {heroPath && (
- -
); -} \ No newline at end of file +}