diff --git a/src/tribler/ui/package-lock.json b/src/tribler/ui/package-lock.json index 86c493fd31..1db75c3130 100644 --- a/src/tribler/ui/package-lock.json +++ b/src/tribler/ui/package-lock.json @@ -1,12 +1,12 @@ { "name": "tribler-webui", - "version": "0.1", + "version": "0.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "tribler-webui", - "version": "0.1", + "version": "0.1.0", "dependencies": { "@hookform/resolvers": "^3.3.2", "@radix-ui/react-accordion": "^1.1.2", @@ -31,6 +31,7 @@ "i18next": "^23.11.4", "javascript-time-ago": "^2.5.10", "js-cookie": "^3.0.5", + "jszip": "^3.10.1", "lucide-react": "^0.292.0", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -56,7 +57,7 @@ "postcss": "^8.4.31", "react-hot-toast": "^2.4.1", "tailwindcss": "^3.3.3", - "typescript": "^4.6.4", + "typescript": "^5.5.0", "vite": "^3.1.0" } }, @@ -2099,6 +2100,11 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, "node_modules/cross-fetch": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", @@ -2781,6 +2787,11 @@ "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", "dev": true }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -2852,6 +2863,11 @@ "node": ">=0.12.0" } }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, "node_modules/javascript-time-ago": { "version": "2.5.10", "resolved": "https://registry.npmjs.org/javascript-time-ago/-/javascript-time-ago-2.5.10.tgz", @@ -2905,6 +2921,52 @@ "node": ">=6" } }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/jszip/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/jszip/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/jszip/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dependencies": { + "immediate": "~3.0.5" + } + }, "node_modules/lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -3182,6 +3244,11 @@ "wrappy": "1" } }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -3351,6 +3418,11 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -3713,6 +3785,11 @@ "semver": "bin/semver.js" } }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, "node_modules/simple-update-notifier": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", @@ -3932,16 +4009,16 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/undefsafe": { @@ -5384,6 +5461,11 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" + }, "cross-fetch": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", @@ -5778,6 +5860,11 @@ "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==", "dev": true }, + "immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==" + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -5834,6 +5921,11 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==" + }, "javascript-time-ago": { "version": "2.5.10", "resolved": "https://registry.npmjs.org/javascript-time-ago/-/javascript-time-ago-2.5.10.tgz", @@ -5869,6 +5961,54 @@ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true }, + "jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "requires": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "requires": { + "immediate": "~3.0.5" + } + }, "lilconfig": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", @@ -6065,6 +6205,11 @@ "wrappy": "1" } }, + "pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==" + }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -6154,6 +6299,11 @@ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==" }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, "proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -6360,6 +6510,11 @@ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, "simple-update-notifier": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-1.1.0.tgz", @@ -6530,9 +6685,9 @@ "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" }, "typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true }, "undefsafe": { diff --git a/src/tribler/ui/package.json b/src/tribler/ui/package.json index 52eb8f9909..45ef3f2e6c 100644 --- a/src/tribler/ui/package.json +++ b/src/tribler/ui/package.json @@ -32,6 +32,7 @@ "i18next": "^23.11.4", "javascript-time-ago": "^2.5.10", "js-cookie": "^3.0.5", + "jszip": "^3.10.1", "lucide-react": "^0.292.0", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/src/tribler/ui/src/lib/utils.ts b/src/tribler/ui/src/lib/utils.ts index 60b8430e2c..26c1be461b 100644 --- a/src/tribler/ui/src/lib/utils.ts +++ b/src/tribler/ui/src/lib/utils.ts @@ -10,8 +10,9 @@ import zh from 'javascript-time-ago/locale/zh' import Cookies from "js-cookie"; import { useTranslation } from "react-i18next"; import { triblerService } from "@/services/tribler.service"; -import { FileTreeItem } from "@/models/file.model"; +import { FileLink, FileTreeItem } from "@/models/file.model"; import { CheckedState } from "@radix-ui/react-checkbox"; +import JSZip from "jszip"; TimeAgo.setDefaultLocale(en.locale) TimeAgo.addLocale(en) @@ -225,3 +226,29 @@ export const getSelectedFilesFromTree = (tree: FileTreeItem, included: boolean = selectedFiles.push(tree.index); return selectedFiles; } + +export function downloadFile(file: FileLink) { + var link = document.createElement("a"); + link.download = file.name; + link.href = file.uri; + link.click(); +} + +export async function downloadFilesAsZip(files: FileLink[], zipName: string) { + const zip = new JSZip(); + for (let i = 0; i < files.length; i++) { + const response = await fetch(files[i].uri); + if (response.status != 200) continue; + const blob = await response.blob(); + + zip.file(files[i].name, blob); + + if (i == files.length - 1) { + const zipData = await zip.generateAsync({ type: "blob" }); + const link = document.createElement("a"); + link.href = window.URL.createObjectURL(zipData); + link.download = zipName; + link.click(); + } + } +} diff --git a/src/tribler/ui/src/models/file.model.tsx b/src/tribler/ui/src/models/file.model.tsx index dd9aee08b7..4acc56dcc3 100644 --- a/src/tribler/ui/src/models/file.model.tsx +++ b/src/tribler/ui/src/models/file.model.tsx @@ -19,3 +19,8 @@ export interface FileTreeItem { included?: CheckedState; subRows?: FileTreeItem[]; } + +export interface FileLink { + uri: string; + name: string; +} diff --git a/src/tribler/ui/src/pages/Downloads/Actions.tsx b/src/tribler/ui/src/pages/Downloads/Actions.tsx index e9c8c1861b..81c40ba45d 100644 --- a/src/tribler/ui/src/pages/Downloads/Actions.tsx +++ b/src/tribler/ui/src/pages/Downloads/Actions.tsx @@ -22,6 +22,7 @@ import { useState } from "react"; import { Label } from "@/components/ui/label"; import { useTranslation } from "react-i18next"; import { PathInput } from "@/components/path-input"; +import { downloadFile, downloadFilesAsZip } from "@/lib/utils"; export default function Actions({ selectedDownloads }: { selectedDownloads: Download[] }) { @@ -36,10 +37,10 @@ export default function Actions({ selectedDownloads }: { selectedDownloads: Down const response = await triblerService.resumeDownload(download.infohash); if (response === undefined) { toast.error(`${t("ToastErrorDownloadPlay")} ${t("ToastErrorGenNetworkErr")}`); - } else if (isErrorDict(response)){ + } else if (isErrorDict(response)) { toast.error(`${t("ToastErrorDownloadPlay")} ${response.error}`); } - })(); + })(); }); } const onPause = () => { @@ -48,10 +49,10 @@ export default function Actions({ selectedDownloads }: { selectedDownloads: Down const response = await triblerService.stopDownload(download.infohash); if (response === undefined) { toast.error(`${t("ToastErrorDownloadStop")} ${t("ToastErrorGenNetworkErr")}`); - } else if (isErrorDict(response)){ + } else if (isErrorDict(response)) { toast.error(`${t("ToastErrorDownloadStop")} ${response.error}`); } - })(); + })(); }); } const onRemove = (removeData: boolean) => { @@ -60,10 +61,10 @@ export default function Actions({ selectedDownloads }: { selectedDownloads: Down const response = await triblerService.removeDownload(download.infohash, removeData); if (response === undefined) { toast.error(`${t("ToastErrorDownloadRemove")} ${t("ToastErrorGenNetworkErr")}`); - } else if (isErrorDict(response)){ + } else if (isErrorDict(response)) { toast.error(`${t("ToastErrorDownloadRemove")} ${response.error}`); } - })(); + })(); }); setRemoveDialogOpen(false); } @@ -73,18 +74,20 @@ export default function Actions({ selectedDownloads }: { selectedDownloads: Down const response = await triblerService.recheckDownload(download.infohash); if (response === undefined) { toast.error(`${t("ToastErrorDownloadCheck")} ${t("ToastErrorGenNetworkErr")}`); - } else if (isErrorDict(response)){ + } else if (isErrorDict(response)) { toast.error(`${t("ToastErrorDownloadCheck")} ${response.error}`); } - })(); + })(); }); } const onExportTorrent = () => { - const link = document.createElement('a'); - link.href = `/api/downloads/${selectedDownloads[0].infohash}/torrent`; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); + const files = selectedDownloads.map((download) => ({ + uri: `/api/downloads/${download.infohash}/torrent`, + name: `${download.infohash}.torrent` + })); + + if (files.length == 1) downloadFile(files[0]); + else if (files.length > 1) downloadFilesAsZip(files, 'torrents.zip'); } const onMoveDownload = () => { if (selectedDownloads.length == 1) { @@ -96,7 +99,7 @@ export default function Actions({ selectedDownloads }: { selectedDownloads: Down triblerService.moveDownload(selectedDownloads[0].infohash, storageLocation).then(async (response) => { if (response === undefined) { toast.error(`${t("ToastErrorDownloadMove")} ${t("ToastErrorGenNetworkErr")}`); - } else if (isErrorDict(response)){ + } else if (isErrorDict(response)) { toast.error(`${t("ToastErrorDownloadMove")} ${response.error}`); } }); @@ -108,10 +111,10 @@ export default function Actions({ selectedDownloads }: { selectedDownloads: Down const response = await triblerService.setDownloadHops(download.infohash, hops); if (response === undefined) { toast.error(`${t("ToastErrorDownloadSetHops")} ${t("ToastErrorGenNetworkErr")}`); - } else if (isErrorDict(response)){ + } else if (isErrorDict(response)) { toast.error(`${t("ToastErrorDownloadSetHops")} ${response.error}`); } - })(); + })(); }); } @@ -212,7 +215,7 @@ export default function Actions({ selectedDownloads }: { selectedDownloads: Down {t('ForceRecheck')} - onExportTorrent()} disabled={selectedDownloads.length !== 1}> + onExportTorrent()} disabled={selectedDownloads.length < 1}> {t('ExportTorrent')}