diff --git a/ludusavi/ludusavi.exe b/ludusavi/ludusavi.exe new file mode 100644 index 00000000..da9835ab Binary files /dev/null and b/ludusavi/ludusavi.exe differ diff --git a/resources/achievement-sound.mp3 b/resources/achievement-sound.mp3 new file mode 100644 index 00000000..e942cb6b Binary files /dev/null and b/resources/achievement-sound.mp3 differ diff --git a/src/main/services/achievements/merge-achievements.ts b/src/main/services/achievements/merge-achievements.ts index ac76b82c..b4decff4 100644 --- a/src/main/services/achievements/merge-achievements.ts +++ b/src/main/services/achievements/merge-achievements.ts @@ -1,6 +1,7 @@ import { gameAchievementRepository, gameRepository } from "@main/repository"; import { publishNewAchievementNotification } from "../notifications"; import type { GameShop, UnlockedAchievement } from "@types"; +import { WindowManager } from "../window-manager"; export const mergeAchievements = async ( objectId: string, @@ -28,6 +29,14 @@ export const mergeAchievements = async ( }); }); + if (!newAchievements.length) return; + + WindowManager.mainWindow?.webContents.send( + "on-achievement-unlocked", + objectId, + shop + ); + for (const achievement of newAchievements) { const completeAchievement = JSON.parse( localGameAchievement?.achievements || "[]" diff --git a/src/preload/index.ts b/src/preload/index.ts index 878f396d..853284b8 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -51,6 +51,16 @@ contextBridge.exposeInMainWorld("electron", { getTrendingGames: () => ipcRenderer.invoke("getTrendingGames"), getGameAchievements: (objectId: string, shop: GameShop) => ipcRenderer.invoke("getGameAchievements", objectId, shop), + onAchievementUnlocked: (cb: (objectId: string, shop: GameShop) => void) => { + const listener = ( + _event: Electron.IpcRendererEvent, + objectId: string, + shop: GameShop + ) => cb(objectId, shop); + ipcRenderer.on("on-achievement-unlocked", listener); + return () => + ipcRenderer.removeListener("on-achievement-unlocked", listener); + }, /* 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 5a885268..601b86c7 100644 --- a/src/renderer/src/context/game-details/game-details.context.tsx +++ b/src/renderer/src/context/game-details/game-details.context.tsx @@ -138,6 +138,7 @@ export function GameDetailsContextProvider({ setGame(null); setIsLoading(true); setisGameRunning(false); + setAchievements([]); dispatch(setHeaderTitle(gameTitle)); }, [objectID, gameTitle, dispatch]); @@ -158,6 +159,22 @@ export function GameDetailsContextProvider({ }; }, [game?.id, isGameRunning, updateGame]); + useEffect(() => { + const unsubscribe = window.electron.onAchievementUnlocked( + (objectId, shop) => { + if (objectID !== objectId || shop !== shop) return; + + window.electron + .getGameAchievements(objectID!, shop as GameShop) + .then(setAchievements); + } + ); + + return () => { + unsubscribe(); + }; + }, [objectID, shop]); + const getDownloadsPath = async () => { if (userPreferences?.downloadsPath) return userPreferences.downloadsPath; return window.electron.getDefaultDownloadsPath(); diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index c81957f4..fca11e74 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -70,6 +70,9 @@ declare global { objectId: string, shop: GameShop ) => Promise; + onAchievementUnlocked: ( + cb: (objectId: string, shop: GameShop) => void + ) => () => Electron.IpcRenderer; /* 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 74092719..e4d8b1b6 100644 --- a/src/renderer/src/pages/game-details/sidebar/sidebar.tsx +++ b/src/renderer/src/pages/game-details/sidebar/sidebar.tsx @@ -48,7 +48,7 @@ export function Sidebar() { isLoading={howLongToBeat.isLoading} /> */} - {achievements.length && ( + {achievements.length > 0 && (

{achievement.displayName}