From 109c12064d0db4f60423ddc303818b54611fbb90 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Fri, 20 Dec 2024 17:07:51 +0000 Subject: [PATCH 01/18] feat: adding source search --- .eslintrc.cjs | 5 + src/main/data-source.ts | 4 - src/main/entity/download-source.entity.ts | 41 - src/main/entity/game.entity.ts | 9 - src/main/entity/index.ts | 2 - src/main/entity/repack.entity.ts | 45 - src/main/events/catalogue/get-catalogue.ts | 22 +- src/main/events/catalogue/get-games.ts | 29 - src/main/events/catalogue/search-games.ts | 27 +- .../delete-download-source.ts | 9 - .../download-sources/get-download-sources.ts | 7 - .../download-sources/put-download-source.ts | 13 + src/main/events/helpers/search-games.ts | 31 - src/main/events/index.ts | 4 +- src/main/repository.ts | 7 - src/preload/index.ts | 13 +- src/renderer/src/app.tsx | 94 +- .../checkbox-field/checkbox-field.css.ts | 38 +- .../checkbox-field/checkbox-field.tsx | 2 +- .../src/components/game-card/game-card.tsx | 26 +- .../src/components/header/header.css.ts | 27 +- src/renderer/src/components/header/header.tsx | 75 +- .../game-details/game-details.context.tsx | 17 +- src/renderer/src/context/index.ts | 1 - .../src/context/repacks/repacks.context.tsx | 67 - src/renderer/src/declaration.d.ts | 15 +- src/renderer/src/dexie.ts | 9 +- src/renderer/src/features/catalogue-search.ts | 37 + src/renderer/src/features/index.ts | 3 +- src/renderer/src/features/repacks-slice.ts | 24 + src/renderer/src/features/search-slice.ts | 25 - src/renderer/src/hooks/index.ts | 1 + src/renderer/src/hooks/use-format.ts | 2 +- src/renderer/src/hooks/use-repacks.ts | 26 + src/renderer/src/main.tsx | 70 +- .../src/pages/catalogue/catalogue.scss | 114 ++ .../src/pages/catalogue/catalogue.tsx | 434 +++++-- .../src/pages/catalogue/filter-section.tsx | 85 ++ src/renderer/src/pages/home/home.tsx | 40 +- .../src/pages/home/search-results.tsx | 131 -- .../settings/add-download-source-modal.tsx | 19 +- .../settings/settings-download-sources.tsx | 11 +- src/renderer/src/scss/globals.scss | 2 +- src/renderer/src/store.ts | 6 +- .../src/workers/download-sources.worker.ts | 111 +- src/renderer/src/workers/index.ts | 2 - src/renderer/src/workers/repacks.worker.ts | 51 - src/types/index.ts | 24 +- yarn.lock | 1111 ++++++++++++++++- 49 files changed, 2010 insertions(+), 958 deletions(-) delete mode 100644 src/main/entity/download-source.entity.ts delete mode 100644 src/main/entity/repack.entity.ts delete mode 100644 src/main/events/catalogue/get-games.ts delete mode 100644 src/main/events/download-sources/delete-download-source.ts delete mode 100644 src/main/events/download-sources/get-download-sources.ts create mode 100644 src/main/events/download-sources/put-download-source.ts delete mode 100644 src/main/events/helpers/search-games.ts delete mode 100644 src/renderer/src/context/repacks/repacks.context.tsx create mode 100644 src/renderer/src/features/catalogue-search.ts create mode 100644 src/renderer/src/features/repacks-slice.ts delete mode 100644 src/renderer/src/features/search-slice.ts create mode 100644 src/renderer/src/hooks/use-repacks.ts create mode 100644 src/renderer/src/pages/catalogue/catalogue.scss create mode 100644 src/renderer/src/pages/catalogue/filter-section.tsx delete mode 100644 src/renderer/src/pages/home/search-results.tsx delete mode 100644 src/renderer/src/workers/repacks.worker.ts diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 6ec93984..6da066af 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -26,4 +26,9 @@ module.exports = { }, ], }, + settings: { + react: { + version: "detect", + }, + }, }; diff --git a/src/main/data-source.ts b/src/main/data-source.ts index 80a40f47..51c8522e 100644 --- a/src/main/data-source.ts +++ b/src/main/data-source.ts @@ -1,10 +1,8 @@ import { DataSource } from "typeorm"; import { DownloadQueue, - DownloadSource, Game, GameShopCache, - Repack, UserPreferences, UserAuth, GameAchievement, @@ -17,12 +15,10 @@ export const dataSource = new DataSource({ type: "better-sqlite3", entities: [ Game, - Repack, UserAuth, UserPreferences, UserSubscription, GameShopCache, - DownloadSource, DownloadQueue, GameAchievement, ], diff --git a/src/main/entity/download-source.entity.ts b/src/main/entity/download-source.entity.ts deleted file mode 100644 index dc59bac4..00000000 --- a/src/main/entity/download-source.entity.ts +++ /dev/null @@ -1,41 +0,0 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - UpdateDateColumn, - OneToMany, -} from "typeorm"; -import type { Repack } from "./repack.entity"; - -import { DownloadSourceStatus } from "@shared"; - -@Entity("download_source") -export class DownloadSource { - @PrimaryGeneratedColumn() - id: number; - - @Column("text", { nullable: true, unique: true }) - url: string; - - @Column("text") - name: string; - - @Column("text", { nullable: true }) - etag: string | null; - - @Column("int", { default: 0 }) - downloadCount: number; - - @Column("text", { default: DownloadSourceStatus.UpToDate }) - status: DownloadSourceStatus; - - @OneToMany("Repack", "downloadSource", { cascade: true }) - repacks: Repack[]; - - @CreateDateColumn() - createdAt: Date; - - @UpdateDateColumn() - updatedAt: Date; -} diff --git a/src/main/entity/game.entity.ts b/src/main/entity/game.entity.ts index 19905c32..c24a9f33 100644 --- a/src/main/entity/game.entity.ts +++ b/src/main/entity/game.entity.ts @@ -5,9 +5,7 @@ import { CreateDateColumn, UpdateDateColumn, OneToOne, - JoinColumn, } from "typeorm"; -import { Repack } from "./repack.entity"; import type { GameShop, GameStatus } from "@types"; import { Downloader } from "@shared"; @@ -72,13 +70,6 @@ export class Game { @Column("text", { nullable: true }) uri: string | null; - /** - * @deprecated - */ - @OneToOne("Repack", "game", { nullable: true }) - @JoinColumn() - repack: Repack; - @OneToOne("DownloadQueue", "game") downloadQueue: DownloadQueue; diff --git a/src/main/entity/index.ts b/src/main/entity/index.ts index 5829e6a2..1625ac8a 100644 --- a/src/main/entity/index.ts +++ b/src/main/entity/index.ts @@ -1,10 +1,8 @@ export * from "./game.entity"; -export * from "./repack.entity"; export * from "./user-auth.entity"; export * from "./user-preferences.entity"; export * from "./user-subscription.entity"; export * from "./game-shop-cache.entity"; export * from "./game.entity"; export * from "./game-achievements.entity"; -export * from "./download-source.entity"; export * from "./download-queue.entity"; diff --git a/src/main/entity/repack.entity.ts b/src/main/entity/repack.entity.ts deleted file mode 100644 index 36de2a7c..00000000 --- a/src/main/entity/repack.entity.ts +++ /dev/null @@ -1,45 +0,0 @@ -import { - Entity, - PrimaryGeneratedColumn, - Column, - CreateDateColumn, - UpdateDateColumn, - ManyToOne, -} from "typeorm"; -import { DownloadSource } from "./download-source.entity"; - -@Entity("repack") -export class Repack { - @PrimaryGeneratedColumn() - id: number; - - @Column("text", { unique: true }) - title: string; - - /** - * @deprecated Use uris instead - */ - @Column("text", { unique: true }) - magnet: string; - - @Column("text") - repacker: string; - - @Column("text") - fileSize: string; - - @Column("datetime") - uploadDate: Date | string; - - @ManyToOne(() => DownloadSource, { nullable: true, onDelete: "CASCADE" }) - downloadSource: DownloadSource; - - @Column("text", { default: "[]" }) - uris: string; - - @CreateDateColumn() - createdAt: Date; - - @UpdateDateColumn() - updatedAt: Date; -} diff --git a/src/main/events/catalogue/get-catalogue.ts b/src/main/events/catalogue/get-catalogue.ts index a0542f25..2c2914aa 100644 --- a/src/main/events/catalogue/get-catalogue.ts +++ b/src/main/events/catalogue/get-catalogue.ts @@ -1,9 +1,6 @@ -import type { GameShop } from "@types"; - import { registerEvent } from "../register-event"; import { HydraApi } from "@main/services"; -import { CatalogueCategory, steamUrlBuilder } from "@shared"; -import { steamGamesWorker } from "@main/workers"; +import { CatalogueCategory } from "@shared"; const getCatalogue = async ( _event: Electron.IpcMainInvokeEvent, @@ -14,26 +11,11 @@ const getCatalogue = async ( skip: "0", }); - const response = await HydraApi.get<{ objectId: string; shop: GameShop }[]>( + return HydraApi.get( `/catalogue/${category}?${params.toString()}`, {}, { needsAuth: false } ); - - return Promise.all( - response.map(async (game) => { - const steamGame = await steamGamesWorker.run(Number(game.objectId), { - name: "getById", - }); - - return { - title: steamGame.name, - shop: game.shop, - cover: steamUrlBuilder.library(game.objectId), - objectId: game.objectId, - }; - }) - ); }; registerEvent("getCatalogue", getCatalogue); diff --git a/src/main/events/catalogue/get-games.ts b/src/main/events/catalogue/get-games.ts deleted file mode 100644 index 3eb1f135..00000000 --- a/src/main/events/catalogue/get-games.ts +++ /dev/null @@ -1,29 +0,0 @@ -import type { CatalogueEntry } from "@types"; - -import { registerEvent } from "../register-event"; -import { HydraApi } from "@main/services"; -import { steamUrlBuilder } from "@shared"; - -const getGames = async ( - _event: Electron.IpcMainInvokeEvent, - take = 12, - skip = 0 -): Promise => { - const searchParams = new URLSearchParams({ - take: take.toString(), - skip: skip.toString(), - }); - - const games = await HydraApi.get( - `/games/catalogue?${searchParams.toString()}`, - undefined, - { needsAuth: false } - ); - - return games.map((game) => ({ - ...game, - cover: steamUrlBuilder.library(game.objectId), - })); -}; - -registerEvent("getGames", getGames); diff --git a/src/main/events/catalogue/search-games.ts b/src/main/events/catalogue/search-games.ts index 4ce42fd8..dd010ae6 100644 --- a/src/main/events/catalogue/search-games.ts +++ b/src/main/events/catalogue/search-games.ts @@ -1,23 +1,16 @@ +import type { CatalogueSearchPayload } from "@types"; import { registerEvent } from "../register-event"; -import { convertSteamGameToCatalogueEntry } from "../helpers/search-games"; -import type { CatalogueEntry } from "@types"; import { HydraApi } from "@main/services"; -const searchGamesEvent = async ( +const searchGames = async ( _event: Electron.IpcMainInvokeEvent, - query: string -): Promise => { - const games = await HydraApi.get< - { objectId: string; title: string; shop: string }[] - >("/games/search", { title: query, take: 12, skip: 0 }, { needsAuth: false }); - - return games.map((game) => { - return convertSteamGameToCatalogueEntry({ - id: Number(game.objectId), - name: game.title, - clientIcon: null, - }); - }); + payload: CatalogueSearchPayload +) => { + return HydraApi.post( + "/catalogue/search", + { ...payload, take: 12, skip: 0 }, + { needsAuth: false } + ); }; -registerEvent("searchGames", searchGamesEvent); +registerEvent("searchGames", searchGames); diff --git a/src/main/events/download-sources/delete-download-source.ts b/src/main/events/download-sources/delete-download-source.ts deleted file mode 100644 index abfbf661..00000000 --- a/src/main/events/download-sources/delete-download-source.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { registerEvent } from "../register-event"; -import { knexClient } from "@main/knex-client"; - -const deleteDownloadSource = async ( - _event: Electron.IpcMainInvokeEvent, - id: number -) => knexClient("download_source").where({ id }).delete(); - -registerEvent("deleteDownloadSource", deleteDownloadSource); diff --git a/src/main/events/download-sources/get-download-sources.ts b/src/main/events/download-sources/get-download-sources.ts deleted file mode 100644 index 97f8a6d8..00000000 --- a/src/main/events/download-sources/get-download-sources.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { registerEvent } from "../register-event"; -import { knexClient } from "@main/knex-client"; - -const getDownloadSources = async (_event: Electron.IpcMainInvokeEvent) => - knexClient.select("*").from("download_source"); - -registerEvent("getDownloadSources", getDownloadSources); diff --git a/src/main/events/download-sources/put-download-source.ts b/src/main/events/download-sources/put-download-source.ts new file mode 100644 index 00000000..3aefd927 --- /dev/null +++ b/src/main/events/download-sources/put-download-source.ts @@ -0,0 +1,13 @@ +import { HydraApi } from "@main/services"; +import { registerEvent } from "../register-event"; + +const putDownloadSource = async ( + _event: Electron.IpcMainInvokeEvent, + objectIds: string[] +) => { + return HydraApi.put<{ fingerprint: string }>("/download-sources", { + objectIds, + }); +}; + +registerEvent("putDownloadSource", putDownloadSource); diff --git a/src/main/events/helpers/search-games.ts b/src/main/events/helpers/search-games.ts deleted file mode 100644 index 74e0b6a8..00000000 --- a/src/main/events/helpers/search-games.ts +++ /dev/null @@ -1,31 +0,0 @@ -import type { GameShop, CatalogueEntry, SteamGame } from "@types"; - -import { steamGamesWorker } from "@main/workers"; -import { steamUrlBuilder } from "@shared"; - -export interface SearchGamesArgs { - query?: string; - take?: number; - skip?: number; -} - -export const convertSteamGameToCatalogueEntry = ( - game: SteamGame -): CatalogueEntry => ({ - objectId: String(game.id), - title: game.name, - shop: "steam" as GameShop, - cover: steamUrlBuilder.library(String(game.id)), -}); - -export const getSteamGameById = async ( - objectId: string -): Promise => { - const steamGame = await steamGamesWorker.run(Number(objectId), { - name: "getById", - }); - - if (!steamGame) return null; - - return convertSteamGameToCatalogueEntry(steamGame); -}; diff --git a/src/main/events/index.ts b/src/main/events/index.ts index eff62531..35863eac 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -3,7 +3,6 @@ import { ipcMain } from "electron"; import "./catalogue/get-catalogue"; import "./catalogue/get-game-shop-details"; -import "./catalogue/get-games"; import "./catalogue/get-how-long-to-beat"; import "./catalogue/get-random-game"; import "./catalogue/search-games"; @@ -38,8 +37,7 @@ import "./user-preferences/auto-launch"; import "./autoupdater/check-for-updates"; import "./autoupdater/restart-and-install-update"; import "./user-preferences/authenticate-real-debrid"; -import "./download-sources/delete-download-source"; -import "./download-sources/get-download-sources"; +import "./download-sources/put-download-source"; import "./auth/sign-out"; import "./auth/open-auth-window"; import "./auth/get-session-hash"; diff --git a/src/main/repository.ts b/src/main/repository.ts index cf3ab143..e0c4204e 100644 --- a/src/main/repository.ts +++ b/src/main/repository.ts @@ -1,10 +1,8 @@ import { dataSource } from "./data-source"; import { DownloadQueue, - DownloadSource, Game, GameShopCache, - Repack, UserPreferences, UserAuth, GameAchievement, @@ -13,16 +11,11 @@ import { export const gameRepository = dataSource.getRepository(Game); -export const repackRepository = dataSource.getRepository(Repack); - export const userPreferencesRepository = dataSource.getRepository(UserPreferences); export const gameShopCacheRepository = dataSource.getRepository(GameShopCache); -export const downloadSourceRepository = - dataSource.getRepository(DownloadSource); - export const downloadQueueRepository = dataSource.getRepository(DownloadQueue); export const userAuthRepository = dataSource.getRepository(UserAuth); diff --git a/src/preload/index.ts b/src/preload/index.ts index f9d19644..d143b73d 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -11,6 +11,7 @@ import type { GameRunning, FriendRequestAction, UpdateProfileRequest, + CatalogueSearchPayload, } from "@types"; import type { CatalogueCategory } from "@shared"; import type { AxiosProgressEvent } from "axios"; @@ -36,7 +37,8 @@ contextBridge.exposeInMainWorld("electron", { }, /* Catalogue */ - searchGames: (query: string) => ipcRenderer.invoke("searchGames", query), + searchGames: (payload: CatalogueSearchPayload) => + ipcRenderer.invoke("searchGames", payload), getCatalogue: (category: CatalogueCategory) => ipcRenderer.invoke("getCatalogue", category), getGameShopDetails: (objectId: string, shop: GameShop, language: string) => @@ -44,10 +46,6 @@ contextBridge.exposeInMainWorld("electron", { getRandomGame: () => ipcRenderer.invoke("getRandomGame"), getHowLongToBeat: (objectId: string, shop: GameShop) => ipcRenderer.invoke("getHowLongToBeat", objectId, shop), - getGames: (take?: number, skip?: number) => - ipcRenderer.invoke("getGames", take, skip), - searchGameRepacks: (query: string) => - ipcRenderer.invoke("searchGameRepacks", query), getGameStats: (objectId: string, shop: GameShop) => ipcRenderer.invoke("getGameStats", objectId, shop), getTrendingGames: () => ipcRenderer.invoke("getTrendingGames"), @@ -78,9 +76,8 @@ contextBridge.exposeInMainWorld("electron", { ipcRenderer.invoke("authenticateRealDebrid", apiToken), /* Download sources */ - getDownloadSources: () => ipcRenderer.invoke("getDownloadSources"), - deleteDownloadSource: (id: number) => - ipcRenderer.invoke("deleteDownloadSource", id), + putDownloadSource: (objectIds: string[]) => + ipcRenderer.invoke("putDownloadSource", objectIds), /* Library */ addGameToLibrary: (objectId: string, title: string, shop: GameShop) => diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx index 1ec7507e..2f571f2f 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -1,4 +1,4 @@ -import { useCallback, useContext, useEffect, useRef } from "react"; +import { useCallback, useEffect, useRef } from "react"; import { Sidebar, BottomPanel, Header, Toast } from "@renderer/components"; @@ -7,6 +7,7 @@ import { useAppSelector, useDownload, useLibrary, + useRepacks, useToast, useUserDetails, } from "@renderer/hooks"; @@ -15,8 +16,6 @@ import * as styles from "./app.css"; import { Outlet, useLocation, useNavigate } from "react-router-dom"; import { - setSearch, - clearSearch, setUserPreferences, toggleDraggingDisabled, closeToast, @@ -27,8 +26,6 @@ import { import { useTranslation } from "react-i18next"; import { UserFriendModal } from "./pages/shared-modals/user-friend-modal"; import { downloadSourcesWorker } from "./workers"; -import { repacksContext } from "./context"; -import { logger } from "./logger"; export interface AppProps { children: React.ReactNode; @@ -42,9 +39,9 @@ export function App() { const downloadSourceMigrationLock = useRef(false); - const { clearDownload, setLastPacket } = useDownload(); + const { updateRepacks } = useRepacks(); - const { indexRepacks } = useContext(repacksContext); + const { clearDownload, setLastPacket } = useDownload(); const { isFriendsModalVisible, @@ -67,8 +64,6 @@ export function App() { const navigate = useNavigate(); const location = useLocation(); - const search = useAppSelector((state) => state.search.value); - const draggingDisabled = useAppSelector( (state) => state.window.draggingDisabled ); @@ -187,31 +182,6 @@ export function App() { }; }, [onSignIn, updateLibrary, clearUserDetails]); - const handleSearch = useCallback( - (query: string) => { - dispatch(setSearch(query)); - - if (query === "") { - navigate(-1); - return; - } - - const searchParams = new URLSearchParams({ - query, - }); - - navigate(`/search?${searchParams.toString()}`, { - replace: location.pathname.startsWith("/search"), - }); - }, - [dispatch, location.pathname, navigate] - ); - - const handleClear = useCallback(() => { - dispatch(clearSearch()); - navigate(-1); - }, [dispatch, navigate]); - useEffect(() => { if (contentRef.current) contentRef.current.scrollTop = 0; }, [location.pathname, location.search]); @@ -232,49 +202,19 @@ export function App() { downloadSourceMigrationLock.current = true; - window.electron.getDownloadSources().then(async (downloadSources) => { - if (!downloadSources.length) { - const id = crypto.randomUUID(); - const channel = new BroadcastChannel(`download_sources:sync:${id}`); + updateRepacks(); - channel.onmessage = (event: MessageEvent) => { - const newRepacksCount = event.data; - window.electron.publishNewRepacksNotification(newRepacksCount); - }; + const id = crypto.randomUUID(); + const channel = new BroadcastChannel(`download_sources:sync:${id}`); - downloadSourcesWorker.postMessage(["SYNC_DOWNLOAD_SOURCES", id]); - } + channel.onmessage = (event: MessageEvent) => { + const newRepacksCount = event.data; + window.electron.publishNewRepacksNotification(newRepacksCount); + updateRepacks(); + }; - for (const downloadSource of downloadSources) { - logger.info("Migrating download source", downloadSource.url); - - const channel = new BroadcastChannel( - `download_sources:import:${downloadSource.url}` - ); - await new Promise((resolve) => { - downloadSourcesWorker.postMessage([ - "IMPORT_DOWNLOAD_SOURCE", - downloadSource.url, - ]); - - channel.onmessage = () => { - window.electron.deleteDownloadSource(downloadSource.id).then(() => { - resolve(true); - logger.info( - "Deleted download source from SQLite", - downloadSource.url - ); - }); - - indexRepacks(); - channel.close(); - }; - }).catch(() => channel.close()); - } - - downloadSourceMigrationLock.current = false; - }); - }, [indexRepacks]); + downloadSourcesWorker.postMessage(["SYNC_DOWNLOAD_SOURCES", id]); + }, [updateRepacks]); const handleToastClose = useCallback(() => { dispatch(closeToast()); @@ -313,11 +253,7 @@ export function App() {
-
+
diff --git a/src/renderer/src/components/checkbox-field/checkbox-field.css.ts b/src/renderer/src/components/checkbox-field/checkbox-field.css.ts index 606b226a..6546d942 100644 --- a/src/renderer/src/components/checkbox-field/checkbox-field.css.ts +++ b/src/renderer/src/components/checkbox-field/checkbox-field.css.ts @@ -1,6 +1,7 @@ import { style } from "@vanilla-extract/css"; import { SPACING_UNIT, vars } from "../../theme.css"; +import { recipe } from "@vanilla-extract/recipes"; export const checkboxField = style({ display: "flex", @@ -10,19 +11,30 @@ export const checkboxField = style({ cursor: "pointer", }); -export const checkbox = style({ - width: "20px", - height: "20px", - borderRadius: "4px", - backgroundColor: vars.color.darkBackground, - display: "flex", - justifyContent: "center", - alignItems: "center", - position: "relative", - transition: "all ease 0.2s", - border: `solid 1px ${vars.color.border}`, - ":hover": { - borderColor: "rgba(255, 255, 255, 0.5)", +export const checkbox = recipe({ + base: { + width: "20px", + height: "20px", + borderRadius: "4px", + backgroundColor: vars.color.darkBackground, + display: "flex", + justifyContent: "center", + alignItems: "center", + position: "relative", + transition: "all ease 0.2s", + border: `solid 1px ${vars.color.border}`, + minWidth: "20px", + minHeight: "20px", + ":hover": { + borderColor: "rgba(255, 255, 255, 0.5)", + }, + }, + variants: { + checked: { + true: { + backgroundColor: vars.color.muted, + }, + }, }, }); diff --git a/src/renderer/src/components/checkbox-field/checkbox-field.tsx b/src/renderer/src/components/checkbox-field/checkbox-field.tsx index bb81a910..f40c05c2 100644 --- a/src/renderer/src/components/checkbox-field/checkbox-field.tsx +++ b/src/renderer/src/components/checkbox-field/checkbox-field.tsx @@ -15,7 +15,7 @@ export function CheckboxField({ label, ...props }: CheckboxFieldProps) { return (
-
+
, HTMLButtonElement > { - game: CatalogueEntry; + game: any; } const shopIcon = { @@ -26,20 +26,12 @@ export function GameCard({ game, ...props }: GameCardProps) { const { t } = useTranslation("game_card"); const [stats, setStats] = useState(null); - const [repacks, setRepacks] = useState([]); - const { searchRepacks, isIndexingRepacks } = useContext(repacksContext); - - useEffect(() => { - if (!isIndexingRepacks) { - searchRepacks(game.title).then((repacks) => { - setRepacks(repacks); - }); - } - }, [game, isIndexingRepacks, searchRepacks]); + const { getRepacksForObjectId } = useRepacks(); + const repacks = getRepacksForObjectId(game.objectId); const uniqueRepackers = Array.from( - new Set(repacks.map(({ repacker }) => repacker)) + new Set(repacks.map((repack) => repack.repacker)) ); const handleHover = useCallback(() => { @@ -61,7 +53,7 @@ export function GameCard({ game, ...props }: GameCardProps) { >
{game.title} void; - onClear: () => void; - search?: string; -} +import { Button } from "../button/button"; const pathTitle: Record = { "/": "home", @@ -22,18 +16,13 @@ const pathTitle: Record = { "/settings": "settings", }; -export function Header({ onSearch, onClear, search }: HeaderProps) { - const inputRef = useRef(null); - +export function Header() { const navigate = useNavigate(); const location = useLocation(); const { headerTitle, draggingDisabled } = useAppSelector( (state) => state.window ); - const dispatch = useAppDispatch(); - - const [isFocused, setIsFocused] = useState(false); const { t } = useTranslation("header"); @@ -46,21 +35,6 @@ export function Header({ onSearch, onClear, search }: HeaderProps) { return t(pathTitle[location.pathname]); }, [location.pathname, headerTitle, t]); - useEffect(() => { - if (search && !location.pathname.startsWith("/search")) { - dispatch(clearSearch()); - } - }, [location.pathname, search, dispatch]); - - const focusInput = () => { - setIsFocused(true); - inputRef.current?.focus(); - }; - - const handleBlur = () => { - setIsFocused(false); - }; - const handleBackButtonClick = () => { navigate(-1); }; @@ -95,37 +69,14 @@ export function Header({ onSearch, onClear, search }: HeaderProps) {
-
- - - onSearch(event.target.value)} - onFocus={() => setIsFocused(true)} - onBlur={handleBlur} - /> - - {search && ( - - )} -
+
diff --git a/src/renderer/src/context/game-details/game-details.context.tsx b/src/renderer/src/context/game-details/game-details.context.tsx index 70da4524..b28b9e6d 100644 --- a/src/renderer/src/context/game-details/game-details.context.tsx +++ b/src/renderer/src/context/game-details/game-details.context.tsx @@ -1,7 +1,6 @@ import { createContext, useCallback, - useContext, useEffect, useMemo, useRef, @@ -14,12 +13,12 @@ import { useAppDispatch, useAppSelector, useDownload, + useRepacks, useUserDetails, } from "@renderer/hooks"; import type { Game, - GameRepack, GameShop, GameStats, ShopDetails, @@ -29,7 +28,6 @@ import type { import { useTranslation } from "react-i18next"; import { GameDetailsContext } from "./game-details.context.types"; import { SteamContentDescriptor } from "@shared"; -import { repacksContext } from "../repacks/repacks.context"; export const gameDetailsContext = createContext({ game: null, @@ -88,17 +86,8 @@ export function GameDetailsContextProvider({ const [showRepacksModal, setShowRepacksModal] = useState(false); const [showGameOptionsModal, setShowGameOptionsModal] = useState(false); - const [repacks, setRepacks] = useState([]); - - const { searchRepacks, isIndexingRepacks } = useContext(repacksContext); - - useEffect(() => { - if (!isIndexingRepacks) { - searchRepacks(gameTitle).then((repacks) => { - setRepacks(repacks); - }); - } - }, [game, gameTitle, isIndexingRepacks, searchRepacks]); + const { getRepacksForObjectId } = useRepacks(); + const repacks = getRepacksForObjectId(objectId); const { i18n } = useTranslation("game_details"); diff --git a/src/renderer/src/context/index.ts b/src/renderer/src/context/index.ts index 948b90b2..8d9c5d1a 100644 --- a/src/renderer/src/context/index.ts +++ b/src/renderer/src/context/index.ts @@ -1,5 +1,4 @@ export * from "./game-details/game-details.context"; export * from "./settings/settings.context"; export * from "./user-profile/user-profile.context"; -export * from "./repacks/repacks.context"; export * from "./cloud-sync/cloud-sync.context"; diff --git a/src/renderer/src/context/repacks/repacks.context.tsx b/src/renderer/src/context/repacks/repacks.context.tsx deleted file mode 100644 index cddbb209..00000000 --- a/src/renderer/src/context/repacks/repacks.context.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import type { GameRepack } from "@types"; -import { createContext, useCallback, useEffect, useState } from "react"; - -import { repacksWorker } from "@renderer/workers"; - -export interface RepacksContext { - searchRepacks: (query: string) => Promise; - indexRepacks: () => void; - isIndexingRepacks: boolean; -} - -export const repacksContext = createContext({ - searchRepacks: async () => [] as GameRepack[], - indexRepacks: () => {}, - isIndexingRepacks: false, -}); - -const { Provider } = repacksContext; -export const { Consumer: RepacksContextConsumer } = repacksContext; - -export interface RepacksContextProps { - children: React.ReactNode; -} - -export function RepacksContextProvider({ children }: RepacksContextProps) { - const [isIndexingRepacks, setIsIndexingRepacks] = useState(true); - - const searchRepacks = useCallback(async (query: string) => { - return new Promise((resolve) => { - const channelId = crypto.randomUUID(); - repacksWorker.postMessage([channelId, query]); - - const channel = new BroadcastChannel(`repacks:search:${channelId}`); - channel.onmessage = (event: MessageEvent) => { - resolve(event.data); - channel.close(); - }; - - return []; - }); - }, []); - - const indexRepacks = useCallback(() => { - setIsIndexingRepacks(true); - repacksWorker.postMessage("INDEX_REPACKS"); - - repacksWorker.onmessage = () => { - setIsIndexingRepacks(false); - }; - }, []); - - useEffect(() => { - indexRepacks(); - }, [indexRepacks]); - - return ( - - {children} - - ); -} diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 93c423e0..c809b4b3 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -1,10 +1,8 @@ import type { CatalogueCategory } from "@shared"; import type { AppUpdaterEvent, - CatalogueEntry, Game, LibraryGame, - GameRepack, GameShop, HowLongToBeatCategory, ShopDetails, @@ -13,7 +11,6 @@ import type { UserPreferences, StartGameDownloadPayload, RealDebridUser, - DownloadSource, UserProfile, FriendRequest, FriendRequestAction, @@ -30,6 +27,7 @@ import type { LudusaviBackup, UserAchievement, ComparedAchievements, + CatalogueSearchPayload, } from "@types"; import type { AxiosProgressEvent } from "axios"; import type { DiskSpace } from "check-disk-space"; @@ -51,8 +49,8 @@ declare global { ) => () => Electron.IpcRenderer; /* Catalogue */ - searchGames: (query: string) => Promise; - getCatalogue: (category: CatalogueCategory) => Promise; + searchGames: (payload: CatalogueSearchPayload) => Promise; + getCatalogue: (category: CatalogueCategory) => Promise; getGameShopDetails: ( objectId: string, shop: GameShop, @@ -63,8 +61,6 @@ declare global { objectId: string, shop: GameShop ) => Promise; - getGames: (take?: number, skip?: number) => Promise; - searchGameRepacks: (query: string) => Promise; getGameStats: (objectId: string, shop: GameShop) => Promise; getTrendingGames: () => Promise; onUpdateAchievements: ( @@ -118,8 +114,9 @@ declare global { authenticateRealDebrid: (apiToken: string) => Promise; /* Download sources */ - getDownloadSources: () => Promise; - deleteDownloadSource: (id: number) => Promise; + putDownloadSource: ( + objectIds: string[] + ) => Promise<{ fingerprint: string }>; /* Hardware */ getDiskFreeSpace: (path: string) => Promise; diff --git a/src/renderer/src/dexie.ts b/src/renderer/src/dexie.ts index f940e13b..78e3a92e 100644 --- a/src/renderer/src/dexie.ts +++ b/src/renderer/src/dexie.ts @@ -21,11 +21,10 @@ export interface CatalogueCache { export const db = new Dexie("Hydra"); -db.version(5).stores({ - repacks: `++id, title, uris, fileSize, uploadDate, downloadSourceId, repacker, createdAt, updatedAt`, - downloadSources: `++id, url, name, etag, downloadCount, status, createdAt, updatedAt`, +db.version(8).stores({ + repacks: `++id, title, uris, fileSize, uploadDate, downloadSourceId, repacker, objectIds, createdAt, updatedAt`, + downloadSources: `++id, url, name, etag, objectIds, downloadCount, status, fingerprint, createdAt, updatedAt`, howLongToBeatEntries: `++id, categories, [shop+objectId], createdAt, updatedAt`, - catalogueCache: `++id, category, games, createdAt, updatedAt, expiresAt`, }); export const downloadSourcesTable = db.table("downloadSources"); @@ -34,6 +33,4 @@ export const howLongToBeatEntriesTable = db.table( "howLongToBeatEntries" ); -export const catalogueCacheTable = db.table("catalogueCache"); - db.open(); diff --git a/src/renderer/src/features/catalogue-search.ts b/src/renderer/src/features/catalogue-search.ts new file mode 100644 index 00000000..90fd5051 --- /dev/null +++ b/src/renderer/src/features/catalogue-search.ts @@ -0,0 +1,37 @@ +import { createSlice } from "@reduxjs/toolkit"; +import type { PayloadAction } from "@reduxjs/toolkit"; + +import type { CatalogueSearchPayload } from "@types"; + +export interface CatalogueSearchState { + value: CatalogueSearchPayload; +} + +const initialState: CatalogueSearchState = { + value: { + title: "", + downloadSourceFingerprints: [], + tags: [], + publishers: [], + genres: [], + developers: [], + }, +}; + +export const catalogueSearchSlice = createSlice({ + name: "catalogueSearch", + initialState, + reducers: { + setSearch: ( + state, + action: PayloadAction> + ) => { + state.value = { ...state.value, ...action.payload }; + }, + clearSearch: (state) => { + state.value = initialState.value; + }, + }, +}); + +export const { setSearch } = catalogueSearchSlice.actions; diff --git a/src/renderer/src/features/index.ts b/src/renderer/src/features/index.ts index fdc23e68..e3c1c7a6 100644 --- a/src/renderer/src/features/index.ts +++ b/src/renderer/src/features/index.ts @@ -1,4 +1,3 @@ -export * from "./search-slice"; export * from "./library-slice"; export * from "./use-preferences-slice"; export * from "./download-slice"; @@ -6,3 +5,5 @@ export * from "./window-slice"; export * from "./toast-slice"; export * from "./user-details-slice"; export * from "./running-game-slice"; +export * from "./repacks-slice"; +export * from "./catalogue-search"; diff --git a/src/renderer/src/features/repacks-slice.ts b/src/renderer/src/features/repacks-slice.ts new file mode 100644 index 00000000..cb96ba69 --- /dev/null +++ b/src/renderer/src/features/repacks-slice.ts @@ -0,0 +1,24 @@ +import { createSlice } from "@reduxjs/toolkit"; +import type { PayloadAction } from "@reduxjs/toolkit"; + +import type { GameRepack } from "@types"; + +export interface RepacksState { + value: GameRepack[]; +} + +const initialState: RepacksState = { + value: [], +}; + +export const repacksSlice = createSlice({ + name: "repacks", + initialState, + reducers: { + setRepacks: (state, action: PayloadAction) => { + state.value = action.payload; + }, + }, +}); + +export const { setRepacks } = repacksSlice.actions; diff --git a/src/renderer/src/features/search-slice.ts b/src/renderer/src/features/search-slice.ts deleted file mode 100644 index 2817a1e1..00000000 --- a/src/renderer/src/features/search-slice.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { createSlice } from "@reduxjs/toolkit"; -import type { PayloadAction } from "@reduxjs/toolkit"; - -export interface SearchState { - value: string; -} - -const initialState: SearchState = { - value: "", -}; - -export const searchSlice = createSlice({ - name: "search", - initialState, - reducers: { - setSearch: (state, action: PayloadAction) => { - state.value = action.payload; - }, - clearSearch: (state) => { - state.value = ""; - }, - }, -}); - -export const { setSearch, clearSearch } = searchSlice.actions; diff --git a/src/renderer/src/hooks/index.ts b/src/renderer/src/hooks/index.ts index 910e7a3c..97f519ef 100644 --- a/src/renderer/src/hooks/index.ts +++ b/src/renderer/src/hooks/index.ts @@ -5,3 +5,4 @@ export * from "./use-toast"; export * from "./redux"; export * from "./use-user-details"; export * from "./use-format"; +export * from "./use-repacks"; diff --git a/src/renderer/src/hooks/use-format.ts b/src/renderer/src/hooks/use-format.ts index 75e3a78b..dbf5f0dc 100644 --- a/src/renderer/src/hooks/use-format.ts +++ b/src/renderer/src/hooks/use-format.ts @@ -10,5 +10,5 @@ export function useFormat() { }); }, [i18n.language]); - return { numberFormatter }; + return { numberFormatter, formatNumber: numberFormatter.format }; } diff --git a/src/renderer/src/hooks/use-repacks.ts b/src/renderer/src/hooks/use-repacks.ts new file mode 100644 index 00000000..e55a2036 --- /dev/null +++ b/src/renderer/src/hooks/use-repacks.ts @@ -0,0 +1,26 @@ +import { repacksTable } from "@renderer/dexie"; +import { setRepacks } from "@renderer/features"; +import { useCallback } from "react"; +import { RootState } from "@renderer/store"; +import { useSelector } from "react-redux"; +import { useAppDispatch } from "./redux"; + +export function useRepacks() { + const dispatch = useAppDispatch(); + const repacks = useSelector((state: RootState) => state.repacks.value); + + const getRepacksForObjectId = useCallback( + (objectId: string) => { + return repacks.filter((repack) => repack.objectIds.includes(objectId)); + }, + [repacks] + ); + + const updateRepacks = useCallback(() => { + repacksTable.toArray().then((repacks) => { + dispatch(setRepacks(repacks)); + }); + }, [dispatch]); + + return { getRepacksForObjectId, updateRepacks }; +} diff --git a/src/renderer/src/main.tsx b/src/renderer/src/main.tsx index f2e326c3..221c2726 100644 --- a/src/renderer/src/main.tsx +++ b/src/renderer/src/main.tsx @@ -18,7 +18,6 @@ import { store } from "./store"; import resources from "@locales"; -import { RepacksContextProvider } from "./context"; import { SuspenseWrapper } from "./components"; import { logger } from "./logger"; import { addCookieInterceptor } from "./cookies"; @@ -28,7 +27,6 @@ const GameDetails = React.lazy( () => import("./pages/game-details/game-details") ); const Downloads = React.lazy(() => import("./pages/downloads/downloads")); -const SearchResults = React.lazy(() => import("./pages/home/search-results")); const Settings = React.lazy(() => import("./pages/settings/settings")); const Catalogue = React.lazy(() => import("./pages/catalogue/catalogue")); const Profile = React.lazy(() => import("./pages/profile/profile")); @@ -64,43 +62,37 @@ i18n ReactDOM.createRoot(document.getElementById("root")!).render( - - - - }> - } /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - - - - + + + }> + } /> + } + /> + } + /> + } + /> + } + /> + } + /> + } + /> + + + ); diff --git a/src/renderer/src/pages/catalogue/catalogue.scss b/src/renderer/src/pages/catalogue/catalogue.scss new file mode 100644 index 00000000..ecf99931 --- /dev/null +++ b/src/renderer/src/pages/catalogue/catalogue.scss @@ -0,0 +1,114 @@ +@use "../../scss/globals.scss"; + +@keyframes gradientBorder { + 0% { + border-image-source: linear-gradient(0deg, #16b195 50%, #3e62c0 100%); + } + 50% { + border-image-source: linear-gradient(90deg, #3e62c0 50%, #16b195 100%); + } + 100% { + border-image-source: linear-gradient(180deg, #16b195 50%, #3e62c0 100%); + } +} + +.catalogue { + display: flex; + flex-direction: column; + gap: calc(globals.$spacing-unit * 2); + width: 100%; + padding: 16px; + + &__search-container { + background-color: globals.$dark-background-color; + display: inline-flex; + transition: all ease 0.2s; + width: 200px; + align-items: center; + border-radius: 8px; + border: 1px solid globals.$border-color; + height: 40px; + + &:hover { + border-color: rgba(255, 255, 255, 0.5); + } + + &--focused { + width: 250px; + border-color: #dadbe1; + } + } + + &__search-icon-button { + color: inherit; + cursor: pointer; + transition: all ease 0.2s; + padding: 8px; + + &:hover { + color: #dadbe1; + } + } + + &__search-clear-button { + color: inherit; + cursor: pointer; + transition: all ease 0.2s; + padding: 8px; + + &:hover { + color: #dadbe1; + } + } + + &__search-input { + background-color: transparent; + border: none; + width: 100%; + height: 100%; + outline: none; + color: #dadbe1; + cursor: default; + font-family: inherit; + text-overflow: ellipsis; + } + + &__game-item { + background-color: globals.$dark-background-color; + width: 100%; + color: #fff; + display: flex; + align-items: center; + overflow: hidden; + position: relative; + border-radius: 4px; + border: 1px solid globals.$border-color; + cursor: pointer; + gap: 12px; + transition: all ease 0.2s; + + &:hover { + background-color: rgba(255, 255, 255, 0.05); + } + } + + &__filters-container { + width: 250px; + min-width: 250px; + max-width: 250px; + background-color: globals.$dark-background-color; + border-radius: 4px; + padding: 16px; + border: 1px solid globals.$border-color; + } + + &__ai-recommendations-button-text { + font-weight: 400; + color: #fff; + background: linear-gradient(0deg, #16b195 50%, #3e62c0 100%); + background-clip: text; + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + font-weight: bold; + } +} diff --git a/src/renderer/src/pages/catalogue/catalogue.tsx b/src/renderer/src/pages/catalogue/catalogue.tsx index ff8f5c00..b2e4588a 100644 --- a/src/renderer/src/pages/catalogue/catalogue.tsx +++ b/src/renderer/src/pages/catalogue/catalogue.tsx @@ -1,114 +1,374 @@ -import { Button, GameCard } from "@renderer/components"; -import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; -import { useTranslation } from "react-i18next"; +import { Badge, Button } from "@renderer/components"; -import type { CatalogueEntry } from "@types"; +import type { DownloadSource } from "@types"; -import { clearSearch } from "@renderer/features"; -import { useAppDispatch } from "@renderer/hooks"; -import { useEffect, useRef, useState } from "react"; -import { useNavigate, useSearchParams } from "react-router-dom"; -import * as styles from "../home/home.css"; -import { ArrowLeftIcon, ArrowRightIcon } from "@primer/octicons-react"; -import { buildGameDetailsPath } from "@renderer/helpers"; +import cn from "classnames"; + +import { useAppDispatch, useAppSelector, useRepacks } from "@renderer/hooks"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { LightBulbIcon, SearchIcon, XIcon } from "@primer/octicons-react"; + +import "./catalogue.scss"; import { SPACING_UNIT, vars } from "@renderer/theme.css"; +import { downloadSourcesTable } from "@renderer/dexie"; +import { steamUrlBuilder } from "@shared"; +import { buildGameDetailsPath } from "@renderer/helpers"; +import { useNavigate, useSearchParams } from "react-router-dom"; +import { FilterSection } from "./filter-section"; +import { setSearch } from "@renderer/features"; +import { useTranslation } from "react-i18next"; export default function Catalogue() { + const inputRef = useRef(null); + + const [focused, setFocused] = useState(false); + + const [searchParams] = useSearchParams(); + const search = searchParams.get("search"); + + const navigate = useNavigate(); + + const [downloadSources, setDownloadSources] = useState([]); + const [games, setGames] = useState([]); + + const filters = useAppSelector((state) => state.catalogueSearch.value); + const dispatch = useAppDispatch(); const { t } = useTranslation("catalogue"); - const [searchResults, setSearchResults] = useState([]); - const [isLoading, setIsLoading] = useState(false); - - const contentRef = useRef(null); - - const navigate = useNavigate(); - - const [searchParams] = useSearchParams(); - const skip = Number(searchParams.get("skip") ?? 0); - - const handleGameClick = (game: CatalogueEntry) => { - dispatch(clearSearch()); - navigate(buildGameDetailsPath(game)); - }; + const { getRepacksForObjectId } = useRepacks(); useEffect(() => { - if (contentRef.current) contentRef.current.scrollTop = 0; - setIsLoading(true); - setSearchResults([]); + setGames([]); - window.electron - .getGames(24, skip) - .then((results) => { - return new Promise((resolve) => { - setTimeout(() => { - setSearchResults(results); - resolve(null); - }, 500); - }); - }) - .finally(() => { - setIsLoading(false); - }); - }, [dispatch, skip, searchParams]); - - const handleNextPage = () => { - const params = new URLSearchParams({ - skip: String(skip + 24), + window.electron.searchGames(filters).then((games) => { + setGames(games); }); + }, [filters]); - navigate(`/catalogue?${params.toString()}`); - }; + const gamesWithRepacks = useMemo(() => { + return games.map((game) => { + const repacks = getRepacksForObjectId(game.objectId); + const uniqueRepackers = Array.from( + new Set(repacks.map((repack) => repack.repacker)) + ); + return { ...game, repacks: uniqueRepackers }; + }); + }, [games, getRepacksForObjectId]); + + useEffect(() => { + downloadSourcesTable.toArray().then((sources) => { + setDownloadSources(sources); + }); + }, [getRepacksForObjectId]); + + const focusInput = useCallback(() => { + setFocused(true); + inputRef.current?.focus(); + }, []); + + const onSearch = useCallback( + (value: string) => { + dispatch(setSearch({ title: value })); + }, + [dispatch] + ); + + useEffect(() => { + if (search) { + focusInput(); + } + }, [search, focusInput]); return ( - -
+
- + - -
+ onSearch(event.target.value)} + onFocus={() => setFocused(true)} + onBlur={() => setFocused(false)} + /> -
-
- {isLoading && - Array.from({ length: 12 }).map((_, index) => ( - - ))} - - {!isLoading && searchResults.length > 0 && ( - <> - {searchResults.map((game) => ( - handleGameClick(game)} - /> - ))} - + {filters.title && ( + )} -
-
-
+ + + +
+
+ {filters.downloadSourceFingerprints.map((fingerprint) => ( + + { + downloadSources.find( + (source) => source.fingerprint === fingerprint + )!.name + } + + ))} +
+ + {/* */} +
+ +
+
+ {gamesWithRepacks.map((game, i) => ( + + ))} + +
+ + +
+
+ +
+ + +
+ { + if (filters.genres.includes(value)) { + dispatch( + setSearch({ + genres: filters.genres.filter((genre) => genre !== value), + }) + ); + } else { + dispatch(setSearch({ genres: [...filters.genres, value] })); + } + }} + items={[ + "Action", + "Strategy", + "RPG", + "Casual", + "Racing", + "Sports", + "Indie", + "Adventure", + "Simulation", + "Massively Multiplayer", + "Free to Play", + "Accounting", + "Animation & Modeling", + "Audio Production", + "Design & Illustration", + "Education", + "Photo Editing", + "Software Training", + "Utilities", + "Video Production", + "Web Publishing", + "Game Development", + "Early Access", + "Sexual Content", + "Nudity", + "Violent", + "Gore", + "Documentary", + "Tutorial", + ].map((genre) => ({ + label: genre, + value: genre, + checked: filters.genres.includes(genre), + }))} + /> + + { + if (filters.tags.includes(value)) { + dispatch( + setSearch({ + tags: filters.tags.filter((tag) => tag !== value), + }) + ); + } else { + dispatch(setSearch({ tags: [...filters.tags, value] })); + } + }} + items={[ + "Action", + "Strategy", + "RPG", + "Casual", + "Racing", + "Sports", + "Indie", + "Adventure", + "Simulation", + "Massively Multiplayer", + "Free to Play", + "Accounting", + "Animation & Modeling", + "Audio Production", + "Design & Illustration", + "Education", + "Photo Editing", + "Software Training", + "Utilities", + "Video Production", + "Web Publishing", + "Game Development", + "Early Access", + "Sexual Content", + "Nudity", + "Violent", + "Gore", + "Documentary", + "Tutorial", + ].map((genre) => ({ + label: genre, + value: genre, + checked: filters.tags.includes(genre), + }))} + /> + + { + if (filters.downloadSourceFingerprints.includes(value)) { + dispatch( + setSearch({ + downloadSourceFingerprints: + filters.downloadSourceFingerprints.filter( + (fingerprint) => fingerprint !== value + ), + }) + ); + } else { + dispatch( + setSearch({ + downloadSourceFingerprints: [ + ...filters.downloadSourceFingerprints, + value, + ], + }) + ); + } + }} + items={downloadSources.map((downloadSource) => ({ + label: `${downloadSource.name} (${downloadSource.objectIds.length})`, + value: downloadSource.fingerprint, + checked: filters.downloadSourceFingerprints.includes( + downloadSource.fingerprint + ), + }))} + /> +
+
+
+ ); } diff --git a/src/renderer/src/pages/catalogue/filter-section.tsx b/src/renderer/src/pages/catalogue/filter-section.tsx new file mode 100644 index 00000000..e56f1dd6 --- /dev/null +++ b/src/renderer/src/pages/catalogue/filter-section.tsx @@ -0,0 +1,85 @@ +import { CheckboxField, TextField } from "@renderer/components"; +import { useFormat } from "@renderer/hooks"; +import { useCallback, useMemo, useState } from "react"; + +export interface FilterSectionProps { + title: string; + items: { + label: string; + value: string; + checked: boolean; + }[]; + onSelect: (value: string) => void; +} + +export function FilterSection({ title, items, onSelect }: FilterSectionProps) { + const [search, setSearch] = useState(""); + + const filteredItems = useMemo(() => { + if (items.length > 10 && search.length > 0) { + return items.filter((item) => + item.label.toLowerCase().includes(search.toLowerCase()) + ); + } + + return items.slice(0, 10); + }, [items, search]); + + const onSearch = useCallback((value: string) => { + setSearch(value); + }, []); + + const { formatNumber } = useFormat(); + + return ( +
+

+ {title} +

+ + + {formatNumber(items.length)} disponíveis + + + onSearch(e.target.value)} + value={search} + containerProps={{ style: { marginBottom: 16 } }} + theme="dark" + /> + +
+ {filteredItems.map((item) => ( +
+ onSelect(item.value)} + /> +
+ ))} + + +
+
+ ); +} diff --git a/src/renderer/src/pages/home/home.tsx b/src/renderer/src/pages/home/home.tsx index e04f53c8..abaaba84 100644 --- a/src/renderer/src/pages/home/home.tsx +++ b/src/renderer/src/pages/home/home.tsx @@ -5,7 +5,7 @@ import { useNavigate } from "react-router-dom"; import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; import { Button, GameCard, Hero } from "@renderer/components"; -import type { Steam250Game, CatalogueEntry } from "@types"; +import type { Steam250Game } from "@types"; import flameIconStatic from "@renderer/assets/icons/flame-static.png"; import flameIconAnimated from "@renderer/assets/icons/flame-animated.gif"; @@ -15,14 +15,6 @@ import * as styles from "./home.css"; import { SPACING_UNIT, vars } from "@renderer/theme.css"; import { buildGameDetailsPath } from "@renderer/helpers"; import { CatalogueCategory } from "@shared"; -import { catalogueCacheTable, db } from "@renderer/dexie"; -import { add } from "date-fns"; - -const categoryCacheDurationInSeconds = { - [CatalogueCategory.Hot]: 60 * 60 * 2, - [CatalogueCategory.Weekly]: 60 * 60 * 24, - [CatalogueCategory.Achievements]: 60 * 60 * 24, -}; export default function Home() { const { t } = useTranslation("home"); @@ -36,9 +28,7 @@ export default function Home() { CatalogueCategory.Hot ); - const [catalogue, setCatalogue] = useState< - Record - >({ + const [catalogue, setCatalogue] = useState>({ [CatalogueCategory.Hot]: [], [CatalogueCategory.Weekly]: [], [CatalogueCategory.Achievements]: [], @@ -46,37 +36,11 @@ export default function Home() { const getCatalogue = useCallback(async (category: CatalogueCategory) => { try { - const catalogueCache = await catalogueCacheTable - .where("expiresAt") - .above(new Date()) - .and((cache) => cache.category === category) - .first(); - setCurrentCatalogueCategory(category); setIsLoading(true); - if (catalogueCache) - return setCatalogue((prev) => ({ - ...prev, - [category]: catalogueCache.games, - })); - const catalogue = await window.electron.getCatalogue(category); - db.transaction("rw", catalogueCacheTable, async () => { - await catalogueCacheTable.where("category").equals(category).delete(); - - await catalogueCacheTable.add({ - category, - games: catalogue, - createdAt: new Date(), - updatedAt: new Date(), - expiresAt: add(new Date(), { - seconds: categoryCacheDurationInSeconds[category], - }), - }); - }); - setCatalogue((prev) => ({ ...prev, [category]: catalogue })); } finally { setIsLoading(false); diff --git a/src/renderer/src/pages/home/search-results.tsx b/src/renderer/src/pages/home/search-results.tsx deleted file mode 100644 index d86a362a..00000000 --- a/src/renderer/src/pages/home/search-results.tsx +++ /dev/null @@ -1,131 +0,0 @@ -import { GameCard } from "@renderer/components"; -import Skeleton, { SkeletonTheme } from "react-loading-skeleton"; - -import type { CatalogueEntry } from "@types"; - -import type { DebouncedFunc } from "lodash"; -import { debounce } from "lodash"; - -import { InboxIcon, SearchIcon } from "@primer/octicons-react"; -import { clearSearch, setSearch } from "@renderer/features"; -import { useAppDispatch } from "@renderer/hooks"; -import { useEffect, useRef, useState } from "react"; -import { useTranslation } from "react-i18next"; -import { useNavigate, useSearchParams } from "react-router-dom"; -import * as styles from "./home.css"; -import { buildGameDetailsPath } from "@renderer/helpers"; - -import { vars } from "@renderer/theme.css"; - -export default function SearchResults() { - const dispatch = useAppDispatch(); - - const { t } = useTranslation("home"); - const [searchParams] = useSearchParams(); - - const [searchResults, setSearchResults] = useState([]); - const [isLoading, setIsLoading] = useState(false); - const [showTypingMessage, setShowTypingMessage] = useState(false); - - const debouncedFunc = useRef void> | null>(null); - const abortControllerRef = useRef(null); - - const navigate = useNavigate(); - - const handleGameClick = (game: CatalogueEntry) => { - dispatch(clearSearch()); - navigate(buildGameDetailsPath(game)); - }; - - useEffect(() => { - dispatch(setSearch(searchParams.get("query") ?? "")); - }, [dispatch, searchParams]); - - useEffect(() => { - setIsLoading(true); - if (debouncedFunc.current) debouncedFunc.current.cancel(); - if (abortControllerRef.current) abortControllerRef.current.abort(); - - const abortController = new AbortController(); - abortControllerRef.current = abortController; - - debouncedFunc.current = debounce(() => { - const query = searchParams.get("query") ?? ""; - - if (query.length < 3) { - setIsLoading(false); - setShowTypingMessage(true); - setSearchResults([]); - return; - } - - setShowTypingMessage(false); - window.electron - .searchGames(query) - .then((results) => { - if (abortController.signal.aborted) return; - - setSearchResults(results); - setIsLoading(false); - }) - .catch(() => { - setIsLoading(false); - }); - }, 500); - - debouncedFunc.current(); - }, [searchParams, dispatch]); - - const noResultsContent = () => { - if (isLoading) return null; - - if (showTypingMessage) { - return ( -
- - -

{t("start_typing")}

-
- ); - } - - if (searchResults.length === 0) { - return ( -
- - -

{t("no_results")}

-
- ); - } - - return null; - }; - - return ( - -
-
- {isLoading && - Array.from({ length: 12 }).map((_, index) => ( - - ))} - - {!isLoading && searchResults.length > 0 && ( - <> - {searchResults.map((game) => ( - handleGameClick(game)} - /> - ))} - - )} -
- - {noResultsContent()} -
-
- ); -} diff --git a/src/renderer/src/pages/settings/add-download-source-modal.tsx b/src/renderer/src/pages/settings/add-download-source-modal.tsx index 5b05d5b8..c2b94b7f 100644 --- a/src/renderer/src/pages/settings/add-download-source-modal.tsx +++ b/src/renderer/src/pages/settings/add-download-source-modal.tsx @@ -100,17 +100,30 @@ export function AddDownloadSourceModal({ } }, [visible, clearErrors, handleSubmit, onSubmit, setValue, sourceUrl]); - const handleAddDownloadSource = async () => { - setIsLoading(true); + const putDownloadSource = async () => { + const downloadSource = await downloadSourcesTable.where({ url }).first(); + if (!downloadSource) return; + window.electron + .putDownloadSource(downloadSource.objectIds) + .then(({ fingerprint }) => { + downloadSourcesTable.update(downloadSource.id, { fingerprint }); + }); + }; + + const handleAddDownloadSource = async () => { if (validationResult) { + setIsLoading(true); + const channel = new BroadcastChannel(`download_sources:import:${url}`); downloadSourcesWorker.postMessage(["IMPORT_DOWNLOAD_SOURCE", url]); - channel.onmessage = () => { + channel.onmessage = async () => { setIsLoading(false); + putDownloadSource(); + onClose(); onAddDownloadSource(); channel.close(); diff --git a/src/renderer/src/pages/settings/settings-download-sources.tsx b/src/renderer/src/pages/settings/settings-download-sources.tsx index d2f45329..94846fc5 100644 --- a/src/renderer/src/pages/settings/settings-download-sources.tsx +++ b/src/renderer/src/pages/settings/settings-download-sources.tsx @@ -7,10 +7,10 @@ import * as styles from "./settings-download-sources.css"; import type { DownloadSource } from "@types"; import { NoEntryIcon, PlusCircleIcon, SyncIcon } from "@primer/octicons-react"; import { AddDownloadSourceModal } from "./add-download-source-modal"; -import { useToast } from "@renderer/hooks"; +import { useRepacks, useToast } from "@renderer/hooks"; import { DownloadSourceStatus } from "@shared"; import { SPACING_UNIT } from "@renderer/theme.css"; -import { repacksContext, settingsContext } from "@renderer/context"; +import { settingsContext } from "@renderer/context"; import { downloadSourcesTable } from "@renderer/dexie"; import { downloadSourcesWorker } from "@renderer/workers"; @@ -28,7 +28,7 @@ export function SettingsDownloadSources() { const { t } = useTranslation("settings"); const { showSuccessToast } = useToast(); - const { indexRepacks } = useContext(repacksContext); + const { updateRepacks } = useRepacks(); const getDownloadSources = async () => { await downloadSourcesTable @@ -57,16 +57,16 @@ export function SettingsDownloadSources() { showSuccessToast(t("removed_download_source")); getDownloadSources(); - indexRepacks(); setIsRemovingDownloadSource(false); channel.close(); + updateRepacks(); }; }; const handleAddDownloadSource = async () => { - indexRepacks(); await getDownloadSources(); showSuccessToast(t("added_download_source")); + updateRepacks(); }; const syncDownloadSources = async () => { @@ -82,6 +82,7 @@ export function SettingsDownloadSources() { getDownloadSources(); setIsSyncingDownloadSources(false); channel.close(); + updateRepacks(); }; }; diff --git a/src/renderer/src/scss/globals.scss b/src/renderer/src/scss/globals.scss index 66478981..1dc4144a 100644 --- a/src/renderer/src/scss/globals.scss +++ b/src/renderer/src/scss/globals.scss @@ -6,7 +6,7 @@ $body-color: #8e919b; $border-color: #424244; $success-color: #1c9749; -$danger-color: #e11d48; +$danger-color: #801d1e; $warning-color: #ffc107; $disabled-opacity: 0.5; diff --git a/src/renderer/src/store.ts b/src/renderer/src/store.ts index 0f2bee9f..36b60309 100644 --- a/src/renderer/src/store.ts +++ b/src/renderer/src/store.ts @@ -3,16 +3,16 @@ import { downloadSlice, windowSlice, librarySlice, - searchSlice, userPreferencesSlice, toastSlice, userDetailsSlice, gameRunningSlice, + repacksSlice, + catalogueSearchSlice, } from "@renderer/features"; export const store = configureStore({ reducer: { - search: searchSlice.reducer, window: windowSlice.reducer, library: librarySlice.reducer, userPreferences: userPreferencesSlice.reducer, @@ -20,6 +20,8 @@ export const store = configureStore({ toast: toastSlice.reducer, userDetails: userDetailsSlice.reducer, gameRunning: gameRunningSlice.reducer, + repacks: repacksSlice.reducer, + catalogueSearch: catalogueSearchSlice.reducer, }, }); diff --git a/src/renderer/src/workers/download-sources.worker.ts b/src/renderer/src/workers/download-sources.worker.ts index 29ab7d87..a847520a 100644 --- a/src/renderer/src/workers/download-sources.worker.ts +++ b/src/renderer/src/workers/download-sources.worker.ts @@ -2,7 +2,10 @@ import { db, downloadSourcesTable, repacksTable } from "@renderer/dexie"; import { z } from "zod"; import axios, { AxiosError, AxiosHeaders } from "axios"; -import { DownloadSourceStatus } from "@shared"; +import { DownloadSourceStatus, formatName, pipe } from "@shared"; +import { GameRepack } from "@types"; + +const formatRepackName = pipe((name) => name.replace("[DL]", ""), formatName); export const downloadSourceSchema = z.object({ name: z.string().max(255), @@ -22,6 +25,64 @@ type Payload = | ["VALIDATE_DOWNLOAD_SOURCE", string] | ["SYNC_DOWNLOAD_SOURCES", string]; +export type SteamGamesByLetter = Record; + +const addNewDownloads = async ( + downloadSource: { id: number; name: string }, + downloads: z.infer["downloads"], + steamGames: SteamGamesByLetter +) => { + const now = new Date(); + + const results = [] as (Omit & { + downloadSourceId: number; + })[]; + + const objectIdsOnSource = new Set(); + + for (const download of downloads) { + const formattedTitle = formatRepackName(download.title); + const [firstLetter] = formattedTitle; + const games = steamGames[firstLetter] || []; + + const gamesInSteam = games.filter((game) => + formattedTitle.startsWith(game.name) + ); + + if (gamesInSteam.length === 0) continue; + + for (const game of gamesInSteam) { + objectIdsOnSource.add(String(game.id)); + } + + results.push({ + objectIds: gamesInSteam.map((game) => String(game.id)), + title: download.title, + uris: download.uris, + fileSize: download.fileSize, + repacker: downloadSource.name, + uploadDate: download.uploadDate, + downloadSourceId: downloadSource.id, + createdAt: now, + updatedAt: now, + }); + } + + await repacksTable.bulkAdd(results); + + await downloadSourcesTable.update(downloadSource.id, { + objectIds: Array.from(objectIdsOnSource), + }); +}; + +const getSteamGames = async () => { + const response = await axios.get( + "https://assets.hydralauncher.gg/steam-games-by-letter.json" + ); + + return response.data; +}; + self.onmessage = async (event: MessageEvent) => { const [type, data] = event.data; @@ -55,6 +116,8 @@ self.onmessage = async (event: MessageEvent) => { const response = await axios.get>(data); + const steamGames = await getSteamGames(); + await db.transaction("rw", repacksTable, downloadSourcesTable, async () => { const now = new Date(); @@ -70,18 +133,11 @@ self.onmessage = async (event: MessageEvent) => { const downloadSource = await downloadSourcesTable.get(id); - const repacks = response.data.downloads.map((download) => ({ - title: download.title, - uris: download.uris, - fileSize: download.fileSize, - repacker: response.data.name, - uploadDate: download.uploadDate, - downloadSourceId: downloadSource!.id, - createdAt: now, - updatedAt: now, - })); - - await repacksTable.bulkAdd(repacks); + await addNewDownloads( + downloadSource, + response.data.downloads, + steamGames + ); }); const channel = new BroadcastChannel(`download_sources:import:${data}`); @@ -110,6 +166,8 @@ self.onmessage = async (event: MessageEvent) => { const source = downloadSourceSchema.parse(response.data); + const steamGames = await getSteamGames(); + await db.transaction( "rw", repacksTable, @@ -121,29 +179,16 @@ self.onmessage = async (event: MessageEvent) => { status: DownloadSourceStatus.UpToDate, }); - const now = new Date(); + const repacks = source.downloads.filter( + (download) => + !existingRepacks.some( + (repack) => repack.title === download.title + ) + ); - const repacks = source.downloads - .filter( - (download) => - !existingRepacks.some( - (repack) => repack.title === download.title - ) - ) - .map((download) => ({ - title: download.title, - uris: download.uris, - fileSize: download.fileSize, - repacker: source.name, - uploadDate: download.uploadDate, - downloadSourceId: downloadSource.id, - createdAt: now, - updatedAt: now, - })); + await addNewDownloads(downloadSource, repacks, steamGames); newRepacksCount += repacks.length; - - await repacksTable.bulkAdd(repacks); } ); } catch (err: unknown) { diff --git a/src/renderer/src/workers/index.ts b/src/renderer/src/workers/index.ts index b8141a8f..39367894 100644 --- a/src/renderer/src/workers/index.ts +++ b/src/renderer/src/workers/index.ts @@ -1,5 +1,3 @@ -import RepacksWorker from "./repacks.worker?worker"; import DownloadSourcesWorker from "./download-sources.worker?worker"; -export const repacksWorker = new RepacksWorker(); export const downloadSourcesWorker = new DownloadSourcesWorker(); diff --git a/src/renderer/src/workers/repacks.worker.ts b/src/renderer/src/workers/repacks.worker.ts deleted file mode 100644 index c2394510..00000000 --- a/src/renderer/src/workers/repacks.worker.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { repacksTable } from "@renderer/dexie"; -import { formatName } from "@shared"; -import type { GameRepack } from "@types"; -import flexSearch from "flexsearch"; - -interface SerializedGameRepack extends Omit { - uris: string; -} - -const state = { - repacks: [] as SerializedGameRepack[], - index: null as flexSearch.Index | null, -}; - -self.onmessage = async ( - event: MessageEvent<[string, string] | "INDEX_REPACKS"> -) => { - if (event.data === "INDEX_REPACKS") { - state.index = new flexSearch.Index(); - - repacksTable - .toCollection() - .sortBy("uploadDate") - .then((results) => { - state.repacks = results.reverse(); - - for (let i = 0; i < state.repacks.length; i++) { - const repack = state.repacks[i]; - const formattedTitle = formatName(repack.title); - state.index!.add(i, formattedTitle); - } - - self.postMessage("INDEXING_COMPLETE"); - }); - } else { - const [requestId, query] = event.data; - - const results = state.index!.search(formatName(query)).map((index) => { - const repack = state.repacks.at(index as number) as SerializedGameRepack; - - return { - ...repack, - uris: [...repack.uris, repack.magnet].filter(Boolean), - }; - }); - - const channel = new BroadcastChannel(`repacks:search:${requestId}`); - - channel.postMessage(results); - } -}; diff --git a/src/types/index.ts b/src/types/index.ts index 434a15e7..48756fcb 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -16,13 +16,10 @@ export type FriendRequestAction = "ACCEPTED" | "REFUSED" | "CANCEL"; export interface GameRepack { id: number; title: string; - /** - * @deprecated Use uris instead - */ - magnet: string; uris: string[]; repacker: string; fileSize: string | null; + objectIds: string[]; uploadDate: Date | string | null; createdAt: Date; updatedAt: Date; @@ -77,15 +74,6 @@ export interface TorrentFile { length: number; } -/* Used by the catalogue */ -export interface CatalogueEntry { - objectId: string; - shop: GameShop; - title: string; - /* Epic Games covers cannot be guessed with objectID */ - cover: string; -} - export interface UserGame { objectId: string; shop: GameShop; @@ -301,6 +289,7 @@ export interface DownloadSource { repackCount: number; status: DownloadSourceStatus; downloadCount: number; + fingerprint: string; etag: string | null; createdAt: Date; updatedAt: Date; @@ -374,6 +363,15 @@ export interface ComparedAchievements { }[]; } +export interface CatalogueSearchPayload { + title: string; + downloadSourceFingerprints: string[]; + tags: number[]; + publishers: string[]; + genres: string[]; + developers: string[]; +} + export * from "./steam.types"; export * from "./real-debrid.types"; export * from "./ludusavi.types"; diff --git a/yarn.lock b/yarn.lock index b6b2d3b9..c8bbd83e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -15,6 +15,608 @@ "@jridgewell/gen-mapping" "^0.3.5" "@jridgewell/trace-mapping" "^0.3.24" +"@aws-crypto/crc32@5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/crc32/-/crc32-5.2.0.tgz#cfcc22570949c98c6689cfcbd2d693d36cdae2e1" + integrity sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg== + dependencies: + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + tslib "^2.6.2" + +"@aws-crypto/crc32c@5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/crc32c/-/crc32c-5.2.0.tgz#4e34aab7f419307821509a98b9b08e84e0c1917e" + integrity sha512-+iWb8qaHLYKrNvGRbiYRHSdKRWhto5XlZUEBwDjYNf+ly5SVYG6zEoYIdxvf5R3zyeP16w4PLBn3rH1xc74Rag== + dependencies: + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + tslib "^2.6.2" + +"@aws-crypto/sha1-browser@5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha1-browser/-/sha1-browser-5.2.0.tgz#b0ee2d2821d3861f017e965ef3b4cb38e3b6a0f4" + integrity sha512-OH6lveCFfcDjX4dbAvCFSYUjJZjDr/3XJ3xHtjn3Oj5b9RjojQo8npoLeA/bNwkOkrSQ0wgrHzXk4tDRxGKJeg== + dependencies: + "@aws-crypto/supports-web-crypto" "^5.2.0" + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + "@aws-sdk/util-locate-window" "^3.0.0" + "@smithy/util-utf8" "^2.0.0" + tslib "^2.6.2" + +"@aws-crypto/sha256-browser@5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz#153895ef1dba6f9fce38af550e0ef58988eb649e" + integrity sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw== + dependencies: + "@aws-crypto/sha256-js" "^5.2.0" + "@aws-crypto/supports-web-crypto" "^5.2.0" + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + "@aws-sdk/util-locate-window" "^3.0.0" + "@smithy/util-utf8" "^2.0.0" + tslib "^2.6.2" + +"@aws-crypto/sha256-js@5.2.0", "@aws-crypto/sha256-js@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz#c4fdb773fdbed9a664fc1a95724e206cf3860042" + integrity sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA== + dependencies: + "@aws-crypto/util" "^5.2.0" + "@aws-sdk/types" "^3.222.0" + tslib "^2.6.2" + +"@aws-crypto/supports-web-crypto@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz#a1e399af29269be08e695109aa15da0a07b5b5fb" + integrity sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg== + dependencies: + tslib "^2.6.2" + +"@aws-crypto/util@5.2.0", "@aws-crypto/util@^5.2.0": + version "5.2.0" + resolved "https://registry.yarnpkg.com/@aws-crypto/util/-/util-5.2.0.tgz#71284c9cffe7927ddadac793c14f14886d3876da" + integrity sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ== + dependencies: + "@aws-sdk/types" "^3.222.0" + "@smithy/util-utf8" "^2.0.0" + tslib "^2.6.2" + +"@aws-sdk/client-s3@^3.705.0": + version "3.712.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-s3/-/client-s3-3.712.0.tgz#2911e9c7a65fd9e5679926fdb838311a0b29e37c" + integrity sha512-Hq1IIwOFutmHtTz3mROR1XhTDL8rxcYbYw3ajjgeMJB5tjcvodpfkfz/L4dxXZMwqylWf6SNQNAiaGh5mlsGGQ== + dependencies: + "@aws-crypto/sha1-browser" "5.2.0" + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/client-sso-oidc" "3.712.0" + "@aws-sdk/client-sts" "3.712.0" + "@aws-sdk/core" "3.709.0" + "@aws-sdk/credential-provider-node" "3.712.0" + "@aws-sdk/middleware-bucket-endpoint" "3.709.0" + "@aws-sdk/middleware-expect-continue" "3.709.0" + "@aws-sdk/middleware-flexible-checksums" "3.709.0" + "@aws-sdk/middleware-host-header" "3.709.0" + "@aws-sdk/middleware-location-constraint" "3.709.0" + "@aws-sdk/middleware-logger" "3.709.0" + "@aws-sdk/middleware-recursion-detection" "3.709.0" + "@aws-sdk/middleware-sdk-s3" "3.709.0" + "@aws-sdk/middleware-ssec" "3.709.0" + "@aws-sdk/middleware-user-agent" "3.709.0" + "@aws-sdk/region-config-resolver" "3.709.0" + "@aws-sdk/signature-v4-multi-region" "3.709.0" + "@aws-sdk/types" "3.709.0" + "@aws-sdk/util-endpoints" "3.709.0" + "@aws-sdk/util-user-agent-browser" "3.709.0" + "@aws-sdk/util-user-agent-node" "3.712.0" + "@aws-sdk/xml-builder" "3.709.0" + "@smithy/config-resolver" "^3.0.13" + "@smithy/core" "^2.5.5" + "@smithy/eventstream-serde-browser" "^3.0.14" + "@smithy/eventstream-serde-config-resolver" "^3.0.11" + "@smithy/eventstream-serde-node" "^3.0.13" + "@smithy/fetch-http-handler" "^4.1.2" + "@smithy/hash-blob-browser" "^3.1.10" + "@smithy/hash-node" "^3.0.11" + "@smithy/hash-stream-node" "^3.1.10" + "@smithy/invalid-dependency" "^3.0.11" + "@smithy/md5-js" "^3.0.11" + "@smithy/middleware-content-length" "^3.0.13" + "@smithy/middleware-endpoint" "^3.2.5" + "@smithy/middleware-retry" "^3.0.30" + "@smithy/middleware-serde" "^3.0.11" + "@smithy/middleware-stack" "^3.0.11" + "@smithy/node-config-provider" "^3.1.12" + "@smithy/node-http-handler" "^3.3.2" + "@smithy/protocol-http" "^4.1.8" + "@smithy/smithy-client" "^3.5.0" + "@smithy/types" "^3.7.2" + "@smithy/url-parser" "^3.0.11" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.30" + "@smithy/util-defaults-mode-node" "^3.0.30" + "@smithy/util-endpoints" "^2.1.7" + "@smithy/util-middleware" "^3.0.11" + "@smithy/util-retry" "^3.0.11" + "@smithy/util-stream" "^3.3.2" + "@smithy/util-utf8" "^3.0.0" + "@smithy/util-waiter" "^3.2.0" + tslib "^2.6.2" + +"@aws-sdk/client-sso-oidc@3.712.0": + version "3.712.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso-oidc/-/client-sso-oidc-3.712.0.tgz#ba3c9ae1b74f3c44e406397c60c812bb9e2e98a4" + integrity sha512-xNFrG9syrG6pxUP7Ld/nu3afQ9+rbJM9qrE+wDNz4VnNZ3vLiJty4fH85zBFhOQ5OF2DIJTWsFzXGi2FYjsCMA== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.709.0" + "@aws-sdk/credential-provider-node" "3.712.0" + "@aws-sdk/middleware-host-header" "3.709.0" + "@aws-sdk/middleware-logger" "3.709.0" + "@aws-sdk/middleware-recursion-detection" "3.709.0" + "@aws-sdk/middleware-user-agent" "3.709.0" + "@aws-sdk/region-config-resolver" "3.709.0" + "@aws-sdk/types" "3.709.0" + "@aws-sdk/util-endpoints" "3.709.0" + "@aws-sdk/util-user-agent-browser" "3.709.0" + "@aws-sdk/util-user-agent-node" "3.712.0" + "@smithy/config-resolver" "^3.0.13" + "@smithy/core" "^2.5.5" + "@smithy/fetch-http-handler" "^4.1.2" + "@smithy/hash-node" "^3.0.11" + "@smithy/invalid-dependency" "^3.0.11" + "@smithy/middleware-content-length" "^3.0.13" + "@smithy/middleware-endpoint" "^3.2.5" + "@smithy/middleware-retry" "^3.0.30" + "@smithy/middleware-serde" "^3.0.11" + "@smithy/middleware-stack" "^3.0.11" + "@smithy/node-config-provider" "^3.1.12" + "@smithy/node-http-handler" "^3.3.2" + "@smithy/protocol-http" "^4.1.8" + "@smithy/smithy-client" "^3.5.0" + "@smithy/types" "^3.7.2" + "@smithy/url-parser" "^3.0.11" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.30" + "@smithy/util-defaults-mode-node" "^3.0.30" + "@smithy/util-endpoints" "^2.1.7" + "@smithy/util-middleware" "^3.0.11" + "@smithy/util-retry" "^3.0.11" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/client-sso@3.712.0": + version "3.712.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sso/-/client-sso-3.712.0.tgz#9644585700f5d96a16151bdb5387755adc524db8" + integrity sha512-tBo/eW3YpZ9f3Q1qA7aA8uliNFJJX0OP7R2IUJ8t6rqVTk15wWCEPNmXzUZKgruDnKUfCaF4+r9q/Yy4fBc9PA== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/core" "3.709.0" + "@aws-sdk/middleware-host-header" "3.709.0" + "@aws-sdk/middleware-logger" "3.709.0" + "@aws-sdk/middleware-recursion-detection" "3.709.0" + "@aws-sdk/middleware-user-agent" "3.709.0" + "@aws-sdk/region-config-resolver" "3.709.0" + "@aws-sdk/types" "3.709.0" + "@aws-sdk/util-endpoints" "3.709.0" + "@aws-sdk/util-user-agent-browser" "3.709.0" + "@aws-sdk/util-user-agent-node" "3.712.0" + "@smithy/config-resolver" "^3.0.13" + "@smithy/core" "^2.5.5" + "@smithy/fetch-http-handler" "^4.1.2" + "@smithy/hash-node" "^3.0.11" + "@smithy/invalid-dependency" "^3.0.11" + "@smithy/middleware-content-length" "^3.0.13" + "@smithy/middleware-endpoint" "^3.2.5" + "@smithy/middleware-retry" "^3.0.30" + "@smithy/middleware-serde" "^3.0.11" + "@smithy/middleware-stack" "^3.0.11" + "@smithy/node-config-provider" "^3.1.12" + "@smithy/node-http-handler" "^3.3.2" + "@smithy/protocol-http" "^4.1.8" + "@smithy/smithy-client" "^3.5.0" + "@smithy/types" "^3.7.2" + "@smithy/url-parser" "^3.0.11" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.30" + "@smithy/util-defaults-mode-node" "^3.0.30" + "@smithy/util-endpoints" "^2.1.7" + "@smithy/util-middleware" "^3.0.11" + "@smithy/util-retry" "^3.0.11" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/client-sts@3.712.0": + version "3.712.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/client-sts/-/client-sts-3.712.0.tgz#455daebd946369c60c7795efbd7a6b5981d0662a" + integrity sha512-gIO6BD+hkEe3GKQhbiFP0zcNQv0EkP1Cl9SOstxS+X9CeudEgVX/xEPUjyoFVkfkntPBJ1g0I1u5xOzzRExl4g== + dependencies: + "@aws-crypto/sha256-browser" "5.2.0" + "@aws-crypto/sha256-js" "5.2.0" + "@aws-sdk/client-sso-oidc" "3.712.0" + "@aws-sdk/core" "3.709.0" + "@aws-sdk/credential-provider-node" "3.712.0" + "@aws-sdk/middleware-host-header" "3.709.0" + "@aws-sdk/middleware-logger" "3.709.0" + "@aws-sdk/middleware-recursion-detection" "3.709.0" + "@aws-sdk/middleware-user-agent" "3.709.0" + "@aws-sdk/region-config-resolver" "3.709.0" + "@aws-sdk/types" "3.709.0" + "@aws-sdk/util-endpoints" "3.709.0" + "@aws-sdk/util-user-agent-browser" "3.709.0" + "@aws-sdk/util-user-agent-node" "3.712.0" + "@smithy/config-resolver" "^3.0.13" + "@smithy/core" "^2.5.5" + "@smithy/fetch-http-handler" "^4.1.2" + "@smithy/hash-node" "^3.0.11" + "@smithy/invalid-dependency" "^3.0.11" + "@smithy/middleware-content-length" "^3.0.13" + "@smithy/middleware-endpoint" "^3.2.5" + "@smithy/middleware-retry" "^3.0.30" + "@smithy/middleware-serde" "^3.0.11" + "@smithy/middleware-stack" "^3.0.11" + "@smithy/node-config-provider" "^3.1.12" + "@smithy/node-http-handler" "^3.3.2" + "@smithy/protocol-http" "^4.1.8" + "@smithy/smithy-client" "^3.5.0" + "@smithy/types" "^3.7.2" + "@smithy/url-parser" "^3.0.11" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-body-length-node" "^3.0.0" + "@smithy/util-defaults-mode-browser" "^3.0.30" + "@smithy/util-defaults-mode-node" "^3.0.30" + "@smithy/util-endpoints" "^2.1.7" + "@smithy/util-middleware" "^3.0.11" + "@smithy/util-retry" "^3.0.11" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/core@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/core/-/core-3.709.0.tgz#d2b3d5b90f6614e3afc109ebdcaaedbb54c2d68b" + integrity sha512-7kuSpzdOTAE026j85wq/fN9UDZ70n0OHw81vFqMWwlEFtm5IQ/MRCLKcC4HkXxTdfy1PqFlmoXxWqeBa15tujw== + dependencies: + "@aws-sdk/types" "3.709.0" + "@smithy/core" "^2.5.5" + "@smithy/node-config-provider" "^3.1.12" + "@smithy/property-provider" "^3.1.11" + "@smithy/protocol-http" "^4.1.8" + "@smithy/signature-v4" "^4.2.4" + "@smithy/smithy-client" "^3.5.0" + "@smithy/types" "^3.7.2" + "@smithy/util-middleware" "^3.0.11" + fast-xml-parser "4.4.1" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-env@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-env/-/credential-provider-env-3.709.0.tgz#a7f75375d8a413f9ab2bc42f743b943da6d3362d" + integrity sha512-ZMAp9LSikvHDFVa84dKpQmow6wsg956Um20cKuioPpX2GGreJFur7oduD+tRJT6FtIOHn+64YH+0MwiXLhsaIQ== + dependencies: + "@aws-sdk/core" "3.709.0" + "@aws-sdk/types" "3.709.0" + "@smithy/property-provider" "^3.1.11" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-http@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-http/-/credential-provider-http-3.709.0.tgz#a378cbcc4cf373cc277944f1e84e9952f3884f5d" + integrity sha512-lIS7XLwCOyJnLD70f+VIRr8DNV1HPQe9oN6aguYrhoczqz7vDiVZLe3lh714cJqq9rdxzFypK5DqKHmcscMEPQ== + dependencies: + "@aws-sdk/core" "3.709.0" + "@aws-sdk/types" "3.709.0" + "@smithy/fetch-http-handler" "^4.1.2" + "@smithy/node-http-handler" "^3.3.2" + "@smithy/property-provider" "^3.1.11" + "@smithy/protocol-http" "^4.1.8" + "@smithy/smithy-client" "^3.5.0" + "@smithy/types" "^3.7.2" + "@smithy/util-stream" "^3.3.2" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-ini@3.712.0": + version "3.712.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.712.0.tgz#21d94d3fbaf5cece29bc62d56cf7f0dfb8b7d25e" + integrity sha512-sTsdQ/Fm/suqMdpjhMuss/5uKL18vcuWnNTQVrG9iGNRqZLbq65MXquwbUpgzfoUmIcH+4CrY6H2ebpTIECIag== + dependencies: + "@aws-sdk/core" "3.709.0" + "@aws-sdk/credential-provider-env" "3.709.0" + "@aws-sdk/credential-provider-http" "3.709.0" + "@aws-sdk/credential-provider-process" "3.709.0" + "@aws-sdk/credential-provider-sso" "3.712.0" + "@aws-sdk/credential-provider-web-identity" "3.709.0" + "@aws-sdk/types" "3.709.0" + "@smithy/credential-provider-imds" "^3.2.8" + "@smithy/property-provider" "^3.1.11" + "@smithy/shared-ini-file-loader" "^3.1.12" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-node@3.712.0": + version "3.712.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-node/-/credential-provider-node-3.712.0.tgz#6f017382b1182578cf62798310f42264b652e36e" + integrity sha512-gXrHymW3rMRYORkPVQwL8Gi5Lu92F16SoZR543x03qCi7rm00oL9tRD85ACxkhprS1Wh8lUIUMNoeiwnYWTNuQ== + dependencies: + "@aws-sdk/credential-provider-env" "3.709.0" + "@aws-sdk/credential-provider-http" "3.709.0" + "@aws-sdk/credential-provider-ini" "3.712.0" + "@aws-sdk/credential-provider-process" "3.709.0" + "@aws-sdk/credential-provider-sso" "3.712.0" + "@aws-sdk/credential-provider-web-identity" "3.709.0" + "@aws-sdk/types" "3.709.0" + "@smithy/credential-provider-imds" "^3.2.8" + "@smithy/property-provider" "^3.1.11" + "@smithy/shared-ini-file-loader" "^3.1.12" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-process@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-process/-/credential-provider-process-3.709.0.tgz#2521f810590f0874c54cc842d3d56f455a728325" + integrity sha512-IAC+jPlGQII6jhIylHOwh3RgSobqlgL59nw2qYTURr8hMCI0Z1p5y2ee646HTVt4WeCYyzUAXfxr6YI/Vitv+Q== + dependencies: + "@aws-sdk/core" "3.709.0" + "@aws-sdk/types" "3.709.0" + "@smithy/property-provider" "^3.1.11" + "@smithy/shared-ini-file-loader" "^3.1.12" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-sso@3.712.0": + version "3.712.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.712.0.tgz#d29c8c14e2460a817ed2eb7ad5d205d7914817af" + integrity sha512-8lCMxY7Lb9VK9qdlNXRJXE3W1UDVURnJZ3a4XWYNY6yr1TfQaN40mMyXX1oNlXXJtMV0szRvjM8dZj37E/ESAw== + dependencies: + "@aws-sdk/client-sso" "3.712.0" + "@aws-sdk/core" "3.709.0" + "@aws-sdk/token-providers" "3.709.0" + "@aws-sdk/types" "3.709.0" + "@smithy/property-provider" "^3.1.11" + "@smithy/shared-ini-file-loader" "^3.1.12" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@aws-sdk/credential-provider-web-identity@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.709.0.tgz#c2b03541cb57ae4c7d6abdca98f99a6a56833ea6" + integrity sha512-2lbDfE0IQ6gma/7BB2JpkjW5G0wGe4AS0x80oybYAYYviJmUtIR3Cn2pXun6bnAWElt4wYKl4su7oC36rs5rNA== + dependencies: + "@aws-sdk/core" "3.709.0" + "@aws-sdk/types" "3.709.0" + "@smithy/property-provider" "^3.1.11" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@aws-sdk/middleware-bucket-endpoint@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-bucket-endpoint/-/middleware-bucket-endpoint-3.709.0.tgz#a69bdebfebb7b5b174d3a396f2361f5025d168f4" + integrity sha512-03+tJOd7KIZOiqWH7Z8BOfQIWkKJgjcpKOJKZ6FR2KjWGUOE1G+bo11wF4UuHQ0RmpKnApt+pQghZmSnE7WEeg== + dependencies: + "@aws-sdk/types" "3.709.0" + "@aws-sdk/util-arn-parser" "3.693.0" + "@smithy/node-config-provider" "^3.1.12" + "@smithy/protocol-http" "^4.1.8" + "@smithy/types" "^3.7.2" + "@smithy/util-config-provider" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-expect-continue@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-expect-continue/-/middleware-expect-continue-3.709.0.tgz#a7fec776da9de32e15088badfc09d69118f5d5ab" + integrity sha512-Tbl/DFvE4rHl8lMb9IzetwK4tf5R3VeHZkvEXQalsWoK0tbEQ8kXWi7wAYO4qbE7bFVvaxKX+irjJjTxf3BrCQ== + dependencies: + "@aws-sdk/types" "3.709.0" + "@smithy/protocol-http" "^4.1.8" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@aws-sdk/middleware-flexible-checksums@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-flexible-checksums/-/middleware-flexible-checksums-3.709.0.tgz#f0fb543c2db724cb43bae215ff0aea942d06a967" + integrity sha512-wbYm9tkyCaqMeU82yjaXw7V5BxCSlSLNupENW63LC7Fvyo/aQzj6LjSMHcBpR2QwjBEhXCtF47L7aQ8SPTNhdw== + dependencies: + "@aws-crypto/crc32" "5.2.0" + "@aws-crypto/crc32c" "5.2.0" + "@aws-crypto/util" "5.2.0" + "@aws-sdk/core" "3.709.0" + "@aws-sdk/types" "3.709.0" + "@smithy/is-array-buffer" "^3.0.0" + "@smithy/node-config-provider" "^3.1.12" + "@smithy/protocol-http" "^4.1.8" + "@smithy/types" "^3.7.2" + "@smithy/util-middleware" "^3.0.11" + "@smithy/util-stream" "^3.3.2" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-host-header@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-host-header/-/middleware-host-header-3.709.0.tgz#f44f5c62f9bd7e5a443603fed68143d2d9725219" + integrity sha512-8gQYCYAaIw4lOCd5WYdf15Y/61MgRsAnrb2eiTl+icMlUOOzl8aOl5iDwm/Idp0oHZTflwxM4XSvGXO83PRWcw== + dependencies: + "@aws-sdk/types" "3.709.0" + "@smithy/protocol-http" "^4.1.8" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@aws-sdk/middleware-location-constraint@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-location-constraint/-/middleware-location-constraint-3.709.0.tgz#4437d3d3cfbbdfca60664b1f237d600b94fd06a5" + integrity sha512-5YQWPXfZq7OE0jB2G0PP8K10GBod/YPJXb+1CfJS6FbQaglRoIm8KZmVEvJNnptSKyGtE62veeCcCQcfAUfFig== + dependencies: + "@aws-sdk/types" "3.709.0" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@aws-sdk/middleware-logger@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-logger/-/middleware-logger-3.709.0.tgz#b9a0b016b7ae09cb502cc4faf45964d4b5745824" + integrity sha512-jDoGSccXv9zebnpUoisjWd5u5ZPIalrmm6TjvPzZ8UqzQt3Beiz0tnQwmxQD6KRc7ADweWP5Ntiqzbw9xpVajg== + dependencies: + "@aws-sdk/types" "3.709.0" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@aws-sdk/middleware-recursion-detection@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.709.0.tgz#d7dc253d4858d496caeb12dd6cddd87b250fb98b" + integrity sha512-PObL/wLr4lkfbQ0yXUWaoCWu/jcwfwZzCjsUiXW/H6hW9b/00enZxmx7OhtJYaR6xmh/Lcx5wbhIoDCbzdv0tw== + dependencies: + "@aws-sdk/types" "3.709.0" + "@smithy/protocol-http" "^4.1.8" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@aws-sdk/middleware-sdk-s3@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-sdk-s3/-/middleware-sdk-s3-3.709.0.tgz#b6f22c77e64760869eb06255af58376f879742b2" + integrity sha512-FwtOG9t9xsLoLOQZ6qAdsWOjx9dsO6t28IjIDV1l6Ixiu2oC0Yks7goONjJUH0IDE4pDDDGzmuq0sn1XtHhheA== + dependencies: + "@aws-sdk/core" "3.709.0" + "@aws-sdk/types" "3.709.0" + "@aws-sdk/util-arn-parser" "3.693.0" + "@smithy/core" "^2.5.5" + "@smithy/node-config-provider" "^3.1.12" + "@smithy/protocol-http" "^4.1.8" + "@smithy/signature-v4" "^4.2.4" + "@smithy/smithy-client" "^3.5.0" + "@smithy/types" "^3.7.2" + "@smithy/util-config-provider" "^3.0.0" + "@smithy/util-middleware" "^3.0.11" + "@smithy/util-stream" "^3.3.2" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@aws-sdk/middleware-ssec@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-ssec/-/middleware-ssec-3.709.0.tgz#bbf5253cdce45ed2759a108fd924fff4b8e049d5" + integrity sha512-2muiLe7YkmlwZp2SKz+goZrDThGfRq3o0FcJF3Puc0XGmcEPEDjih537mCoTrGgcXNFlBc7YChd84r3t72ySaQ== + dependencies: + "@aws-sdk/types" "3.709.0" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@aws-sdk/middleware-user-agent@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.709.0.tgz#2a467f14b3f4a9270bcdfde32e3d4e38701aaafe" + integrity sha512-ooc9ZJvgkjPhi9q05XwSfNTXkEBEIfL4hleo5rQBKwHG3aTHvwOM7LLzhdX56QZVa6sorPBp6fwULuRDSqiQHw== + dependencies: + "@aws-sdk/core" "3.709.0" + "@aws-sdk/types" "3.709.0" + "@aws-sdk/util-endpoints" "3.709.0" + "@smithy/core" "^2.5.5" + "@smithy/protocol-http" "^4.1.8" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@aws-sdk/region-config-resolver@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/region-config-resolver/-/region-config-resolver-3.709.0.tgz#64547b333842e5804e1793e4d6d29578c0b34a68" + integrity sha512-/NoCAMEVKAg3kBKOrNtgOfL+ECt6nrl+L7q2SyYmrcY4tVCmwuECVqewQaHc03fTnJijfKLccw0Fj+6wOCnB6w== + dependencies: + "@aws-sdk/types" "3.709.0" + "@smithy/node-config-provider" "^3.1.12" + "@smithy/types" "^3.7.2" + "@smithy/util-config-provider" "^3.0.0" + "@smithy/util-middleware" "^3.0.11" + tslib "^2.6.2" + +"@aws-sdk/signature-v4-multi-region@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/signature-v4-multi-region/-/signature-v4-multi-region-3.709.0.tgz#0c6f9d3e2978158163b63a4085356616237223c9" + integrity sha512-m0vhJEy6SLbjL11K9cHzX/ZhCIj//1GkTbYk2d4tTQFSuPyJEkjmoeHk9dYm2mJy0wH48j29OJadI1JUsR5bOw== + dependencies: + "@aws-sdk/middleware-sdk-s3" "3.709.0" + "@aws-sdk/types" "3.709.0" + "@smithy/protocol-http" "^4.1.8" + "@smithy/signature-v4" "^4.2.4" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@aws-sdk/token-providers@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/token-providers/-/token-providers-3.709.0.tgz#56305ab187660a711fd172c329dc953ca754fa80" + integrity sha512-q5Ar6k71nci43IbULFgC8a89d/3EHpmd7HvBzqVGRcHnoPwh8eZDBfbBXKH83NGwcS1qPSRYiDbVfeWPm4/1jA== + dependencies: + "@aws-sdk/types" "3.709.0" + "@smithy/property-provider" "^3.1.11" + "@smithy/shared-ini-file-loader" "^3.1.12" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@aws-sdk/types@3.709.0", "@aws-sdk/types@^3.222.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/types/-/types-3.709.0.tgz#f8d7ab07e253d3ed0e3b360e09fc67c7430a73b9" + integrity sha512-ArtLTMxgjf13Kfu3gWH3Ez9Q5TkDdcRZUofpKH3pMGB/C6KAbeSCtIIDKfoRTUABzyGlPyCrZdnFjKyH+ypIpg== + dependencies: + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@aws-sdk/util-arn-parser@3.693.0": + version "3.693.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-arn-parser/-/util-arn-parser-3.693.0.tgz#8dae27eb822ab4f88be28bb3c0fc11f1f13d3948" + integrity sha512-WC8x6ca+NRrtpAH64rWu+ryDZI3HuLwlEr8EU6/dbC/pt+r/zC0PBoC15VEygUaBA+isppCikQpGyEDu0Yj7gQ== + dependencies: + tslib "^2.6.2" + +"@aws-sdk/util-endpoints@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-endpoints/-/util-endpoints-3.709.0.tgz#32dfe339d78b699ada68392bbb3bec25441bae5c" + integrity sha512-Mbc7AtL5WGCTKC16IGeUTz+sjpC3ptBda2t0CcK0kMVw3THDdcSq6ZlNKO747cNqdbwUvW34oHteUiHv4/z88Q== + dependencies: + "@aws-sdk/types" "3.709.0" + "@smithy/types" "^3.7.2" + "@smithy/util-endpoints" "^2.1.7" + tslib "^2.6.2" + +"@aws-sdk/util-locate-window@^3.0.0": + version "3.693.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-locate-window/-/util-locate-window-3.693.0.tgz#1160f6d055cf074ca198eb8ecf89b6311537ad6c" + integrity sha512-ttrag6haJLWABhLqtg1Uf+4LgHWIMOVSYL+VYZmAp2v4PUGOwWmWQH0Zk8RM7YuQcLfH/EoR72/Yxz6A4FKcuw== + dependencies: + tslib "^2.6.2" + +"@aws-sdk/util-user-agent-browser@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.709.0.tgz#ad6e867bdd348923ec10ddd6c37740ce0986cd8f" + integrity sha512-/rL2GasJzdTWUURCQKFldw2wqBtY4k4kCiA2tVZSKg3y4Ey7zO34SW8ebaeCE2/xoWOyLR2/etdKyphoo4Zrtg== + dependencies: + "@aws-sdk/types" "3.709.0" + "@smithy/types" "^3.7.2" + bowser "^2.11.0" + tslib "^2.6.2" + +"@aws-sdk/util-user-agent-node@3.712.0": + version "3.712.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.712.0.tgz#7634627775e0993eace70dea1dd915122f1a053f" + integrity sha512-26X21bZ4FWsVpqs33uOXiB60TOWQdVlr7T7XONDFL/XN7GEpUJkWuuIB4PTok6VOmh1viYcdxZQqekXPuzXexQ== + dependencies: + "@aws-sdk/middleware-user-agent" "3.709.0" + "@aws-sdk/types" "3.709.0" + "@smithy/node-config-provider" "^3.1.12" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@aws-sdk/xml-builder@3.709.0": + version "3.709.0" + resolved "https://registry.yarnpkg.com/@aws-sdk/xml-builder/-/xml-builder-3.709.0.tgz#5841faa1e78afcea064557a1a56709978b325758" + integrity sha512-2GPCwlNxeHspoK/Mc8nbk9cBOkSpp3j2SJUQmFnyQK6V/pR6II2oPRyZkMomug1Rc10hqlBHByMecq4zhV2uUw== + dependencies: + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + "@babel/code-frame@^7.0.0", "@babel/code-frame@^7.23.5", "@babel/code-frame@^7.24.2": version "7.24.2" resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.24.2.tgz" @@ -1415,6 +2017,496 @@ resolved "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz" integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== +"@smithy/abort-controller@^3.1.9": + version "3.1.9" + resolved "https://registry.yarnpkg.com/@smithy/abort-controller/-/abort-controller-3.1.9.tgz#47d323f754136a489e972d7fd465d534d72fcbff" + integrity sha512-yiW0WI30zj8ZKoSYNx90no7ugVn3khlyH/z5W8qtKBtVE6awRALbhSG+2SAHA1r6bO/6M9utxYKVZ3PCJ1rWxw== + dependencies: + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/chunked-blob-reader-native@^3.0.1": + version "3.0.1" + resolved "https://registry.yarnpkg.com/@smithy/chunked-blob-reader-native/-/chunked-blob-reader-native-3.0.1.tgz#39045ed278ee1b6f4c12715c7565678557274c29" + integrity sha512-VEYtPvh5rs/xlyqpm5NRnfYLZn+q0SRPELbvBV+C/G7IQ+ouTuo+NKKa3ShG5OaFR8NYVMXls9hPYLTvIKKDrQ== + dependencies: + "@smithy/util-base64" "^3.0.0" + tslib "^2.6.2" + +"@smithy/chunked-blob-reader@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@smithy/chunked-blob-reader/-/chunked-blob-reader-4.0.0.tgz#754099909957fb1986c16eb88afad75919d7129d" + integrity sha512-jSqRnZvkT4egkq/7b6/QRCNXmmYVcHwnJldqJ3IhVpQE2atObVJ137xmGeuGFhjFUr8gCEVAOKwSY79OvpbDaQ== + dependencies: + tslib "^2.6.2" + +"@smithy/config-resolver@^3.0.13": + version "3.0.13" + resolved "https://registry.yarnpkg.com/@smithy/config-resolver/-/config-resolver-3.0.13.tgz#653643a77a33d0f5907a5e7582353886b07ba752" + integrity sha512-Gr/qwzyPaTL1tZcq8WQyHhTZREER5R1Wytmz4WnVGL4onA3dNk6Btll55c8Vr58pLdvWZmtG8oZxJTw3t3q7Jg== + dependencies: + "@smithy/node-config-provider" "^3.1.12" + "@smithy/types" "^3.7.2" + "@smithy/util-config-provider" "^3.0.0" + "@smithy/util-middleware" "^3.0.11" + tslib "^2.6.2" + +"@smithy/core@^2.5.5": + version "2.5.5" + resolved "https://registry.yarnpkg.com/@smithy/core/-/core-2.5.5.tgz#c75b15caee9e58c800db3e6b99e9e373532d394a" + integrity sha512-G8G/sDDhXA7o0bOvkc7bgai6POuSld/+XhNnWAbpQTpLv2OZPvyqQ58tLPPlz0bSNsXktldDDREIv1LczFeNEw== + dependencies: + "@smithy/middleware-serde" "^3.0.11" + "@smithy/protocol-http" "^4.1.8" + "@smithy/types" "^3.7.2" + "@smithy/util-body-length-browser" "^3.0.0" + "@smithy/util-middleware" "^3.0.11" + "@smithy/util-stream" "^3.3.2" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/credential-provider-imds@^3.2.8": + version "3.2.8" + resolved "https://registry.yarnpkg.com/@smithy/credential-provider-imds/-/credential-provider-imds-3.2.8.tgz#27ed2747074c86a7d627a98e56f324a65cba88de" + integrity sha512-ZCY2yD0BY+K9iMXkkbnjo+08T2h8/34oHd0Jmh6BZUSZwaaGlGCyBT/3wnS7u7Xl33/EEfN4B6nQr3Gx5bYxgw== + dependencies: + "@smithy/node-config-provider" "^3.1.12" + "@smithy/property-provider" "^3.1.11" + "@smithy/types" "^3.7.2" + "@smithy/url-parser" "^3.0.11" + tslib "^2.6.2" + +"@smithy/eventstream-codec@^3.1.10": + version "3.1.10" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-codec/-/eventstream-codec-3.1.10.tgz#0c1a3457e7a23b71cd71525ceb668f8569a84dad" + integrity sha512-323B8YckSbUH0nMIpXn7HZsAVKHYHFUODa8gG9cHo0ySvA1fr5iWaNT+iIL0UCqUzG6QPHA3BSsBtRQou4mMqQ== + dependencies: + "@aws-crypto/crc32" "5.2.0" + "@smithy/types" "^3.7.2" + "@smithy/util-hex-encoding" "^3.0.0" + tslib "^2.6.2" + +"@smithy/eventstream-serde-browser@^3.0.14": + version "3.0.14" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-3.0.14.tgz#0c3584c7cde2e210aacdfbbd2b57c1d7e2ca3b95" + integrity sha512-kbrt0vjOIihW3V7Cqj1SXQvAI5BR8SnyQYsandva0AOR307cXAc+IhPngxIPslxTLfxwDpNu0HzCAq6g42kCPg== + dependencies: + "@smithy/eventstream-serde-universal" "^3.0.13" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/eventstream-serde-config-resolver@^3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-3.0.11.tgz#5edceba836debea165ea93145231036f6286d67c" + integrity sha512-P2pnEp4n75O+QHjyO7cbw/vsw5l93K/8EWyjNCAAybYwUmj3M+hjSQZ9P5TVdUgEG08ueMAP5R4FkuSkElZ5tQ== + dependencies: + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/eventstream-serde-node@^3.0.13": + version "3.0.13" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-node/-/eventstream-serde-node-3.0.13.tgz#5aebd7b553becee277e411a2b69f6af8c9d7b3a6" + integrity sha512-zqy/9iwbj8Wysmvi7Lq7XFLeDgjRpTbCfwBhJa8WbrylTAHiAu6oQTwdY7iu2lxigbc9YYr9vPv5SzYny5tCXQ== + dependencies: + "@smithy/eventstream-serde-universal" "^3.0.13" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/eventstream-serde-universal@^3.0.13": + version "3.0.13" + resolved "https://registry.yarnpkg.com/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-3.0.13.tgz#609c922ea14a0a3eed23a28ac110344c935704eb" + integrity sha512-L1Ib66+gg9uTnqp/18Gz4MDpJPKRE44geOjOQ2SVc0eiaO5l255ADziATZgjQjqumC7yPtp1XnjHlF1srcwjKw== + dependencies: + "@smithy/eventstream-codec" "^3.1.10" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/fetch-http-handler@^4.1.2": + version "4.1.2" + resolved "https://registry.yarnpkg.com/@smithy/fetch-http-handler/-/fetch-http-handler-4.1.2.tgz#f034ff16416b37d92908a1381ef5fddbf4ef1879" + integrity sha512-R7rU7Ae3ItU4rC0c5mB2sP5mJNbCfoDc8I5XlYjIZnquyUwec7fEo78F6DA3SmgJgkU1qTMcZJuGblxZsl10ZA== + dependencies: + "@smithy/protocol-http" "^4.1.8" + "@smithy/querystring-builder" "^3.0.11" + "@smithy/types" "^3.7.2" + "@smithy/util-base64" "^3.0.0" + tslib "^2.6.2" + +"@smithy/hash-blob-browser@^3.1.10": + version "3.1.10" + resolved "https://registry.yarnpkg.com/@smithy/hash-blob-browser/-/hash-blob-browser-3.1.10.tgz#985e308189c2687a15004152b97506882ffb2b13" + integrity sha512-elwslXOoNunmfS0fh55jHggyhccobFkexLYC1ZeZ1xP2BTSrcIBaHV2b4xUQOdctrSNOpMqOZH1r2XzWTEhyfA== + dependencies: + "@smithy/chunked-blob-reader" "^4.0.0" + "@smithy/chunked-blob-reader-native" "^3.0.1" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/hash-node@^3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@smithy/hash-node/-/hash-node-3.0.11.tgz#99e09ead3fc99c8cd7ca0f254ea0e35714f2a0d3" + integrity sha512-emP23rwYyZhQBvklqTtwetkQlqbNYirDiEEwXl2v0GYWMnCzxst7ZaRAnWuy28njp5kAH54lvkdG37MblZzaHA== + dependencies: + "@smithy/types" "^3.7.2" + "@smithy/util-buffer-from" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/hash-stream-node@^3.1.10": + version "3.1.10" + resolved "https://registry.yarnpkg.com/@smithy/hash-stream-node/-/hash-stream-node-3.1.10.tgz#94716b4556f4ccf2807e605f47bb5b018ed7dfb0" + integrity sha512-olomK/jZQ93OMayW1zfTHwcbwBdhcZOHsyWyiZ9h9IXvc1mCD/VuvzbLb3Gy/qNJwI4MANPLctTp2BucV2oU/Q== + dependencies: + "@smithy/types" "^3.7.2" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/invalid-dependency@^3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@smithy/invalid-dependency/-/invalid-dependency-3.0.11.tgz#8144d7b0af9d34ab5f672e1f674f97f8740bb9ae" + integrity sha512-NuQmVPEJjUX6c+UELyVz8kUx8Q539EDeNwbRyu4IIF8MeV7hUtq1FB3SHVyki2u++5XLMFqngeMKk7ccspnNyQ== + dependencies: + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/is-array-buffer@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz#f84f0d9f9a36601a9ca9381688bd1b726fd39111" + integrity sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA== + dependencies: + tslib "^2.6.2" + +"@smithy/is-array-buffer@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/is-array-buffer/-/is-array-buffer-3.0.0.tgz#9a95c2d46b8768946a9eec7f935feaddcffa5e7a" + integrity sha512-+Fsu6Q6C4RSJiy81Y8eApjEB5gVtM+oFKTffg+jSuwtvomJJrhUJBu2zS8wjXSgH/g1MKEWrzyChTBe6clb5FQ== + dependencies: + tslib "^2.6.2" + +"@smithy/md5-js@^3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@smithy/md5-js/-/md5-js-3.0.11.tgz#27e4dab616348ff94aed24dc75e4017c582df40f" + integrity sha512-3NM0L3i2Zm4bbgG6Ymi9NBcxXhryi3uE8fIfHJZIOfZVxOkGdjdgjR9A06SFIZCfnEIWKXZdm6Yq5/aPXFFhsQ== + dependencies: + "@smithy/types" "^3.7.2" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/middleware-content-length@^3.0.13": + version "3.0.13" + resolved "https://registry.yarnpkg.com/@smithy/middleware-content-length/-/middleware-content-length-3.0.13.tgz#6e08fe52739ac8fb3996088e0f8837e4b2ea187f" + integrity sha512-zfMhzojhFpIX3P5ug7jxTjfUcIPcGjcQYzB9t+rv0g1TX7B0QdwONW+ATouaLoD7h7LOw/ZlXfkq4xJ/g2TrIw== + dependencies: + "@smithy/protocol-http" "^4.1.8" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/middleware-endpoint@^3.2.5": + version "3.2.5" + resolved "https://registry.yarnpkg.com/@smithy/middleware-endpoint/-/middleware-endpoint-3.2.5.tgz#bdcfdf1f342cf933b0b8a709996f9a8fbb8148f4" + integrity sha512-VhJNs/s/lyx4weiZdXSloBgoLoS8osV0dKIain8nGmx7of3QFKu5BSdEuk1z/U8x9iwes1i+XCiNusEvuK1ijg== + dependencies: + "@smithy/core" "^2.5.5" + "@smithy/middleware-serde" "^3.0.11" + "@smithy/node-config-provider" "^3.1.12" + "@smithy/shared-ini-file-loader" "^3.1.12" + "@smithy/types" "^3.7.2" + "@smithy/url-parser" "^3.0.11" + "@smithy/util-middleware" "^3.0.11" + tslib "^2.6.2" + +"@smithy/middleware-retry@^3.0.30": + version "3.0.30" + resolved "https://registry.yarnpkg.com/@smithy/middleware-retry/-/middleware-retry-3.0.30.tgz#2580322d0d28ad782b5b8c07c150b14efdc3b2f9" + integrity sha512-6323RL2BvAR3VQpTjHpa52kH/iSHyxd/G9ohb2MkBk2Ucu+oMtRXT8yi7KTSIS9nb58aupG6nO0OlXnQOAcvmQ== + dependencies: + "@smithy/node-config-provider" "^3.1.12" + "@smithy/protocol-http" "^4.1.8" + "@smithy/service-error-classification" "^3.0.11" + "@smithy/smithy-client" "^3.5.0" + "@smithy/types" "^3.7.2" + "@smithy/util-middleware" "^3.0.11" + "@smithy/util-retry" "^3.0.11" + tslib "^2.6.2" + uuid "^9.0.1" + +"@smithy/middleware-serde@^3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@smithy/middleware-serde/-/middleware-serde-3.0.11.tgz#c7d54e0add4f83e05c6878a011fc664e21022f12" + integrity sha512-KzPAeySp/fOoQA82TpnwItvX8BBURecpx6ZMu75EZDkAcnPtO6vf7q4aH5QHs/F1s3/snQaSFbbUMcFFZ086Mw== + dependencies: + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/middleware-stack@^3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@smithy/middleware-stack/-/middleware-stack-3.0.11.tgz#453af2096924e4064d9da4e053cfdf65d9a36acc" + integrity sha512-1HGo9a6/ikgOMrTrWL/WiN9N8GSVYpuRQO5kjstAq4CvV59bjqnh7TbdXGQ4vxLD3xlSjfBjq5t1SOELePsLnA== + dependencies: + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/node-config-provider@^3.1.12": + version "3.1.12" + resolved "https://registry.yarnpkg.com/@smithy/node-config-provider/-/node-config-provider-3.1.12.tgz#1b1d674fc83f943dc7b3017e37f16f374e878a6c" + integrity sha512-O9LVEu5J/u/FuNlZs+L7Ikn3lz7VB9hb0GtPT9MQeiBmtK8RSY3ULmsZgXhe6VAlgTw0YO+paQx4p8xdbs43vQ== + dependencies: + "@smithy/property-provider" "^3.1.11" + "@smithy/shared-ini-file-loader" "^3.1.12" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/node-http-handler@^3.3.2": + version "3.3.2" + resolved "https://registry.yarnpkg.com/@smithy/node-http-handler/-/node-http-handler-3.3.2.tgz#b34685863b74dabdaf7860aa81b42d0d5437c7e0" + integrity sha512-t4ng1DAd527vlxvOfKFYEe6/QFBcsj7WpNlWTyjorwXXcKw3XlltBGbyHfSJ24QT84nF+agDha9tNYpzmSRZPA== + dependencies: + "@smithy/abort-controller" "^3.1.9" + "@smithy/protocol-http" "^4.1.8" + "@smithy/querystring-builder" "^3.0.11" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/property-provider@^3.1.11": + version "3.1.11" + resolved "https://registry.yarnpkg.com/@smithy/property-provider/-/property-provider-3.1.11.tgz#161cf1c2a2ada361e417382c57f5ba6fbca8acad" + integrity sha512-I/+TMc4XTQ3QAjXfOcUWbSS073oOEAxgx4aZy8jHaf8JQnRkq2SZWw8+PfDtBvLUjcGMdxl+YwtzWe6i5uhL/A== + dependencies: + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/protocol-http@^4.1.8": + version "4.1.8" + resolved "https://registry.yarnpkg.com/@smithy/protocol-http/-/protocol-http-4.1.8.tgz#0461758671335f65e8ff3fc0885ab7ed253819c9" + integrity sha512-hmgIAVyxw1LySOwkgMIUN0kjN8TG9Nc85LJeEmEE/cNEe2rkHDUWhnJf2gxcSRFLWsyqWsrZGw40ROjUogg+Iw== + dependencies: + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/querystring-builder@^3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@smithy/querystring-builder/-/querystring-builder-3.0.11.tgz#2ed04adbe725671824c5613d0d6f9376d791a909" + integrity sha512-u+5HV/9uJaeLj5XTb6+IEF/dokWWkEqJ0XiaRRogyREmKGUgZnNecLucADLdauWFKUNbQfulHFEZEdjwEBjXRg== + dependencies: + "@smithy/types" "^3.7.2" + "@smithy/util-uri-escape" "^3.0.0" + tslib "^2.6.2" + +"@smithy/querystring-parser@^3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@smithy/querystring-parser/-/querystring-parser-3.0.11.tgz#9d3177ea19ce8462f18d9712b395239e1ca1f969" + integrity sha512-Je3kFvCsFMnso1ilPwA7GtlbPaTixa3WwC+K21kmMZHsBEOZYQaqxcMqeFFoU7/slFjKDIpiiPydvdJm8Q/MCw== + dependencies: + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/service-error-classification@^3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@smithy/service-error-classification/-/service-error-classification-3.0.11.tgz#d3d7fc0aacd2e60d022507367e55c7939e5bcb8a" + integrity sha512-QnYDPkyewrJzCyaeI2Rmp7pDwbUETe+hU8ADkXmgNusO1bgHBH7ovXJiYmba8t0fNfJx75fE8dlM6SEmZxheog== + dependencies: + "@smithy/types" "^3.7.2" + +"@smithy/shared-ini-file-loader@^3.1.12": + version "3.1.12" + resolved "https://registry.yarnpkg.com/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-3.1.12.tgz#d98b1b663eb18935ce2cbc79024631d34f54042a" + integrity sha512-1xKSGI+U9KKdbG2qDvIR9dGrw3CNx+baqJfyr0igKEpjbHL5stsqAesYBzHChYHlelWtb87VnLWlhvfCz13H8Q== + dependencies: + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/signature-v4@^4.2.4": + version "4.2.4" + resolved "https://registry.yarnpkg.com/@smithy/signature-v4/-/signature-v4-4.2.4.tgz#3501d3d09fd82768867bfc00a7be4bad62f62f4d" + integrity sha512-5JWeMQYg81TgU4cG+OexAWdvDTs5JDdbEZx+Qr1iPbvo91QFGzjy0IkXAKaXUHqmKUJgSHK0ZxnCkgZpzkeNTA== + dependencies: + "@smithy/is-array-buffer" "^3.0.0" + "@smithy/protocol-http" "^4.1.8" + "@smithy/types" "^3.7.2" + "@smithy/util-hex-encoding" "^3.0.0" + "@smithy/util-middleware" "^3.0.11" + "@smithy/util-uri-escape" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/smithy-client@^3.5.0": + version "3.5.0" + resolved "https://registry.yarnpkg.com/@smithy/smithy-client/-/smithy-client-3.5.0.tgz#65cff262801b009998c1196764ee69929ee06f8a" + integrity sha512-Y8FeOa7gbDfCWf7njrkoRATPa5eNLUEjlJS5z5rXatYuGkCb80LbHcu8AQR8qgAZZaNHCLyo2N+pxPsV7l+ivg== + dependencies: + "@smithy/core" "^2.5.5" + "@smithy/middleware-endpoint" "^3.2.5" + "@smithy/middleware-stack" "^3.0.11" + "@smithy/protocol-http" "^4.1.8" + "@smithy/types" "^3.7.2" + "@smithy/util-stream" "^3.3.2" + tslib "^2.6.2" + +"@smithy/types@^3.7.2": + version "3.7.2" + resolved "https://registry.yarnpkg.com/@smithy/types/-/types-3.7.2.tgz#05cb14840ada6f966de1bf9a9c7dd86027343e10" + integrity sha512-bNwBYYmN8Eh9RyjS1p2gW6MIhSO2rl7X9QeLM8iTdcGRP+eDiIWDt66c9IysCc22gefKszZv+ubV9qZc7hdESg== + dependencies: + tslib "^2.6.2" + +"@smithy/url-parser@^3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@smithy/url-parser/-/url-parser-3.0.11.tgz#e5f5ffabfb6230159167cf4cc970705fca6b8b2d" + integrity sha512-TmlqXkSk8ZPhfc+SQutjmFr5FjC0av3GZP4B/10caK1SbRwe/v+Wzu/R6xEKxoNqL+8nY18s1byiy6HqPG37Aw== + dependencies: + "@smithy/querystring-parser" "^3.0.11" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/util-base64@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-base64/-/util-base64-3.0.0.tgz#f7a9a82adf34e27a72d0719395713edf0e493017" + integrity sha512-Kxvoh5Qtt0CDsfajiZOCpJxgtPHXOKwmM+Zy4waD43UoEMA+qPxxa98aE/7ZhdnBFZFXMOiBR5xbcaMhLtznQQ== + dependencies: + "@smithy/util-buffer-from" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/util-body-length-browser@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-body-length-browser/-/util-body-length-browser-3.0.0.tgz#86ec2f6256310b4845a2f064e2f571c1ca164ded" + integrity sha512-cbjJs2A1mLYmqmyVl80uoLTJhAcfzMOyPgjwAYusWKMdLeNtzmMz9YxNl3/jRLoxSS3wkqkf0jwNdtXWtyEBaQ== + dependencies: + tslib "^2.6.2" + +"@smithy/util-body-length-node@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-body-length-node/-/util-body-length-node-3.0.0.tgz#99a291bae40d8932166907fe981d6a1f54298a6d" + integrity sha512-Tj7pZ4bUloNUP6PzwhN7K386tmSmEET9QtQg0TgdNOnxhZvCssHji+oZTUIuzxECRfG8rdm2PMw2WCFs6eIYkA== + dependencies: + tslib "^2.6.2" + +"@smithy/util-buffer-from@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz#6fc88585165ec73f8681d426d96de5d402021e4b" + integrity sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA== + dependencies: + "@smithy/is-array-buffer" "^2.2.0" + tslib "^2.6.2" + +"@smithy/util-buffer-from@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-buffer-from/-/util-buffer-from-3.0.0.tgz#559fc1c86138a89b2edaefc1e6677780c24594e3" + integrity sha512-aEOHCgq5RWFbP+UDPvPot26EJHjOC+bRgse5A8V3FSShqd5E5UN4qc7zkwsvJPPAVsf73QwYcHN1/gt/rtLwQA== + dependencies: + "@smithy/is-array-buffer" "^3.0.0" + tslib "^2.6.2" + +"@smithy/util-config-provider@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-config-provider/-/util-config-provider-3.0.0.tgz#62c6b73b22a430e84888a8f8da4b6029dd5b8efe" + integrity sha512-pbjk4s0fwq3Di/ANL+rCvJMKM5bzAQdE5S/6RL5NXgMExFAi6UgQMPOm5yPaIWPpr+EOXKXRonJ3FoxKf4mCJQ== + dependencies: + tslib "^2.6.2" + +"@smithy/util-defaults-mode-browser@^3.0.30": + version "3.0.30" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-3.0.30.tgz#6c0d95af3f15bef8f1fe3f6217cc4f5ba8df5554" + integrity sha512-nLuGmgfcr0gzm64pqF2UT4SGWVG8UGviAdayDlVzJPNa6Z4lqvpDzdRXmLxtOdEjVlTOEdpZ9dd3ZMMu488mzg== + dependencies: + "@smithy/property-provider" "^3.1.11" + "@smithy/smithy-client" "^3.5.0" + "@smithy/types" "^3.7.2" + bowser "^2.11.0" + tslib "^2.6.2" + +"@smithy/util-defaults-mode-node@^3.0.30": + version "3.0.30" + resolved "https://registry.yarnpkg.com/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-3.0.30.tgz#33cdb02f90944b9ff221e2f8e0904a63ac1e335f" + integrity sha512-OD63eWoH68vp75mYcfYyuVH+p7Li/mY4sYOROnauDrtObo1cS4uWfsy/zhOTW8F8ZPxQC1ZXZKVxoxvMGUv2Ow== + dependencies: + "@smithy/config-resolver" "^3.0.13" + "@smithy/credential-provider-imds" "^3.2.8" + "@smithy/node-config-provider" "^3.1.12" + "@smithy/property-provider" "^3.1.11" + "@smithy/smithy-client" "^3.5.0" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/util-endpoints@^2.1.7": + version "2.1.7" + resolved "https://registry.yarnpkg.com/@smithy/util-endpoints/-/util-endpoints-2.1.7.tgz#a088ebfab946a7219dd4763bfced82709894b82d" + integrity sha512-tSfcqKcN/Oo2STEYCABVuKgJ76nyyr6skGl9t15hs+YaiU06sgMkN7QYjo0BbVw+KT26zok3IzbdSOksQ4YzVw== + dependencies: + "@smithy/node-config-provider" "^3.1.12" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/util-hex-encoding@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-hex-encoding/-/util-hex-encoding-3.0.0.tgz#32938b33d5bf2a15796cd3f178a55b4155c535e6" + integrity sha512-eFndh1WEK5YMUYvy3lPlVmYY/fZcQE1D8oSf41Id2vCeIkKJXPcYDCZD+4+xViI6b1XSd7tE+s5AmXzz5ilabQ== + dependencies: + tslib "^2.6.2" + +"@smithy/util-middleware@^3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@smithy/util-middleware/-/util-middleware-3.0.11.tgz#2ab5c17266b42c225e62befcffb048afa682b5bf" + integrity sha512-dWpyc1e1R6VoXrwLoLDd57U1z6CwNSdkM69Ie4+6uYh2GC7Vg51Qtan7ITzczuVpqezdDTKJGJB95fFvvjU/ow== + dependencies: + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/util-retry@^3.0.11": + version "3.0.11" + resolved "https://registry.yarnpkg.com/@smithy/util-retry/-/util-retry-3.0.11.tgz#d267e5ccb290165cee69732547fea17b695a7425" + integrity sha512-hJUC6W7A3DQgaee3Hp9ZFcOxVDZzmBIRBPlUAk8/fSOEl7pE/aX7Dci0JycNOnm9Mfr0KV2XjIlUOcGWXQUdVQ== + dependencies: + "@smithy/service-error-classification" "^3.0.11" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + +"@smithy/util-stream@^3.3.2": + version "3.3.2" + resolved "https://registry.yarnpkg.com/@smithy/util-stream/-/util-stream-3.3.2.tgz#daeea26397e8541cf2499ce65bf0b8d528cba421" + integrity sha512-sInAqdiVeisUGYAv/FrXpmJ0b4WTFmciTRqzhb7wVuem9BHvhIG7tpiYHLDWrl2stOokNZpTTGqz3mzB2qFwXg== + dependencies: + "@smithy/fetch-http-handler" "^4.1.2" + "@smithy/node-http-handler" "^3.3.2" + "@smithy/types" "^3.7.2" + "@smithy/util-base64" "^3.0.0" + "@smithy/util-buffer-from" "^3.0.0" + "@smithy/util-hex-encoding" "^3.0.0" + "@smithy/util-utf8" "^3.0.0" + tslib "^2.6.2" + +"@smithy/util-uri-escape@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-uri-escape/-/util-uri-escape-3.0.0.tgz#e43358a78bf45d50bb736770077f0f09195b6f54" + integrity sha512-LqR7qYLgZTD7nWLBecUi4aqolw8Mhza9ArpNEQ881MJJIU2sE5iHCK6TdyqqzcDLy0OPe10IY4T8ctVdtynubg== + dependencies: + tslib "^2.6.2" + +"@smithy/util-utf8@^2.0.0": + version "2.3.0" + resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-2.3.0.tgz#dd96d7640363259924a214313c3cf16e7dd329c5" + integrity sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A== + dependencies: + "@smithy/util-buffer-from" "^2.2.0" + tslib "^2.6.2" + +"@smithy/util-utf8@^3.0.0": + version "3.0.0" + resolved "https://registry.yarnpkg.com/@smithy/util-utf8/-/util-utf8-3.0.0.tgz#1a6a823d47cbec1fd6933e5fc87df975286d9d6a" + integrity sha512-rUeT12bxFnplYDe815GXbq/oixEGHfRFFtcTF3YdDi/JaENIM6aSYYLJydG83UNzLXeRI5K8abYd/8Sp/QM0kA== + dependencies: + "@smithy/util-buffer-from" "^3.0.0" + tslib "^2.6.2" + +"@smithy/util-waiter@^3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@smithy/util-waiter/-/util-waiter-3.2.0.tgz#1e52f870e77d2e5572025f7606053e6ff00df93d" + integrity sha512-PpjSboaDUE6yl+1qlg3Si57++e84oXdWGbuFUSAciXsVfEZJJJupR2Nb0QuXHiunt2vGR+1PTizOMvnUPaG2Qg== + dependencies: + "@smithy/abort-controller" "^3.1.9" + "@smithy/types" "^3.7.2" + tslib "^2.6.2" + "@sqltools/formatter@^1.2.5": version "1.2.5" resolved "https://registry.npmjs.org/@sqltools/formatter/-/formatter-1.2.5.tgz" @@ -2508,6 +3600,11 @@ boolean@^3.0.1: resolved "https://registry.yarnpkg.com/boolean/-/boolean-3.2.0.tgz#9e5294af4e98314494cbb17979fa54ca159f116b" integrity sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw== +bowser@^2.11.0: + version "2.11.0" + resolved "https://registry.yarnpkg.com/bowser/-/bowser-2.11.0.tgz#5ca3c35757a7aa5771500c70a73a9f91ef420a8f" + integrity sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -3896,6 +4993,13 @@ fast-levenshtein@^2.0.6: resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== +fast-xml-parser@4.4.1: + version "4.4.1" + resolved "https://registry.yarnpkg.com/fast-xml-parser/-/fast-xml-parser-4.4.1.tgz#86dbf3f18edf8739326447bcaac31b4ae7f6514f" + integrity sha512-xkjOecfnKGkSsOwtZ5Pz7Us/T6mrbPQrq0nh+aCO5V9nk5NLWmasAHumTKjiPJPWANe+kAZ84Jc8ooJkzZ88Sw== + dependencies: + strnum "^1.0.5" + fastq@^1.6.0: version "1.17.1" resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" @@ -6902,6 +8006,11 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ== +strnum@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/strnum/-/strnum-1.0.5.tgz#5c4e829fe15ad4ff0d20c3db5ac97b73c9b072db" + integrity sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA== + strtok3@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-9.0.1.tgz#7e3d7bbd2b829c9def6a7bb90d82e240abdd32be" @@ -7383,7 +8492,7 @@ util-deprecate@^1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -uuid@^9.0.0: +uuid@^9.0.0, uuid@^9.0.1: version "9.0.1" resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.1.tgz#e188d4c8853cc722220392c424cd637f32293f30" integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== From 5d5ce092b75247feb43ae72c0a942699085e514c Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Fri, 20 Dec 2024 17:09:56 +0000 Subject: [PATCH 02/18] feat: adding source search --- .../src/pages/catalogue/catalogue.tsx | 34 ++----------------- .../src/pages/catalogue/filter-section.tsx | 12 ++++--- 2 files changed, 10 insertions(+), 36 deletions(-) diff --git a/src/renderer/src/pages/catalogue/catalogue.tsx b/src/renderer/src/pages/catalogue/catalogue.tsx index b2e4588a..534af11d 100644 --- a/src/renderer/src/pages/catalogue/catalogue.tsx +++ b/src/renderer/src/pages/catalogue/catalogue.tsx @@ -298,38 +298,8 @@ export default function Catalogue() { dispatch(setSearch({ tags: [...filters.tags, value] })); } }} - items={[ - "Action", - "Strategy", - "RPG", - "Casual", - "Racing", - "Sports", - "Indie", - "Adventure", - "Simulation", - "Massively Multiplayer", - "Free to Play", - "Accounting", - "Animation & Modeling", - "Audio Production", - "Design & Illustration", - "Education", - "Photo Editing", - "Software Training", - "Utilities", - "Video Production", - "Web Publishing", - "Game Development", - "Early Access", - "Sexual Content", - "Nudity", - "Violent", - "Gore", - "Documentary", - "Tutorial", - ].map((genre) => ({ - label: genre, + items={[1, 2, 3, 4, 5].map((genre) => ({ + label: genre.toString(), value: genre, checked: filters.tags.includes(genre), }))} diff --git a/src/renderer/src/pages/catalogue/filter-section.tsx b/src/renderer/src/pages/catalogue/filter-section.tsx index e56f1dd6..1cb9d9fc 100644 --- a/src/renderer/src/pages/catalogue/filter-section.tsx +++ b/src/renderer/src/pages/catalogue/filter-section.tsx @@ -2,17 +2,21 @@ import { CheckboxField, TextField } from "@renderer/components"; import { useFormat } from "@renderer/hooks"; import { useCallback, useMemo, useState } from "react"; -export interface FilterSectionProps { +export interface FilterSectionProps { title: string; items: { label: string; - value: string; + value: T; checked: boolean; }[]; - onSelect: (value: string) => void; + onSelect: (value: T) => void; } -export function FilterSection({ title, items, onSelect }: FilterSectionProps) { +export function FilterSection({ + title, + items, + onSelect, +}: FilterSectionProps) { const [search, setSearch] = useState(""); const filteredItems = useMemo(() => { From eb744346f0d3e1a91966f3e4f9f0e0452898a43f Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Fri, 20 Dec 2024 17:10:33 +0000 Subject: [PATCH 03/18] feat: adding source search --- src/types/index.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/types/index.ts b/src/types/index.ts index 48756fcb..f048b3f1 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -288,6 +288,7 @@ export interface DownloadSource { url: string; repackCount: number; status: DownloadSourceStatus; + objectIds: string[]; downloadCount: number; fingerprint: string; etag: string | null; From ede1dbc5e73044ad8bb6ac93ecad3fba941fba6d Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Fri, 20 Dec 2024 17:12:59 +0000 Subject: [PATCH 04/18] feat: adding source search --- package.json | 1 - yarn.lock | 5 ----- 2 files changed, 6 deletions(-) diff --git a/package.json b/package.json index 43c8ee05..55a7f3bb 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,6 @@ "electron-log": "^5.2.4", "electron-updater": "^6.3.9", "file-type": "^19.6.0", - "flexsearch": "^0.7.43", "i18next": "^23.11.2", "i18next-browser-languagedetector": "^7.2.1", "jsdom": "^24.0.0", diff --git a/yarn.lock b/yarn.lock index b96d134a..b0ef653b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5140,11 +5140,6 @@ flatted@^3.2.9: resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== -flexsearch@^0.7.43: - version "0.7.43" - resolved "https://registry.yarnpkg.com/flexsearch/-/flexsearch-0.7.43.tgz#34f89b36278a466ce379c5bf6fb341965ed3f16c" - integrity sha512-c5o/+Um8aqCSOXGcZoqZOm+NqtVwNsvVpWv6lfmSclU954O3wvQKxxK8zj74fPaSJbXpSLTs4PRhh+wnoCXnKg== - follow-redirects@^1.15.6: version "1.15.9" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" From 3be9053b76386481bbd4952102d8555f9d9412b3 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Fri, 20 Dec 2024 17:33:13 +0000 Subject: [PATCH 05/18] feat: adding pacman --- scripts/upload-build.cjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/upload-build.cjs b/scripts/upload-build.cjs index 9f19ca89..c00b565a 100644 --- a/scripts/upload-build.cjs +++ b/scripts/upload-build.cjs @@ -20,7 +20,7 @@ const s3 = new S3Client({ const dist = path.resolve(__dirname, "..", "dist"); -const extensionsToUpload = [".deb", ".exe"]; +const extensionsToUpload = [".deb", ".exe", ".pacman"]; fs.readdir(dist, async (err, files) => { if (err) throw err; From e78de55fe7d6c36fc6b5338e51ffbb2acebbf09e Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Fri, 20 Dec 2024 18:39:40 +0000 Subject: [PATCH 06/18] feat: adding user tags --- .../src/pages/catalogue/catalogue.tsx | 29 +- .../src/pages/catalogue/filter-section.tsx | 46 +- .../src/pages/catalogue/steam-user-tags.ts | 452 ++++++++++++++++++ .../src/workers/download-sources.worker.ts | 2 +- 4 files changed, 498 insertions(+), 31 deletions(-) create mode 100644 src/renderer/src/pages/catalogue/steam-user-tags.ts diff --git a/src/renderer/src/pages/catalogue/catalogue.tsx b/src/renderer/src/pages/catalogue/catalogue.tsx index 534af11d..cf2ac507 100644 --- a/src/renderer/src/pages/catalogue/catalogue.tsx +++ b/src/renderer/src/pages/catalogue/catalogue.tsx @@ -18,6 +18,7 @@ import { useNavigate, useSearchParams } from "react-router-dom"; import { FilterSection } from "./filter-section"; import { setSearch } from "@renderer/features"; import { useTranslation } from "react-i18next"; +import { steamUserTags } from "./steam-user-tags"; export default function Catalogue() { const inputRef = useRef(null); @@ -142,7 +143,7 @@ export default function Catalogue() { { downloadSources.find( (source) => source.fingerprint === fingerprint - )!.name + )?.name } ))} @@ -204,7 +205,7 @@ export default function Catalogue() { fontSize: 12, }} > - {game.genres.join(", ")} + {game.genres?.join(", ")}
@@ -278,11 +279,13 @@ export default function Catalogue() { "Gore", "Documentary", "Tutorial", - ].map((genre) => ({ - label: genre, - value: genre, - checked: filters.genres.includes(genre), - }))} + ] + .sort() + .map((genre) => ({ + label: genre, + value: genre, + checked: filters.genres.includes(genre), + }))} /> ({ - label: genre.toString(), - value: genre, - checked: filters.tags.includes(genre), - }))} + items={Object.entries(steamUserTags) + .sort(([keyA], [keyB]) => keyA.localeCompare(keyB)) + .map(([key, value]) => ({ + label: key, + value: value, + checked: filters.tags.includes(value), + }))} /> ({ onSelect, }: FilterSectionProps) { const [search, setSearch] = useState(""); + const [showMore, setShowMore] = useState(false); const filteredItems = useMemo(() => { - if (items.length > 10 && search.length > 0) { - return items.filter((item) => - item.label.toLowerCase().includes(search.toLowerCase()) - ); + if (search.length > 0) { + return items + .filter((item) => + item.label.toLowerCase().includes(search.toLowerCase()) + ) + .slice(0, 10); + } + + if (showMore) { + return items; } return items.slice(0, 10); - }, [items, search]); + }, [items, search, showMore]); const onSearch = useCallback((value: string) => { setSearch(value); @@ -70,19 +77,22 @@ export function FilterSection({
))} - + {!search && items.length > 10 && ( + + )} ); diff --git a/src/renderer/src/pages/catalogue/steam-user-tags.ts b/src/renderer/src/pages/catalogue/steam-user-tags.ts new file mode 100644 index 00000000..76721d4f --- /dev/null +++ b/src/renderer/src/pages/catalogue/steam-user-tags.ts @@ -0,0 +1,452 @@ +export const steamUserTags = { + Atmospheric: 4166, + Fantasy: 1684, + Relaxing: 1654, + Funny: 4136, + "Sci-fi": 3942, + Horror: 1667, + "Family Friendly": 5350, + Retro: 4004, + Survival: 1662, + Mystery: 5716, + Dark: 4342, + Comedy: 1719, + "Psychological Horror": 1721, + Sports: 701, + "Old School": 3916, + Medieval: 4172, + Magic: 4057, + Racing: 699, + Building: 1643, + Management: 12472, + Space: 1755, + Tactical: 1708, + Drama: 5984, + Futuristic: 4295, + Logic: 6129, + Romance: 4947, + Crafting: 1702, + "Dark Fantasy": 4604, + Emotional: 5608, + "Survival Horror": 3978, + "1980s": 7743, + Nature: 30358, + Education: 1036, + "1990's": 6691, + Surreal: 1710, + "Post-apocalyptic": 3835, + War: 1678, + Zombies: 1659, + Historical: 3987, + Stealth: 1687, + Investigation: 8369, + Military: 4168, + "LGBTQ+": 44868, + Cyberpunk: 4115, + "Lore-Rich": 3854, + Detective: 5613, + Aliens: 1673, + Thriller: 4064, + Economy: 4695, + Robots: 5752, + Demons: 9541, + "Dark Humor": 5923, + Psychological: 5186, + Driving: 1644, + Supernatural: 10808, + "Comic Book": 1751, + Modern: 5673, + Psychedelic: 1714, + Dystopian: 5030, + Flight: 15045, + "Artificial Intelligence": 7926, + Loot: 4236, + Memes: 10397, + "Alternate History": 4598, + Cats: 17894, + Parkour: 4036, + Mythology: 16094, + Crime: 6378, + "Game Development": 13906, + Destruction: 5363, + Philosophical: 15277, + Capitalism: 4845, + "Dark Comedy": 19995, + Automation: 255534, + Lovecraftian: 7432, + Noir: 6052, + Swordplay: 4608, + Science: 5794, + Cooking: 3920, + America: 13190, + Dragons: 4046, + "World War II": 4150, + Parody: 4878, + Agriculture: 22602, + Conspiracy: 5372, + Gothic: 3952, + "Martial Arts": 6915, + Mechs: 4821, + Underground: 21006, + Satire: 1651, + Pirates: 1681, + Steampunk: 1777, + Dog: 1638, + "Time Travel": 10679, + Mining: 5981, + Transportation: 10383, + Ninja: 1688, + Vampire: 4018, + Tanks: 13276, + Political: 4853, + Otome: 31579, + Underwater: 9157, + Hunting: 9564, + Fishing: 15564, + Trains: 1616, + Dinosaurs: 5160, + Western: 1647, + Hacking: 5502, + Faith: 180368, + Programming: 5432, + Superhero: 1671, + Politics: 4754, + Assassin: 4376, + Gambling: 16250, + Naval: 6910, + Diplomacy: 6310, + Heist: 1680, + Snow: 9803, + Archery: 13382, + "Cold War": 5179, + Sailing: 13577, + "Football (Soccer)": 1254546, + Foreign: 51306, + Offroad: 7622, + Horses: 6041, + Illuminati: 7478, + Sniper: 7423, + Transhumanism: 4137, + Werewolves: 17015, + Mars: 6702, + Boxing: 12190, + Jet: 92092, + Motorbike: 198913, + Golf: 7038, + Bikes: 123332, + "World War I": 5382, + Rome: 6948, + Submarine: 19780, + Basketball: 1746, + Baseball: 5727, + LEGO: 1736, + Skateboarding: 1753, + "Mini Golf": 22955, + Wrestling: 47827, + "Football (American)": 1254552, + Tennis: 5914, + Pool: 17927, + Skating: 96359, + Cycling: 19568, + Motocross: 15868, + "Warhammer 40K": 12286, + Lemmings: 17337, + Hockey: 324176, + Bowling: 7328, + Snowboarding: 28444, + Skiing: 7309, + BMX: 252854, + ATV: 129761, + Indie: 492, + Action: 19, + Casual: 597, + Adventure: 21, + Simulation: 599, + RPG: 122, + Strategy: 9, + "Action-Adventure": 4106, + Software: 8013, + "2D": 3871, + "3D": 4191, + Colorful: 4305, + "Pixel Graphics": 3964, + Cute: 4726, + "First-Person": 3839, + Anime: 4085, + "Third Person": 1697, + Stylized: 4252, + "Top-Down": 4791, + Realistic: 4175, + Cartoony: 4195, + Minimalist: 4094, + "Hand-drawn": 6815, + VR: 21978, + Cartoon: 4562, + "Text-Based": 31275, + Cinematic: 4145, + "2.5D": 4975, + Isometric: 5851, + Abstract: 4400, + "Split Screen": 10816, + "3D Vision": 29363, + Voxel: 1732, + Beautiful: 5411, + FMV: 18594, + "360 Video": 776177, + "Story Rich": 1742, + Combat: 3993, + Controller: 7481, + "Female Protagonist": 7208, + "Choices Matter": 6426, + "Open World": 1695, + PvP: 1775, + PvE: 6730, + Linear: 7250, + "Multiple Endings": 6971, + Physics: 3968, + "Character Customization": 4747, + "Procedural Generation": 5125, + Tabletop: 17389, + "Turn-Based Combat": 4325, + "Turn-Based Tactics": 14139, + "Hack and Slash": 1646, + "Resource Management": 8945, + "Base-Building": 7332, + "Score Attack": 5154, + Narration: 5094, + Conversation: 15172, + Nonlinear: 6869, + Tutorial: 12057, + "Perma Death": 1759, + "Team-Based": 5711, + Deckbuilding: 32322, + "Inventory Management": 6276, + "Level Editor": 8122, + "Grid-Based Movement": 7569, + Moddable: 1669, + "Class-Based": 4155, + "Vehicular Combat": 11104, + "Gun Customization": 5765, + "6DOF": 4835, + Trading: 4202, + "Bullet Time": 5796, + "Time Manipulation": 6625, + "Quick-Time Events": 4559, + "Dynamic Narration": 9592, + "Hex Grid": 1717, + "Naval Combat": 4994, + "Music-Based Procedural Generation": 8253, + "Asymmetric VR": 856791, + Puzzle: 1664, + Arcade: 1773, + Shooter: 1774, + Platformer: 1625, + "Visual Novel": 3799, + Sandbox: 3810, + "Rogue-like": 1716, + "Action RPG": 4231, + "Point & Click": 1698, + "Action Roguelike": 42804, + "Interactive Fiction": 11014, + "Turn-Based Strategy": 1741, + "Dating Sim": 9551, + JRPG: 4434, + "Party-Based RPG": 10695, + "Walking Simulator": 5900, + "Design & Illustration": 84, + "Card Game": 1666, + "Life Sim": 10235, + Utilities: 87, + "Strategy RPG": 17305, + "Board Game": 1770, + RTS: 1676, + "Tower Defense": 1645, + "Web Publishing": 1038, + "City Builder": 4328, + "Beat 'em up": 4158, + "Automobile Sim": 1100687, + "2D Fighter": 4736, + Rhythm: 1752, + "3D Fighter": 6506, + "Farming Sim": 87918, + "Animation & Modeling": 872, + "e-sports": 5055, + "Grand Strategy": 4364, + "Space Sim": 16598, + "Colony Sim": 220585, + "Word Game": 24003, + "Battle Royale": 176981, + MMORPG: 1754, + "Auto Battler": 1084988, + "Audio Production": 1027, + "Video Production": 784, + "God Game": 5300, + "4X": 1670, + MOBA: 1718, + "Photo Editing": 809, + Trivia: 10437, + "Immersive Sim": 9204, + "Political Sim": 26921, + "Outbreak Sim": 1100686, + "Medical Sim": 1100688, + Short: 4234, + Movie: 4700, + Episodic: 4242, + Gaming: 150626, + Documentary: 15339, + Exploration: 3834, + "2D Platformer": 5379, + FPS: 1663, + "Rogue-lite": 3959, + "Shoot 'Em Up": 4255, + "3D Platformer": 5395, + "Side Scroller": 3798, + "Choose Your Own Adventure": 4486, + "Puzzle-Platformer": 5537, + "Hidden Object": 1738, + "Bullet Hell": 4885, + "Dungeon Crawler": 1720, + "Top-Down Shooter": 4637, + Clicker: 379975, + "Third-Person Shooter": 3814, + "Precision Platformer": 3877, + "Time Management": 16689, + "Real Time Tactics": 3813, + "Arena Shooter": 5547, + Collectathon: 5652, + "Tactical RPG": 21725, + Idler: 615955, + Wargame: 4684, + Metroidvania: 1628, + Runner: 8666, + "Card Battler": 791774, + "Souls-like": 29482, + CRPG: 4474, + "Creature Collector": 916648, + "Twin Stick Shooter": 4758, + "Match 3": 1665, + "Mystery Dungeon": 198631, + "Hero Shooter": 620519, + "Spectacle fighter": 4777, + "Looter Shooter": 353880, + Solitaire: 13070, + "Combat Racing": 4102, + "Action RTS": 1723, + Sokoban: 1730, + "Trading Card Game": 9271, + Typing: 1674, + "Boomer Shooter": 1023537, + "Traditional Roguelike": 454187, + "On-Rails Shooter": 56690, + Spelling: 71389, + Roguevania: 922563, + Singleplayer: 4182, + Multiplayer: 3859, + "Co-op": 1685, + "Online Co-Op": 3843, + "Massively Multiplayer": 128, + "Local Multiplayer": 7368, + "Local Co-Op": 3841, + "4 Player Local": 4840, + "Co-op Campaign": 4508, + "Asynchronous Multiplayer": 17770, + "Profile Features Limited": 1003823, + "Demo Available": 21491, + "Adult Content": 65443, + Hentai: 9130, + Fighting: 1743, + Classic: 1693, + Cozy: 97376, + "Open World Survival Craft": 1100689, + Wholesome: 552282, + "Roguelike Deckbuilder": 1091588, + Narrative: 7702, + Immersive: 3934, + "Party Game": 7178, + Party: 7108, + "Escape Room": 769306, + Addictive: 4190, + Nostalgia: 14720, + Farming: 4520, + "Cult Classic": 7782, + Spaceships: 4291, + "Electronic Music": 61357, + Pinball: 6621, + "Social Deduction": 745697, + Ambient: 29855, + Dwarf: 7918, + "Job Simulator": 35079, + Epic: 3965, + "Instrumental Music": 189941, + "Jump Scare": 42089, + "Boss Rush": 11095, + "Rock Music": 337964, + "Tile-Matching": 176733, + Vikings: 11634, + "Extraction Shooter": 1199779, + "8-bit Music": 117648, + "Well-Written": 8461, + Mahjong: 33572, + "Shop Keeper": 91114, + Electronic: 143739, + Birds: 6214, + Dice: 7556, + Musou: 323922, + Fox: 30927, + Coding: 42329, + Elf: 102530, + "Hobby Sim": 1220528, + Cricket: 158638, + Rugby: 49213, + Volleyball: 847164, + Snooker: 363767, + Reboot: 5941, + "Based On A Novel": 3796, + "Free to Play": 113, + "Early Access": 493, + Experimental: 13782, + "Software Training": 1445, + Minigames: 8093, + Remake: 5708, + Sequel: 5230, + Experience: 9994, + Kickstarter: 5153, + Crowdfunded: 7113, + Benchmark: 5407, + "Feature Film": 233824, + Difficult: 4026, + Competitive: 3878, + Unforgiving: 1733, + "Turn-Based": 1677, + "Replay Value": 4711, + "Fast-Paced": 1734, + "Real-Time": 4161, + "Real-Time with Pause": 7107, + "Time Attack": 5390, + "Sexual Content": 12095, + Nudity: 6650, + Mature: 5611, + NSFW: 24904, + "Character Action Game": 3955, + "Villain Protagonist": 11333, + "Silent Protagonist": 15954, + Chess: 4184, + Violent: 4667, + Gore: 4345, + Blood: 5228, + Soundtrack: 7948, + "Great Soundtrack": 1756, + Music: 1621, + "Mouse only": 11123, + "Touch-Friendly": 25085, + "Intentionally Awkward Controls": 14906, + "Voice Control": 27758, + Mod: 5348, + RPGMaker: 5577, + GameMaker: 1649, + "Dungeons & Dragons": 14153, + "Games Workshop": 5310, + TrackIR: 8075, + Hardware: 603297, + "Steam Machine": 348922, +}; diff --git a/src/renderer/src/workers/download-sources.worker.ts b/src/renderer/src/workers/download-sources.worker.ts index a847520a..40665dc9 100644 --- a/src/renderer/src/workers/download-sources.worker.ts +++ b/src/renderer/src/workers/download-sources.worker.ts @@ -77,7 +77,7 @@ const addNewDownloads = async ( const getSteamGames = async () => { const response = await axios.get( - "https://assets.hydralauncher.gg/steam-games-by-letter.json" + `${import.meta.env.RENDERER_VITE_EXTERNAL_RESOURCES_URL}/steam-games-by-letter.json` ); return response.data; From a557bbb94825f3b2537852152eb9e62f8671ab83 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Fri, 20 Dec 2024 18:43:17 +0000 Subject: [PATCH 07/18] feat: adding user tags --- src/renderer/src/pages/catalogue/catalogue.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/renderer/src/pages/catalogue/catalogue.tsx b/src/renderer/src/pages/catalogue/catalogue.tsx index cf2ac507..a99ac050 100644 --- a/src/renderer/src/pages/catalogue/catalogue.tsx +++ b/src/renderer/src/pages/catalogue/catalogue.tsx @@ -23,6 +23,8 @@ import { steamUserTags } from "./steam-user-tags"; export default function Catalogue() { const inputRef = useRef(null); + const abortControllerRef = useRef(null); + const [focused, setFocused] = useState(false); const [searchParams] = useSearchParams(); @@ -43,8 +45,16 @@ export default function Catalogue() { useEffect(() => { setGames([]); + abortControllerRef.current?.abort(); + + const abortController = new AbortController(); + abortControllerRef.current = abortController; window.electron.searchGames(filters).then((games) => { + if (abortController.signal.aborted) { + return; + } + setGames(games); }); }, [filters]); From 3af0ae9f857e953d34e459e0c2fbab7392520152 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Fri, 20 Dec 2024 18:59:51 +0000 Subject: [PATCH 08/18] feat: adding scroll to filters --- .../src/pages/catalogue/filter-section.tsx | 44 ++++++------------- 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/src/renderer/src/pages/catalogue/filter-section.tsx b/src/renderer/src/pages/catalogue/filter-section.tsx index b5df3c47..940394a1 100644 --- a/src/renderer/src/pages/catalogue/filter-section.tsx +++ b/src/renderer/src/pages/catalogue/filter-section.tsx @@ -18,23 +18,16 @@ export function FilterSection({ onSelect, }: FilterSectionProps) { const [search, setSearch] = useState(""); - const [showMore, setShowMore] = useState(false); const filteredItems = useMemo(() => { if (search.length > 0) { - return items - .filter((item) => - item.label.toLowerCase().includes(search.toLowerCase()) - ) - .slice(0, 10); + return items.filter((item) => + item.label.toLowerCase().includes(search.toLowerCase()) + ); } - if (showMore) { - return items; - } - - return items.slice(0, 10); - }, [items, search, showMore]); + return items; + }, [items, search]); const onSearch = useCallback((value: string) => { setSearch(value); @@ -66,7 +59,15 @@ export function FilterSection({ theme="dark" /> -
+
{filteredItems.map((item) => (
({ />
))} - - {!search && items.length > 10 && ( - - )}
); From d3450c5f652abe389332974c7191fa9c91895374 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Fri, 20 Dec 2024 21:28:51 +0000 Subject: [PATCH 09/18] feat: adding link direct from sources --- package.json | 2 + src/main/events/index.ts | 2 + src/preload/index.ts | 2 + src/renderer/src/components/badge/badge.tsx | 6 +- .../checkbox-field/checkbox-field.css.ts | 1 + src/renderer/src/declaration.d.ts | 2 + src/renderer/src/features/catalogue-search.ts | 2 +- .../src/pages/catalogue/catalogue.tsx | 120 +++++++++++++++++- .../src/pages/catalogue/filter-section.tsx | 60 ++++++--- .../settings/settings-download-sources.tsx | 26 +++- yarn.lock | 79 +++++++++++- 11 files changed, 265 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index 55a7f3bb..ee93d418 100644 --- a/package.json +++ b/package.json @@ -62,11 +62,13 @@ "lodash-es": "^4.17.21", "parse-torrent": "^11.0.17", "piscina": "^4.7.0", + "rc-virtual-list": "^3.16.1", "react-hook-form": "^7.53.0", "react-i18next": "^14.1.0", "react-loading-skeleton": "^3.4.0", "react-redux": "^9.1.1", "react-router-dom": "^6.22.3", + "react-virtualized": "^9.22.5", "sound-play": "^1.1.0", "sudo-prompt": "^9.2.1", "tar": "^7.4.3", diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 35863eac..723127e8 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -8,6 +8,8 @@ import "./catalogue/get-random-game"; import "./catalogue/search-games"; import "./catalogue/get-game-stats"; import "./catalogue/get-trending-games"; +import "./catalogue/get-publishers"; +import "./catalogue/get-developers"; import "./hardware/get-disk-free-space"; import "./library/add-game-to-library"; import "./library/create-game-shortcut"; diff --git a/src/preload/index.ts b/src/preload/index.ts index cab56fe2..86414e1a 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -65,6 +65,8 @@ contextBridge.exposeInMainWorld("electron", { listener ); }, + getPublishers: () => ipcRenderer.invoke("getPublishers"), + getDevelopers: () => ipcRenderer.invoke("getDevelopers"), /* User preferences */ getUserPreferences: () => ipcRenderer.invoke("getUserPreferences"), diff --git a/src/renderer/src/components/badge/badge.tsx b/src/renderer/src/components/badge/badge.tsx index 752a33ba..c4819ae3 100644 --- a/src/renderer/src/components/badge/badge.tsx +++ b/src/renderer/src/components/badge/badge.tsx @@ -7,9 +7,5 @@ export interface BadgeProps { } export function Badge({ children }: BadgeProps) { - return ( -
- {children} -
- ); + return
{children}
; } diff --git a/src/renderer/src/components/checkbox-field/checkbox-field.css.ts b/src/renderer/src/components/checkbox-field/checkbox-field.css.ts index 6546d942..b4d3afc8 100644 --- a/src/renderer/src/components/checkbox-field/checkbox-field.css.ts +++ b/src/renderer/src/components/checkbox-field/checkbox-field.css.ts @@ -25,6 +25,7 @@ export const checkbox = recipe({ border: `solid 1px ${vars.color.border}`, minWidth: "20px", minHeight: "20px", + color: vars.color.darkBackground, ":hover": { borderColor: "rgba(255, 255, 255, 0.5)", }, diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index c809b4b3..c8033d7e 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -68,6 +68,8 @@ declare global { shop: GameShop, cb: (achievements: GameAchievement[]) => void ) => () => Electron.IpcRenderer; + getPublishers: () => Promise; + getDevelopers: () => Promise; /* Library */ addGameToLibrary: ( diff --git a/src/renderer/src/features/catalogue-search.ts b/src/renderer/src/features/catalogue-search.ts index 90fd5051..a6318a26 100644 --- a/src/renderer/src/features/catalogue-search.ts +++ b/src/renderer/src/features/catalogue-search.ts @@ -34,4 +34,4 @@ export const catalogueSearchSlice = createSlice({ }, }); -export const { setSearch } = catalogueSearchSlice.actions; +export const { setSearch, clearSearch } = catalogueSearchSlice.actions; diff --git a/src/renderer/src/pages/catalogue/catalogue.tsx b/src/renderer/src/pages/catalogue/catalogue.tsx index a99ac050..9bbcd358 100644 --- a/src/renderer/src/pages/catalogue/catalogue.tsx +++ b/src/renderer/src/pages/catalogue/catalogue.tsx @@ -20,6 +20,14 @@ import { setSearch } from "@renderer/features"; import { useTranslation } from "react-i18next"; import { steamUserTags } from "./steam-user-tags"; +const filterCategoryColors = { + genres: "hsl(262deg 50% 47%)", + tags: "hsl(95deg 50% 20%)", + downloadSourceFingerprints: "hsl(27deg 50% 40%)", + developers: "hsl(340deg 50% 46%)", + publishers: "hsl(200deg 50% 30%)", +}; + export default function Catalogue() { const inputRef = useRef(null); @@ -34,6 +42,8 @@ export default function Catalogue() { const [downloadSources, setDownloadSources] = useState([]); const [games, setGames] = useState([]); + const [publishers, setPublishers] = useState([]); + const [developers, setDevelopers] = useState([]); const filters = useAppSelector((state) => state.catalogueSearch.value); @@ -59,6 +69,16 @@ export default function Catalogue() { }); }, [filters]); + useEffect(() => { + window.electron.getDevelopers().then((developers) => { + setDevelopers(developers); + }); + + window.electron.getPublishers().then((publishers) => { + setPublishers(publishers); + }); + }, []); + const gamesWithRepacks = useMemo(() => { return games.map((game) => { const repacks = getRepacksForObjectId(game.objectId); @@ -148,13 +168,50 @@ export default function Catalogue() { }} >
+ {filters.genres.map((genre) => ( + +
+
+ + {genre} +
+ + ))} + + {filters.tags.map((tag) => ( + +
+ {tag} +
+
+ ))} + {filters.downloadSourceFingerprints.map((fingerprint) => ( - { - downloadSources.find( - (source) => source.fingerprint === fingerprint - )?.name - } +
+
+ + { + downloadSources.find( + (source) => source.fingerprint === fingerprint + )?.name + } +
))}
@@ -248,6 +305,7 @@ export default function Catalogue() {
{ if (filters.genres.includes(value)) { dispatch( @@ -300,6 +358,7 @@ export default function Catalogue() { { if (filters.tags.includes(value)) { dispatch( @@ -322,6 +381,7 @@ export default function Catalogue() { { if (filters.downloadSourceFingerprints.includes(value)) { dispatch( @@ -351,6 +411,56 @@ export default function Catalogue() { ), }))} /> + + { + if (filters.developers.includes(value)) { + dispatch( + setSearch({ + developers: filters.developers.filter( + (developer) => developer !== value + ), + }) + ); + } else { + dispatch( + setSearch({ developers: [...filters.developers, value] }) + ); + } + }} + items={developers.map((developer) => ({ + label: developer, + value: developer, + checked: filters.developers.includes(developer), + }))} + /> + + { + if (filters.publishers.includes(value)) { + dispatch( + setSearch({ + publishers: filters.publishers.filter( + (publisher) => publisher !== value + ), + }) + ); + } else { + dispatch( + setSearch({ publishers: [...filters.publishers, value] }) + ); + } + }} + items={publishers.map((publisher) => ({ + label: publisher, + value: publisher, + checked: filters.publishers.includes(publisher), + }))} + />
diff --git a/src/renderer/src/pages/catalogue/filter-section.tsx b/src/renderer/src/pages/catalogue/filter-section.tsx index 940394a1..ea1b7224 100644 --- a/src/renderer/src/pages/catalogue/filter-section.tsx +++ b/src/renderer/src/pages/catalogue/filter-section.tsx @@ -2,6 +2,8 @@ import { CheckboxField, TextField } from "@renderer/components"; import { useFormat } from "@renderer/hooks"; import { useCallback, useMemo, useState } from "react"; +import List from "rc-virtual-list"; + export interface FilterSectionProps { title: string; items: { @@ -10,11 +12,13 @@ export interface FilterSectionProps { checked: boolean; }[]; onSelect: (value: T) => void; + color: string; } export function FilterSection({ title, items, + color, onSelect, }: FilterSectionProps) { const [search, setSearch] = useState(""); @@ -37,15 +41,25 @@ export function FilterSection({ return (
-

- {title} -

+
+
+

+ {title} +

+
{formatNumber(items.length)} disponíveis @@ -59,25 +73,31 @@ export function FilterSection({ theme="dark" /> -
- {filteredItems.map((item) => ( -
+ {(item) => ( +
onSelect(item.value)} />
- ))} -
+ )} +
); } diff --git a/src/renderer/src/pages/settings/settings-download-sources.tsx b/src/renderer/src/pages/settings/settings-download-sources.tsx index 94846fc5..f5c01a21 100644 --- a/src/renderer/src/pages/settings/settings-download-sources.tsx +++ b/src/renderer/src/pages/settings/settings-download-sources.tsx @@ -7,12 +7,14 @@ import * as styles from "./settings-download-sources.css"; import type { DownloadSource } from "@types"; import { NoEntryIcon, PlusCircleIcon, SyncIcon } from "@primer/octicons-react"; import { AddDownloadSourceModal } from "./add-download-source-modal"; -import { useRepacks, useToast } from "@renderer/hooks"; +import { useAppDispatch, useRepacks, useToast } from "@renderer/hooks"; import { DownloadSourceStatus } from "@shared"; -import { SPACING_UNIT } from "@renderer/theme.css"; +import { SPACING_UNIT, vars } from "@renderer/theme.css"; import { settingsContext } from "@renderer/context"; import { downloadSourcesTable } from "@renderer/dexie"; import { downloadSourcesWorker } from "@renderer/workers"; +import { clearSearch, setSearch } from "@renderer/features"; +import { useNavigate } from "react-router-dom"; export function SettingsDownloadSources() { const [showAddDownloadSourceModal, setShowAddDownloadSourceModal] = @@ -28,6 +30,10 @@ export function SettingsDownloadSources() { const { t } = useTranslation("settings"); const { showSuccessToast } = useToast(); + const dispatch = useAppDispatch(); + + const navigate = useNavigate(); + const { updateRepacks } = useRepacks(); const getDownloadSources = async () => { @@ -96,6 +102,13 @@ export function SettingsDownloadSources() { setShowAddDownloadSourceModal(false); }; + const navigateToCatalogue = (fingerprint: string) => { + dispatch(clearSearch()); + dispatch(setSearch({ downloadSourceFingerprints: [fingerprint] })); + + navigate("/catalogue"); + }; + return ( <> {statusTitle[downloadSource.status]}
-
navigateToCatalogue(downloadSource.fingerprint)} > {t("download_count", { @@ -161,7 +179,7 @@ export function SettingsDownloadSources() { downloadSource.downloadCount.toLocaleString(), })} -
+
Date: Fri, 20 Dec 2024 21:33:17 +0000 Subject: [PATCH 10/18] feat: adding link direct from sources --- src/main/events/catalogue/get-developers.ts | 10 ++++++++++ src/main/events/catalogue/get-publishers.ts | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 src/main/events/catalogue/get-developers.ts create mode 100644 src/main/events/catalogue/get-publishers.ts diff --git a/src/main/events/catalogue/get-developers.ts b/src/main/events/catalogue/get-developers.ts new file mode 100644 index 00000000..76ae566b --- /dev/null +++ b/src/main/events/catalogue/get-developers.ts @@ -0,0 +1,10 @@ +import { HydraApi } from "@main/services"; +import { registerEvent } from "../register-event"; + +const getDevelopers = async (_event: Electron.IpcMainInvokeEvent) => { + return HydraApi.get(`/catalogue/developers`, null, { + needsAuth: false, + }); +}; + +registerEvent("getDevelopers", getDevelopers); diff --git a/src/main/events/catalogue/get-publishers.ts b/src/main/events/catalogue/get-publishers.ts new file mode 100644 index 00000000..3b8fdc5f --- /dev/null +++ b/src/main/events/catalogue/get-publishers.ts @@ -0,0 +1,10 @@ +import { HydraApi } from "@main/services"; +import { registerEvent } from "../register-event"; + +const getPublishers = async (_event: Electron.IpcMainInvokeEvent) => { + return HydraApi.get(`/catalogue/publishers`, null, { + needsAuth: false, + }); +}; + +registerEvent("getPublishers", getPublishers); From b28d34cf661e670d0e9770afaac0565172cdf8c0 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Fri, 20 Dec 2024 22:14:58 +0000 Subject: [PATCH 11/18] feat: increasing number of results --- src/main/events/catalogue/search-games.ts | 2 +- .../src/components/checkbox-field/checkbox-field.css.ts | 3 +++ src/renderer/src/pages/catalogue/catalogue.scss | 1 + src/renderer/src/pages/catalogue/catalogue.tsx | 2 +- src/renderer/src/pages/catalogue/filter-section.tsx | 2 +- 5 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/main/events/catalogue/search-games.ts b/src/main/events/catalogue/search-games.ts index dd010ae6..40b3b033 100644 --- a/src/main/events/catalogue/search-games.ts +++ b/src/main/events/catalogue/search-games.ts @@ -8,7 +8,7 @@ const searchGames = async ( ) => { return HydraApi.post( "/catalogue/search", - { ...payload, take: 12, skip: 0 }, + { ...payload, take: 24, skip: 0 }, { needsAuth: false } ); }; diff --git a/src/renderer/src/components/checkbox-field/checkbox-field.css.ts b/src/renderer/src/components/checkbox-field/checkbox-field.css.ts index b4d3afc8..ce7aead8 100644 --- a/src/renderer/src/components/checkbox-field/checkbox-field.css.ts +++ b/src/renderer/src/components/checkbox-field/checkbox-field.css.ts @@ -51,4 +51,7 @@ export const checkboxInput = style({ export const checkboxLabel = style({ cursor: "pointer", + textOverflow: "ellipsis", + overflow: "hidden", + whiteSpace: "nowrap", }); diff --git a/src/renderer/src/pages/catalogue/catalogue.scss b/src/renderer/src/pages/catalogue/catalogue.scss index ecf99931..902cdcd8 100644 --- a/src/renderer/src/pages/catalogue/catalogue.scss +++ b/src/renderer/src/pages/catalogue/catalogue.scss @@ -100,6 +100,7 @@ border-radius: 4px; padding: 16px; border: 1px solid globals.$border-color; + align-self: flex-start; } &__ai-recommendations-button-text { diff --git a/src/renderer/src/pages/catalogue/catalogue.tsx b/src/renderer/src/pages/catalogue/catalogue.tsx index 9bbcd358..62bb3f79 100644 --- a/src/renderer/src/pages/catalogue/catalogue.tsx +++ b/src/renderer/src/pages/catalogue/catalogue.tsx @@ -404,7 +404,7 @@ export default function Catalogue() { } }} items={downloadSources.map((downloadSource) => ({ - label: `${downloadSource.name} (${downloadSource.objectIds.length})`, + label: downloadSource.name, value: downloadSource.fingerprint, checked: filters.downloadSourceFingerprints.includes( downloadSource.fingerprint diff --git a/src/renderer/src/pages/catalogue/filter-section.tsx b/src/renderer/src/pages/catalogue/filter-section.tsx index ea1b7224..976b92c7 100644 --- a/src/renderer/src/pages/catalogue/filter-section.tsx +++ b/src/renderer/src/pages/catalogue/filter-section.tsx @@ -75,7 +75,7 @@ export function FilterSection({ 10 ? 10 : filteredItems.length)} itemHeight={28} itemKey="value" styles={{ From f37b92261fc5121e0e5a882fd71225fa2f1d0727 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Fri, 20 Dec 2024 22:40:41 +0000 Subject: [PATCH 12/18] feat: adding button to clear selected filters --- .../download-sources/put-download-source.ts | 10 +++++-- .../src/pages/catalogue/catalogue.tsx | 7 +++++ .../src/pages/catalogue/filter-section.tsx | 29 +++++++++++++++++-- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/main/events/download-sources/put-download-source.ts b/src/main/events/download-sources/put-download-source.ts index 3aefd927..72297059 100644 --- a/src/main/events/download-sources/put-download-source.ts +++ b/src/main/events/download-sources/put-download-source.ts @@ -5,9 +5,13 @@ const putDownloadSource = async ( _event: Electron.IpcMainInvokeEvent, objectIds: string[] ) => { - return HydraApi.put<{ fingerprint: string }>("/download-sources", { - objectIds, - }); + return HydraApi.put<{ fingerprint: string }>( + "/download-sources", + { + objectIds, + }, + { needsAuth: false } + ); }; registerEvent("putDownloadSource", putDownloadSource); diff --git a/src/renderer/src/pages/catalogue/catalogue.tsx b/src/renderer/src/pages/catalogue/catalogue.tsx index 62bb3f79..96bb3dd2 100644 --- a/src/renderer/src/pages/catalogue/catalogue.tsx +++ b/src/renderer/src/pages/catalogue/catalogue.tsx @@ -305,6 +305,7 @@ export default function Catalogue() {
dispatch(setSearch({ genres: [] }))} color={filterCategoryColors.genres} onSelect={(value) => { if (filters.genres.includes(value)) { @@ -359,6 +360,7 @@ export default function Catalogue() { dispatch(setSearch({ tags: [] }))} onSelect={(value) => { if (filters.tags.includes(value)) { dispatch( @@ -382,6 +384,9 @@ export default function Catalogue() { + dispatch(setSearch({ downloadSourceFingerprints: [] })) + } onSelect={(value) => { if (filters.downloadSourceFingerprints.includes(value)) { dispatch( @@ -415,6 +420,7 @@ export default function Catalogue() { dispatch(setSearch({ developers: [] }))} onSelect={(value) => { if (filters.developers.includes(value)) { dispatch( @@ -440,6 +446,7 @@ export default function Catalogue() { dispatch(setSearch({ publishers: [] }))} onSelect={(value) => { if (filters.publishers.includes(value)) { dispatch( diff --git a/src/renderer/src/pages/catalogue/filter-section.tsx b/src/renderer/src/pages/catalogue/filter-section.tsx index 976b92c7..8448dd96 100644 --- a/src/renderer/src/pages/catalogue/filter-section.tsx +++ b/src/renderer/src/pages/catalogue/filter-section.tsx @@ -13,6 +13,7 @@ export interface FilterSectionProps { }[]; onSelect: (value: T) => void; color: string; + onClear: () => void; } export function FilterSection({ @@ -20,6 +21,7 @@ export function FilterSection({ items, color, onSelect, + onClear, }: FilterSectionProps) { const [search, setSearch] = useState(""); @@ -33,6 +35,10 @@ export function FilterSection({ return items; }, [items, search]); + const selectedItemsCount = useMemo(() => { + return items.filter((item) => item.checked).length; + }, [items]); + const onSearch = useCallback((value: string) => { setSearch(value); }, []); @@ -61,9 +67,26 @@ export function FilterSection({
- - {formatNumber(items.length)} disponíveis - + {selectedItemsCount > 0 ? ( + + ) : ( + + {formatNumber(items.length)} disponíveis + + )} Date: Sun, 22 Dec 2024 23:38:54 +0000 Subject: [PATCH 13/18] fix: fixing download sources migration --- src/locales/pt-BR/translation.json | 7 +- .../src/components/header/header.css.ts | 15 +- src/renderer/src/components/header/header.tsx | 6 +- .../src/pages/catalogue/catalogue.scss | 16 +- .../src/pages/catalogue/catalogue.tsx | 239 +++++---- .../src/pages/catalogue/steam-user-tags.ts | 452 ------------------ .../src/workers/download-sources.worker.ts | 69 +-- 7 files changed, 204 insertions(+), 600 deletions(-) delete mode 100644 src/renderer/src/pages/catalogue/steam-user-tags.ts diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index a1197c78..fa11ef27 100644 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -279,8 +279,11 @@ "instructions": "Verifique a forma correta de instalar algum deles no seu distro Linux, garantindo assim a execução normal do jogo" }, "catalogue": { - "next_page": "Próxima página", - "previous_page": "Página anterior" + "search": "Pesquisar…", + "developers": "Desenvolvedores", + "genres": "Gêneros", + "tags": "Tags", + "download_sources": "Fontes de download" }, "modal": { "close": "Botão de fechar" diff --git a/src/renderer/src/components/header/header.css.ts b/src/renderer/src/components/header/header.css.ts index a592f35b..903f4a42 100644 --- a/src/renderer/src/components/header/header.css.ts +++ b/src/renderer/src/components/header/header.css.ts @@ -74,9 +74,18 @@ export const search = recipe({ }, }); -export const searchButton = style({ - WebkitAppRegion: "no-drag", -} as ComplexStyleRule); +export const searchButton = recipe({ + base: { + WebkitAppRegion: "no-drag", + } as ComplexStyleRule, + variants: { + hidden: { + true: { + visibility: "hidden", + }, + }, + }, +}); export const section = style({ display: "flex", diff --git a/src/renderer/src/components/header/header.tsx b/src/renderer/src/components/header/header.tsx index eb6a9f3b..2c79b1d4 100644 --- a/src/renderer/src/components/header/header.tsx +++ b/src/renderer/src/components/header/header.tsx @@ -35,6 +35,10 @@ export function Header() { return t(pathTitle[location.pathname]); }, [location.pathname, headerTitle, t]); + const showSearchButton = useMemo(() => { + return location.pathname.startsWith("/catalogue"); + }, [location.pathname]); + const handleBackButtonClick = () => { navigate(-1); }; @@ -71,7 +75,7 @@ export function Header() {
+ {/* - ))} + + )) + )} -
+ {/*
-
+
*/}
- -
dispatch(setSearch({ genres: [] }))} color={filterCategoryColors.genres} onSelect={(value) => { @@ -358,7 +399,7 @@ export default function Catalogue() { /> dispatch(setSearch({ tags: [] }))} onSelect={(value) => { @@ -382,7 +423,7 @@ export default function Catalogue() { /> dispatch(setSearch({ downloadSourceFingerprints: [] })) @@ -418,7 +459,7 @@ export default function Catalogue() { /> dispatch(setSearch({ developers: [] }))} onSelect={(value) => { @@ -444,7 +485,7 @@ export default function Catalogue() { /> dispatch(setSearch({ publishers: [] }))} onSelect={(value) => { diff --git a/src/renderer/src/pages/catalogue/steam-user-tags.ts b/src/renderer/src/pages/catalogue/steam-user-tags.ts deleted file mode 100644 index 76721d4f..00000000 --- a/src/renderer/src/pages/catalogue/steam-user-tags.ts +++ /dev/null @@ -1,452 +0,0 @@ -export const steamUserTags = { - Atmospheric: 4166, - Fantasy: 1684, - Relaxing: 1654, - Funny: 4136, - "Sci-fi": 3942, - Horror: 1667, - "Family Friendly": 5350, - Retro: 4004, - Survival: 1662, - Mystery: 5716, - Dark: 4342, - Comedy: 1719, - "Psychological Horror": 1721, - Sports: 701, - "Old School": 3916, - Medieval: 4172, - Magic: 4057, - Racing: 699, - Building: 1643, - Management: 12472, - Space: 1755, - Tactical: 1708, - Drama: 5984, - Futuristic: 4295, - Logic: 6129, - Romance: 4947, - Crafting: 1702, - "Dark Fantasy": 4604, - Emotional: 5608, - "Survival Horror": 3978, - "1980s": 7743, - Nature: 30358, - Education: 1036, - "1990's": 6691, - Surreal: 1710, - "Post-apocalyptic": 3835, - War: 1678, - Zombies: 1659, - Historical: 3987, - Stealth: 1687, - Investigation: 8369, - Military: 4168, - "LGBTQ+": 44868, - Cyberpunk: 4115, - "Lore-Rich": 3854, - Detective: 5613, - Aliens: 1673, - Thriller: 4064, - Economy: 4695, - Robots: 5752, - Demons: 9541, - "Dark Humor": 5923, - Psychological: 5186, - Driving: 1644, - Supernatural: 10808, - "Comic Book": 1751, - Modern: 5673, - Psychedelic: 1714, - Dystopian: 5030, - Flight: 15045, - "Artificial Intelligence": 7926, - Loot: 4236, - Memes: 10397, - "Alternate History": 4598, - Cats: 17894, - Parkour: 4036, - Mythology: 16094, - Crime: 6378, - "Game Development": 13906, - Destruction: 5363, - Philosophical: 15277, - Capitalism: 4845, - "Dark Comedy": 19995, - Automation: 255534, - Lovecraftian: 7432, - Noir: 6052, - Swordplay: 4608, - Science: 5794, - Cooking: 3920, - America: 13190, - Dragons: 4046, - "World War II": 4150, - Parody: 4878, - Agriculture: 22602, - Conspiracy: 5372, - Gothic: 3952, - "Martial Arts": 6915, - Mechs: 4821, - Underground: 21006, - Satire: 1651, - Pirates: 1681, - Steampunk: 1777, - Dog: 1638, - "Time Travel": 10679, - Mining: 5981, - Transportation: 10383, - Ninja: 1688, - Vampire: 4018, - Tanks: 13276, - Political: 4853, - Otome: 31579, - Underwater: 9157, - Hunting: 9564, - Fishing: 15564, - Trains: 1616, - Dinosaurs: 5160, - Western: 1647, - Hacking: 5502, - Faith: 180368, - Programming: 5432, - Superhero: 1671, - Politics: 4754, - Assassin: 4376, - Gambling: 16250, - Naval: 6910, - Diplomacy: 6310, - Heist: 1680, - Snow: 9803, - Archery: 13382, - "Cold War": 5179, - Sailing: 13577, - "Football (Soccer)": 1254546, - Foreign: 51306, - Offroad: 7622, - Horses: 6041, - Illuminati: 7478, - Sniper: 7423, - Transhumanism: 4137, - Werewolves: 17015, - Mars: 6702, - Boxing: 12190, - Jet: 92092, - Motorbike: 198913, - Golf: 7038, - Bikes: 123332, - "World War I": 5382, - Rome: 6948, - Submarine: 19780, - Basketball: 1746, - Baseball: 5727, - LEGO: 1736, - Skateboarding: 1753, - "Mini Golf": 22955, - Wrestling: 47827, - "Football (American)": 1254552, - Tennis: 5914, - Pool: 17927, - Skating: 96359, - Cycling: 19568, - Motocross: 15868, - "Warhammer 40K": 12286, - Lemmings: 17337, - Hockey: 324176, - Bowling: 7328, - Snowboarding: 28444, - Skiing: 7309, - BMX: 252854, - ATV: 129761, - Indie: 492, - Action: 19, - Casual: 597, - Adventure: 21, - Simulation: 599, - RPG: 122, - Strategy: 9, - "Action-Adventure": 4106, - Software: 8013, - "2D": 3871, - "3D": 4191, - Colorful: 4305, - "Pixel Graphics": 3964, - Cute: 4726, - "First-Person": 3839, - Anime: 4085, - "Third Person": 1697, - Stylized: 4252, - "Top-Down": 4791, - Realistic: 4175, - Cartoony: 4195, - Minimalist: 4094, - "Hand-drawn": 6815, - VR: 21978, - Cartoon: 4562, - "Text-Based": 31275, - Cinematic: 4145, - "2.5D": 4975, - Isometric: 5851, - Abstract: 4400, - "Split Screen": 10816, - "3D Vision": 29363, - Voxel: 1732, - Beautiful: 5411, - FMV: 18594, - "360 Video": 776177, - "Story Rich": 1742, - Combat: 3993, - Controller: 7481, - "Female Protagonist": 7208, - "Choices Matter": 6426, - "Open World": 1695, - PvP: 1775, - PvE: 6730, - Linear: 7250, - "Multiple Endings": 6971, - Physics: 3968, - "Character Customization": 4747, - "Procedural Generation": 5125, - Tabletop: 17389, - "Turn-Based Combat": 4325, - "Turn-Based Tactics": 14139, - "Hack and Slash": 1646, - "Resource Management": 8945, - "Base-Building": 7332, - "Score Attack": 5154, - Narration: 5094, - Conversation: 15172, - Nonlinear: 6869, - Tutorial: 12057, - "Perma Death": 1759, - "Team-Based": 5711, - Deckbuilding: 32322, - "Inventory Management": 6276, - "Level Editor": 8122, - "Grid-Based Movement": 7569, - Moddable: 1669, - "Class-Based": 4155, - "Vehicular Combat": 11104, - "Gun Customization": 5765, - "6DOF": 4835, - Trading: 4202, - "Bullet Time": 5796, - "Time Manipulation": 6625, - "Quick-Time Events": 4559, - "Dynamic Narration": 9592, - "Hex Grid": 1717, - "Naval Combat": 4994, - "Music-Based Procedural Generation": 8253, - "Asymmetric VR": 856791, - Puzzle: 1664, - Arcade: 1773, - Shooter: 1774, - Platformer: 1625, - "Visual Novel": 3799, - Sandbox: 3810, - "Rogue-like": 1716, - "Action RPG": 4231, - "Point & Click": 1698, - "Action Roguelike": 42804, - "Interactive Fiction": 11014, - "Turn-Based Strategy": 1741, - "Dating Sim": 9551, - JRPG: 4434, - "Party-Based RPG": 10695, - "Walking Simulator": 5900, - "Design & Illustration": 84, - "Card Game": 1666, - "Life Sim": 10235, - Utilities: 87, - "Strategy RPG": 17305, - "Board Game": 1770, - RTS: 1676, - "Tower Defense": 1645, - "Web Publishing": 1038, - "City Builder": 4328, - "Beat 'em up": 4158, - "Automobile Sim": 1100687, - "2D Fighter": 4736, - Rhythm: 1752, - "3D Fighter": 6506, - "Farming Sim": 87918, - "Animation & Modeling": 872, - "e-sports": 5055, - "Grand Strategy": 4364, - "Space Sim": 16598, - "Colony Sim": 220585, - "Word Game": 24003, - "Battle Royale": 176981, - MMORPG: 1754, - "Auto Battler": 1084988, - "Audio Production": 1027, - "Video Production": 784, - "God Game": 5300, - "4X": 1670, - MOBA: 1718, - "Photo Editing": 809, - Trivia: 10437, - "Immersive Sim": 9204, - "Political Sim": 26921, - "Outbreak Sim": 1100686, - "Medical Sim": 1100688, - Short: 4234, - Movie: 4700, - Episodic: 4242, - Gaming: 150626, - Documentary: 15339, - Exploration: 3834, - "2D Platformer": 5379, - FPS: 1663, - "Rogue-lite": 3959, - "Shoot 'Em Up": 4255, - "3D Platformer": 5395, - "Side Scroller": 3798, - "Choose Your Own Adventure": 4486, - "Puzzle-Platformer": 5537, - "Hidden Object": 1738, - "Bullet Hell": 4885, - "Dungeon Crawler": 1720, - "Top-Down Shooter": 4637, - Clicker: 379975, - "Third-Person Shooter": 3814, - "Precision Platformer": 3877, - "Time Management": 16689, - "Real Time Tactics": 3813, - "Arena Shooter": 5547, - Collectathon: 5652, - "Tactical RPG": 21725, - Idler: 615955, - Wargame: 4684, - Metroidvania: 1628, - Runner: 8666, - "Card Battler": 791774, - "Souls-like": 29482, - CRPG: 4474, - "Creature Collector": 916648, - "Twin Stick Shooter": 4758, - "Match 3": 1665, - "Mystery Dungeon": 198631, - "Hero Shooter": 620519, - "Spectacle fighter": 4777, - "Looter Shooter": 353880, - Solitaire: 13070, - "Combat Racing": 4102, - "Action RTS": 1723, - Sokoban: 1730, - "Trading Card Game": 9271, - Typing: 1674, - "Boomer Shooter": 1023537, - "Traditional Roguelike": 454187, - "On-Rails Shooter": 56690, - Spelling: 71389, - Roguevania: 922563, - Singleplayer: 4182, - Multiplayer: 3859, - "Co-op": 1685, - "Online Co-Op": 3843, - "Massively Multiplayer": 128, - "Local Multiplayer": 7368, - "Local Co-Op": 3841, - "4 Player Local": 4840, - "Co-op Campaign": 4508, - "Asynchronous Multiplayer": 17770, - "Profile Features Limited": 1003823, - "Demo Available": 21491, - "Adult Content": 65443, - Hentai: 9130, - Fighting: 1743, - Classic: 1693, - Cozy: 97376, - "Open World Survival Craft": 1100689, - Wholesome: 552282, - "Roguelike Deckbuilder": 1091588, - Narrative: 7702, - Immersive: 3934, - "Party Game": 7178, - Party: 7108, - "Escape Room": 769306, - Addictive: 4190, - Nostalgia: 14720, - Farming: 4520, - "Cult Classic": 7782, - Spaceships: 4291, - "Electronic Music": 61357, - Pinball: 6621, - "Social Deduction": 745697, - Ambient: 29855, - Dwarf: 7918, - "Job Simulator": 35079, - Epic: 3965, - "Instrumental Music": 189941, - "Jump Scare": 42089, - "Boss Rush": 11095, - "Rock Music": 337964, - "Tile-Matching": 176733, - Vikings: 11634, - "Extraction Shooter": 1199779, - "8-bit Music": 117648, - "Well-Written": 8461, - Mahjong: 33572, - "Shop Keeper": 91114, - Electronic: 143739, - Birds: 6214, - Dice: 7556, - Musou: 323922, - Fox: 30927, - Coding: 42329, - Elf: 102530, - "Hobby Sim": 1220528, - Cricket: 158638, - Rugby: 49213, - Volleyball: 847164, - Snooker: 363767, - Reboot: 5941, - "Based On A Novel": 3796, - "Free to Play": 113, - "Early Access": 493, - Experimental: 13782, - "Software Training": 1445, - Minigames: 8093, - Remake: 5708, - Sequel: 5230, - Experience: 9994, - Kickstarter: 5153, - Crowdfunded: 7113, - Benchmark: 5407, - "Feature Film": 233824, - Difficult: 4026, - Competitive: 3878, - Unforgiving: 1733, - "Turn-Based": 1677, - "Replay Value": 4711, - "Fast-Paced": 1734, - "Real-Time": 4161, - "Real-Time with Pause": 7107, - "Time Attack": 5390, - "Sexual Content": 12095, - Nudity: 6650, - Mature: 5611, - NSFW: 24904, - "Character Action Game": 3955, - "Villain Protagonist": 11333, - "Silent Protagonist": 15954, - Chess: 4184, - Violent: 4667, - Gore: 4345, - Blood: 5228, - Soundtrack: 7948, - "Great Soundtrack": 1756, - Music: 1621, - "Mouse only": 11123, - "Touch-Friendly": 25085, - "Intentionally Awkward Controls": 14906, - "Voice Control": 27758, - Mod: 5348, - RPGMaker: 5577, - GameMaker: 1649, - "Dungeons & Dragons": 14153, - "Games Workshop": 5310, - TrackIR: 8075, - Hardware: 603297, - "Steam Machine": 348922, -}; diff --git a/src/renderer/src/workers/download-sources.worker.ts b/src/renderer/src/workers/download-sources.worker.ts index 40665dc9..185afed5 100644 --- a/src/renderer/src/workers/download-sources.worker.ts +++ b/src/renderer/src/workers/download-sources.worker.ts @@ -83,6 +83,37 @@ const getSteamGames = async () => { return response.data; }; +const importDownloadSource = async (url: string) => { + const response = await axios.get>(url); + + const steamGames = await getSteamGames(); + + await db.transaction("rw", repacksTable, downloadSourcesTable, async () => { + const now = new Date(); + + const id = await downloadSourcesTable.add({ + url, + name: response.data.name, + etag: response.headers["etag"], + status: DownloadSourceStatus.UpToDate, + downloadCount: response.data.downloads.length, + createdAt: now, + updatedAt: now, + }); + + const downloadSource = await downloadSourcesTable.get(id); + + await addNewDownloads(downloadSource, response.data.downloads, steamGames); + }); +}; + +const deleteDownloadSource = async (id: number) => { + await db.transaction("rw", repacksTable, downloadSourcesTable, async () => { + await repacksTable.where({ downloadSourceId: id }).delete(); + await downloadSourcesTable.where({ id }).delete(); + }); +}; + self.onmessage = async (event: MessageEvent) => { const [type, data] = event.data; @@ -102,10 +133,7 @@ self.onmessage = async (event: MessageEvent) => { } if (type === "DELETE_DOWNLOAD_SOURCE") { - await db.transaction("rw", repacksTable, downloadSourcesTable, async () => { - await repacksTable.where({ downloadSourceId: data }).delete(); - await downloadSourcesTable.where({ id: data }).delete(); - }); + await deleteDownloadSource(data); const channel = new BroadcastChannel(`download_sources:delete:${data}`); @@ -113,32 +141,7 @@ self.onmessage = async (event: MessageEvent) => { } if (type === "IMPORT_DOWNLOAD_SOURCE") { - const response = - await axios.get>(data); - - const steamGames = await getSteamGames(); - - await db.transaction("rw", repacksTable, downloadSourcesTable, async () => { - const now = new Date(); - - const id = await downloadSourcesTable.add({ - url: data, - name: response.data.name, - etag: response.headers["etag"], - status: DownloadSourceStatus.UpToDate, - downloadCount: response.data.downloads.length, - createdAt: now, - updatedAt: now, - }); - - const downloadSource = await downloadSourcesTable.get(id); - - await addNewDownloads( - downloadSource, - response.data.downloads, - steamGames - ); - }); + await importDownloadSource(data); const channel = new BroadcastChannel(`download_sources:import:${data}`); channel.postMessage(true); @@ -153,6 +156,12 @@ self.onmessage = async (event: MessageEvent) => { const existingRepacks = await repacksTable.toArray(); for (const downloadSource of downloadSources) { + if (!downloadSource.fingerprint) { + await deleteDownloadSource(downloadSource.id); + await importDownloadSource(downloadSource.url); + continue; + } + const headers = new AxiosHeaders(); if (downloadSource.etag) { From 496924b2a49c0ce6b61e681504f656bff0b9b1e4 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Sun, 22 Dec 2024 23:42:49 +0000 Subject: [PATCH 14/18] fix: fixing download sources migration --- .../src/pages/catalogue/catalogue.tsx | 24 +++++++++++-------- .../src/pages/catalogue/filter-section.tsx | 10 ++++---- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/src/renderer/src/pages/catalogue/catalogue.tsx b/src/renderer/src/pages/catalogue/catalogue.tsx index e5787ea8..e85e94b5 100644 --- a/src/renderer/src/pages/catalogue/catalogue.tsx +++ b/src/renderer/src/pages/catalogue/catalogue.tsx @@ -403,23 +403,27 @@ export default function Catalogue() { color={filterCategoryColors.tags} onClear={() => dispatch(setSearch({ tags: [] }))} onSelect={(value) => { - if (filters.tags.includes(value)) { + if (filters.tags.includes(Number(value))) { dispatch( setSearch({ - tags: filters.tags.filter((tag) => tag !== value), + tags: filters.tags.filter((tag) => tag !== Number(value)), }) ); } else { - dispatch(setSearch({ tags: [...filters.tags, value] })); + dispatch( + setSearch({ tags: [...filters.tags, Number(value)] }) + ); } }} - items={Object.entries(steamUserTags) - .sort(([keyA], [keyB]) => keyA.localeCompare(keyB)) - .map(([key, value]) => ({ - label: key, - value: value, - checked: filters.tags.includes(value), - }))} + items={ + Object.entries(steamUserTags) + .sort(([keyA], [keyB]) => keyA.localeCompare(keyB)) + .map(([key, value]) => ({ + label: key, + value: value, + checked: filters.tags.includes(value as number), + })) as any + } /> { +export interface FilterSectionProps { title: string; items: { label: string; - value: T; + value: string; checked: boolean; }[]; - onSelect: (value: T) => void; + onSelect: (value: string) => void; color: string; onClear: () => void; } -export function FilterSection({ +export function FilterSection({ title, items, color, onSelect, onClear, -}: FilterSectionProps) { +}: FilterSectionProps) { const [search, setSearch] = useState(""); const filteredItems = useMemo(() => { From c9c6d5ee4660245f8f8089eee2c64e331ec4c0ba Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Mon, 23 Dec 2024 00:05:27 +0000 Subject: [PATCH 15/18] fix: fixing download sources migration --- .../src/components/header/header.css.ts | 32 +++++--- src/renderer/src/components/header/header.tsx | 76 +++++++++++++++---- src/renderer/src/hooks/use-repacks.ts | 4 +- .../src/pages/catalogue/catalogue.scss | 54 ------------- .../src/pages/catalogue/catalogue.tsx | 45 ----------- 5 files changed, 86 insertions(+), 125 deletions(-) diff --git a/src/renderer/src/components/header/header.css.ts b/src/renderer/src/components/header/header.css.ts index 903f4a42..12855986 100644 --- a/src/renderer/src/components/header/header.css.ts +++ b/src/renderer/src/components/header/header.css.ts @@ -74,16 +74,28 @@ export const search = recipe({ }, }); -export const searchButton = recipe({ - base: { - WebkitAppRegion: "no-drag", - } as ComplexStyleRule, - variants: { - hidden: { - true: { - visibility: "hidden", - }, - }, +export const searchInput = style({ + backgroundColor: "transparent", + border: "none", + width: "100%", + height: "100%", + outline: "none", + color: "#DADBE1", + cursor: "default", + fontFamily: "inherit", + textOverflow: "ellipsis", + ":focus": { + cursor: "text", + }, +}); + +export const actionButton = style({ + color: "inherit", + cursor: "pointer", + transition: "all ease 0.2s", + padding: `${SPACING_UNIT}px`, + ":hover": { + color: "#DADBE1", }, }); diff --git a/src/renderer/src/components/header/header.tsx b/src/renderer/src/components/header/header.tsx index 2c79b1d4..97ecbdc2 100644 --- a/src/renderer/src/components/header/header.tsx +++ b/src/renderer/src/components/header/header.tsx @@ -1,13 +1,13 @@ import { useTranslation } from "react-i18next"; -import { useMemo } from "react"; +import { useMemo, useRef, useState } from "react"; import { useLocation, useNavigate } from "react-router-dom"; -import { ArrowLeftIcon, SearchIcon } from "@primer/octicons-react"; +import { ArrowLeftIcon, SearchIcon, XIcon } from "@primer/octicons-react"; -import { useAppSelector } from "@renderer/hooks"; +import { useAppDispatch, useAppSelector } from "@renderer/hooks"; import * as styles from "./header.css"; import { AutoUpdateSubHeader } from "./auto-update-sub-header"; -import { Button } from "../button/button"; +import { setSearch } from "@renderer/features"; const pathTitle: Record = { "/": "home", @@ -17,6 +17,8 @@ const pathTitle: Record = { }; export function Header() { + const inputRef = useRef(null); + const navigate = useNavigate(); const location = useLocation(); @@ -24,6 +26,14 @@ export function Header() { (state) => state.window ); + const searchValue = useAppSelector( + (state) => state.catalogueSearch.value.title + ); + + const dispatch = useAppDispatch(); + + const [isFocused, setIsFocused] = useState(false); + const { t } = useTranslation("header"); const title = useMemo(() => { @@ -35,14 +45,27 @@ export function Header() { return t(pathTitle[location.pathname]); }, [location.pathname, headerTitle, t]); - const showSearchButton = useMemo(() => { - return location.pathname.startsWith("/catalogue"); - }, [location.pathname]); + const focusInput = () => { + setIsFocused(true); + inputRef.current?.focus(); + }; + + const handleBlur = () => { + setIsFocused(false); + }; const handleBackButtonClick = () => { navigate(-1); }; + const handleSearch = (value: string) => { + dispatch(setSearch({ title: value })); + + if (!location.pathname.startsWith("/catalogue")) { + navigate("/catalogue"); + } + }; + return ( <>
- +
+ + + handleSearch(event.target.value)} + onFocus={() => setIsFocused(true)} + onBlur={handleBlur} + /> + + {searchValue && ( + + )} +
diff --git a/src/renderer/src/hooks/use-repacks.ts b/src/renderer/src/hooks/use-repacks.ts index e55a2036..1c160be6 100644 --- a/src/renderer/src/hooks/use-repacks.ts +++ b/src/renderer/src/hooks/use-repacks.ts @@ -18,7 +18,9 @@ export function useRepacks() { const updateRepacks = useCallback(() => { repacksTable.toArray().then((repacks) => { - dispatch(setRepacks(repacks)); + dispatch( + setRepacks(repacks.filter((repack) => Array.isArray(repack.objectIds))) + ); }); }, [dispatch]); diff --git a/src/renderer/src/pages/catalogue/catalogue.scss b/src/renderer/src/pages/catalogue/catalogue.scss index a066ab7f..e1ba3113 100644 --- a/src/renderer/src/pages/catalogue/catalogue.scss +++ b/src/renderer/src/pages/catalogue/catalogue.scss @@ -19,60 +19,6 @@ width: 100%; padding: 16px; - &__search-container { - background-color: globals.$dark-background-color; - display: inline-flex; - transition: all ease 0.2s; - width: 200px; - align-items: center; - border-radius: 8px; - border: 1px solid globals.$border-color; - height: 40px; - - &:hover { - border-color: rgba(255, 255, 255, 0.5); - } - - &--focused { - width: 250px; - border-color: #dadbe1; - } - } - - &__search-icon-button { - color: inherit; - cursor: pointer; - transition: all ease 0.2s; - padding: 8px; - - &:hover { - color: #dadbe1; - } - } - - &__search-clear-button { - color: inherit; - cursor: pointer; - transition: all ease 0.2s; - padding: 8px; - - &:hover { - color: #dadbe1; - } - } - - &__search-input { - background-color: transparent; - border: none; - width: 100%; - height: 100%; - outline: none; - color: #dadbe1; - cursor: default; - font-family: inherit; - text-overflow: ellipsis; - } - &__game-item { background-color: globals.$dark-background-color; width: 100%; diff --git a/src/renderer/src/pages/catalogue/catalogue.tsx b/src/renderer/src/pages/catalogue/catalogue.tsx index e85e94b5..bb44801b 100644 --- a/src/renderer/src/pages/catalogue/catalogue.tsx +++ b/src/renderer/src/pages/catalogue/catalogue.tsx @@ -141,51 +141,6 @@ export default function Catalogue() { return (
-
-
- - - onSearch(event.target.value)} - onFocus={() => setFocused(true)} - onBlur={() => setFocused(false)} - /> - - {filters.title && ( - - )} -
-
-
Date: Mon, 23 Dec 2024 00:06:23 +0000 Subject: [PATCH 16/18] fix: fixing download sources migration --- .../src/pages/catalogue/catalogue.tsx | 24 +------------------ 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/src/renderer/src/pages/catalogue/catalogue.tsx b/src/renderer/src/pages/catalogue/catalogue.tsx index bb44801b..d3372eaf 100644 --- a/src/renderer/src/pages/catalogue/catalogue.tsx +++ b/src/renderer/src/pages/catalogue/catalogue.tsx @@ -2,11 +2,9 @@ import { Badge } from "@renderer/components"; import type { DownloadSource } from "@types"; -import cn from "classnames"; - import { useAppDispatch, useAppSelector, useRepacks } from "@renderer/hooks"; import { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { SearchIcon, XIcon } from "@primer/octicons-react"; +import { XIcon } from "@primer/octicons-react"; import "./catalogue.scss"; @@ -34,8 +32,6 @@ export default function Catalogue() { const abortControllerRef = useRef(null); - const [focused, setFocused] = useState(false); - const [steamUserTags, setSteamUserTags] = useState({}); const [searchParams] = useSearchParams(); @@ -105,18 +101,6 @@ export default function Catalogue() { }); }, [getRepacksForObjectId]); - const focusInput = useCallback(() => { - setFocused(true); - inputRef.current?.focus(); - }, []); - - const onSearch = useCallback( - (value: string) => { - dispatch(setSearch({ title: value })); - }, - [dispatch] - ); - useEffect(() => { axios .get( @@ -133,12 +117,6 @@ export default function Catalogue() { }); }, [i18n.language]); - useEffect(() => { - if (search) { - focusInput(); - } - }, [search, focusInput]); - return (
Date: Mon, 23 Dec 2024 00:07:02 +0000 Subject: [PATCH 17/18] fix: fixing download sources migration --- src/renderer/src/pages/catalogue/catalogue.tsx | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/renderer/src/pages/catalogue/catalogue.tsx b/src/renderer/src/pages/catalogue/catalogue.tsx index d3372eaf..3890a3e1 100644 --- a/src/renderer/src/pages/catalogue/catalogue.tsx +++ b/src/renderer/src/pages/catalogue/catalogue.tsx @@ -3,7 +3,7 @@ import { Badge } from "@renderer/components"; import type { DownloadSource } from "@types"; import { useAppDispatch, useAppSelector, useRepacks } from "@renderer/hooks"; -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { XIcon } from "@primer/octicons-react"; import "./catalogue.scss"; @@ -28,15 +28,10 @@ const filterCategoryColors = { }; export default function Catalogue() { - const inputRef = useRef(null); - const abortControllerRef = useRef(null); const [steamUserTags, setSteamUserTags] = useState({}); - const [searchParams] = useSearchParams(); - const search = searchParams.get("search"); - const navigate = useNavigate(); const [downloadSources, setDownloadSources] = useState([]); From 4607665908cba84edaa5628b73140fed76817ca1 Mon Sep 17 00:00:00 2001 From: Chubby Granny Chaser Date: Mon, 23 Dec 2024 00:07:35 +0000 Subject: [PATCH 18/18] fix: fixing download sources migration --- src/renderer/src/pages/catalogue/catalogue.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/pages/catalogue/catalogue.tsx b/src/renderer/src/pages/catalogue/catalogue.tsx index 3890a3e1..2a120b05 100644 --- a/src/renderer/src/pages/catalogue/catalogue.tsx +++ b/src/renderer/src/pages/catalogue/catalogue.tsx @@ -12,7 +12,7 @@ import { SPACING_UNIT, vars } from "@renderer/theme.css"; import { downloadSourcesTable } from "@renderer/dexie"; import { steamUrlBuilder } from "@shared"; import { buildGameDetailsPath } from "@renderer/helpers"; -import { useNavigate, useSearchParams } from "react-router-dom"; +import { useNavigate } from "react-router-dom"; import { FilterSection } from "./filter-section"; import { setSearch } from "@renderer/features"; import { useTranslation } from "react-i18next";