diff --git a/backend/settings/settings.go b/backend/settings/settings.go index 7f0b1a26..2cb321ef 100644 --- a/backend/settings/settings.go +++ b/backend/settings/settings.go @@ -15,9 +15,12 @@ import ( "github.com/satisfactorymodding/SatisfactoryModManager/backend/utils" ) +type TagSearchMode string + type SavedModFilters struct { - Order string `json:"order"` - Filter string `json:"filter"` + Order string `json:"order"` + Filter string `json:"filter"` + TagSearchMode TagSearchMode `json:"tagSearchMode,omitempty"` } type View string @@ -35,6 +38,11 @@ var ( UpdateAsk UpdateCheckMode = "ask" ) +const ( + TagSearchModeAny TagSearchMode = "any" + TagSearchModeAll TagSearchMode = "all" +) + type settings struct { WindowPosition *utils.Position `json:"windowPosition,omitempty"` Maximized bool `json:"maximized,omitempty"` @@ -83,8 +91,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 +191,15 @@ func (s *settings) SetModFiltersFilter(filter string) { _ = SaveSettings() } +func (s *settings) GetModFiltersTagSearchMode() TagSearchMode { + return s.ModFilters.TagSearchMode +} + +func (s *settings) SetModFiltersTagSearchMode(mode TagSearchMode) { + s.ModFilters.TagSearchMode = mode + _ = SaveSettings() +} + func (s *settings) emitFavoriteMods() { wailsRuntime.EventsEmit(common.AppContext, "favoriteMods", s.FavoriteMods) } @@ -463,3 +481,11 @@ func SaveSettings() error { return nil } + +var AllTagSearchModes = []struct { + Value TagSearchMode + TSName string +}{ + {TagSearchModeAll, "ALL"}, + {TagSearchModeAny, "ANY"}, +} diff --git a/frontend/src/lib/components/mods-list/ModsList.svelte b/frontend/src/lib/components/mods-list/ModsList.svelte index 57aebff2..f2e2b1fd 100644 --- a/frontend/src/lib/components/mods-list/ModsList.svelte +++ b/frontend/src/lib/components/mods-list/ModsList.svelte @@ -14,9 +14,13 @@ 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 PartialSMRMod, filter, filterOptions, order, search, + selectedTags, + } from '$lib/store/modFiltersStore'; + import { offline, startView, tagSearchMode } from '$lib/store/settingsStore'; import { OfflineGetMods } from '$wailsjs/go/ficsitcli/ficsitCLI'; + import { settings } from '$wailsjs/go/models'; const dispatch = createEventDispatcher(); @@ -25,7 +29,7 @@ const client = getContextClient(); let fetchingMods = false; - let onlineMods: PartialMod[] = []; + let onlineMods: PartialSMRMod[] = []; async function fetchAllModsOnline() { try { const result = await client.query(GetModCountDocument, {}, { requestPolicy: 'network-only' }).toPromise(); @@ -46,7 +50,7 @@ } } - let offlineMods: PartialMod[] = []; + let offlineMods: OfflineMod[] = []; async function fetchAllModsOffline() { offlineMods = (await OfflineGetMods()).map((mod) => ({ ...mod, @@ -92,6 +96,8 @@ $: mods = [...knownMods, ...unknownMods]; + $: availableTags = _.sortBy(_.uniqBy(onlineMods?.map((mod) => mod.tags ?? []).flat() ?? [], 'id'), 'name'); + let filteredMods: PartialMod[] = []; let filteringMods = false; $: { @@ -101,13 +107,25 @@ $favoriteMods; $queuedMods; $selectedProfileTargets; + $selectedTags; + $tagSearchMode; filteringMods = true; - Promise.all(mods.map((mod) => $filter.func(mod, client))).then((results) => { - filteredMods = mods.filter((_, i) => results[i]); - }).then(() => { - filteringMods = false; - }); + Promise.all(mods.map((mod) => $filter.func(mod, client))) + .then((results) => mods.filter((_, i) => results[i])) + .then((filtered) => { + filteredMods = (filtered.filter((mod) => !('tags' in mod) || !mod.tags || matchTags(mod.tags.map((t) => t.id), $selectedTags))); + filteringMods = false; + }); + } + + function matchTags(modTags: string[], tagIds: string[]): boolean { + if (tagIds.length === 0) return true; + const modTagsSet = new Set(modTags); + if ($tagSearchMode === settings.TagSearchMode.ALL) { + return tagIds.every((id) => modTagsSet.has(id)); + } + return tagIds.some((id) => modTagsSet.has(id)); } let sortedMods: PartialMod[] = []; @@ -165,12 +183,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 +198,7 @@