From 7e3cf0a00e74df450de64a1a35b151b608a3793d Mon Sep 17 00:00:00 2001 From: Zamitto <167933696+zamitto@users.noreply.github.com> Date: Tue, 24 Sep 2024 13:56:32 -0300 Subject: [PATCH] feat: starting showing local achievements --- src/main/entity/game-achievements.entity.ts | 4 +- src/main/entity/game.entity.ts | 4 ++ .../events/catalogue/get-game-achievements.ts | 46 +++++++++++++++++++ src/main/events/catalogue/get-game-stats.ts | 9 ++-- src/main/events/index.ts | 1 + src/main/index.ts | 1 + src/main/main.ts | 2 +- .../game-achievements-observer.ts | 0 .../get-game-achievements-to-watch.ts | 0 .../save-all-local-steam-achivements.ts | 0 .../steam/steam-achievement-info.ts | 0 .../steam/steam-achievement-merge.ts | 0 .../steam/steam-find-game-achivement-files.ts | 0 .../steam/steam-get-achivement.ts | 0 .../steam-global-achievement-percentages.ts | 0 .../achievements/types/index.ts | 0 .../util/check-unlocked-achievements.ts | 0 .../achievements/util/parseAchievementFile.ts | 0 src/main/services/process-watcher.ts | 2 +- src/preload/index.ts | 3 ++ .../game-details/game-details.context.tsx | 11 ++++- .../game-details.context.types.ts | 2 + src/renderer/src/declaration.d.ts | 5 ++ .../pages/game-details/sidebar/sidebar.tsx | 14 +++++- src/types/index.ts | 10 ++++ 25 files changed, 102 insertions(+), 12 deletions(-) create mode 100644 src/main/events/catalogue/get-game-achievements.ts rename src/main/{events => services}/achievements/game-achievements-observer.ts (100%) rename src/main/{events => services}/achievements/services/get-game-achievements-to-watch.ts (100%) rename src/main/{events => services}/achievements/services/save-all-local-steam-achivements.ts (100%) rename src/main/{events => services}/achievements/steam/steam-achievement-info.ts (100%) rename src/main/{events => services}/achievements/steam/steam-achievement-merge.ts (100%) rename src/main/{events => services}/achievements/steam/steam-find-game-achivement-files.ts (100%) rename src/main/{events => services}/achievements/steam/steam-get-achivement.ts (100%) rename src/main/{events => services}/achievements/steam/steam-global-achievement-percentages.ts (100%) rename src/main/{events => services}/achievements/types/index.ts (100%) rename src/main/{events => services}/achievements/util/check-unlocked-achievements.ts (100%) rename src/main/{events => services}/achievements/util/parseAchievementFile.ts (100%) diff --git a/src/main/entity/game-achievements.entity.ts b/src/main/entity/game-achievements.entity.ts index 9bad73e5..48ca958b 100644 --- a/src/main/entity/game-achievements.entity.ts +++ b/src/main/entity/game-achievements.entity.ts @@ -5,14 +5,14 @@ import { OneToOne, PrimaryGeneratedColumn, } from "typeorm"; -import { Game } from "./game.entity"; +import type { Game } from "./game.entity"; @Entity("game_achievement") export class GameAchievement { @PrimaryGeneratedColumn() id: number; - @OneToOne(() => Game) + @OneToOne("Game", "achievements") @JoinColumn() game: Game; diff --git a/src/main/entity/game.entity.ts b/src/main/entity/game.entity.ts index 190e7470..98606167 100644 --- a/src/main/entity/game.entity.ts +++ b/src/main/entity/game.entity.ts @@ -12,6 +12,7 @@ import { Repack } from "./repack.entity"; import type { GameShop, GameStatus } from "@types"; import { Downloader } from "@shared"; import type { DownloadQueue } from "./download-queue.entity"; +import { GameAchievement } from "./game-achievements.entity"; @Entity("game") export class Game { @@ -76,6 +77,9 @@ export class Game { @JoinColumn() repack: Repack; + @OneToOne("GameAchievement", "game") + achievements: GameAchievement; + @OneToOne("DownloadQueue", "game") downloadQueue: DownloadQueue; diff --git a/src/main/events/catalogue/get-game-achievements.ts b/src/main/events/catalogue/get-game-achievements.ts new file mode 100644 index 00000000..3325b3c0 --- /dev/null +++ b/src/main/events/catalogue/get-game-achievements.ts @@ -0,0 +1,46 @@ +import type { GameShop } from "@types"; + +import { registerEvent } from "../register-event"; +import { HydraApi } from "@main/services"; +import { gameRepository } from "@main/repository"; +import { GameAchievement } from "@main/entity"; + +const getGameAchievements = async ( + _event: Electron.IpcMainInvokeEvent, + objectId: string, + shop: GameShop +): Promise => { + const game = await gameRepository.findOne({ + where: { objectID: objectId, shop }, + relations: { + achievements: true, + }, + }); + const gameAchievements = await HydraApi.get( + "/games/achievements", + { objectId, shop }, + { needsAuth: false } + ); + + const unlockedAchievements = JSON.parse( + game?.achievements?.unlockedAchievements || "[]" + ) as { name: string; unlockTime: number }[]; + + return gameAchievements.map((achievement) => { + const unlockedAchiement = unlockedAchievements.find((localAchievement) => { + return localAchievement.name == achievement.name; + }); + + if (unlockedAchiement) { + return { + ...achievement, + unlocked: true, + unlockTime: unlockedAchiement.unlockTime * 1000, + }; + } + + return { ...achievement, unlocked: false, unlockTime: null }; + }); +}; + +registerEvent("getGameAchievements", getGameAchievements); diff --git a/src/main/events/catalogue/get-game-stats.ts b/src/main/events/catalogue/get-game-stats.ts index 87cba054..0d03b42d 100644 --- a/src/main/events/catalogue/get-game-stats.ts +++ b/src/main/events/catalogue/get-game-stats.ts @@ -9,13 +9,10 @@ const getGameStats = async ( objectId: string, shop: GameShop ) => { - const params = new URLSearchParams({ - objectId, - shop, - }); - const response = await HydraApi.get( - `/games/stats?${params.toString()}` + `/games/stats`, + { objectId, shop }, + { needsAuth: false } ); return response; }; diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 0337c9d8..da11ccc3 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -10,6 +10,7 @@ import "./catalogue/search-games"; import "./catalogue/search-game-repacks"; import "./catalogue/get-game-stats"; import "./catalogue/get-trending-games"; +import "./catalogue/get-game-achievements"; import "./hardware/get-disk-free-space"; import "./library/add-game-to-library"; import "./library/create-game-shortcut"; diff --git a/src/main/index.ts b/src/main/index.ts index 00311b46..7281d1fc 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -67,6 +67,7 @@ const runMigrations = async () => { ); }); + await knexClient.migrate.down(migrationConfig); await knexClient.migrate.latest(migrationConfig); await knexClient.destroy(); }; diff --git a/src/main/main.ts b/src/main/main.ts index abaa93a7..5e34a593 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -16,7 +16,7 @@ import { publishNewRepacksNotifications } from "./services/notifications"; import { MoreThan } from "typeorm"; import { HydraApi } from "./services/hydra-api"; import { uploadGamesBatch } from "./services/library-sync"; -import { saveAllLocalSteamAchivements } from "./events/achievements/services/save-all-local-steam-achivements"; +import { saveAllLocalSteamAchivements } from "./services/achievements/services/save-all-local-steam-achivements"; const loadState = async (userPreferences: UserPreferences | null) => { RepacksManager.updateRepacks(); diff --git a/src/main/events/achievements/game-achievements-observer.ts b/src/main/services/achievements/game-achievements-observer.ts similarity index 100% rename from src/main/events/achievements/game-achievements-observer.ts rename to src/main/services/achievements/game-achievements-observer.ts diff --git a/src/main/events/achievements/services/get-game-achievements-to-watch.ts b/src/main/services/achievements/services/get-game-achievements-to-watch.ts similarity index 100% rename from src/main/events/achievements/services/get-game-achievements-to-watch.ts rename to src/main/services/achievements/services/get-game-achievements-to-watch.ts diff --git a/src/main/events/achievements/services/save-all-local-steam-achivements.ts b/src/main/services/achievements/services/save-all-local-steam-achivements.ts similarity index 100% rename from src/main/events/achievements/services/save-all-local-steam-achivements.ts rename to src/main/services/achievements/services/save-all-local-steam-achivements.ts diff --git a/src/main/events/achievements/steam/steam-achievement-info.ts b/src/main/services/achievements/steam/steam-achievement-info.ts similarity index 100% rename from src/main/events/achievements/steam/steam-achievement-info.ts rename to src/main/services/achievements/steam/steam-achievement-info.ts diff --git a/src/main/events/achievements/steam/steam-achievement-merge.ts b/src/main/services/achievements/steam/steam-achievement-merge.ts similarity index 100% rename from src/main/events/achievements/steam/steam-achievement-merge.ts rename to src/main/services/achievements/steam/steam-achievement-merge.ts diff --git a/src/main/events/achievements/steam/steam-find-game-achivement-files.ts b/src/main/services/achievements/steam/steam-find-game-achivement-files.ts similarity index 100% rename from src/main/events/achievements/steam/steam-find-game-achivement-files.ts rename to src/main/services/achievements/steam/steam-find-game-achivement-files.ts diff --git a/src/main/events/achievements/steam/steam-get-achivement.ts b/src/main/services/achievements/steam/steam-get-achivement.ts similarity index 100% rename from src/main/events/achievements/steam/steam-get-achivement.ts rename to src/main/services/achievements/steam/steam-get-achivement.ts diff --git a/src/main/events/achievements/steam/steam-global-achievement-percentages.ts b/src/main/services/achievements/steam/steam-global-achievement-percentages.ts similarity index 100% rename from src/main/events/achievements/steam/steam-global-achievement-percentages.ts rename to src/main/services/achievements/steam/steam-global-achievement-percentages.ts diff --git a/src/main/events/achievements/types/index.ts b/src/main/services/achievements/types/index.ts similarity index 100% rename from src/main/events/achievements/types/index.ts rename to src/main/services/achievements/types/index.ts diff --git a/src/main/events/achievements/util/check-unlocked-achievements.ts b/src/main/services/achievements/util/check-unlocked-achievements.ts similarity index 100% rename from src/main/events/achievements/util/check-unlocked-achievements.ts rename to src/main/services/achievements/util/check-unlocked-achievements.ts diff --git a/src/main/events/achievements/util/parseAchievementFile.ts b/src/main/services/achievements/util/parseAchievementFile.ts similarity index 100% rename from src/main/events/achievements/util/parseAchievementFile.ts rename to src/main/services/achievements/util/parseAchievementFile.ts diff --git a/src/main/services/process-watcher.ts b/src/main/services/process-watcher.ts index 50ef42a9..e4c72f1d 100644 --- a/src/main/services/process-watcher.ts +++ b/src/main/services/process-watcher.ts @@ -8,7 +8,7 @@ import { Game } from "@main/entity"; import { startGameAchievementObserver, stopGameAchievementObserver, -} from "@main/events/achievements/game-achievements-observer"; +} from "@main/services/achievements/game-achievements-observer"; export const gamesPlaytime = new Map< number, diff --git a/src/preload/index.ts b/src/preload/index.ts index 0f135b99..223d2201 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -13,6 +13,7 @@ import type { UpdateProfileRequest, } from "@types"; import type { CatalogueCategory } from "@shared"; +import { Game } from "@main/entity"; contextBridge.exposeInMainWorld("electron", { /* Torrenting */ @@ -49,6 +50,8 @@ contextBridge.exposeInMainWorld("electron", { getGameStats: (objectId: string, shop: GameShop) => ipcRenderer.invoke("getGameStats", objectId, shop), getTrendingGames: () => ipcRenderer.invoke("getTrendingGames"), + getGameAchievements: (objectId: string, shop: GameShop) => + ipcRenderer.invoke("getGameAchievements", objectId, shop), /* User preferences */ getUserPreferences: () => ipcRenderer.invoke("getUserPreferences"), diff --git a/src/renderer/src/context/game-details/game-details.context.tsx b/src/renderer/src/context/game-details/game-details.context.tsx index e723779f..cd02f7a2 100644 --- a/src/renderer/src/context/game-details/game-details.context.tsx +++ b/src/renderer/src/context/game-details/game-details.context.tsx @@ -7,6 +7,7 @@ import { useAppDispatch, useAppSelector, useDownload } from "@renderer/hooks"; import type { Game, + GameAchievement, GameRepack, GameShop, GameStats, @@ -53,6 +54,7 @@ export function GameDetailsContextProvider({ const [shopDetails, setShopDetails] = useState(null); const [repacks, setRepacks] = useState([]); + const [achievements, setAchievements] = useState([]); const [game, setGame] = useState(null); const [hasNSFWContentBlocked, setHasNSFWContentBlocked] = useState(false); @@ -99,8 +101,9 @@ export function GameDetailsContextProvider({ ), window.electron.searchGameRepacks(gameTitle), window.electron.getGameStats(objectID!, shop as GameShop), + window.electron.getGameAchievements(objectID!, shop as GameShop), ]) - .then(([appDetailsResult, repacksResult, statsResult]) => { + .then(([appDetailsResult, repacksResult, statsResult, achievements]) => { if (appDetailsResult.status === "fulfilled") { setShopDetails(appDetailsResult.value); @@ -117,6 +120,11 @@ export function GameDetailsContextProvider({ setRepacks(repacksResult.value); if (statsResult.status === "fulfilled") setStats(statsResult.value); + + if (achievements.status === "fulfilled") { + console.log(achievements.value); + setAchievements(achievements.value); + } }) .finally(() => { setIsLoading(false); @@ -193,6 +201,7 @@ export function GameDetailsContextProvider({ showGameOptionsModal, showRepacksModal, stats, + achievements, hasNSFWContentBlocked, setHasNSFWContentBlocked, setGameColor, diff --git a/src/renderer/src/context/game-details/game-details.context.types.ts b/src/renderer/src/context/game-details/game-details.context.types.ts index 7c3bd20b..507f0a9e 100644 --- a/src/renderer/src/context/game-details/game-details.context.types.ts +++ b/src/renderer/src/context/game-details/game-details.context.types.ts @@ -1,5 +1,6 @@ import type { Game, + GameAchievement, GameRepack, GameShop, GameStats, @@ -19,6 +20,7 @@ export interface GameDetailsContext { showRepacksModal: boolean; showGameOptionsModal: boolean; stats: GameStats | null; + achievements: GameAchievement[]; hasNSFWContentBlocked: boolean; setGameColor: React.Dispatch>; selectGameExecutable: () => Promise; diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 70b77eec..c81957f4 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -25,6 +25,7 @@ import type { UserStats, UserDetails, FriendRequestSync, + GameAchievement, } from "@types"; import type { DiskSpace } from "check-disk-space"; @@ -65,6 +66,10 @@ declare global { searchGameRepacks: (query: string) => Promise; getGameStats: (objectId: string, shop: GameShop) => Promise; getTrendingGames: () => Promise; + getGameAchievements: ( + objectId: string, + shop: GameShop + ) => Promise; /* Library */ addGameToLibrary: ( diff --git a/src/renderer/src/pages/game-details/sidebar/sidebar.tsx b/src/renderer/src/pages/game-details/sidebar/sidebar.tsx index e56f0764..624b27c4 100644 --- a/src/renderer/src/pages/game-details/sidebar/sidebar.tsx +++ b/src/renderer/src/pages/game-details/sidebar/sidebar.tsx @@ -17,7 +17,8 @@ export function Sidebar() { const [activeRequirement, setActiveRequirement] = useState("minimum"); - const { gameTitle, shopDetails, stats } = useContext(gameDetailsContext); + const { gameTitle, shopDetails, stats, achievements } = + useContext(gameDetailsContext); const { t } = useTranslation("game_details"); @@ -45,6 +46,17 @@ export function Sidebar() { isLoading={howLongToBeat.isLoading} /> */} + {achievements.map((achievement, index) => ( +
+ +

{achievement.displayName}

+ {achievement.unlockTime && + new Date(achievement.unlockTime).toDateString()} +
+ ))} + {stats && ( <>