diff --git a/src/main/events/auth/sign-out.ts b/src/main/events/auth/sign-out.ts index 0ba23e2b..fe640b9d 100644 --- a/src/main/events/auth/sign-out.ts +++ b/src/main/events/auth/sign-out.ts @@ -28,7 +28,7 @@ const signOut = async (_event: Electron.IpcMainInvokeEvent) => { await Promise.all([ databaseOperations, - HydraApi.post("/auth/logout").catch(), + HydraApi.post("/auth/logout").catch(() => {}), ]); }; diff --git a/src/main/events/index.ts b/src/main/events/index.ts index d1075e3e..1b500be9 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -22,7 +22,6 @@ import "./library/open-game-installer-path"; import "./library/update-executable-path"; import "./library/remove-game"; import "./library/remove-game-from-library"; -import "./misc/is-user-logged-in"; import "./misc/open-external"; import "./misc/show-open-dialog"; import "./torrenting/cancel-game-download"; diff --git a/src/main/events/library/add-game-to-library.ts b/src/main/events/library/add-game-to-library.ts index 8187d41e..1bda0c93 100644 --- a/src/main/events/library/add-game-to-library.ts +++ b/src/main/events/library/add-game-to-library.ts @@ -53,18 +53,7 @@ const addGameToLibrary = async ( const game = await gameRepository.findOne({ where: { objectID } }); - createGame(game!).then((response) => { - const { - id: remoteId, - playTimeInMilliseconds, - lastTimePlayed, - } = response.data; - - gameRepository.update( - { objectID }, - { remoteId, playTimeInMilliseconds, lastTimePlayed } - ); - }); + createGame(game!); }); }; diff --git a/src/main/events/library/remove-game-from-library.ts b/src/main/events/library/remove-game-from-library.ts index d7874b8f..468f5b26 100644 --- a/src/main/events/library/remove-game-from-library.ts +++ b/src/main/events/library/remove-game-from-library.ts @@ -20,7 +20,7 @@ const removeRemoveGameFromLibrary = async (gameId: number) => { const game = await gameRepository.findOne({ where: { id: gameId } }); if (game?.remoteId) { - HydraApi.delete(`/games/${game.remoteId}`); + HydraApi.delete(`/games/${game.remoteId}`).catch(() => {}); } }; diff --git a/src/main/events/misc/is-user-logged-in.ts b/src/main/events/misc/is-user-logged-in.ts deleted file mode 100644 index f5f79e15..00000000 --- a/src/main/events/misc/is-user-logged-in.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { registerEvent } from "../register-event"; -import { HydraApi } from "@main/services"; - -const isUserLoggedIn = async (_event: Electron.IpcMainInvokeEvent) => { - return HydraApi.isLoggedIn(); -}; - -registerEvent("isUserLoggedIn", isUserLoggedIn); diff --git a/src/main/events/profile/get-me.ts b/src/main/events/profile/get-me.ts index 17eb3d38..83463680 100644 --- a/src/main/events/profile/get-me.ts +++ b/src/main/events/profile/get-me.ts @@ -3,7 +3,7 @@ import * as Sentry from "@sentry/electron/main"; import { HydraApi } from "@main/services"; import { UserProfile } from "@types"; import { userAuthRepository } from "@main/repository"; -import { logger } from "@main/services"; +import { UserNotLoggedInError } from "@shared"; const getMe = async ( _event: Electron.IpcMainInvokeEvent @@ -27,7 +27,10 @@ const getMe = async ( return me; }) .catch((err) => { - logger.error("getMe", err.message); + if (err instanceof UserNotLoggedInError) { + return null; + } + return userAuthRepository.findOne({ where: { id: 1 } }); }); }; diff --git a/src/main/events/profile/update-profile.ts b/src/main/events/profile/update-profile.ts index fbed0606..fe79d345 100644 --- a/src/main/events/profile/update-profile.ts +++ b/src/main/events/profile/update-profile.ts @@ -26,9 +26,11 @@ const updateProfile = async ( _event: Electron.IpcMainInvokeEvent, displayName: string, newProfileImagePath: string | null -): Promise => { +) => { if (!newProfileImagePath) { - return (await patchUserProfile(displayName)).data; + return patchUserProfile(displayName).then( + (response) => response.data as UserProfile + ); } const stats = fs.statSync(newProfileImagePath); @@ -51,11 +53,11 @@ const updateProfile = async ( }); return profileImageUrl; }) - .catch(() => { - return undefined; - }); + .catch(() => undefined); - return (await patchUserProfile(displayName, profileImageUrl)).data; + return patchUserProfile(displayName, profileImageUrl).then( + (response) => response.data as UserProfile + ); }; registerEvent("updateProfile", updateProfile); diff --git a/src/main/events/torrenting/start-game-download.ts b/src/main/events/torrenting/start-game-download.ts index 0aa01fc9..cea41596 100644 --- a/src/main/events/torrenting/start-game-download.ts +++ b/src/main/events/torrenting/start-game-download.ts @@ -95,18 +95,7 @@ const startGameDownload = async ( }, }); - createGame(updatedGame!).then((response) => { - const { - id: remoteId, - playTimeInMilliseconds, - lastTimePlayed, - } = response.data; - - gameRepository.update( - { objectID }, - { remoteId, playTimeInMilliseconds, lastTimePlayed } - ); - }); + createGame(updatedGame!); await downloadQueueRepository.delete({ game: { id: updatedGame!.id } }); await downloadQueueRepository.insert({ game: { id: updatedGame!.id } }); diff --git a/src/main/events/user-preferences/auto-launch.ts b/src/main/events/user-preferences/auto-launch.ts index ade7833d..712f388b 100644 --- a/src/main/events/user-preferences/auto-launch.ts +++ b/src/main/events/user-preferences/auto-launch.ts @@ -13,9 +13,9 @@ const autoLaunch = async ( }); if (enabled) { - appLauncher.enable().catch(); + appLauncher.enable().catch(() => {}); } else { - appLauncher.disable().catch(); + appLauncher.disable().catch(() => {}); } }; diff --git a/src/main/main.ts b/src/main/main.ts index c80f0368..ec95ccd5 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -25,8 +25,8 @@ const loadState = async (userPreferences: UserPreferences | null) => { if (userPreferences?.realDebridApiToken) RealDebridClient.authorize(userPreferences?.realDebridApiToken); - HydraApi.setupApi().then(async () => { - if (HydraApi.isLoggedIn()) uploadGamesBatch(); + HydraApi.setupApi().then(() => { + uploadGamesBatch(); }); const [nextQueueItem] = await downloadQueueRepository.find({ diff --git a/src/main/services/hydra-api.ts b/src/main/services/hydra-api.ts index fb8b9a28..98b783f3 100644 --- a/src/main/services/hydra-api.ts +++ b/src/main/services/hydra-api.ts @@ -5,6 +5,7 @@ import url from "url"; import { uploadGamesBatch } from "./library-sync"; import { clearGamesRemoteIds } from "./library-sync/clear-games-remote-id"; import { logger } from "./logger"; +import { UserNotLoggedInError } from "@shared"; export class HydraApi { private static instance: AxiosInstance; @@ -19,7 +20,7 @@ export class HydraApi { expirationTimestamp: 0, }; - static isLoggedIn() { + private static isLoggedIn() { return this.userAuth.authToken !== ""; } @@ -127,14 +128,8 @@ export class HydraApi { } private static async revalidateAccessTokenIfExpired() { - if (!this.userAuth.authToken) { - userAuthRepository.delete({ id: 1 }); - logger.error("user is not logged in"); - this.sendSignOutEvent(); - throw new Error("user is not logged in"); - } - const now = new Date(); + if (this.userAuth.expirationTimestamp < now.getTime()) { try { const response = await this.instance.post(`/auth/refresh`, { @@ -190,6 +185,8 @@ export class HydraApi { }; static async get(url: string) { + if (!this.isLoggedIn()) throw new UserNotLoggedInError(); + await this.revalidateAccessTokenIfExpired(); return this.instance .get(url, this.getAxiosConfig()) @@ -197,6 +194,8 @@ export class HydraApi { } static async post(url: string, data?: any) { + if (!this.isLoggedIn()) throw new UserNotLoggedInError(); + await this.revalidateAccessTokenIfExpired(); return this.instance .post(url, data, this.getAxiosConfig()) @@ -204,6 +203,8 @@ export class HydraApi { } static async put(url: string, data?: any) { + if (!this.isLoggedIn()) throw new UserNotLoggedInError(); + await this.revalidateAccessTokenIfExpired(); return this.instance .put(url, data, this.getAxiosConfig()) @@ -211,6 +212,8 @@ export class HydraApi { } static async patch(url: string, data?: any) { + if (!this.isLoggedIn()) throw new UserNotLoggedInError(); + await this.revalidateAccessTokenIfExpired(); return this.instance .patch(url, data, this.getAxiosConfig()) @@ -218,6 +221,8 @@ export class HydraApi { } static async delete(url: string) { + if (!this.isLoggedIn()) throw new UserNotLoggedInError(); + await this.revalidateAccessTokenIfExpired(); return this.instance .delete(url, this.getAxiosConfig()) diff --git a/src/main/services/library-sync/create-game.ts b/src/main/services/library-sync/create-game.ts index 6e56a3de..c0e8b1f8 100644 --- a/src/main/services/library-sync/create-game.ts +++ b/src/main/services/library-sync/create-game.ts @@ -1,11 +1,25 @@ import { Game } from "@main/entity"; import { HydraApi } from "../hydra-api"; +import { gameRepository } from "@main/repository"; export const createGame = async (game: Game) => { - return HydraApi.post(`/games`, { + HydraApi.post(`/games`, { objectId: game.objectID, playTimeInMilliseconds: Math.trunc(game.playTimeInMilliseconds), shop: game.shop, lastTimePlayed: game.lastTimePlayed, - }); + }) + .then((response) => { + const { + id: remoteId, + playTimeInMilliseconds, + lastTimePlayed, + } = response.data; + + gameRepository.update( + { objectID: game.objectID }, + { remoteId, playTimeInMilliseconds, lastTimePlayed } + ); + }) + .catch(() => {}); }; diff --git a/src/main/services/library-sync/merge-with-remote-games.ts b/src/main/services/library-sync/merge-with-remote-games.ts index 3a2db640..2162ea58 100644 --- a/src/main/services/library-sync/merge-with-remote-games.ts +++ b/src/main/services/library-sync/merge-with-remote-games.ts @@ -2,71 +2,63 @@ import { gameRepository } from "@main/repository"; import { HydraApi } from "../hydra-api"; import { steamGamesWorker } from "@main/workers"; import { getSteamAppAsset } from "@main/helpers"; -import { logger } from "../logger"; -import { AxiosError } from "axios"; export const mergeWithRemoteGames = async () => { - try { - const games = await HydraApi.get("/games"); - - for (const game of games.data) { - const localGame = await gameRepository.findOne({ - where: { - objectID: game.objectId, - }, - }); - - if (localGame) { - const updatedLastTimePlayed = - localGame.lastTimePlayed == null || - (game.lastTimePlayed && - new Date(game.lastTimePlayed) > localGame.lastTimePlayed) - ? game.lastTimePlayed - : localGame.lastTimePlayed; - - const updatedPlayTime = - localGame.playTimeInMilliseconds < game.playTimeInMilliseconds - ? game.playTimeInMilliseconds - : localGame.playTimeInMilliseconds; - - gameRepository.update( - { + return HydraApi.get("/games") + .then(async (response) => { + for (const game of response.data) { + const localGame = await gameRepository.findOne({ + where: { objectID: game.objectId, - shop: "steam", }, - { - remoteId: game.id, - lastTimePlayed: updatedLastTimePlayed, - playTimeInMilliseconds: updatedPlayTime, - } - ); - } else { - const steamGame = await steamGamesWorker.run(Number(game.objectId), { - name: "getById", }); - if (steamGame) { - const iconUrl = steamGame?.clientIcon - ? getSteamAppAsset("icon", game.objectId, steamGame.clientIcon) - : null; + if (localGame) { + const updatedLastTimePlayed = + localGame.lastTimePlayed == null || + (game.lastTimePlayed && + new Date(game.lastTimePlayed) > localGame.lastTimePlayed) + ? game.lastTimePlayed + : localGame.lastTimePlayed; - gameRepository.insert({ - objectID: game.objectId, - title: steamGame?.name, - remoteId: game.id, - shop: game.shop, - iconUrl, - lastTimePlayed: game.lastTimePlayed, - playTimeInMilliseconds: game.playTimeInMilliseconds, + const updatedPlayTime = + localGame.playTimeInMilliseconds < game.playTimeInMilliseconds + ? game.playTimeInMilliseconds + : localGame.playTimeInMilliseconds; + + gameRepository.update( + { + objectID: game.objectId, + shop: "steam", + }, + { + remoteId: game.id, + lastTimePlayed: updatedLastTimePlayed, + playTimeInMilliseconds: updatedPlayTime, + } + ); + } else { + const steamGame = await steamGamesWorker.run(Number(game.objectId), { + name: "getById", }); + + if (steamGame) { + const iconUrl = steamGame?.clientIcon + ? getSteamAppAsset("icon", game.objectId, steamGame.clientIcon) + : null; + + gameRepository.insert({ + objectID: game.objectId, + title: steamGame?.name, + remoteId: game.id, + shop: game.shop, + iconUrl, + lastTimePlayed: game.lastTimePlayed, + playTimeInMilliseconds: game.playTimeInMilliseconds, + }); + } } } - } - } catch (err) { - if (err instanceof AxiosError) { - logger.error("getRemoteGames", err.message); - } else { - logger.error("getRemoteGames", err); - } - } + }) + .catch(() => {}); }; diff --git a/src/main/services/library-sync/update-game-playtime.ts b/src/main/services/library-sync/update-game-playtime.ts index 190d7e7d..39206a12 100644 --- a/src/main/services/library-sync/update-game-playtime.ts +++ b/src/main/services/library-sync/update-game-playtime.ts @@ -6,8 +6,8 @@ export const updateGamePlaytime = async ( deltaInMillis: number, lastTimePlayed: Date ) => { - return HydraApi.put(`/games/${game.remoteId}`, { + HydraApi.put(`/games/${game.remoteId}`, { playTimeDeltaInSeconds: Math.trunc(deltaInMillis / 1000), lastTimePlayed, - }); + }).catch(() => {}); }; diff --git a/src/main/services/library-sync/upload-games-batch.ts b/src/main/services/library-sync/upload-games-batch.ts index 63eee82a..88f02375 100644 --- a/src/main/services/library-sync/upload-games-batch.ts +++ b/src/main/services/library-sync/upload-games-batch.ts @@ -2,43 +2,32 @@ import { gameRepository } from "@main/repository"; import { chunk } from "lodash-es"; import { IsNull } from "typeorm"; import { HydraApi } from "../hydra-api"; -import { logger } from "../logger"; -import { AxiosError } from "axios"; - import { mergeWithRemoteGames } from "./merge-with-remote-games"; import { WindowManager } from "../window-manager"; export const uploadGamesBatch = async () => { - try { - const games = await gameRepository.find({ - where: { remoteId: IsNull(), isDeleted: false }, - }); + const games = await gameRepository.find({ + where: { remoteId: IsNull(), isDeleted: false }, + }); - const gamesChunks = chunk(games, 200); + const gamesChunks = chunk(games, 200); - for (const chunk of gamesChunks) { - await HydraApi.post( - "/games/batch", - chunk.map((game) => { - return { - objectId: game.objectID, - playTimeInMilliseconds: Math.trunc(game.playTimeInMilliseconds), - shop: game.shop, - lastTimePlayed: game.lastTimePlayed, - }; - }) - ); - } - - await mergeWithRemoteGames(); - - if (WindowManager.mainWindow) - WindowManager.mainWindow.webContents.send("on-library-batch-complete"); - } catch (err) { - if (err instanceof AxiosError) { - logger.error("uploadGamesBatch", err.response, err.message); - } else { - logger.error("uploadGamesBatch", err); - } + for (const chunk of gamesChunks) { + await HydraApi.post( + "/games/batch", + chunk.map((game) => { + return { + objectId: game.objectID, + playTimeInMilliseconds: Math.trunc(game.playTimeInMilliseconds), + shop: game.shop, + lastTimePlayed: game.lastTimePlayed, + }; + }) + ).catch(() => {}); } + + await mergeWithRemoteGames(); + + if (WindowManager.mainWindow) + WindowManager.mainWindow.webContents.send("on-library-batch-complete"); }; diff --git a/src/main/services/process-watcher.ts b/src/main/services/process-watcher.ts index 4ee78334..0f7efa62 100644 --- a/src/main/services/process-watcher.ts +++ b/src/main/services/process-watcher.ts @@ -48,12 +48,7 @@ export const watchProcesses = async () => { if (game.remoteId) { updateGamePlaytime(game, 0, new Date()); } else { - createGame({ ...game, lastTimePlayed: new Date() }).then( - (response) => { - const { id: remoteId } = response.data; - gameRepository.update({ objectID: game.objectID }, { remoteId }); - } - ); + createGame({ ...game, lastTimePlayed: new Date() }); } gamesPlaytime.set(game.id, { @@ -72,10 +67,7 @@ export const watchProcesses = async () => { game.lastTimePlayed! ); } else { - createGame(game).then((response) => { - const { id: remoteId } = response.data; - gameRepository.update({ objectID: game.objectID }, { remoteId }); - }); + createGame(game); } } } diff --git a/src/preload/index.ts b/src/preload/index.ts index ecabe9e8..0cadbc03 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -112,7 +112,6 @@ contextBridge.exposeInMainWorld("electron", { getDefaultDownloadsPath: () => ipcRenderer.invoke("getDefaultDownloadsPath"), isPortableVersion: () => ipcRenderer.invoke("isPortableVersion"), openExternal: (src: string) => ipcRenderer.invoke("openExternal", src), - isUserLoggedIn: () => ipcRenderer.invoke("isUserLoggedIn"), showOpenDialog: (options: Electron.OpenDialogOptions) => ipcRenderer.invoke("showOpenDialog", options), platform: process.platform, diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx index 0f9d3972..afce9622 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -93,12 +93,8 @@ export function App() { dispatch(setProfileBackground(profileBackground)); } - window.electron.isUserLoggedIn().then((isLoggedIn) => { - if (isLoggedIn) { - fetchUserDetails().then((response) => { - if (response) updateUserDetails(response); - }); - } + fetchUserDetails().then((response) => { + if (response) updateUserDetails(response); }); }, [fetchUserDetails, updateUserDetails, dispatch]); diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index ae89bb8b..48fa7aae 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -100,7 +100,6 @@ declare global { /* Misc */ openExternal: (src: string) => Promise; - isUserLoggedIn: () => Promise; getVersion: () => Promise; ping: () => string; getDefaultDownloadsPath: () => Promise; diff --git a/src/renderer/src/hooks/use-user-details.ts b/src/renderer/src/hooks/use-user-details.ts index 1d8257f4..e87f8ff6 100644 --- a/src/renderer/src/hooks/use-user-details.ts +++ b/src/renderer/src/hooks/use-user-details.ts @@ -57,8 +57,14 @@ export function useUserDetails() { ); const fetchUserDetails = useCallback(async () => { - return window.electron.getMe(); - }, []); + return window.electron.getMe().then((userDetails) => { + if (userDetails == null) { + clearUserDetails(); + } + + return userDetails; + }); + }, [clearUserDetails]); const patchUser = useCallback( async (displayName: string, imageProfileUrl: string | null) => { diff --git a/src/shared/index.ts b/src/shared/index.ts index 7a2933d7..ff556ad2 100644 --- a/src/shared/index.ts +++ b/src/shared/index.ts @@ -8,6 +8,13 @@ export enum DownloadSourceStatus { Errored, } +export class UserNotLoggedInError extends Error { + constructor() { + super("user not logged in"); + this.name = "UserNotLoggedInError"; + } +} + const FORMAT = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"]; export const formatBytes = (bytes: number): string => {