feat: update achievement audio and refactors

This commit is contained in:
Zamitto 2024-10-01 18:29:50 -03:00
parent 084b7f5b9c
commit c18c41ac95
13 changed files with 99 additions and 55 deletions

View File

@ -315,5 +315,8 @@
"report_reason_other": "Other", "report_reason_other": "Other",
"profile_reported": "Profile reported", "profile_reported": "Profile reported",
"your_friend_code": "Your friend code:" "your_friend_code": "Your friend code:"
},
"achievement": {
"achievement_unlocked": "Achievement unlocked"
} }
} }

View File

@ -317,5 +317,8 @@
"report_reason_other": "Outro", "report_reason_other": "Outro",
"profile_reported": "Perfil reportado", "profile_reported": "Perfil reportado",
"your_friend_code": "Seu código de amigo:" "your_friend_code": "Seu código de amigo:"
},
"achievement": {
"achievement_unlocked": "Conquista desbloqueada"
} }
} }

View File

@ -277,5 +277,8 @@
"friend_code_copied": "Código de amigo copiado", "friend_code_copied": "Código de amigo copiado",
"image_process_failure": "Falha ao processar a imagem", "image_process_failure": "Falha ao processar a imagem",
"your_friend_code": "Seu código de amigo:" "your_friend_code": "Seu código de amigo:"
},
"achievement": {
"achievement_unlocked": "Conquista desbloqueada"
} }
} }

View File

@ -7,23 +7,18 @@ import {
userPreferencesRepository, userPreferencesRepository,
} from "@main/repository"; } from "@main/repository";
import { UserNotLoggedInError } from "@shared"; import { UserNotLoggedInError } from "@shared";
import { Game } from "@main/entity";
const getGameAchievements = async ( const getAchievementsDataFromApi = async (
_event: Electron.IpcMainInvokeEvent,
objectId: string, objectId: string,
shop: GameShop shop: string,
): Promise<GameAchievement[]> => { game: Game | null
const [game, cachedAchievements, userPreferences] = await Promise.all([ ) => {
gameRepository.findOne({ const userPreferences = await userPreferencesRepository.findOne({
where: { objectID: objectId, shop }, where: { id: 1 },
}), });
gameAchievementRepository.findOne({ where: { objectId, shop } }),
userPreferencesRepository.findOne({
where: { id: 1 },
}),
]);
const apiAchievement = HydraApi.get("/games/achievements", { return HydraApi.get("/games/achievements", {
objectId, objectId,
shop, shop,
language: userPreferences?.language || "en", language: userPreferences?.language || "en",
@ -46,10 +41,23 @@ const getGameAchievements = async (
if (err instanceof UserNotLoggedInError) throw err; if (err instanceof UserNotLoggedInError) throw err;
return []; return [];
}); });
};
const getGameAchievements = async (
_event: Electron.IpcMainInvokeEvent,
objectId: string,
shop: GameShop
): Promise<GameAchievement[]> => {
const [game, cachedAchievements] = await Promise.all([
gameRepository.findOne({
where: { objectID: objectId, shop },
}),
gameAchievementRepository.findOne({ where: { objectId, shop } }),
]);
const gameAchievements = cachedAchievements?.achievements const gameAchievements = cachedAchievements?.achievements
? JSON.parse(cachedAchievements.achievements) ? JSON.parse(cachedAchievements.achievements)
: await apiAchievement; : await getAchievementsDataFromApi(objectId, shop, game);
const unlockedAchievements = JSON.parse( const unlockedAchievements = JSON.parse(
cachedAchievements?.unlockedAchievements || "[]" cachedAchievements?.unlockedAchievements || "[]"

View File

@ -0,0 +1,11 @@
import { registerEvent } from "../register-event";
import { updateLocalUnlockedAchivements } from "@main/services/achievements/update-local-unlocked-achivements";
const updateGameUnlockedAchievements = async (
_event: Electron.IpcMainInvokeEvent,
objectId: string
) => {
return updateLocalUnlockedAchivements(false, objectId);
};
registerEvent("updateGameUnlockedAchievements", updateGameUnlockedAchievements);

View File

@ -10,6 +10,7 @@ import "./catalogue/search-games";
import "./catalogue/get-game-stats"; import "./catalogue/get-game-stats";
import "./catalogue/get-trending-games"; import "./catalogue/get-trending-games";
import "./catalogue/get-game-achievements"; import "./catalogue/get-game-achievements";
import "./catalogue/update-game-unlocked-achievements";
import "./hardware/get-disk-free-space"; import "./hardware/get-disk-free-space";
import "./library/add-game-to-library"; import "./library/add-game-to-library";
import "./library/create-game-shortcut"; import "./library/create-game-shortcut";

View File

@ -62,29 +62,35 @@ export const mergeAchievements = async (
}; };
}); });
if (newAchievements.length) { if (newAchievements.length && publishNotification) {
const achievement = newAchievements.at(-1)!; const achievementsInfo = newAchievements
const achievementInfo = JSON.parse( .map((achievement) => {
localGameAchievement?.achievements || "[]" return JSON.parse(localGameAchievement?.achievements || "[]").find(
).find((steamAchievement) => { (steamAchievement) => {
return achievement.name === steamAchievement.name; return achievement.name === steamAchievement.name;
}); }
);
})
.filter((achievement) => achievement)
.map((achievement) => {
return {
displayName: achievement.displayName,
iconUrl: achievement.icon,
};
});
if (publishNotification) { WindowManager.notificationWindow?.webContents.send(
WindowManager.notificationWindow?.webContents.send( "on-achievement-unlocked",
"on-achievement-unlocked", objectId,
objectId, shop,
shop, achievementsInfo
achievementInfo.displayName, );
achievementInfo.icon
);
WindowManager.notificationWindow?.setBounds({ y: 50 }); WindowManager.notificationWindow?.setBounds({ y: 50 });
setTimeout(() => { setTimeout(() => {
WindowManager.notificationWindow?.setBounds({ y: -9999 }); WindowManager.notificationWindow?.setBounds({ y: -9999 });
}, 4000); }, 4000);
}
} }
const mergedLocalAchievements = unlockedAchievements.concat(newAchievements); const mergedLocalAchievements = unlockedAchievements.concat(newAchievements);

