diff --git a/src/main/events/catalogue/get-games.ts b/src/main/events/catalogue/get-games.ts index 859a0de5..81717806 100644 --- a/src/main/events/catalogue/get-games.ts +++ b/src/main/events/catalogue/get-games.ts @@ -2,6 +2,7 @@ import type { CatalogueEntry } from "@types"; import { registerEvent } from "../register-event"; import { steamGamesWorker } from "@main/workers"; +import { steamUrlBuilder } from "@shared"; const getGames = async ( _event: Electron.IpcMainInvokeEvent, @@ -14,7 +15,12 @@ const getGames = async ( ); return { - results: steamGames, + results: steamGames.map((steamGame) => ({ + title: steamGame.name, + shop: "steam", + cover: steamUrlBuilder.library(steamGame.id), + objectID: steamGame.id, + })), cursor: cursor + steamGames.length, }; }; diff --git a/src/main/events/download-sources/add-download-source.ts b/src/main/events/download-sources/add-download-source.ts deleted file mode 100644 index b762c95d..00000000 --- a/src/main/events/download-sources/add-download-source.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { registerEvent } from "../register-event"; -import { dataSource } from "@main/data-source"; -import { DownloadSource } from "@main/entity"; -import axios from "axios"; -import { downloadSourceSchema } from "../helpers/validators"; -import { insertDownloadsFromSource } from "@main/helpers"; - -const addDownloadSource = async ( - _event: Electron.IpcMainInvokeEvent, - url: string -) => { - const response = await axios.get(url); - - const source = downloadSourceSchema.parse(response.data); - - const downloadSource = await dataSource.transaction( - async (transactionalEntityManager) => { - const downloadSource = await transactionalEntityManager - .getRepository(DownloadSource) - .save({ - url, - name: source.name, - downloadCount: source.downloads.length, - }); - - await insertDownloadsFromSource( - transactionalEntityManager, - downloadSource, - source.downloads - ); - - return downloadSource; - } - ); - - return downloadSource; -}; - -registerEvent("addDownloadSource", addDownloadSource); diff --git a/src/main/events/download-sources/remove-download-source.ts b/src/main/events/download-sources/remove-download-source.ts deleted file mode 100644 index 8d67df13..00000000 --- a/src/main/events/download-sources/remove-download-source.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { downloadSourceRepository } from "@main/repository"; -import { registerEvent } from "../register-event"; -import { RepacksManager } from "@main/services"; - -const removeDownloadSource = async ( - _event: Electron.IpcMainInvokeEvent, - id: number -) => downloadSourceRepository.delete(id); - -registerEvent("removeDownloadSource", removeDownloadSource); diff --git a/src/main/events/download-sources/sync-download-sources.ts b/src/main/events/download-sources/sync-download-sources.ts index 2e000e64..49380f30 100644 --- a/src/main/events/download-sources/sync-download-sources.ts +++ b/src/main/events/download-sources/sync-download-sources.ts @@ -1,7 +1,13 @@ +import { downloadSourcesWorker } from "@main/workers"; import { registerEvent } from "../register-event"; -import { fetchDownloadSourcesAndUpdate } from "@main/helpers"; +import type { DownloadSource } from "@types"; -const syncDownloadSources = async (_event: Electron.IpcMainInvokeEvent) => - fetchDownloadSourcesAndUpdate(); +const syncDownloadSources = async ( + _event: Electron.IpcMainInvokeEvent, + downloadSources: DownloadSource[] +) => + downloadSourcesWorker.run(downloadSources, { + name: "getUpdatedRepacks", + }); registerEvent("syncDownloadSources", syncDownloadSources); diff --git a/src/main/events/download-sources/validate-download-source.ts b/src/main/events/download-sources/validate-download-source.ts index fdb67961..4f43ca08 100644 --- a/src/main/events/download-sources/validate-download-source.ts +++ b/src/main/events/download-sources/validate-download-source.ts @@ -1,27 +1,12 @@ import { registerEvent } from "../register-event"; -import { downloadSourceRepository } from "@main/repository"; -import { RepacksManager } from "@main/services"; -import { downloadSourceWorker } from "@main/workers"; +import { downloadSourcesWorker } from "@main/workers"; const validateDownloadSource = async ( _event: Electron.IpcMainInvokeEvent, url: string -) => { - const existingSource = await downloadSourceRepository.findOne({ - where: { url }, +) => + downloadSourcesWorker.run(url, { + name: "validateDownloadSource", }); - if (existingSource) - throw new Error("Source with the same url already exists"); - - const repacks = RepacksManager.repacks; - - return downloadSourceWorker.run( - { url, repacks }, - { - name: "validateDownloadSource", - } - ); -}; - registerEvent("validateDownloadSource", validateDownloadSource); diff --git a/src/main/events/index.ts b/src/main/events/index.ts index 5e2c17a1..73bf38f4 100644 --- a/src/main/events/index.ts +++ b/src/main/events/index.ts @@ -39,8 +39,6 @@ import "./autoupdater/restart-and-install-update"; import "./user-preferences/authenticate-real-debrid"; import "./download-sources/get-download-sources"; import "./download-sources/validate-download-source"; -import "./download-sources/add-download-source"; -import "./download-sources/remove-download-source"; import "./download-sources/sync-download-sources"; import "./auth/sign-out"; import "./auth/open-auth-window"; diff --git a/src/main/events/user/get-user.ts b/src/main/events/user/get-user.ts index b8bd7a0a..6bbab9c4 100644 --- a/src/main/events/user/get-user.ts +++ b/src/main/events/user/get-user.ts @@ -73,7 +73,6 @@ const getUser = async ( recentGames, }; } catch (err) { - console.log(err); return null; } }; diff --git a/src/main/helpers/download-source.ts b/src/main/helpers/download-source.ts deleted file mode 100644 index c216212a..00000000 --- a/src/main/helpers/download-source.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { dataSource } from "@main/data-source"; -import { DownloadSource, Repack } from "@main/entity"; -import { downloadSourceSchema } from "@main/events/helpers/validators"; -import { downloadSourceRepository } from "@main/repository"; -import { RepacksManager } from "@main/services"; -import { downloadSourceWorker } from "@main/workers"; -import { chunk } from "lodash-es"; -import type { EntityManager } from "typeorm"; -import type { QueryDeepPartialEntity } from "typeorm/query-builder/QueryPartialEntity"; -import { z } from "zod"; - -export const insertDownloadsFromSource = async ( - trx: EntityManager, - downloadSource: DownloadSource, - downloads: z.infer["downloads"] -) => { - const repacks: QueryDeepPartialEntity[] = downloads.map( - (download) => ({ - title: download.title, - uris: JSON.stringify(download.uris), - magnet: download.uris[0]!, - fileSize: download.fileSize, - repacker: downloadSource.name, - uploadDate: download.uploadDate, - downloadSource: { id: downloadSource.id }, - }) - ); - - const downloadsChunks = chunk(repacks, 800); - - for (const chunk of downloadsChunks) { - await trx - .getRepository(Repack) - .createQueryBuilder() - .insert() - .values(chunk) - .updateEntity(false) - .orIgnore() - .execute(); - } -}; - -export const fetchDownloadSourcesAndUpdate = async () => { - const downloadSources = await downloadSourceRepository.find({ - order: { - id: "desc", - }, - }); - - const results = await downloadSourceWorker.run(downloadSources, { - name: "getUpdatedRepacks", - }); - - await dataSource.transaction(async (transactionalEntityManager) => { - for (const result of results) { - if (result.etag !== null) { - await transactionalEntityManager.getRepository(DownloadSource).update( - { id: result.id }, - { - etag: result.etag, - status: result.status, - downloadCount: result.downloads.length, - } - ); - - await insertDownloadsFromSource( - transactionalEntityManager, - result, - result.downloads - ); - } - } - - await RepacksManager.updateRepacks(); - }); -}; diff --git a/src/main/helpers/index.ts b/src/main/helpers/index.ts index 91ce0eb9..a9dcae6c 100644 --- a/src/main/helpers/index.ts +++ b/src/main/helpers/index.ts @@ -36,6 +36,4 @@ export const requestWebPage = async (url: string) => { }; export const isPortableVersion = () => - process.env.PORTABLE_EXECUTABLE_FILE != null; - -export * from "./download-source"; + process.env.PORTABLE_EXECUTABLE_FILE !== null; diff --git a/src/main/main.ts b/src/main/main.ts index 690282f6..b71bab8c 100644 --- a/src/main/main.ts +++ b/src/main/main.ts @@ -1,14 +1,14 @@ import { DownloadManager, PythonInstance, startMainLoop } from "./services"; import { downloadQueueRepository, - repackRepository, + // repackRepository, userPreferencesRepository, } from "./repository"; import { UserPreferences } from "./entity"; import { RealDebridClient } from "./services/real-debrid"; -import { fetchDownloadSourcesAndUpdate } from "./helpers"; -import { publishNewRepacksNotifications } from "./services/notifications"; -import { MoreThan } from "typeorm"; +// import { fetchDownloadSourcesAndUpdate } from "./helpers"; +// import { publishNewRepacksNotifications } from "./services/notifications"; +// import { MoreThan } from "typeorm"; import { HydraApi } from "./services/hydra-api"; import { uploadGamesBatch } from "./services/library-sync"; @@ -40,17 +40,17 @@ const loadState = async (userPreferences: UserPreferences | null) => { startMainLoop(); - const now = new Date(); + // const now = new Date(); - fetchDownloadSourcesAndUpdate().then(async () => { - const newRepacksCount = await repackRepository.count({ - where: { - createdAt: MoreThan(now), - }, - }); + // fetchDownloadSourcesAndUpdate().then(async () => { + // const newRepacksCount = await repackRepository.count({ + // where: { + // createdAt: MoreThan(now), + // }, + // }); - if (newRepacksCount > 0) publishNewRepacksNotifications(newRepacksCount); - }); + // if (newRepacksCount > 0) publishNewRepacksNotifications(newRepacksCount); + // }); }; userPreferencesRepository diff --git a/src/main/workers/download-source.worker.ts b/src/main/workers/download-sources.worker.ts similarity index 74% rename from src/main/workers/download-source.worker.ts rename to src/main/workers/download-sources.worker.ts index 5ec37c7f..c660ad00 100644 --- a/src/main/workers/download-source.worker.ts +++ b/src/main/workers/download-sources.worker.ts @@ -1,6 +1,6 @@ import { downloadSourceSchema } from "@main/events/helpers/validators"; import { DownloadSourceStatus } from "@shared"; -import type { DownloadSource, GameRepack } from "@types"; +import type { DownloadSource } from "@types"; import axios, { AxiosError, AxiosHeaders } from "axios"; import { z } from "zod"; @@ -49,23 +49,11 @@ export const getUpdatedRepacks = async (downloadSources: DownloadSource[]) => { return results; }; -export const validateDownloadSource = async ({ - url, - repacks, -}: { - url: string; - repacks: GameRepack[]; -}) => { +export const validateDownloadSource = async (url: string) => { const response = await axios.get(url); - const source = downloadSourceSchema.parse(response.data); - - const existingUris = source.downloads - .flatMap((download) => download.uris) - .filter((uri) => repacks.some((repack) => repack.magnet === uri)); - return { - name: source.name, - downloadCount: source.downloads.length - existingUris.length, + ...downloadSourceSchema.parse(response.data), + etag: response.headers["etag"], }; }; diff --git a/src/main/workers/index.ts b/src/main/workers/index.ts index b0f9721f..799ed2ef 100644 --- a/src/main/workers/index.ts +++ b/src/main/workers/index.ts @@ -1,6 +1,6 @@ import path from "node:path"; import steamGamesWorkerPath from "./steam-games.worker?modulePath"; -import downloadSourceWorkerPath from "./download-source.worker?modulePath"; +import downloadSourcesWorkerPath from "./download-sources.worker?modulePath"; import Piscina from "piscina"; @@ -14,6 +14,6 @@ export const steamGamesWorker = new Piscina({ maxThreads: 1, }); -export const downloadSourceWorker = new Piscina({ - filename: downloadSourceWorkerPath, +export const downloadSourcesWorker = new Piscina({ + filename: downloadSourcesWorkerPath, }); diff --git a/src/preload/index.ts b/src/preload/index.ts index 38df190d..4d7b7183 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -11,6 +11,7 @@ import type { GameRunning, FriendRequestAction, UpdateProfileRequest, + DownloadSource, } from "@types"; import type { CatalogueCategory } from "@shared"; @@ -64,11 +65,8 @@ contextBridge.exposeInMainWorld("electron", { getDownloadSources: () => ipcRenderer.invoke("getDownloadSources"), validateDownloadSource: (url: string) => ipcRenderer.invoke("validateDownloadSource", url), - addDownloadSource: (url: string) => - ipcRenderer.invoke("addDownloadSource", url), - removeDownloadSource: (id: number) => - ipcRenderer.invoke("removeDownloadSource", id), - syncDownloadSources: () => ipcRenderer.invoke("syncDownloadSources"), + syncDownloadSources: (downloadSources: DownloadSource[]) => + ipcRenderer.invoke("syncDownloadSources", downloadSources), /* Library */ addGameToLibrary: (objectID: string, title: string, shop: GameShop) => diff --git a/src/renderer/src/app.tsx b/src/renderer/src/app.tsx index 7b1a2c03..1488cd49 100644 --- a/src/renderer/src/app.tsx +++ b/src/renderer/src/app.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef } from "react"; +import { useCallback, useContext, useEffect, useRef } from "react"; import { Sidebar, BottomPanel, Header, Toast } from "@renderer/components"; @@ -26,10 +26,8 @@ import { } from "@renderer/features"; import { useTranslation } from "react-i18next"; import { UserFriendModal } from "./pages/shared-modals/user-friend-modal"; -import { RepacksContextProvider } from "./context"; -import { downloadSourcesWorker } from "./workers"; - -downloadSourcesWorker.postMessage("OK"); +import { migrationWorker } from "./workers"; +import { repacksContext } from "./context"; export interface AppProps { children: React.ReactNode; @@ -43,6 +41,8 @@ export function App() { const { clearDownload, setLastPacket } = useDownload(); + const { indexRepacks } = useContext(repacksContext); + const { isFriendsModalVisible, friendRequetsModalTab, @@ -210,53 +210,70 @@ export function App() { }); }, [dispatch, draggingDisabled]); + useEffect(() => { + // window.electron.getRepacks().then((repacks) => { + // migrationWorker.postMessage(["MIGRATE_REPACKS", repacks]); + // }); + // window.electron.getDownloadSources().then((downloadSources) => { + // migrationWorker.postMessage([ + // "MIGRATE_DOWNLOAD_SOURCES", + // downloadSources, + // ]); + // }); + // migrationWorker.onmessage = ( + // event: MessageEvent<"MIGRATE_REPACKS_COMPLETE"> + // ) => { + // if (event.data === "MIGRATE_REPACKS_COMPLETE") { + // indexRepacks(); + // } + // }; + }, [indexRepacks]); + const handleToastClose = useCallback(() => { dispatch(closeToast()); }, [dispatch]); return ( - - <> - {window.electron.platform === "win32" && ( -
-

Hydra

-
- )} + <> + {window.electron.platform === "win32" && ( +
+

Hydra

+
+ )} - + + {userDetails && ( + + )} - {userDetails && ( - + + +
+
- )} -
- +
+ +
+
+ -
-
- -
- -
-
- - - - -
+ + ); } diff --git a/src/renderer/src/context/repacks/repacks.context.tsx b/src/renderer/src/context/repacks/repacks.context.tsx index a2e4101b..c59d5792 100644 --- a/src/renderer/src/context/repacks/repacks.context.tsx +++ b/src/renderer/src/context/repacks/repacks.context.tsx @@ -5,11 +5,13 @@ 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, }); @@ -37,7 +39,8 @@ export function RepacksContextProvider({ children }: RepacksContextProps) { }); }, []); - useEffect(() => { + const indexRepacks = useCallback(() => { + setIsIndexingRepacks(true); repacksWorker.postMessage("INDEX_REPACKS"); repacksWorker.onmessage = () => { @@ -45,10 +48,15 @@ export function RepacksContextProvider({ children }: RepacksContextProps) { }; }, []); + useEffect(() => { + indexRepacks(); + }, [indexRepacks]); + return ( diff --git a/src/renderer/src/declaration.d.ts b/src/renderer/src/declaration.d.ts index 3673ec08..28c5caf7 100644 --- a/src/renderer/src/declaration.d.ts +++ b/src/renderer/src/declaration.d.ts @@ -25,6 +25,7 @@ import type { UserStats, UserDetails, FriendRequestSync, + DownloadSourceValidationResult, } from "@types"; import type { DiskSpace } from "check-disk-space"; @@ -106,10 +107,8 @@ declare global { getDownloadSources: () => Promise; validateDownloadSource: ( url: string - ) => Promise<{ name: string; downloadCount: number }>; - addDownloadSource: (url: string) => Promise; - removeDownloadSource: (id: number) => Promise; - syncDownloadSources: () => Promise; + ) => Promise; + syncDownloadSources: (downloadSources: DownloadSource[]) => Promise; /* Hardware */ getDiskFreeSpace: (path: string) => Promise; diff --git a/src/renderer/src/dexie.ts b/src/renderer/src/dexie.ts index 2b9f0aa6..23f0bf83 100644 --- a/src/renderer/src/dexie.ts +++ b/src/renderer/src/dexie.ts @@ -3,7 +3,7 @@ import { Dexie } from "dexie"; export const db = new Dexie("Hydra"); db.version(1).stores({ - repacks: `++id, title, uri, fileSize, uploadDate, downloadSourceId, repacker, createdAt, updatedAt`, + repacks: `++id, title, uris, fileSize, uploadDate, downloadSourceId, repacker, createdAt, updatedAt`, downloadSources: `++id, url, name, etag, downloadCount, status, createdAt, updatedAt`, }); diff --git a/src/renderer/src/main.tsx b/src/renderer/src/main.tsx index 5d9b2197..d845e028 100644 --- a/src/renderer/src/main.tsx +++ b/src/renderer/src/main.tsx @@ -30,6 +30,7 @@ import { store } from "./store"; import resources from "@locales"; import "./workers"; +import { RepacksContextProvider } from "./context"; Sentry.init({}); @@ -56,19 +57,21 @@ i18n ReactDOM.createRoot(document.getElementById("root")!).render( - - - }> - - - - - - - - - - + + + + }> + + + + + + + + + + + ); 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 fba890d1..8e34cbe2 100644 --- a/src/renderer/src/pages/settings/add-download-source-modal.tsx +++ b/src/renderer/src/pages/settings/add-download-source-modal.tsx @@ -9,6 +9,8 @@ import { useForm } from "react-hook-form"; import * as yup from "yup"; import { yupResolver } from "@hookform/resolvers/yup"; import { downloadSourcesTable } from "@renderer/dexie"; +import { DownloadSourceValidationResult } from "@types"; +import { downloadSourcesWorker } from "@renderer/workers"; interface AddDownloadSourceModalProps { visible: boolean; @@ -40,41 +42,35 @@ export function AddDownloadSourceModal({ setValue, setError, clearErrors, - formState: { errors }, + formState: { errors, isSubmitting }, } = useForm({ resolver: yupResolver(schema), }); - const [validationResult, setValidationResult] = useState<{ - name: string; - downloadCount: number; - } | null>(null); + const [validationResult, setValidationResult] = + useState(null); const { sourceUrl } = useContext(settingsContext); const onSubmit = useCallback( async (values: FormValues) => { - setIsLoading(true); + const existingDownloadSource = await downloadSourcesTable + .where({ url: values.url }) + .first(); - try { - const result = await window.electron.validateDownloadSource(values.url); - setValidationResult(result); + if (existingDownloadSource) { + setError("url", { + type: "server", + message: t("source_already_exists"), + }); - setUrl(values.url); - } catch (error: unknown) { - if (error instanceof Error) { - if ( - error.message.endsWith("Source with the same url already exists") - ) { - setError("url", { - type: "server", - message: t("source_already_exists"), - }); - } - } - } finally { - setIsLoading(false); + return; } + + const result = await window.electron.validateDownloadSource(values.url); + setValidationResult(result); + + setUrl(values.url); }, [setError, t] ); @@ -92,12 +88,23 @@ export function AddDownloadSourceModal({ }, [visible, clearErrors, handleSubmit, onSubmit, setValue, sourceUrl]); const handleAddDownloadSource = async () => { - await downloadSourcesTable.add({ - url, - }); - await window.electron.addDownloadSource(url); - onClose(); - onAddDownloadSource(); + setIsLoading(true); + + if (validationResult) { + const channel = new BroadcastChannel(`download_sources:import:${url}`); + + downloadSourcesWorker.postMessage([ + "IMPORT_DOWNLOAD_SOURCE", + { ...validationResult, url }, + ]); + + channel.onmessage = () => { + setIsLoading(false); + + onClose(); + onAddDownloadSource(); + }; + } }; return ( @@ -126,7 +133,7 @@ export function AddDownloadSourceModal({ theme="outline" style={{ alignSelf: "flex-end" }} onClick={handleSubmit(onSubmit)} - disabled={isLoading} + disabled={isSubmitting || isLoading} > {t("validate_download_source")} @@ -152,9 +159,9 @@ export function AddDownloadSourceModal({

{validationResult?.name}

{t("found_download_option", { - count: validationResult?.downloadCount, + count: validationResult?.downloads.length, countFormatted: - validationResult?.downloadCount.toLocaleString(), + validationResult?.downloads.length.toLocaleString(), })} diff --git a/src/renderer/src/pages/settings/settings-download-sources.tsx b/src/renderer/src/pages/settings/settings-download-sources.tsx index 53c14348..4f70ff6b 100644 --- a/src/renderer/src/pages/settings/settings-download-sources.tsx +++ b/src/renderer/src/pages/settings/settings-download-sources.tsx @@ -10,8 +10,9 @@ import { AddDownloadSourceModal } from "./add-download-source-modal"; import { useToast } from "@renderer/hooks"; import { DownloadSourceStatus } from "@shared"; import { SPACING_UNIT } from "@renderer/theme.css"; -import { settingsContext } from "@renderer/context"; -import { db, downloadSourcesTable, repacksTable } from "@renderer/dexie"; +import { repacksContext, settingsContext } from "@renderer/context"; +import { downloadSourcesTable } from "@renderer/dexie"; +import { downloadSourcesWorker } from "@renderer/workers"; export function SettingsDownloadSources() { const [showAddDownloadSourceModal, setShowAddDownloadSourceModal] = @@ -19,16 +20,23 @@ export function SettingsDownloadSources() { const [downloadSources, setDownloadSources] = useState([]); const [isSyncingDownloadSources, setIsSyncingDownloadSources] = useState(false); + const [isRemovingDownloadSource, setIsRemovingDownloadSource] = + useState(false); const { sourceUrl, clearSourceUrl } = useContext(settingsContext); const { t } = useTranslation("settings"); const { showSuccessToast } = useToast(); + const { indexRepacks } = useContext(repacksContext); + const getDownloadSources = async () => { - downloadSourcesTable.toArray().then((sources) => { - setDownloadSources(sources); - }); + await downloadSourcesTable + .toCollection() + .sortBy("createdAt") + .then((sources) => { + setDownloadSources(sources.reverse()); + }); }; useEffect(() => { @@ -39,18 +47,23 @@ export function SettingsDownloadSources() { if (sourceUrl) setShowAddDownloadSourceModal(true); }, [sourceUrl]); - const handleRemoveSource = async (id: number) => { - await db.transaction("rw", downloadSourcesTable, repacksTable, async () => { - await downloadSourcesTable.where({ id }).delete(); - await repacksTable.where({ downloadSourceId: id }).delete(); - }); + const handleRemoveSource = (id: number) => { + setIsRemovingDownloadSource(true); + const channel = new BroadcastChannel(`download_sources:delete:${id}`); - showSuccessToast(t("removed_download_source")); + downloadSourcesWorker.postMessage(["DELETE_DOWNLOAD_SOURCE", id]); - getDownloadSources(); + channel.onmessage = () => { + showSuccessToast(t("removed_download_source")); + + getDownloadSources(); + indexRepacks(); + setIsRemovingDownloadSource(false); + }; }; const handleAddDownloadSource = async () => { + indexRepacks(); await getDownloadSources(); showSuccessToast(t("added_download_source")); }; @@ -59,7 +72,7 @@ export function SettingsDownloadSources() { setIsSyncingDownloadSources(true); window.electron - .syncDownloadSources() + .syncDownloadSources(downloadSources) .then(() => { showSuccessToast(t("download_sources_synced")); getDownloadSources(); @@ -93,7 +106,11 @@ export function SettingsDownloadSources() {