diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8daa505250d91..8e6863fcca3d4 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -885,6 +885,9 @@ importers:
'@automattic/jetpack-shared-extension-utils':
specifier: workspace:*
version: link:../shared-extension-utils
+ '@automattic/popup-monitor':
+ specifier: 1.0.2
+ version: 1.0.2
'@automattic/social-previews':
specifier: 2.0.1-beta.13
version: 2.0.1-beta.13(@babel/runtime@7.24.0)(@types/react@18.3.1)(react-dom@18.2.0)(react@18.2.0)
diff --git a/projects/js-packages/publicize-components/index.ts b/projects/js-packages/publicize-components/index.ts
index 182382810cd64..7cd6a740997c4 100644
--- a/projects/js-packages/publicize-components/index.ts
+++ b/projects/js-packages/publicize-components/index.ts
@@ -5,6 +5,7 @@ import './src/social-store';
export { default as Connection } from './src/components/connection';
export { default as ConnectionVerify } from './src/components/connection-verify';
+export { default as ConnectButton } from './src/components/connect-button';
export { default as Form } from './src/components/form';
export { default as SocialPreviewsModal } from './src/components/social-previews/modal';
export { default as SocialPreviewsPanel } from './src/components/social-previews/panel';
diff --git a/projects/js-packages/publicize-components/package.json b/projects/js-packages/publicize-components/package.json
index 762b8e6ff5d11..b3923cf3ad81a 100644
--- a/projects/js-packages/publicize-components/package.json
+++ b/projects/js-packages/publicize-components/package.json
@@ -22,6 +22,7 @@
"@automattic/jetpack-components": "workspace:*",
"@automattic/jetpack-connection": "workspace:*",
"@automattic/jetpack-shared-extension-utils": "workspace:*",
+ "@automattic/popup-monitor": "1.0.2",
"@automattic/social-previews": "2.0.1-beta.13",
"@wordpress/annotations": "2.56.0",
"@wordpress/api-fetch": "6.53.0",
diff --git a/projects/js-packages/publicize-components/src/components/connect-button/index.js b/projects/js-packages/publicize-components/src/components/connect-button/index.js
new file mode 100644
index 0000000000000..14ef99b742943
--- /dev/null
+++ b/projects/js-packages/publicize-components/src/components/connect-button/index.js
@@ -0,0 +1,18 @@
+import { Button } from '@automattic/jetpack-components';
+import { useCallback } from '@wordpress/element';
+import { __ } from '@wordpress/i18n';
+import { requestExternalAccess } from '../../utils/request-external-access.js';
+
+const ConnectButton = ( { connectUrl, onClose, className } ) => {
+ const requestConnection = useCallback(
+ () => requestExternalAccess( connectUrl, onClose ),
+ [ connectUrl, onClose ]
+ );
+ return (
+
+ );
+};
+
+export default ConnectButton;
diff --git a/projects/js-packages/publicize-components/src/social-store/selectors/connection-data.js b/projects/js-packages/publicize-components/src/social-store/selectors/connection-data.js
index 026433b35f531..c3d21d4b5340e 100644
--- a/projects/js-packages/publicize-components/src/social-store/selectors/connection-data.js
+++ b/projects/js-packages/publicize-components/src/social-store/selectors/connection-data.js
@@ -106,3 +106,14 @@ export function getConnectionProfileDetails( state, service, { forceDefaults = f
return { displayName, profileImage, username };
}
+
+/**
+ * Returns the services list from the store.
+ *
+ * @param {import("../types").SocialStoreState} state - State object.
+ *
+ * @returns {Array} The services list
+ */
+export function getServices( state ) {
+ return state.connectionData?.services ?? [];
+}
diff --git a/projects/js-packages/publicize-components/src/utils/index.js b/projects/js-packages/publicize-components/src/utils/index.js
index 490673ea42d3d..65f2dd163c5c5 100644
--- a/projects/js-packages/publicize-components/src/utils/index.js
+++ b/projects/js-packages/publicize-components/src/utils/index.js
@@ -1,3 +1,4 @@
export * from './get-share-message-max-length';
export * from './get-supported-additional-connections';
+export * from './request-external-access';
export * from './types';
diff --git a/projects/js-packages/publicize-components/src/utils/request-external-access.js b/projects/js-packages/publicize-components/src/utils/request-external-access.js
new file mode 100644
index 0000000000000..e1ed72812d21c
--- /dev/null
+++ b/projects/js-packages/publicize-components/src/utils/request-external-access.js
@@ -0,0 +1,34 @@
+import PopupMonitor from '@automattic/popup-monitor';
+
+/**
+ * The callback function of the requestExternalAccess utility.
+ * @callback requestCallback
+ * @param {object} result - Received authentication data.
+ * @param {number} result.keyring_id
+ * @param {string} result.id_token
+ * @param {object} result.user
+ */
+
+/**
+ * Utility for requesting authorization of sharing services.
+ * @param {string} url - The URL to be loaded in the newly opened window.
+ * @param {requestCallback} cb - The callback that handles the response.
+ */
+export const requestExternalAccess = ( url, cb ) => {
+ const popupMonitor = new PopupMonitor();
+ let lastMessage;
+
+ popupMonitor.open(
+ url,
+ null,
+ 'toolbar=0,location=0,status=0,menubar=0,' + popupMonitor.getScreenCenterSpecs( 780, 700 )
+ );
+
+ popupMonitor.once( 'close', () => {
+ cb( lastMessage?.ID ? lastMessage : {} );
+ } );
+
+ popupMonitor.on( 'message', message => {
+ lastMessage = message?.data;
+ } );
+};
diff --git a/projects/plugins/social/src/class-jetpack-social.php b/projects/plugins/social/src/class-jetpack-social.php
index ebacfa738ae1d..0bf9b01a91a6f 100644
--- a/projects/plugins/social/src/class-jetpack-social.php
+++ b/projects/plugins/social/src/class-jetpack-social.php
@@ -11,6 +11,7 @@
use Automattic\Jetpack\Admin_UI\Admin_Menu;
use Automattic\Jetpack\Assets;
+use Automattic\Jetpack\Connection\Client;
use Automattic\Jetpack\Connection\Initial_State as Connection_Initial_State;
use Automattic\Jetpack\Connection\Manager as Connection_Manager;
use Automattic\Jetpack\Connection\Rest_Authentication as Connection_Rest_Authentication;
@@ -256,6 +257,7 @@ public function initial_state() {
'connectionData' => array(
'connections' => $publicize->get_all_connections_for_user(), // TODO: Sanitize the array
'adminUrl' => esc_url_raw( $publicize->publicize_connections_url( 'jetpack-social-connections-admin-page' ) ),
+ 'services' => $this->get_services(),
),
'sharesData' => $publicize->get_publicize_shares_info( Jetpack_Options::get_option( 'id' ) ),
),
@@ -266,6 +268,20 @@ public function initial_state() {
return $state;
}
+ public function get_services() {
+ $site_id = Connection_Manager::get_site_id();
+ if ( is_wp_error( $site_id ) ) {
+ return [];
+ }
+ $path = sprintf( '/sites/%d/external-services', $site_id );
+ $response = Client::wpcom_json_api_request_as_user( $path );
+ if ( is_wp_error( $response ) ) {
+ return [];
+ }
+ $body = json_decode( wp_remote_retrieve_body( $response ) );
+ return array_values( (array) $body->services ) ?? [];
+ }
+
/**
* Returns a boolean as to whether we have a plan that supports
* sharing beyond the free limit.
diff --git a/projects/plugins/social/src/js/components/social-module-toggle/index.tsx b/projects/plugins/social/src/js/components/social-module-toggle/index.tsx
index 91752cb1b01b1..84e0245ba9f1e 100644
--- a/projects/plugins/social/src/js/components/social-module-toggle/index.tsx
+++ b/projects/plugins/social/src/js/components/social-module-toggle/index.tsx
@@ -1,5 +1,5 @@
import { Button, Text, useBreakpointMatch } from '@automattic/jetpack-components';
-import { SOCIAL_STORE_ID } from '@automattic/jetpack-publicize-components';
+import { SOCIAL_STORE_ID, ConnectButton } from '@automattic/jetpack-publicize-components';
import { ExternalLink } from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
@@ -9,12 +9,13 @@ import { SocialStoreSelectors } from '../types/types';
import styles from './styles.module.scss';
const SocialModuleToggle: React.FC = () => {
- const { connectionsAdminUrl, isModuleEnabled, isUpdating } = useSelect( select => {
+ const { connectionsAdminUrl, isModuleEnabled, isUpdating, services } = useSelect( select => {
const store = select( SOCIAL_STORE_ID ) as SocialStoreSelectors;
return {
isModuleEnabled: store.isModuleEnabled(),
isUpdating: store.isUpdatingJetpackSettings(),
connectionsAdminUrl: store.getConnectionsAdminUrl(),
+ services: store.getServices(),
};
}, [] );
@@ -59,6 +60,16 @@ const SocialModuleToggle: React.FC = () => {
{ __( 'Manage social media connections', 'jetpack-social' ) }
) }
+ { services.map( service =>
+ service.type === 'publicize' ? (
+ console.log( res ) }
+ className={ styles.button }
+ key={ service.ID }
+ />
+ ) : null
+ ) }
);
};