From b9ddd7047c5256f3a927e8356fed8d85e1f946a0 Mon Sep 17 00:00:00 2001 From: Stephen Zhou <38493346+hyoban@users.noreply.github.com> Date: Fri, 13 Feb 2026 11:39:31 +0800 Subject: [PATCH] fix: use query for main app --- .../components/plugins/marketplace/atoms.ts | 59 +++++++++++++++---- .../plugins/marketplace/hydration-client.tsx | 17 ++++++ .../components/plugins/marketplace/index.tsx | 13 ++-- .../plugins/marketplace/search-params.ts | 7 ++- 4 files changed, 80 insertions(+), 16 deletions(-) create mode 100644 web/app/components/plugins/marketplace/hydration-client.tsx diff --git a/web/app/components/plugins/marketplace/atoms.ts b/web/app/components/plugins/marketplace/atoms.ts index 36a608beed..e2fe2604be 100644 --- a/web/app/components/plugins/marketplace/atoms.ts +++ b/web/app/components/plugins/marketplace/atoms.ts @@ -7,6 +7,8 @@ import { useCallback, useMemo } from 'react' import { CATEGORY_ALL, DEFAULT_PLUGIN_SORT, DEFAULT_TEMPLATE_SORT, getValidatedPluginCategory, getValidatedTemplateCategory, PLUGIN_CATEGORY_WITH_COLLECTIONS } from './constants' import { CREATION_TYPE, marketplaceSearchParamsParsers } from './search-params' +export const isMarketplacePlatformAtom = atom(false) + const marketplacePluginSortAtom = atom(DEFAULT_PLUGIN_SORT) export function useMarketplacePluginSort() { return useAtom(marketplacePluginSortAtom) @@ -33,33 +35,59 @@ export function useSearchText() { return useQueryState('q', marketplaceSearchParamsParsers.q) } export function useActivePluginCategory() { + const isAtMarketplace = useAtomValue(isMarketplacePlatformAtom) + + const [category, setCategory] = useQueryState('category', marketplaceSearchParamsParsers.category) + const router = useRouter() const pathname = usePathname() const segments = pathname.split('/').filter(Boolean) const categoryFromPath = segments[1] || CATEGORY_ALL const validatedCategory = getValidatedPluginCategory(categoryFromPath) - const handleChange = (newCategory: string) => { - router.push(`/plugins/${newCategory}`) + const handleChange = useCallback( + (newCategory: string) => { + router.push(`/plugins/${newCategory}`) + }, + [router], + ) + + if (isAtMarketplace) { + return [validatedCategory, handleChange] as const } - return [validatedCategory, handleChange] as const + return [getValidatedPluginCategory(category), setCategory] as const } export function useActiveTemplateCategory() { + const isAtMarketplace = useAtomValue(isMarketplacePlatformAtom) + + const [category, setCategory] = useQueryState('category', marketplaceSearchParamsParsers.category) + const router = useRouter() const pathname = usePathname() const segments = pathname.split('/').filter(Boolean) const categoryFromPath = segments[1] || CATEGORY_ALL const validatedCategory = getValidatedTemplateCategory(categoryFromPath) - const handleChange = (newCategory: string) => { - router.push(`/${CREATION_TYPE.templates}/${newCategory}`) + const handleChange = useCallback( + (newCategory: string) => { + router.push(`/${CREATION_TYPE.templates}/${newCategory}`) + }, + [router], + ) + + if (isAtMarketplace) { + return [validatedCategory, handleChange] as const } - return [validatedCategory, handleChange] as const + return [getValidatedTemplateCategory(category), setCategory] as const } export function useFilterPluginTags() { return useQueryState('tags', marketplaceSearchParamsParsers.tags) } export function useSearchTab() { + const isAtMarketplace = useAtomValue(isMarketplacePlatformAtom) + + const state = useQueryState('searchTab', marketplaceSearchParamsParsers.searchTab) + const router = useRouter() // /search/[searchTab] const { searchTab } = useParams() @@ -71,16 +99,27 @@ export function useSearchTab() { }, [router], ) - return [searchTab, handleChange] as const + + if (isAtMarketplace) { + return [searchTab, handleChange] as const + } + return state } export function useCreationType() { + const isAtMarketplace = useAtomValue(isMarketplacePlatformAtom) + + const [creationType] = useQueryState('creationType', marketplaceSearchParamsParsers.creationType) + const pathname = usePathname() const segments = pathname.split('/').filter(Boolean) - if (segments[0] === CREATION_TYPE.templates || segments[0] === 'template') - return CREATION_TYPE.templates - return CREATION_TYPE.plugins + if (isAtMarketplace) { + if (segments[0] === CREATION_TYPE.templates || segments[0] === 'template') + return CREATION_TYPE.templates + return CREATION_TYPE.plugins + } + return creationType } // Search-page-specific filter hooks (separate from list-page category/tags) diff --git a/web/app/components/plugins/marketplace/hydration-client.tsx b/web/app/components/plugins/marketplace/hydration-client.tsx new file mode 100644 index 0000000000..8a2ab6f691 --- /dev/null +++ b/web/app/components/plugins/marketplace/hydration-client.tsx @@ -0,0 +1,17 @@ +'use client' + +import { useHydrateAtoms } from 'jotai/utils' +import { isMarketplacePlatformAtom } from './atoms' + +export function HydrateClient({ + isMarketplacePlatform = false, + children, +}: { + isMarketplacePlatform?: boolean + children?: React.ReactNode +}) { + useHydrateAtoms([ + [isMarketplacePlatformAtom, isMarketplacePlatform], + ]) + return <>{children} +} diff --git a/web/app/components/plugins/marketplace/index.tsx b/web/app/components/plugins/marketplace/index.tsx index 4ddb6fcf3d..28c0734df7 100644 --- a/web/app/components/plugins/marketplace/index.tsx +++ b/web/app/components/plugins/marketplace/index.tsx @@ -2,6 +2,7 @@ import type { SearchParams } from 'nuqs' import type { Awaitable } from './hydration-server' import { TanstackQueryInitializer } from '@/context/query-client' import { cn } from '@/utils/classnames' +import { HydrateClient } from './hydration-client' import { HydrateQueryClient } from './hydration-server' import MarketplaceContent from './marketplace-content' import MarketplaceHeader from './marketplace-header' @@ -20,7 +21,7 @@ type MarketplaceProps = { marketplaceNav?: React.ReactNode } -const Marketplace = async ({ +const Marketplace = ({ showInstallButton = true, params, searchParams, @@ -30,10 +31,12 @@ const Marketplace = async ({ return ( - - + + + + ) diff --git a/web/app/components/plugins/marketplace/search-params.ts b/web/app/components/plugins/marketplace/search-params.ts index 11de007ad6..df19224725 100644 --- a/web/app/components/plugins/marketplace/search-params.ts +++ b/web/app/components/plugins/marketplace/search-params.ts @@ -1,4 +1,4 @@ -import { parseAsArrayOf, parseAsString } from 'nuqs/server' +import { parseAsArrayOf, parseAsString, parseAsStringEnum } from 'nuqs/server' export const CREATION_TYPE = { plugins: 'plugins', @@ -17,4 +17,9 @@ export const marketplaceSearchParamsParsers = { searchLanguages: parseAsArrayOf(parseAsString).withDefault([]).withOptions({ history: 'replace' }), searchType: parseAsString.withDefault('all').withOptions({ history: 'replace' }), searchTags: parseAsArrayOf(parseAsString).withDefault([]).withOptions({ history: 'replace' }), + + // In marketplace, we use path instead of query + category: parseAsString.withDefault('all').withOptions({ history: 'replace', clearOnDefault: false }), + creationType: parseAsStringEnum([CREATION_TYPE.plugins, CREATION_TYPE.templates]).withDefault(CREATION_TYPE.plugins).withOptions({ history: 'replace' }), + searchTab: parseAsStringEnum(['all', 'plugins', 'templates', 'creators']).withDefault('').withOptions({ history: 'replace' }), }