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 ( return (
<PluginPage <PluginPage
plugins={<PluginsPanel />} plugins={<PluginsPanel />}
marketplace={<Marketplace locale={locale} />} marketplace={<Marketplace locale={locale} shouldExclude />}
/> />
) )
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -3,7 +3,6 @@ import { PluginType } from '@/app/components/plugins/types'
import type { import type {
CollectionsAndPluginsSearchParams, CollectionsAndPluginsSearchParams,
MarketplaceCollection, MarketplaceCollection,
PluginsSearchParams,
} from '@/app/components/plugins/marketplace/types' } from '@/app/components/plugins/marketplace/types'
import { MARKETPLACE_API_PREFIX } from '@/config' import { MARKETPLACE_API_PREFIX } from '@/config'
@ -22,10 +21,18 @@ export const getMarketplaceCollectionsAndPlugins = async (query?: CollectionsAnd
const marketplaceCollectionsDataJson = await marketplaceCollectionsData.json() const marketplaceCollectionsDataJson = await marketplaceCollectionsData.json()
marketplaceCollections = marketplaceCollectionsDataJson.data.collections marketplaceCollections = marketplaceCollectionsDataJson.data.collections
await Promise.all(marketplaceCollections.map(async (collection: MarketplaceCollection) => { await Promise.all(marketplaceCollections.map(async (collection: MarketplaceCollection) => {
let url = `${MARKETPLACE_API_PREFIX}/collections/${collection.name}/plugins?page=1&page_size=100` const url = `${MARKETPLACE_API_PREFIX}/collections/${collection.name}/plugins`
if (query?.category) const marketplaceCollectionPluginsData = await globalThis.fetch(
url += `&category=${query.category}` url,
const marketplaceCollectionPluginsData = await globalThis.fetch(url, { cache: 'no-store' }) {
cache: 'no-store',
method: 'POST',
body: JSON.stringify({
category: query?.category,
exclude: query?.exclude,
}),
},
)
const marketplaceCollectionPluginsDataJson = await marketplaceCollectionPluginsData.json() const marketplaceCollectionPluginsDataJson = await marketplaceCollectionPluginsData.json()
const plugins = marketplaceCollectionPluginsDataJson.data.plugins.map((plugin: Plugin) => { const plugins = marketplaceCollectionPluginsDataJson.data.plugins.map((plugin: Plugin) => {
return { 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) => { export const getMarketplaceListCondition = (pluginType: string) => {
if (pluginType === PluginType.tool) if (pluginType === PluginType.tool)
return 'category=tool' return 'category=tool'

View File

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

View File

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