feat: marketplace add exclude

This commit is contained in:
StyleZhang 2024-11-27 16:14:15 +08:00
parent e145dba487
commit d4cda69b0e
8 changed files with 81 additions and 62 deletions

View File

@ -8,7 +8,7 @@ const PluginList = async () => {
return (
<PluginPage
plugins={<PluginsPanel />}
marketplace={<Marketplace locale={locale} />}
marketplace={<Marketplace locale={locale} shouldExclude />}
/>
)
}

View File

@ -6,6 +6,7 @@ import type {
import {
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react'
@ -30,6 +31,7 @@ import {
useMarketplacePlugins,
} from './hooks'
import { getMarketplaceListCondition } from './utils'
import { useInstalledPluginList } from '@/service/use-plugins'
export type MarketplaceContextValue = {
intersected: boolean
@ -74,6 +76,7 @@ export const MarketplaceContext = createContext<MarketplaceContextValue>({
type MarketplaceContextProviderProps = {
children: ReactNode
searchParams?: SearchParams
shouldExclude?: boolean
}
export function useMarketplaceContext(selector: (value: MarketplaceContextValue) => any) {
@ -83,7 +86,13 @@ export function useMarketplaceContext(selector: (value: MarketplaceContextValue)
export const MarketplaceContextProvider = ({
children,
searchParams,
shouldExclude,
}: MarketplaceContextProviderProps) => {
const { data, isSuccess } = useInstalledPluginList(!shouldExclude)
const exclude = useMemo(() => {
if (shouldExclude)
return data?.plugins.map(plugin => plugin.plugin_id)
}, [data?.plugins, shouldExclude])
const queryFromSearchParams = searchParams?.q || ''
const tagsFromSearchParams = searchParams?.tags ? getValidTagKeys(searchParams.tags.split(',')) : []
const hasValidTags = !!tagsFromSearchParams.length
@ -125,8 +134,15 @@ export const MarketplaceContextProvider = ({
})
history.pushState({}, '', `/${searchParams?.language ? `?language=${searchParams?.language}` : ''}`)
}
else {
if (shouldExclude && isSuccess) {
queryMarketplaceCollectionsAndPlugins({
exclude,
})
}
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [queryPlugins])
}, [queryPlugins, queryMarketplaceCollectionsAndPlugins, isSuccess, exclude])
const handleSearchPluginTextChange = useCallback((text: string) => {
setSearchPluginText(text)
@ -136,6 +152,7 @@ export const MarketplaceContextProvider = ({
queryMarketplaceCollectionsAndPlugins({
category: activePluginTypeRef.current === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : activePluginTypeRef.current,
condition: getMarketplaceListCondition(activePluginTypeRef.current),
exclude,
})
resetPlugins()
@ -148,8 +165,9 @@ export const MarketplaceContextProvider = ({
tags: filterPluginTagsRef.current,
sortBy: sortRef.current.sortBy,
sortOrder: sortRef.current.sortOrder,
exclude,
})
}, [queryPluginsWithDebounced, queryMarketplaceCollectionsAndPlugins, resetPlugins])
}, [queryPluginsWithDebounced, queryMarketplaceCollectionsAndPlugins, resetPlugins, exclude])
const handleFilterPluginTagsChange = useCallback((tags: string[]) => {
setFilterPluginTags(tags)
@ -159,6 +177,7 @@ export const MarketplaceContextProvider = ({
queryMarketplaceCollectionsAndPlugins({
category: activePluginTypeRef.current === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : activePluginTypeRef.current,
condition: getMarketplaceListCondition(activePluginTypeRef.current),
exclude,
})
resetPlugins()
@ -171,8 +190,9 @@ export const MarketplaceContextProvider = ({
tags,
sortBy: sortRef.current.sortBy,
sortOrder: sortRef.current.sortOrder,
exclude,
})
}, [queryPlugins, resetPlugins, queryMarketplaceCollectionsAndPlugins])
}, [queryPlugins, resetPlugins, queryMarketplaceCollectionsAndPlugins, exclude])
const handleActivePluginTypeChange = useCallback((type: string) => {
setActivePluginType(type)
@ -182,6 +202,7 @@ export const MarketplaceContextProvider = ({
queryMarketplaceCollectionsAndPlugins({
category: type === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : type,
condition: getMarketplaceListCondition(type),
exclude,
})
resetPlugins()
@ -194,8 +215,9 @@ export const MarketplaceContextProvider = ({
tags: filterPluginTagsRef.current,
sortBy: sortRef.current.sortBy,
sortOrder: sortRef.current.sortOrder,
exclude,
})
}, [queryPlugins, resetPlugins, queryMarketplaceCollectionsAndPlugins])
}, [queryPlugins, resetPlugins, queryMarketplaceCollectionsAndPlugins, exclude])
const handleSortChange = useCallback((sort: PluginsSort) => {
setSort(sort)
@ -207,8 +229,9 @@ export const MarketplaceContextProvider = ({
tags: filterPluginTagsRef.current,
sortBy: sortRef.current.sortBy,
sortOrder: sortRef.current.sortOrder,
exclude,
})
}, [queryPlugins])
}, [queryPlugins, exclude])
return (
<MarketplaceContext.Provider

View File

@ -17,7 +17,9 @@ import {
getPluginIconInMarketplace,
} from './utils'
import i18n from '@/i18n/i18next-config'
import { useMutationPluginsFromMarketplace } from '@/service/use-plugins'
import {
useMutationPluginsFromMarketplace,
} from '@/service/use-plugins'
export const useMarketplaceCollectionsAndPlugins = () => {
const [isLoading, setIsLoading] = useState(false)
@ -55,7 +57,7 @@ export const useMarketplacePlugins = () => {
mutate(pluginsSearchParams)
}, [mutate])
const { run: queryPluginsWithDebounced } = useDebounceFn((pluginsSearchParams) => {
const { run: queryPluginsWithDebounced } = useDebounceFn((pluginsSearchParams: PluginsSearchParams) => {
mutate(pluginsSearchParams)
}, {
wait: 500,

View File

@ -11,18 +11,26 @@ import { TanstackQueryIniter } from '@/context/query-client'
type MarketplaceProps = {
locale: string
showInstallButton?: boolean
shouldExclude?: boolean
searchParams?: SearchParams
}
const Marketplace = async ({
locale,
showInstallButton = true,
shouldExclude,
searchParams,
}: MarketplaceProps) => {
const { marketplaceCollections, marketplaceCollectionPluginsMap } = await getMarketplaceCollectionsAndPlugins()
let marketplaceCollections: any = []
let marketplaceCollectionPluginsMap = {}
if (!shouldExclude) {
const marketplaceCollectionsAndPluginsData = await getMarketplaceCollectionsAndPlugins()
marketplaceCollections = marketplaceCollectionsAndPluginsData.marketplaceCollections
marketplaceCollectionPluginsMap = marketplaceCollectionsAndPluginsData.marketplaceCollectionPluginsMap
}
return (
<TanstackQueryIniter>
<MarketplaceContextProvider searchParams={searchParams}>
<MarketplaceContextProvider searchParams={searchParams} shouldExclude={shouldExclude}>
<Description locale={locale} />
<IntersectionLine />
<SearchBoxWrapper locale={locale} />

View File

@ -27,6 +27,7 @@ export type PluginsSearchParams = {
sortOrder?: string
category?: string
tags?: string[]
exclude?: string[]
}
export type PluginsSort = {
@ -37,6 +38,7 @@ export type PluginsSort = {
export type CollectionsAndPluginsSearchParams = {
category?: string
condition?: string
exclude?: string[]
}
export type SearchParams = {

View File

@ -3,7 +3,6 @@ import { PluginType } from '@/app/components/plugins/types'
import type {
CollectionsAndPluginsSearchParams,
MarketplaceCollection,
PluginsSearchParams,
} from '@/app/components/plugins/marketplace/types'
import { MARKETPLACE_API_PREFIX } from '@/config'
@ -22,10 +21,18 @@ export const getMarketplaceCollectionsAndPlugins = async (query?: CollectionsAnd
const marketplaceCollectionsDataJson = await marketplaceCollectionsData.json()
marketplaceCollections = marketplaceCollectionsDataJson.data.collections
await Promise.all(marketplaceCollections.map(async (collection: MarketplaceCollection) => {
let url = `${MARKETPLACE_API_PREFIX}/collections/${collection.name}/plugins?page=1&page_size=100`
if (query?.category)
url += `&category=${query.category}`
const marketplaceCollectionPluginsData = await globalThis.fetch(url, { cache: 'no-store' })
const url = `${MARKETPLACE_API_PREFIX}/collections/${collection.name}/plugins`
const marketplaceCollectionPluginsData = await globalThis.fetch(
url,
{
cache: 'no-store',
method: 'POST',
body: JSON.stringify({
category: query?.category,
exclude: query?.exclude,
}),
},
)
const marketplaceCollectionPluginsDataJson = await marketplaceCollectionPluginsData.json()
const plugins = marketplaceCollectionPluginsDataJson.data.plugins.map((plugin: Plugin) => {
return {
@ -49,45 +56,6 @@ export const getMarketplaceCollectionsAndPlugins = async (query?: CollectionsAnd
}
}
export const getMarketplacePlugins = async (query: PluginsSearchParams) => {
let marketplacePlugins = [] as Plugin[]
try {
const marketplacePluginsData = await globalThis.fetch(
`${MARKETPLACE_API_PREFIX}/plugins/search/basic`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
page: 1,
page_size: 10,
query: query.query,
sort_by: query.sortBy,
sort_order: query.sortOrder,
category: query.category,
tags: query.tags,
}),
},
)
const marketplacePluginsDataJson = await marketplacePluginsData.json()
marketplacePlugins = marketplacePluginsDataJson.data.plugins.map((plugin: Plugin) => {
return {
...plugin,
icon: getPluginIconInMarketplace(plugin),
}
})
}
// eslint-disable-next-line unused-imports/no-unused-vars
catch (e) {
marketplacePlugins = []
}
return {
marketplacePlugins,
}
}
export const getMarketplaceListCondition = (pluginType: string) => {
if (pluginType === PluginType.tool)
return 'category=tool'

View File

@ -1,5 +1,6 @@
import {
useEffect,
useMemo,
} from 'react'
import {
useMarketplaceCollectionsAndPlugins,
@ -7,8 +8,14 @@ import {
} from '@/app/components/plugins/marketplace/hooks'
import { PluginType } from '@/app/components/plugins/types'
import { getMarketplaceListCondition } from '@/app/components/plugins/marketplace/utils'
import { useAllToolProviders } from '@/service/use-tools'
export const useMarketplace = (searchPluginText: string, filterPluginTags: string[]) => {
const { data: toolProvidersData, isSuccess } = useAllToolProviders()
const exclude = useMemo(() => {
if (isSuccess)
return toolProvidersData?.filter(toolProvider => !!toolProvider.plugin_id).map(toolProvider => toolProvider.plugin_id!)
}, [isSuccess, toolProvidersData])
const {
isLoading,
marketplaceCollections,
@ -24,12 +31,13 @@ export const useMarketplace = (searchPluginText: string, filterPluginTags: strin
} = useMarketplacePlugins()
useEffect(() => {
if (searchPluginText || filterPluginTags.length) {
if ((searchPluginText || filterPluginTags.length) && isSuccess) {
if (searchPluginText) {
queryPluginsWithDebounced({
category: PluginType.tool,
query: searchPluginText,
tags: filterPluginTags,
exclude,
})
return
}
@ -37,16 +45,20 @@ export const useMarketplace = (searchPluginText: string, filterPluginTags: strin
category: PluginType.tool,
query: searchPluginText,
tags: filterPluginTags,
exclude,
})
}
else {
queryMarketplaceCollectionsAndPlugins({
category: PluginType.tool,
condition: getMarketplaceListCondition(PluginType.tool),
})
resetPlugins()
if (isSuccess) {
queryMarketplaceCollectionsAndPlugins({
category: PluginType.tool,
condition: getMarketplaceListCondition(PluginType.tool),
exclude,
})
resetPlugins()
}
}
}, [searchPluginText, filterPluginTags, queryPlugins, queryMarketplaceCollectionsAndPlugins, queryPluginsWithDebounced, resetPlugins])
}, [searchPluginText, filterPluginTags, queryPlugins, queryMarketplaceCollectionsAndPlugins, queryPluginsWithDebounced, resetPlugins, exclude, isSuccess])
return {
isLoading: isLoading || isPluginsLoading,

View File

@ -29,10 +29,12 @@ import { useInvalidateAllBuiltInTools } from './use-tools'
const NAME_SPACE = 'plugins'
const useInstalledPluginListKey = [NAME_SPACE, 'installedPluginList']
export const useInstalledPluginList = () => {
export const useInstalledPluginList = (disable?: boolean) => {
return useQuery<InstalledPluginListResponse>({
queryKey: useInstalledPluginListKey,
queryFn: () => get<InstalledPluginListResponse>('/workspaces/current/plugin/list'),
enabled: !disable,
initialData: !disable ? undefined : { plugins: [] },
})
}
@ -225,6 +227,7 @@ export const useMutationPluginsFromMarketplace = () => {
sortOrder,
category,
tags,
exclude,
} = pluginsSearchParams
return postMarketplace<{ data: PluginsFromMarketplaceResponse }>('/plugins/search/basic', {
body: {
@ -235,6 +238,7 @@ export const useMutationPluginsFromMarketplace = () => {
sort_order: sortOrder,
category: category !== 'all' ? category : '',
tags,
exclude,
},
})
},