From 1d5be940f9416ebe11639cbc0e56aede3dcc7e68 Mon Sep 17 00:00:00 2001 From: caduHD4 Date: Sun, 28 Sep 2025 01:00:02 -0300 Subject: [PATCH 1/4] feat: add filter by source in modal repacks --- src/locales/en/translation.json | 3 +- src/locales/pt-BR/translation.json | 3 +- .../game-details/modals/repacks-modal.scss | 140 +++++++++++++- .../game-details/modals/repacks-modal.tsx | 180 ++++++++++++++---- 4 files changed, 286 insertions(+), 40 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 781cd946..4542d448 100755 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -244,7 +244,8 @@ "supported_languages": "Supported languages", "language": "Language", "caption": "Caption", - "audio": "Audio" + "audio": "Audio", + "filter_by_source": "Filter by source" }, "activation": { "title": "Activate Hydra", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index fd6fbd97..20e21fd5 100755 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -221,7 +221,8 @@ "supported_languages": "Idiomas suportados", "language": "Idioma", "caption": "Legenda", - "audio": "Áudio" + "audio": "Áudio", + "filter_by_source": "Filtrar por fonte" }, "activation": { "title": "Ativação", diff --git a/src/renderer/src/pages/game-details/modals/repacks-modal.scss b/src/renderer/src/pages/game-details/modals/repacks-modal.scss index 2eb2512d..c1f3b3ba 100644 --- a/src/renderer/src/pages/game-details/modals/repacks-modal.scss +++ b/src/renderer/src/pages/game-details/modals/repacks-modal.scss @@ -2,7 +2,48 @@ .repacks-modal { &__filter-container { - margin-bottom: calc(globals.$spacing-unit * 2); + margin-bottom: 1rem; + transition: min-height 0.3s ease; + + &--drawer-open { + min-height: 250px; + } + } + + &__filter-top { + margin-bottom: 1rem; + display: flex; + flex-direction: column; + gap: 0.5rem; + } + + &__filter-toggle { + align-self: flex-start; + display: flex; + align-items: center; + gap: 0.5rem; + font-size: globals.$small-font-size; + font-weight: 600; + color: var(--color-text-secondary); + padding: 0.5rem 0.75rem; + border-radius: 6px; + transition: background-color 0.2s ease; + display: flex; + flex-direction: column; + gap: 0.5rem; + } + + &__filter-toggle { + align-self: flex-start; + display: flex; + align-items: center; + gap: 0.5rem; + font-size: globals.$small-font-size; + font-weight: 600; + color: var(--color-text-secondary); + padding: 0.5rem 0.75rem; + border-radius: 6px; + transition: background-color 0.2s ease; } &__repacks { @@ -29,4 +70,101 @@ &__repack-info { font-size: globals.$small-font-size; } + + &__no-results { + width: 100%; + padding: calc(globals.$spacing-unit * 4) 0; + text-align: center; + color: globals.$muted-color; + font-size: globals.$small-font-size; + display: flex; + align-items: center; + justify-content: center; + } + + &__no-results-content { + display: flex; + flex-direction: column; + align-items: center; + gap: calc(globals.$spacing-unit * 1.5); + max-width: 480px; + width: 100%; + } + + &__no-results-text { + color: globals.$muted-color; + font-size: globals.$small-font-size; + text-align: center; + } + + &__no-results-button { + display: flex; + justify-content: center; + width: 100%; + } + + &__download-sources { + padding: 0; + background-color: var(--color-background-light); + border-radius: 8px; + margin-bottom: 1rem; + margin-top: calc(globals.$spacing-unit * 0.5); + max-height: 0; + overflow: hidden; + transition: + max-height 0.3s ease, + padding 0.3s ease; + + &--open { + padding: 0.75rem; + max-height: 250px; + } + } + + &__filter-label { + display: none; + font-size: globals.$small-font-size; + font-weight: 600; + margin-bottom: 0.75rem; + color: var(--color-text-secondary); + width: 100%; + } + + &__source-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 0.5rem; + max-height: 200px; + overflow-y: auto; + overflow-x: hidden; + align-items: start; + padding-right: 0.25rem; + } + + &__source-item { + padding: 0.35rem 0.5rem; + background: var(--color-surface, rgba(0, 0, 0, 0.03)); + border: 1px solid var(--color-border); + border-radius: 6px; + display: flex; + align-items: center; + min-height: auto; + box-sizing: border-box; + width: 100%; + } + + &__source-item :global(.checkbox-field) { + width: 100%; + min-width: 0; + } + + &__source-item :global(.checkbox-field__label) { + white-space: normal; + overflow: visible; + text-overflow: unset; + display: block; + font-size: 0.85rem; + width: 100%; + word-break: break-word; + } } diff --git a/src/renderer/src/pages/game-details/modals/repacks-modal.tsx b/src/renderer/src/pages/game-details/modals/repacks-modal.tsx index d38c7856..b2c55d4f 100644 --- a/src/renderer/src/pages/game-details/modals/repacks-modal.tsx +++ b/src/renderer/src/pages/game-details/modals/repacks-modal.tsx @@ -1,5 +1,11 @@ import { useContext, useEffect, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; +import { useNavigate } from "react-router-dom"; +import { + PlusCircleIcon, + ChevronDownIcon, + ChevronUpIcon, +} from "@primer/octicons-react"; import { Badge, @@ -7,7 +13,10 @@ import { DebridBadge, Modal, TextField, + CheckboxField, } from "@renderer/components"; +import { downloadSourcesTable } from "@renderer/dexie"; +import type { DownloadSource } from "@types"; import type { GameRepack } from "@types"; import { DownloadSettingsModal } from "./download-settings-modal"; @@ -36,6 +45,11 @@ export function RepacksModal({ const [filteredRepacks, setFilteredRepacks] = useState([]); const [repack, setRepack] = useState(null); const [showSelectFolderModal, setShowSelectFolderModal] = useState(false); + const [downloadSources, setDownloadSources] = useState([]); + const [selectedFingerprints, setSelectedFingerprints] = useState( + [] + ); + const [filterTerm, setFilterTerm] = useState(""); const [hashesInDebrid, setHashesInDebrid] = useState>( {} @@ -46,6 +60,7 @@ export function RepacksModal({ const { t } = useTranslation("game_details"); const { formatDate } = useDate(); + const navigate = useNavigate(); const getHashFromMagnet = (magnet: string) => { if (!magnet || typeof magnet !== "string") { @@ -90,8 +105,37 @@ export function RepacksModal({ }, [repacks, hashesInDebrid]); useEffect(() => { - setFilteredRepacks(sortedRepacks); - }, [sortedRepacks, visible, game]); + downloadSourcesTable.toArray().then((sources) => { + const uniqueRepackers = new Set(sortedRepacks.map((r) => r.repacker)); + const filteredSources = sources.filter( + (s) => s.name && uniqueRepackers.has(s.name) && !!s.fingerprint + ); + setDownloadSources(filteredSources); + }); + }, [sortedRepacks]); + + useEffect(() => { + const term = filterTerm.trim().toLowerCase(); + + const byTerm = sortedRepacks.filter((repack) => { + if (!term) return true; + const lowerTitle = repack.title.toLowerCase(); + const lowerRepacker = repack.repacker.toLowerCase(); + return lowerTitle.includes(term) || lowerRepacker.includes(term); + }); + + const bySource = byTerm.filter((repack) => { + if (selectedFingerprints.length === 0) return true; + + return downloadSources.some( + (src) => + selectedFingerprints.includes(src.fingerprint) && + src.name === repack.repacker + ); + }); + + setFilteredRepacks(bySource); + }, [sortedRepacks, filterTerm, selectedFingerprints, downloadSources]); const handleRepackClick = (repack: GameRepack) => { setRepack(repack); @@ -99,17 +143,14 @@ export function RepacksModal({ }; const handleFilter: React.ChangeEventHandler = (event) => { - const term = event.target.value.toLocaleLowerCase(); + setFilterTerm(event.target.value); + }; - setFilteredRepacks( - sortedRepacks.filter((repack) => { - const lowerCaseTitle = repack.title.toLowerCase(); - const lowerCaseRepacker = repack.repacker.toLowerCase(); - - return [lowerCaseTitle, lowerCaseRepacker].some((value) => - value.includes(term) - ); - }) + const toggleFingerprint = (fingerprint: string) => { + setSelectedFingerprints((prev) => + prev.includes(fingerprint) + ? prev.filter((f) => f !== fingerprint) + : [...prev, fingerprint] ); }; @@ -118,6 +159,8 @@ export function RepacksModal({ return repack.uris.some((uri) => uri.includes(game.download!.uri)); }; + const [isFilterDrawerOpen, setIsFilterDrawerOpen] = useState(false); + return ( <> -
- +
+
+ + +
+ +
+
+ {downloadSources.map((source) => { + const label = source.name || source.url; + const truncatedLabel = + label.length > 16 ? label.substring(0, 16) + "..." : label; + return ( +
+ toggleFingerprint(source.fingerprint)} + /> +
+ ); + })} +
+
- {filteredRepacks.map((repack) => { - const isLastDownloadedOption = checkIfLastDownloadedOption(repack); + {filteredRepacks.length === 0 ? ( +
+
+
+ {t("no_repacks_found")} +
+
+ +
+
+
+ ) : ( + filteredRepacks.map((repack) => { + const isLastDownloadedOption = + checkIfLastDownloadedOption(repack); - return ( - - ); - })} + {hashesInDebrid[getHashFromMagnet(repack.uris[0]) ?? ""] && ( + + )} + + ); + }) + )}
From 6f5baf0df59eef0c33500d701b11e4d27ac3eeab Mon Sep 17 00:00:00 2001 From: caduHD4 Date: Sun, 28 Sep 2025 01:16:33 -0300 Subject: [PATCH 2/4] refactor: duplicate code --- .../pages/game-details/modals/repacks-modal.scss | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/src/renderer/src/pages/game-details/modals/repacks-modal.scss b/src/renderer/src/pages/game-details/modals/repacks-modal.scss index c1f3b3ba..cecadb91 100644 --- a/src/renderer/src/pages/game-details/modals/repacks-modal.scss +++ b/src/renderer/src/pages/game-details/modals/repacks-modal.scss @@ -17,22 +17,6 @@ gap: 0.5rem; } - &__filter-toggle { - align-self: flex-start; - display: flex; - align-items: center; - gap: 0.5rem; - font-size: globals.$small-font-size; - font-weight: 600; - color: var(--color-text-secondary); - padding: 0.5rem 0.75rem; - border-radius: 6px; - transition: background-color 0.2s ease; - display: flex; - flex-direction: column; - gap: 0.5rem; - } - &__filter-toggle { align-self: flex-start; display: flex; From 81f001ade455b747a9cdc87cdb2551a52f94e934 Mon Sep 17 00:00:00 2001 From: caduHD4 Date: Sun, 28 Sep 2025 02:18:35 -0300 Subject: [PATCH 3/4] fix: add 'no sources found for this game' message in translation files --- src/locales/en/translation.json | 3 ++- src/locales/pt-BR/translation.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 4542d448..52a9473d 100755 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -245,7 +245,8 @@ "language": "Language", "caption": "Caption", "audio": "Audio", - "filter_by_source": "Filter by source" + "filter_by_source": "Filter by source", + "no_repacks_found": "No sources found for this game" }, "activation": { "title": "Activate Hydra", diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 20e21fd5..d7e67eb1 100755 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -222,7 +222,8 @@ "language": "Idioma", "caption": "Legenda", "audio": "Áudio", - "filter_by_source": "Filtrar por fonte" + "filter_by_source": "Filtrar por fonte", + "no_repacks_found": "Nenhuma fonte encontrada para este jogo" }, "activation": { "title": "Ativação", From e3cd596fb2ed98e18cf492d7fa85b0a0e321233a Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Mariano Garcia Pereira <69264602+caduHD4@users.noreply.github.com> Date: Tue, 30 Sep 2025 14:46:16 -0300 Subject: [PATCH 4/4] Fix "," for 'no_repacks_found' key --- src/locales/pt-BR/translation.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/locales/pt-BR/translation.json b/src/locales/pt-BR/translation.json index 6dac122f..b361dba1 100755 --- a/src/locales/pt-BR/translation.json +++ b/src/locales/pt-BR/translation.json @@ -236,7 +236,7 @@ "caption": "Legenda", "audio": "Áudio", "filter_by_source": "Filtrar por fonte", - "no_repacks_found": "Nenhuma fonte encontrada para este jogo" + "no_repacks_found": "Nenhuma fonte encontrada para este jogo", "edit_game_modal_button": "Alterar detalhes do jogo", "game_added_to_pinned": "Jogo adicionado aos fixados", "game_removed_from_pinned": "Jogo removido dos fixados",