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 + ) } ); };