Skip to content

Commit 496bbae

Browse files
authored
fix: server search (#5591)
* fix: server search regression * fix badly passed props
1 parent 1848ba3 commit 496bbae

1 file changed

Lines changed: 194 additions & 56 deletions

File tree

  • apps/frontend/src/pages/discover/[type]

apps/frontend/src/pages/discover/[type]/index.vue

Lines changed: 194 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ import {
3636
StyledInput,
3737
useDebugLogger,
3838
useSearch,
39+
useServerSearch,
3940
useVIntl,
4041
} from '@modrinth/ui'
4142
import { capitalizeString, cycleValue } from '@modrinth/utils'
@@ -87,17 +88,17 @@ const handleProjectMouseEnter = (result: Labrinth.Search.v2.ResultSearchProject)
8788
prefetchTimeout.start()
8889
}
8990
90-
const _handleServerProjectMouseEnter = (result: Labrinth.Search.v3.ResultSearchProject) => {
91+
const handleServerProjectMouseEnter = (result: Labrinth.Search.v3.ResultSearchProject) => {
9192
const slug = result.slug || result.project_id
9293
9394
prefetchTimeout = useTimeoutFn(
9495
async () => {
95-
queryClient.prefetchQuery(projectQueryOptions.v2(slug, modrinthClient))
96-
queryClient.prefetchQuery(projectQueryOptions.v3(slug, modrinthClient))
96+
queryClient.prefetchQuery(projectQueryOptions.v2(slug, client))
97+
queryClient.prefetchQuery(projectQueryOptions.v3(slug, client))
9798
9899
const content = result.minecraft_java_server?.content
99100
if (content?.kind === 'modpack' && content.version_id) {
100-
queryClient.prefetchQuery(versionQueryOptions.v3(content.version_id, modrinthClient))
101+
queryClient.prefetchQuery(versionQueryOptions.v3(content.version_id, client))
101102
}
102103
},
103104
HOVER_DURATION_TO_PREFETCH_MS,
@@ -114,6 +115,8 @@ const currentType = computed(() =>
114115
queryAsStringOrEmpty(route.params.type).replaceAll(/^\/|s\/?$/g, ''),
115116
)
116117
118+
const isServerType = computed(() => currentType.value === 'server')
119+
117120
const projectType = computed(() => tags.value.projectTypes.find((x) => x.id === currentType.value))
118121
const projectTypes = computed(() => (projectType.value ? [projectType.value.id] : []))
119122
@@ -330,6 +333,36 @@ const {
330333
} = useSearch(projectTypes, tags, serverFilters)
331334
debug('useSearch initialized, requestParams:', requestParams.value)
332335
336+
const {
337+
serverCurrentSortType,
338+
serverCurrentFilters,
339+
serverToggledGroups,
340+
serverSortTypes,
341+
serverFilterTypes,
342+
serverRequestParams,
343+
createServerPageParams,
344+
} = useServerSearch({ tags, query, maxResults, currentPage })
345+
346+
const effectiveRequestParams = computed(() =>
347+
isServerType.value ? serverRequestParams.value : requestParams.value,
348+
)
349+
const effectiveSortTypes = computed(() =>
350+
isServerType.value ? (serverSortTypes as readonly SortType[]) : sortTypes,
351+
)
352+
const effectiveCurrentSortType = computed({
353+
get: () => (isServerType.value ? serverCurrentSortType.value : currentSortType.value),
354+
set: (v: SortType) => {
355+
if (isServerType.value) serverCurrentSortType.value = v
356+
else currentSortType.value = v
357+
},
358+
})
359+
const effectiveCurrentFilters = computed({
360+
get: () => (isServerType.value ? serverCurrentFilters.value : currentFilters.value),
361+
set: (v) => {
362+
if (isServerType.value) serverCurrentFilters.value = v
363+
else currentFilters.value = v
364+
},
365+
})
333366
const selectedFilterTags = computed(() =>
334367
currentFilters.value
335368
.filter(
@@ -458,6 +491,22 @@ async function serverInstall(project: InstallableSearchResult) {
458491
project.installing = false
459492
}
460493
494+
function getServerModpackContent(project: Labrinth.Search.v3.ResultSearchProject) {
495+
const content = project.minecraft_java_server?.content
496+
if (content?.kind === 'modpack') {
497+
const { project_name, project_icon, project_id } = content
498+
if (!project_name) return undefined
499+
return {
500+
name: project_name,
501+
icon: project_icon,
502+
onclick:
503+
project_id !== project.project_id ? () => navigateTo(`/project/${project_id}`) : undefined,
504+
showCustomModpackTooltip: project_id === project.project_id,
505+
}
506+
}
507+
return undefined
508+
}
509+
461510
const noLoad = ref(false)
462511
const {
463512
data: rawResults,
@@ -466,19 +515,31 @@ const {
466515
} = useLazyFetch(
467516
() => {
468517
const config = useRuntimeConfig()
469-
const base = import.meta.server ? config.apiBaseUrl : config.public.apiBaseUrl
518+
let base = import.meta.server ? config.apiBaseUrl : config.public.apiBaseUrl
519+
520+
if (currentType.value === 'server') {
521+
base = base.replace(/\/v\d\//, '/v3/').replace(/\/v\d$/, '/v3')
522+
}
470523
471-
const url = `${base}search${requestParams.value}`
472-
debug('useLazyFetch URL:', url)
473-
return url
524+
return `${base}search${effectiveRequestParams.value}`
474525
},
475526
{
476527
headers: computed(() => withLabrinthCanaryHeader()),
528+
477529
watch: false,
478-
transform: (hits) => {
479-
debug('useLazyFetch transform, hits:', (hits as any)?.total_hits)
530+
transform: (
531+
hits: Labrinth.Search.v2.SearchResults | Labrinth.Search.v3.SearchResults,
532+
): Labrinth.Search.v2.SearchResults => {
480533
noLoad.value = false
481-
return hits as Labrinth.Search.v2.SearchResults
534+
if ('hits_per_page' in hits) {
535+
return {
536+
hits: hits.hits as unknown as Labrinth.Search.v2.ResultSearchProject[],
537+
total_hits: hits.total_hits,
538+
limit: hits.hits_per_page,
539+
offset: (hits.page - 1) * hits.hits_per_page,
540+
}
541+
}
542+
return hits
482543
},
483544
},
484545
)
@@ -487,9 +548,18 @@ watch(searchLoading, (val) => debug('searchLoading:', val))
487548
watch(rawResults, (val) => debug('rawResults changed, total_hits:', val?.total_hits))
488549
489550
const results = computed(() => rawResults.value)
490-
const pageCount = computed(() =>
491-
results.value ? Math.ceil(results.value.total_hits / results.value.limit) : 1,
551+
const serverResults = computed(() =>
552+
isServerType.value ? (results.value as Labrinth.Search.v3.SearchResults | null) : null,
553+
)
554+
const projectResults = computed(() =>
555+
isServerType.value ? null : (results.value as Labrinth.Search.v2.SearchResults | null),
492556
)
557+
const pageCount = computed(() => {
558+
if (!results.value) return 1
559+
// @ts-expect-error
560+
const perPage = 'limit' in results.value ? results.value.limit : results.value.hits_per_page
561+
return Math.ceil(results.value.total_hits / perPage)
562+
})
493563
494564
function scrollToTop(behavior: ScrollBehavior = 'smooth') {
495565
window.scrollTo({ top: 0, behavior })
@@ -535,14 +605,14 @@ function updateSearchResults(pageNumber: number = 1, resetScroll = true) {
535605
536606
const params = {
537607
...persistentParams,
538-
...createPageParams(),
608+
...(isServerType.value ? createServerPageParams() : createPageParams()),
539609
}
540610
541611
router.replace({ path: route.path, query: params })
542612
}
543613
}
544614
545-
watch([currentFilters], () => {
615+
watch([effectiveCurrentFilters], () => {
546616
updateSearchResults(1, false)
547617
})
548618
@@ -734,41 +804,73 @@ useSeoMeta({
734804
@update:model-value="updateSearchResults()"
735805
/>
736806
</div>
737-
<SearchSidebarFilter
738-
v-for="filter in filters.filter((f) => f.display !== 'none')"
739-
:key="`filter-${filter.id}`"
740-
v-model:selected-filters="currentFilters"
741-
v-model:toggled-groups="toggledGroups"
742-
v-model:overridden-provided-filter-types="overriddenProvidedFilterTypes"
743-
:provided-filters="serverFilters"
744-
:filter-type="filter"
745-
:class="
746-
filtersMenuOpen
747-
? 'border-0 border-b-[1px] border-solid border-divider last:border-b-0'
748-
: 'card-shadow rounded-2xl bg-bg-raised'
749-
"
750-
button-class="button-animation flex flex-col gap-1 px-6 py-4 w-full bg-transparent cursor-pointer border-none"
751-
content-class="mb-4 mx-3"
752-
inner-panel-class="p-1"
753-
:open-by-default="!(currentType === 'shader' && filter.id === 'game_version')"
754-
>
755-
<template #header>
756-
<h3 class="m-0 text-lg">{{ filter.formatted_name }}</h3>
757-
</template>
758-
<template v-if="currentType === 'shader' && filter.id === 'game_version'" #prefix>
759-
<div class="mb-4 grid grid-cols-[auto_1fr] gap-2 px-3 text-sm font-medium text-blue">
760-
<InfoIcon class="mt-1 size-4" />
761-
<span> {{ formatMessage(messages.gameVersionShaderMessage) }}</span>
762-
</div>
763-
</template>
764-
<template #locked-game_version>
765-
{{ formatMessage(messages.gameVersionProvidedByServer) }}
766-
</template>
767-
<template #locked-mod_loader>
768-
{{ formatMessage(messages.modLoaderProvidedByServer) }}
769-
</template>
770-
<template #sync-button> {{ formatMessage(messages.syncFilterButton) }}</template>
771-
</SearchSidebarFilter>
807+
<template v-if="isServerType">
808+
<SearchSidebarFilter
809+
v-for="filterType in serverFilterTypes.filter((f) => f.options.length > 0)"
810+
:key="`server-filter-${filterType.id}`"
811+
v-model:selected-filters="serverCurrentFilters"
812+
v-model:toggled-groups="serverToggledGroups"
813+
:provided-filters="[]"
814+
:filter-type="filterType"
815+
:class="
816+
filtersMenuOpen
817+
? 'border-0 border-b-[1px] border-solid border-divider last:border-b-0'
818+
: 'card-shadow rounded-2xl bg-bg-raised'
819+
"
820+
button-class="button-animation flex flex-col gap-1 px-6 py-4 w-full bg-transparent cursor-pointer border-none"
821+
content-class="mb-4 mx-3"
822+
inner-panel-class="p-1"
823+
:open-by-default="
824+
![
825+
'server_category_minecraft_server_meta',
826+
'server_category_minecraft_server_community',
827+
'server_game_version',
828+
'server_status',
829+
].includes(filterType.id)
830+
"
831+
>
832+
<template #header>
833+
<h3 class="m-0 text-lg">{{ filterType.formatted_name }}</h3>
834+
</template>
835+
</SearchSidebarFilter>
836+
</template>
837+
<template v-else>
838+
<SearchSidebarFilter
839+
v-for="filter in filters.filter((f) => f.display !== 'none')"
840+
:key="`filter-${filter.id}`"
841+
v-model:selected-filters="currentFilters"
842+
v-model:toggled-groups="toggledGroups"
843+
v-model:overridden-provided-filter-types="overriddenProvidedFilterTypes"
844+
:provided-filters="serverFilters"
845+
:filter-type="filter"
846+
:class="
847+
filtersMenuOpen
848+
? 'border-0 border-b-[1px] border-solid border-divider last:border-b-0'
849+
: 'card-shadow rounded-2xl bg-bg-raised'
850+
"
851+
button-class="button-animation flex flex-col gap-1 px-6 py-4 w-full bg-transparent cursor-pointer border-none"
852+
content-class="mb-4 mx-3"
853+
inner-panel-class="p-1"
854+
:open-by-default="!(currentType === 'shader' && filter.id === 'game_version')"
855+
>
856+
<template #header>
857+
<h3 class="m-0 text-lg">{{ filter.formatted_name }}</h3>
858+
</template>
859+
<template v-if="currentType === 'shader' && filter.id === 'game_version'" #prefix>
860+
<div class="mb-4 grid grid-cols-[auto_1fr] gap-2 px-3 text-sm font-medium text-blue">
861+
<InfoIcon class="mt-1 size-4" />
862+
<span> {{ formatMessage(messages.gameVersionShaderMessage) }}</span>
863+
</div>
864+
</template>
865+
<template #locked-game_version>
866+
{{ formatMessage(messages.gameVersionProvidedByServer) }}
867+
</template>
868+
<template #locked-mod_loader>
869+
{{ formatMessage(messages.modLoaderProvidedByServer) }}
870+
</template>
871+
<template #sync-button> {{ formatMessage(messages.syncFilterButton) }}</template>
872+
</SearchSidebarFilter>
873+
</template>
772874
</div>
773875
</aside>
774876
<section class="normal-page__content">
@@ -788,10 +890,10 @@ useSeoMeta({
788890
<div class="flex flex-wrap items-center gap-2">
789891
<DropdownSelect
790892
v-slot="{ selected }"
791-
v-model="currentSortType"
893+
v-model="effectiveCurrentSortType"
792894
class="!w-auto flex-grow md:flex-grow-0"
793895
name="Sort by"
794-
:options="[...sortTypes]"
896+
:options="[...effectiveSortTypes]"
795897
:display-name="(option?: SortType) => option?.display"
796898
@change="updateSearchResults()"
797899
>
@@ -837,6 +939,14 @@ useSeoMeta({
837939
/>
838940
</div>
839941
<SearchFilterControl
942+
v-if="isServerType"
943+
v-model:selected-filters="serverCurrentFilters"
944+
:filters="serverFilterTypes"
945+
:provided-filters="[]"
946+
:overridden-provided-filter-types="[]"
947+
/>
948+
<SearchFilterControl
949+
v-else
840950
v-model:selected-filters="currentFilters"
841951
:filters="filters.filter((f) => f.display !== 'none')"
842952
:provided-filters="serverFilters"
@@ -854,15 +964,43 @@ useSeoMeta({
854964
resultsDisplayMode === 'grid' || resultsDisplayMode === 'gallery' ? 'grid' : 'list'
855965
"
856966
>
857-
<template v-for="result in results?.hits" :key="result.project_id">
967+
<template v-if="isServerType">
968+
<ProjectCard
969+
v-for="result in serverResults?.hits"
970+
:key="`server-${result.project_id}`"
971+
:link="`/server/${result.slug ?? result.project_id}`"
972+
:title="result.name"
973+
:icon-url="result.icon_url || undefined"
974+
:summary="result.summary"
975+
:tags="result.categories"
976+
:server-online-players="result.minecraft_java_server?.ping?.data?.players_online ?? 0"
977+
:server-region="result.minecraft_server?.region"
978+
:server-recent-plays="result.minecraft_java_server?.verified_plays_2w ?? 0"
979+
:server-status-online="!!result.minecraft_java_server?.ping?.data"
980+
:server-modpack-content="getServerModpackContent(result)"
981+
is-server-project
982+
exclude-loaders
983+
:color="result.color ?? undefined"
984+
:banner="result.featured_gallery ?? undefined"
985+
:layout="
986+
resultsDisplayMode === 'grid' || resultsDisplayMode === 'gallery' ? 'grid' : 'list'
987+
"
988+
:max-tags="2"
989+
@mouseenter="handleServerProjectMouseEnter(result)"
990+
@mouseleave="handleProjectHoverEnd"
991+
/>
992+
</template>
993+
<template v-else>
858994
<ProjectCard
995+
v-for="result in projectResults?.hits"
996+
:key="result.project_id"
859997
:link="`/${projectType?.id ?? 'project'}/${result.slug ? result.slug : result.project_id}`"
860998
:title="result.title"
861999
:icon-url="result.icon_url"
8621000
:author="{ name: result.author, link: `/user/${result.author}` }"
8631001
:date-updated="result.date_modified"
8641002
:date-published="result.date_created"
865-
:displayed-date="currentSortType.name === 'newest' ? 'published' : 'updated'"
1003+
:displayed-date="effectiveCurrentSortType.name === 'newest' ? 'published' : 'updated'"
8661004
:downloads="result.downloads"
8671005
:summary="result.description"
8681006
:tags="result.display_categories"
@@ -875,8 +1013,8 @@ useSeoMeta({
8751013
:environment="
8761014
['mod', 'modpack'].includes(currentType)
8771015
? {
878-
clientSide: result.client_side,
879-
serverSide: result.server_side,
1016+
clientSide: result.client_side as Labrinth.Projects.v2.Environment,
1017+
serverSide: result.server_side as Labrinth.Projects.v2.Environment,
8801018
}
8811019
: undefined
8821020
"

0 commit comments

Comments
 (0)