mirror of https://github.com/langgenius/dify.git
feat: marketplace list url support search & tags
This commit is contained in:
parent
c5c06c18f1
commit
43d7a538dc
|
|
@ -0,0 +1,25 @@
|
|||
export const tagKeys = [
|
||||
'search',
|
||||
'image',
|
||||
'videos',
|
||||
'weather',
|
||||
'finance',
|
||||
'design',
|
||||
'travel',
|
||||
'social',
|
||||
'news',
|
||||
'medical',
|
||||
'productivity',
|
||||
'education',
|
||||
'business',
|
||||
'entertainment',
|
||||
'utilities',
|
||||
'other',
|
||||
]
|
||||
|
||||
export const categoryKeys = [
|
||||
'model',
|
||||
'tool',
|
||||
'extension',
|
||||
'bundle',
|
||||
]
|
||||
|
|
@ -1,5 +1,9 @@
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import type { TFunction } from 'i18next'
|
||||
import {
|
||||
categoryKeys,
|
||||
tagKeys,
|
||||
} from './constants'
|
||||
|
||||
type Tag = {
|
||||
name: string
|
||||
|
|
@ -10,72 +14,12 @@ export const useTags = (translateFromOut?: TFunction) => {
|
|||
const { t: translation } = useTranslation()
|
||||
const t = translateFromOut || translation
|
||||
|
||||
const tags = [
|
||||
{
|
||||
name: 'search',
|
||||
label: t('pluginTags.tags.search'),
|
||||
},
|
||||
{
|
||||
name: 'image',
|
||||
label: t('pluginTags.tags.image'),
|
||||
},
|
||||
{
|
||||
name: 'videos',
|
||||
label: t('pluginTags.tags.videos'),
|
||||
},
|
||||
{
|
||||
name: 'weather',
|
||||
label: t('pluginTags.tags.weather'),
|
||||
},
|
||||
{
|
||||
name: 'finance',
|
||||
label: t('pluginTags.tags.finance'),
|
||||
},
|
||||
{
|
||||
name: 'design',
|
||||
label: t('pluginTags.tags.design'),
|
||||
},
|
||||
{
|
||||
name: 'travel',
|
||||
label: t('pluginTags.tags.travel'),
|
||||
},
|
||||
{
|
||||
name: 'social',
|
||||
label: t('pluginTags.tags.social'),
|
||||
},
|
||||
{
|
||||
name: 'news',
|
||||
label: t('pluginTags.tags.news'),
|
||||
},
|
||||
{
|
||||
name: 'medical',
|
||||
label: t('pluginTags.tags.medical'),
|
||||
},
|
||||
{
|
||||
name: 'productivity',
|
||||
label: t('pluginTags.tags.productivity'),
|
||||
},
|
||||
{
|
||||
name: 'education',
|
||||
label: t('pluginTags.tags.education'),
|
||||
},
|
||||
{
|
||||
name: 'business',
|
||||
label: t('pluginTags.tags.business'),
|
||||
},
|
||||
{
|
||||
name: 'entertainment',
|
||||
label: t('pluginTags.tags.entertainment'),
|
||||
},
|
||||
{
|
||||
name: 'utilities',
|
||||
label: t('pluginTags.tags.utilities'),
|
||||
},
|
||||
{
|
||||
name: 'other',
|
||||
label: t('pluginTags.tags.other'),
|
||||
},
|
||||
]
|
||||
const tags = tagKeys.map((tag) => {
|
||||
return {
|
||||
name: tag,
|
||||
label: t(`pluginTags.tags.${tag}`),
|
||||
}
|
||||
})
|
||||
|
||||
const tagsMap = tags.reduce((acc, tag) => {
|
||||
acc[tag.name] = tag
|
||||
|
|
@ -97,24 +41,12 @@ export const useCategories = (translateFromOut?: TFunction) => {
|
|||
const { t: translation } = useTranslation()
|
||||
const t = translateFromOut || translation
|
||||
|
||||
const categories = [
|
||||
{
|
||||
name: 'model',
|
||||
label: t('plugin.category.models'),
|
||||
},
|
||||
{
|
||||
name: 'tool',
|
||||
label: t('plugin.category.tools'),
|
||||
},
|
||||
{
|
||||
name: 'extension',
|
||||
label: t('plugin.category.extensions'),
|
||||
},
|
||||
{
|
||||
name: 'bundle',
|
||||
label: t('plugin.category.bundles'),
|
||||
},
|
||||
]
|
||||
const categories = categoryKeys.map((category) => {
|
||||
return {
|
||||
name: category,
|
||||
label: t(`plugin.category.${category}s`),
|
||||
}
|
||||
})
|
||||
|
||||
const categoriesMap = categories.reduce((acc, category) => {
|
||||
acc[category.name] = category
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import type {
|
|||
} from 'react'
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react'
|
||||
|
|
@ -14,9 +15,14 @@ import {
|
|||
} from 'use-context-selector'
|
||||
import { PLUGIN_TYPE_SEARCH_MAP } from './plugin-type-switch'
|
||||
import type { Plugin } from '../types'
|
||||
import {
|
||||
getValidCategoryKeys,
|
||||
getValidTagKeys,
|
||||
} from '../utils'
|
||||
import type {
|
||||
MarketplaceCollection,
|
||||
PluginsSort,
|
||||
SearchParams,
|
||||
} from './types'
|
||||
import { DEFAULT_SORT } from './constants'
|
||||
import {
|
||||
|
|
@ -66,6 +72,7 @@ export const MarketplaceContext = createContext<MarketplaceContextValue>({
|
|||
|
||||
type MarketplaceContextProviderProps = {
|
||||
children: ReactNode
|
||||
searchParams?: SearchParams
|
||||
}
|
||||
|
||||
export function useMarketplaceContext(selector: (value: MarketplaceContextValue) => any) {
|
||||
|
|
@ -74,13 +81,19 @@ export function useMarketplaceContext(selector: (value: MarketplaceContextValue)
|
|||
|
||||
export const MarketplaceContextProvider = ({
|
||||
children,
|
||||
searchParams,
|
||||
}: MarketplaceContextProviderProps) => {
|
||||
const queryFromSearchParams = searchParams?.q || ''
|
||||
const tagsFromSearchParams = searchParams?.tags ? getValidTagKeys(searchParams.tags.split(',')) : []
|
||||
const hasValidTags = !!tagsFromSearchParams.length
|
||||
const hasValidCategory = getValidCategoryKeys(searchParams?.category)
|
||||
const categoryFromSearchParams = hasValidCategory || PLUGIN_TYPE_SEARCH_MAP.all
|
||||
const [intersected, setIntersected] = useState(true)
|
||||
const [searchPluginText, setSearchPluginText] = useState('')
|
||||
const [searchPluginText, setSearchPluginText] = useState(queryFromSearchParams)
|
||||
const searchPluginTextRef = useRef(searchPluginText)
|
||||
const [filterPluginTags, setFilterPluginTags] = useState<string[]>([])
|
||||
const [filterPluginTags, setFilterPluginTags] = useState<string[]>(tagsFromSearchParams)
|
||||
const filterPluginTagsRef = useRef(filterPluginTags)
|
||||
const [activePluginType, setActivePluginType] = useState(PLUGIN_TYPE_SEARCH_MAP.all)
|
||||
const [activePluginType, setActivePluginType] = useState(categoryFromSearchParams)
|
||||
const activePluginTypeRef = useRef(activePluginType)
|
||||
const [sort, setSort] = useState(DEFAULT_SORT)
|
||||
const sortRef = useRef(sort)
|
||||
|
|
@ -100,6 +113,20 @@ export const MarketplaceContextProvider = ({
|
|||
isLoading: isPluginsLoading,
|
||||
} = useMarketplacePlugins()
|
||||
|
||||
useEffect(() => {
|
||||
if (queryFromSearchParams || hasValidTags || hasValidCategory) {
|
||||
queryPlugins({
|
||||
query: queryFromSearchParams,
|
||||
category: hasValidCategory,
|
||||
tags: hasValidTags ? tagsFromSearchParams : [],
|
||||
sortBy: sortRef.current.sortBy,
|
||||
sortOrder: sortRef.current.sortOrder,
|
||||
})
|
||||
history.pushState({}, '', `/${searchParams?.language ? `?language=${searchParams?.language}` : ''}`)
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [queryPlugins])
|
||||
|
||||
const handleSearchPluginTextChange = useCallback((text: string) => {
|
||||
setSearchPluginText(text)
|
||||
searchPluginTextRef.current = text
|
||||
|
|
|
|||
|
|
@ -4,22 +4,25 @@ import IntersectionLine from './intersection-line'
|
|||
import SearchBoxWrapper from './search-box/search-box-wrapper'
|
||||
import PluginTypeSwitch from './plugin-type-switch'
|
||||
import ListWrapper from './list/list-wrapper'
|
||||
import type { SearchParams } from './types'
|
||||
import { getMarketplaceCollectionsAndPlugins } from './utils'
|
||||
import { TanstackQueryIniter } from '@/context/query-client'
|
||||
|
||||
type MarketplaceProps = {
|
||||
locale?: string
|
||||
showInstallButton?: boolean
|
||||
searchParams?: SearchParams
|
||||
}
|
||||
const Marketplace = async ({
|
||||
locale,
|
||||
showInstallButton = true,
|
||||
searchParams,
|
||||
}: MarketplaceProps) => {
|
||||
const { marketplaceCollections, marketplaceCollectionPluginsMap } = await getMarketplaceCollectionsAndPlugins()
|
||||
|
||||
return (
|
||||
<TanstackQueryIniter>
|
||||
<MarketplaceContextProvider>
|
||||
<MarketplaceContextProvider searchParams={searchParams}>
|
||||
<Description locale={locale} />
|
||||
<IntersectionLine />
|
||||
<SearchBoxWrapper locale={locale} />
|
||||
|
|
|
|||
|
|
@ -36,3 +36,10 @@ export type PluginsSort = {
|
|||
export type CollectionsAndPluginsSearchParams = {
|
||||
category?: string
|
||||
}
|
||||
|
||||
export type SearchParams = {
|
||||
language?: string
|
||||
q?: string
|
||||
tags?: string
|
||||
category?: string
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
import {
|
||||
categoryKeys,
|
||||
tagKeys,
|
||||
} from './constants'
|
||||
|
||||
export const getValidTagKeys = (tags: string[]) => {
|
||||
return tags.filter(tag => tagKeys.includes(tag))
|
||||
}
|
||||
|
||||
export const getValidCategoryKeys = (category?: string) => {
|
||||
const currentCategory = categoryKeys.find(key => key === category)
|
||||
return currentCategory ? `${currentCategory}s` : ''
|
||||
}
|
||||
|
|
@ -57,7 +57,7 @@ const Marketplace = ({
|
|||
</span>
|
||||
{t('common.operation.in')}
|
||||
<a
|
||||
href={`${MARKETPLACE_URL_PREFIX}?language=${locale}`}
|
||||
href={`${MARKETPLACE_URL_PREFIX}?language=${locale}&q=${searchPluginText}&tags=${filterPluginTags.join(',')}`}
|
||||
className='flex items-center ml-1 system-sm-medium text-text-accent'
|
||||
target='_blank'
|
||||
>
|
||||
|
|
|
|||
Loading…
Reference in New Issue