Skip to content

Commit ff26925

Browse files
authored
Merge pull request #1706 from woocommerce/update/1610-integrate-syncable-product-api
Free Listings + Paid Ads: Fetch the number of syncable products for the product feed status section
2 parents b6e27ea + 8511198 commit ff26925

File tree

4 files changed

+114
-33
lines changed

4 files changed

+114
-33
lines changed

js/src/get-started-page/index.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import { useEffect } from '@wordpress/element';
5+
16
/**
27
* Internal dependencies
38
*/
49
import './index.scss';
10+
import useSyncableProductsCalculation from '.~/hooks/useSyncableProductsCalculation';
511
import BenefitsCard from './benefits-card';
612
import CustomerQuotesCard from './customer-quotes-card';
713
import Faqs from './faqs';
@@ -11,6 +17,13 @@ import GetStartedWithVideoCard from './get-started-with-video-card';
1117
import UnsupportedNotices from './unsupported-notices';
1218

1319
const GetStartedPage = () => {
20+
const { request } = useSyncableProductsCalculation();
21+
22+
// Trigger the calculation here for later use during the onboarding flow.
23+
useEffect( () => {
24+
request();
25+
}, [ request ] );
26+
1427
return (
1528
<div className="woocommerce-marketing-google-get-started-page">
1629
<UnsupportedNotices />
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/**
2+
* External dependencies
3+
*/
4+
import { useEffect, useCallback } from '@wordpress/element';
5+
6+
/**
7+
* Internal dependencies
8+
*/
9+
import { API_NAMESPACE } from '.~/data/constants';
10+
import useApiFetchCallback from './useApiFetchCallback';
11+
import useCountdown from './useCountdown';
12+
13+
/**
14+
* @typedef {Object} SyncableProductsCalculation
15+
* @property {number|null} count The number of syncable products. `null` if it's still in the calculation.
16+
* @property {()=>Promise} request The function requesting the start of a calculation.
17+
* @property {()=>Promise} retrieve The function retrieving the result of a requested calculation by polling with 5-second timer.
18+
*/
19+
20+
const getOptions = {
21+
path: `${ API_NAMESPACE }/mc/syncable-products-count`,
22+
};
23+
const postOptions = {
24+
...getOptions,
25+
method: 'POST',
26+
};
27+
28+
/**
29+
* A hook for communicating with the calculation of the syncable products and for returning the result.
30+
*
31+
* If a shop has a large number of products, requesting the result with a single API may encounter
32+
* a timeout or out-of-memory problem. Therefore, we use an API to schedule a batch for calculating,
33+
* and another one to poll the result.
34+
*
35+
* @return {SyncableProductsCalculation} Payload containing the result of calculation and the functions for requesting and retrieving a calculation.
36+
*/
37+
export default function useSyncableProductsCalculation() {
38+
const { second, callCount, startCountdown } = useCountdown();
39+
const [ fetch, { data } ] = useApiFetchCallback( getOptions );
40+
const [ request ] = useApiFetchCallback( postOptions );
41+
42+
// The number 0 is a valid value.
43+
const count = data?.count ?? null;
44+
45+
const retrieve = useCallback( () => {
46+
const promise = fetch();
47+
promise.finally( () => startCountdown( 5 ) );
48+
return promise;
49+
}, [ fetch, startCountdown ] );
50+
51+
useEffect( () => {
52+
if ( second === 0 && callCount > 0 && count === null ) {
53+
retrieve();
54+
}
55+
}, [ second, callCount, count, retrieve ] );
56+
57+
return {
58+
request,
59+
retrieve,
60+
count,
61+
};
62+
}

js/src/setup-mc/setup-stepper/setup-paid-ads/product-feed-status-section.js

Lines changed: 30 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,21 @@
22
* External dependencies
33
*/
44
import { sprintf, __, _n } from '@wordpress/i18n';
5+
import { useEffect } from '@wordpress/element';
56
import { Flex, FlexItem, FlexBlock } from '@wordpress/components';
7+
import { Spinner } from '@woocommerce/components';
68

79
/**
810
* Internal dependencies
911
*/
1012
import Section from '.~/wcdl/section';
1113
import AppDocumentationLink from '.~/components/app-documentation-link';
12-
import SyncIcon from '.~/components/sync-icon';
14+
import SuccessIcon from '.~/components/success-icon';
1315
import AppTooltip from '.~/components/app-tooltip';
14-
import getNumberOfSyncProducts from '.~/utils/getNumberOfSyncProducts';
16+
import useSyncableProductsCalculation from '.~/hooks/useSyncableProductsCalculation';
1517
import './product-feed-status-section.scss';
1618

1719
function ProductQuantity( { quantity } ) {
18-
if ( ! Number.isInteger( quantity ) ) {
19-
return null;
20-
}
21-
2220
const text = sprintf(
2321
// translators: %d: number of products will be synced to Google Merchant Center.
2422
_n( '%d product', '%d products', quantity, 'google-listings-and-ads' ),
@@ -51,25 +49,14 @@ function ProductQuantity( { quantity } ) {
5149
* and show the number of products will be synced to Google Merchant Center.
5250
*/
5351
export default function ProductFeedStatusSection() {
54-
/*
55-
const { data, hasFinishedResolution } = useAppSelectDispatch(
56-
'getMCProductStatistics'
57-
);
58-
*/
59-
// TODO: Replace the dummy data with the above code later to use the adjusted API.
60-
const data = {
61-
statistics: {
62-
active: 1,
63-
expiring: 2,
64-
pending: 3,
65-
disapproved: 4,
66-
not_synced: 5,
67-
},
68-
};
69-
const hasFinishedResolution = true;
70-
const productQuantity = hasFinishedResolution
71-
? getNumberOfSyncProducts( data.statistics )
72-
: null;
52+
const { retrieve, count } = useSyncableProductsCalculation();
53+
54+
// Retrieve the result of calculation that was requested when entering the Get Started page.
55+
useEffect( () => {
56+
retrieve();
57+
}, [ retrieve ] );
58+
59+
const isReady = Number.isInteger( count );
7360

7461
return (
7562
<Section
@@ -89,15 +76,28 @@ export default function ProductFeedStatusSection() {
8976
<Section.Card.Body>
9077
<Flex align="flex-start" gap={ 3 }>
9178
<FlexItem>
92-
<SyncIcon />
79+
{ isReady ? (
80+
<SuccessIcon size={ 20 } />
81+
) : (
82+
<Spinner />
83+
) }
9384
</FlexItem>
9485
<FlexBlock>
9586
<Section.Card.Title>
96-
{ __(
97-
'Your product listings are being uploaded',
98-
'google-listings-and-ads'
87+
{ isReady ? (
88+
<>
89+
{ __(
90+
'Your product listings are ready to be uploaded',
91+
'google-listings-and-ads'
92+
) }
93+
<ProductQuantity quantity={ count } />
94+
</>
95+
) : (
96+
__(
97+
'Preparing your product listings',
98+
'google-listings-and-ads'
99+
)
99100
) }
100-
<ProductQuantity quantity={ productQuantity } />
101101
</Section.Card.Title>
102102
{ __(
103103
'Google will review your product listings within 3-5 days. Once approved, your products will automatically be live and searchable on Google. You’ll be notified if there are any product feed issues.',

js/src/setup-mc/setup-stepper/setup-paid-ads/product-feed-status-section.scss

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
.gla-product-feed-status-section {
2-
.gla-sync-icon {
3-
fill: $gla-color-green;
4-
transform: rotateZ(90deg);
2+
.woocommerce-spinner {
3+
width: 20px;
4+
height: 20px;
5+
min-width: 20px;
6+
7+
&__circle {
8+
stroke: $gray-700;
9+
stroke-width: 8px;
10+
}
511
}
612

713
.wcdl-subsection-title {

0 commit comments

Comments
 (0)