diff --git a/projects/packages/my-jetpack/_inc/components/product-cards-section/index.tsx b/projects/packages/my-jetpack/_inc/components/product-cards-section/index.tsx index be198ae05a8da..2d6427a2258af 100644 --- a/projects/packages/my-jetpack/_inc/components/product-cards-section/index.tsx +++ b/projects/packages/my-jetpack/_inc/components/product-cards-section/index.tsx @@ -2,6 +2,7 @@ import { Container, Col, Text, AdminSectionHero } from '@automattic/jetpack-comp import { __ } from '@wordpress/i18n'; import { useMemo } from 'react'; import { PRODUCT_SLUGS } from '../../data/constants'; +import useProductsByOwnership from '../../data/products/use-products-by-ownership'; import { getMyJetpackWindowInitialState } from '../../data/utils/get-my-jetpack-window-state'; import StatsSection from '../stats-section'; import AiCard from './ai-card'; @@ -93,8 +94,9 @@ interface ProductCardsSectionProps { } const ProductCardsSection: FC< ProductCardsSectionProps > = ( { noticeMessage } ) => { - const { ownedProducts = [], unownedProducts = [] } = - getMyJetpackWindowInitialState( 'lifecycleStats' ); + const { + data: { ownedProducts, unownedProducts }, + } = useProductsByOwnership(); const unownedSectionTitle = useMemo( () => { return ownedProducts.length > 0 diff --git a/projects/packages/my-jetpack/_inc/components/welcome-flow/ConnectionStep.tsx b/projects/packages/my-jetpack/_inc/components/welcome-flow/ConnectionStep.tsx index ae45311131c18..3d963ff52a61d 100644 --- a/projects/packages/my-jetpack/_inc/components/welcome-flow/ConnectionStep.tsx +++ b/projects/packages/my-jetpack/_inc/components/welcome-flow/ConnectionStep.tsx @@ -4,6 +4,7 @@ import { __ } from '@wordpress/i18n'; import { useCallback, useContext } from 'react'; import { NoticeContext } from '../../context/notices/noticeContext'; import { NOTICE_SITE_CONNECTED } from '../../context/notices/noticeTemplates'; +import useProductsByOwnership from '../../data/products/use-products-by-ownership'; import useAnalytics from '../../hooks/use-analytics'; import sideloadTracks from '../../utils/side-load-tracks'; import styles from './style.module.scss'; @@ -34,6 +35,7 @@ const ConnectionStep = ( { const { setNotice, resetNotice } = useContext( NoticeContext ); const activationButtonLabel = __( 'Activate Jetpack in one click', 'jetpack-my-jetpack' ); + const { refetch: refetchOwnershipData } = useProductsByOwnership(); const onConnectSiteClick = useCallback( async () => { recordEvent( 'jetpack_myjetpack_welcome_banner_connect_site_click' ); @@ -58,10 +60,18 @@ const ConnectionStep = ( { } finally { resetNotice(); setNotice( NOTICE_SITE_CONNECTED, resetNotice ); + refetchOwnershipData(); onUpdateWelcomeFlowExperiment( state => ( { ...state, isLoading: false } ) ); } - }, [ onActivateSite, onUpdateWelcomeFlowExperiment, recordEvent, resetNotice, setNotice ] ); + }, [ + onActivateSite, + onUpdateWelcomeFlowExperiment, + recordEvent, + refetchOwnershipData, + resetNotice, + setNotice, + ] ); return ( <> diff --git a/projects/packages/my-jetpack/_inc/context/value-store/valueStoreContext.tsx b/projects/packages/my-jetpack/_inc/context/value-store/valueStoreContext.tsx index d8902c9f2b7dc..0774e0aadeca6 100644 --- a/projects/packages/my-jetpack/_inc/context/value-store/valueStoreContext.tsx +++ b/projects/packages/my-jetpack/_inc/context/value-store/valueStoreContext.tsx @@ -6,6 +6,10 @@ type ValueStoreType = { isLoadingWelcomeFlowExperiment?: boolean; recommendedModules: JetpackModule[] | null; recommendedModulesVisible: boolean; + productsOwnership: { + ownedProducts: JetpackModule[]; + unownedProducts: JetpackModule[]; + }; }; type ValueStoreContextType = { diff --git a/projects/packages/my-jetpack/_inc/data/constants.ts b/projects/packages/my-jetpack/_inc/data/constants.ts index 2f09c4c96707f..c11217819409d 100644 --- a/projects/packages/my-jetpack/_inc/data/constants.ts +++ b/projects/packages/my-jetpack/_inc/data/constants.ts @@ -8,6 +8,7 @@ export const REST_API_COUNT_BACKUP_ITEMS_ENDPOINT = `${ REST_API_NAMESPACE }/sit export const REST_API_CHAT_AVAILABILITY_ENDPOINT = `${ REST_API_NAMESPACE }/chat/availability`; export const REST_API_CHAT_AUTHENTICATION_ENDPOINT = `${ REST_API_NAMESPACE }/chat/authentication`; export const REST_API_SITE_PRODUCTS_ENDPOINT = `${ REST_API_NAMESPACE }/site/products`; +export const REST_API_SITE_PRODUCTS_OWNERSHIP_ENDPOINT = `${ REST_API_NAMESPACE }/site/products-ownership`; export const REST_API_VIDEOPRESS_FEATURED_STATS = 'videopress/v1/stats/featured'; export const REST_API_SITE_DISMISS_BANNER = `${ REST_API_NAMESPACE }/site/dismiss-welcome-banner`; export const REST_API_EVALUATE_SITE_RECOMMENDATIONS = `${ REST_API_NAMESPACE }/site/recommendations/evaluation`; @@ -18,6 +19,7 @@ export const getStatsHighlightsEndpoint = ( blogId: string ) => // Query names export const QUERY_PRODUCT_KEY = 'product'; +export const QUERY_PRODUCT_BY_OWNERSHIP_KEY = 'product ownership'; export const QUERY_ACTIVATE_PRODUCT_KEY = 'activate product'; export const QUERY_INSTALL_PRODUCT_KEY = 'install product'; export const QUERY_VIDEOPRESS_STATS_KEY = 'videopress stats'; diff --git a/projects/packages/my-jetpack/_inc/data/products/use-products-by-ownership.ts b/projects/packages/my-jetpack/_inc/data/products/use-products-by-ownership.ts new file mode 100644 index 0000000000000..804cf56ba285e --- /dev/null +++ b/projects/packages/my-jetpack/_inc/data/products/use-products-by-ownership.ts @@ -0,0 +1,44 @@ +import { useEffect } from 'react'; +import { useValueStore } from '../../context/value-store/valueStoreContext'; +import { getMyJetpackWindowInitialState } from '../../data/utils/get-my-jetpack-window-state'; +import { QUERY_PRODUCT_BY_OWNERSHIP_KEY } from '../constants'; +import { REST_API_SITE_PRODUCTS_OWNERSHIP_ENDPOINT } from '../constants'; +import useSimpleQuery from '../use-simple-query'; + +// Create query to fetch new product data from the server +const useFetchProductsByOwnership = () => { + const queryResult = useSimpleQuery< + Record< 'ownedProducts' | 'unownedProducts', JetpackModule[] > + >( { + name: `${ QUERY_PRODUCT_BY_OWNERSHIP_KEY }`, + query: { + path: REST_API_SITE_PRODUCTS_OWNERSHIP_ENDPOINT, + }, + } ); + + return queryResult; +}; + +const useProductsByOwnership = () => { + const [ productsOwnership, setProductsOwnership ] = useValueStore( 'productsOwnership', { + ownedProducts: getMyJetpackWindowInitialState( 'lifecycleStats' ).ownedProducts, + unownedProducts: getMyJetpackWindowInitialState( 'lifecycleStats' ).unownedProducts, + } ); + + const { data, refetch, isLoading } = useFetchProductsByOwnership(); + + useEffect( () => { + if ( ! isLoading && data ) { + const { ownedProducts = [], unownedProducts = [] } = data; + setProductsOwnership( { ownedProducts, unownedProducts } ); + } + }, [ data, isLoading, setProductsOwnership ] ); + + return { + refetch, + data: productsOwnership, + isLoading, + }; +}; + +export default useProductsByOwnership; diff --git a/projects/packages/my-jetpack/_inc/hooks/use-notification-watcher/use-site-connection-notice.tsx b/projects/packages/my-jetpack/_inc/hooks/use-notification-watcher/use-site-connection-notice.tsx index f38df9d053b02..7234d51aa341b 100644 --- a/projects/packages/my-jetpack/_inc/hooks/use-notification-watcher/use-site-connection-notice.tsx +++ b/projects/packages/my-jetpack/_inc/hooks/use-notification-watcher/use-site-connection-notice.tsx @@ -6,6 +6,7 @@ import { NOTICE_PRIORITY_HIGH } from '../../context/constants'; import { NoticeContext } from '../../context/notices/noticeContext'; import { NOTICE_SITE_CONNECTED } from '../../context/notices/noticeTemplates'; import { useAllProducts } from '../../data/products/use-product'; +import useProductsByOwnership from '../../data/products/use-products-by-ownership'; import getProductSlugsThatRequireUserConnection from '../../data/utils/get-product-slugs-that-require-user-connection'; import useAnalytics from '../use-analytics'; import useMyJetpackConnection from '../use-my-jetpack-connection'; @@ -25,6 +26,8 @@ const useSiteConnectionNotice = ( redBubbleAlerts: RedBubbleAlerts ) => { const redBubbleSlug = 'missing-connection'; const connectionError = redBubbleAlerts[ redBubbleSlug ]; + const { refetch: refetchOwnershipData } = useProductsByOwnership(); + useEffect( () => { if ( ! connectionError ) { return; @@ -45,6 +48,8 @@ const useSiteConnectionNotice = ( redBubbleAlerts: RedBubbleAlerts ) => { setNotice( NOTICE_SITE_CONNECTED, resetNotice ); delete redBubbleAlerts[ redBubbleSlug ]; window.myJetpackInitialState.redBubbleAlerts = redBubbleAlerts; + + refetchOwnershipData(); } ); }; @@ -125,6 +130,7 @@ const useSiteConnectionNotice = ( redBubbleAlerts: RedBubbleAlerts ) => { setNotice, siteIsRegistering, connectionError, + refetchOwnershipData, ] ); }; diff --git a/projects/packages/my-jetpack/changelog/myjetpack-connected-products b/projects/packages/my-jetpack/changelog/myjetpack-connected-products new file mode 100644 index 0000000000000..94309db11c9d7 --- /dev/null +++ b/projects/packages/my-jetpack/changelog/myjetpack-connected-products @@ -0,0 +1,5 @@ +Significance: patch +Type: fixed +Comment: The fix is updating product list after connecting Jetpack, without reloading the page. + + diff --git a/projects/packages/my-jetpack/src/class-rest-products.php b/projects/packages/my-jetpack/src/class-rest-products.php index cb253ce1625f6..dfa60108b7c56 100644 --- a/projects/packages/my-jetpack/src/class-rest-products.php +++ b/projects/packages/my-jetpack/src/class-rest-products.php @@ -77,9 +77,18 @@ public function __construct() { 'methods' => \WP_REST_Server::EDITABLE, 'callback' => __CLASS__ . '::install_standalone', 'permission_callback' => __CLASS__ . '::edit_permissions_callback', - 'args' => array( - 'product' => $product_arg, - ), + ), + ) + ); + + register_rest_route( + 'my-jetpack/v1', + 'site/products-ownership', + array( + array( + 'methods' => \WP_REST_Server::READABLE, + 'callback' => __CLASS__ . '::get_products_by_ownership', + 'permission_callback' => __CLASS__ . '::permissions_callback', ), ) ); @@ -142,6 +151,19 @@ public static function get_products() { return rest_ensure_response( $response, 200 ); } + /** + * Site products endpoint. + * + * @return array of site products list. + */ + public static function get_products_by_ownership() { + $response = array( + 'unownedProducts' => Products::get_products_by_ownership( 'unowned' ), + 'ownedProducts' => Products::get_products_by_ownership( 'owned' ), + ); + return rest_ensure_response( $response, 200 ); + } + /** * Site single product endpoint. *