fix: server search (#5591)
* fix: server search regression * fix badly passed props
This commit is contained in:
@@ -36,6 +36,7 @@ import {
|
|||||||
StyledInput,
|
StyledInput,
|
||||||
useDebugLogger,
|
useDebugLogger,
|
||||||
useSearch,
|
useSearch,
|
||||||
|
useServerSearch,
|
||||||
useVIntl,
|
useVIntl,
|
||||||
} from '@modrinth/ui'
|
} from '@modrinth/ui'
|
||||||
import { capitalizeString, cycleValue } from '@modrinth/utils'
|
import { capitalizeString, cycleValue } from '@modrinth/utils'
|
||||||
@@ -87,17 +88,17 @@ const handleProjectMouseEnter = (result: Labrinth.Search.v2.ResultSearchProject)
|
|||||||
prefetchTimeout.start()
|
prefetchTimeout.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
const _handleServerProjectMouseEnter = (result: Labrinth.Search.v3.ResultSearchProject) => {
|
const handleServerProjectMouseEnter = (result: Labrinth.Search.v3.ResultSearchProject) => {
|
||||||
const slug = result.slug || result.project_id
|
const slug = result.slug || result.project_id
|
||||||
|
|
||||||
prefetchTimeout = useTimeoutFn(
|
prefetchTimeout = useTimeoutFn(
|
||||||
async () => {
|
async () => {
|
||||||
queryClient.prefetchQuery(projectQueryOptions.v2(slug, modrinthClient))
|
queryClient.prefetchQuery(projectQueryOptions.v2(slug, client))
|
||||||
queryClient.prefetchQuery(projectQueryOptions.v3(slug, modrinthClient))
|
queryClient.prefetchQuery(projectQueryOptions.v3(slug, client))
|
||||||
|
|
||||||
const content = result.minecraft_java_server?.content
|
const content = result.minecraft_java_server?.content
|
||||||
if (content?.kind === 'modpack' && content.version_id) {
|
if (content?.kind === 'modpack' && content.version_id) {
|
||||||
queryClient.prefetchQuery(versionQueryOptions.v3(content.version_id, modrinthClient))
|
queryClient.prefetchQuery(versionQueryOptions.v3(content.version_id, client))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
HOVER_DURATION_TO_PREFETCH_MS,
|
HOVER_DURATION_TO_PREFETCH_MS,
|
||||||
@@ -114,6 +115,8 @@ const currentType = computed(() =>
|
|||||||
queryAsStringOrEmpty(route.params.type).replaceAll(/^\/|s\/?$/g, ''),
|
queryAsStringOrEmpty(route.params.type).replaceAll(/^\/|s\/?$/g, ''),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const isServerType = computed(() => currentType.value === 'server')
|
||||||
|
|
||||||
const projectType = computed(() => tags.value.projectTypes.find((x) => x.id === currentType.value))
|
const projectType = computed(() => tags.value.projectTypes.find((x) => x.id === currentType.value))
|
||||||
const projectTypes = computed(() => (projectType.value ? [projectType.value.id] : []))
|
const projectTypes = computed(() => (projectType.value ? [projectType.value.id] : []))
|
||||||
|
|
||||||
@@ -330,6 +333,36 @@ const {
|
|||||||
} = useSearch(projectTypes, tags, serverFilters)
|
} = useSearch(projectTypes, tags, serverFilters)
|
||||||
debug('useSearch initialized, requestParams:', requestParams.value)
|
debug('useSearch initialized, requestParams:', requestParams.value)
|
||||||
|
|
||||||
|
const {
|
||||||
|
serverCurrentSortType,
|
||||||
|
serverCurrentFilters,
|
||||||
|
serverToggledGroups,
|
||||||
|
serverSortTypes,
|
||||||
|
serverFilterTypes,
|
||||||
|
serverRequestParams,
|
||||||
|
createServerPageParams,
|
||||||
|
} = useServerSearch({ tags, query, maxResults, currentPage })
|
||||||
|
|
||||||
|
const effectiveRequestParams = computed(() =>
|
||||||
|
isServerType.value ? serverRequestParams.value : requestParams.value,
|
||||||
|
)
|
||||||
|
const effectiveSortTypes = computed(() =>
|
||||||
|
isServerType.value ? (serverSortTypes as readonly SortType[]) : sortTypes,
|
||||||
|
)
|
||||||
|
const effectiveCurrentSortType = computed({
|
||||||
|
get: () => (isServerType.value ? serverCurrentSortType.value : currentSortType.value),
|
||||||
|
set: (v: SortType) => {
|
||||||
|
if (isServerType.value) serverCurrentSortType.value = v
|
||||||
|
else currentSortType.value = v
|
||||||
|
},
|
||||||
|
})
|
||||||
|
const effectiveCurrentFilters = computed({
|
||||||
|
get: () => (isServerType.value ? serverCurrentFilters.value : currentFilters.value),
|
||||||
|
set: (v) => {
|
||||||
|
if (isServerType.value) serverCurrentFilters.value = v
|
||||||
|
else currentFilters.value = v
|
||||||
|
},
|
||||||
|
})
|
||||||
const selectedFilterTags = computed(() =>
|
const selectedFilterTags = computed(() =>
|
||||||
currentFilters.value
|
currentFilters.value
|
||||||
.filter(
|
.filter(
|
||||||
@@ -458,6 +491,22 @@ async function serverInstall(project: InstallableSearchResult) {
|
|||||||
project.installing = false
|
project.installing = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getServerModpackContent(project: Labrinth.Search.v3.ResultSearchProject) {
|
||||||
|
const content = project.minecraft_java_server?.content
|
||||||
|
if (content?.kind === 'modpack') {
|
||||||
|
const { project_name, project_icon, project_id } = content
|
||||||
|
if (!project_name) return undefined
|
||||||
|
return {
|
||||||
|
name: project_name,
|
||||||
|
icon: project_icon,
|
||||||
|
onclick:
|
||||||
|
project_id !== project.project_id ? () => navigateTo(`/project/${project_id}`) : undefined,
|
||||||
|
showCustomModpackTooltip: project_id === project.project_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
const noLoad = ref(false)
|
const noLoad = ref(false)
|
||||||
const {
|
const {
|
||||||
data: rawResults,
|
data: rawResults,
|
||||||
@@ -466,19 +515,31 @@ const {
|
|||||||
} = useLazyFetch(
|
} = useLazyFetch(
|
||||||
() => {
|
() => {
|
||||||
const config = useRuntimeConfig()
|
const config = useRuntimeConfig()
|
||||||
const base = import.meta.server ? config.apiBaseUrl : config.public.apiBaseUrl
|
let base = import.meta.server ? config.apiBaseUrl : config.public.apiBaseUrl
|
||||||
|
|
||||||
const url = `${base}search${requestParams.value}`
|
if (currentType.value === 'server') {
|
||||||
debug('useLazyFetch URL:', url)
|
base = base.replace(/\/v\d\//, '/v3/').replace(/\/v\d$/, '/v3')
|
||||||
return url
|
}
|
||||||
|
|
||||||
|
return `${base}search${effectiveRequestParams.value}`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
headers: computed(() => withLabrinthCanaryHeader()),
|
headers: computed(() => withLabrinthCanaryHeader()),
|
||||||
|
|
||||||
watch: false,
|
watch: false,
|
||||||
transform: (hits) => {
|
transform: (
|
||||||
debug('useLazyFetch transform, hits:', (hits as any)?.total_hits)
|
hits: Labrinth.Search.v2.SearchResults | Labrinth.Search.v3.SearchResults,
|
||||||
|
): Labrinth.Search.v2.SearchResults => {
|
||||||
noLoad.value = false
|
noLoad.value = false
|
||||||
return hits as Labrinth.Search.v2.SearchResults
|
if ('hits_per_page' in hits) {
|
||||||
|
return {
|
||||||
|
hits: hits.hits as unknown as Labrinth.Search.v2.ResultSearchProject[],
|
||||||
|
total_hits: hits.total_hits,
|
||||||
|
limit: hits.hits_per_page,
|
||||||
|
offset: (hits.page - 1) * hits.hits_per_page,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hits
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
@@ -487,9 +548,18 @@ watch(searchLoading, (val) => debug('searchLoading:', val))
|
|||||||
watch(rawResults, (val) => debug('rawResults changed, total_hits:', val?.total_hits))
|
watch(rawResults, (val) => debug('rawResults changed, total_hits:', val?.total_hits))
|
||||||
|
|
||||||
const results = computed(() => rawResults.value)
|
const results = computed(() => rawResults.value)
|
||||||
const pageCount = computed(() =>
|
const serverResults = computed(() =>
|
||||||
results.value ? Math.ceil(results.value.total_hits / results.value.limit) : 1,
|
isServerType.value ? (results.value as Labrinth.Search.v3.SearchResults | null) : null,
|
||||||
)
|
)
|
||||||
|
const projectResults = computed(() =>
|
||||||
|
isServerType.value ? null : (results.value as Labrinth.Search.v2.SearchResults | null),
|
||||||
|
)
|
||||||
|
const pageCount = computed(() => {
|
||||||
|
if (!results.value) return 1
|
||||||
|
// @ts-expect-error
|
||||||
|
const perPage = 'limit' in results.value ? results.value.limit : results.value.hits_per_page
|
||||||
|
return Math.ceil(results.value.total_hits / perPage)
|
||||||
|
})
|
||||||
|
|
||||||
function scrollToTop(behavior: ScrollBehavior = 'smooth') {
|
function scrollToTop(behavior: ScrollBehavior = 'smooth') {
|
||||||
window.scrollTo({ top: 0, behavior })
|
window.scrollTo({ top: 0, behavior })
|
||||||
@@ -535,14 +605,14 @@ function updateSearchResults(pageNumber: number = 1, resetScroll = true) {
|
|||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
...persistentParams,
|
...persistentParams,
|
||||||
...createPageParams(),
|
...(isServerType.value ? createServerPageParams() : createPageParams()),
|
||||||
}
|
}
|
||||||
|
|
||||||
router.replace({ path: route.path, query: params })
|
router.replace({ path: route.path, query: params })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watch([currentFilters], () => {
|
watch([effectiveCurrentFilters], () => {
|
||||||
updateSearchResults(1, false)
|
updateSearchResults(1, false)
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -734,41 +804,73 @@ useSeoMeta({
|
|||||||
@update:model-value="updateSearchResults()"
|
@update:model-value="updateSearchResults()"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<SearchSidebarFilter
|
<template v-if="isServerType">
|
||||||
v-for="filter in filters.filter((f) => f.display !== 'none')"
|
<SearchSidebarFilter
|
||||||
:key="`filter-${filter.id}`"
|
v-for="filterType in serverFilterTypes.filter((f) => f.options.length > 0)"
|
||||||
v-model:selected-filters="currentFilters"
|
:key="`server-filter-${filterType.id}`"
|
||||||
v-model:toggled-groups="toggledGroups"
|
v-model:selected-filters="serverCurrentFilters"
|
||||||
v-model:overridden-provided-filter-types="overriddenProvidedFilterTypes"
|
v-model:toggled-groups="serverToggledGroups"
|
||||||
:provided-filters="serverFilters"
|
:provided-filters="[]"
|
||||||
:filter-type="filter"
|
:filter-type="filterType"
|
||||||
:class="
|
:class="
|
||||||
filtersMenuOpen
|
filtersMenuOpen
|
||||||
? 'border-0 border-b-[1px] border-solid border-divider last:border-b-0'
|
? 'border-0 border-b-[1px] border-solid border-divider last:border-b-0'
|
||||||
: 'card-shadow rounded-2xl bg-bg-raised'
|
: 'card-shadow rounded-2xl bg-bg-raised'
|
||||||
"
|
"
|
||||||
button-class="button-animation flex flex-col gap-1 px-6 py-4 w-full bg-transparent cursor-pointer border-none"
|
button-class="button-animation flex flex-col gap-1 px-6 py-4 w-full bg-transparent cursor-pointer border-none"
|
||||||
content-class="mb-4 mx-3"
|
content-class="mb-4 mx-3"
|
||||||
inner-panel-class="p-1"
|
inner-panel-class="p-1"
|
||||||
:open-by-default="!(currentType === 'shader' && filter.id === 'game_version')"
|
:open-by-default="
|
||||||
>
|
![
|
||||||
<template #header>
|
'server_category_minecraft_server_meta',
|
||||||
<h3 class="m-0 text-lg">{{ filter.formatted_name }}</h3>
|
'server_category_minecraft_server_community',
|
||||||
</template>
|
'server_game_version',
|
||||||
<template v-if="currentType === 'shader' && filter.id === 'game_version'" #prefix>
|
'server_status',
|
||||||
<div class="mb-4 grid grid-cols-[auto_1fr] gap-2 px-3 text-sm font-medium text-blue">
|
].includes(filterType.id)
|
||||||
<InfoIcon class="mt-1 size-4" />
|
"
|
||||||
<span> {{ formatMessage(messages.gameVersionShaderMessage) }}</span>
|
>
|
||||||
</div>
|
<template #header>
|
||||||
</template>
|
<h3 class="m-0 text-lg">{{ filterType.formatted_name }}</h3>
|
||||||
<template #locked-game_version>
|
</template>
|
||||||
{{ formatMessage(messages.gameVersionProvidedByServer) }}
|
</SearchSidebarFilter>
|
||||||
</template>
|
</template>
|
||||||
<template #locked-mod_loader>
|
<template v-else>
|
||||||
{{ formatMessage(messages.modLoaderProvidedByServer) }}
|
<SearchSidebarFilter
|
||||||
</template>
|
v-for="filter in filters.filter((f) => f.display !== 'none')"
|
||||||
<template #sync-button> {{ formatMessage(messages.syncFilterButton) }}</template>
|
:key="`filter-${filter.id}`"
|
||||||
</SearchSidebarFilter>
|
v-model:selected-filters="currentFilters"
|
||||||
|
v-model:toggled-groups="toggledGroups"
|
||||||
|
v-model:overridden-provided-filter-types="overriddenProvidedFilterTypes"
|
||||||
|
:provided-filters="serverFilters"
|
||||||
|
:filter-type="filter"
|
||||||
|
:class="
|
||||||
|
filtersMenuOpen
|
||||||
|
? 'border-0 border-b-[1px] border-solid border-divider last:border-b-0'
|
||||||
|
: 'card-shadow rounded-2xl bg-bg-raised'
|
||||||
|
"
|
||||||
|
button-class="button-animation flex flex-col gap-1 px-6 py-4 w-full bg-transparent cursor-pointer border-none"
|
||||||
|
content-class="mb-4 mx-3"
|
||||||
|
inner-panel-class="p-1"
|
||||||
|
:open-by-default="!(currentType === 'shader' && filter.id === 'game_version')"
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<h3 class="m-0 text-lg">{{ filter.formatted_name }}</h3>
|
||||||
|
</template>
|
||||||
|
<template v-if="currentType === 'shader' && filter.id === 'game_version'" #prefix>
|
||||||
|
<div class="mb-4 grid grid-cols-[auto_1fr] gap-2 px-3 text-sm font-medium text-blue">
|
||||||
|
<InfoIcon class="mt-1 size-4" />
|
||||||
|
<span> {{ formatMessage(messages.gameVersionShaderMessage) }}</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template #locked-game_version>
|
||||||
|
{{ formatMessage(messages.gameVersionProvidedByServer) }}
|
||||||
|
</template>
|
||||||
|
<template #locked-mod_loader>
|
||||||
|
{{ formatMessage(messages.modLoaderProvidedByServer) }}
|
||||||
|
</template>
|
||||||
|
<template #sync-button> {{ formatMessage(messages.syncFilterButton) }}</template>
|
||||||
|
</SearchSidebarFilter>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
<section class="normal-page__content">
|
<section class="normal-page__content">
|
||||||
@@ -788,10 +890,10 @@ useSeoMeta({
|
|||||||
<div class="flex flex-wrap items-center gap-2">
|
<div class="flex flex-wrap items-center gap-2">
|
||||||
<DropdownSelect
|
<DropdownSelect
|
||||||
v-slot="{ selected }"
|
v-slot="{ selected }"
|
||||||
v-model="currentSortType"
|
v-model="effectiveCurrentSortType"
|
||||||
class="!w-auto flex-grow md:flex-grow-0"
|
class="!w-auto flex-grow md:flex-grow-0"
|
||||||
name="Sort by"
|
name="Sort by"
|
||||||
:options="[...sortTypes]"
|
:options="[...effectiveSortTypes]"
|
||||||
:display-name="(option?: SortType) => option?.display"
|
:display-name="(option?: SortType) => option?.display"
|
||||||
@change="updateSearchResults()"
|
@change="updateSearchResults()"
|
||||||
>
|
>
|
||||||
@@ -837,6 +939,14 @@ useSeoMeta({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<SearchFilterControl
|
<SearchFilterControl
|
||||||
|
v-if="isServerType"
|
||||||
|
v-model:selected-filters="serverCurrentFilters"
|
||||||
|
:filters="serverFilterTypes"
|
||||||
|
:provided-filters="[]"
|
||||||
|
:overridden-provided-filter-types="[]"
|
||||||
|
/>
|
||||||
|
<SearchFilterControl
|
||||||
|
v-else
|
||||||
v-model:selected-filters="currentFilters"
|
v-model:selected-filters="currentFilters"
|
||||||
:filters="filters.filter((f) => f.display !== 'none')"
|
:filters="filters.filter((f) => f.display !== 'none')"
|
||||||
:provided-filters="serverFilters"
|
:provided-filters="serverFilters"
|
||||||
@@ -854,15 +964,43 @@ useSeoMeta({
|
|||||||
resultsDisplayMode === 'grid' || resultsDisplayMode === 'gallery' ? 'grid' : 'list'
|
resultsDisplayMode === 'grid' || resultsDisplayMode === 'gallery' ? 'grid' : 'list'
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<template v-for="result in results?.hits" :key="result.project_id">
|
<template v-if="isServerType">
|
||||||
<ProjectCard
|
<ProjectCard
|
||||||
|
v-for="result in serverResults?.hits"
|
||||||
|
:key="`server-${result.project_id}`"
|
||||||
|
:link="`/server/${result.slug ?? result.project_id}`"
|
||||||
|
:title="result.name"
|
||||||
|
:icon-url="result.icon_url || undefined"
|
||||||
|
:summary="result.summary"
|
||||||
|
:tags="result.categories"
|
||||||
|
:server-online-players="result.minecraft_java_server?.ping?.data?.players_online ?? 0"
|
||||||
|
:server-region="result.minecraft_server?.region"
|
||||||
|
:server-recent-plays="result.minecraft_java_server?.verified_plays_2w ?? 0"
|
||||||
|
:server-status-online="!!result.minecraft_java_server?.ping?.data"
|
||||||
|
:server-modpack-content="getServerModpackContent(result)"
|
||||||
|
is-server-project
|
||||||
|
exclude-loaders
|
||||||
|
:color="result.color ?? undefined"
|
||||||
|
:banner="result.featured_gallery ?? undefined"
|
||||||
|
:layout="
|
||||||
|
resultsDisplayMode === 'grid' || resultsDisplayMode === 'gallery' ? 'grid' : 'list'
|
||||||
|
"
|
||||||
|
:max-tags="2"
|
||||||
|
@mouseenter="handleServerProjectMouseEnter(result)"
|
||||||
|
@mouseleave="handleProjectHoverEnd"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<ProjectCard
|
||||||
|
v-for="result in projectResults?.hits"
|
||||||
|
:key="result.project_id"
|
||||||
:link="`/${projectType?.id ?? 'project'}/${result.slug ? result.slug : result.project_id}`"
|
:link="`/${projectType?.id ?? 'project'}/${result.slug ? result.slug : result.project_id}`"
|
||||||
:title="result.title"
|
:title="result.title"
|
||||||
:icon-url="result.icon_url"
|
:icon-url="result.icon_url"
|
||||||
:author="{ name: result.author, link: `/user/${result.author}` }"
|
:author="{ name: result.author, link: `/user/${result.author}` }"
|
||||||
:date-updated="result.date_modified"
|
:date-updated="result.date_modified"
|
||||||
:date-published="result.date_created"
|
:date-published="result.date_created"
|
||||||
:displayed-date="currentSortType.name === 'newest' ? 'published' : 'updated'"
|
:displayed-date="effectiveCurrentSortType.name === 'newest' ? 'published' : 'updated'"
|
||||||
:downloads="result.downloads"
|
:downloads="result.downloads"
|
||||||
:summary="result.description"
|
:summary="result.description"
|
||||||
:tags="result.display_categories"
|
:tags="result.display_categories"
|
||||||
@@ -875,8 +1013,8 @@ useSeoMeta({
|
|||||||
:environment="
|
:environment="
|
||||||
['mod', 'modpack'].includes(currentType)
|
['mod', 'modpack'].includes(currentType)
|
||||||
? {
|
? {
|
||||||
clientSide: result.client_side,
|
clientSide: result.client_side as Labrinth.Projects.v2.Environment,
|
||||||
serverSide: result.server_side,
|
serverSide: result.server_side as Labrinth.Projects.v2.Environment,
|
||||||
}
|
}
|
||||||
: undefined
|
: undefined
|
||||||
"
|
"
|
||||||
|
|||||||
Reference in New Issue
Block a user