From d6a4cbc6ccf107874825beaac2586fa7904a4429 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Tue, 3 Dec 2024 18:02:57 +0800 Subject: [PATCH] fix: marketplace list --- web/app/(commonLayout)/plugins/page.tsx | 2 +- .../model-provider-page/hooks.ts | 22 ++++++ .../model-provider-page/index.tsx | 22 +++++- .../plugins/marketplace/context.tsx | 77 ++++++++++++++++++- .../components/plugins/marketplace/hooks.ts | 49 ++++++++++-- .../components/plugins/marketplace/index.tsx | 7 +- .../plugins/marketplace/list/list-wrapper.tsx | 8 ++ .../marketplace/plugin-type-switch.tsx | 5 +- web/app/components/plugins/types.ts | 1 + web/service/use-plugins.ts | 5 +- 10 files changed, 182 insertions(+), 16 deletions(-) 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,