Skip to content

Commit 63aab5c

Browse files
WTW0313Copilot
andauthored
feat: add search params to url (#17684)
Co-authored-by: Copilot <[email protected]>
1 parent 0e136b4 commit 63aab5c

File tree

8 files changed

+114
-27
lines changed

8 files changed

+114
-27
lines changed

web/app/(commonLayout)/plugins/page.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ const PluginList = async () => {
88
return (
99
<PluginPage
1010
plugins={<PluginsPanel />}
11-
marketplace={<Marketplace locale={locale} pluginTypeSwitchClassName='top-[60px]' searchBoxAutoAnimate={false} />}
11+
marketplace={<Marketplace locale={locale} pluginTypeSwitchClassName='top-[60px]' searchBoxAutoAnimate={false} showSearchParams={false} />}
1212
/>
1313
)
1414
}

web/app/components/plugins/hooks.ts

+14
Original file line numberDiff line numberDiff line change
@@ -92,3 +92,17 @@ export const useSingleCategories = (translateFromOut?: TFunction) => {
9292
categoriesMap,
9393
}
9494
}
95+
96+
export const PLUGIN_PAGE_TABS_MAP = {
97+
plugins: 'plugins',
98+
marketplace: 'discover',
99+
}
100+
101+
export const usePluginPageTabs = () => {
102+
const { t } = useTranslation()
103+
const tabs = [
104+
{ value: PLUGIN_PAGE_TABS_MAP.plugins, text: t('common.menus.plugins') },
105+
{ value: PLUGIN_PAGE_TABS_MAP.marketplace, text: t('common.menus.exploreMarketplace') },
106+
]
107+
return tabs
108+
}

web/app/components/plugins/marketplace/context.tsx

