diff --git a/projects/js-packages/connection/changelog/add-skip-pricing-page-when-connecting-from-editor-blocks b/projects/js-packages/connection/changelog/add-skip-pricing-page-when-connecting-from-editor-blocks new file mode 100644 index 0000000000000..e0bf3be61bdba --- /dev/null +++ b/projects/js-packages/connection/changelog/add-skip-pricing-page-when-connecting-from-editor-blocks @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Skip pricing page when connecting via block editor diff --git a/projects/js-packages/connection/components/connect-screen/basic/index.tsx b/projects/js-packages/connection/components/connect-screen/basic/index.tsx index 0b454ba1acc11..48ebfbe4ead66 100644 --- a/projects/js-packages/connection/components/connect-screen/basic/index.tsx +++ b/projects/js-packages/connection/components/connect-screen/basic/index.tsx @@ -30,6 +30,8 @@ export type Props = { assetBaseUrl?: string; // Whether to not require a user connection and just redirect after site connection skipUserConnection?: boolean; + // Whether to skip the pricing page after the connection screen + skipPricingPage?: boolean; // Additional page elements to show after the call to action footer?: React.ReactNode; // The logo to display at the top of the component @@ -54,6 +56,7 @@ const ConnectScreen: React.FC< Props > = ( { autoTrigger, footer, skipUserConnection, + skipPricingPage, logo, } ) => { const { @@ -70,6 +73,7 @@ const ConnectScreen: React.FC< Props > = ( { autoTrigger, from, skipUserConnection, + skipPricingPage, } ); const displayButtonError = Boolean( registrationError ); diff --git a/projects/js-packages/connection/components/use-connection/index.jsx b/projects/js-packages/connection/components/use-connection/index.jsx index d77757bb3c057..cc16f2e5967fa 100644 --- a/projects/js-packages/connection/components/use-connection/index.jsx +++ b/projects/js-packages/connection/components/use-connection/index.jsx @@ -14,6 +14,7 @@ export default ( { autoTrigger, from, skipUserConnection, + skipPricingPage, } = {} ) => { const { registerSite, connectUser, refreshConnectedPlugins } = useDispatch( STORE_ID ); @@ -45,7 +46,7 @@ export default ( { */ const handleConnectUser = () => { if ( ! skipUserConnection ) { - return connectUser( { from, redirectUri } ); + return connectUser( { from, redirectUri, skipPricingPage } ); } else if ( redirectUri ) { window.location = redirectUri; return Promise.resolve( redirectUri ); diff --git a/projects/js-packages/connection/state/actions.jsx b/projects/js-packages/connection/state/actions.jsx index 4286a4b7642b9..ff077e0e49310 100644 --- a/projects/js-packages/connection/state/actions.jsx +++ b/projects/js-packages/connection/state/actions.jsx @@ -72,15 +72,16 @@ const setIsOfflineMode = isOfflineMode => { /** * Connect site with wp.com user * - * @param {object} Object - contains from and redirectFunc - * @param {string} Object.from - Value that represents the redirect origin - * @param {Function} Object.redirectFunc - A function to handle the redirect, defaults to location.assign - * @param {string} [Object.redirectUri] - A URI that the user will be redirected to + * @param {object} Object - contains from and redirectFunc + * @param {string} Object.from - Value that represents the redirect origin + * @param {Function} Object.redirectFunc - A function to handle the redirect, defaults to location.assign + * @param {string} [Object.redirectUri] - A URI that the user will be redirected to + * @param {boolean} [Object.skipPricingPage] - A flag to skip the pricing page in the connection flow * @yield {object} Action object that will be yielded */ -function* connectUser( { from, redirectFunc, redirectUri } = {} ) { +function* connectUser( { from, redirectFunc, redirectUri, skipPricingPage } = {} ) { yield setUserIsConnecting( true ); - yield { type: CONNECT_USER, from, redirectFunc, redirectUri }; + yield { type: CONNECT_USER, from, redirectFunc, redirectUri, skipPricingPage }; } /** diff --git a/projects/js-packages/connection/state/controls.jsx b/projects/js-packages/connection/state/controls.jsx index b629d1612e59e..d76726ea644a5 100644 --- a/projects/js-packages/connection/state/controls.jsx +++ b/projects/js-packages/connection/state/controls.jsx @@ -7,7 +7,7 @@ const REGISTER_SITE = ( { registrationNonce, redirectUri, from } ) => const CONNECT_USER = createRegistryControl( ( { resolveSelect } ) => - ( { from, redirectFunc, redirectUri } = {} ) => { + ( { from, redirectFunc, redirectUri, skipPricingPage } = {} ) => { return new Promise( ( resolve, reject ) => { resolveSelect( STORE_ID ) .getAuthorizationUrl( redirectUri ) @@ -16,6 +16,10 @@ const CONNECT_USER = createRegistryControl( const url = new URL( authorizationUrl ); + if ( skipPricingPage ) { + url.searchParams.set( 'skip_pricing', 'true' ); + } + if ( from ) { url.searchParams.set( 'from', encodeURIComponent( from ) ); } diff --git a/projects/packages/my-jetpack/_inc/admin.jsx b/projects/packages/my-jetpack/_inc/admin.jsx index 7ccb559796a71..b7ddc9a0b331c 100644 --- a/projects/packages/my-jetpack/_inc/admin.jsx +++ b/projects/packages/my-jetpack/_inc/admin.jsx @@ -4,7 +4,7 @@ import { ThemeProvider } from '@automattic/jetpack-components'; import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { createRoot } from '@wordpress/element'; -import React, { useEffect } from 'react'; +import { useEffect } from 'react'; import { HashRouter, Navigate, Routes, Route, useLocation } from 'react-router-dom'; /** * Internal dependencies diff --git a/projects/packages/my-jetpack/_inc/components/connection-screen/body.tsx b/projects/packages/my-jetpack/_inc/components/connection-screen/body.tsx index c46d8ed0a3740..19f8a329a544c 100644 --- a/projects/packages/my-jetpack/_inc/components/connection-screen/body.tsx +++ b/projects/packages/my-jetpack/_inc/components/connection-screen/body.tsx @@ -5,9 +5,47 @@ import { __ } from '@wordpress/i18n'; import { Icon, external } from '@wordpress/icons'; import connectImage from './connect.png'; import styles from './styles.module.scss'; -import type { Props as ConnectScreenProps } from '@automattic/jetpack-connection'; +import type { FC } from 'react'; -const ConnectionScreenBody: React.FC< ConnectScreenProps > = props => { +// This is copied from the connection package. +// The connection package main file is not TypeScript currently and therefore cannot export types. +// Doing this here to avoid editing the connection package too much as it is widely used. +interface ConnectScreenProps { + // API root + apiRoot: string; + // API nonce + apiNonce: string; + // Registration nonce + registrationNonce: string; + // The redirect admin UR + redirectUri: string; + // Additional page elements to show before the call to action + children?: React.ReactNode; + // The Title + title?: string; + // The Connect Button label + buttonLabel?: string; + // The text read by screen readers when connecting + loadingLabel?: string; + // Where the connection request is coming from + from?: string; + // Whether to initiate the connection process automatically upon rendering the component + autoTrigger?: boolean; + // Images to display on the right side + images?: string[]; + // The assets base URL + assetBaseUrl?: string; + // Whether to not require a user connection and just redirect after site connection + skipUserConnection?: boolean; + // Whether to skip the pricing page after the connection screen + skipPricingPage?: boolean; + // Additional page elements to show after the call to action + footer?: React.ReactNode; + // The logo to display at the top of the component + logo?: React.ReactNode; +} + +const ConnectionScreenBody: FC< ConnectScreenProps > = props => { const { title } = props; return ( diff --git a/projects/packages/my-jetpack/_inc/components/connection-screen/index.tsx b/projects/packages/my-jetpack/_inc/components/connection-screen/index.tsx index 8228b525e903a..52954e68d9a06 100644 --- a/projects/packages/my-jetpack/_inc/components/connection-screen/index.tsx +++ b/projects/packages/my-jetpack/_inc/components/connection-screen/index.tsx @@ -6,8 +6,9 @@ import CloseLink from '../close-link'; import ConnectionScreenBody from './body'; import ConnectionScreenFooter from './footer'; import styles from './styles.module.scss'; +import type { FC } from 'react'; -const ConnectionScreen: React.FC = () => { +const ConnectionScreen: FC = () => { const returnToPage = useMyJetpackReturnToPage(); const { apiRoot, apiNonce, registrationNonce } = useMyJetpackConnection(); diff --git a/projects/packages/my-jetpack/changelog/add-skip-pricing-page-when-connecting-from-editor-blocks b/projects/packages/my-jetpack/changelog/add-skip-pricing-page-when-connecting-from-editor-blocks new file mode 100644 index 0000000000000..e0bf3be61bdba --- /dev/null +++ b/projects/packages/my-jetpack/changelog/add-skip-pricing-page-when-connecting-from-editor-blocks @@ -0,0 +1,4 @@ +Significance: patch +Type: changed + +Skip pricing page when connecting via block editor diff --git a/projects/plugins/jetpack/changelog/add-skip-pricing-page-when-connecting-from-editor-blocks b/projects/plugins/jetpack/changelog/add-skip-pricing-page-when-connecting-from-editor-blocks new file mode 100644 index 0000000000000..fab417549855a --- /dev/null +++ b/projects/plugins/jetpack/changelog/add-skip-pricing-page-when-connecting-from-editor-blocks @@ -0,0 +1,4 @@ +Significance: patch +Type: other + +Skip pricing page when connecting from editor blocks diff --git a/projects/plugins/jetpack/extensions/shared/components/connect-banner/index.tsx b/projects/plugins/jetpack/extensions/shared/components/connect-banner/index.tsx index 547cb632ffa5d..8af620f3857b9 100644 --- a/projects/plugins/jetpack/extensions/shared/components/connect-banner/index.tsx +++ b/projects/plugins/jetpack/extensions/shared/components/connect-banner/index.tsx @@ -1,8 +1,10 @@ /* * External dependencies */ +import { useConnection } from '@automattic/jetpack-connection'; import { useAnalytics } from '@automattic/jetpack-shared-extension-utils'; import { __ } from '@wordpress/i18n'; +import { useState, useEffect } from 'react'; /* * Internal dependencies */ @@ -17,25 +19,52 @@ interface ConnectBannerProps { import './style.scss'; +const getRedirectUri = () => { + const pathname = window?.location?.pathname.replace( 'wp-admin/', '' ); + const search = window?.location?.search; + + return `${ pathname }${ search }`; +}; + const ConnectBanner: FC< ConnectBannerProps > = ( { block, explanation = null } ) => { - const checkoutUrl = `${ window?.Jetpack_Editor_Initial_State?.adminUrl }admin.php?page=my-jetpack#/connection`; - const { autosaveAndRedirect, isRedirecting } = useAutosaveAndRedirect( checkoutUrl ); + const [ isReadyToRedirect, setIsReadyToRedirect ] = useState( false ); + const [ isSaving, setIsSaving ] = useState( false ); + const { handleConnectUser } = useConnection( { + from: 'editor', + redirectUri: getRedirectUri(), + autoTrigger: false, + skipPricingPage: true, + } ); + const { autosave } = useAutosaveAndRedirect(); const { tracks } = useAnalytics(); - const goToCheckoutPage = ( event: MouseEvent< HTMLButtonElement > ) => { - tracks.recordEvent( 'jetpack_editor_connect_banner_click', { block } ); - autosaveAndRedirect( event ); + const goToConnectPage = ( event: MouseEvent< HTMLButtonElement > ) => { + setIsSaving( true ); + autosave( event ).then( () => { + tracks.recordEvent( 'jetpack_editor_connect_banner_click', { block } ); + setIsReadyToRedirect( true ); + setIsSaving( false ); + } ); }; + useEffect( () => { + // The redirection is handled this way to ensure we get the right redirectUri + // In the case that the post is new and unsaved, the component requires a re-render + // in order to get the correct URI when the handler is called. + if ( isReadyToRedirect ) { + handleConnectUser(); + } + }, [ isReadyToRedirect, handleConnectUser ] ); + return (
{ explanation && (