diff --git a/src/main/data-source.ts b/src/main/data-source.ts index 05fdb04d..7414a758 100644 --- a/src/main/data-source.ts +++ b/src/main/data-source.ts @@ -1,23 +1,11 @@ import { DataSource } from "typeorm"; -import { - DownloadQueue, - Game, - GameShopCache, - UserPreferences, - GameAchievement, -} from "@main/entity"; +import { DownloadQueue, Game, UserPreferences } from "@main/entity"; import { databasePath } from "./constants"; export const dataSource = new DataSource({ type: "better-sqlite3", - entities: [ - Game, - UserPreferences, - GameShopCache, - DownloadQueue, - GameAchievement, - ], + entities: [Game, UserPreferences, DownloadQueue], synchronize: false, database: databasePath, }); diff --git a/src/main/entity/game-achievements.entity.ts b/src/main/entity/game-achievements.entity.ts deleted file mode 100644 index 0cb15f6e..00000000 --- a/src/main/entity/game-achievements.entity.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Column, Entity, PrimaryGeneratedColumn } from "typeorm"; - -@Entity("game_achievement") -export class GameAchievement { - @PrimaryGeneratedColumn() - id: number; - - @Column("text") - objectId: string; - - @Column("text") - shop: string; - - @Column("text", { nullable: true }) - unlockedAchievements: string | null; - - @Column("text", { nullable: true }) - achievements: string | null; -} diff --git a/src/main/entity/game-shop-cache.entity.ts b/src/main/entity/game-shop-cache.entity.ts deleted file mode 100644 index 3382da1c..00000000 --- a/src/main/entity/game-shop-cache.entity.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { - Entity, - PrimaryColumn, - Column, - CreateDateColumn, - UpdateDateColumn, -} from "typeorm"; -import type { GameShop } from "@types"; - -@Entity("game_shop_cache") -export class GameShopCache { - @PrimaryColumn("text", { unique: true }) - objectID: string; - - @Column("text") - shop: GameShop; - - @Column("text", { nullable: true }) - serializedData: string; - - /** - * @deprecated Use IndexedDB's `howLongToBeatEntries` instead - */ - @Column("text", { nullable: true }) - howLongToBeatSerializedData: string; - - @Column("text", { nullable: true }) - language: string; - - @CreateDateColumn() - createdAt: Date; - - @UpdateDateColumn() - updatedAt: Date; -} diff --git a/src/main/entity/index.ts b/src/main/entity/index.ts index 06b543d4..f35f643d 100644 --- a/src/main/entity/index.ts +++ b/src/main/entity/index.ts @@ -1,5 +1,3 @@ export * from "./game.entity"; export * from "./user-preferences.entity"; -export * from "./game-shop-cache.entity"; -export * from "./game-achievements.entity"; export * from "./download-queue.entity"; diff --git a/src/main/events/auth/get-session-hash.ts b/src/main/events/auth/get-session-hash.ts index 293fb62e..5848cbd7 100644 --- a/src/main/events/auth/get-session-hash.ts +++ b/src/main/events/auth/get-session-hash.ts @@ -3,7 +3,7 @@ import jwt from "jsonwebtoken"; import { registerEvent } from "../register-event"; import { db } from "@main/level"; import type { Auth } from "@types"; -import { levelKeys } from "@main/level/sublevels/keys"; +import { levelKeys } from "@main/level"; import { Crypto } from "@main/services"; const getSessionHash = async (_event: Electron.IpcMainInvokeEvent) => { diff --git a/src/main/events/auth/sign-out.ts b/src/main/events/auth/sign-out.ts index 1fb3a054..866d1ec0 100644 --- a/src/main/events/auth/sign-out.ts +++ b/src/main/events/auth/sign-out.ts @@ -4,7 +4,7 @@ import { dataSource } from "@main/data-source"; import { DownloadQueue, Game } from "@main/entity"; import { PythonRPC } from "@main/services/python-rpc"; import { db } from "@main/level"; -import { levelKeys } from "@main/level/sublevels/keys"; +import { levelKeys } from "@main/level"; const signOut = async (_event: Electron.IpcMainInvokeEvent) => { const databaseOperations = dataSource diff --git a/src/main/events/catalogue/get-game-shop-details.ts b/src/main/events/catalogue/get-game-shop-details.ts index 08366abc..39f8425b 100644 --- a/src/main/events/catalogue/get-game-shop-details.ts +++ b/src/main/events/catalogue/get-game-shop-details.ts @@ -1,10 +1,10 @@ -import { gameShopCacheRepository } from "@main/repository"; -import { getSteamAppDetails } from "@main/services"; +import { getSteamAppDetails, logger } from "@main/services"; -import type { ShopDetails, GameShop, SteamAppDetails } from "@types"; +import type { ShopDetails, GameShop } from "@types"; import { registerEvent } from "../register-event"; import { steamGamesWorker } from "@main/workers"; +import { gamesShopCacheSublevel, levelKeys } from "@main/level"; const getLocalizedSteamAppDetails = async ( objectId: string, @@ -39,35 +39,27 @@ const getGameShopDetails = async ( language: string ): Promise => { if (shop === "steam") { - const cachedData = await gameShopCacheRepository.findOne({ - where: { objectID: objectId, language }, - }); + const cachedData = await gamesShopCacheSublevel.get( + levelKeys.gameShopCacheItem(shop, objectId, language) + ); const appDetails = getLocalizedSteamAppDetails(objectId, language).then( (result) => { if (result) { - gameShopCacheRepository.upsert( - { - objectID: objectId, - shop: "steam", - language, - serializedData: JSON.stringify(result), - }, - ["objectID"] - ); + gamesShopCacheSublevel + .put(levelKeys.gameShopCacheItem(shop, objectId, language), result) + .catch((err) => { + logger.error("Could not cache game details", err); + }); } return result; } ); - const cachedGame = cachedData?.serializedData - ? (JSON.parse(cachedData?.serializedData) as SteamAppDetails) - : null; - - if (cachedGame) { + if (cachedData) { return { - ...cachedGame, + ...cachedData, objectId, } as ShopDetails; } diff --git a/src/main/events/library/reset-game-achievements.ts b/src/main/events/library/reset-game-achievements.ts index 8d52a3a6..0ea26adf 100644 --- a/src/main/events/library/reset-game-achievements.ts +++ b/src/main/events/library/reset-game-achievements.ts @@ -1,9 +1,10 @@ -import { gameAchievementRepository, gameRepository } from "@main/repository"; +import { gameRepository } from "@main/repository"; import { registerEvent } from "../register-event"; import { findAchievementFiles } from "@main/services/achievements/find-achivement-files"; import fs from "fs"; import { achievementsLogger, HydraApi, WindowManager } from "@main/services"; import { getUnlockedAchievements } from "../user/get-unlocked-achievements"; +import { gameAchievementsSublevel, levelKeys } from "@main/level"; const resetGameAchievements = async ( _event: Electron.IpcMainInvokeEvent, @@ -23,12 +24,21 @@ const resetGameAchievements = async ( } } - await gameAchievementRepository.update( - { objectId: game.objectID }, - { - unlockedAchievements: null, - } - ); + const levelKey = levelKeys.game(game.shop, game.objectID); + + await gameAchievementsSublevel + .get(levelKey) + .then(async (gameAchievements) => { + if (gameAchievements) { + await gameAchievementsSublevel.put( + levelKeys.game(game.shop, game.objectID), + { + ...gameAchievements, + unlockedAchievements: [], + } + ); + } + }); await HydraApi.delete(`/profile/games/achievements/${game.remoteId}`).then( () => diff --git a/src/main/events/misc/open-checkout.ts b/src/main/events/misc/open-checkout.ts index 76e7fe09..95d76d5b 100644 --- a/src/main/events/misc/open-checkout.ts +++ b/src/main/events/misc/open-checkout.ts @@ -3,7 +3,7 @@ import { registerEvent } from "../register-event"; import { Crypto, HydraApi } from "@main/services"; import { db } from "@main/level"; import type { Auth } from "@types"; -import { levelKeys } from "@main/level/sublevels/keys"; +import { levelKeys } from "@main/level"; const openCheckout = async (_event: Electron.IpcMainInvokeEvent) => { const auth = await db.get(levelKeys.auth, { diff --git a/src/main/events/user/get-unlocked-achievements.ts b/src/main/events/user/get-unlocked-achievements.ts index ffa25399..78820a94 100644 --- a/src/main/events/user/get-unlocked-achievements.ts +++ b/src/main/events/user/get-unlocked-achievements.ts @@ -1,19 +1,17 @@ -import type { GameShop, UnlockedAchievement, UserAchievement } from "@types"; +import type { GameShop, UserAchievement } from "@types"; import { registerEvent } from "../register-event"; -import { - gameAchievementRepository, - userPreferencesRepository, -} from "@main/repository"; +import { userPreferencesRepository } from "@main/repository"; import { getGameAchievementData } from "@main/services/achievements/get-game-achievement-data"; +import { gameAchievementsSublevel, levelKeys } from "@main/level"; export const getUnlockedAchievements = async ( objectId: string, shop: GameShop, useCachedData: boolean ): Promise => { - const cachedAchievements = await gameAchievementRepository.findOne({ - where: { objectId, shop }, - }); + const cachedAchievements = await gameAchievementsSublevel.get( + levelKeys.game(shop, objectId) + ); const userPreferences = await userPreferencesRepository.findOne({ where: { id: 1 }, @@ -25,12 +23,10 @@ export const getUnlockedAchievements = async ( const achievementsData = await getGameAchievementData( objectId, shop, - useCachedData ? cachedAchievements : null + useCachedData ); - const unlockedAchievements = JSON.parse( - cachedAchievements?.unlockedAchievements || "[]" - ) as UnlockedAchievement[]; + const unlockedAchievements = cachedAchievements?.unlockedAchievements ?? []; return achievementsData .map((achievementData) => { diff --git a/src/main/events/user/get-user-friends.ts b/src/main/events/user/get-user-friends.ts index 7c308506..aefc7052 100644 --- a/src/main/events/user/get-user-friends.ts +++ b/src/main/events/user/get-user-friends.ts @@ -2,7 +2,7 @@ import { db } from "@main/level"; import { registerEvent } from "../register-event"; import { HydraApi } from "@main/services"; import type { User, UserFriends } from "@types"; -import { levelKeys } from "@main/level/sublevels/keys"; +import { levelKeys } from "@main/level/sublevels"; export const getUserFriends = async ( userId: string, diff --git a/src/main/level/sublevels/game-achievements.ts b/src/main/level/sublevels/game-achievements.ts new file mode 100644 index 00000000..4b1fa0c8 --- /dev/null +++ b/src/main/level/sublevels/game-achievements.ts @@ -0,0 +1,11 @@ +import type { GameAchievement } from "@types"; + +import { db } from "../level"; +import { levelKeys } from "./keys"; + +export const gameAchievementsSublevel = db.sublevel( + levelKeys.gameAchievements, + { + valueEncoding: "json", + } +); diff --git a/src/main/level/sublevels/game-shop-cache.ts b/src/main/level/sublevels/game-shop-cache.ts new file mode 100644 index 00000000..8187e5c0 --- /dev/null +++ b/src/main/level/sublevels/game-shop-cache.ts @@ -0,0 +1,11 @@ +import type { ShopDetails } from "@types"; + +import { db } from "../level"; +import { levelKeys } from "./keys"; + +export const gamesShopCacheSublevel = db.sublevel( + levelKeys.gameShopCache, + { + valueEncoding: "json", + } +); diff --git a/src/main/level/sublevels/games.ts b/src/main/level/sublevels/games.ts index bc0cad30..ce7492f1 100644 --- a/src/main/level/sublevels/games.ts +++ b/src/main/level/sublevels/games.ts @@ -1,4 +1,5 @@ -import { Game } from "@types"; +import type { Game } from "@types"; + import { db } from "../level"; import { levelKeys } from "./keys"; diff --git a/src/main/level/sublevels/index.ts b/src/main/level/sublevels/index.ts index 9d316e1a..ce61c4e2 100644 --- a/src/main/level/sublevels/index.ts +++ b/src/main/level/sublevels/index.ts @@ -1 +1,5 @@ export * from "./games"; +export * from "./game-shop-cache"; +export * from "./game-achievements"; + +export * from "./keys"; diff --git a/src/main/level/sublevels/keys.ts b/src/main/level/sublevels/keys.ts index 6bb54c4a..f2bb6f3c 100644 --- a/src/main/level/sublevels/keys.ts +++ b/src/main/level/sublevels/keys.ts @@ -5,4 +5,8 @@ export const levelKeys = { game: (shop: GameShop, objectId: string) => `${shop}:${objectId}`, user: "user", auth: "auth", + gameShopCache: "gameShopCache", + gameShopCacheItem: (shop: GameShop, objectId: string, language: string) => + `${shop}:${objectId}:${language}`, + gameAchievements: "gameAchievements", }; diff --git a/src/main/repository.ts b/src/main/repository.ts index ef120f7e..5bbfaf9f 100644 --- a/src/main/repository.ts +++ b/src/main/repository.ts @@ -1,20 +1,9 @@ import { dataSource } from "./data-source"; -import { - DownloadQueue, - Game, - GameShopCache, - UserPreferences, - GameAchievement, -} from "@main/entity"; +import { DownloadQueue, Game, UserPreferences } from "@main/entity"; export const gameRepository = dataSource.getRepository(Game); export const userPreferencesRepository = dataSource.getRepository(UserPreferences); -export const gameShopCacheRepository = dataSource.getRepository(GameShopCache); - export const downloadQueueRepository = dataSource.getRepository(DownloadQueue); - -export const gameAchievementRepository = - dataSource.getRepository(GameAchievement); diff --git a/src/main/services/achievements/get-game-achievement-data.ts b/src/main/services/achievements/get-game-achievement-data.ts index daac7e11..2dc643c1 100644 --- a/src/main/services/achievements/get-game-achievement-data.ts +++ b/src/main/services/achievements/get-game-achievement-data.ts @@ -1,40 +1,36 @@ -import { - gameAchievementRepository, - userPreferencesRepository, -} from "@main/repository"; +import { userPreferencesRepository } from "@main/repository"; import { HydraApi } from "../hydra-api"; -import type { AchievementData, GameShop } from "@types"; +import type { GameShop, SteamAchievement } from "@types"; import { UserNotLoggedInError } from "@shared"; import { logger } from "../logger"; -import { GameAchievement } from "@main/entity"; +import { gameAchievementsSublevel, levelKeys } from "@main/level"; export const getGameAchievementData = async ( objectId: string, shop: GameShop, - cachedAchievements: GameAchievement | null + useCachedData: boolean ) => { - if (cachedAchievements && cachedAchievements.achievements) { - return JSON.parse(cachedAchievements.achievements) as AchievementData[]; - } + const cachedAchievements = await gameAchievementsSublevel.get( + levelKeys.game(shop, objectId) + ); + + if (cachedAchievements && useCachedData) + return cachedAchievements.achievements; const userPreferences = await userPreferencesRepository.findOne({ where: { id: 1 }, }); - return HydraApi.get("/games/achievements", { + return HydraApi.get("/games/achievements", { shop, objectId, language: userPreferences?.language || "en", }) - .then((achievements) => { - gameAchievementRepository.upsert( - { - objectId, - shop, - achievements: JSON.stringify(achievements), - }, - ["objectId", "shop"] - ); + .then(async (achievements) => { + await gameAchievementsSublevel.put(levelKeys.game(shop, objectId), { + unlockedAchievements: cachedAchievements?.unlockedAchievements ?? [], + achievements, + }); return achievements; }) @@ -42,15 +38,9 @@ export const getGameAchievementData = async ( if (err instanceof UserNotLoggedInError) { throw err; } + logger.error("Failed to get game achievements", err); - return gameAchievementRepository - .findOne({ - where: { objectId, shop }, - }) - .then((gameAchievements) => { - return JSON.parse( - gameAchievements?.achievements || "[]" - ) as AchievementData[]; - }); + + return []; }); }; diff --git a/src/main/services/achievements/merge-achievements.ts b/src/main/services/achievements/merge-achievements.ts index dd8c877d..ac2f69d1 100644 --- a/src/main/services/achievements/merge-achievements.ts +++ b/src/main/services/achievements/merge-achievements.ts @@ -1,8 +1,5 @@ -import { - gameAchievementRepository, - userPreferencesRepository, -} from "@main/repository"; -import type { AchievementData, GameShop, UnlockedAchievement } from "@types"; +import { userPreferencesRepository } from "@main/repository"; +import type { GameShop, UnlockedAchievement } from "@types"; import { WindowManager } from "../window-manager"; import { HydraApi } from "../hydra-api"; import { getUnlockedAchievements } from "@main/events/user/get-unlocked-achievements"; @@ -10,33 +7,36 @@ import { Game } from "@main/entity"; import { publishNewAchievementNotification } from "../notifications"; import { SubscriptionRequiredError } from "@shared"; import { achievementsLogger } from "../logger"; +import { gameAchievementsSublevel, levelKeys } from "@main/level"; const saveAchievementsOnLocal = async ( objectId: string, shop: GameShop, - achievements: UnlockedAchievement[], + unlockedAchievements: UnlockedAchievement[], sendUpdateEvent: boolean ) => { - return gameAchievementRepository - .upsert( - { - objectId, - shop, - unlockedAchievements: JSON.stringify(achievements), - }, - ["objectId", "shop"] - ) - .then(() => { - if (!sendUpdateEvent) return; + const levelKey = levelKeys.game(shop, objectId); - return getUnlockedAchievements(objectId, shop, true) - .then((achievements) => { - WindowManager.mainWindow?.webContents.send( - `on-update-achievements-${objectId}-${shop}`, - achievements - ); - }) - .catch(() => {}); + return gameAchievementsSublevel + .get(levelKey) + .then(async (gameAchievement) => { + if (gameAchievement) { + await gameAchievementsSublevel.put(levelKey, { + ...gameAchievement, + unlockedAchievements: unlockedAchievements, + }); + + if (!sendUpdateEvent) return; + + return getUnlockedAchievements(objectId, shop, true) + .then((achievements) => { + WindowManager.mainWindow?.webContents.send( + `on-update-achievements-${objectId}-${shop}`, + achievements + ); + }) + .catch(() => {}); + } }); }; @@ -46,22 +46,12 @@ export const mergeAchievements = async ( publishNotification: boolean ) => { const [localGameAchievement, userPreferences] = await Promise.all([ - gameAchievementRepository.findOne({ - where: { - objectId: game.objectID, - shop: game.shop, - }, - }), + gameAchievementsSublevel.get(levelKeys.game(game.shop, game.objectID)), userPreferencesRepository.findOne({ where: { id: 1 } }), ]); - const achievementsData = JSON.parse( - localGameAchievement?.achievements || "[]" - ) as AchievementData[]; - - const unlockedAchievements = JSON.parse( - localGameAchievement?.unlockedAchievements || "[]" - ).filter((achievement) => achievement.name) as UnlockedAchievement[]; + const achievementsData = localGameAchievement?.achievements ?? []; + const unlockedAchievements = localGameAchievement?.unlockedAchievements ?? []; const newAchievementsMap = new Map( achievements.reverse().map((achievement) => { diff --git a/src/main/services/hydra-api.ts b/src/main/services/hydra-api.ts index 6cf9a8af..5f7a5034 100644 --- a/src/main/services/hydra-api.ts +++ b/src/main/services/hydra-api.ts @@ -10,7 +10,7 @@ import { appVersion } from "@main/constants"; import { getUserData } from "./user/get-user-data"; import { isFuture, isToday } from "date-fns"; import { db } from "@main/level"; -import { levelKeys } from "@main/level/sublevels/keys"; +import { levelKeys } from "@main/level/sublevels"; import type { Auth, User } from "@types"; import { Crypto } from "./crypto"; diff --git a/src/main/services/user/get-user-data.ts b/src/main/services/user/get-user-data.ts index e6cf1c71..ed07c61e 100644 --- a/src/main/services/user/get-user-data.ts +++ b/src/main/services/user/get-user-data.ts @@ -3,7 +3,7 @@ import { HydraApi } from "../hydra-api"; import { UserNotLoggedInError } from "@shared"; import { logger } from "../logger"; import { db } from "@main/level"; -import { levelKeys } from "@main/level/sublevels/keys"; +import { levelKeys } from "@main/level/sublevels"; export const getUserData = async () => { return HydraApi.get(`/profile/me`) diff --git a/src/renderer/src/components/sidebar/sidebar.tsx b/src/renderer/src/components/sidebar/sidebar.tsx index 355d04b2..ae22f552 100644 --- a/src/renderer/src/components/sidebar/sidebar.tsx +++ b/src/renderer/src/components/sidebar/sidebar.tsx @@ -2,7 +2,7 @@ import { useEffect, useMemo, useRef, useState } from "react"; import { useTranslation } from "react-i18next"; import { useLocation, useNavigate } from "react-router-dom"; -import type { LibraryGame } from "@types"; +import type { Game } from "@types"; import { TextField } from "@renderer/components"; import { @@ -35,7 +35,7 @@ export function Sidebar() { const { library, updateLibrary } = useLibrary(); const navigate = useNavigate(); - const [filteredLibrary, setFilteredLibrary] = useState([]); + const [filteredLibrary, setFilteredLibrary] = useState([]); const [isResizing, setIsResizing] = useState(false); const [sidebarWidth, setSidebarWidth] = useState( @@ -117,7 +117,7 @@ export function Sidebar() { }; }, [isResizing]); - const getGameTitle = (game: LibraryGame) => { + const getGameTitle = (game: Game) => { if (lastPacket?.game.id === game.id) { return t("downloading", { title: game.title, @@ -140,10 +140,7 @@ export function Sidebar() { } }; - const handleSidebarGameClick = ( - event: React.MouseEvent, - game: LibraryGame - ) => { + const handleSidebarGameClick = (event: React.MouseEvent, game: Game) => { const path = buildGameDetailsPath({ ...game, objectId: game.objectID, diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 88f3297f..2ee60347 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -2,7 +2,6 @@ import type { CatalogueCategory } from "@shared"; import type { AppUpdaterEvent, Game, - LibraryGame, GameShop, HowLongToBeatCategory, ShopDetails, @@ -23,7 +22,6 @@ import type { UserStats, UserDetails, FriendRequestSync, - GameAchievement, GameArtifact, LudusaviBackup, UserAchievement, @@ -77,7 +75,7 @@ declare global { onUpdateAchievements: ( objectId: string, shop: GameShop, - cb: (achievements: GameAchievement[]) => void + cb: (achievements: UserAchievement[]) => void ) => () => Electron.IpcRenderer; getPublishers: () => Promise; getDevelopers: () => Promise; @@ -102,7 +100,7 @@ declare global { winePrefixPath: string | null ) => Promise; verifyExecutablePathInUse: (executablePath: string) => Promise; - getLibrary: () => Promise; + getLibrary: () => Promise; openGameInstaller: (gameId: number) => Promise; openGameInstallerPath: (gameId: number) => Promise; openGameExecutablePath: (gameId: number) => Promise; diff --git a/src/renderer/src/features/library-slice.ts b/src/renderer/src/features/library-slice.ts index 6c95aa79..f536ace7 100644 --- a/src/renderer/src/features/library-slice.ts +++ b/src/renderer/src/features/library-slice.ts @@ -1,10 +1,10 @@ import { createSlice } from "@reduxjs/toolkit"; import type { PayloadAction } from "@reduxjs/toolkit"; -import type { LibraryGame } from "@types"; +import type { Game } from "@types"; export interface LibraryState { - value: LibraryGame[]; + value: Game[]; } const initialState: LibraryState = { diff --git a/src/renderer/src/pages/achievements/achievement-list.tsx b/src/renderer/src/pages/achievements/achievement-list.tsx index ef178b50..6066f241 100644 --- a/src/renderer/src/pages/achievements/achievement-list.tsx +++ b/src/renderer/src/pages/achievements/achievement-list.tsx @@ -47,7 +47,14 @@ export function AchievementList({ achievements }: AchievementListProps) {

{achievement.description}

-
+
{achievement.points != undefined ? (
void; openGameInstaller: (gameId: number) => void; @@ -65,7 +65,7 @@ export function DownloadGroup({ resumeSeeding, } = useDownload(); - const getFinalDownloadSize = (game: LibraryGame) => { + const getFinalDownloadSize = (game: Game) => { const isGameDownloading = lastPacket?.game.id === game.id; if (game.fileSize) return formatBytes(game.fileSize); @@ -86,7 +86,7 @@ export function DownloadGroup({ return map; }, [seedingStatus]); - const getGameInfo = (game: LibraryGame) => { + const getGameInfo = (game: Game) => { const isGameDownloading = lastPacket?.game.id === game.id; const finalDownloadSize = getFinalDownloadSize(game); const seedingStatus = seedingMap.get(game.id); @@ -165,7 +165,7 @@ export function DownloadGroup({ return

{t(game.status as string)}

; }; - const getGameActions = (game: LibraryGame): DropdownMenuItem[] => { + const getGameActions = (game: Game): DropdownMenuItem[] => { const isGameDownloading = lastPacket?.game.id === game.id; const deleting = isGameDeleting(game.id); diff --git a/src/renderer/src/pages/downloads/downloads.tsx b/src/renderer/src/pages/downloads/downloads.tsx index 5c5a121a..41dbae90 100644 --- a/src/renderer/src/pages/downloads/downloads.tsx +++ b/src/renderer/src/pages/downloads/downloads.tsx @@ -7,7 +7,7 @@ import { BinaryNotFoundModal } from "../shared-modals/binary-not-found-modal"; import * as styles from "./downloads.css"; import { DeleteGameModal } from "./delete-game-modal"; import { DownloadGroup } from "./download-group"; -import type { LibraryGame, SeedingStatus } from "@types"; +import type { Game, SeedingStatus } from "@types"; import { orderBy } from "lodash-es"; import { ArrowDownIcon } from "@primer/octicons-react"; @@ -49,8 +49,8 @@ export default function Downloads() { setShowDeleteModal(true); }; - const libraryGroup: Record = useMemo(() => { - const initialValue: Record = { + const libraryGroup: Record = useMemo(() => { + const initialValue: Record = { downloading: [], queued: [], complete: [], diff --git a/src/renderer/src/pages/game-details/sidebar/sidebar.tsx b/src/renderer/src/pages/game-details/sidebar/sidebar.tsx index 7787b22a..d3a65ae5 100644 --- a/src/renderer/src/pages/game-details/sidebar/sidebar.tsx +++ b/src/renderer/src/pages/game-details/sidebar/sidebar.tsx @@ -23,7 +23,7 @@ import { buildGameAchievementPath } from "@renderer/helpers"; import { SPACING_UNIT } from "@renderer/theme.css"; import { useSubscription } from "@renderer/hooks/use-subscription"; -const fakeAchievements: UserAchievement[] = [ +const achievementsPlaceholder: UserAchievement[] = [ { displayName: "Timber!!", name: "", @@ -140,7 +140,7 @@ export function Sidebar() {

{t("sign_in_to_see_achievements")}

    - {fakeAchievements.map((achievement, index) => ( + {achievementsPlaceholder.map((achievement, index) => (
  • ; - export interface GameRunning { id?: number; title: string; @@ -139,24 +53,6 @@ export interface GameRunning { sessionDurationInMillis: number; } -export interface DownloadProgress { - downloadSpeed: number; - timeRemaining: number; - numPeers: number; - numSeeds: number; - isDownloadingMetadata: boolean; - isCheckingFiles: boolean; - progress: number; - gameId: number; - game: LibraryGame; -} - -export interface SeedingStatus { - gameId: number; - status: GameStatus; - uploadSpeed: number; -} - export interface UserPreferences { downloadsPath: string | null; language: string; @@ -344,11 +240,6 @@ export interface UserStats { unlockedAchievementSum?: number; } -export interface UnlockedAchievement { - name: string; - unlockTime: number; -} - export interface AchievementFile { type: Cracker; filePath: string; @@ -407,9 +298,9 @@ export interface CatalogueSearchPayload { developers: string[]; } +export * from "./game.types"; export * from "./steam.types"; -export * from "./real-debrid.types"; +export * from "./download.types"; export * from "./ludusavi.types"; export * from "./how-long-to-beat.types"; -export * from "./torbox.types"; export * from "./level.types"; diff --git a/src/types/level.types.ts b/src/types/level.types.ts index 490ab060..3a9c0c14 100644 --- a/src/types/level.types.ts +++ b/src/types/level.types.ts @@ -1,3 +1,5 @@ +import type { SteamAchievement, UnlockedAchievement } from "./game.types"; + export type SubscriptionStatus = "active" | "pending" | "cancelled"; export interface Subscription { @@ -21,3 +23,8 @@ export interface User { backgroundImageUrl: string | null; subscription: Subscription | null; } + +export interface GameAchievement { + achievements: SteamAchievement[]; + unlockedAchievements: UnlockedAchievement[]; +} diff --git a/src/types/real-debrid.types.ts b/src/types/real-debrid.types.ts deleted file mode 100644 index 6b16ecfd..00000000 --- a/src/types/real-debrid.types.ts +++ /dev/null @@ -1,66 +0,0 @@ -export interface RealDebridUnrestrictLink { - id: string; - filename: string; - mimeType: string; - filesize: number; - link: string; - host: string; - host_icon: string; - chunks: number; - crc: number; - download: string; - streamable: number; -} - -export interface RealDebridAddMagnet { - id: string; - // URL of the created resource - uri: string; -} - -export interface RealDebridTorrentInfo { - id: string; - filename: string; - original_filename: string; - hash: string; - bytes: number; - original_bytes: number; - host: string; - split: number; - progress: number; - status: - | "magnet_error" - | "magnet_conversion" - | "waiting_files_selection" - | "queued" - | "downloading" - | "downloaded" - | "error" - | "virus" - | "compressing" - | "uploading" - | "dead"; - added: string; - files: { - id: number; - path: string; - bytes: number; - selected: number; - }[]; - links: string[]; - ended: string; - speed: number; - seeders: number; -} - -export interface RealDebridUser { - id: number; - username: string; - email: string; - points: number; - locale: string; - avatar: string; - type: string; - premium: number; - expiration: string; -} diff --git a/src/types/torbox.types.ts b/src/types/torbox.types.ts deleted file mode 100644 index a53ccc4c..00000000 --- a/src/types/torbox.types.ts +++ /dev/null @@ -1,77 +0,0 @@ -export interface TorBoxUser { - id: number; - email: string; - plan: string; - expiration: string; -} - -export interface TorBoxUserRequest { - success: boolean; - detail: string; - error: string; - data: TorBoxUser; -} - -export interface TorBoxFile { - id: number; - md5: string; - s3_path: string; - name: string; - size: number; - mimetype: string; - short_name: string; -} - -export interface TorBoxTorrentInfo { - id: number; - hash: string; - created_at: string; - updated_at: string; - magnet: string; - size: number; - active: boolean; - cached: boolean; - auth_id: string; - download_state: - | "downloading" - | "uploading" - | "stalled (no seeds)" - | "paused" - | "completed" - | "cached" - | "metaDL" - | "checkingResumeData"; - seeds: number; - ratio: number; - progress: number; - download_speed: number; - upload_speed: number; - name: string; - eta: number; - files: TorBoxFile[]; -} - -export interface TorBoxTorrentInfoRequest { - success: boolean; - detail: string; - error: string; - data: TorBoxTorrentInfo[]; -} - -export interface TorBoxAddTorrentRequest { - success: boolean; - detail: string; - error: string; - data: { - torrent_id: number; - name: string; - hash: string; - }; -} - -export interface TorBoxRequestLinkRequest { - success: boolean; - detail: string; - error: string; - data: string; -}