+36-7
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@ import {
3535
import {
3636
getMarketplaceListCondition,
3737
getMarketplaceListFilterType,
38+
updateSearchParams,
3839
} from './utils'
3940
import { useInstalledPluginList } from '@/service/use-plugins'
40-
import { noop } from 'lodash-es'
41+
import { debounce, noop } from 'lodash-es'
4142

4243
export type MarketplaceContextValue = {
4344
intersected: boolean
@@ -96,6 +97,7 @@ type MarketplaceContextProviderProps = {
9697
searchParams?: SearchParams
9798
shouldExclude?: boolean
9899
scrollContainerId?: string
100+
showSearchParams?: boolean
99101
}
100102

101103
export function useMarketplaceContext(selector: (value: MarketplaceContextValue) => any) {
@@ -107,6 +109,7 @@ export const MarketplaceContextProvider = ({
107109
searchParams,
108110
shouldExclude,
109111
scrollContainerId,
112+
showSearchParams,
110113
}: MarketplaceContextProviderProps) => {
111114
const { data, isSuccess } = useInstalledPluginList(!shouldExclude)
112115
const exclude = useMemo(() => {
@@ -159,7 +162,10 @@ export const MarketplaceContextProvider = ({
159162
type: getMarketplaceListFilterType(activePluginTypeRef.current),
160163
page: pageRef.current,
161164
})
162-
history.pushState({}, '', `/${searchParams?.language ? `?language=${searchParams?.language}` : ''}`)
165+
const url = new URL(window.location.href)
166+
if (searchParams?.language)
167+
url.searchParams.set('language', searchParams?.language)
168+
history.replaceState({}, '', url)
163169
}
164170
else {
165171
if (shouldExclude && isSuccess) {
@@ -182,7 +188,31 @@ export const MarketplaceContextProvider = ({
182188
resetPlugins()
183189
}, [exclude, queryMarketplaceCollectionsAndPlugins, resetPlugins])
184190

191+
const debouncedUpdateSearchParams = useMemo(() => debounce(() => {
192+
updateSearchParams({
193+
query: searchPluginTextRef.current,
194+
category: activePluginTypeRef.current,
195+
tags: filterPluginTagsRef.current,
196+
})
197+
}, 500), [])
198+
199+
const handleUpdateSearchParams = useCallback((debounced?: boolean) => {
200+
if (!showSearchParams)
201+
return
202+
if (debounced) {
203+
debouncedUpdateSearchParams()
204+
}
205+
else {
206+
updateSearchParams({
207+
query: searchPluginTextRef.current,
208+
category: activePluginTypeRef.current,
209+
tags: filterPluginTagsRef.current,
210+
})
211+
}
212+
}, [debouncedUpdateSearchParams, showSearchParams])
213+
185214
const handleQueryPlugins = useCallback((debounced?: boolean) => {
215+
handleUpdateSearchParams(debounced)
186216
if (debounced) {
187217
queryPluginsWithDebounced({
188218
query: searchPluginTextRef.current,
@@ -207,17 +237,18 @@ export const MarketplaceContextProvider = ({
207237
page: pageRef.current,
208238
})
209239
}
210-
}, [exclude, queryPluginsWithDebounced, queryPlugins])
240+
}, [exclude, queryPluginsWithDebounced, queryPlugins, handleUpdateSearchParams])
211241

212242
const handleQuery = useCallback((debounced?: boolean) => {
213243
if (!searchPluginTextRef.current && !filterPluginTagsRef.current.length) {
244+
handleUpdateSearchParams(debounced)
214245
cancelQueryPluginsWithDebounced()
215246
handleQueryMarketplaceCollectionsAndPlugins()
216247
return
217248
}
218249

219250
handleQueryPlugins(debounced)
220-
}, [handleQueryMarketplaceCollectionsAndPlugins, handleQueryPlugins, cancelQueryPluginsWithDebounced])
251+
}, [handleQueryMarketplaceCollectionsAndPlugins, handleQueryPlugins, cancelQueryPluginsWithDebounced, handleUpdateSearchParams])
221252

222253
const handleSearchPluginTextChange = useCallback((text: string) => {
223254
setSearchPluginText(text)
@@ -242,11 +273,9 @@ export const MarketplaceContextProvider = ({
242273
activePluginTypeRef.current = type
243274
setPage(1)
244275
pageRef.current = 1
245-
}, [])
246276

247-
useEffect(() => {
248277
handleQuery()
249-
}, [activePluginType, handleQuery])
278+
}, [handleQuery])
250279

251280
const handleSortChange = useCallback((sort: PluginsSort) => {
252281
setSort(sort)

web/app/components/plugins/marketplace/index.tsx

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ type MarketplaceProps = {
1717
pluginTypeSwitchClassName?: string
1818
intersectionContainerId?: string
1919
scrollContainerId?: string
20+
showSearchParams?: boolean
2021
}
2122
const Marketplace = async ({
2223
locale,
@@ -27,6 +28,7 @@ const Marketplace = async ({
2728
pluginTypeSwitchClassName,
2829
intersectionContainerId,
2930
scrollContainerId,
31+
showSearchParams = true,
3032
}: MarketplaceProps) => {
3133
let marketplaceCollections: any = []
3234
let marketplaceCollectionPluginsMap = {}
@@ -42,6 +44,7 @@ const Marketplace = async ({
4244
searchParams={searchParams}
4345
shouldExclude={shouldExclude}
4446
scrollContainerId={scrollContainerId}
47+
showSearchParams={showSearchParams}
4548
>
4649
<Description locale={locale} />
4750
<IntersectionLine intersectionContainerId={intersectionContainerId} />
@@ -53,6 +56,7 @@ const Marketplace = async ({
5356
locale={locale}
5457
className={pluginTypeSwitchClassName}
5558
searchBoxAutoAnimate={searchBoxAutoAnimate}
59+
showSearchParams={showSearchParams}
5660
/>
5761
<ListWrapper
5862
locale={locale}

web/app/components/plugins/marketplace/plugin-type-switch.tsx

+20
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
useSearchBoxAutoAnimate,
1414
} from './hooks'
1515
import cn from '@/utils/classnames'
16+
import { useCallback, useEffect } from 'react'
1617

1718
export const PLUGIN_TYPE_SEARCH_MAP = {
1819
all: 'all',
@@ -26,11 +27,13 @@ type PluginTypeSwitchProps = {
2627
locale?: string
2728
className?: string
2829
searchBoxAutoAnimate?: boolean
30+
showSearchParams?: boolean
2931
}
3032
const PluginTypeSwitch = ({
3133
locale,
3234
className,
3335
searchBoxAutoAnimate,
36+
showSearchParams,
3437
}: PluginTypeSwitchProps) => {
3538
const { t } = useMixedTranslation(locale)
3639
const activePluginType = useMarketplaceContext(s => s.activePluginType)
@@ -70,6 +73,23 @@ const PluginTypeSwitch = ({
7073
},
7174
]
7275

76+
const handlePopState = useCallback(() => {
77+
if (!showSearchParams)
78+
return
79+
const url = new URL(window.location.href)
80+
const category = url.searchParams.get('category') || PLUGIN_TYPE_SEARCH_MAP.all
81+
handleActivePluginTypeChange(category)
82+
}, [showSearchParams, handleActivePluginTypeChange])
83+
84+
useEffect(() => {
85+
window.addEventListener('popstate', () => {
86+
handlePopState()
87+
})
88+
return () => {
89+
window.removeEventListener('popstate', handlePopState)
90+
}
91+
}, [handlePopState])
92+
7393
return (
7494
<div className={cn(
7595
'flex shrink-0 items-center justify-center space-x-2 bg-background-body py-3',

web/app/components/plugins/marketplace/utils.ts

+20
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { PluginType } from '@/app/components/plugins/types'
44
import type {
55
CollectionsAndPluginsSearchParams,
66
MarketplaceCollection,
7+
PluginsSearchParams,
78
} from '@/app/components/plugins/marketplace/types'
89
import {
910
MARKETPLACE_API_PREFIX,
@@ -125,3 +126,22 @@ export const getMarketplaceListFilterType = (category: string) => {
125126

126127
return 'plugin'
127128
}
129+
130+
export const updateSearchParams = (pluginsSearchParams: PluginsSearchParams) => {
131+
const { query, category, tags } = pluginsSearchParams
132+
const url = new URL(window.location.href)
133+
const categoryChanged = url.searchParams.get('category') !== category
134+
if (query)
135+
url.searchParams.set('q', query)
136+
else
137+
url.searchParams.delete('q')
138+
if (category)
139+
url.searchParams.set('category', category)
140+
else
141+
url.searchParams.delete('category')
142+
if (tags && tags.length)
143+
url.searchParams.set('tags', tags.join(','))
144+
else
145+
url.searchParams.delete('tags')
146+
history[`${categoryChanged ? 'pushState' : 'replaceState'}`]({}, '', url)
147+
}

web/app/components/plugins/plugin-page/context.tsx

+4-11
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ import {
1212
} from 'use-context-selector'
1313
import { useSelector as useAppContextSelector } from '@/context/app-context'
1414
import type { FilterState } from './filter-management'
15-
import { useTranslation } from 'react-i18next'
1615
import { useTabSearchParams } from '@/hooks/use-tab-searchparams'
1716
import { noop } from 'lodash-es'
17+
import { PLUGIN_PAGE_TABS_MAP, usePluginPageTabs } from '../hooks'
1818

1919
export type PluginPageContextValue = {
2020
containerRef: React.RefObject<HTMLDivElement>
@@ -53,7 +53,6 @@ export function usePluginPageContext(selector: (value: PluginPageContextValue) =
5353
export const PluginPageContextProvider = ({
5454
children,
5555
}: PluginPageContextProviderProps) => {
56-
const { t } = useTranslation()
5756
const containerRef = useRef<HTMLDivElement>(null)
5857
const [filters, setFilters] = useState<FilterState>({
5958
categories: [],
@@ -63,16 +62,10 @@ export const PluginPageContextProvider = ({
6362
const [currentPluginID, setCurrentPluginID] = useState<string | undefined>()
6463

6564
const { enable_marketplace } = useAppContextSelector(s => s.systemFeatures)
65+
const tabs = usePluginPageTabs()
6666
const options = useMemo(() => {
67-
return [
68-
{ value: 'plugins', text: t('common.menus.plugins') },
69-
...(
70-
enable_marketplace
71-
? [{ value: 'discover', text: t('common.menus.exploreMarketplace') }]
72-
: []
73-
),
74-
]
75-
}, [t, enable_marketplace])
67+
return enable_marketplace ? tabs : tabs.filter(tab => tab.value !== PLUGIN_PAGE_TABS_MAP.marketplace)
68+
}, [tabs, enable_marketplace])
7669
const [activeTab, setActiveTab] = useTabSearchParams({
7770
defaultTab: options[0].value,
7871
})

web/app/components/plugins/plugin-page/index.tsx

+15-8
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ import { SUPPORT_INSTALL_LOCAL_FILE_EXTENSIONS } from '@/config'
4040
import { LanguagesSupported } from '@/i18n/language'
4141
import I18n from '@/context/i18n'
4242
import { noop } from 'lodash-es'
43+
import { PLUGIN_TYPE_SEARCH_MAP } from '../marketplace/plugin-type-switch'
44+
import { PLUGIN_PAGE_TABS_MAP } from '../hooks'
4345

4446
const PACKAGE_IDS_KEY = 'package-ids'
4547
const BUNDLE_INFO_KEY = 'bundle-info'
@@ -136,40 +138,45 @@ const PluginPage = ({
136138
const setActiveTab = usePluginPageContext(v => v.setActiveTab)
137139
const { enable_marketplace } = useAppContextSelector(s => s.systemFeatures)
138140

141+
const isPluginsTab = useMemo(() => activeTab === PLUGIN_PAGE_TABS_MAP.plugins, [activeTab])
142+
const isExploringMarketplace = useMemo(() => {
143+
const values = Object.values(PLUGIN_TYPE_SEARCH_MAP)
144+
return activeTab === PLUGIN_PAGE_TABS_MAP.marketplace || values.includes(activeTab)
145+
}, [activeTab])
146+
139147
const uploaderProps = useUploader({
140148
onFileChange: setCurrentFile,
141149
containerRef,
142-
enabled: activeTab === 'plugins',
150+
enabled: isPluginsTab,
143151
})
144152

145153
const { dragging, fileUploader, fileChangeHandle, removeFile } = uploaderProps
146-
147154
return (
148155
<div
149156
id='marketplace-container'
150157
ref={containerRef}
151158
style={{ scrollbarGutter: 'stable' }}
152-
className={cn('relative flex grow flex-col overflow-y-auto border-t border-divider-subtle', activeTab === 'plugins'
159+
className={cn('relative flex grow flex-col overflow-y-auto border-t border-divider-subtle', isPluginsTab
153160
? 'rounded-t-xl bg-components-panel-bg'
154161
: 'bg-background-body',
155162
)}
156163
>
157164
<div
158165
className={cn(
159-
'sticky top-0 z-10 flex min-h-[60px] items-center gap-1 self-stretch bg-components-panel-bg px-12 pb-2 pt-4', activeTab === 'discover' && 'bg-background-body',
166+
'sticky top-0 z-10 flex min-h-[60px] items-center gap-1 self-stretch bg-components-panel-bg px-12 pb-2 pt-4', isExploringMarketplace && 'bg-background-body',
160167
)}
161168
>
162169
<div className='flex w-full items-center justify-between'>
163170
<div className='flex-1'>
164171
<TabSlider
165-
value={activeTab}
172+
value={isPluginsTab ? PLUGIN_PAGE_TABS_MAP.plugins : PLUGIN_PAGE_TABS_MAP.marketplace}
166173
onChange={setActiveTab}
167174
options={options}
168175
/>
169176
</div>
170177
<div className='flex shrink-0 items-center gap-1'>
171178
{
172-
activeTab === 'discover' && (
179+
isExploringMarketplace && (
173180
<>
174181
<Link
175182
href={`https://docs.dify.ai/${locale === LanguagesSupported[1] ? 'v/zh-hans/' : ''}plugins/publish-plugins/publish-to-dify-marketplace`}
@@ -215,7 +222,7 @@ const PluginPage = ({
215222
</div>
216223
</div>
217224
</div>
218-
{activeTab === 'plugins' && (
225+
{isPluginsTab && (
219226
<>
220227
{plugins}
221228
{dragging && (
@@ -246,7 +253,7 @@ const PluginPage = ({
246253
</>
247254
)}
248255
{
249-
activeTab === 'discover' && enable_marketplace && marketplace
256+
isExploringMarketplace && enable_marketplace && marketplace
250257
}
251258

252259
{showPluginSettingModal && (

0 commit comments

Comments
 (0)