Repository: Automattic/jetpack
Pull request #42187: Social: Load share data async
Author: login: gmjuhasz
Labels: name: Enhancement
description: Changes to an existing feature — removing, adding, or changing parts of it
name: [Feature] Publicize
description: Now Jetpack Social, auto-sharing
name: RNA
name: [Package] Publicize
name: [JS Package] Publicize Components
name: Coverage tests to be added later
description: Use to ignore the Code coverage requirement check when tests will be added in a follow-up PR
Created: 2025-03-03T17:33:43Z | Merged: 2025-03-18T13:38:22Z

<!--- Provide a general summary of your changes in the Title above -->
<!-- Would you like this feature to be tested by Beta testers?
Please add testing instructions to projects/plugins/jetpack/to-test.md in a new commit as part of your PR. -->
<!-- a12s: If you have an expected version that you're aiming for the PR to add, please use the Milestone field to communicate it. If you leave it blank, that indicates there isn't a preference. -->

Fixes https://github.com/Automattic/jetpack-reach/issues/780

## Proposed changes:

We want to make sure that the shares data is not stopping the page from loading, and make the request async after the page finished loading. With this solution we show the previous values on page load, then fire an API call async, and update the values if needed.

As discussed on the parent issue, the request is not cached and is made on every page load as before

It's easier to read following the commits:
<!--- Explain what functional changes your PR includes -->
* Added a new controller for the endpoint and registered it -- bddbc9813991b5ab7752d153dedb8559d4b5b8ec
* Updated the base class to just return the transient, and not make any calls -- e0bd13a8da8661404ce8f59ceb09513b13929324
* Refactored the utils functions to be part of a hook, which handles the refresh once on page load -- 66ed5caa988a4f04dc37f387d5faa30c0fccd5da

### Other information:

