Skip to content

Commit e181dd2

Browse files
committed
fix: use query param for search provider instead of cookie
1 parent d62de7c commit e181dd2

9 files changed

Lines changed: 104 additions & 57 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: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ const emit = defineEmits(['blur', 'focus'])
1515
1616
const router = useRouter()
1717
const route = useRoute()
18-
const { isAlgolia } = useSearchProvider()
18+
// The actual search provider (from URL, used for API calls)
19+
const searchProviderParam = computed(() => {
20+
const p = normalizeSearchParam(route.query.p)
21+
return p === 'npm' ? 'npm' : 'algolia'
22+
})
23+
const { searchProvider } = useSearchProvider()
24+
const searchProviderValue = computed(() => searchProviderParam.value || searchProvider.value)
1925
2026
const isSearchFocused = shallowRef(false)
2127
@@ -35,7 +41,7 @@ function updateUrlQueryImpl(value: string) {
3541
return
3642
}
3743
if (route.name === 'search') {
38-
router.replace({ query: { q: value || undefined } })
44+
router.replace({ query: { q: value || undefined, p: searchProviderValue.value } })
3945
return
4046
}
4147
if (!value) {
@@ -46,6 +52,7 @@ function updateUrlQueryImpl(value: string) {
4652
name: 'search',
4753
query: {
4854
q: value,
55+
p: searchProviderValue.value,
4956
},
5057
})
5158
}
@@ -54,9 +61,11 @@ const updateUrlQueryNpm = debounce(updateUrlQueryImpl, 250)
5461
const updateUrlQueryAlgolia = debounce(updateUrlQueryImpl, 80)
5562
5663
const updateUrlQuery = Object.assign(
57-
(value: string) => (isAlgolia.value ? updateUrlQueryAlgolia : updateUrlQueryNpm)(value),
64+
(value: string) =>
65+
(searchProviderValue.value === 'algolia' ? updateUrlQueryAlgolia : updateUrlQueryNpm)(value),
5866
{
59-
flush: () => (isAlgolia.value ? updateUrlQueryAlgolia : updateUrlQueryNpm).flush(),
67+
flush: () =>
68+
(searchProviderValue.value === 'algolia' ? updateUrlQueryAlgolia : updateUrlQueryNpm).flush(),
6069
},
6170
)
6271
@@ -85,6 +94,7 @@ function handleSubmit() {
8594
name: 'search',
8695
query: {
8796
q: searchQuery.value,
97+
p: searchProviderValue.value,
8898
},
8999
})
90100
} 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 searchProviderParam = computed(() => {
5+
const p = normalizeSearchParam(route.query.p)
6+
return p === 'npm' ? 'npm' : 'algolia'
7+
})
8+
const { searchProvider } = useSearchProvider()
9+
const searchProviderValue = computed(() => searchProviderParam.value || searchProvider.value)
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: 'algolia' } })
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()
14+
const searchProviderParam = computed(() => {
15+
const p = normalizeSearchParam(route.query.p)
16+
return p === 'npm' ? 'npm' : 'algolia'
17+
})
1318
const { searchProvider } = useSearchProvider()
19+
const searchProviderValue = computed(() => searchProviderParam.value || searchProvider.value)
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) {

app/composables/npm/useSearch.ts

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,10 @@ export interface UseSearchConfig {
2727

2828
export function useSearch(
2929
query: MaybeRefOrGetter<string>,
30+
searchProvider: MaybeRefOrGetter<SearchProvider>,
3031
options: MaybeRefOrGetter<SearchOptions> = {},
3132
config: UseSearchConfig = {},
3233
) {
33-
const { searchProvider } = useSearchProvider()
3434
const { search: searchAlgolia, searchWithSuggestions: algoliaMultiSearch } = useAlgoliaSearch()
3535
const {
3636
search: searchNpm,
@@ -143,10 +143,10 @@ export function useSearch(
143143
}
144144

145145
const asyncData = useLazyAsyncData(
146-
() => `search:${searchProvider.value}:${toValue(query)}`,
146+
() => `search:${toValue(searchProvider)}:${toValue(query)}`,
147147
async (_nuxtApp, { signal }) => {
148148
const q = toValue(query)
149-
const provider = searchProvider.value
149+
const provider = toValue(searchProvider)
150150

151151
if (!q.trim()) {
152152
isRateLimited.value = false
@@ -227,7 +227,7 @@ export function useSearch(
227227

228228
async function fetchMore(targetSize: number): Promise<void> {
229229
const q = toValue(query).trim()
230-
const provider = searchProvider.value
230+
const provider = toValue(searchProvider)
231231

232232
if (!q) {
233233
cache.value = null
@@ -307,15 +307,18 @@ export function useSearch(
307307
},
308308
)
309309

310-
watch(searchProvider, async () => {
311-
cache.value = null
312-
existenceCache.value = {}
313-
await asyncData.refresh()
314-
const targetSize = toValue(options).size
315-
if (targetSize) {
316-
await fetchMore(targetSize)
317-
}
318-
})
310+
watch(
311+
() => toValue(searchProvider),
312+
async () => {
313+
cache.value = null
314+
existenceCache.value = {}
315+
await asyncData.refresh()
316+
const targetSize = toValue(options).size
317+
if (targetSize) {
318+
await fetchMore(targetSize)
319+
}
320+
},
321+
)
319322

320323
const data = computed<NpmSearchResponse | null>(() => {
321324
if (cache.value) {
@@ -427,10 +430,10 @@ export function useSearch(
427430
}
428431

429432
const npmSuggestions = useLazyAsyncData(
430-
() => `npm-suggestions:${searchProvider.value}:${toValue(query)}`,
433+
() => `npm-suggestions:${toValue(searchProvider)}:${toValue(query)}`,
431434
async () => {
432435
const q = toValue(query).trim()
433-
if (searchProvider.value === 'algolia' || !q)
436+
if (toValue(searchProvider) === 'algolia' || !q)
434437
return { suggestions: [], packageAvailability: null }
435438
const { intent, name } = parseSuggestionIntent(q)
436439
if (!intent || !name) return { suggestions: [], packageAvailability: null }

app/composables/npm/useUserPackages.ts

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@ const MAX_RESULTS = 250
2222
* ```
2323
*/
2424
export function useUserPackages(username: MaybeRefOrGetter<string>) {
25+
const route = useRoute()
26+
const searchProviderParam = computed(() => {
27+
const p = normalizeSearchParam(route.query.p)
28+
return p === 'npm' ? 'npm' : 'algolia'
29+
})
2530
const { searchProvider } = useSearchProvider()
31+
const searchProviderValue = computed(() => searchProviderParam.value || searchProvider.value)
2632
// this is only used in npm path, but we need to extract it when the composable runs
2733
const { $npmRegistry } = useNuxtApp()
2834
const { searchByOwner } = useAlgoliaSearch()
@@ -32,7 +38,7 @@ export function useUserPackages(username: MaybeRefOrGetter<string>) {
3238

3339
/** Tracks which provider actually served the current data (may differ from
3440
* searchProvider when Algolia returns empty and we fall through to npm) */
35-
const activeProvider = shallowRef<'npm' | 'algolia'>(searchProvider.value)
41+
const activeProvider = shallowRef<'npm' | 'algolia'>(searchProviderValue.value)
3642

3743
const cache = shallowRef<{
3844
username: string
@@ -43,22 +49,22 @@ export function useUserPackages(username: MaybeRefOrGetter<string>) {
4349
const isLoadingMore = shallowRef(false)
4450

4551
const asyncData = useLazyAsyncData(
46-
() => `user-packages:${searchProvider.value}:${toValue(username)}`,
52+
() => `user-packages:${searchProviderValue.value}:${toValue(username)}`,
4753
async ({ $npmRegistry }, { signal }) => {
4854
const user = toValue(username)
4955
if (!user) {
5056
return emptySearchResponse()
5157
}
5258

53-
const provider = searchProvider.value
59+
const provider = searchProviderValue.value
5460

5561
// --- Algolia: fetch all at once ---
5662
if (provider === 'algolia') {
5763
try {
5864
const response = await searchByOwner(user)
5965

6066
// Guard against stale response (user/provider changed during await)
61-
if (user !== toValue(username) || provider !== searchProvider.value) {
67+
if (user !== toValue(username) || provider !== searchProviderValue.value) {
6268
return emptySearchResponse()
6369
}
6470

@@ -95,7 +101,7 @@ export function useUserPackages(username: MaybeRefOrGetter<string>) {
95101
)
96102

97103
// Guard against stale response (user/provider changed during await)
98-
if (user !== toValue(username) || provider !== searchProvider.value) {
104+
if (user !== toValue(username) || provider !== searchProviderValue.value) {
99105
return emptySearchResponse()
100106
}
101107

@@ -193,11 +199,14 @@ export function useUserPackages(username: MaybeRefOrGetter<string>) {
193199

194200
// asyncdata will automatically rerun due to key, but we need to reset cache/page
195201
// when provider changes
196-
watch(searchProvider, newProvider => {
197-
cache.value = null
198-
currentPage.value = 1
199-
activeProvider.value = newProvider
200-
})
202+
watch(
203+
() => searchProviderValue.value,
204+
newProvider => {
205+
cache.value = null
206+
currentPage.value = 1
207+
activeProvider.value = newProvider
208+
},
209+
)
201210

202211
// Computed data that uses cache (only if it belongs to the current username)
203212
const data = computed<NpmSearchResponse | null>(() => {

app/composables/useSettings.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -115,18 +115,11 @@ export function useAccentColor() {
115115
* Composable for managing the search provider setting.
116116
*/
117117
export function useSearchProvider() {
118-
const cookie = useCookie('search-provider', {
119-
secure: true,
120-
sameSite: 'strict',
121-
maxAge: 60 * 60 * 24 * 30,
122-
path: '/',
123-
})
124118
const { settings } = useSettings()
125119

126120
const searchProvider = computed({
127-
get: () => (cookie.value === 'npm' ? 'npm' : settings.value.searchProvider),
121+
get: () => settings.value.searchProvider,
128122
set: (value: SearchProvider) => {
129-
cookie.value = value
130123
settings.value.searchProvider = value
131124
},
132125
})

app/pages/index.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { debounce } from 'perfect-debounce'
33
import { SHOWCASED_FRAMEWORKS } from '~/utils/frameworks'
44
5-
const { isAlgolia } = useSearchProvider()
5+
const { searchProvider } = useSearchProvider()
66
77
const searchQuery = shallowRef('')
88
const isSearchFocused = shallowRef(false)
@@ -12,7 +12,7 @@ async function search() {
1212
if (!query) return
1313
await navigateTo({
1414
path: '/search',
15-
query: query ? { q: query } : undefined,
15+
query: query ? { q: query, p: searchProvider.value } : undefined,
1616
})
1717
const newQuery = searchQuery.value.trim()
1818
if (newQuery !== query) {
@@ -26,7 +26,7 @@ const handleInputAlgolia = debounce(search, 80, { leading: true, trailing: true
2626
function handleInput() {
2727
if (isTouchDevice()) {
2828
search()
29-
} else if (isAlgolia.value) {
29+
} else if (searchProvider.value === 'algolia') {
3030
handleInputAlgolia()
3131
} else {
3232
handleInputNpm()

0 commit comments

Comments
 (0)