mirror of
https://github.com/hydralauncher/hydra.git
synced 2025-02-03 00:33:49 +03:00
Merge branch 'rc/v2.0' into feature/user-profile
# Conflicts: # src/main/services/hydra-api.ts # src/renderer/src/app.tsx
This commit is contained in:
commit
4f32043966
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "hydralauncher",
|
"name": "hydralauncher",
|
||||||
"version": "1.2.4",
|
"version": "2.0.0",
|
||||||
"description": "Hydra",
|
"description": "Hydra",
|
||||||
"main": "./out/main/index.js",
|
"main": "./out/main/index.js",
|
||||||
"author": "Los Broxas",
|
"author": "Los Broxas",
|
||||||
|
@ -22,6 +22,9 @@ export class Game {
|
|||||||
@Column("text", { unique: true })
|
@Column("text", { unique: true })
|
||||||
objectID: string;
|
objectID: string;
|
||||||
|
|
||||||
|
@Column("text", { unique: true, nullable: true })
|
||||||
|
remoteId: string | null;
|
||||||
|
|
||||||
@Column("text")
|
@Column("text")
|
||||||
title: string;
|
title: string;
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import type { GameShop } from "@types";
|
|||||||
import { getFileBase64, getSteamAppAsset } from "@main/helpers";
|
import { getFileBase64, getSteamAppAsset } from "@main/helpers";
|
||||||
|
|
||||||
import { steamGamesWorker } from "@main/workers";
|
import { steamGamesWorker } from "@main/workers";
|
||||||
|
import { createGame } from "@main/services/library-sync";
|
||||||
|
|
||||||
const addGameToLibrary = async (
|
const addGameToLibrary = async (
|
||||||
_event: Electron.IpcMainInvokeEvent,
|
_event: Electron.IpcMainInvokeEvent,
|
||||||
@ -49,6 +50,21 @@ 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 }
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { registerEvent } from "../register-event";
|
import { registerEvent } from "../register-event";
|
||||||
import { gameRepository } from "../../repository";
|
import { gameRepository } from "../../repository";
|
||||||
|
import { HydraApi } from "@main/services/hydra-api";
|
||||||
|
import { logger } from "@main/services";
|
||||||
|
|
||||||
const removeGameFromLibrary = async (
|
const removeGameFromLibrary = async (
|
||||||
_event: Electron.IpcMainInvokeEvent,
|
_event: Electron.IpcMainInvokeEvent,
|
||||||
@ -9,6 +11,18 @@ const removeGameFromLibrary = async (
|
|||||||
{ id: gameId },
|
{ id: gameId },
|
||||||
{ isDeleted: true, executablePath: null }
|
{ isDeleted: true, executablePath: null }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
removeRemoveGameFromLibrary(gameId).catch((err) => {
|
||||||
|
logger.error("removeRemoveGameFromLibrary", err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const removeRemoveGameFromLibrary = async (gameId: number) => {
|
||||||
|
const game = await gameRepository.findOne({ where: { id: gameId } });
|
||||||
|
|
||||||
|
if (game?.remoteId) {
|
||||||
|
HydraApi.delete(`/games/${game.remoteId}`);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
registerEvent("removeGameFromLibrary", removeGameFromLibrary);
|
registerEvent("removeGameFromLibrary", removeGameFromLibrary);
|
||||||
|
@ -12,6 +12,7 @@ import { DownloadManager } from "@main/services";
|
|||||||
|
|
||||||
import { Not } from "typeorm";
|
import { Not } from "typeorm";
|
||||||
import { steamGamesWorker } from "@main/workers";
|
import { steamGamesWorker } from "@main/workers";
|
||||||
|
import { createGame } from "@main/services/library-sync";
|
||||||
|
|
||||||
const startGameDownload = async (
|
const startGameDownload = async (
|
||||||
_event: Electron.IpcMainInvokeEvent,
|
_event: Electron.IpcMainInvokeEvent,
|
||||||
@ -94,6 +95,19 @@ const startGameDownload = async (
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
createGame(updatedGame!).then((response) => {
|
||||||
|
const {
|
||||||
|
id: remoteId,
|
||||||
|
playTimeInMilliseconds,
|
||||||
|
lastTimePlayed,
|
||||||
|
} = response.data;
|
||||||
|
|
||||||
|
gameRepository.update(
|
||||||
|
{ objectID },
|
||||||
|
{ remoteId, playTimeInMilliseconds, lastTimePlayed }
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
await downloadQueueRepository.delete({ game: { id: updatedGame!.id } });
|
await downloadQueueRepository.delete({ game: { id: updatedGame!.id } });
|
||||||
await downloadQueueRepository.insert({ game: { id: updatedGame!.id } });
|
await downloadQueueRepository.insert({ game: { id: updatedGame!.id } });
|
||||||
|
|
||||||
|
@ -10,6 +10,7 @@ import { fetchDownloadSourcesAndUpdate } from "./helpers";
|
|||||||
import { publishNewRepacksNotifications } from "./services/notifications";
|
import { publishNewRepacksNotifications } from "./services/notifications";
|
||||||
import { MoreThan } from "typeorm";
|
import { MoreThan } from "typeorm";
|
||||||
import { HydraApi } from "./services/hydra-api";
|
import { HydraApi } from "./services/hydra-api";
|
||||||
|
import { uploadGamesBatch } from "./services/library-sync";
|
||||||
|
|
||||||
startMainLoop();
|
startMainLoop();
|
||||||
|
|
||||||
@ -21,7 +22,9 @@ const loadState = async (userPreferences: UserPreferences | null) => {
|
|||||||
if (userPreferences?.realDebridApiToken)
|
if (userPreferences?.realDebridApiToken)
|
||||||
RealDebridClient.authorize(userPreferences?.realDebridApiToken);
|
RealDebridClient.authorize(userPreferences?.realDebridApiToken);
|
||||||
|
|
||||||
HydraApi.setupApi();
|
HydraApi.setupApi().then(async () => {
|
||||||
|
if (HydraApi.isLoggedIn()) uploadGamesBatch();
|
||||||
|
});
|
||||||
|
|
||||||
const [nextQueueItem] = await downloadQueueRepository.find({
|
const [nextQueueItem] = await downloadQueueRepository.find({
|
||||||
order: {
|
order: {
|
||||||
|
@ -2,6 +2,9 @@ import { userAuthRepository } from "@main/repository";
|
|||||||
import axios, { AxiosError, AxiosInstance } from "axios";
|
import axios, { AxiosError, AxiosInstance } from "axios";
|
||||||
import { WindowManager } from "./window-manager";
|
import { WindowManager } from "./window-manager";
|
||||||
import url from "url";
|
import url from "url";
|
||||||
|
import { uploadGamesBatch } from "./library-sync";
|
||||||
|
import { clearGamesRemoteIds } from "./library-sync/clear-games-remote-id";
|
||||||
|
import { logger } from "./logger";
|
||||||
|
|
||||||
export class HydraApi {
|
export class HydraApi {
|
||||||
private static instance: AxiosInstance;
|
private static instance: AxiosInstance;
|
||||||
@ -53,6 +56,8 @@ export class HydraApi {
|
|||||||
|
|
||||||
if (WindowManager.mainWindow) {
|
if (WindowManager.mainWindow) {
|
||||||
WindowManager.mainWindow.webContents.send("on-signin");
|
WindowManager.mainWindow.webContents.send("on-signin");
|
||||||
|
await clearGamesRemoteIds();
|
||||||
|
uploadGamesBatch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,20 +68,20 @@ export class HydraApi {
|
|||||||
|
|
||||||
this.instance.interceptors.request.use(
|
this.instance.interceptors.request.use(
|
||||||
(request) => {
|
(request) => {
|
||||||
console.log(" ---- REQUEST -----");
|
logger.log(" ---- REQUEST -----");
|
||||||
console.log(request.method, request.url, request.data);
|
logger.log(request.method, request.url, request.data);
|
||||||
return request;
|
return request;
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
console.log("request error", error);
|
logger.log("request error", error);
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
this.instance.interceptors.response.use(
|
this.instance.interceptors.response.use(
|
||||||
(response) => {
|
(response) => {
|
||||||
console.log(" ---- RESPONSE -----");
|
logger.log(" ---- RESPONSE -----");
|
||||||
console.log(
|
logger.log(
|
||||||
response.status,
|
response.status,
|
||||||
response.config.method,
|
response.config.method,
|
||||||
response.config.url,
|
response.config.url,
|
||||||
@ -85,7 +90,7 @@ export class HydraApi {
|
|||||||
return response;
|
return response;
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
console.log("response error", error);
|
logger.error("response error", error);
|
||||||
return Promise.reject(error);
|
return Promise.reject(error);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -102,7 +107,11 @@ export class HydraApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static async revalidateAccessTokenIfExpired() {
|
private static async revalidateAccessTokenIfExpired() {
|
||||||
if (!this.userAuth.authToken) throw new Error("user is not logged in");
|
if (!this.userAuth.authToken) {
|
||||||
|
userAuthRepository.delete({ id: 1 });
|
||||||
|
logger.error("user is not logged in");
|
||||||
|
throw new Error("user is not logged in");
|
||||||
|
}
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
if (this.userAuth.expirationTimestamp < now.getTime()) {
|
if (this.userAuth.expirationTimestamp < now.getTime()) {
|
||||||
@ -145,6 +154,8 @@ export class HydraApi {
|
|||||||
if (WindowManager.mainWindow) {
|
if (WindowManager.mainWindow) {
|
||||||
WindowManager.mainWindow.webContents.send("on-signout");
|
WindowManager.mainWindow.webContents.send("on-signout");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.log("user refresh token expired");
|
||||||
}
|
}
|
||||||
|
|
||||||
throw err;
|
throw err;
|
||||||
@ -179,4 +190,9 @@ export class HydraApi {
|
|||||||
await this.revalidateAccessTokenIfExpired();
|
await this.revalidateAccessTokenIfExpired();
|
||||||
return this.instance.patch(url, data, this.getAxiosConfig());
|
return this.instance.patch(url, data, this.getAxiosConfig());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async delete(url: string) {
|
||||||
|
await this.revalidateAccessTokenIfExpired();
|
||||||
|
return this.instance.delete(url, this.getAxiosConfig());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
5
src/main/services/library-sync/clear-games-remote-id.ts
Normal file
5
src/main/services/library-sync/clear-games-remote-id.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { gameRepository } from "@main/repository";
|
||||||
|
|
||||||
|
export const clearGamesRemoteIds = () => {
|
||||||
|
return gameRepository.update({}, { remoteId: null });
|
||||||
|
};
|
11
src/main/services/library-sync/create-game.ts
Normal file
11
src/main/services/library-sync/create-game.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { Game } from "@main/entity";
|
||||||
|
import { HydraApi } from "../hydra-api";
|
||||||
|
|
||||||
|
export const createGame = async (game: Game) => {
|
||||||
|
return HydraApi.post(`/games`, {
|
||||||
|
objectId: game.objectID,
|
||||||
|
playTimeInMilliseconds: Math.trunc(game.playTimeInMilliseconds),
|
||||||
|
shop: game.shop,
|
||||||
|
lastTimePlayed: game.lastTimePlayed,
|
||||||
|
});
|
||||||
|
};
|
4
src/main/services/library-sync/index.ts
Normal file
4
src/main/services/library-sync/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export * from "./merge-with-remote-games";
|
||||||
|
export * from "./upload-games-batch";
|
||||||
|
export * from "./update-game-playtime";
|
||||||
|
export * from "./create-game";
|
69
src/main/services/library-sync/merge-with-remote-games.ts
Normal file
69
src/main/services/library-sync/merge-with-remote-games.ts
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
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 ||
|
||||||
|
new Date(game.lastTimePlayed) > localGame.lastTimePlayed
|
||||||
|
? new Date(game.lastTimePlayed)
|
||||||
|
: localGame.lastTimePlayed;
|
||||||
|
|
||||||
|
const updatedPlayTime =
|
||||||
|
localGame.playTimeInMilliseconds < game.playTimeInMilliseconds
|
||||||
|
? game.playTimeInMilliseconds
|
||||||
|
: localGame.playTimeInMilliseconds;
|
||||||
|
|
||||||
|
gameRepository.update(
|
||||||
|
{
|
||||||
|
objectID: game.objectId,
|
||||||
|
shop: "steam",
|
||||||
|
lastTimePlayed: updatedLastTimePlayed,
|
||||||
|
playTimeInMilliseconds: updatedPlayTime,
|
||||||
|
},
|
||||||
|
{ remoteId: game.id }
|
||||||
|
);
|
||||||
|
} 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.response, err.message);
|
||||||
|
} else {
|
||||||
|
logger.error("getRemoteGames", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
13
src/main/services/library-sync/update-game-playtime.ts
Normal file
13
src/main/services/library-sync/update-game-playtime.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { Game } from "@main/entity";
|
||||||
|
import { HydraApi } from "../hydra-api";
|
||||||
|
|
||||||
|
export const updateGamePlaytime = async (
|
||||||
|
game: Game,
|
||||||
|
delta: number,
|
||||||
|
lastTimePlayed: Date
|
||||||
|
) => {
|
||||||
|
return HydraApi.put(`/games/${game.remoteId}`, {
|
||||||
|
playTimeDeltaInSeconds: Math.trunc(delta),
|
||||||
|
lastTimePlayed,
|
||||||
|
});
|
||||||
|
};
|
44
src/main/services/library-sync/upload-games-batch.ts
Normal file
44
src/main/services/library-sync/upload-games-batch.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
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 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -4,8 +4,12 @@ import { IsNull, Not } from "typeorm";
|
|||||||
import { gameRepository } from "@main/repository";
|
import { gameRepository } from "@main/repository";
|
||||||
import { getProcesses } from "@main/helpers";
|
import { getProcesses } from "@main/helpers";
|
||||||
import { WindowManager } from "./window-manager";
|
import { WindowManager } from "./window-manager";
|
||||||
|
import { createGame, updateGamePlaytime } from "./library-sync";
|
||||||
|
|
||||||
const gamesPlaytime = new Map<number, number>();
|
const gamesPlaytime = new Map<
|
||||||
|
number,
|
||||||
|
{ lastTick: number; firstTick: number }
|
||||||
|
>();
|
||||||
|
|
||||||
export const watchProcesses = async () => {
|
export const watchProcesses = async () => {
|
||||||
const games = await gameRepository.find({
|
const games = await gameRepository.find({
|
||||||
@ -37,7 +41,9 @@ export const watchProcesses = async () => {
|
|||||||
|
|
||||||
if (gameProcess) {
|
if (gameProcess) {
|
||||||
if (gamesPlaytime.has(game.id)) {
|
if (gamesPlaytime.has(game.id)) {
|
||||||
const zero = gamesPlaytime.get(game.id) ?? 0;
|
const gamePlaytime = gamesPlaytime.get(game.id)!;
|
||||||
|
|
||||||
|
const zero = gamePlaytime.lastTick;
|
||||||
const delta = performance.now() - zero;
|
const delta = performance.now() - zero;
|
||||||
|
|
||||||
if (WindowManager.mainWindow) {
|
if (WindowManager.mainWindow) {
|
||||||
@ -48,12 +54,45 @@ export const watchProcesses = async () => {
|
|||||||
playTimeInMilliseconds: game.playTimeInMilliseconds + delta,
|
playTimeInMilliseconds: game.playTimeInMilliseconds + delta,
|
||||||
lastTimePlayed: new Date(),
|
lastTimePlayed: new Date(),
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
gamesPlaytime.set(game.id, performance.now());
|
gamesPlaytime.set(game.id, {
|
||||||
|
...gamePlaytime,
|
||||||
|
lastTick: performance.now(),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
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 });
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
gamesPlaytime.set(game.id, {
|
||||||
|
lastTick: performance.now(),
|
||||||
|
firstTick: performance.now(),
|
||||||
|
});
|
||||||
|
}
|
||||||
} else if (gamesPlaytime.has(game.id)) {
|
} else if (gamesPlaytime.has(game.id)) {
|
||||||
|
const gamePlaytime = gamesPlaytime.get(game.id)!;
|
||||||
gamesPlaytime.delete(game.id);
|
gamesPlaytime.delete(game.id);
|
||||||
|
|
||||||
|
if (game.remoteId) {
|
||||||
|
updateGamePlaytime(
|
||||||
|
game,
|
||||||
|
performance.now() - gamePlaytime.firstTick,
|
||||||
|
game.lastTimePlayed!
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
createGame(game).then((response) => {
|
||||||
|
const { id: remoteId } = response.data;
|
||||||
|
gameRepository.update({ objectID: game.objectID }, { remoteId });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (WindowManager.mainWindow) {
|
if (WindowManager.mainWindow) {
|
||||||
WindowManager.mainWindow.webContents.send("on-game-close", game.id);
|
WindowManager.mainWindow.webContents.send("on-game-close", game.id);
|
||||||
}
|
}
|
||||||
|
@ -96,6 +96,12 @@ contextBridge.exposeInMainWorld("electron", {
|
|||||||
ipcRenderer.on("on-game-close", listener);
|
ipcRenderer.on("on-game-close", listener);
|
||||||
return () => ipcRenderer.removeListener("on-game-close", listener);
|
return () => ipcRenderer.removeListener("on-game-close", listener);
|
||||||
},
|
},
|
||||||
|
onLibraryBatchComplete: (cb: () => void) => {
|
||||||
|
const listener = (_event: Electron.IpcRendererEvent) => cb();
|
||||||
|
ipcRenderer.on("on-library-batch-complete", listener);
|
||||||
|
return () =>
|
||||||
|
ipcRenderer.removeListener("on-library-batch-complete", listener);
|
||||||
|
},
|
||||||
|
|
||||||
/* Hardware */
|
/* Hardware */
|
||||||
getDiskFreeSpace: (path: string) =>
|
getDiskFreeSpace: (path: string) =>
|
||||||
|
@ -102,6 +102,9 @@ export function App() {
|
|||||||
if (response) updateUserDetails(response);
|
if (response) updateUserDetails(response);
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
|
window.electron.onLibraryBatchComplete(() => {
|
||||||
|
updateLibrary();
|
||||||
|
}),
|
||||||
window.electron.onSignOut(() => {
|
window.electron.onSignOut(() => {
|
||||||
clearUserDetails();
|
clearUserDetails();
|
||||||
}),
|
}),
|
||||||
|
@ -9,8 +9,8 @@ import {
|
|||||||
} from "@renderer/helpers";
|
} from "@renderer/helpers";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
const FEATURED_GAME_TITLE = "Horizon Forbidden West™ Complete Edition";
|
const FEATURED_GAME_TITLE = "Ghost of Tsushima DIRECTOR'S CUT";
|
||||||
const FEATURED_GAME_ID = "2420110";
|
const FEATURED_GAME_ID = "2215430";
|
||||||
|
|
||||||
export function Hero() {
|
export function Hero() {
|
||||||
const [featuredGameDetails, setFeaturedGameDetails] =
|
const [featuredGameDetails, setFeaturedGameDetails] =
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { Downloader } from "@shared";
|
import { Downloader } from "@shared";
|
||||||
|
|
||||||
export const VERSION_CODENAME = "Exodus";
|
export const VERSION_CODENAME = "Leviticus";
|
||||||
|
|
||||||
export const DOWNLOADER_NAME = {
|
export const DOWNLOADER_NAME = {
|
||||||
[Downloader.RealDebrid]: "Real-Debrid",
|
[Downloader.RealDebrid]: "Real-Debrid",
|
||||||
|
3
src/renderer/src/declaration.d.ts
vendored
3
src/renderer/src/declaration.d.ts
vendored
@ -73,6 +73,7 @@ declare global {
|
|||||||
getGameByObjectID: (objectID: string) => Promise<Game | null>;
|
getGameByObjectID: (objectID: string) => Promise<Game | null>;
|
||||||
onPlaytime: (cb: (gameId: number) => void) => () => Electron.IpcRenderer;
|
onPlaytime: (cb: (gameId: number) => void) => () => Electron.IpcRenderer;
|
||||||
onGameClose: (cb: (gameId: number) => void) => () => Electron.IpcRenderer;
|
onGameClose: (cb: (gameId: number) => void) => () => Electron.IpcRenderer;
|
||||||
|
onLibraryBatchComplete: (cb: () => void) => () => Electron.IpcRenderer;
|
||||||
|
|
||||||
/* User preferences */
|
/* User preferences */
|
||||||
getUserPreferences: () => Promise<UserPreferences | null>;
|
getUserPreferences: () => Promise<UserPreferences | null>;
|
||||||
@ -112,7 +113,7 @@ declare global {
|
|||||||
checkForUpdates: () => Promise<boolean>;
|
checkForUpdates: () => Promise<boolean>;
|
||||||
restartAndInstallUpdate: () => Promise<void>;
|
restartAndInstallUpdate: () => Promise<void>;
|
||||||
|
|
||||||
/* Authg */
|
/* Auth */
|
||||||
signOut: () => Promise<void>;
|
signOut: () => Promise<void>;
|
||||||
onSignIn: (cb: () => void) => () => Electron.IpcRenderer;
|
onSignIn: (cb: () => void) => () => Electron.IpcRenderer;
|
||||||
onSignOut: (cb: () => void) => () => Electron.IpcRenderer;
|
onSignOut: (cb: () => void) => () => Electron.IpcRenderer;
|
||||||
|
Loading…
Reference in New Issue
Block a user