From 44c29dd2f37e6226c03f435417ad1da8c3bc307c Mon Sep 17 00:00:00 2001 From: Boro Sitnikovski Date: Wed, 11 Sep 2024 17:14:11 +0200 Subject: [PATCH] When a search is initiated, fetch all categories to keep the tab counts up to date. The necessary filtering to display data to the current screen will be performed on the frontend. --- .../components/content/content.tsx | 214 +++++++++--------- 1 file changed, 107 insertions(+), 107 deletions(-) diff --git a/plugins/woocommerce-admin/client/marketplace/components/content/content.tsx b/plugins/woocommerce-admin/client/marketplace/components/content/content.tsx index dd700fc44a9..8f41e1d6667 100644 --- a/plugins/woocommerce-admin/client/marketplace/components/content/content.tsx +++ b/plugins/woocommerce-admin/client/marketplace/components/content/content.tsx @@ -8,7 +8,7 @@ import { useQuery } from '@woocommerce/navigation'; * Internal dependencies */ import './content.scss'; -import { Product, ProductType, SearchResultType } from '../product-list/types'; +import { Product, ProductType } from '../product-list/types'; import { getAdminSetting } from '~/utils/admin-settings'; import Discover from '../discover/discover'; import Products from '../products/products'; @@ -30,7 +30,10 @@ import SubscriptionsExpiredExpiringNotice from '~/marketplace/components/my-subs export default function Content(): JSX.Element { const marketplaceContextValue = useContext( MarketplaceContext ); - const [ products, setProducts ] = useState< Product[] >( [] ); + const [ allProducts, setAllProducts ] = useState< Product[] >( [] ); + const [ filteredProducts, setFilteredProducts ] = useState< Product[] >( + [] + ); const { setIsLoading, selectedTab, @@ -39,21 +42,40 @@ export default function Content(): JSX.Element { } = marketplaceContextValue; const query = useQuery(); - // On initial load of the in-app marketplace, fetch extensions, themes and business services - // and check if there are any business services available on WCCOM + // Function to tag products with their type + const tagProductsWithType = ( + products: Product[], + type: ProductType + ): Product[] => { + return products.map( ( product ) => ( { + ...product, + type, // Adding the product type to each product + } ) ); + }; + + // Fetch all categories when query.term or query.category changes useEffect( () => { - const categories: Array< keyof SearchResultsCountType > = [ - 'extensions', - 'themes', - 'business-services', + const categories: Array< { + key: keyof SearchResultsCountType; + type: ProductType; + } > = [ + { key: 'extensions', type: ProductType.extension }, + { key: 'themes', type: ProductType.theme }, + { key: 'business-services', type: ProductType.businessService }, ]; const abortControllers = categories.map( () => new AbortController() ); - categories.forEach( - ( category: keyof SearchResultsCountType, index ) => { + setIsLoading( true ); + setAllProducts( [] ); + + Promise.all( + categories.map( ( { key, type }, index ) => { const params = new URLSearchParams(); - if ( category !== 'extensions' ) { - params.append( 'category', category ); + if ( key !== 'extensions' ) { + params.append( 'category', key ); + } + if ( query.term ) { + params.append( 'term', query.term ); } const wccomSettings = getAdminSetting( 'wccomHelper', false ); @@ -61,114 +83,96 @@ export default function Content(): JSX.Element { params.append( 'country', wccomSettings.storeCountry ); } - fetchSearchResults( + return fetchSearchResults( params, abortControllers[ index ].signal ).then( ( productList ) => { - if ( category === 'business-services' ) { - setHasBusinessServices( productList.length > 0 ); + // Tag the products with their type (extension, theme, or business-service) + const typedProducts = tagProductsWithType( + productList, + type + ); + if ( key === 'business-services' ) { + setHasBusinessServices( typedProducts.length > 0 ); } + return typedProducts; } ); - return () => { - abortControllers.forEach( ( controller ) => { - controller.abort(); - } ); - }; - } - ); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [] ); + } ) + ) + .then( ( results ) => { + const combinedProducts = results.flat(); + setAllProducts( combinedProducts ); - // Get the content for this screen - useEffect( () => { - const abortController = new AbortController(); - - if ( - query.tab === undefined || - ( query.tab && [ '', 'discover' ].includes( query.tab ) ) - ) { - return; - } - - setIsLoading( true ); - setProducts( [] ); - - const params = new URLSearchParams(); - - if ( query.term ) { - params.append( 'term', query.term ); - } - - if ( query.category ) { - params.append( - 'category', - query.category === '_all' ? '' : query.category - ); - } else if ( query?.tab === 'themes' ) { - params.append( 'category', 'themes' ); - } else if ( query?.tab === 'business-services' ) { - params.append( 'category', 'business-services' ); - } - - const wccomSettings = getAdminSetting( 'wccomHelper', false ); - if ( wccomSettings.storeCountry ) { - params.append( 'country', wccomSettings.storeCountry ); - } - - fetchSearchResults( params, abortController.signal ) - .then( ( productList ) => { - setProducts( productList ); - - if ( query.term ) { - setSearchResultsCount( { - extensions: productList.filter( - ( p ) => p.type === 'extension' - ).length, - themes: productList.filter( - ( p ) => p.type === 'theme' - ).length, - 'business-services': productList.filter( - ( p ) => p.type === 'business-service' - ).length, - } ); - } + // Set the search results count based on product types + setSearchResultsCount( { + extensions: combinedProducts.filter( + ( p ) => p.type === ProductType.extension + ).length, + themes: combinedProducts.filter( + ( p ) => p.type === ProductType.theme + ).length, + 'business-services': combinedProducts.filter( + ( p ) => p.type === ProductType.businessService + ).length, + } ); } ) .catch( () => { - setProducts( [] ); + setAllProducts( [] ); } ) .finally( () => { - // we are recording both the new and legacy events here for now - // they're separate methods to make it easier to remove the legacy one later - const marketplaceViewProps = { - view: query?.tab, - search_term: query?.term, - product_type: query?.section, - category: query?.category, - }; - - recordMarketplaceView( marketplaceViewProps ); - recordLegacyTabView( marketplaceViewProps ); setIsLoading( false ); } ); + return () => { - abortController.abort(); + abortControllers.forEach( ( controller ) => { + controller.abort(); + } ); }; - }, [ - query.term, - query.category, - query?.tab, - setIsLoading, - query?.section, - ] ); + }, [ query.term, query.category ] ); // Depend on term and category + + // Filter the products based on the selected tab + useEffect( () => { + let filtered: Product[] | null; + switch ( selectedTab ) { + case 'extensions': + filtered = allProducts.filter( + ( p ) => p.type === ProductType.extension + ); + break; + case 'themes': + filtered = allProducts.filter( + ( p ) => p.type === ProductType.theme + ); + break; + case 'business-services': + filtered = allProducts.filter( + ( p ) => p.type === ProductType.businessService + ); + break; + default: + filtered = []; + } + setFilteredProducts( filtered ); + }, [ selectedTab, allProducts ] ); + + // Record tab view events when the query changes + useEffect( () => { + const marketplaceViewProps = { + view: query?.tab, + search_term: query?.term, + product_type: query?.section, + category: query?.category, + }; + recordMarketplaceView( marketplaceViewProps ); + recordLegacyTabView( marketplaceViewProps ); + }, [ query?.tab, query?.term, query?.section, query?.category ] ); const renderContent = (): JSX.Element => { switch ( selectedTab ) { case 'extensions': return ( p.type === 'extension' - ) } + products={ filteredProducts } categorySelector={ true } type={ ProductType.extension } /> @@ -176,9 +180,7 @@ export default function Content(): JSX.Element { case 'themes': return ( p.type === 'theme' - ) } + products={ filteredProducts } categorySelector={ true } type={ ProductType.theme } /> @@ -186,9 +188,7 @@ export default function Content(): JSX.Element { case 'business-services': return ( p.type === 'business-service' - ) } + products={ filteredProducts } categorySelector={ true } type={ ProductType.businessService } /> @@ -209,7 +209,7 @@ export default function Content(): JSX.Element { return (
- + { selectedTab !== 'business-services' && selectedTab !== 'my-subscriptions' && } { selectedTab !== 'business-services' && }