diff --git a/web/app/(commonLayout)/plugins/page.tsx b/web/app/(commonLayout)/plugins/page.tsx
index 516cc138a2..f44ff6522a 100644
--- a/web/app/(commonLayout)/plugins/page.tsx
+++ b/web/app/(commonLayout)/plugins/page.tsx
@@ -8,7 +8,7 @@ const PluginList = async () => {
return (
}
- marketplace={}
+ marketplace={}
/>
)
}
diff --git a/web/app/components/plugins/marketplace/context.tsx b/web/app/components/plugins/marketplace/context.tsx
index 4b692cac28..57e3dae420 100644
--- a/web/app/components/plugins/marketplace/context.tsx
+++ b/web/app/components/plugins/marketplace/context.tsx
@@ -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({
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 (
{
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,
diff --git a/web/app/components/plugins/marketplace/index.tsx b/web/app/components/plugins/marketplace/index.tsx
index 5afb8c31ae..9f8bbb2e76 100644
--- a/web/app/components/plugins/marketplace/index.tsx
+++ b/web/app/components/plugins/marketplace/index.tsx
@@ -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 (
-
+
diff --git a/web/app/components/plugins/marketplace/types.ts b/web/app/components/plugins/marketplace/types.ts
index 58424d6c68..844ced0e00 100644
--- a/web/app/components/plugins/marketplace/types.ts
+++ b/web/app/components/plugins/marketplace/types.ts
@@ -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 = {
diff --git a/web/app/components/plugins/marketplace/utils.ts b/web/app/components/plugins/marketplace/utils.ts
index c9f77318f7..7252b9d315 100644
--- a/web/app/components/plugins/marketplace/utils.ts
+++ b/web/app/components/plugins/marketplace/utils.ts
@@ -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'
diff --git a/web/app/components/tools/marketplace/hooks.ts b/web/app/components/tools/marketplace/hooks.ts
index 3aec42be75..e29920ab29 100644
--- a/web/app/components/tools/marketplace/hooks.ts
+++ b/web/app/components/tools/marketplace/hooks.ts
@@ -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,
diff --git a/web/service/use-plugins.ts b/web/service/use-plugins.ts
index 9072810426..9bdf63c5c5 100644
--- a/web/service/use-plugins.ts
+++ b/web/service/use-plugins.ts
@@ -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({
queryKey: useInstalledPluginListKey,
queryFn: () => get('/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,
},
})
},