mirror of
https://github.com/hydralauncher/hydra.git
synced 2026-01-26 20:31:03 +00:00
Merge branch 'feat/reviews-and-commenting' of https://github.com/hydralauncher/hydra into feat/reviews-and-commenting
This commit is contained in:
@@ -253,6 +253,7 @@
|
|||||||
"uploading_backup": "Uploading backup…",
|
"uploading_backup": "Uploading backup…",
|
||||||
"no_backups": "You haven't created any backups for this game yet",
|
"no_backups": "You haven't created any backups for this game yet",
|
||||||
"backup_uploaded": "Backup uploaded",
|
"backup_uploaded": "Backup uploaded",
|
||||||
|
"backup_failed": "Backup failed",
|
||||||
"backup_deleted": "Backup deleted",
|
"backup_deleted": "Backup deleted",
|
||||||
"backup_restored": "Backup restored",
|
"backup_restored": "Backup restored",
|
||||||
"see_all_achievements": "See all achievements",
|
"see_all_achievements": "See all achievements",
|
||||||
|
|||||||
@@ -204,6 +204,7 @@
|
|||||||
"uploading_backup": "Subiendo copia de seguridad…",
|
"uploading_backup": "Subiendo copia de seguridad…",
|
||||||
"no_backups": "No has creado ninguna copia de seguridad para este juego todavía",
|
"no_backups": "No has creado ninguna copia de seguridad para este juego todavía",
|
||||||
"backup_uploaded": "Copia de seguridad subida",
|
"backup_uploaded": "Copia de seguridad subida",
|
||||||
|
"backup_failed": "Copia de seguridad fallida",
|
||||||
"backup_deleted": "Copia de seguridad eliminada",
|
"backup_deleted": "Copia de seguridad eliminada",
|
||||||
"backup_restored": "Copia de seguridad restaurada",
|
"backup_restored": "Copia de seguridad restaurada",
|
||||||
"see_all_achievements": "Ver todos los logros",
|
"see_all_achievements": "Ver todos los logros",
|
||||||
|
|||||||
@@ -142,6 +142,7 @@
|
|||||||
"uploading_backup": "A criar backup…",
|
"uploading_backup": "A criar backup…",
|
||||||
"no_backups": "Ainda não fizeste nenhum backup deste jogo",
|
"no_backups": "Ainda não fizeste nenhum backup deste jogo",
|
||||||
"backup_uploaded": "Backup criado",
|
"backup_uploaded": "Backup criado",
|
||||||
|
"backup_failed": "Falha ao criar backup",
|
||||||
"backup_deleted": "Backup apagado",
|
"backup_deleted": "Backup apagado",
|
||||||
"backup_restored": "Backup restaurado",
|
"backup_restored": "Backup restaurado",
|
||||||
"see_all_achievements": "Ver todas as conquistas",
|
"see_all_achievements": "Ver todas as conquistas",
|
||||||
|
|||||||
51
src/main/events/catalogue/get-game-assets.ts
Normal file
51
src/main/events/catalogue/get-game-assets.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import type { GameShop, ShopAssets } from "@types";
|
||||||
|
import { registerEvent } from "../register-event";
|
||||||
|
import { HydraApi } from "@main/services";
|
||||||
|
import { gamesShopAssetsSublevel, levelKeys } from "@main/level";
|
||||||
|
|
||||||
|
const LOCAL_CACHE_EXPIRATION = 1000 * 60 * 60 * 8; // 8 hours
|
||||||
|
|
||||||
|
export const getGameAssets = async (objectId: string, shop: GameShop) => {
|
||||||
|
const cachedAssets = await gamesShopAssetsSublevel.get(
|
||||||
|
levelKeys.game(shop, objectId)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
cachedAssets &&
|
||||||
|
cachedAssets.updatedAt + LOCAL_CACHE_EXPIRATION > Date.now()
|
||||||
|
) {
|
||||||
|
return cachedAssets;
|
||||||
|
}
|
||||||
|
|
||||||
|
return HydraApi.get<ShopAssets | null>(
|
||||||
|
`/games/${shop}/${objectId}/assets`,
|
||||||
|
null,
|
||||||
|
{
|
||||||
|
needsAuth: false,
|
||||||
|
}
|
||||||
|
).then(async (assets) => {
|
||||||
|
if (!assets) return null;
|
||||||
|
|
||||||
|
// Preserve existing title if it differs from the incoming title (indicating it was customized)
|
||||||
|
const shouldPreserveTitle =
|
||||||
|
cachedAssets?.title && cachedAssets.title !== assets.title;
|
||||||
|
|
||||||
|
await gamesShopAssetsSublevel.put(levelKeys.game(shop, objectId), {
|
||||||
|
...assets,
|
||||||
|
title: shouldPreserveTitle ? cachedAssets.title : assets.title,
|
||||||
|
updatedAt: Date.now(),
|
||||||
|
});
|
||||||
|
|
||||||
|
return assets;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getGameAssetsEvent = async (
|
||||||
|
_event: Electron.IpcMainInvokeEvent,
|
||||||
|
objectId: string,
|
||||||
|
shop: GameShop
|
||||||
|
) => {
|
||||||
|
return getGameAssets(objectId, shop);
|
||||||
|
};
|
||||||
|
|
||||||
|
registerEvent("getGameAssets", getGameAssetsEvent);
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import type { GameShop, ShopAssets } from "@types";
|
|
||||||
import { gamesShopAssetsSublevel, levelKeys } from "@main/level";
|
|
||||||
import { registerEvent } from "../register-event";
|
|
||||||
|
|
||||||
const saveGameShopAssets = async (
|
|
||||||
_event: Electron.IpcMainInvokeEvent,
|
|
||||||
objectId: string,
|
|
||||||
shop: GameShop,
|
|
||||||
assets: ShopAssets
|
|
||||||
): Promise<void> => {
|
|
||||||
const key = levelKeys.game(shop, objectId);
|
|
||||||
const existingAssets = await gamesShopAssetsSublevel.get(key);
|
|
||||||
|
|
||||||
// Preserve existing title if it differs from the incoming title (indicating it was customized)
|
|
||||||
const shouldPreserveTitle =
|
|
||||||
existingAssets?.title && existingAssets.title !== assets.title;
|
|
||||||
|
|
||||||
return gamesShopAssetsSublevel.put(key, {
|
|
||||||
...existingAssets,
|
|
||||||
...assets,
|
|
||||||
title: shouldPreserveTitle ? existingAssets.title : assets.title,
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
registerEvent("saveGameShopAssets", saveGameShopAssets);
|
|
||||||
@@ -3,7 +3,6 @@ import { ipcMain } from "electron";
|
|||||||
|
|
||||||
import "./catalogue/get-catalogue";
|
import "./catalogue/get-catalogue";
|
||||||
import "./catalogue/get-game-shop-details";
|
import "./catalogue/get-game-shop-details";
|
||||||
import "./catalogue/save-game-shop-assets";
|
|
||||||
import "./catalogue/get-how-long-to-beat";
|
import "./catalogue/get-how-long-to-beat";
|
||||||
import "./catalogue/get-random-game";
|
import "./catalogue/get-random-game";
|
||||||
import "./catalogue/search-games";
|
import "./catalogue/search-games";
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ const addCustomGameToLibrary = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const assets = {
|
const assets = {
|
||||||
|
updatedAt: Date.now(),
|
||||||
objectId,
|
objectId,
|
||||||
shop,
|
shop,
|
||||||
title,
|
title,
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
import { registerEvent } from "../register-event";
|
import { registerEvent } from "../register-event";
|
||||||
import type { GameShop, GameStats } from "@types";
|
import type { GameShop, ShopAssets } from "@types";
|
||||||
import { gamesSublevel, levelKeys } from "@main/level";
|
import { gamesSublevel, levelKeys } from "@main/level";
|
||||||
import {
|
import {
|
||||||
composeSteamShortcut,
|
composeSteamShortcut,
|
||||||
getSteamLocation,
|
getSteamLocation,
|
||||||
getSteamShortcuts,
|
getSteamShortcuts,
|
||||||
getSteamUsersIds,
|
getSteamUsersIds,
|
||||||
HydraApi,
|
|
||||||
logger,
|
logger,
|
||||||
SystemPath,
|
SystemPath,
|
||||||
writeSteamShortcuts,
|
writeSteamShortcuts,
|
||||||
@@ -15,6 +14,7 @@ import fs from "node:fs";
|
|||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import { ASSETS_PATH } from "@main/constants";
|
import { ASSETS_PATH } from "@main/constants";
|
||||||
|
import { getGameAssets } from "../catalogue/get-game-assets";
|
||||||
|
|
||||||
const downloadAsset = async (downloadPath: string, url?: string | null) => {
|
const downloadAsset = async (downloadPath: string, url?: string | null) => {
|
||||||
try {
|
try {
|
||||||
@@ -41,7 +41,7 @@ const downloadAsset = async (downloadPath: string, url?: string | null) => {
|
|||||||
const downloadAssetsFromSteam = async (
|
const downloadAssetsFromSteam = async (
|
||||||
shop: GameShop,
|
shop: GameShop,
|
||||||
objectId: string,
|
objectId: string,
|
||||||
assets: GameStats["assets"]
|
assets: ShopAssets | null
|
||||||
) => {
|
) => {
|
||||||
const gameAssetsPath = path.join(ASSETS_PATH, `${shop}-${objectId}`);
|
const gameAssetsPath = path.join(ASSETS_PATH, `${shop}-${objectId}`);
|
||||||
|
|
||||||
@@ -86,9 +86,7 @@ const createSteamShortcut = async (
|
|||||||
throw new Error("No executable path found for game");
|
throw new Error("No executable path found for game");
|
||||||
}
|
}
|
||||||
|
|
||||||
const { assets } = await HydraApi.get<GameStats>(
|
const assets = await getGameAssets(objectId, shop);
|
||||||
`/games/${shop}/${objectId}/stats`
|
|
||||||
);
|
|
||||||
|
|
||||||
const steamUserIds = await getSteamUsersIds();
|
const steamUserIds = await getSteamUsersIds();
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,9 @@ import type { ShopAssets } from "@types";
|
|||||||
import { db } from "../level";
|
import { db } from "../level";
|
||||||
import { levelKeys } from "./keys";
|
import { levelKeys } from "./keys";
|
||||||
|
|
||||||
export const gamesShopAssetsSublevel = db.sublevel<string, ShopAssets>(
|
export const gamesShopAssetsSublevel = db.sublevel<
|
||||||
levelKeys.gameShopAssets,
|
string,
|
||||||
{
|
ShopAssets & { updatedAt: number }
|
||||||
valueEncoding: "json",
|
>(levelKeys.gameShopAssets, {
|
||||||
}
|
valueEncoding: "json",
|
||||||
);
|
});
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ export class CloudSync {
|
|||||||
try {
|
try {
|
||||||
await fs.promises.rm(backupPath, { recursive: true });
|
await fs.promises.rm(backupPath, { recursive: true });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("Failed to remove backup path", error);
|
logger.error("Failed to remove backup path", { backupPath, error });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ export class CloudSync {
|
|||||||
try {
|
try {
|
||||||
await fs.promises.unlink(bundleLocation);
|
await fs.promises.unlink(bundleLocation);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error("Failed to remove tar file", error);
|
logger.error("Failed to remove tar file", { bundleLocation, error });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ export const mergeWithRemoteGames = async () => {
|
|||||||
const updatedLastTimePlayed =
|
const updatedLastTimePlayed =
|
||||||
localGame.lastTimePlayed == null ||
|
localGame.lastTimePlayed == null ||
|
||||||
(game.lastTimePlayed &&
|
(game.lastTimePlayed &&
|
||||||
new Date(game.lastTimePlayed) > localGame.lastTimePlayed)
|
new Date(game.lastTimePlayed) >
|
||||||
|
new Date(localGame.lastTimePlayed))
|
||||||
? game.lastTimePlayed
|
? game.lastTimePlayed
|
||||||
: localGame.lastTimePlayed;
|
: localGame.lastTimePlayed;
|
||||||
|
|
||||||
@@ -57,7 +58,11 @@ export const mergeWithRemoteGames = async () => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const localGameShopAsset = await gamesShopAssetsSublevel.get(gameKey);
|
||||||
|
|
||||||
await gamesShopAssetsSublevel.put(gameKey, {
|
await gamesShopAssetsSublevel.put(gameKey, {
|
||||||
|
updatedAt: Date.now(),
|
||||||
|
...localGameShopAsset,
|
||||||
shop: game.shop,
|
shop: game.shop,
|
||||||
objectId: game.objectId,
|
objectId: game.objectId,
|
||||||
title: localGame?.title || game.title, // Preserve local title if it exists
|
title: localGame?.title || game.title, // Preserve local title if it exists
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import icon from "@resources/icon.png?asset";
|
|||||||
import { NotificationOptions, toXmlString } from "./xml";
|
import { NotificationOptions, toXmlString } from "./xml";
|
||||||
import { logger } from "../logger";
|
import { logger } from "../logger";
|
||||||
import { WindowManager } from "../window-manager";
|
import { WindowManager } from "../window-manager";
|
||||||
import type { Game, GameStats, UserPreferences, UserProfile } from "@types";
|
import type { Game, UserPreferences, UserProfile } from "@types";
|
||||||
import { db, levelKeys } from "@main/level";
|
import { db, levelKeys } from "@main/level";
|
||||||
import { restartAndInstallUpdate } from "@main/events/autoupdater/restart-and-install-update";
|
import { restartAndInstallUpdate } from "@main/events/autoupdater/restart-and-install-update";
|
||||||
import { SystemPath } from "../system-path";
|
import { SystemPath } from "../system-path";
|
||||||
@@ -108,15 +108,14 @@ export const publishNewFriendRequestNotification = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const publishFriendStartedPlayingGameNotification = async (
|
export const publishFriendStartedPlayingGameNotification = async (
|
||||||
friend: UserProfile,
|
friend: UserProfile
|
||||||
game: GameStats
|
|
||||||
) => {
|
) => {
|
||||||
new Notification({
|
new Notification({
|
||||||
title: t("friend_started_playing_game", {
|
title: t("friend_started_playing_game", {
|
||||||
ns: "notifications",
|
ns: "notifications",
|
||||||
displayName: friend.displayName,
|
displayName: friend.displayName,
|
||||||
}),
|
}),
|
||||||
body: game.assets?.title,
|
body: friend?.currentGame?.title,
|
||||||
icon: friend?.profileImageUrl
|
icon: friend?.profileImageUrl
|
||||||
? await downloadImage(friend.profileImageUrl)
|
? await downloadImage(friend.profileImageUrl)
|
||||||
: trayIcon,
|
: trayIcon,
|
||||||
|
|||||||
@@ -76,7 +76,11 @@ export const getSteamAppDetails = async (
|
|||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
logger.error(err, { method: "getSteamAppDetails" });
|
logger.error("Error on getSteamAppDetails", {
|
||||||
|
message: err?.message,
|
||||||
|
code: err?.code,
|
||||||
|
name: err?.name,
|
||||||
|
});
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { FriendGameSession } from "@main/generated/envelope";
|
|||||||
import { db, levelKeys } from "@main/level";
|
import { db, levelKeys } from "@main/level";
|
||||||
import { HydraApi } from "@main/services/hydra-api";
|
import { HydraApi } from "@main/services/hydra-api";
|
||||||
import { publishFriendStartedPlayingGameNotification } from "@main/services/notifications";
|
import { publishFriendStartedPlayingGameNotification } from "@main/services/notifications";
|
||||||
import type { GameStats, UserPreferences, UserProfile } from "@types";
|
import type { UserPreferences, UserProfile } from "@types";
|
||||||
|
|
||||||
export const friendGameSessionEvent = async (payload: FriendGameSession) => {
|
export const friendGameSessionEvent = async (payload: FriendGameSession) => {
|
||||||
const userPreferences = await db.get<string, UserPreferences | null>(
|
const userPreferences = await db.get<string, UserPreferences | null>(
|
||||||
@@ -14,12 +14,9 @@ export const friendGameSessionEvent = async (payload: FriendGameSession) => {
|
|||||||
|
|
||||||
if (userPreferences?.friendStartGameNotificationsEnabled === false) return;
|
if (userPreferences?.friendStartGameNotificationsEnabled === false) return;
|
||||||
|
|
||||||
const [friend, gameStats] = await Promise.all([
|
const friend = await HydraApi.get<UserProfile>(`/users/${payload.friendId}`);
|
||||||
HydraApi.get<UserProfile>(`/users/${payload.friendId}`),
|
|
||||||
HydraApi.get<GameStats>(`/games/steam/${payload.objectId}/stats`),
|
|
||||||
]).catch(() => [null, null]);
|
|
||||||
|
|
||||||
if (friend && gameStats) {
|
if (friend) {
|
||||||
publishFriendStartedPlayingGameNotification(friend, gameStats);
|
publishFriendStartedPlayingGameNotification(friend);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import type {
|
|||||||
Theme,
|
Theme,
|
||||||
FriendRequestSync,
|
FriendRequestSync,
|
||||||
ShortcutLocation,
|
ShortcutLocation,
|
||||||
ShopAssets,
|
|
||||||
AchievementCustomNotificationPosition,
|
AchievementCustomNotificationPosition,
|
||||||
AchievementNotificationInfo,
|
AchievementNotificationInfo,
|
||||||
} from "@types";
|
} from "@types";
|
||||||
@@ -67,8 +66,6 @@ contextBridge.exposeInMainWorld("electron", {
|
|||||||
ipcRenderer.invoke("searchGames", payload, take, skip),
|
ipcRenderer.invoke("searchGames", payload, take, skip),
|
||||||
getCatalogue: (category: CatalogueCategory) =>
|
getCatalogue: (category: CatalogueCategory) =>
|
||||||
ipcRenderer.invoke("getCatalogue", category),
|
ipcRenderer.invoke("getCatalogue", category),
|
||||||
saveGameShopAssets: (objectId: string, shop: GameShop, assets: ShopAssets) =>
|
|
||||||
ipcRenderer.invoke("saveGameShopAssets", objectId, shop, assets),
|
|
||||||
getGameShopDetails: (objectId: string, shop: GameShop, language: string) =>
|
getGameShopDetails: (objectId: string, shop: GameShop, language: string) =>
|
||||||
ipcRenderer.invoke("getGameShopDetails", objectId, shop, language),
|
ipcRenderer.invoke("getGameShopDetails", objectId, shop, language),
|
||||||
getRandomGame: () => ipcRenderer.invoke("getRandomGame"),
|
getRandomGame: () => ipcRenderer.invoke("getRandomGame"),
|
||||||
@@ -76,6 +73,8 @@ contextBridge.exposeInMainWorld("electron", {
|
|||||||
ipcRenderer.invoke("getHowLongToBeat", objectId, shop),
|
ipcRenderer.invoke("getHowLongToBeat", objectId, shop),
|
||||||
getGameStats: (objectId: string, shop: GameShop) =>
|
getGameStats: (objectId: string, shop: GameShop) =>
|
||||||
ipcRenderer.invoke("getGameStats", objectId, shop),
|
ipcRenderer.invoke("getGameStats", objectId, shop),
|
||||||
|
getGameAssets: (objectId: string, shop: GameShop) =>
|
||||||
|
ipcRenderer.invoke("getGameAssets", objectId, shop),
|
||||||
getTrendingGames: () => ipcRenderer.invoke("getTrendingGames"),
|
getTrendingGames: () => ipcRenderer.invoke("getTrendingGames"),
|
||||||
createGameReview: (
|
createGameReview: (
|
||||||
shop: GameShop,
|
shop: GameShop,
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ export function CloudSyncContextProvider({
|
|||||||
const [loadingPreview, setLoadingPreview] = useState(false);
|
const [loadingPreview, setLoadingPreview] = useState(false);
|
||||||
const [freezingArtifact, setFreezingArtifact] = useState(false);
|
const [freezingArtifact, setFreezingArtifact] = useState(false);
|
||||||
|
|
||||||
const { showSuccessToast } = useToast();
|
const { showSuccessToast, showErrorToast } = useToast();
|
||||||
|
|
||||||
const downloadGameArtifact = useCallback(
|
const downloadGameArtifact = useCallback(
|
||||||
async (gameArtifactId: string) => {
|
async (gameArtifactId: string) => {
|
||||||
@@ -122,9 +122,15 @@ export function CloudSyncContextProvider({
|
|||||||
const uploadSaveGame = useCallback(
|
const uploadSaveGame = useCallback(
|
||||||
async (downloadOptionTitle: string | null) => {
|
async (downloadOptionTitle: string | null) => {
|
||||||
setUploadingBackup(true);
|
setUploadingBackup(true);
|
||||||
window.electron.uploadSaveGame(objectId, shop, downloadOptionTitle);
|
window.electron
|
||||||
|
.uploadSaveGame(objectId, shop, downloadOptionTitle)
|
||||||
|
.catch((err) => {
|
||||||
|
setUploadingBackup(false);
|
||||||
|
logger.error("Failed to upload save game", { objectId, shop, err });
|
||||||
|
showErrorToast(t("backup_failed"));
|
||||||
|
});
|
||||||
},
|
},
|
||||||
[objectId, shop]
|
[objectId, shop, t, showErrorToast]
|
||||||
);
|
);
|
||||||
|
|
||||||
const toggleArtifactFreeze = useCallback(
|
const toggleArtifactFreeze = useCallback(
|
||||||
|
|||||||
@@ -142,29 +142,23 @@ export function GameDetailsContextProvider({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const statsPromise = window.electron
|
window.electron.getGameStats(objectId, shop).then((result) => {
|
||||||
.getGameStats(objectId, shop)
|
if (abortController.signal.aborted) return;
|
||||||
.then((result) => {
|
setStats(result);
|
||||||
if (abortController.signal.aborted) return null;
|
});
|
||||||
setStats(result);
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
|
|
||||||
Promise.all([shopDetailsPromise, statsPromise])
|
const assetsPromise = window.electron.getGameAssets(objectId, shop);
|
||||||
.then(([_, stats]) => {
|
|
||||||
if (stats) {
|
|
||||||
const assets = stats.assets;
|
|
||||||
if (assets) {
|
|
||||||
window.electron.saveGameShopAssets(objectId, shop, assets);
|
|
||||||
|
|
||||||
setShopDetails((prev) => {
|
Promise.all([shopDetailsPromise, assetsPromise])
|
||||||
if (!prev) return null;
|
.then(([_, assets]) => {
|
||||||
return {
|
if (assets) {
|
||||||
...prev,
|
setShopDetails((prev) => {
|
||||||
assets,
|
if (!prev) return null;
|
||||||
};
|
return {
|
||||||
});
|
...prev,
|
||||||
}
|
assets,
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.finally(() => {
|
.finally(() => {
|
||||||
@@ -207,8 +201,8 @@ export function GameDetailsContextProvider({
|
|||||||
setShowRepacksModal(true);
|
setShowRepacksModal(true);
|
||||||
try {
|
try {
|
||||||
window.history.replaceState({}, document.title, location.pathname);
|
window.history.replaceState({}, document.title, location.pathname);
|
||||||
} catch (_e) {
|
} catch (e) {
|
||||||
void _e;
|
console.error(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [location]);
|
}, [location]);
|
||||||
|
|||||||
10
src/renderer/src/declaration.d.ts
vendored
10
src/renderer/src/declaration.d.ts
vendored
@@ -39,6 +39,7 @@ import type {
|
|||||||
AchievementCustomNotificationPosition,
|
AchievementCustomNotificationPosition,
|
||||||
AchievementNotificationInfo,
|
AchievementNotificationInfo,
|
||||||
UserLibraryResponse,
|
UserLibraryResponse,
|
||||||
|
Game,
|
||||||
} from "@types";
|
} from "@types";
|
||||||
import type { AxiosProgressEvent } from "axios";
|
import type { AxiosProgressEvent } from "axios";
|
||||||
import type disk from "diskusage";
|
import type disk from "diskusage";
|
||||||
@@ -77,11 +78,6 @@ declare global {
|
|||||||
skip: number
|
skip: number
|
||||||
) => Promise<{ edges: CatalogueSearchResult[]; count: number }>;
|
) => Promise<{ edges: CatalogueSearchResult[]; count: number }>;
|
||||||
getCatalogue: (category: CatalogueCategory) => Promise<ShopAssets[]>;
|
getCatalogue: (category: CatalogueCategory) => Promise<ShopAssets[]>;
|
||||||
saveGameShopAssets: (
|
|
||||||
objectId: string,
|
|
||||||
shop: GameShop,
|
|
||||||
assets: ShopAssets
|
|
||||||
) => Promise<void>;
|
|
||||||
getGameShopDetails: (
|
getGameShopDetails: (
|
||||||
objectId: string,
|
objectId: string,
|
||||||
shop: GameShop,
|
shop: GameShop,
|
||||||
@@ -93,6 +89,10 @@ declare global {
|
|||||||
shop: GameShop
|
shop: GameShop
|
||||||
) => Promise<HowLongToBeatCategory[] | null>;
|
) => Promise<HowLongToBeatCategory[] | null>;
|
||||||
getGameStats: (objectId: string, shop: GameShop) => Promise<GameStats>;
|
getGameStats: (objectId: string, shop: GameShop) => Promise<GameStats>;
|
||||||
|
getGameAssets: (
|
||||||
|
objectId: string,
|
||||||
|
shop: GameShop
|
||||||
|
) => Promise<ShopAssets | null>;
|
||||||
getTrendingGames: () => Promise<TrendingGame[]>;
|
getTrendingGames: () => Promise<TrendingGame[]>;
|
||||||
createGameReview: (
|
createGameReview: (
|
||||||
shop: GameShop,
|
shop: GameShop,
|
||||||
|
|||||||
@@ -234,7 +234,7 @@ export function UserLibraryGameCard({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<img
|
<img
|
||||||
src={game.coverImageUrl}
|
src={game.coverImageUrl ?? undefined}
|
||||||
alt={game.title}
|
alt={game.title}
|
||||||
className="user-library-game__game-image"
|
className="user-library-game__game-image"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ export interface ShopAssets {
|
|||||||
libraryImageUrl: string;
|
libraryImageUrl: string;
|
||||||
logoImageUrl: string;
|
logoImageUrl: string;
|
||||||
logoPosition: string | null;
|
logoPosition: string | null;
|
||||||
coverImageUrl: string;
|
coverImageUrl: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ShopDetails = SteamAppDetails & {
|
export type ShopDetails = SteamAppDetails & {
|
||||||
@@ -235,8 +235,8 @@ export interface DownloadSourceValidationResult {
|
|||||||
export interface GameStats {
|
export interface GameStats {
|
||||||
downloadCount: number;
|
downloadCount: number;
|
||||||
playerCount: number;
|
playerCount: number;
|
||||||
assets: ShopAssets | null;
|
|
||||||
averageScore: number | null;
|
averageScore: number | null;
|
||||||
|
reviewCount: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface GameReview {
|
export interface GameReview {
|
||||||
|
|||||||
Reference in New Issue
Block a user