diff --git a/src/main/events/catalogue/get-game-achievements.ts b/src/main/events/catalogue/get-game-achievements.ts index 8b040874..511e255f 100644 --- a/src/main/events/catalogue/get-game-achievements.ts +++ b/src/main/events/catalogue/get-game-achievements.ts @@ -1,23 +1,81 @@ import type { AchievementData, - GameAchievement, GameShop, + RemoteUnlockedAchievement, UnlockedAchievement, + UserAchievement, } from "@types"; import { registerEvent } from "../register-event"; import { gameAchievementRepository, - userAuthRepository, + userPreferencesRepository, } from "@main/repository"; import { getGameAchievementData } from "@main/services/achievements/get-game-achievement-data"; -import { HydraApi } from "@main/services"; +import { HydraApi, logger } from "@main/services"; -const getAchievements = async ( +const getAchievementLocalUser = async (shop: string, objectId: string) => { + const cachedAchievements = await gameAchievementRepository.findOne({ + where: { objectId, shop }, + }); + + const achievementsData: AchievementData[] = cachedAchievements?.achievements + ? JSON.parse(cachedAchievements.achievements) + : await getGameAchievementData(objectId, shop); + + const unlockedAchievements = JSON.parse( + cachedAchievements?.unlockedAchievements || "[]" + ) as UnlockedAchievement[]; + + return achievementsData + .map((achievementData) => { + logger.info("unclockedAchievements", unlockedAchievements); + + const unlockedAchiementData = unlockedAchievements.find( + (localAchievement) => { + return ( + localAchievement.name.toUpperCase() == + achievementData.name.toUpperCase() + ); + } + ); + + const icongray = achievementData.icongray.endsWith("/") + ? achievementData.icon + : achievementData.icongray; + + if (unlockedAchiementData) { + return { + ...achievementData, + unlocked: true, + unlockTime: unlockedAchiementData.unlockTime, + }; + } + + return { + ...achievementData, + unlocked: false, + unlockTime: null, + icon: icongray, + } as UserAchievement; + }) + .sort((a, b) => { + if (a.unlocked && !b.unlocked) return -1; + if (!a.unlocked && b.unlocked) return 1; + if (a.unlocked && b.unlocked) { + return b.unlockTime! - a.unlockTime!; + } + return Number(a.hidden) - Number(b.hidden); + }); +}; + +const getAchievementsRemoteUser = async ( shop: string, objectId: string, - userId?: string + userId: string ) => { - const userAuth = await userAuthRepository.findOne({ where: { userId } }); + const userPreferences = await userPreferencesRepository.findOne({ + where: { id: 1 }, + }); const cachedAchievements = await gameAchievementRepository.findOne({ where: { objectId, shop }, @@ -27,31 +85,9 @@ const getAchievements = async ( ? JSON.parse(cachedAchievements.achievements) : await getGameAchievementData(objectId, shop); - if (!userId || userAuth) { - const unlockedAchievements = JSON.parse( - cachedAchievements?.unlockedAchievements || "[]" - ) as UnlockedAchievement[]; - - return { achievementsData, unlockedAchievements }; - } - - const unlockedAchievements = await HydraApi.get( + const unlockedAchievements = await HydraApi.get( `/users/${userId}/games/achievements`, - { shop, objectId, language: "en" } - ); - - return { achievementsData, unlockedAchievements }; -}; - -export const getGameAchievements = async ( - objectId: string, - shop: GameShop, - userId?: string -): Promise => { - const { achievementsData, unlockedAchievements } = await getAchievements( - shop, - objectId, - userId + { shop, objectId, language: userPreferences?.language || "en" } ); return achievementsData @@ -74,7 +110,6 @@ export const getGameAchievements = async ( ...achievementData, unlocked: true, unlockTime: unlockedAchiementData.unlockTime, - icongray, }; } @@ -82,8 +117,8 @@ export const getGameAchievements = async ( ...achievementData, unlocked: false, unlockTime: null, - icongray, - } as GameAchievement; + icon: icongray, + } as UserAchievement; }) .sort((a, b) => { if (a.unlocked && !b.unlocked) return -1; @@ -95,12 +130,24 @@ export const getGameAchievements = async ( }); }; +export const getGameAchievements = async ( + objectId: string, + shop: GameShop, + userId?: string +): Promise => { + if (!userId) { + return getAchievementLocalUser(shop, objectId); + } + + return getAchievementsRemoteUser(shop, objectId, userId); +}; + const getGameAchievementsEvent = async ( _event: Electron.IpcMainInvokeEvent, objectId: string, shop: GameShop, userId?: string -): Promise => { +): Promise => { return getGameAchievements(objectId, shop, userId); }; 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 7ff8bb09..a08f4a55 100644 --- a/src/renderer/src/context/game-details/game-details.context.tsx +++ b/src/renderer/src/context/game-details/game-details.context.tsx @@ -12,11 +12,11 @@ import { useAppDispatch, useAppSelector, useDownload } from "@renderer/hooks"; import type { Game, - GameAchievement, GameRepack, GameShop, GameStats, ShopDetails, + UserAchievement, } from "@types"; import { useTranslation } from "react-i18next"; @@ -64,7 +64,7 @@ export function GameDetailsContextProvider({ shop, }: GameDetailsContextProps) { const [shopDetails, setShopDetails] = useState(null); - const [achievements, setAchievements] = useState([]); + const [achievements, setAchievements] = useState([]); const [game, setGame] = useState(null); const [hasNSFWContentBlocked, setHasNSFWContentBlocked] = useState(false); 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 956d5c68..d410334b 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,10 +1,10 @@ import type { Game, - GameAchievement, GameRepack, GameShop, GameStats, ShopDetails, + UserAchievement, } from "@types"; export interface GameDetailsContext { @@ -20,7 +20,7 @@ export interface GameDetailsContext { showRepacksModal: boolean; showGameOptionsModal: boolean; stats: GameStats | null; - achievements: GameAchievement[]; + achievements: UserAchievement[]; hasNSFWContentBlocked: boolean; setGameColor: React.Dispatch>; selectGameExecutable: () => Promise; diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 677c3ee2..f0342a43 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -28,6 +28,7 @@ import type { GameAchievement, GameArtifact, LudusaviBackup, + UserAchievement, } from "@types"; import type { AxiosProgressEvent } from "axios"; import type { DiskSpace } from "check-disk-space"; @@ -68,7 +69,7 @@ declare global { objectId: string, shop: GameShop, userId?: string - ) => Promise; + ) => Promise; onAchievementUnlocked: ( cb: ( objectId: string, diff --git a/src/renderer/src/pages/achievement/achievements.tsx b/src/renderer/src/pages/achievement/achievements.tsx index 272473dd..943cd64d 100644 --- a/src/renderer/src/pages/achievement/achievements.tsx +++ b/src/renderer/src/pages/achievement/achievements.tsx @@ -1,7 +1,7 @@ import { setHeaderTitle } from "@renderer/features"; import { useAppDispatch, useDate } from "@renderer/hooks"; import { steamUrlBuilder } from "@shared"; -import type { GameAchievement, GameShop } from "@types"; +import type { GameShop, UserAchievement } from "@types"; import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { useNavigate, useSearchParams } from "react-router-dom"; @@ -28,7 +28,7 @@ export function Achievement() { const dispatch = useAppDispatch(); - const [achievements, setAchievements] = useState([]); + const [achievements, setAchievements] = useState([]); useEffect(() => { if (objectId && shop) { @@ -130,9 +130,7 @@ export function Achievement() { className={styles.listItemImage({ unlocked: achievement.unlocked, })} - src={ - achievement.unlocked ? achievement.icon : achievement.icongray - } + src={achievement.icon} alt={achievement.displayName} loading="lazy" /> diff --git a/src/renderer/src/pages/game-details/sidebar/sidebar.tsx b/src/renderer/src/pages/game-details/sidebar/sidebar.tsx index e05a9187..d04afbc6 100644 --- a/src/renderer/src/pages/game-details/sidebar/sidebar.tsx +++ b/src/renderer/src/pages/game-details/sidebar/sidebar.tsx @@ -91,11 +91,7 @@ export function Sidebar() { className={styles.listItemImage({ unlocked: achievement.unlocked, })} - src={ - achievement.unlocked - ? achievement.icon - : achievement.icongray - } + src={achievement.icon} alt={achievement.displayName} loading="lazy" /> diff --git a/src/types/index.ts b/src/types/index.ts index 7c6028be..7ef8073a 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -37,15 +37,34 @@ export interface AchievementData { hidden: boolean; } +export interface UserAchievement { + name: string; + hidden: boolean; + displayName: string; + description?: string; + unlocked: boolean; + unlockTime: number | null; + icon: string; +} + +export interface RemoteUnlockedAchievement { + name: string; + hidden: boolean; + icon: string; + displayName: string; + description?: string; + unlockTime: number; +} + export interface GameAchievement { name: string; + hidden: boolean; displayName: string; description?: string; unlocked: boolean; unlockTime: number | null; icon: string; icongray: string; - hidden: boolean; } export type ShopDetails = SteamAppDetails & {