View File

@ -29,12 +29,12 @@ export const watchProcesses = async () => {
if (games.length === 0) return; if (games.length === 0) return;
const processes = await PythonInstance.getProcessList(); const processes = await PythonInstance.getProcessList();
const processSet = new Set(processes.map((process) => process.exe));
for (const game of games) { for (const game of games) {
const executablePath = game.executablePath!; const executablePath = game.executablePath!;
const gameProcess = processes.find((runningProcess) => { const gameProcess = processSet.has(executablePath);
return executablePath == runningProcess.exe;
});
if (gameProcess) { if (gameProcess) {
if (gamesPlaytime.has(game.id)) { if (gamesPlaytime.has(game.id)) {

View File

@ -51,21 +51,21 @@ contextBridge.exposeInMainWorld("electron", {
getTrendingGames: () => ipcRenderer.invoke("getTrendingGames"), getTrendingGames: () => ipcRenderer.invoke("getTrendingGames"),
getGameAchievements: (objectId: string, shop: GameShop) => getGameAchievements: (objectId: string, shop: GameShop) =>
ipcRenderer.invoke("getGameAchievements", objectId, shop), ipcRenderer.invoke("getGameAchievements", objectId, shop),
updateGameUnlockedAchievements: (objectId: string) =>
ipcRenderer.invoke("updateGameUnlockedAchievements", objectId),
onAchievementUnlocked: ( onAchievementUnlocked: (
cb: ( cb: (
objectId: string, objectId: string,
shop: GameShop, shop: GameShop,
displayName: string, achievements?: { displayName: string; iconUrl: string }[]
iconUrl: string
) => void ) => void
) => { ) => {
const listener = ( const listener = (
_event: Electron.IpcRendererEvent, _event: Electron.IpcRendererEvent,
objectId: string, objectId: string,
shop: GameShop, shop: GameShop,
displayName: string, achievements?: { displayName: string; iconUrl: string }[]
iconUrl: string ) => cb(objectId, shop, achievements);
) => cb(objectId, shop, displayName, iconUrl);
ipcRenderer.on("on-achievement-unlocked", listener); ipcRenderer.on("on-achievement-unlocked", listener);
return () => return () =>
ipcRenderer.removeListener("on-achievement-unlocked", listener); ipcRenderer.removeListener("on-achievement-unlocked", listener);

Binary file not shown.

View File

@ -179,6 +179,8 @@ export function GameDetailsContextProvider({
}, [game?.id, isGameRunning, updateGame]); }, [game?.id, isGameRunning, updateGame]);
useEffect(() => { useEffect(() => {
window.electron.updateGameUnlockedAchievements(objectID!).catch(() => {});
const unsubscribe = window.electron.onAchievementUnlocked( const unsubscribe = window.electron.onAchievementUnlocked(
(objectId, shop) => { (objectId, shop) => {
if (objectID !== objectId || shop !== shop) return; if (objectID !== objectId || shop !== shop) return;

View File

@ -70,12 +70,12 @@ declare global {
objectId: string, objectId: string,
shop: GameShop shop: GameShop
) => Promise<GameAchievement[]>; ) => Promise<GameAchievement[]>;
updateGameUnlockedAchievements: (objectId: string) => Promise<void>;
onAchievementUnlocked: ( onAchievementUnlocked: (
cb: ( cb: (
objectId: string, objectId: string,
shop: GameShop, shop: GameShop,
displayName: string, achievements?: { displayName: string; iconUrl: string }[]
iconUrl: string
) => void ) => void
) => () => Electron.IpcRenderer; ) => () => Electron.IpcRenderer;

View File

@ -1,27 +1,34 @@
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import achievementSound from "@renderer/assets/audio/achievement.wav";
import { useTranslation } from "react-i18next";
export function Achievemnt() { export function Achievemnt() {
const { t } = useTranslation("achievement");
const [achievementInfo, setAchievementInfo] = useState<{ const [achievementInfo, setAchievementInfo] = useState<{
displayName: string; displayName: string;
icon: string; icon: string;
} | null>(null); } | null>(null);
const audio = useMemo(() => { const audio = useMemo(() => {
const audio = new Audio( const audio = new Audio(achievementSound);
"https://cms-public-artifacts.artlist.io/content/sfx/aac/94201_690187_Classics_-_Achievement_Unlocked_-_MASTERED_-_2496.aac" audio.volume = 0.2;
);
audio.preload = "auto"; audio.preload = "auto";
return audio; return audio;
}, []); }, []);
useEffect(() => { useEffect(() => {
const unsubscribe = window.electron.onAchievementUnlocked( const unsubscribe = window.electron.onAchievementUnlocked(
(_object, _shop, displayName, icon) => { (_object, _shop, achievements) => {
setAchievementInfo({ if (!achievements) return;
displayName,
icon, if (achievements.length) {
}); const achievement = achievements[0];
setAchievementInfo({
displayName: achievement.displayName,
icon: achievement.iconUrl,
});
}
audio.play(); audio.play();
} }
@ -49,7 +56,7 @@ export function Achievemnt() {
style={{ width: 60, height: 60 }} style={{ width: 60, height: 60 }}
/> />
<div> <div>
<p>Achievement unlocked</p> <p>{t("achievement_unlocked")}</p>
<p>{achievementInfo.displayName}</p> <p>{achievementInfo.displayName}</p>
</div> </div>
</div> </div>