disclosures in search

This commit is contained in:
Prospector
2026-08-06 18:43:22 -07:00
parent ebf19eb828
commit 746bc9a55e
28 changed files with 329 additions and 184 deletions
@@ -0,0 +1,100 @@
<template>
<NewModal
ref="modal"
:header="formatMessage(messages.title)"
:on-hide="handleHide"
:on-after-hide="handleAfterHide"
:closable="false"
fade="warning"
max-width="544px"
>
<div class="flex flex-col gap-6">
<p class="m-0 leading-normal">
{{ formatMessage(messages.body1) }}
</p>
<p class="m-0 leading-normal">
{{ formatMessage(messages.body2) }}
</p>
<div class="flex justify-between items-center">
<Checkbox v-model="dontShowAgain" :label="formatMessage(messages.dontShowAgain)" />
<Button type="colored" color="brand" native-type="button" @click="acknowledge">
<CheckCircleIcon class="size-4" />
{{ formatMessage(messages.iUnderstand) }}
</Button>
</div>
</div>
</NewModal>
</template>
<script setup lang="ts">
import { CheckCircleIcon } from '@modrinth/assets'
import { ref, useTemplateRef } from 'vue'
import { Button } from '#ui/components/base/buttons'
import { defineMessages, useVIntl } from '../../composables/i18n'
import Checkbox from '../base/Checkbox.vue'
import NewModal from './NewModal.vue'
const emit = defineEmits<{
dismiss: [dontShowAgain: boolean]
}>()
const { formatMessage } = useVIntl()
const modal = useTemplateRef('modal')
const dontShowAgain = ref(false)
let pendingDontShowAgain = false
const messages = defineMessages({
title: {
id: 'search.photosensitivity-warning-modal.title',
defaultMessage: 'Results are not guaranteed to be safe',
},
body1: {
id: 'search.photosensitivity-warning-modal.body.1',
defaultMessage:
'We cannot guarantee that all content on Modrinth that may be dangerous for users with photosensitivity is labeled appropriately with a photosensitivity warning.',
},
body2: {
id: 'search.photosensitivity-warning-modal.body.2',
defaultMessage:
'The label is self-assigned by the users who uploaded the content, and do not indicate that the content has gone through any kind of safety testing. Using any content on Modrinth is at your own risk. Please stay safe.',
},
dontShowAgain: {
id: 'search.photosensitivity-warning-modal.dont-show-again',
defaultMessage: "Don't show this again",
},
iUnderstand: {
id: 'search.photosensitivity-warning-modal.i-understand',
defaultMessage: 'I understand',
},
})
function show() {
dontShowAgain.value = false
pendingDontShowAgain = false
modal.value?.show()
}
function hide() {
modal.value?.hide()
}
function handleHide() {
pendingDontShowAgain = dontShowAgain.value
}
function handleAfterHide() {
emit('dismiss', pendingDontShowAgain)
dontShowAgain.value = false
pendingDontShowAgain = false
}
function acknowledge() {
pendingDontShowAgain = dontShowAgain.value
modal.value?.hide()
}
defineExpose({ show, hide })
</script>
@@ -3,6 +3,7 @@ export { default as ConfirmModal } from './ConfirmModal.vue'
export { default as NewModal } from './NewModal.vue'
export type { ServerProject as OpenInAppModalServerProject } from './OpenInAppModal.vue'
export { default as OpenInAppModal } from './OpenInAppModal.vue'
export { default as PhotosensitivityWarningModal } from './PhotosensitivityWarningModal.vue'
export { default as ShareModal } from './ShareModal.vue'
export type { Tab as TabbedModalTab } from './TabbedModal.vue'
export { default as TabbedModal } from './TabbedModal.vue'
@@ -74,6 +74,7 @@ export interface BrowseManagerContext {
serverOnlyLabel?: ComputedRef<string>
hiddenFilterTypes?: ComputedRef<string[]>
advancedFiltersCollapsed?: Ref<boolean>
dismissedPhotosensitivityFilterWarning?: Ref<boolean>
onInstalled?: (projectId: string) => void
displayMode?: Ref<'list' | 'grid' | 'gallery'> | ComputedRef<'list' | 'grid' | 'gallery'>
@@ -1,15 +1,18 @@
<script setup lang="ts">
import { InfoIcon, XIcon } from '@modrinth/assets'
import { computed, toValue } from 'vue'
import { computed, nextTick, toValue, useTemplateRef, watch } from 'vue'
import { IconButton } from '#ui/components/base/buttons'
import Toggle from '#ui/components/base/Toggle.vue'
import PhotosensitivityWarningModal from '#ui/components/modal/PhotosensitivityWarningModal.vue'
import SearchSidebarFilter from '#ui/components/search/SearchSidebarFilter.vue'
import { useVIntl } from '#ui/composables/i18n'
import { commonMessages } from '#ui/utils/common-messages'
import { injectBrowseManager } from './providers/browse-manager'
const PHOTOSENSITIVITY_FILTER_OPTION = 'epilepsy_triggers'
const ctx = injectBrowseManager()
const { formatMessage } = useVIntl()
@@ -18,6 +21,7 @@ const lockedMessages = computed(() => toValue(ctx.lockedFilterMessages))
const hiddenFilterTypes = computed(() => ctx.hiddenFilterTypes?.value ?? [])
const advancedFiltersCollapsed = computed(() => ctx.advancedFiltersCollapsed?.value ?? true)
const photosensitivityWarningModal = useTemplateRef('photosensitivityWarningModal')
function setAdvancedFiltersCollapsed(collapsed: boolean) {
if (ctx.advancedFiltersCollapsed) {
@@ -25,6 +29,28 @@ function setAdvancedFiltersCollapsed(collapsed: boolean) {
}
}
function isPhotosensitivityExclusionSelected(filters: { type: string; option: string }[]) {
return filters.some(
(filter) => filter.type === 'advanced' && filter.option === PHOTOSENSITIVITY_FILTER_OPTION,
)
}
watch(
() => isPhotosensitivityExclusionSelected(ctx.currentFilters.value),
(isSelected, wasSelected) => {
if (isSelected && !wasSelected && !ctx.dismissedPhotosensitivityFilterWarning?.value) {
nextTick(() => photosensitivityWarningModal.value?.show())
}
},
{ immediate: true },
)
function onPhotosensitivityWarningDismiss(dontShowAgain: boolean) {
if (dontShowAgain && ctx.dismissedPhotosensitivityFilterWarning) {
ctx.dismissedPhotosensitivityFilterWarning.value = true
}
}
function closeFiltersMenu() {
if (ctx.filtersMenuOpen) {
ctx.filtersMenuOpen.value = false
@@ -88,6 +114,11 @@ function getFilterOpenByDefault(filterId: string): boolean {
<template>
<slot name="prepend" />
<PhotosensitivityWarningModal
ref="photosensitivityWarningModal"
@dismiss="onPhotosensitivityWarningDismiss"
/>
<div v-if="ctx.filtersMenuOpen?.value" class="fixed inset-0 z-40 bg-bg" />
<div
-10
View File
@@ -3836,15 +3836,6 @@
"search.filter_type.advanced": {
"defaultMessage": "Erweitert"
},
"search.filter_type.advanced.exclude_datapack": {
"defaultMessage": "Datenpakete ausschließen"
},
"search.filter_type.advanced.exclude_mod": {
"defaultMessage": "Mods ausschließen"
},
"search.filter_type.advanced.exclude_plugin": {
"defaultMessage": "Plugins ausschließen"
},
"search.filter_type.environment": {
"defaultMessage": "Umgebung"
},
@@ -6240,4 +6231,3 @@
"defaultMessage": "Typ"
}
}
-10
View File
@@ -3836,15 +3836,6 @@
"search.filter_type.advanced": {
"defaultMessage": "Erweitert"
},
"search.filter_type.advanced.exclude_datapack": {
"defaultMessage": "Datenpakete ausschließen"
},
"search.filter_type.advanced.exclude_mod": {
"defaultMessage": "Mods ausschließen"
},
"search.filter_type.advanced.exclude_plugin": {
"defaultMessage": "Plugins ausschließen"
},
"search.filter_type.environment": {
"defaultMessage": "Umgebung"
},
@@ -6240,4 +6231,3 @@
"defaultMessage": "Typ"
}
}
+37 -7
View File
@@ -3876,16 +3876,31 @@
"defaultMessage": "Show more"
},
"search.filter_type.advanced": {
"defaultMessage": "Advanced"
"defaultMessage": "Advanced exclusions"
},
"search.filter_type.advanced.exclude_datapack": {
"defaultMessage": "Exclude data packs"
"search.filter_type.advanced.disclosure.advertisements": {
"defaultMessage": "Advertisements"
},
"search.filter_type.advanced.exclude_mod": {
"defaultMessage": "Exclude mods"
"search.filter_type.advanced.disclosure.ai_content": {
"defaultMessage": "AI-generated content"
},
"search.filter_type.advanced.exclude_plugin": {
"defaultMessage": "Exclude plugins"
"search.filter_type.advanced.disclosure.archived": {
"defaultMessage": "Archived"
},
"search.filter_type.advanced.disclosure.derivative_work": {
"defaultMessage": "Derivative content"
},
"search.filter_type.advanced.disclosure.epilepsy_triggers": {
"defaultMessage": "Photosensitivity triggers"
},
"search.filter_type.advanced.disclosure.paid_features": {
"defaultMessage": "Paid features"
},
"search.filter_type.advanced.disclosure.system_interactions": {
"defaultMessage": "External system interactions"
},
"search.filter_type.advanced.disclosure.telemetry": {
"defaultMessage": "Telemetry"
},
"search.filter_type.environment": {
"defaultMessage": "Environment"
@@ -3938,6 +3953,21 @@
"search.filter_type.shader_loader": {
"defaultMessage": "Loader"
},
"search.photosensitivity-warning-modal.body.1": {
"defaultMessage": "We cannot guarantee that all content on Modrinth that may be dangerous for users with photosensitivity is labeled appropriately with a photosensitivity warning."
},
"search.photosensitivity-warning-modal.body.2": {
"defaultMessage": "The label is self-assigned by the users who uploaded the content, and do not indicate that the content has gone through any kind of safety testing. Using any content on Modrinth is at your own risk. Please stay safe."
},
"search.photosensitivity-warning-modal.dont-show-again": {
"defaultMessage": "Don't show this again"
},
"search.photosensitivity-warning-modal.i-understand": {
"defaultMessage": "I understand"
},
"search.photosensitivity-warning-modal.title": {
"defaultMessage": "Results are not guaranteed to be safe"
},
"search.server_content_type.modpack": {
"defaultMessage": "Modded"
},
-10
View File
@@ -3851,15 +3851,6 @@
"search.filter_type.advanced": {
"defaultMessage": "Avanzado"
},
"search.filter_type.advanced.exclude_datapack": {
"defaultMessage": "Excluir paquetes de datos"
},
"search.filter_type.advanced.exclude_mod": {
"defaultMessage": "Excluir mods"
},
"search.filter_type.advanced.exclude_plugin": {
"defaultMessage": "Excluir complementos"
},
"search.filter_type.environment": {
"defaultMessage": "Entorno"
},
@@ -6255,4 +6246,3 @@
"defaultMessage": "Tipo"
}
}
-9
View File
@@ -3830,15 +3830,6 @@
"search.filter_type.advanced": {
"defaultMessage": "Avancé"
},
"search.filter_type.advanced.exclude_datapack": {
"defaultMessage": "Exclure les data packs"
},
"search.filter_type.advanced.exclude_mod": {
"defaultMessage": "Exclure les mods"
},
"search.filter_type.advanced.exclude_plugin": {
"defaultMessage": "Exclure les plugins"
},
"search.filter_type.environment": {
"defaultMessage": "Environnement"
},
-9
View File
@@ -3809,15 +3809,6 @@
"search.filter_type.advanced": {
"defaultMessage": "Avanzate"
},
"search.filter_type.advanced.exclude_datapack": {
"defaultMessage": "Escludi pacchetti di dati"
},
"search.filter_type.advanced.exclude_mod": {
"defaultMessage": "Escludi mod"
},
"search.filter_type.advanced.exclude_plugin": {
"defaultMessage": "Escludi plugin"
},
"search.filter_type.environment": {
"defaultMessage": "Ambiente"
},
-3
View File
@@ -3572,9 +3572,6 @@
"search.filter_type.advanced": {
"defaultMessage": "もっと見る"
},
"search.filter_type.advanced.exclude_datapack": {
"defaultMessage": "データパックを除く"
},
"search.filter_type.environment": {
"defaultMessage": "環境"
},
-9
View File
@@ -3836,15 +3836,6 @@
"search.filter_type.advanced": {
"defaultMessage": "고급"
},
"search.filter_type.advanced.exclude_datapack": {
"defaultMessage": "데이터 팩 제외"
},
"search.filter_type.advanced.exclude_mod": {
"defaultMessage": "모드 제외"
},
"search.filter_type.advanced.exclude_plugin": {
"defaultMessage": "플러그인 제외"
},
"search.filter_type.environment": {
"defaultMessage": "실행 환경"
},
-9
View File
@@ -3770,15 +3770,6 @@
"search.filter_type.advanced": {
"defaultMessage": "Gevorderd"
},
"search.filter_type.advanced.exclude_datapack": {
"defaultMessage": "Gegevenspakketten uitsluiten"
},
"search.filter_type.advanced.exclude_mod": {
"defaultMessage": "Mods uitsluiten"
},
"search.filter_type.advanced.exclude_plugin": {
"defaultMessage": "Plug-ins uitsluiten"
},
"search.filter_type.environment": {
"defaultMessage": "Omgeving"
},
-9
View File
@@ -3815,15 +3815,6 @@
"search.filter_type.advanced": {
"defaultMessage": "Zaawansowane"
},
"search.filter_type.advanced.exclude_datapack": {
"defaultMessage": "Wyklucz paczki danych"
},
"search.filter_type.advanced.exclude_mod": {
"defaultMessage": "Wyklucz mody"
},
"search.filter_type.advanced.exclude_plugin": {
"defaultMessage": "Wyklucz pluginy"
},
"search.filter_type.environment": {
"defaultMessage": "Środowisko"
},
-10
View File
@@ -3851,15 +3851,6 @@
"search.filter_type.advanced": {
"defaultMessage": "Avançado"
},
"search.filter_type.advanced.exclude_datapack": {
"defaultMessage": "Excluir pacotes de dados"
},
"search.filter_type.advanced.exclude_mod": {
"defaultMessage": "Excluir mods"
},
"search.filter_type.advanced.exclude_plugin": {
"defaultMessage": "Excluir plugins"
},
"search.filter_type.environment": {
"defaultMessage": "Ambiente"
},
@@ -6255,4 +6246,3 @@
"defaultMessage": "Tipo"
}
}
-9
View File
@@ -3764,15 +3764,6 @@
"search.filter_type.advanced": {
"defaultMessage": "Расширенные"
},
"search.filter_type.advanced.exclude_datapack": {
"defaultMessage": "Исключать наборы данных"
},
"search.filter_type.advanced.exclude_mod": {
"defaultMessage": "Исключать моды"
},
"search.filter_type.advanced.exclude_plugin": {
"defaultMessage": "Исключать плагины"
},
"search.filter_type.environment": {
"defaultMessage": "Среда"
},
-10
View File
@@ -3593,15 +3593,6 @@
"search.filter_type.advanced": {
"defaultMessage": "Napredno"
},
"search.filter_type.advanced.exclude_datapack": {
"defaultMessage": "Isključi data pakete"
},
"search.filter_type.advanced.exclude_mod": {
"defaultMessage": "Isključi modove"
},
"search.filter_type.advanced.exclude_plugin": {
"defaultMessage": "Isključi plugine"
},
"search.filter_type.environment": {
"defaultMessage": "Okruže"
},
@@ -5784,4 +5775,3 @@
"defaultMessage": "Tip"
}
}
-7
View File
@@ -3455,12 +3455,6 @@
"search.filter_type.advanced": {
"defaultMessage": "Avancerat"
},
"search.filter_type.advanced.exclude_datapack": {
"defaultMessage": "Exkludera datapaket"
},
"search.filter_type.advanced.exclude_mod": {
"defaultMessage": "Exkludera moddar"
},
"search.filter_type.environment": {
"defaultMessage": "Miljö"
},
@@ -5442,4 +5436,3 @@
"defaultMessage": "Typ"
}
}
-9
View File
@@ -3653,15 +3653,6 @@
"search.filter_type.advanced": {
"defaultMessage": "Gelişmiş"
},
"search.filter_type.advanced.exclude_datapack": {
"defaultMessage": "Veri paketlerini hariç tut"
},
"search.filter_type.advanced.exclude_mod": {
"defaultMessage": "Modları hariç tut"
},
"search.filter_type.advanced.exclude_plugin": {
"defaultMessage": "Eklentileri hariç tut"
},
"search.filter_type.environment": {
"defaultMessage": "Ortam"
},
-9
View File
@@ -3800,15 +3800,6 @@
"search.filter_type.advanced": {
"defaultMessage": "Більше"
},
"search.filter_type.advanced.exclude_datapack": {
"defaultMessage": "Виключити пакети даних"
},
"search.filter_type.advanced.exclude_mod": {
"defaultMessage": "Виключити моди"
},
"search.filter_type.advanced.exclude_plugin": {
"defaultMessage": "Виключити плаґіни"
},
"search.filter_type.environment": {
"defaultMessage": "Середовище"
},
-10
View File
@@ -3851,15 +3851,6 @@
"search.filter_type.advanced": {
"defaultMessage": "高级"
},
"search.filter_type.advanced.exclude_datapack": {
"defaultMessage": "排除数据包"
},
"search.filter_type.advanced.exclude_mod": {
"defaultMessage": "排除模组"
},
"search.filter_type.advanced.exclude_plugin": {
"defaultMessage": "排除插件"
},
"search.filter_type.environment": {
"defaultMessage": "运行环境"
},
@@ -6255,4 +6246,3 @@
"defaultMessage": "类型"
}
}
-10
View File
@@ -3851,15 +3851,6 @@
"search.filter_type.advanced": {
"defaultMessage": "進階篩選"
},
"search.filter_type.advanced.exclude_datapack": {
"defaultMessage": "排除資料包"
},
"search.filter_type.advanced.exclude_mod": {
"defaultMessage": "排除模組"
},
"search.filter_type.advanced.exclude_plugin": {
"defaultMessage": "排除插件"
},
"search.filter_type.environment": {
"defaultMessage": "環境"
},
@@ -6255,4 +6246,3 @@
"defaultMessage": "類型"
}
}
+132 -24
View File
@@ -1,10 +1,25 @@
import type { Labrinth } from '@modrinth/api-client'
import { ClientIcon, getCategoryIcon, getLoaderIcon, ServerIcon } from '@modrinth/assets'
import {
ArchiveIcon,
CircleDollarSignIcon,
CircuitBoardIcon,
ClientIcon,
EyeIcon,
getCategoryIcon,
getLoaderIcon,
GitForkIcon,
MegaphoneIcon,
RadioTowerIcon,
ServerIcon,
SparklesIcon,
} from '@modrinth/assets'
import { sortedCategories } from '@modrinth/utils'
import { type Component, computed, readonly, type Ref, ref } from 'vue'
import { type LocationQueryRaw, type LocationQueryValue, useRoute } from 'vue-router'
import { defineMessage, useVIntl } from '../composables/i18n'
import { getProjectTypeIcon } from './auto-icons'
import { getProjectTypeCategoryMessage } from './common-messages'
import {
DEFAULT_MOD_LOADERS,
DEFAULT_PLUGIN_LOADERS,
@@ -120,6 +135,48 @@ const PROJECT_TYPE_EXCLUSION_FILTERS: Partial<Record<ProjectType, ProjectType[]>
datapack: ['mod', 'plugin'],
}
const DISCLOSURE_TYPE_FILTERS = [
'ai_content',
'advertisements',
'epilepsy_triggers',
'system_interactions',
'telemetry',
'derivative_work',
'paid_features',
'archived',
] as const
type DisclosureTypeFilter = (typeof DISCLOSURE_TYPE_FILTERS)[number]
const DISCLOSURE_TYPE_SUPPORTED_PROJECT_TYPES: Record<
DisclosureTypeFilter,
readonly ProjectType[]
> = {
ai_content: ALL_PROJECT_TYPES,
advertisements: ALL_PROJECT_TYPES,
epilepsy_triggers: ALL_PROJECT_TYPES,
derivative_work: ALL_PROJECT_TYPES,
paid_features: ALL_PROJECT_TYPES,
archived: ALL_PROJECT_TYPES,
telemetry: ['mod', 'plugin', 'modpack', 'server'],
system_interactions: ['mod', 'plugin', 'modpack'],
}
const DISCLOSURE_TYPE_ICONS: Record<DisclosureTypeFilter, Component> = {
ai_content: SparklesIcon,
advertisements: MegaphoneIcon,
epilepsy_triggers: EyeIcon,
system_interactions: CircuitBoardIcon,
telemetry: RadioTowerIcon,
derivative_work: GitForkIcon,
paid_features: CircleDollarSignIcon,
archived: ArchiveIcon,
}
function isProjectTypeExclusionOption(optionId: string): optionId is ProjectType {
return (ALL_PROJECT_TYPES as string[]).includes(optionId)
}
export function useSearch(
projectTypes: Ref<ProjectType[]>,
tags: Ref<Tags>,
@@ -151,31 +208,64 @@ export function useSearch(
return formatCategory(formatMessage, categoryName)
}
const formatExcludeProjectTypeLabel = (projectType: ProjectType): string => {
switch (projectType) {
case 'mod':
const formatDisclosureTypeLabel = (disclosureType: DisclosureTypeFilter): string => {
switch (disclosureType) {
case 'ai_content':
return formatMessage(
defineMessage({
id: 'search.filter_type.advanced.exclude_mod',
defaultMessage: 'Exclude mods',
id: 'search.filter_type.advanced.disclosure.ai_content',
defaultMessage: 'AI-generated content',
}),
)
case 'plugin':
case 'advertisements':
return formatMessage(
defineMessage({
id: 'search.filter_type.advanced.exclude_plugin',
defaultMessage: 'Exclude plugins',
id: 'search.filter_type.advanced.disclosure.advertisements',
defaultMessage: 'Advertisements',
}),
)
case 'datapack':
case 'epilepsy_triggers':
return formatMessage(
defineMessage({
id: 'search.filter_type.advanced.exclude_datapack',
defaultMessage: 'Exclude data packs',
id: 'search.filter_type.advanced.disclosure.epilepsy_triggers',
defaultMessage: 'Photosensitivity triggers',
}),
)
case 'system_interactions':
return formatMessage(
defineMessage({
id: 'search.filter_type.advanced.disclosure.system_interactions',
defaultMessage: 'External system interactions',
}),
)
case 'telemetry':
return formatMessage(
defineMessage({
id: 'search.filter_type.advanced.disclosure.telemetry',
defaultMessage: 'Telemetry',
}),
)
case 'derivative_work':
return formatMessage(
defineMessage({
id: 'search.filter_type.advanced.disclosure.derivative_work',
defaultMessage: 'Derivative content',
}),
)
case 'paid_features':
return formatMessage(
defineMessage({
id: 'search.filter_type.advanced.disclosure.paid_features',
defaultMessage: 'Paid features',
}),
)
case 'archived':
return formatMessage(
defineMessage({
id: 'search.filter_type.advanced.disclosure.archived',
defaultMessage: 'Archived',
}),
)
default:
return projectType
}
}
@@ -479,21 +569,35 @@ export function useSearch(
formatted_name: formatMessage(
defineMessage({
id: 'search.filter_type.advanced',
defaultMessage: 'Advanced',
defaultMessage: 'Advanced exclusions',
}),
),
supported_project_types: ['mod', 'plugin', 'datapack'],
supported_project_types: ALL_PROJECT_TYPES,
display: 'all',
query_param: 'a',
supports: ['exclude'],
searchable: false,
ordering: -1000,
options: excludeableProjectTypes.map((target) => ({
id: target,
formatted_name: formatExcludeProjectTypeLabel(target),
method: 'and',
value: `all_project_types:${mapProjectTypeToSearch(target)}`,
})),
options: [
...DISCLOSURE_TYPE_FILTERS.filter((disclosureType) =>
DISCLOSURE_TYPE_SUPPORTED_PROJECT_TYPES[disclosureType].some((projectType) =>
projectTypes.value.includes(projectType),
),
).map((disclosureType) => ({
id: disclosureType,
formatted_name: formatDisclosureTypeLabel(disclosureType),
icon: DISCLOSURE_TYPE_ICONS[disclosureType],
method: 'or' as const,
value: `disclosure_types:${disclosureType}`,
})),
...excludeableProjectTypes.map((target) => ({
id: target,
formatted_name: formatMessage(getProjectTypeCategoryMessage(target)),
icon: getProjectTypeIcon(target),
method: 'and' as const,
value: `all_project_types:${mapProjectTypeToSearch(target)}`,
})),
],
},
]
@@ -503,6 +607,7 @@ export function useSearch(
projectTypes.value.includes(projectType),
),
)
.filter((filterType) => filterType.id !== 'advanced' || filterType.options.length > 0)
.sort((a, b) => (b.ordering ?? 0) - (a.ordering ?? 0))
})
@@ -526,7 +631,7 @@ export function useSearch(
console.error(`Filter type ${filterValue.type} not found`)
continue
}
if (type.id === 'advanced') {
if (type.id === 'advanced' && isProjectTypeExclusionOption(filterValue.option)) {
continue
}
let option = type?.options.find((option) => option.id === filterValue.option)
@@ -620,7 +725,10 @@ export function useSearch(
}
const excludedProjectTypes = filterValues
.filter((filterValue) => filterValue.type === 'advanced')
.filter(
(filterValue) =>
filterValue.type === 'advanced' && isProjectTypeExclusionOption(filterValue.option),
)
.map((filterValue) =>
formatSearchFilterValue(mapProjectTypeToSearch(filterValue.option as ProjectType)),
)