- [ ] Have you written new tests for your changes, if applicable?
- [ ] Have you checked the E2E test CI results, and verified that your changes do not break them?
- [ ] Have you tested your changes on WordPress.com, if applicable (if so, you'll see a generated comment below with a script to run)?

## Does this pull request change what data or activity we track or use?
<!--- If so, please add the "[Status] Needs Privacy Updates" label and explain what changes there are. -->
<!--- Check existing Jetpack support documents for a preview of the information we need. -->
No.
## Testing instructions:
<!-- If you were reviewing this PR, how would you like the instructions to be presented? -->
<!-- Please include detailed testing steps, explaining how to test your change. -->
<!-- Bear in mind that context you working on is not obvious for everyone.  -->
<!-- Adding "simple" configuration steps will help reviewers to get to your PR as quickly as possible. -->
<!-- "Before / After" screenshots can also be very helpful when the change is visual. -->

* Sandbox the this to your sandbox and the public-api
* Go to the social admin page. The cards should not appear on first page load, but appear after a second when the call finishes.
* Make sure that the api call is fired once per page load in the network tab to `wpcom/v2/jetpack-social`
* Make some shares / posts and wait a minute, then refresh the admin page, the values should be updated when the call is finished.
* You can run `jetpack docker wp transient delete jetpack_publicize_shares_info` to delete the transient and try again
* Check that for Simple and Atomic sites the call is not made. You also should not see the cards because the sharinglimit is off.




Diff:
diff --git a/projects/js-packages/publicize-components/changelog/update-social-load-share-data-async b/projects/js-packages/publicize-components/changelog/update-social-load-share-data-async
new file mode 100644
index 000000000000..e1aacc9e601b
--- /dev/null
+++ b/projects/js-packages/publicize-components/changelog/update-social-load-share-data-async
@@ -0,0 +1,4 @@
+Significance: minor
+Type: changed
+
+Moved the jetpack-social endpoint ot the publicize package
diff --git a/projects/js-packages/publicize-components/index.ts b/projects/js-packages/publicize-components/index.ts
index 24e30962d992..64ca5e8e31ae 100644
--- a/projects/js-packages/publicize-components/index.ts
+++ b/projects/js-packages/publicize-components/index.ts
@@ -34,5 +34,4 @@ export * from './src/hooks/use-post-can-use-sig';
 export * from './src/components/share-buttons';
 export * from './src/components/manage-connections-modal';
 export * from './src/utils/script-data';
-export * from './src/utils/shares-data';
 export * from './src/components/global-modals';
diff --git a/projects/js-packages/publicize-components/src/components/admin-page/header/index.js b/projects/js-packages/publicize-components/src/components/admin-page/header/index.js
index fa0a1979942f..ec28b799b339 100644
--- a/projects/js-packages/publicize-components/src/components/admin-page/header/index.js
+++ b/projects/js-packages/publicize-components/src/components/admin-page/header/index.js
@@ -13,20 +13,24 @@ import { __ } from '@wordpress/i18n';
 import { Icon, postList } from '@wordpress/icons';
 import { store as socialStore } from '../../../social-store';
 import { getSocialScriptData } from '../../../utils';
-import {
-	getSharedPostsCount,
-	getTotalSharesCount,
-	isShareLimitEnabled,
-} from '../../../utils/shares-data';
 import StatCards from './stat-cards';
 import styles from './styles.module.scss';
 
 const Header = () => {
-	const { hasConnections, isModuleEnabled } = useSelect( select => {
+	const {
+		hasConnections,
+		isModuleEnabled,
+		totalSharesCount,
+		sharedPostsCount,
+		isShareLimitEnabled,
+	} = useSelect( select => {
 		const store = select( socialStore );
 		return {
 			hasConnections: store.getConnections().length > 0,
 			isModuleEnabled: store.getSocialModuleSettings().publicize,
+			totalSharesCount: store.getTotalSharesCount(),
+			sharedPostsCount: store.getSharedPostsCount(),
+			isShareLimitEnabled: store.isShareLimitEnabled(),
 		};
 	} );
 
@@ -80,19 +84,19 @@ const Header = () => {
 						</Button>
 					</div>
 				</Col>
-				{ isShareLimitEnabled() ? (
+				{ isShareLimitEnabled ? (
 					<Col sm={ 4 } md={ 4 } lg={ { start: 7, end: 12 } }>
 						<StatCards
 							stats={ [
 								{
 									icon: <SocialIcon />,
 									label: __( 'Total shares past 30 days', 'jetpack-publicize-components' ),
-									value: formatter.format( getTotalSharesCount() ),
+									value: formatter.format( totalSharesCount ),
 								},
 								{
 									icon: <Icon icon={ postList } />,
 									label: __( 'Posted this month', 'jetpack-publicize-components' ),
-									value: formatter.format( getSharedPostsCount() ),
+									value: formatter.format( sharedPostsCount ),
 								},
 							] }
 						/>
diff --git a/projects/js-packages/publicize-components/src/social-store/hydrate-stores.ts b/projects/js-packages/publicize-components/src/social-store/hydrate-stores.ts
index 5fd2567465f2..e78ed2203750 100644
--- a/projects/js-packages/publicize-components/src/social-store/hydrate-stores.ts
+++ b/projects/js-packages/publicize-components/src/social-store/hydrate-stores.ts
@@ -61,6 +61,17 @@ export async function hydrateStores() {
 		await finishResolution( 'getEntityRecords', [ 'wpcom/v2', 'publicize/services' ] );
 	}
 
+	if ( ! wpcomEntities.some( ( { name } ) => name === 'publicize/shares-data' ) ) {
+		await addEntities( [
+			{
+				kind: 'wpcom/v2',
+				name: 'publicize/shares-data',
+				baseURL: '/wpcom/v2/publicize/shares-data',
+				label: __( 'Publicize shares data', 'jetpack-publicize-components' ),
+			},
+		] );
+	}
+
 	if ( ! wpcomEntities.some( ( { name } ) => name === 'publicize/scheduled-actions' ) ) {
 		await addEntities( [
 			{
diff --git a/projects/js-packages/publicize-components/src/social-store/selectors/index.ts b/projects/js-packages/publicize-components/src/social-store/selectors/index.ts
index 3f256c502b70..ef2858c80775 100644
--- a/projects/js-packages/publicize-components/src/social-store/selectors/index.ts
+++ b/projects/js-packages/publicize-components/src/social-store/selectors/index.ts
@@ -3,6 +3,7 @@ import * as scheduledSharesSelectors from './scheduled-shares';
 import * as servicesSelectors from './services';
 import * as sharepostSelectors from './share-post';
 import * as shareStatusSelectors from './share-status';
+import * as sharesDataSelectors from './shares-data';
 import * as socialModuleSelectors from './social-module-settings';
 import * as socialSettingsSelectors from './social-settings';
 
@@ -13,6 +14,7 @@ const selectors = {
 	...socialModuleSelectors,
 	...socialSettingsSelectors,
 	...servicesSelectors,
+	...sharesDataSelectors,
 	...scheduledSharesSelectors,
 };
 
diff --git a/projects/js-packages/publicize-components/src/social-store/selectors/shares-data.ts b/projects/js-packages/publicize-components/src/social-store/selectors/shares-data.ts
new file mode 100644
index 000000000000..3a3ff959591b
--- /dev/null
+++ b/projects/js-packages/publicize-components/src/social-store/selectors/shares-data.ts
@@ -0,0 +1,57 @@
+import { store as coreStore } from '@wordpress/core-data';
+import { createRegistrySelector } from '@wordpress/data';
+import { getSocialScriptData } from '../../utils';
+import { SharesData } from '../types';
+
+/**
+ * Get the shares data from the store or script data
+ * @param select - Select function
+ * @return The shares data
+ */
+const getSharesData = ( select ): SharesData => {
+	const data = select( coreStore ).getEntityRecord( 'wpcom/v2', 'publicize/shares-data' );
+	return data || getSocialScriptData().shares_data || {};
+};
+
+/**
+ * Helper function to get the count of shares already used
+ * @param data - Shares data object
+ * @return Number of shares used
+ */
+const getSharesUsedCount = ( data: SharesData ) => {
+	return data?.publicized_count ?? 0;
+};
+
+/**
+ * Helper function to get the count of scheduled shares
+ * @param data - Shares data object
+ * @return Number of scheduled shares
+ */
+const getScheduledSharesCount = ( data: SharesData ) => {
+	return data?.to_be_publicized_count ?? 0;
+};
+
+/**
+ * Returns the total number of shares used and scheduled.
+ */
+export const getTotalSharesCount = createRegistrySelector( select => (): number => {
+	const data = getSharesData( select );
+	const count = getSharesUsedCount( data ) + getScheduledSharesCount( data );
+	return Math.max( count, 0 );
+} );
+
+/**
+ * Number of posts shared this month
+ */
+export const getSharedPostsCount = createRegistrySelector( select => (): number => {
+	const data = getSharesData( select );
+	return data?.shared_posts_count ?? 0;
+} );
+
+/**
+ * Get whether the sharing limits are enabled.
+ */
+export const isShareLimitEnabled = createRegistrySelector( select => (): boolean => {
+	const data = getSharesData( select );
+	return data?.is_share_limit_enabled ?? false;
+} );
diff --git a/projects/js-packages/publicize-components/src/social-store/types.ts b/projects/js-packages/publicize-components/src/social-store/types.ts
index bb6fb8775165..aef68e296487 100644
--- a/projects/js-packages/publicize-components/src/social-store/types.ts
+++ b/projects/js-packages/publicize-components/src/social-store/types.ts
@@ -126,3 +126,10 @@ export type ScheduledShare = {
 	timestamp: number;
 	wpcom_user_id: number;
 };
+
+export type SharesData = {
+	publicized_count: number;
+	to_be_publicized_count: number;
+	shared_posts_count: number;
+	is_share_limit_enabled: boolean;
+};
diff --git a/projects/js-packages/publicize-components/src/utils/shares-data.ts b/projects/js-packages/publicize-components/src/utils/shares-data.ts
deleted file mode 100644
index e1184b4c2c17..000000000000
--- a/projects/js-packages/publicize-components/src/utils/shares-data.ts
+++ /dev/null
@@ -1,59 +0,0 @@
-import { getSocialScriptData } from './script-data';
-
-/**
- * Returns the shares data.
- *
- * @return {import('../types/types').SharesData} Shares data
- */
-export function getSharesData() {
-	return getSocialScriptData().shares_data;
-}
-/**
- * Returns the total number of shares already used.
- *
- * @return {number} Total number of shares used
- */
-export function getSharesUsedCount() {
-	return getSharesData().publicized_count ?? 0;
-}
-
-/**
- * Returns the number of shares scheduled.
- *
- * @return {number} Number of shares scheduled
- */
-export function getScheduledSharesCount() {
-	return getSharesData().to_be_publicized_count ?? 0;
-}
-
-/**
- * Returns the total number of shares used and scheduled.
- *
- * @return {number} Total number of shares used and scheduled
- */
-export function getTotalSharesCount() {
-	const count = getSharesUsedCount() + getScheduledSharesCount();
-
-	return Math.max( count, 0 );
-}
-
-/**
- * Number of posts shared this month
- *
- * @return {number} Number of posts shared this month
- */
-export function getSharedPostsCount() {
-	return getSharesData().shared_posts_count ?? 0;
-}
-
-/**
- * Get whether the sharing limits are enabled. This is partially
- * irrelevant now as we have given the feature to all sites, but
- * if the limits are enabled we count the shares made, and can
- * display those stats.
- *
- * @return {boolean} Whether the sharing limits are enabled
- */
-export function isShareLimitEnabled() {
-	return getSharesData().is_share_limit_enabled ?? false;
-}
diff --git a/projects/packages/publicize/.phan/baseline.php b/projects/packages/publicize/.phan/baseline.php
index 714aafc6e132..13b9ad5502e5 100644
--- a/projects/packages/publicize/.phan/baseline.php
+++ b/projects/packages/publicize/.phan/baseline.php
@@ -13,6 +13,7 @@
     // PhanPluginMixedKeyNoKey : 6 occurrences
     // PhanTypeMismatchArgument : 6 occurrences
     // PhanPluginUnreachableCode : 4 occurrences
+    // PhanTypeMismatchArgument : 4 occurrences
     // PhanUndeclaredClassMethod : 3 occurrences
     // PhanNoopNew : 2 occurrences
     // PhanPossiblyUndeclaredVariable : 2 occurrences
@@ -37,7 +38,7 @@
         'src/class-connections.php' => ['PhanUndeclaredClassMethod', 'PhanUndeclaredMethod'],
         'src/class-keyring-helper.php' => ['PhanTypeMismatchArgumentProbablyReal', 'PhanTypeMismatchDefault'],
         'src/class-publicize-base.php' => ['PhanImpossibleCondition', 'PhanPluginDuplicateConditionalNullCoalescing', 'PhanPluginSimplifyExpressionBool', 'PhanSuspiciousMagicConstant', 'PhanTypeMismatchArgument', 'PhanTypeMismatchArgumentNullable', 'PhanTypeMismatchArgumentNullableInternal', 'PhanTypeMismatchDimFetch', 'PhanTypeMismatchReturn'],
-        'src/class-publicize-setup.php' => ['PhanNoopNew', 'PhanTypeMismatchArgument'],
+        'src/class-publicize-setup.php' => ['PhanNoopNew'],
         'src/class-publicize-ui.php' => ['PhanPluginDuplicateExpressionAssignmentOperation', 'PhanTypeMismatchReturnProbablyReal'],
         'src/class-publicize.php' => ['PhanParamSignatureMismatch', 'PhanPossiblyUndeclaredVariable', 'PhanTypeMismatchArgument', 'PhanTypeMissingReturn'],
         'src/class-rest-controller.php' => ['PhanPluginDuplicateConditionalNullCoalescing', 'PhanTypeMismatchReturnProbablyReal'],
@@ -45,6 +46,7 @@
         'src/rest-api/class-connections-post-field.php' => ['PhanPluginDuplicateConditionalNullCoalescing'],
         'src/rest-api/class-scheduled-actions-controller.php' => ['PhanPluginMixedKeyNoKey'],
         'src/rest-api/class-services-controller.php' => ['PhanPluginMixedKeyNoKey'],
+        'src/rest-api/class-shares-data-controller.php' => ['PhanPluginMixedKeyNoKey'],
         'src/social-image-generator/class-post-settings.php' => ['PhanPluginDuplicateConditionalNullCoalescing'],
         'src/social-image-generator/class-rest-settings-controller.php' => ['PhanPluginMixedKeyNoKey'],
         'src/social-image-generator/class-settings.php' => ['PhanPluginDuplicateConditionalNullCoalescing'],
diff --git a/projects/packages/publicize/changelog/update-social-load-share-data-async b/projects/packages/publicize/changelog/update-social-load-share-data-async
new file mode 100644
index 000000000000..e1aacc9e601b
--- /dev/null
+++ b/projects/packages/publicize/changelog/update-social-load-share-data-async
@@ -0,0 +1,4 @@
+Significance: minor
+Type: changed
+
+Moved the jetpack-social endpoint ot the publicize package
diff --git a/projects/packages/publicize/src/class-publicize-base.php b/projects/packages/publicize/src/class-publicize-base.php
index 2032a4565c11..3504c8b5ea09 100644
--- a/projects/packages/publicize/src/class-publicize-base.php
+++ b/projects/packages/publicize/src/class-publicize-base.php
@@ -1928,17 +1928,19 @@ public function get_api_data( $blog_id ) {
 	/**
 	 * Get the Publicize shares info.
 	 *
-	 * @param string $blog_id The WPCOM blog_id for the current blog.
+	 * This function is overwritten in class-publicize-wpcom.php
+	 *
+	 * @param int $blog_id The WPCOM blog_id for the current blog.
 	 * @return ?array
 	 */
-	public function get_publicize_shares_info( $blog_id ) {
-		$data = $this->get_api_data( $blog_id );
+	public function get_publicize_shares_info( $blog_id ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+		$shares_info = get_transient( 'jetpack_publicize_shares_info' );
 
-		if ( empty( $data ) ) {
-			return null;
+		if ( ! empty( $shares_info ) ) {
+			return $shares_info;
 		}
 
-		return $data;
+		return null;
 	}
 
 	/**
diff --git a/projects/packages/publicize/src/class-publicize-setup.php b/projects/packages/publicize/src/class-publicize-setup.php
index 04d5e43278a6..9a9df9321724 100644
--- a/projects/packages/publicize/src/class-publicize-setup.php
+++ b/projects/packages/publicize/src/class-publicize-setup.php
@@ -43,6 +43,7 @@ public static function pre_initialization() {
 			REST_API\Connections_Controller::class,
 			REST_API\Scheduled_Actions_Controller::class,
 			REST_API\Services_Controller::class,
+			REST_API\Shares_Data_Controller::class,
 			REST_API\Share_Post_Controller::class,
 		);
 
diff --git a/projects/packages/publicize/src/rest-api/class-shares-data-controller.php b/projects/packages/publicize/src/rest-api/class-shares-data-controller.php
new file mode 100644
index 000000000000..00d8114d8f4a
--- /dev/null
+++ b/projects/packages/publicize/src/rest-api/class-shares-data-controller.php
@@ -0,0 +1,127 @@
+<?php
+/**
+ * The Jetpack Social Controller class.
+ *
+ * @package automattic/jetpack-publicize
+ */
+
+namespace Automattic\Jetpack\Publicize\REST_API;
+
+use Automattic\Jetpack\Connection\Traits\WPCOM_REST_API_Proxy_Request;
+use Automattic\Jetpack\Publicize\Publicize_Utils as Utils;
+use WP_Error;
+use WP_REST_Request;
+use WP_REST_Response;
+use WP_REST_Server;
+
+/**
+ * Jetpack Social Controller class.
+ */
+class Shares_Data_Controller extends Base_Controller {
+
+	use WPCOM_REST_API_Proxy_Request;
+
+	/**
+	 * Constructor.
+	 */
+	public function __construct() {
+		parent::__construct();
+
+		$this->base_api_path = 'wpcom';
+		$this->version       = 'v2';
+
+		$this->namespace = "{$this->base_api_path}/{$this->version}";
+		$this->rest_base = 'publicize/shares-data';
+
+		add_action( 'rest_api_init', array( $this, 'register_routes' ) );
+	}
+
+	/**
+	 * Register the routes.
+	 */
+	public function register_routes() {
+		register_rest_route(
+			$this->namespace,
+			'/' . $this->rest_base,
+			array(
+				array(
+					'methods'             => WP_REST_Server::READABLE,
+					'callback'            => array( $this, 'get_items' ),
+					'permission_callback' => array( $this, 'get_items_permissions_check' ),
+				),
+				'schema' => array( $this, 'get_public_item_schema' ),
+			)
+		);
+	}
+
+	/**
+	 * Get Jetpack Social data.
+	 *
+	 * @param WP_REST_Request $request Full details about the request.
+	 * @return WP_REST_Response|WP_Error
+	 */
+	public function get_items( $request ) {
+		if ( Utils::is_wpcom() ) {
+			global $publicize;
+
+			return rest_ensure_response(
+				$publicize->get_publicize_shares_info( get_current_blog_id() )
+			);
+		}
+
+		$response = $this->proxy_request_to_wpcom_as_user(
+			$request
+		);
+
+		if ( is_wp_error( $response ) ) {
+			return $response;
+		}
+
+		set_transient( 'jetpack_publicize_shares_info', $response, 1 * MONTH_IN_SECONDS );
+
+		return rest_ensure_response( $response );
+	}
+
+	/**
+	 * Verify that the request has access to Jetpack Social data.
+	 *
+	 * @param WP_REST_Request $request Full details about the request.
+	 * @return true|WP_Error
+	 */
+	public function get_items_permissions_check( $request ) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+		return $this->publicize_permissions_check();
+	}
+
+	/**
+	 * Schema for the endpoint.
+	 *
+	 * @return array
+	 */
+	public function get_item_schema() {
+		$schema = array(
+			'$schema'    => 'http://json-schema.org/draft-04/schema#',
+			'title'      => 'jetpack-social-shares-data',
+			'type'       => 'object',
+			'properties' => array(
+				'publicized_count'       => array(
+					'description' => __( 'Number of shares already used.', 'jetpack-publicize-pkg' ),
+					'type'        => 'integer',
+				),
+				'to_be_publicized_count' => array(
+					'description' => __( 'Number of scheduled shares.', 'jetpack-publicize-pkg' ),
+					'type'        => 'integer',
+				),
+				'shared_posts_count'     => array(
+					'description' => __( 'Number of posts shared.', 'jetpack-publicize-pkg' ),
+					'type'        => 'integer',
+				),
+				'is_share_limit_enabled' => array(
+					'description' => __( 'Whether the share limit is enabled.', 'jetpack-publicize-pkg' ),
+					'type'        => 'boolean',
+				),
+			),
+		);
+
+		return $this->add_additional_fields_schema( $schema );
+	}
+}


Reviews:
user: login: gmjuhasz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-03T17:35:52Z
user: login: manzoorwanijk
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-04T04:25:56Z
user: login: manzoorwanijk
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-04T04:28:33Z
user: login: manzoorwanijk
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-04T04:29:40Z
user: login: manzoorwanijk
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-04T04:30:28Z
user: login: gmjuhasz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-04T08:47:44Z
user: login: manzoorwanijk
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-04T08:49:31Z
user: login: gmjuhasz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-04T08:50:16Z
user: login: gmjuhasz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-04T11:38:58Z
user: login: gmjuhasz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-04T11:43:56Z
user: login: gmjuhasz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-04T11:46:50Z
user: login: gmjuhasz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-04T11:55:07Z
user: login: pablinos
body: It looks generally good. There's perhaps a couple of things to look at, but we also don't want to spend too long on this. We can always create follow-up tasks if necessary.
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-04T22:01:51Z
user: login: manzoorwanijk
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-05T08:00:40Z
user: login: manzoorwanijk
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-05T08:04:29Z
user: login: pablinos
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-05T16:28:29Z
user: login: pablinos
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T18:56:58Z
user: login: gmjuhasz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-14T19:34:45Z
user: login: manzoorwanijk
body: This works well but I have some suggestions/questions.
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-17T09:17:07Z
user: login: gmjuhasz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T09:35:12Z
user: login: gmjuhasz
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-03-17T09:37:51Z
user: login: manzoorwanijk
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-17T09:41:34Z
user: login: manzoorwanijk
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-17T09:42:44Z
user: login: manzoorwanijk
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-17T09:45:13Z
user: login: manzoorwanijk
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-18T08:06:24Z
user: login: manzoorwanijk
body: This looks good to me now.
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-03-18T08:06:37Z
user: login: manzoorwanijk
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-03-18T10:54:05Z
user: login: manzoorwanijk
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-20T07:41:39Z

Review comments:
diff_hunk: @@ -1943,17 +1943,19 @@ public function get_api_data( $blog_id ) {
 	/**
 	 * Get the Publicize shares info.
 	 *
+	 * This function is overwritten in class-publicize-wpcom.php
+	 *
 	 * @param string $blog_id The WPCOM blog_id for the current blog.
 	 * @return ?array
 	 */
-	public function get_publicize_shares_info( $blog_id ) {
path: projects/packages/publicize/src/class-publicize-base.php
user: login: gmjuhasz
body: This function is overriden in wpcom fbhepr%2Skers%2Sjcpbz%2Sjc%2Qpbagrag%2Snqzva%2Qcyhtvaf%2Schoyvpvmr%2Schoyvpvmr%2Qjcpbz.cuc%3Se%3Qp72qssq8%232572-og
created_at: 2025-03-03T17:35:51Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,80 @@
+import { isWpcomPlatformSite } from '@automattic/jetpack-script-data';
+import apiFetch from '@wordpress/api-fetch';
+import { useCallback, useEffect, useMemo, useState } from '@wordpress/element';
+import { getSocialScriptData } from '../../utils/script-data';
+
+/**
+ * Hook that provides access to share data and related utility functions.
+ *
+ * @return {object} Object containing share data and utility functions
+ */
+export default function useSharesData() {
path: projects/js-packages/publicize-components/src/hooks/use-shares-data/index.ts
user: login: manzoorwanijk
body: Instead of this hook to implement `apiFetch`, we can make use of `getEntityRecord` of the core data store to handle it for us.

For that we will have to register the entity config in [`hydrateStores`](https://github.com/Automattic/jetpack/blob/79d9b3f7cc87eedd6602ab15872c78fa8f8316f5/projects/js-packages/publicize-components/src/social-store/hydrate-stores.ts#L9)
created_at: 2025-03-04T04:25:55Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,91 @@
+<?php
+/**
+ * The Jetpack Social Controller class.
+ *
+ * @package automattic/jetpack-publicize
+ */
+
+namespace Automattic\Jetpack\Publicize\REST_API;
+
+use Automattic\Jetpack\Connection\Traits\WPCOM_REST_API_Proxy_Request;
+use Automattic\Jetpack\Publicize\Publicize_Utils as Utils;
+use WP_Error;
+use WP_REST_Request;
+use WP_REST_Response;
+use WP_REST_Server;
+
+/**
+ * Jetpack Social Controller class.
+ */
+class Jetpack_Social_Controller extends Base_Controller {
path: projects/packages/publicize/src/rest-api/class-jetpack-social-controller.php
user: login: manzoorwanijk
body: `Jetpack_Social_*` is a very generic name. I think it's better to have specific classes/controllers for specific endpoints. It may be better to name this class as `Shares_Data_Controller` and likewise the endpoint.
created_at: 2025-03-04T04:28:33Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,91 @@
+<?php
+/**
+ * The Jetpack Social Controller class.
+ *
+ * @package automattic/jetpack-publicize
+ */
+
+namespace Automattic\Jetpack\Publicize\REST_API;
+
+use Automattic\Jetpack\Connection\Traits\WPCOM_REST_API_Proxy_Request;
+use Automattic\Jetpack\Publicize\Publicize_Utils as Utils;
+use WP_Error;
+use WP_REST_Request;
+use WP_REST_Response;
+use WP_REST_Server;
+
+/**
+ * Jetpack Social Controller class.
+ */
+class Jetpack_Social_Controller extends Base_Controller {
+
+	use WPCOM_REST_API_Proxy_Request;
+
+	/**
+	 * Constructor.
+	 */
+	public function __construct() {
+		parent::__construct();
+
+		$this->base_api_path = 'wpcom';
+		$this->version       = 'v2';
+
+		$this->namespace = "{$this->base_api_path}/{$this->version}";
+		$this->rest_base = 'jetpack-social';
path: projects/packages/publicize/src/rest-api/class-jetpack-social-controller.php
user: login: manzoorwanijk
body: As mentioned above, let use a specific name like `publicize/shares-data` as the `$rest_base`
created_at: 2025-03-04T04:29:40Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,91 @@
+<?php
+/**
+ * The Jetpack Social Controller class.
+ *
+ * @package automattic/jetpack-publicize
+ */
+
+namespace Automattic\Jetpack\Publicize\REST_API;
+
+use Automattic\Jetpack\Connection\Traits\WPCOM_REST_API_Proxy_Request;
+use Automattic\Jetpack\Publicize\Publicize_Utils as Utils;
+use WP_Error;
+use WP_REST_Request;
+use WP_REST_Response;
+use WP_REST_Server;
+
+/**
+ * Jetpack Social Controller class.
+ */
+class Jetpack_Social_Controller extends Base_Controller {
+
+	use WPCOM_REST_API_Proxy_Request;
+
+	/**
+	 * Constructor.
+	 */
+	public function __construct() {
+		parent::__construct();
+
+		$this->base_api_path = 'wpcom';
+		$this->version       = 'v2';
+
+		$this->namespace = "{$this->base_api_path}/{$this->version}";
+		$this->rest_base = 'jetpack-social';
+
+		add_action( 'rest_api_init', array( $this, 'register_routes' ) );
+	}
+
+	/**
+	 * Register the routes.
+	 */
+	public function register_routes() {
+		register_rest_route(
+			$this->namespace,
+			'/' . $this->rest_base,
+			array(
+				array(
+					'methods'             => WP_REST_Server::READABLE,
+					'callback'            => array( $this, 'get_items' ),
+					'permission_callback' => array( $this, 'get_items_permissions_check' ),
+				),
+				'schema' => array( $this, 'get_public_item_schema' ),
+			)
+		);
+	}
+
+	/**
+	 * Get Jetpack Social data.
+	 *
+	 * @param WP_REST_Request $request Full details about the request.
+	 * @return WP_REST_Response|WP_Error
+	 */
+	public function get_items( $request ) {
+		// We only support this endpoint for self-hosted sites.
+		if ( Utils::is_wpcom() ) {
+			return new WP_Error( 'invalid_request', 'This endpoint is not available for WordPress.com sites.', array( 'status' => 404 ) );
+		}
path: projects/packages/publicize/src/rest-api/class-jetpack-social-controller.php
user: login: manzoorwanijk
body: Should this be a part of the permissions check?
created_at: 2025-03-04T04:30:27Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,80 @@
+import { isWpcomPlatformSite } from '@automattic/jetpack-script-data';
+import apiFetch from '@wordpress/api-fetch';
+import { useCallback, useEffect, useMemo, useState } from '@wordpress/element';
+import { getSocialScriptData } from '../../utils/script-data';
+
+/**
+ * Hook that provides access to share data and related utility functions.
+ *
+ * @return {object} Object containing share data and utility functions
+ */
+export default function useSharesData() {
path: projects/js-packages/publicize-components/src/hooks/use-shares-data/index.ts
user: login: gmjuhasz
body: Wouldn't that make it so it's hydrates it every time the store is used? I think that would introduce unneeded calls as this fetch is only needed on the admin page, all of the utility functions are only used in this component
created_at: 2025-03-04T08:47:44Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,80 @@
+import { isWpcomPlatformSite } from '@automattic/jetpack-script-data';
+import apiFetch from '@wordpress/api-fetch';
+import { useCallback, useEffect, useMemo, useState } from '@wordpress/element';
+import { getSocialScriptData } from '../../utils/script-data';
+
+/**
+ * Hook that provides access to share data and related utility functions.
+ *
+ * @return {object} Object containing share data and utility functions
+ */
+export default function useSharesData() {
path: projects/js-packages/publicize-components/src/hooks/use-shares-data/index.ts
user: login: manzoorwanijk
body: No, the API call won't happen until the selector is called, which will happen only on the admin page.
created_at: 2025-03-04T08:49:31Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,80 @@
+import { isWpcomPlatformSite } from '@automattic/jetpack-script-data';
+import apiFetch from '@wordpress/api-fetch';
+import { useCallback, useEffect, useMemo, useState } from '@wordpress/element';
+import { getSocialScriptData } from '../../utils/script-data';
+
+/**
+ * Hook that provides access to share data and related utility functions.
+ *
+ * @return {object} Object containing share data and utility functions
+ */
+export default function useSharesData() {
path: projects/js-packages/publicize-components/src/hooks/use-shares-data/index.ts
user: login: gmjuhasz
body: I see. I never used that pattern before but I'll try to mimic from the current ones there
created_at: 2025-03-04T08:50:16Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,91 @@
+<?php
+/**
+ * The Jetpack Social Controller class.
+ *
+ * @package automattic/jetpack-publicize
+ */
+
+namespace Automattic\Jetpack\Publicize\REST_API;
+
+use Automattic\Jetpack\Connection\Traits\WPCOM_REST_API_Proxy_Request;
+use Automattic\Jetpack\Publicize\Publicize_Utils as Utils;
+use WP_Error;
+use WP_REST_Request;
+use WP_REST_Response;
+use WP_REST_Server;
+
+/**
+ * Jetpack Social Controller class.
+ */
+class Jetpack_Social_Controller extends Base_Controller {
path: projects/packages/publicize/src/rest-api/class-jetpack-social-controller.php
user: login: gmjuhasz
body: The endpoint is called jetpack-social on wpcom, do you want to change that too? If we keep it like this we do not have to touch that
created_at: 2025-03-04T11:38:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,91 @@
+<?php
+/**
+ * The Jetpack Social Controller class.
+ *
+ * @package automattic/jetpack-publicize
+ */
+
+namespace Automattic\Jetpack\Publicize\REST_API;
+
+use Automattic\Jetpack\Connection\Traits\WPCOM_REST_API_Proxy_Request;
+use Automattic\Jetpack\Publicize\Publicize_Utils as Utils;
+use WP_Error;
+use WP_REST_Request;
+use WP_REST_Response;
+use WP_REST_Server;
+
+/**
+ * Jetpack Social Controller class.
+ */
+class Jetpack_Social_Controller extends Base_Controller {
+
+	use WPCOM_REST_API_Proxy_Request;
+
+	/**
+	 * Constructor.
+	 */
+	public function __construct() {
+		parent::__construct();
+
+		$this->base_api_path = 'wpcom';
+		$this->version       = 'v2';
+
+		$this->namespace = "{$this->base_api_path}/{$this->version}";
+		$this->rest_base = 'jetpack-social';
path: projects/packages/publicize/src/rest-api/class-jetpack-social-controller.php
user: login: gmjuhasz
body: Let's talk about this on the other thread
created_at: 2025-03-04T11:43:56Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,91 @@
+<?php
+/**
+ * The Jetpack Social Controller class.
+ *
+ * @package automattic/jetpack-publicize
+ */
+
+namespace Automattic\Jetpack\Publicize\REST_API;
+
+use Automattic\Jetpack\Connection\Traits\WPCOM_REST_API_Proxy_Request;
+use Automattic\Jetpack\Publicize\Publicize_Utils as Utils;
+use WP_Error;
+use WP_REST_Request;
+use WP_REST_Response;
+use WP_REST_Server;
+
+/**
+ * Jetpack Social Controller class.
+ */
+class Jetpack_Social_Controller extends Base_Controller {
path: projects/packages/publicize/src/rest-api/class-jetpack-social-controller.php
user: login: gmjuhasz
body: I'm not sure if it's worth the hassle of deployments, what do you think?
created_at: 2025-03-04T11:46:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,91 @@
+<?php
+/**
+ * The Jetpack Social Controller class.
+ *
+ * @package automattic/jetpack-publicize
+ */
+
+namespace Automattic\Jetpack\Publicize\REST_API;
+
+use Automattic\Jetpack\Connection\Traits\WPCOM_REST_API_Proxy_Request;
+use Automattic\Jetpack\Publicize\Publicize_Utils as Utils;
+use WP_Error;
+use WP_REST_Request;
+use WP_REST_Response;
+use WP_REST_Server;
+
+/**
+ * Jetpack Social Controller class.
+ */
+class Jetpack_Social_Controller extends Base_Controller {
path: projects/packages/publicize/src/rest-api/class-jetpack-social-controller.php
user: login: gmjuhasz
body: Also it returns more than just share data:
```
{
    "is_share_limit_enabled": true,
    "to_be_publicized_count": 0,
    "share_limit": 30,
    "publicized_count": 63,
    "shared_posts_count": 17,
    "shares_remaining": 0,
    "computed_shares_remaining": 0,
    "is_enhanced_publishing_enabled": true,
    "is_social_image_generator_enabled": true,
    "show_advanced_plan_upgrade_nudge": false
}
```
created_at: 2025-03-04T11:55:07Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,91 @@
+<?php
+/**
+ * The Jetpack Social Controller class.
+ *
+ * @package automattic/jetpack-publicize
+ */
+
+namespace Automattic\Jetpack\Publicize\REST_API;
+
+use Automattic\Jetpack\Connection\Traits\WPCOM_REST_API_Proxy_Request;
+use Automattic\Jetpack\Publicize\Publicize_Utils as Utils;
+use WP_Error;
+use WP_REST_Request;
+use WP_REST_Response;
+use WP_REST_Server;
+
+/**
+ * Jetpack Social Controller class.
+ */
+class Jetpack_Social_Controller extends Base_Controller {
+
+	use WPCOM_REST_API_Proxy_Request;
+
+	/**
+	 * Constructor.
+	 */
+	public function __construct() {
+		parent::__construct();
+
+		$this->base_api_path = 'wpcom';
+		$this->version       = 'v2';
+
+		$this->namespace = "{$this->base_api_path}/{$this->version}";
+		$this->rest_base = 'jetpack-social';
+
+		add_action( 'rest_api_init', array( $this, 'register_routes' ) );
+	}
+
+	/**
+	 * Register the routes.
+	 */
+	public function register_routes() {
+		register_rest_route(
+			$this->namespace,
+			'/' . $this->rest_base,
+			array(
+				array(
+					'methods'             => WP_REST_Server::READABLE,
+					'callback'            => array( $this, 'get_items' ),
+					'permission_callback' => array( $this, 'get_items_permissions_check' ),
+				),
+				'schema' => array( $this, 'get_public_item_schema' ),
+			)
+		);
+	}
+
+	/**
+	 * Get Jetpack Social data.
+	 *
+	 * @param WP_REST_Request $request Full details about the request.
+	 * @return WP_REST_Response|WP_Error
+	 */
+	public function get_items( $request ) {
+		// We only support this endpoint for self-hosted sites.
+		if ( Utils::is_wpcom() ) {
+			return new WP_Error( 'invalid_request', 'This endpoint is not available for WordPress.com sites.', array( 'status' => 404 ) );
+		}
path: projects/packages/publicize/src/rest-api/class-jetpack-social-controller.php
user: login: pablinos
body: Does this work with this PR running on WPCOM? It could be that this registers the endpoint second, and it would takeover the existing one on WPCOM.
created_at: 2025-03-04T20:57:22Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,91 @@
+<?php
+/**
+ * The Jetpack Social Controller class.
+ *
+ * @package automattic/jetpack-publicize
+ */
+
+namespace Automattic\Jetpack\Publicize\REST_API;
+
+use Automattic\Jetpack\Connection\Traits\WPCOM_REST_API_Proxy_Request;
+use Automattic\Jetpack\Publicize\Publicize_Utils as Utils;
+use WP_Error;
+use WP_REST_Request;
+use WP_REST_Response;
+use WP_REST_Server;
+
+/**
+ * Jetpack Social Controller class.
+ */
+class Jetpack_Social_Controller extends Base_Controller {
path: projects/packages/publicize/src/rest-api/class-jetpack-social-controller.php
user: login: pablinos
body: I think it would be good to do this, but it feels like a bit of rabbit hole. Looking at this, it's only the last three items that are not shares related.  If we still need those then perhaps it should be something like Jetpack_Social_Data_Controller, but that's pretty generic too!

We're not using `show_advanced_plan_upgrade_nudge` [anymore](https://github.com/Automattic/jetpack/pull/38926) and I can't see the other two being used either, so perhaps we create this as a new `Shares_Data_Controller`, and we can deprecate the old one on WPCOM?
created_at: 2025-03-04T21:07:43Z
author_association: CONTRIBUTOR
diff_hunk: @@ -60,4 +60,25 @@ export async function hydrateStores() {
 
 		await finishResolution( 'getEntityRecords', [ 'wpcom/v2', 'publicize/services' ] );
 	}
+
+	if ( ! wpcomEntities.some( ( { name } ) => name === 'jetpack-social' ) ) {
+		await addEntities( [
+			{
+				kind: 'wpcom/v2',
+				name: 'jetpack-social',
+				baseURL: '/wpcom/v2/jetpack-social',
+				label: __( 'Publicize shares data', 'jetpack-publicize-components' ),
+			},
+		] );
+
+		// @ts-expect-error Only 3 arguments are required, rest are optional but types expect 7
+		await receiveEntityRecords(
+			'wpcom/v2',
+			'jetpack-social',
+			getSocialScriptData()?.shares_data,
+			true
+		);
+
+		await finishResolution( 'getEntityRecords', [ 'wpcom/v2', 'jetpack-social' ] );
+	}
path: projects/js-packages/publicize-components/src/social-store/hydrate-stores.ts
user: login: pablinos
body: This seems like quite a bit of boilerplate. We're doing this for at least three endpoints now. Shall we create a helper function? (not for this PR!)
created_at: 2025-03-04T21:13:37Z
author_association: CONTRIBUTOR
diff_hunk: @@ -60,4 +60,25 @@ export async function hydrateStores() {
 
 		await finishResolution( 'getEntityRecords', [ 'wpcom/v2', 'publicize/services' ] );
 	}
+
+	if ( ! wpcomEntities.some( ( { name } ) => name === 'jetpack-social' ) ) {
+		await addEntities( [
+			{
+				kind: 'wpcom/v2',
+				name: 'jetpack-social',
+				baseURL: '/wpcom/v2/jetpack-social',
+				label: __( 'Publicize shares data', 'jetpack-publicize-components' ),
+			},
+		] );
+
+		// @ts-expect-error Only 3 arguments are required, rest are optional but types expect 7
+		await receiveEntityRecords(
+			'wpcom/v2',
+			'jetpack-social',
+			getSocialScriptData()?.shares_data,
+			true
+		);
+
+		await finishResolution( 'getEntityRecords', [ 'wpcom/v2', 'jetpack-social' ] );
path: projects/js-packages/publicize-components/src/social-store/hydrate-stores.ts
user: login: pablinos
body: Now we've resolved the shares info from the cached values, we won't make the API request. I think we should either not call this, or we'll need to call `invalidateResolution` to make the API request.
created_at: 2025-03-04T21:33:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,62 @@
+import { isWpcomPlatformSite } from '@automattic/jetpack-script-data';
+import { store as coreStore } from '@wordpress/core-data';
+import { createRegistrySelector } from '@wordpress/data';
+import { getSocialScriptData } from '../../utils';
+
+/**
+ * Get the shares data from the store or script data
+ * @param select - Select function
+ * @return The shares data
+ */
+const getSharesData = select => {
+	// On WordPress.com platform sites, use script data directly
+	if ( isWpcomPlatformSite() ) {
+		return getSocialScriptData().shares_data || {};
+	}
path: projects/js-packages/publicize-components/src/social-store/selectors/shares-data.ts
user: login: pablinos
body: Is this definitely necessary? The shares data will be initialised, but if we invalidate the resolution, I suppose we would make an unnecessary API call.

If we could avoid this, it would be great. Having different behaviour in the different environments sometimes causes us problems.
created_at: 2025-03-04T22:00:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,91 @@
+<?php
+/**
+ * The Jetpack Social Controller class.
+ *
+ * @package automattic/jetpack-publicize
+ */
+
+namespace Automattic\Jetpack\Publicize\REST_API;
+
+use Automattic\Jetpack\Connection\Traits\WPCOM_REST_API_Proxy_Request;
+use Automattic\Jetpack\Publicize\Publicize_Utils as Utils;
+use WP_Error;
+use WP_REST_Request;
+use WP_REST_Response;
+use WP_REST_Server;
+
+/**
+ * Jetpack Social Controller class.
+ */
+class Jetpack_Social_Controller extends Base_Controller {
path: projects/packages/publicize/src/rest-api/class-jetpack-social-controller.php
user: login: manzoorwanijk
body: Yes, we no longer use that other data.



> so perhaps we create this as a new `Shares_Data_Controller`, and we can deprecate the old one on WPCOM?

Yeah, that is better.
created_at: 2025-03-05T08:00:39Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,62 @@
+import { isWpcomPlatformSite } from '@automattic/jetpack-script-data';
+import { store as coreStore } from '@wordpress/core-data';
+import { createRegistrySelector } from '@wordpress/data';
+import { getSocialScriptData } from '../../utils';
+
+/**
+ * Get the shares data from the store or script data
+ * @param select - Select function
+ * @return The shares data
+ */
+const getSharesData = select => {
+	// On WordPress.com platform sites, use script data directly
+	if ( isWpcomPlatformSite() ) {
+		return getSocialScriptData().shares_data || {};
+	}
path: projects/js-packages/publicize-components/src/social-store/selectors/shares-data.ts
user: login: manzoorwanijk
body: Yes, the selector should ideally be unaware of this logic. Whether to show the shares data or not should be handled in the component itself.
created_at: 2025-03-05T08:04:29Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,62 @@
+import { isWpcomPlatformSite } from '@automattic/jetpack-script-data';
+import { store as coreStore } from '@wordpress/core-data';
+import { createRegistrySelector } from '@wordpress/data';
+import { getSocialScriptData } from '../../utils';
+
+/**
+ * Get the shares data from the store or script data
+ * @param select - Select function
+ * @return The shares data
+ */
+const getSharesData = select => {
+	// On WordPress.com platform sites, use script data directly
+	if ( isWpcomPlatformSite() ) {
+		return getSocialScriptData().shares_data || {};
+	}
path: projects/js-packages/publicize-components/src/social-store/selectors/shares-data.ts
user: login: pablinos
body: I don't think it's about whether to show the shares data, but where it gets it from. It may not be necessary though as we have never enabled the share limits on wpcom, so shouldn't be displaying the cards and therefore shouldn't be calling this selector.

We could probably rely on that, as the worse that would happen is an extra API call to update the values.
created_at: 2025-03-05T16:28:29Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,16 +70,6 @@ export async function hydrateStores() {
 				label: __( 'Publicize shares data', 'jetpack-publicize-components' ),
 			},
 		] );
-
-		// @ts-expect-error Only 3 arguments are required, rest are optional but types expect 7
-		await receiveEntityRecords(
-			'wpcom/v2',
-			'publicize/shares-data',
-			getSocialScriptData()?.shares_data,
-			true
-		);
path: projects/js-packages/publicize-components/src/social-store/hydrate-stores.ts
user: login: pablinos
body: We might still want to do this I think, so that, to begin with, the cards will show the cached values. That might need some testing though!
created_at: 2025-03-14T18:56:58Z
author_association: CONTRIBUTOR
diff_hunk: @@ -70,16 +70,6 @@ export async function hydrateStores() {
 				label: __( 'Publicize shares data', 'jetpack-publicize-components' ),
 			},
 		] );
-
-		// @ts-expect-error Only 3 arguments are required, rest are optional but types expect 7
-		await receiveEntityRecords(
-			'wpcom/v2',
-			'publicize/shares-data',
-			getSocialScriptData()?.shares_data,
-			true
-		);
path: projects/js-packages/publicize-components/src/social-store/hydrate-stores.ts
user: login: gmjuhasz
body: They do show the values without this as per my testing, coming from script data
created_at: 2025-03-14T19:34:45Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,94 @@
+<?php
+/**
+ * The Jetpack Social Controller class.
+ *
+ * @package automattic/jetpack-publicize
+ */
+
+namespace Automattic\Jetpack\Publicize\REST_API;
+
+use Automattic\Jetpack\Connection\Traits\WPCOM_REST_API_Proxy_Request;
+use Automattic\Jetpack\Publicize\Publicize_Utils as Utils;
+use WP_Error;
+use WP_REST_Request;
+use WP_REST_Response;
+use WP_REST_Server;
+
+/**
+ * Jetpack Social Controller class.
+ */
+class Shares_Data_Controller extends Base_Controller {
+
+	use WPCOM_REST_API_Proxy_Request;
+
+	/**
+	 * Constructor.
+	 */
+	public function __construct() {
+		parent::__construct();
+
+		$this->base_api_path = 'wpcom';
+		$this->version       = 'v2';
+
+		$this->namespace = "{$this->base_api_path}/{$this->version}";
+		$this->rest_base = 'publicize/shares-data';
+
+		add_action( 'rest_api_init', array( $this, 'register_routes' ) );
+	}
+
+	/**
+	 * Register the routes.
+	 */
+	public function register_routes() {
+		register_rest_route(
+			$this->namespace,
+			'/' . $this->rest_base,
+			array(
+				array(
+					'methods'             => WP_REST_Server::READABLE,
+					'callback'            => array( $this, 'get_items' ),
+					'permission_callback' => array( $this, 'get_items_permissions_check' ),
+				),
+				'schema' => array( $this, 'get_public_item_schema' ),
path: projects/packages/publicize/src/rest-api/class-shares-data-controller.php
user: login: manzoorwanijk
body: We haven't defined any schema here.
created_at: 2025-03-17T08:12:51Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,56 @@
+import { store as coreStore } from '@wordpress/core-data';
+import { createRegistrySelector } from '@wordpress/data';
+import { getSocialScriptData } from '../../utils';
+
+/**
+ * Get the shares data from the store or script data
+ * @param select - Select function
+ * @return The shares data
+ */
+const getSharesData = select => {
+	const data = select( coreStore ).getEntityRecord( 'wpcom/v2', 'publicize/shares-data' );
+	return data || getSocialScriptData().shares_data || {};
+};
+
+/**
+ * Helper function to get the count of shares already used
+ * @param data - Shares data object
+ * @return Number of shares used
+ */
+const getSharesUsedCount = data => {
+	return data?.publicized_count ?? 0;
+};
+
+/**
+ * Helper function to get the count of scheduled shares
+ * @param data - Shares data object
+ * @return Number of scheduled shares
+ */
+const getScheduledSharesCount = data => {
+	return data?.to_be_publicized_count ?? 0;
+};
path: projects/js-packages/publicize-components/src/social-store/selectors/shares-data.ts
user: login: manzoorwanijk
body: Why do we need these helper functions? You can make `getSharesData` a selector and then use that in other selectors.
created_at: 2025-03-17T08:22:23Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,56 @@
+import { store as coreStore } from '@wordpress/core-data';
+import { createRegistrySelector } from '@wordpress/data';
+import { getSocialScriptData } from '../../utils';
+
+/**
+ * Get the shares data from the store or script data
+ * @param select - Select function
+ * @return The shares data
+ */
+const getSharesData = select => {
+	const data = select( coreStore ).getEntityRecord( 'wpcom/v2', 'publicize/shares-data' );
+	return data || getSocialScriptData().shares_data || {};
+};
+
+/**
+ * Helper function to get the count of shares already used
+ * @param data - Shares data object
+ * @return Number of shares used
+ */
+const getSharesUsedCount = data => {
+	return data?.publicized_count ?? 0;
+};
+
+/**
+ * Helper function to get the count of scheduled shares
+ * @param data - Shares data object
+ * @return Number of scheduled shares
+ */
+const getScheduledSharesCount = data => {
+	return data?.to_be_publicized_count ?? 0;
+};
path: projects/js-packages/publicize-components/src/social-store/selectors/shares-data.ts
user: login: gmjuhasz
body: I already use getSharesData in getTotalSharesCount and getSharedPostsCount, the ones that we actually export and use. I just added these two helpers so the calculations in getTotalSharesCount is more clear and easier to read imo. 
created_at: 2025-03-17T09:35:12Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,94 @@
+<?php
+/**
+ * The Jetpack Social Controller class.
+ *
+ * @package automattic/jetpack-publicize
+ */
+
+namespace Automattic\Jetpack\Publicize\REST_API;
+
+use Automattic\Jetpack\Connection\Traits\WPCOM_REST_API_Proxy_Request;
+use Automattic\Jetpack\Publicize\Publicize_Utils as Utils;
+use WP_Error;
+use WP_REST_Request;
+use WP_REST_Response;
+use WP_REST_Server;
+
+/**
+ * Jetpack Social Controller class.
+ */
+class Shares_Data_Controller extends Base_Controller {
+
+	use WPCOM_REST_API_Proxy_Request;
+
+	/**
+	 * Constructor.
+	 */
+	public function __construct() {
+		parent::__construct();
+
+		$this->base_api_path = 'wpcom';
+		$this->version       = 'v2';
+
+		$this->namespace = "{$this->base_api_path}/{$this->version}";
+		$this->rest_base = 'publicize/shares-data';
+
+		add_action( 'rest_api_init', array( $this, 'register_routes' ) );
+	}
+
+	/**
+	 * Register the routes.
+	 */
+	public function register_routes() {
+		register_rest_route(
+			$this->namespace,
+			'/' . $this->rest_base,
+			array(
+				array(
+					'methods'             => WP_REST_Server::READABLE,
+					'callback'            => array( $this, 'get_items' ),
+					'permission_callback' => array( $this, 'get_items_permissions_check' ),
+				),
+				'schema' => array( $this, 'get_public_item_schema' ),
path: projects/packages/publicize/src/rest-api/class-shares-data-controller.php
user: login: gmjuhasz
body: That's a parent function isnt it? We do it like this in the other controllers as well if I am right.

fbhepr%2Sfrnepu%3Scebwrpg%3Qwrgcnpx%26shyy%3Qtrg_choyvp_vgrz_fpurzn%26qrsf%3Q%26cngu%3Q.%2Scebwrpgf%2Scnpxntrf%2Schoyvpvmr%2Sfep%2Serfg%2Qncv%26uvfg%3Q%26glcr%3Q%26keq%3Q%26aa%3Q1-og
created_at: 2025-03-17T09:37:50Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,94 @@
+<?php
+/**
+ * The Jetpack Social Controller class.
+ *
+ * @package automattic/jetpack-publicize
+ */
+
+namespace Automattic\Jetpack\Publicize\REST_API;
+
+use Automattic\Jetpack\Connection\Traits\WPCOM_REST_API_Proxy_Request;
+use Automattic\Jetpack\Publicize\Publicize_Utils as Utils;
+use WP_Error;
+use WP_REST_Request;
+use WP_REST_Response;
+use WP_REST_Server;
+
+/**
+ * Jetpack Social Controller class.
+ */
+class Shares_Data_Controller extends Base_Controller {
+
+	use WPCOM_REST_API_Proxy_Request;
+
+	/**
+	 * Constructor.
+	 */
+	public function __construct() {
+		parent::__construct();
+
+		$this->base_api_path = 'wpcom';
+		$this->version       = 'v2';
+
+		$this->namespace = "{$this->base_api_path}/{$this->version}";
+		$this->rest_base = 'publicize/shares-data';
+
+		add_action( 'rest_api_init', array( $this, 'register_routes' ) );
+	}
+
+	/**
+	 * Register the routes.
+	 */
+	public function register_routes() {
+		register_rest_route(
+			$this->namespace,
+			'/' . $this->rest_base,
+			array(
+				array(
+					'methods'             => WP_REST_Server::READABLE,
+					'callback'            => array( $this, 'get_items' ),
+					'permission_callback' => array( $this, 'get_items_permissions_check' ),
+				),
+				'schema' => array( $this, 'get_public_item_schema' ),
path: projects/packages/publicize/src/rest-api/class-shares-data-controller.php
user: login: manzoorwanijk
body: No, I mean we are not defining any schema at all for this endpoint. Since, this is not an endpoint to list items, we can define the schema directly without using `get_public_item_schema`.
created_at: 2025-03-17T09:41:34Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,56 @@
+import { store as coreStore } from '@wordpress/core-data';
+import { createRegistrySelector } from '@wordpress/data';
+import { getSocialScriptData } from '../../utils';
+
+/**
+ * Get the shares data from the store or script data
+ * @param select - Select function
+ * @return The shares data
+ */
+const getSharesData = select => {
+	const data = select( coreStore ).getEntityRecord( 'wpcom/v2', 'publicize/shares-data' );
+	return data || getSocialScriptData().shares_data || {};
+};
+
+/**
+ * Helper function to get the count of shares already used
+ * @param data - Shares data object
+ * @return Number of shares used
+ */
+const getSharesUsedCount = data => {
+	return data?.publicized_count ?? 0;
+};
+
+/**
+ * Helper function to get the count of scheduled shares
+ * @param data - Shares data object
+ * @return Number of scheduled shares
+ */
+const getScheduledSharesCount = data => {
+	return data?.to_be_publicized_count ?? 0;
+};
path: projects/js-packages/publicize-components/src/social-store/selectors/shares-data.ts
user: login: manzoorwanijk
body: Alright. Can we add some types for those params to make them type-safe?
created_at: 2025-03-17T09:42:44Z
author_association: MEMBER
diff_hunk: @@ -1928,17 +1928,19 @@ public function get_api_data( $blog_id ) {
 	/**
 	 * Get the Publicize shares info.
 	 *
+	 * This function is overwritten in class-publicize-wpcom.php
+	 *
 	 * @param string $blog_id The WPCOM blog_id for the current blog.
path: projects/packages/publicize/src/class-publicize-base.php
user: login: manzoorwanijk
body: Can we fix the type here by changing it to `int` to fix the Phan error?

```suggestion
	 * @param int $blog_id The WPCOM blog_id for the current blog.
```
created_at: 2025-03-17T09:45:13Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,127 @@
+<?php
+/**
+ * The Jetpack Social Controller class.
+ *
+ * @package automattic/jetpack-publicize
+ */
+
+namespace Automattic\Jetpack\Publicize\REST_API;
+
+use Automattic\Jetpack\Connection\Traits\WPCOM_REST_API_Proxy_Request;
+use Automattic\Jetpack\Publicize\Publicize_Utils as Utils;
+use WP_Error;
+use WP_REST_Request;
+use WP_REST_Response;
+use WP_REST_Server;
+
+/**
+ * Jetpack Social Controller class.
+ */
+class Shares_Data_Controller extends Base_Controller {
+
+	use WPCOM_REST_API_Proxy_Request;
+
+	/**
+	 * Constructor.
+	 */
+	public function __construct() {
+		parent::__construct();
+
+		$this->base_api_path = 'wpcom';
+		$this->version       = 'v2';
+
+		$this->namespace = "{$this->base_api_path}/{$this->version}";
+		$this->rest_base = 'publicize/shares-data';
+
+		add_action( 'rest_api_init', array( $this, 'register_routes' ) );
+	}
+
+	/**
+	 * Register the routes.
+	 */
+	public function register_routes() {
+		register_rest_route(
+			$this->namespace,
+			'/' . $this->rest_base,
+			array(
+				array(
+					'methods'             => WP_REST_Server::READABLE,
+					'callback'            => array( $this, 'get_items' ),
+					'permission_callback' => array( $this, 'get_items_permissions_check' ),
+				),
+				'schema' => array( $this, 'get_public_item_schema' ),
+			)
+		);
+	}
+
+	/**
+	 * Get Jetpack Social data.
+	 *
+	 * @param WP_REST_Request $request Full details about the request.
+	 * @return WP_REST_Response|WP_Error
+	 */
+	public function get_items( $request ) {
+		if ( Utils::is_wpcom() ) {
+			global $publicize;
+
+			return rest_ensure_response(
+				$publicize->get_publicize_shares_info( get_current_blog_id() )
+			);
+		}
+
+		$response = $this->proxy_request_to_wpcom_as_user(
+			$request
+		);
+
+		if ( is_wp_error( $response ) ) {
+			return $response;
+		}
+
+		set_transient( 'jetpack_publicize_shares_info', $response, 1 * MONTH_IN_SECONDS );
+
+		return rest_ensure_response( $response );
+	}
+
+	/**
+	 * Verify that the request has access to Jetpack Social data.
+	 *
+	 * @param WP_REST_Request $request Full details about the request.
+	 * @return true|WP_Error
+	 */
+	public function get_items_permissions_check( $request ) {// phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
+		return $this->publicize_permissions_check();
+	}
+
+	/**
+	 * Schema for the endpoint.
+	 *
+	 * @return array
+	 */
+	public function get_item_schema() {
+		$schema = array(
+			'$schema'    => 'http://json-schema.org/draft-04/schema#',
+			'title'      => 'jetpack-social-shares-data',
+			'type'       => 'object',
+			'properties' => array(
+				'publicized_count'       => array(
+					'description' => __( 'Number of shares already used', 'jetpack-publicize-pkg' ),
+					'type'        => 'integer',
+				),
+				'to_be_publicized_count' => array(
+					'description' => __( 'Number of scheduled shares', 'jetpack-publicize-pkg' ),
+					'type'        => 'integer',
+				),
+				'shared_posts_count'     => array(
+					'description' => __( 'Number of posts shared', 'jetpack-publicize-pkg' ),
+					'type'        => 'integer',
+				),
+				'is_share_limit_enabled' => array(
+					'description' => __( 'Whether the share limit is enabled', 'jetpack-publicize-pkg' ),
+					'type'        => 'boolean',
+				),
path: projects/packages/publicize/src/rest-api/class-shares-data-controller.php
user: login: manzoorwanijk
body: Can we please end the description with a full-stop?
created_at: 2025-03-18T08:06:23Z
author_association: MEMBER
diff_hunk: @@ -0,0 +1,57 @@
+import { store as coreStore } from '@wordpress/core-data';
+import { createRegistrySelector } from '@wordpress/data';
+import { getSocialScriptData } from '../../utils';
+import { SharesData } from '../types';
+
+/**
+ * Get the shares data from the store or script data
+ * @param select - Select function
+ * @return The shares data
+ */
+const getSharesData = ( select ): SharesData => {
+	const data = select( coreStore ).getEntityRecord( 'wpcom/v2', 'publicize/shares-data' );
path: projects/js-packages/publicize-components/src/social-store/selectors/shares-data.ts
user: login: manzoorwanijk
body: A minor issue here that we didn't think about is that this API call gets fired even if Publicize module is OFF and thus results in 403 error.
created_at: 2025-03-20T07:41:39Z
author_association: MEMBER

PR comments:
user: login: github-actions[bot]
created_at: 2025-03-03T17:34:45Z
body: **Thank you for your PR!**

When contributing to Jetpack, we have [a few suggestions](https://github.com/Automattic/jetpack/blob/trunk/.github/PULL_REQUEST_TEMPLATE.md) that can help us test and review your patch:<br>
- :white_check_mark: Include a description of your PR changes.<br>
- :white_check_mark: Add a "[Status]" label (In Progress, Needs Review, ...).<br>
- :white_check_mark: Add a "[Type]" label (Bug, Enhancement, Janitorial, Task).<br>
- :white_check_mark: Add testing instructions.<br>
- :white_check_mark: Specify whether this PR includes any changes to data or privacy.<br>
- :white_check_mark: Add changelog entries to affected projects<br>


This comment will be updated as you work on your PR and make changes. If you think that some of those checks are not needed for your PR, please explain why you think so. Thanks for cooperation :robot:

******

**Follow this PR Review Process:**

1. Ensure all required checks appearing at the bottom of this PR are passing.
2. Make sure to test your changes on all platforms that it applies to. *You're responsible for the quality of the code you ship*.
3. You can use [GitHub's Reviewers functionality](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/requesting-a-pull-request-review) to request a review.
4. When it's reviewed and merged, you will be pinged in Slack to deploy the changes to WordPress.com simple once the build is done.

If you have questions about anything, reach out in #jetpack-developers for guidance!
author_association: CONTRIBUTOR
user: login: github-actions[bot]
created_at: 2025-03-03T17:34:58Z
body: <!-- wpcom-reminder-comment -->
Are you an Automattician? Please test your changes on all WordPress.com environments to help mitigate accidental explosions.

- To test on WoA, go to the Plugins menu on a WoA dev site. Click on the "Upload" button and follow the upgrade flow to be able to upload, install, and activate [the Jetpack Beta plugin](https://jetpack.com/download-jetpack-beta/). Once the plugin is active, go to Jetpack > Jetpack Beta, select your plugin (Jetpack), and enable the `update/social-load-share-data-async` branch.
- To test on Simple, run the following command on your sandbox:
```
bin/jetpack-downloader test jetpack update/social-load-share-data-async
```

*Interested in more tips and information?*

- In your local development environment, use the `jetpack rsync` command to sync your changes to a WoA dev blog.
- Read more about our development workflow here: PCYsg-eg0-p2
- Figure out **when your changes will be shipped to customers** here: PCYsg-eg5-p2
author_association: CONTRIBUTOR
user: login: jp-launch-control[bot]
created_at: 2025-03-03T17:45:36Z
body: ### Code Coverage Summary

Coverage changed in 2 files.

| File | Coverage | Δ% | Δ Uncovered |
| :--- | :------: | :---: | :---: |
| projects/packages/publicize/src/class-publicize-setup.php | 0/50 (0.00%) | 0.00% | 1 ❤️‍🩹 |
| projects/js-packages/publicize-components/src/social-store/hydrate-stores.ts | 16/16 (100.00%) | 0.00% | 0 💚 |

2 files are newly checked for coverage.

| File | Coverage |
| :--- | :------: |
| projects/packages/publicize/src/rest-api/class-shares-data-controller.php | 12/54 (22.22%) 💔 |
| projects/js-packages/publicize-components/src/social-store/selectors/shares-data.ts | 17/17 (100.00%) 💚 |



[Full summary](https://jetpackcodecoverage.atomicsites.blog/prs/42187/) · [PHP report](https://jetpackcodecoverage.atomicsites.blog/prs/42187/php/) · [JS report](https://jetpackcodecoverage.atomicsites.blog/prs/42187/js/)

<sub>Coverage check overridden by https://github.com/Automattic/jetpack/labels/Coverage%20tests%20to%20be%20added%20later.</sub>
author_association: NONE