diff --git a/backend/settings/settings.go b/backend/settings/settings.go index 7f0b1a26..b7aa8793 100644 --- a/backend/settings/settings.go +++ b/backend/settings/settings.go @@ -16,8 +16,9 @@ import ( ) type SavedModFilters struct { - Order string `json:"order"` - Filter string `json:"filter"` + Order string `json:"order"` + Filter string `json:"filter"` + TagSearchMode string `json:"tagSearchMode,omitempty"` } type View string @@ -35,6 +36,11 @@ var ( UpdateAsk UpdateCheckMode = "ask" ) +const ( + TagSearchModeAny = "any" + TagSearchModeAnd = "and" +) + type settings struct { WindowPosition *utils.Position `json:"windowPosition,omitempty"` Maximized bool `json:"maximized,omitempty"` @@ -83,8 +89,9 @@ var Settings = &settings{ FavoriteMods: []string{}, ModFilters: SavedModFilters{ - Order: "last-updated", - Filter: "compatible", + Order: "last-updated", + Filter: "compatible", + TagSearchMode: TagSearchModeAny, }, RemoteNames: map[string]string{}, @@ -182,6 +189,21 @@ func (s *settings) SetModFiltersFilter(filter string) { _ = SaveSettings() } +func (s *settings) GetModFiltersTagSearchMode() string { + if s.ModFilters.TagSearchMode != TagSearchModeAnd && s.ModFilters.TagSearchMode != TagSearchModeAny { + return TagSearchModeAny + } + return s.ModFilters.TagSearchMode +} + +func (s *settings) SetModFiltersTagSearchMode(mode string) { + if mode != TagSearchModeAnd && mode != TagSearchModeAny { + return + } + s.ModFilters.TagSearchMode = mode + _ = SaveSettings() +} + func (s *settings) emitFavoriteMods() { wailsRuntime.EventsEmit(common.AppContext, "favoriteMods", s.FavoriteMods) } diff --git a/frontend/src/lib/components/mods-list/ModsList.svelte b/frontend/src/lib/components/mods-list/ModsList.svelte index 57aebff2..60e6a1bb 100644 --- a/frontend/src/lib/components/mods-list/ModsList.svelte +++ b/frontend/src/lib/components/mods-list/ModsList.svelte @@ -14,8 +14,8 @@ import { queuedMods } from '$lib/store/actionQueue'; import { favoriteMods, lockfileMods, manifestMods, selectedProfileTargets } from '$lib/store/ficsitCLIStore'; import { expandedMod, hasFetchedMods } from '$lib/store/generalStore'; - import { type OfflineMod, type PartialMod, filter, filterOptions, order, search } from '$lib/store/modFiltersStore'; - import { offline, startView } from '$lib/store/settingsStore'; + import { type OfflineMod, type PartialMod, type TagOption, filter, filterOptions, order, search, selectedTags } from '$lib/store/modFiltersStore'; + import { offline, startView, tagSearchMode } from '$lib/store/settingsStore'; import { OfflineGetMods } from '$wailsjs/go/ficsitcli/ficsitCLI'; const dispatch = createEventDispatcher(); @@ -92,6 +92,18 @@ $: mods = [...knownMods, ...unknownMods]; + $: availableTags = ((): TagOption[] => { + const tagNames = new Set(); + for (const mod of knownMods) { + if ('tags' in mod && mod.tags) { + for (const t of mod.tags) { + tagNames.add(t.name); + } + } + } + return [...tagNames].sort((a, b) => a.localeCompare(b)).map((name) => ({ id: name, name })); + })(); + let filteredMods: PartialMod[] = []; let filteringMods = false; $: { @@ -121,13 +133,26 @@ sortedMods = _.sortBy(filteredMods, $order.func) as PartialMod[]; } + $: selectedTagIds = new Set($selectedTags.map((t) => t.id)); + $: tagFilteredMods = (() => { + if (selectedTagIds.size === 0) return sortedMods; + return sortedMods.filter((mod) => { + if (!('tags' in mod) || !mod.tags) return false; + const modTagNames = new Set(mod.tags.map((t) => t.name)); + if ($tagSearchMode === 'and') { + return [...selectedTagIds].every((id) => modTagNames.has(id)); + } + return [...selectedTagIds].some((id) => modTagNames.has(id)); + }); + })(); + let displayMods: PartialMod[] = []; $: if(!$search) { - displayMods = sortedMods; + displayMods = tagFilteredMods; } else { const modifiedSearchString = $search.replace(/(?:author:"(.+?)"|author:([^\s"]+))/g, '="$1$2"'); - const fuse = new Fuse(sortedMods, { + const fuse = new Fuse(tagFilteredMods, { keys: [ { name: 'name', @@ -165,12 +190,14 @@ $: userHasSearchText = $search != ''; $: userHasSearchFilters = $filter != filterOptions[0]; + $: userHasTagFilter = $selectedTags.length > 0; const removeSearchText = () => { $search = ''; }; const removeSearchFilters = () => { $filter = filterOptions[0]; + $selectedTags = []; }; export let hideMods: boolean = false; @@ -178,7 +205,7 @@
- +
{#if hideMods} @@ -195,7 +222,7 @@
{#if mods.length !== 0}

- {#if userHasSearchFilters} + {#if userHasSearchFilters || userHasTagFilter} {#if userHasSearchText}
+
+
+ +
+ +