Skip to content

Commit 7294962

Browse files
authored
fix: search hydration errors (#1358)
1 parent 8722e87 commit 7294962

9 files changed

Lines changed: 250 additions & 150 deletions

File tree

app/components/Compare/PackageSelector.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@ const inputValue = shallowRef('')
1515
const isInputFocused = shallowRef(false)
1616
1717
// Use the shared search composable (supports both npm and Algolia providers)
18-
const { data: searchData, status } = useSearch(inputValue, { size: 15 })
18+
const { searchProvider } = useSearchProvider()
19+
const { data: searchData, status } = useSearch(inputValue, searchProvider, { size: 15 })
1920
2021
const isSearching = computed(() => status.value === 'pending')
2122

app/components/Header/SearchBox.vue

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@ const emit = defineEmits(['blur', 'focus'])
1515
1616
const router = useRouter()
1717
const route = useRoute()
18-
const { isAlgolia } = useSearchProvider()
18+
const { searchProvider } = useSearchProvider()
19+
const searchProviderValue = computed(() => {
20+
const p = normalizeSearchParam(route.query.p)
21+
if (p === 'npm' || searchProvider.value === 'npm') return 'npm'
22+
return 'algolia'
23+
})
1924
2025
const isSearchFocused = shallowRef(false)
2126
@@ -29,13 +34,13 @@ const searchQuery = shallowRef(normalizeSearchParam(route.query.q))
2934
// Pages that have their own local filter using ?q
3035
const pagesWithLocalFilter = new Set(['~username', 'org'])
3136
32-
function updateUrlQueryImpl(value: string) {
37+
function updateUrlQueryImpl(value: string, provider: 'npm' | 'algolia') {
3338
// Don't navigate away from pages that use ?q for local filtering
3439
if (pagesWithLocalFilter.has(route.name as string)) {
3540
return
3641
}
3742
if (route.name === 'search') {
38-
router.replace({ query: { q: value || undefined } })
43+
router.replace({ query: { q: value || undefined, p: provider === 'npm' ? 'npm' : undefined } })
3944
return
4045
}
4146
if (!value) {
@@ -46,6 +51,7 @@ function updateUrlQueryImpl(value: string) {
4651
name: 'search',
4752
query: {
4853
q: value,
54+
p: provider === 'npm' ? 'npm' : undefined,
4955
},
5056
})
5157
}
@@ -54,9 +60,14 @@ const updateUrlQueryNpm = debounce(updateUrlQueryImpl, 250)
5460
const updateUrlQueryAlgolia = debounce(updateUrlQueryImpl, 80)
5561
5662
const updateUrlQuery = Object.assign(
57-
(value: string) => (isAlgolia.value ? updateUrlQueryAlgolia : updateUrlQueryNpm)(value),
63+
(value: string) =>
64+
(searchProviderValue.value === 'algolia' ? updateUrlQueryAlgolia : updateUrlQueryNpm)(
65+
value,
66+
searchProviderValue.value,
67+
),
5868
{
59-
flush: () => (isAlgolia.value ? updateUrlQueryAlgolia : updateUrlQueryNpm).flush(),
69+
flush: () =>
70+
(searchProviderValue.value === 'algolia' ? updateUrlQueryAlgolia : updateUrlQueryNpm).flush(),
6071
},
6172
)
6273
@@ -85,6 +96,7 @@ function handleSubmit() {
8596
name: 'search',
8697
query: {
8798
q: searchQuery.value,
99+
p: searchProviderValue.value === 'npm' ? 'npm' : undefined,
88100
},
89101
})
90102
} else {

app/components/SearchProviderToggle.client.vue

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
<script setup lang="ts">
2-
const { searchProvider, isAlgolia } = useSearchProvider()
2+
const route = useRoute()
3+
const router = useRouter()
4+
const { searchProvider } = useSearchProvider()
5+
const searchProviderValue = computed(() => {
6+
const p = normalizeSearchParam(route.query.p)
7+
if (p === 'npm' || searchProvider.value === 'npm') return 'npm'
8+
return 'algolia'
9+
})
310
411
const isOpen = shallowRef(false)
512
const toggleRef = useTemplateRef('toggleRef')
@@ -47,21 +54,25 @@ useEventListener('keydown', event => {
4754
type="button"
4855
role="menuitem"
4956
class="w-full flex items-start gap-3 px-3 py-2.5 rounded-md text-start transition-colors hover:bg-bg-muted"
50-
:class="[!isAlgolia ? 'bg-bg-muted' : '']"
57+
:class="[searchProviderValue !== 'algolia' ? 'bg-bg-muted' : '']"
5158
@click="
5259
() => {
5360
searchProvider = 'npm'
61+
router.push({ query: { ...route.query, p: 'npm' } })
5462
isOpen = false
5563
}
5664
"
5765
>
5866
<span
5967
class="i-carbon:catalog w-4 h-4 mt-0.5 shrink-0"
60-
:class="!isAlgolia ? 'text-accent' : 'text-fg-muted'"
68+
:class="searchProviderValue !== 'algolia' ? 'text-accent' : 'text-fg-muted'"
6169
aria-hidden="true"
6270
/>
6371
<div class="min-w-0 flex-1">
64-
<div class="text-sm font-medium" :class="!isAlgolia ? 'text-fg' : 'text-fg-muted'">
72+
<div
73+
class="text-sm font-medium"
74+
:class="searchProviderValue !== 'algolia' ? 'text-fg' : 'text-fg-muted'"
75+
>
6576
{{ $t('settings.data_source.npm') }}
6677
</div>
6778
<p class="text-xs text-fg-subtle mt-0.5">
@@ -75,21 +86,25 @@ useEventListener('keydown', event => {
7586
type="button"
7687
role="menuitem"
7788
class="w-full flex items-start gap-3 px-3 py-2.5 rounded-md text-start transition-colors hover:bg-bg-muted mt-1"
78-
:class="[isAlgolia ? 'bg-bg-muted' : '']"
89+
:class="[searchProviderValue === 'algolia' ? 'bg-bg-muted' : '']"
7990
@click="
8091
() => {
8192
searchProvider = 'algolia'
93+
router.push({ query: { ...route.query, p: undefined } })
8294
isOpen = false
8395
}
8496
"
8597
>
8698
<span
8799
class="i-carbon:search w-4 h-4 mt-0.5 shrink-0"
88-
:class="isAlgolia ? 'text-accent' : 'text-fg-muted'"
100+
:class="searchProviderValue === 'algolia' ? 'text-accent' : 'text-fg-muted'"
89101
aria-hidden="true"
90102
/>
91103
<div class="min-w-0 flex-1">
92-
<div class="text-sm font-medium" :class="isAlgolia ? 'text-fg' : 'text-fg-muted'">
104+
<div
105+
class="text-sm font-medium"
106+
:class="searchProviderValue === 'algolia' ? 'text-fg' : 'text-fg-muted'"
107+
>
93108
{{ $t('settings.data_source.algolia') }}
94109
</div>
95110
<p class="text-xs text-fg-subtle mt-0.5">
@@ -99,7 +114,10 @@ useEventListener('keydown', event => {
99114
</button>
100115

101116
<!-- Algolia attribution -->
102-
<div v-if="isAlgolia" class="border-t border-border mx-1 mt-1 pt-2 pb-1">
117+
<div
118+
v-if="searchProviderValue === 'algolia'"
119+
class="border-t border-border mx-1 mt-1 pt-2 pb-1"
120+
>
103121
<a
104122
href="https://www.algolia.com/developers"
105123
target="_blank"

app/composables/npm/useOrgPackages.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,17 @@ import { mapWithConcurrency } from '#shared/utils/async'
1010
* 3. Falls back to lightweight server-side package-meta lookups
1111
*/
1212
export function useOrgPackages(orgName: MaybeRefOrGetter<string>) {
13+
const route = useRoute()
1314
const { searchProvider } = useSearchProvider()
15+
const searchProviderValue = computed(() => {
16+
const p = normalizeSearchParam(route.query.p)
17+
if (p === 'npm' || searchProvider.value === 'npm') return 'npm'
18+
return 'algolia'
19+
})
1420
const { getPackagesByName } = useAlgoliaSearch()
1521

1622
const asyncData = useLazyAsyncData(
17-
() => `org-packages:${searchProvider.value}:${toValue(orgName)}`,
23+
() => `org-packages:${searchProviderValue.value}:${toValue(orgName)}`,
1824
async ({ ssrContext }, { signal }) => {
1925
const org = toValue(orgName)
2026
if (!org) {
@@ -51,7 +57,7 @@ export function useOrgPackages(orgName: MaybeRefOrGetter<string>) {
5157
}
5258

5359
// Fetch metadata + downloads from Algolia (single request via getObjects)
54-
if (searchProvider.value === 'algolia') {
60+
if (searchProviderValue.value === 'algolia') {
5561
try {
5662
const response = await getPackagesByName(packageNames)
5763
if (response.objects.length > 0) {

0 commit comments

Comments
 (0)