diff --git a/web/app/(commonLayout)/plugins/page.tsx b/web/app/(commonLayout)/plugins/page.tsx
index f44ff6522a..921b129781 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/header/account-setting/model-provider-page/hooks.ts b/web/app/components/header/account-setting/model-provider-page/hooks.ts
index ab3e3e5593..c0d2e86885 100644
--- a/web/app/components/header/account-setting/model-provider-page/hooks.ts
+++ b/web/app/components/header/account-setting/model-provider-page/hooks.ts
@@ -285,3 +285,25 @@ export const useMarketplace = (providers: ModelProvider[], searchText: string) =
plugins: plugins?.filter(plugin => plugin.type !== 'bundle'),
}
}
+
+export const useMarketplaceAllPlugins = () => {
+ const {
+ plugins,
+ queryPlugins,
+ isLoading,
+ } = useMarketplacePlugins()
+
+ useEffect(() => {
+ queryPlugins({
+ query: '',
+ category: PluginType.model,
+ type: 'plugin',
+ pageSize: 1000,
+ })
+ }, [queryPlugins])
+
+ return {
+ plugins: plugins?.filter(plugin => plugin.type !== 'bundle'),
+ isLoading,
+ }
+}
diff --git a/web/app/components/header/account-setting/model-provider-page/index.tsx b/web/app/components/header/account-setting/model-provider-page/index.tsx
index 8eea7d3472..35946a73e2 100644
--- a/web/app/components/header/account-setting/model-provider-page/index.tsx
+++ b/web/app/components/header/account-setting/model-provider-page/index.tsx
@@ -22,6 +22,7 @@ import {
import {
useDefaultModel,
useMarketplace,
+ useMarketplaceAllPlugins,
useUpdateModelList,
useUpdateModelProviders,
} from './hooks'
@@ -128,6 +129,10 @@ const ModelProviderPage = ({ searchText }: Props) => {
marketplaceCollectionPluginsMap,
isLoading: isPluginsLoading,
} = useMarketplace(providers, searchText)
+ const {
+ plugins: allPlugins,
+ isLoading: isAllPluginsLoading,
+ } = useMarketplaceAllPlugins()
const cardRender = useCallback((plugin: Plugin) => {
if (plugin.type === 'bundle')
@@ -206,12 +211,12 @@ const ModelProviderPage = ({ searchText }: Props) => {
{t('common.modelProvider.discoverMore')}
- Dify Marketplace
+ {t('plugin.marketplace.difyMarketplace')}
- {!collapse && isPluginsLoading && }
+ {!collapse && (isPluginsLoading || isAllPluginsLoading) && }
{
!isPluginsLoading && (
{
/>
)
}
+ {
+ !isAllPluginsLoading && (
+
+ )
+ }
)
diff --git a/web/app/components/plugins/marketplace/context.tsx b/web/app/components/plugins/marketplace/context.tsx
index 1579ab620d..3236e1aefc 100644
--- a/web/app/components/plugins/marketplace/context.tsx
+++ b/web/app/components/plugins/marketplace/context.tsx
@@ -45,15 +45,19 @@ export type MarketplaceContextValue = {
handleFilterPluginTagsChange: (tags: string[]) => void
activePluginType: string
handleActivePluginTypeChange: (type: string) => void
+ page: number
+ handlePageChange: (page: number) => void
plugins?: Plugin[]
resetPlugins: () => void
sort: PluginsSort
handleSortChange: (sort: PluginsSort) => void
+ handleQueryPluginsWhenNoCollection: () => void
marketplaceCollectionsFromClient?: MarketplaceCollection[]
setMarketplaceCollectionsFromClient: (collections: MarketplaceCollection[]) => void
marketplaceCollectionPluginsMapFromClient?: Record
setMarketplaceCollectionPluginsMapFromClient: (map: Record) => void
isLoading: boolean
+ isSuccessCollections: boolean
}
export const MarketplaceContext = createContext({
@@ -65,15 +69,19 @@ export const MarketplaceContext = createContext({
handleFilterPluginTagsChange: () => {},
activePluginType: 'all',
handleActivePluginTypeChange: () => {},
+ page: 1,
+ handlePageChange: () => {},
plugins: undefined,
resetPlugins: () => {},
sort: DEFAULT_SORT,
handleSortChange: () => {},
+ handleQueryPluginsWhenNoCollection: () => {},
marketplaceCollectionsFromClient: [],
setMarketplaceCollectionsFromClient: () => {},
marketplaceCollectionPluginsMapFromClient: {},
setMarketplaceCollectionPluginsMapFromClient: () => {},
isLoading: false,
+ isSuccessCollections: false,
})
type MarketplaceContextProviderProps = {
@@ -108,6 +116,8 @@ export const MarketplaceContextProvider = ({
const filterPluginTagsRef = useRef(filterPluginTags)
const [activePluginType, setActivePluginType] = useState(categoryFromSearchParams)
const activePluginTypeRef = useRef(activePluginType)
+ const [page, setPage] = useState(1)
+ const pageRef = useRef(page)
const [sort, setSort] = useState(DEFAULT_SORT)
const sortRef = useRef(sort)
const {
@@ -117,6 +127,7 @@ export const MarketplaceContextProvider = ({
setMarketplaceCollectionPluginsMap: setMarketplaceCollectionPluginsMapFromClient,
queryMarketplaceCollectionsAndPlugins,
isLoading,
+ isSuccess: isSuccessCollections,
} = useMarketplaceCollectionsAndPlugins()
const {
plugins,
@@ -135,6 +146,7 @@ export const MarketplaceContextProvider = ({
sortBy: sortRef.current.sortBy,
sortOrder: sortRef.current.sortOrder,
type: getMarketplaceListFilterType(activePluginTypeRef.current),
+ page: pageRef.current,
})
history.pushState({}, '', `/${searchParams?.language ? `?language=${searchParams?.language}` : ''}`)
}
@@ -152,6 +164,8 @@ export const MarketplaceContextProvider = ({
const handleSearchPluginTextChange = useCallback((text: string) => {
setSearchPluginText(text)
searchPluginTextRef.current = text
+ setPage(1)
+ pageRef.current = 1
if (!searchPluginTextRef.current && !filterPluginTagsRef.current.length) {
queryMarketplaceCollectionsAndPlugins({
@@ -172,12 +186,15 @@ export const MarketplaceContextProvider = ({
sortBy: sortRef.current.sortBy,
sortOrder: sortRef.current.sortOrder,
exclude,
+ page: pageRef.current,
})
}, [queryPluginsWithDebounced, queryMarketplaceCollectionsAndPlugins, resetPlugins, exclude])
const handleFilterPluginTagsChange = useCallback((tags: string[]) => {
setFilterPluginTags(tags)
filterPluginTagsRef.current = tags
+ setPage(1)
+ pageRef.current = 1
if (!searchPluginTextRef.current && !filterPluginTagsRef.current.length) {
queryMarketplaceCollectionsAndPlugins({
@@ -199,12 +216,15 @@ export const MarketplaceContextProvider = ({
sortOrder: sortRef.current.sortOrder,
exclude,
type: getMarketplaceListFilterType(activePluginTypeRef.current),
+ page: pageRef.current,
})
}, [queryPlugins, resetPlugins, queryMarketplaceCollectionsAndPlugins, exclude])
const handleActivePluginTypeChange = useCallback((type: string) => {
setActivePluginType(type)
activePluginTypeRef.current = type
+ setPage(1)
+ pageRef.current = 1
if (!searchPluginTextRef.current && !filterPluginTagsRef.current.length) {
queryMarketplaceCollectionsAndPlugins({
@@ -226,12 +246,25 @@ export const MarketplaceContextProvider = ({
sortOrder: sortRef.current.sortOrder,
exclude,
type: getMarketplaceListFilterType(activePluginTypeRef.current),
+ page: pageRef.current,
})
}, [queryPlugins, resetPlugins, queryMarketplaceCollectionsAndPlugins, exclude])
- const handleSortChange = useCallback((sort: PluginsSort) => {
- setSort(sort)
- sortRef.current = sort
+ const handlePageChange = useCallback(() => {
+ setPage(pageRef.current + 1)
+ pageRef.current++
+
+ if (!searchPluginTextRef.current && !filterPluginTagsRef.current.length) {
+ queryMarketplaceCollectionsAndPlugins({
+ category: activePluginTypeRef.current === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : activePluginTypeRef.current,
+ condition: getMarketplaceListCondition(activePluginTypeRef.current),
+ exclude,
+ type: getMarketplaceListFilterType(activePluginTypeRef.current),
+ })
+ resetPlugins()
+
+ return
+ }
queryPlugins({
query: searchPluginTextRef.current,
@@ -241,9 +274,43 @@ export const MarketplaceContextProvider = ({
sortOrder: sortRef.current.sortOrder,
exclude,
type: getMarketplaceListFilterType(activePluginTypeRef.current),
+ page: pageRef.current,
+ })
+ }, [exclude, queryPlugins, queryMarketplaceCollectionsAndPlugins, resetPlugins])
+
+ const handleSortChange = useCallback((sort: PluginsSort) => {
+ setSort(sort)
+ sortRef.current = sort
+ setPage(1)
+ pageRef.current = 1
+
+ queryPlugins({
+ query: searchPluginTextRef.current,
+ category: activePluginTypeRef.current === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : activePluginTypeRef.current,
+ tags: filterPluginTagsRef.current,
+ sortBy: sortRef.current.sortBy,
+ sortOrder: sortRef.current.sortOrder,
+ exclude,
+ type: getMarketplaceListFilterType(activePluginTypeRef.current),
+ page: pageRef.current,
})
}, [queryPlugins, exclude])
+ const handleQueryPluginsWhenNoCollection = useCallback(() => {
+ queryPlugins({
+ query: searchPluginTextRef.current,
+ category: activePluginTypeRef.current === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : activePluginTypeRef.current,
+ tags: filterPluginTagsRef.current,
+ sortBy: sortRef.current.sortBy,
+ sortOrder: sortRef.current.sortOrder,
+ exclude,
+ type: getMarketplaceListFilterType(activePluginTypeRef.current),
+ page: pageRef.current,
+ })
+ }, [exclude, queryPlugins])
+
+ // useMarketplaceContainerScroll(handlePageChange)
+
return (
{children}
diff --git a/web/app/components/plugins/marketplace/hooks.ts b/web/app/components/plugins/marketplace/hooks.ts
index f58bb9bacd..1d68d9ec70 100644
--- a/web/app/components/plugins/marketplace/hooks.ts
+++ b/web/app/components/plugins/marketplace/hooks.ts
@@ -1,5 +1,6 @@
import {
useCallback,
+ useEffect,
useState,
} from 'react'
import { useTranslation } from 'react-i18next'
@@ -23,16 +24,25 @@ import {
export const useMarketplaceCollectionsAndPlugins = () => {
const [isLoading, setIsLoading] = useState(false)
+ const [isSuccess, setIsSuccess] = useState(false)
const [marketplaceCollections, setMarketplaceCollections] = useState()
const [marketplaceCollectionPluginsMap, setMarketplaceCollectionPluginsMap] = useState>()
const queryMarketplaceCollectionsAndPlugins = useCallback(async (query?: CollectionsAndPluginsSearchParams) => {
- setIsLoading(true)
- const { marketplaceCollections, marketplaceCollectionPluginsMap } = await getMarketplaceCollectionsAndPlugins(query)
- setIsLoading(false)
-
- setMarketplaceCollections(marketplaceCollections)
- setMarketplaceCollectionPluginsMap(marketplaceCollectionPluginsMap)
+ try {
+ setIsLoading(true)
+ setIsSuccess(false)
+ const { marketplaceCollections, marketplaceCollectionPluginsMap } = await getMarketplaceCollectionsAndPlugins(query)
+ setIsLoading(false)
+ setIsSuccess(true)
+ setMarketplaceCollections(marketplaceCollections)
+ setMarketplaceCollectionPluginsMap(marketplaceCollectionPluginsMap)
+ }
+ // eslint-disable-next-line unused-imports/no-unused-vars
+ catch (e) {
+ setIsLoading(false)
+ setIsSuccess(false)
+ }
}, [])
return {
@@ -42,6 +52,7 @@ export const useMarketplaceCollectionsAndPlugins = () => {
setMarketplaceCollectionPluginsMap,
queryMarketplaceCollectionsAndPlugins,
isLoading,
+ isSuccess,
}
}
@@ -67,6 +78,7 @@ export const useMarketplacePlugins = () => {
plugins: data?.data?.plugins.map((plugin) => {
return getFormattedPlugin(plugin)
}),
+ total: data?.data?.total,
resetPlugins: reset,
queryPlugins,
queryPluginsWithDebounced,
@@ -84,3 +96,28 @@ export const useMixedTranslation = (localeFromOuter?: string) => {
t,
}
}
+
+export const useMarketplaceContainerScroll = (callback: () => void) => {
+ const container = document.getElementById('marketplace-container')
+
+ const handleScroll = useCallback((e: Event) => {
+ const target = e.target as HTMLDivElement
+ const {
+ scrollTop,
+ scrollHeight,
+ clientHeight,
+ } = target
+ if (scrollTop + clientHeight >= scrollHeight - 5 && scrollTop > 0)
+ callback()
+ }, [callback])
+
+ useEffect(() => {
+ if (container)
+ container.addEventListener('scroll', handleScroll)
+
+ return () => {
+ if (container)
+ container.removeEventListener('scroll', handleScroll)
+ }
+ }, [container, handleScroll])
+}
diff --git a/web/app/components/plugins/marketplace/index.tsx b/web/app/components/plugins/marketplace/index.tsx
index 9f8bbb2e76..8549206e06 100644
--- a/web/app/components/plugins/marketplace/index.tsx
+++ b/web/app/components/plugins/marketplace/index.tsx
@@ -13,12 +13,14 @@ type MarketplaceProps = {
showInstallButton?: boolean
shouldExclude?: boolean
searchParams?: SearchParams
+ pluginTypeSwitchClassName?: string
}
const Marketplace = async ({
locale,
showInstallButton = true,
shouldExclude,
searchParams,
+ pluginTypeSwitchClassName,
}: MarketplaceProps) => {
let marketplaceCollections: any = []
let marketplaceCollectionPluginsMap = {}
@@ -34,7 +36,10 @@ const Marketplace = async ({
-
+
v.marketplaceCollectionsFromClient)
const marketplaceCollectionPluginsMapFromClient = useMarketplaceContext(v => v.marketplaceCollectionPluginsMapFromClient)
const isLoading = useMarketplaceContext(v => v.isLoading)
+ const isSuccessCollections = useMarketplaceContext(v => v.isSuccessCollections)
+ const handleQueryPluginsWhenNoCollection = useMarketplaceContext(v => v.handleQueryPluginsWhenNoCollection)
+
+ useEffect(() => {
+ if (!marketplaceCollectionsFromClient?.length && isSuccessCollections)
+ handleQueryPluginsWhenNoCollection()
+ }, [handleQueryPluginsWhenNoCollection, marketplaceCollections, marketplaceCollectionsFromClient, isSuccessCollections])
return (
diff --git a/web/app/components/plugins/marketplace/plugin-type-switch.tsx b/web/app/components/plugins/marketplace/plugin-type-switch.tsx
index 82758ad87d..796e3a5073 100644
--- a/web/app/components/plugins/marketplace/plugin-type-switch.tsx
+++ b/web/app/components/plugins/marketplace/plugin-type-switch.tsx
@@ -19,9 +19,11 @@ export const PLUGIN_TYPE_SEARCH_MAP = {
}
type PluginTypeSwitchProps = {
locale?: string
+ className?: string
}
const PluginTypeSwitch = ({
locale,
+ className,
}: PluginTypeSwitchProps) => {
const { t } = useMixedTranslation(locale)
const activePluginType = useMarketplaceContext(s => s.activePluginType)
@@ -57,7 +59,8 @@ const PluginTypeSwitch = ({
return (
{
options.map(option => (
diff --git a/web/app/components/plugins/types.ts b/web/app/components/plugins/types.ts
index f743507ae4..dd59ed6c57 100644
--- a/web/app/components/plugins/types.ts
+++ b/web/app/components/plugins/types.ts
@@ -312,6 +312,7 @@ export type UninstallPluginResponse = {
export type PluginsFromMarketplaceResponse = {
plugins: Plugin[]
+ total: number
}
export type PluginsFromMarketplaceByInfoResponse = {
list: {
diff --git a/web/service/use-plugins.ts b/web/service/use-plugins.ts
index e787f6ed84..dbc93acaf4 100644
--- a/web/service/use-plugins.ts
+++ b/web/service/use-plugins.ts
@@ -297,11 +297,12 @@ export const useMutationPluginsFromMarketplace = () => {
tags,
exclude,
type,
+ page = 1,
} = pluginsSearchParams
return postMarketplace<{ data: PluginsFromMarketplaceResponse }>('/plugins/search/basic', {
body: {
- page: 1,
- page_size: 10,
+ page,
+ page_size: 100,
query,
sort_by: sortBy,
sort_order: sortOrder,