From 0e70e7259461effb3427ca91a0121304059abbb5 Mon Sep 17 00:00:00 2001 From: StyleZhang Date: Thu, 5 Dec 2024 14:54:04 +0800 Subject: [PATCH] fix: marketplace list --- .../plugins/marketplace/context.tsx | 187 +++++++----------- .../components/plugins/marketplace/hooks.ts | 42 +++- .../components/plugins/marketplace/index.tsx | 12 +- .../marketplace/intersection-line/hooks.ts | 5 +- .../marketplace/intersection-line/index.tsx | 9 +- .../plugins/marketplace/list/list-wrapper.tsx | 13 +- web/service/use-plugins.ts | 3 +- 7 files changed, 128 insertions(+), 143 deletions(-) diff --git a/web/app/components/plugins/marketplace/context.tsx b/web/app/components/plugins/marketplace/context.tsx index 3236e1aefc..79618c6c9e 100644 --- a/web/app/components/plugins/marketplace/context.tsx +++ b/web/app/components/plugins/marketplace/context.tsx @@ -28,6 +28,7 @@ import type { import { DEFAULT_SORT } from './constants' import { useMarketplaceCollectionsAndPlugins, + useMarketplaceContainerScroll, useMarketplacePlugins, } from './hooks' import { @@ -51,7 +52,7 @@ export type MarketplaceContextValue = { resetPlugins: () => void sort: PluginsSort handleSortChange: (sort: PluginsSort) => void - handleQueryPluginsWhenNoCollection: () => void + handleQueryPlugins: () => void marketplaceCollectionsFromClient?: MarketplaceCollection[] setMarketplaceCollectionsFromClient: (collections: MarketplaceCollection[]) => void marketplaceCollectionPluginsMapFromClient?: Record @@ -75,7 +76,7 @@ export const MarketplaceContext = createContext({ resetPlugins: () => {}, sort: DEFAULT_SORT, handleSortChange: () => {}, - handleQueryPluginsWhenNoCollection: () => {}, + handleQueryPlugins: () => {}, marketplaceCollectionsFromClient: [], setMarketplaceCollectionsFromClient: () => {}, marketplaceCollectionPluginsMapFromClient: {}, @@ -88,6 +89,7 @@ type MarketplaceContextProviderProps = { children: ReactNode searchParams?: SearchParams shouldExclude?: boolean + scrollContainerId?: string } export function useMarketplaceContext(selector: (value: MarketplaceContextValue) => any) { @@ -98,6 +100,7 @@ export const MarketplaceContextProvider = ({ children, searchParams, shouldExclude, + scrollContainerId, }: MarketplaceContextProviderProps) => { const { data, isSuccess } = useInstalledPluginList(!shouldExclude) const exclude = useMemo(() => { @@ -131,6 +134,7 @@ export const MarketplaceContextProvider = ({ } = useMarketplaceCollectionsAndPlugins() const { plugins, + total: pluginsTotal, resetPlugins, queryPlugins, queryPluginsWithDebounced, @@ -161,34 +165,60 @@ export const MarketplaceContextProvider = ({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [queryPlugins, queryMarketplaceCollectionsAndPlugins, isSuccess, exclude]) - const handleSearchPluginTextChange = useCallback((text: string) => { - setSearchPluginText(text) - searchPluginTextRef.current = text - setPage(1) - pageRef.current = 1 + const handleQueryMarketplaceCollectionsAndPlugins = useCallback(() => { + queryMarketplaceCollectionsAndPlugins({ + category: activePluginTypeRef.current === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : activePluginTypeRef.current, + condition: getMarketplaceListCondition(activePluginTypeRef.current), + exclude, + type: getMarketplaceListFilterType(activePluginTypeRef.current), + }) + resetPlugins() + }, [exclude, queryMarketplaceCollectionsAndPlugins, resetPlugins]) - if (!searchPluginTextRef.current && !filterPluginTagsRef.current.length) { - queryMarketplaceCollectionsAndPlugins({ + const handleQueryPlugins = useCallback((debounced?: boolean) => { + if (debounced) { + queryPluginsWithDebounced({ + query: searchPluginTextRef.current, category: activePluginTypeRef.current === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : activePluginTypeRef.current, - condition: getMarketplaceListCondition(activePluginTypeRef.current), + tags: filterPluginTagsRef.current, + sortBy: sortRef.current.sortBy, + sortOrder: sortRef.current.sortOrder, exclude, type: getMarketplaceListFilterType(activePluginTypeRef.current), + page: pageRef.current, }) - resetPlugins() + } + else { + 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, queryPluginsWithDebounced, queryPlugins]) + const handleQuery = useCallback((debounced?: boolean) => { + if (!searchPluginTextRef.current && !filterPluginTagsRef.current.length) { + handleQueryMarketplaceCollectionsAndPlugins() return } - queryPluginsWithDebounced({ - query: text, - category: activePluginTypeRef.current === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : activePluginTypeRef.current, - tags: filterPluginTagsRef.current, - sortBy: sortRef.current.sortBy, - sortOrder: sortRef.current.sortOrder, - exclude, - page: pageRef.current, - }) - }, [queryPluginsWithDebounced, queryMarketplaceCollectionsAndPlugins, resetPlugins, exclude]) + handleQueryPlugins(debounced) + }, [handleQueryMarketplaceCollectionsAndPlugins, handleQueryPlugins]) + + const handleSearchPluginTextChange = useCallback((text: string) => { + setSearchPluginText(text) + searchPluginTextRef.current = text + setPage(1) + pageRef.current = 1 + + handleQuery(true) + }, [handleQuery]) const handleFilterPluginTagsChange = useCallback((tags: string[]) => { setFilterPluginTags(tags) @@ -196,29 +226,8 @@ export const MarketplaceContextProvider = ({ setPage(1) pageRef.current = 1 - 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, - category: activePluginTypeRef.current === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : activePluginTypeRef.current, - tags, - sortBy: sortRef.current.sortBy, - sortOrder: sortRef.current.sortOrder, - exclude, - type: getMarketplaceListFilterType(activePluginTypeRef.current), - page: pageRef.current, - }) - }, [queryPlugins, resetPlugins, queryMarketplaceCollectionsAndPlugins, exclude]) + handleQuery() + }, [handleQuery]) const handleActivePluginTypeChange = useCallback((type: string) => { setActivePluginType(type) @@ -226,57 +235,8 @@ export const MarketplaceContextProvider = ({ setPage(1) pageRef.current = 1 - if (!searchPluginTextRef.current && !filterPluginTagsRef.current.length) { - queryMarketplaceCollectionsAndPlugins({ - category: type === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : type, - condition: getMarketplaceListCondition(type), - exclude, - type: getMarketplaceListFilterType(activePluginTypeRef.current), - }) - resetPlugins() - - return - } - - queryPlugins({ - query: searchPluginTextRef.current, - category: type === PLUGIN_TYPE_SEARCH_MAP.all ? undefined : type, - tags: filterPluginTagsRef.current, - sortBy: sortRef.current.sortBy, - sortOrder: sortRef.current.sortOrder, - exclude, - type: getMarketplaceListFilterType(activePluginTypeRef.current), - page: pageRef.current, - }) - }, [queryPlugins, resetPlugins, queryMarketplaceCollectionsAndPlugins, exclude]) - - 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, - 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, queryMarketplaceCollectionsAndPlugins, resetPlugins]) + handleQuery() + }, [handleQuery]) const handleSortChange = useCallback((sort: PluginsSort) => { setSort(sort) @@ -284,32 +244,19 @@ export const MarketplaceContextProvider = ({ 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]) + handleQueryPlugins() + }, [handleQueryPlugins]) - 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]) + const handlePageChange = useCallback(() => { + if (pluginsTotal && plugins && pluginsTotal > plugins.length && (!!searchPluginTextRef.current || !!filterPluginTagsRef.current.length)) { + setPage(pageRef.current + 1) + pageRef.current++ + + handleQueryPlugins() + } + }, [handleQueryPlugins, plugins, pluginsTotal]) - // useMarketplaceContainerScroll(handlePageChange) + useMarketplaceContainerScroll(handlePageChange, scrollContainerId) return ( { export const useMarketplacePlugins = () => { const { data, - mutate, + mutateAsync, reset, isPending, } = useMutationPluginsFromMarketplace() + const [prevPlugins, setPrevPlugins] = useState() + const resetPlugins = useCallback(() => { + reset() + setPrevPlugins(undefined) + }, [reset]) + const handleUpdatePlugins = useCallback((pluginsSearchParams: PluginsSearchParams) => { + mutateAsync(pluginsSearchParams).then((res) => { + const currentPage = pluginsSearchParams.page || 1 + const resPlugins = res.data.plugins + if (currentPage > 1) { + setPrevPlugins(prevPlugins => [...(prevPlugins || []), ...resPlugins.map((plugin) => { + return getFormattedPlugin(plugin) + })]) + } + else { + setPrevPlugins(resPlugins.map((plugin) => { + return getFormattedPlugin(plugin) + })) + } + }) + }, [mutateAsync]) const queryPlugins = useCallback((pluginsSearchParams: PluginsSearchParams) => { - mutate(pluginsSearchParams) - }, [mutate]) + handleUpdatePlugins(pluginsSearchParams) + }, [handleUpdatePlugins]) const { run: queryPluginsWithDebounced } = useDebounceFn((pluginsSearchParams: PluginsSearchParams) => { - mutate(pluginsSearchParams) + handleUpdatePlugins(pluginsSearchParams) }, { wait: 500, }) return { - plugins: data?.data?.plugins.map((plugin) => { - return getFormattedPlugin(plugin) - }), + plugins: prevPlugins, total: data?.data?.total, - resetPlugins: reset, + resetPlugins, queryPlugins, queryPluginsWithDebounced, isLoading: isPending, @@ -97,8 +116,11 @@ export const useMixedTranslation = (localeFromOuter?: string) => { } } -export const useMarketplaceContainerScroll = (callback: () => void) => { - const container = document.getElementById('marketplace-container') +export const useMarketplaceContainerScroll = ( + callback: () => void, + scrollContainerId = 'marketplace-container', +) => { + const container = document.getElementById(scrollContainerId) const handleScroll = useCallback((e: Event) => { const target = e.target as HTMLDivElement diff --git a/web/app/components/plugins/marketplace/index.tsx b/web/app/components/plugins/marketplace/index.tsx index 8549206e06..4402866c96 100644 --- a/web/app/components/plugins/marketplace/index.tsx +++ b/web/app/components/plugins/marketplace/index.tsx @@ -14,6 +14,8 @@ type MarketplaceProps = { shouldExclude?: boolean searchParams?: SearchParams pluginTypeSwitchClassName?: string + intersectionContainerId?: string + scrollContainerId?: string } const Marketplace = async ({ locale, @@ -21,6 +23,8 @@ const Marketplace = async ({ shouldExclude, searchParams, pluginTypeSwitchClassName, + intersectionContainerId, + scrollContainerId, }: MarketplaceProps) => { let marketplaceCollections: any = [] let marketplaceCollectionPluginsMap = {} @@ -32,9 +36,13 @@ const Marketplace = async ({ return ( - + - + , + intersectionContainerId = 'marketplace-container', ) => { const intersected = useMarketplaceContext(v => v.intersected) const setIntersected = useMarketplaceContext(v => v.setIntersected) useEffect(() => { - const container = document.getElementById('marketplace-container') + const container = document.getElementById(intersectionContainerId) let observer: IntersectionObserver | undefined if (container && anchorRef.current) { observer = new IntersectionObserver((entries) => { @@ -25,5 +26,5 @@ export const useScrollIntersection = ( observer.observe(anchorRef.current) } return () => observer?.disconnect() - }, [anchorRef, intersected, setIntersected]) + }, [anchorRef, intersected, setIntersected, intersectionContainerId]) } diff --git a/web/app/components/plugins/marketplace/intersection-line/index.tsx b/web/app/components/plugins/marketplace/intersection-line/index.tsx index e521d43f8c..6f8e4b02ab 100644 --- a/web/app/components/plugins/marketplace/intersection-line/index.tsx +++ b/web/app/components/plugins/marketplace/intersection-line/index.tsx @@ -3,10 +3,15 @@ import { useRef } from 'react' import { useScrollIntersection } from './hooks' -const IntersectionLine = () => { +type IntersectionLineProps = { + intersectionContainerId?: string +} +const IntersectionLine = ({ + intersectionContainerId, +}: IntersectionLineProps) => { const ref = useRef(null) - useScrollIntersection(ref) + useScrollIntersection(ref, intersectionContainerId) return (
diff --git a/web/app/components/plugins/marketplace/list/list-wrapper.tsx b/web/app/components/plugins/marketplace/list/list-wrapper.tsx index 173b9c615d..8ae23c5258 100644 --- a/web/app/components/plugins/marketplace/list/list-wrapper.tsx +++ b/web/app/components/plugins/marketplace/list/list-wrapper.tsx @@ -26,18 +26,19 @@ const ListWrapper = ({ const marketplaceCollectionPluginsMapFromClient = useMarketplaceContext(v => v.marketplaceCollectionPluginsMapFromClient) const isLoading = useMarketplaceContext(v => v.isLoading) const isSuccessCollections = useMarketplaceContext(v => v.isSuccessCollections) - const handleQueryPluginsWhenNoCollection = useMarketplaceContext(v => v.handleQueryPluginsWhenNoCollection) + const handleQueryPlugins = useMarketplaceContext(v => v.handleQueryPlugins) + const page = useMarketplaceContext(v => v.page) useEffect(() => { if (!marketplaceCollectionsFromClient?.length && isSuccessCollections) - handleQueryPluginsWhenNoCollection() - }, [handleQueryPluginsWhenNoCollection, marketplaceCollections, marketplaceCollectionsFromClient, isSuccessCollections]) + handleQueryPlugins() + }, [handleQueryPlugins, marketplaceCollections, marketplaceCollectionsFromClient, isSuccessCollections]) return (
{ plugins && ( -
+
{t('plugin.marketplace.pluginsResult', { num: plugins.length })}
@@ -45,14 +46,14 @@ const ListWrapper = ({ ) } { - isLoading && ( + isLoading && page === 1 && (
) } { - !isLoading && ( + (!isLoading || page > 1) && ( { exclude, type, page = 1, + pageSize = 20, } = pluginsSearchParams return postMarketplace<{ data: PluginsFromMarketplaceResponse }>('/plugins/search/basic', { body: { page, - page_size: 100, + page_size: pageSize, query, sort_by: sortBy, sort_order: sortOrder,