diff --git a/src/main/services/achievements/AchievementWatcherManager.ts b/src/main/services/achievements/achievement-watcher-manager.ts similarity index 65% rename from src/main/services/achievements/AchievementWatcherManager.ts rename to src/main/services/achievements/achievement-watcher-manager.ts index 31c913f7..42509276 100644 --- a/src/main/services/achievements/AchievementWatcherManager.ts +++ b/src/main/services/achievements/achievement-watcher-manager.ts @@ -40,10 +40,8 @@ const watchAchievementsWindows = async () => { ); } - if (!gameAchievementFiles.length) continue; - for (const file of gameAchievementFiles) { - await compareFile(game, file); + compareFile(game, file); } } }; @@ -56,8 +54,6 @@ const watchAchievementsWithWine = async () => { }, }); - if (games.length === 0) return; - for (const game of games) { const gameAchievementFiles = findAchievementFiles(game); const achievementFileInsideDirectory = @@ -65,10 +61,8 @@ const watchAchievementsWithWine = async () => { gameAchievementFiles.push(...achievementFileInsideDirectory); - if (!gameAchievementFiles.length) continue; - for (const file of gameAchievementFiles) { - await compareFile(game, file); + compareFile(game, file); } } }; @@ -104,7 +98,7 @@ const compareFile = (game: Game, file: AchievementFile) => { const previousStat = fileStats.get(file.filePath); fileStats.set(file.filePath, currentStat.mtimeMs); - if (!previousStat) { + if (!previousStat || previousStat === -1) { if (currentStat.mtimeMs) { achievementsLogger.log( "First change in file", @@ -153,17 +147,55 @@ const processAchievementFileDiff = async ( export class AchievementWatcherManager { private static hasFinishedMergingWithRemote = false; - public static watchAchievements = async () => { + public static watchAchievements = () => { if (!this.hasFinishedMergingWithRemote) return; if (process.platform === "win32") { return watchAchievementsWindows(); } - watchAchievementsWithWine(); + return watchAchievementsWithWine(); }; - public static preSearchAchievements = async () => { + private static preProcessGameAchievementFiles = ( + game: Game, + gameAchievementFiles: AchievementFile[] + ) => { + const unlockedAchievements: UnlockedAchievement[] = []; + for (const achievementFile of gameAchievementFiles) { + const parsedAchievements = parseAchievementFile( + achievementFile.filePath, + achievementFile.type + ); + + try { + const currentStat = fs.statSync(achievementFile.filePath); + fileStats.set(achievementFile.filePath, currentStat.mtimeMs); + } catch { + fileStats.set(achievementFile.filePath, -1); + } + + if (parsedAchievements.length) { + unlockedAchievements.push(...parsedAchievements); + + achievementsLogger.log( + "Achievement file for", + game.title, + achievementFile.filePath, + parsedAchievements + ); + } + } + + return mergeAchievements( + game.objectID, + "steam", + unlockedAchievements, + false + ); + }; + + private static preSearchAchievementsWindows = async () => { const games = await gameRepository.find({ where: { isDeleted: false, @@ -172,20 +204,19 @@ export class AchievementWatcherManager { const gameAchievementFilesMap = findAllAchievementFiles(); - await Promise.all( - games.map(async (game) => { + return Promise.all( + games.map((game) => { gameAchievementRepository .findOne({ - where: { objectId: game.objectID, shop: "steam" }, + where: { objectId: game.objectID, shop: game.shop }, }) .then((localAchievements) => { if (!localAchievements || !localAchievements.achievements) { - getGameAchievementData(game.objectID, "steam"); + getGameAchievementData(game.objectID, game.shop); } }); const gameAchievementFiles: AchievementFile[] = []; - const unlockedAchievements: UnlockedAchievement[] = []; for (const objectId of getAlternativeObjectIds(game.objectID)) { gameAchievementFiles.push( @@ -197,39 +228,48 @@ export class AchievementWatcherManager { ); } - for (const achievementFile of gameAchievementFiles) { - const parsedAchievements = parseAchievementFile( - achievementFile.filePath, - achievementFile.type - ); - - try { - const currentStat = fs.statSync(achievementFile.filePath); - fileStats.set(achievementFile.filePath, currentStat.mtimeMs); - } catch { - fileStats.set(achievementFile.filePath, -1); - } - - if (parsedAchievements.length) { - unlockedAchievements.push(...parsedAchievements); - - achievementsLogger.log( - "Achievement file for", - game.title, - achievementFile.filePath, - parsedAchievements - ); - } - - await mergeAchievements( - game.objectID, - "steam", - unlockedAchievements, - false - ); - } + return this.preProcessGameAchievementFiles(game, gameAchievementFiles); }) ); + }; + + private static preSearchAchievementsWithWine = async () => { + const games = await gameRepository.find({ + where: { + isDeleted: false, + winePrefixPath: Not(IsNull()), + }, + }); + + return Promise.all( + games.map((game) => { + gameAchievementRepository + .findOne({ + where: { objectId: game.objectID, shop: game.shop }, + }) + .then((localAchievements) => { + if (!localAchievements || !localAchievements.achievements) { + getGameAchievementData(game.objectID, game.shop); + } + }); + + const gameAchievementFiles = findAchievementFiles(game); + const achievementFileInsideDirectory = + findAchievementFileInExecutableDirectory(game); + + gameAchievementFiles.push(...achievementFileInsideDirectory); + + return this.preProcessGameAchievementFiles(game, gameAchievementFiles); + }) + ); + }; + + public static preSearchAchievements = async () => { + if (process.platform === "win32") { + await this.preSearchAchievementsWindows(); + } else { + await this.preSearchAchievementsWithWine(); + } this.hasFinishedMergingWithRemote = true; }; diff --git a/src/main/services/achievements/merge-achievements.ts b/src/main/services/achievements/merge-achievements.ts index 5c37d436..3a330d8f 100644 --- a/src/main/services/achievements/merge-achievements.ts +++ b/src/main/services/achievements/merge-achievements.ts @@ -11,7 +11,8 @@ import { getUnlockedAchievements } from "@main/events/user/get-unlocked-achievem const saveAchievementsOnLocal = async ( objectId: string, shop: GameShop, - achievements: any[] + achievements: any[], + sendUpdateEvent: boolean ) => { return gameAchievementRepository .upsert( @@ -23,6 +24,8 @@ const saveAchievementsOnLocal = async ( ["objectId", "shop"] ) .then(() => { + if (!sendUpdateEvent) return; + return getUnlockedAchievements(objectId, shop) .then((achievements) => { WindowManager.mainWindow?.webContents.send( @@ -133,13 +136,24 @@ export const mergeAchievements = async ( return saveAchievementsOnLocal( response.objectId, response.shop, - response.achievements + response.achievements, + publishNotification ); }) .catch(() => { - return saveAchievementsOnLocal(objectId, shop, mergedLocalAchievements); + return saveAchievementsOnLocal( + objectId, + shop, + mergedLocalAchievements, + publishNotification + ); }); } - return saveAchievementsOnLocal(objectId, shop, mergedLocalAchievements); + return saveAchievementsOnLocal( + objectId, + shop, + mergedLocalAchievements, + publishNotification + ); }; diff --git a/src/main/services/hydra-api.ts b/src/main/services/hydra-api.ts index 4ae6a32e..c6c60b72 100644 --- a/src/main/services/hydra-api.ts +++ b/src/main/services/hydra-api.ts @@ -14,6 +14,7 @@ import { } from "@shared"; // import { omit } from "lodash-es"; import { appVersion } from "@main/constants"; +import { omit } from "lodash-es"; interface HydraApiOptions { needsAuth?: boolean; @@ -24,7 +25,7 @@ export class HydraApi { private static instance: AxiosInstance; private static readonly EXPIRATION_OFFSET_IN_MS = 1000 * 60 * 5; // 5 minutes - private static readonly ADD_LOG_INTERCEPTOR = true; + private static readonly ADD_LOG_INTERCEPTOR = false; private static secondsToMilliseconds = (seconds: number) => seconds * 1000; @@ -109,59 +110,59 @@ export class HydraApi { }); if (this.ADD_LOG_INTERCEPTOR) { - // this.instance.interceptors.request.use( - // (request) => { - // logger.log(" ---- REQUEST -----"); - // const data = Array.isArray(request.data) - // ? request.data - // : omit(request.data, ["refreshToken"]); - // logger.log(request.method, request.url, request.params, data); - // return request; - // }, - // (error) => { - // logger.error("request error", error); - // return Promise.reject(error); - // } - // ); - // this.instance.interceptors.response.use( - // (response) => { - // logger.log(" ---- RESPONSE -----"); - // const data = Array.isArray(response.data) - // ? response.data - // : omit(response.data, ["username", "accessToken", "refreshToken"]); - // logger.log( - // response.status, - // response.config.method, - // response.config.url, - // data - // ); - // return response; - // }, - // (error) => { - // logger.error(" ---- RESPONSE ERROR -----"); - // const { config } = error; - // logger.error( - // config.method, - // config.baseURL, - // config.url, - // config.headers, - // config.data - // ); - // if (error.response) { - // logger.error( - // "Response", - // error.response.status, - // error.response.data - // ); - // } else if (error.request) { - // logger.error("Request", error.request); - // } else { - // logger.error("Error", error.message); - // } - // logger.error(" ----- END RESPONSE ERROR -------"); - // return Promise.reject(error); - // } - // ); + this.instance.interceptors.request.use( + (request) => { + logger.log(" ---- REQUEST -----"); + const data = Array.isArray(request.data) + ? request.data + : omit(request.data, ["refreshToken"]); + logger.log(request.method, request.url, request.params, data); + return request; + }, + (error) => { + logger.error("request error", error); + return Promise.reject(error); + } + ); + this.instance.interceptors.response.use( + (response) => { + logger.log(" ---- RESPONSE -----"); + const data = Array.isArray(response.data) + ? response.data + : omit(response.data, ["username", "accessToken", "refreshToken"]); + logger.log( + response.status, + response.config.method, + response.config.url, + data + ); + return response; + }, + (error) => { + logger.error(" ---- RESPONSE ERROR -----"); + const { config } = error; + logger.error( + config.method, + config.baseURL, + config.url, + config.headers, + config.data + ); + if (error.response) { + logger.error( + "Response", + error.response.status, + error.response.data + ); + } else if (error.request) { + logger.error("Request", error.request); + } else { + logger.error("Error", error.message); + } + logger.error(" ----- END RESPONSE ERROR -------"); + return Promise.reject(error); + } + ); } const userAuth = await userAuthRepository.findOne({ diff --git a/src/main/services/library-sync/upload-games-batch.ts b/src/main/services/library-sync/upload-games-batch.ts index b40bfcda..79559a35 100644 --- a/src/main/services/library-sync/upload-games-batch.ts +++ b/src/main/services/library-sync/upload-games-batch.ts @@ -4,7 +4,7 @@ import { IsNull } from "typeorm"; import { HydraApi } from "../hydra-api"; import { mergeWithRemoteGames } from "./merge-with-remote-games"; import { WindowManager } from "../window-manager"; -import { AchievementWatcherManager } from "../achievements/AchievementWatcherManager"; +import { AchievementWatcherManager } from "../achievements/achievement-watcher-manager"; export const uploadGamesBatch = async () => { const games = await gameRepository.find({ @@ -29,7 +29,7 @@ export const uploadGamesBatch = async () => { await mergeWithRemoteGames(); - await AchievementWatcherManager.preSearchAchievements(); + AchievementWatcherManager.preSearchAchievements(); if (WindowManager.mainWindow) WindowManager.mainWindow.webContents.send("on-library-batch-complete"); diff --git a/src/main/services/main-loop.ts b/src/main/services/main-loop.ts index 48db4887..b4836b46 100644 --- a/src/main/services/main-loop.ts +++ b/src/main/services/main-loop.ts @@ -1,7 +1,7 @@ import { sleep } from "@main/helpers"; import { DownloadManager } from "./download"; import { watchProcesses } from "./process-watcher"; -import { AchievementWatcherManager } from "./achievements/AchievementWatcherManager"; +import { AchievementWatcherManager } from "./achievements/achievement-watcher-manager"; export const startMainLoop = async () => { // eslint-disable-next-line no-constant-condition diff --git a/src/renderer/src/components/sidebar/sidebar-profile.tsx b/src/renderer/src/components/sidebar/sidebar-profile.tsx index 79acf414..bae3501a 100644 --- a/src/renderer/src/components/sidebar/sidebar-profile.tsx +++ b/src/renderer/src/components/sidebar/sidebar-profile.tsx @@ -2,7 +2,7 @@ import { useNavigate } from "react-router-dom"; import { PeopleIcon } from "@primer/octicons-react"; import * as styles from "./sidebar-profile.css"; import { useAppSelector, useUserDetails } from "@renderer/hooks"; -import { useEffect, useMemo, useRef } from "react"; +import { useEffect, useMemo } from "react"; import { useTranslation } from "react-i18next"; import { UserFriendModalTab } from "@renderer/pages/shared-modals/user-friend-modal"; import SteamLogo from "@renderer/assets/steam-logo.svg?react"; @@ -13,8 +13,6 @@ const LONG_POLLING_INTERVAL = 60_000; export function SidebarProfile() { const navigate = useNavigate(); - const pollingInterval = useRef(null); - const { t } = useTranslation("sidebar"); const { @@ -36,14 +34,12 @@ export function SidebarProfile() { }; useEffect(() => { - pollingInterval.current = setInterval(() => { + const pollingInterval = setInterval(() => { syncFriendRequests(); }, LONG_POLLING_INTERVAL); return () => { - if (pollingInterval.current) { - clearInterval(pollingInterval.current); - } + clearInterval(pollingInterval); }; }, [syncFriendRequests]);