Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Launch Site: implement direct site launch from the WP Admin masterbar button via an ExPlat experiment, with a shared mutation hook and celebration modal for use across launch entry points.
3 changes: 2 additions & 1 deletion projects/packages/explat/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
"@wordpress/api-fetch": "7.42.0",
"@wordpress/url": "4.42.0",
"cookie": "1.0.2",
"debug": "4.4.3"
"debug": "4.4.3",
"wpcom-proxy-request": "^7.0.7"
},
"devDependencies": {
"@automattic/jetpack-webpack-config": "workspace:*",
Expand Down
11 changes: 8 additions & 3 deletions projects/packages/explat/src/class-rest-controller.php
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,19 @@ public function get_assignments( $request ) {
$platform = $request->get_param( 'platform' );
$request_path = '/experiments/' . self::EXPLAT_API_VERSION . '/assignments/' . $platform;
$args = array(
'experiment_name' => $request['experiment_name'],
'anon_id' => $request['anon_id'],
'experiment_names' => $request['experiment_name'],
'anon_id' => $request['anon_id'],
);

if ( $request['as_connected_user'] && $is_user_connected ) {
$response = Client::wpcom_json_api_request_as_user(
add_query_arg( $args, $request_path ),
'v2'
'v2',
array(
'headers' => array(
'User-Agent' => 'Jetpack MU WPCOM Plugin Experiment Assignment',
),
)
);
} else {
$response = wp_remote_get(
Expand Down
40 changes: 19 additions & 21 deletions projects/packages/explat/src/client/assignment.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import apiFetch from '@wordpress/api-fetch';
import { addQueryArgs } from '@wordpress/url';
import debugFactory from 'debug';

const debug = debugFactory( 'jetpack-explat:client:assignment' );
import wpcomRequest, { canAccessWpcomApis } from 'wpcom-proxy-request';

const fetchExperimentAssignment =
( asConnectedUser = false ) =>
Expand All @@ -13,24 +11,24 @@ const fetchExperimentAssignment =
experimentName: string;
anonId: string | null;
} ): Promise< unknown > => {
if ( ! anonId ) {
debug( 'anonId is null' );
throw new Error( `Tracking is disabled, can't fetch experimentAssignment` );
}

const params = {
experiment_name: experimentName,
anon_id: anonId ?? undefined,
as_connected_user: asConnectedUser,
};

debug( 'params', params );

const assignmentsRequestUrl = addQueryArgs( 'jetpack/v4/explat/assignments', params );

debug( 'assignmentsRequestUrl', assignmentsRequestUrl );

return await apiFetch( { path: assignmentsRequestUrl } );
const platform = experimentName.split( '_' )[ 0 ];

return canAccessWpcomApis()
? wpcomRequest( {
path: addQueryArgs( `/experiments/0.1.0/assignments/${ platform }`, {
experiment_names: experimentName,
anon_id: anonId ?? undefined,
} ),
apiNamespace: 'wpcom/v2',
} )
: apiFetch( {
path: addQueryArgs( 'jetpack/v4/explat/assignments', {
experiment_name: experimentName,
anon_id: anonId ?? undefined,
as_connected_user: asConnectedUser,
platform,
} ),
} );
};

export const fetchExperimentAssignmentAnonymously = fetchExperimentAssignment( false );
Expand Down
7 changes: 6 additions & 1 deletion projects/packages/explat/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,10 @@
"extends": "jetpack-js-tools/tsconfig.base.json",
// List all sources and source-containing subdirs.
"include": [ "./src/client/**/*" ],
"files": [ "./global.d.ts" ]
"files": [ "./global.d.ts" ],
"compilerOptions": {
"typeRoots": [ "./node_modules/@types/", "src" ],
"sourceMap": true,
"outDir": "./build/"
}
}
11 changes: 7 additions & 4 deletions projects/packages/explat/webpack.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ const jetpackWebpackConfig = require( '@automattic/jetpack-webpack-config/webpac

module.exports = [
{
entry: {
index: './src/client/index.ts',
},
entry: './src/client/index.ts',
mode: jetpackWebpackConfig.mode,
devtool: jetpackWebpackConfig.devtool,
output: {
Expand All @@ -19,7 +17,12 @@ module.exports = [
...jetpackWebpackConfig.resolve,
},
node: false,
plugins: [ ...jetpackWebpackConfig.StandardPlugins() ],
plugins: [
...jetpackWebpackConfig.StandardPlugins( {
// Generate `.d.ts` files per tsconfig settings.
ForkTSCheckerPlugin: {},
} ),
],
module: {
strictExportPresence: true,
rules: [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Significance: patch
Type: changed

Launch Site: implement direct site launch from the WP Admin masterbar button via an ExPlat experiment, with a shared mutation hook and celebration modal for use across launch entry points.
2 changes: 2 additions & 0 deletions projects/packages/jetpack-mu-wpcom/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
"@automattic/i18n-utils": "1.2.3",
"@automattic/jetpack-base-styles": "workspace:*",
"@automattic/jetpack-components": "workspace:*",
"@automattic/jetpack-explat": "workspace:*",
"@automattic/jetpack-script-data": "workspace:*",
"@automattic/jetpack-shared-extension-utils": "workspace:*",
"@automattic/launchpad": "1.1.5",
"@automattic/typography": "1.0.0",
Expand Down
17 changes: 17 additions & 0 deletions projects/packages/jetpack-mu-wpcom/src/class-jetpack-mu-wpcom.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ public static function init() {
// Filter to ensure JetpackScriptData.site.host and is_wpcom_platform is set, to ensure Jetpack blocks work as expected via P2.
add_filter( 'jetpack_public_js_script_data', array( __CLASS__, 'add_jetpack_script_data_for_p2' ), 10, 1 );

// Filter to populate JetpackScriptData.site.wpcom.blog_id with the actual WP.com blog ID.
add_filter( 'jetpack_admin_js_script_data', array( __CLASS__, 'set_wpcom_blog_id_script_data' ), 10, 1 );

/**
* Runs right after the Jetpack_Mu_Wpcom package is initialized.
*
Expand Down Expand Up @@ -747,6 +750,20 @@ public static function load_social_links() {
}
}

/**
* Populate JetpackScriptData.site.wpcom.blog_id with the actual WP.com blog ID.
*
* @param array $data The script data.
* @return array
*/
public static function set_wpcom_blog_id_script_data( $data ) {
$blog_id = get_wpcom_blog_id();
if ( $blog_id ) {
$data['site']['wpcom']['blog_id'] = $blog_id;
}
return $data;
}

/**
* Add Jetpack script data with host information on P2
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import { useCopyToClipboard } from '@wordpress/compose';
import { useState, useEffect, createInterpolateElement } from '@wordpress/element';
import { __ } from '@wordpress/i18n';
import { Icon, copy } from '@wordpress/icons';
import { wpcomTrackEvent } from '../../../common/tracks';
import ConfettiAnimation from './confetti-animation';
import { wpcomTrackEvent } from './tracks';

import './celebrate-launch-modal.scss';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export { default as useCanvasMode } from './use-canvas-mode';
export { default as useLaunchSiteMutation } from './use-launch-site-mutation';
export { default as useLocation } from './use-location';
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { getSiteData } from '@automattic/jetpack-script-data';
import { useMutation } from '@tanstack/react-query';
import apiFetch from '@wordpress/api-fetch';
import wpcomRequest, { canAccessWpcomApis } from 'wpcom-proxy-request';

const useLaunchSiteMutation = ( onSuccess: () => void ) => {
const siteData = getSiteData();

return useMutation( {
mutationFn: () => {
if ( canAccessWpcomApis() ) {
return wpcomRequest( {
path: `/sites/${ siteData.wpcom.blog_id }/launch`,
apiVersion: '1.1',
method: 'POST',
} );
}
return apiFetch( {
path: '/wpcom/v2/launch-site',
method: 'POST',
} );
},
onSuccess,
} );
};

export default useLaunchSiteMutation;
Original file line number Diff line number Diff line change
@@ -1,11 +1,30 @@
import { wpcomTrackEvent } from '../../common/tracks';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { createRoot } from 'react-dom/client';
import { LaunchButton } from './launch-button';

document.addEventListener( 'DOMContentLoaded', () => {
const queryClient = new QueryClient();

/**
* Renders the launch button.
* @return {Promise<void>}
*/
async function renderLaunchButton() {
const launchButton = document.querySelector( '#wpadminbar .launch-site' );
if ( ! launchButton ) {
return;
}
launchButton.addEventListener( 'click', () => {
wpcomTrackEvent( 'wpcom_adminbar_launch_site' );
} );
} );

const root = createRoot( launchButton );
root.render(
<QueryClientProvider client={ queryClient }>
<LaunchButton
onCelebrationModalClose={ () => {
root.unmount();
launchButton.remove();
} }
/>
</QueryClientProvider>
);
}

document.addEventListener( 'DOMContentLoaded', renderLaunchButton, { once: true } );
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,13 @@ function wpcom_add_launch_button_to_admin_bar( WP_Admin_Bar $admin_bar ) {
* Enqueue the necessary styles for the admin bar button.
*/
function wpcom_enqueue_launch_button_styles() {
if ( ! current_user_can( 'manage_options' ) ) {
// phpcs:ignore WordPress.Security.NonceVerification.Recommended -- This is a context check, not a form submission.
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were rendering the masterbar while loading the preview and that was assigning the user unintentionally. This snippet prevents loading the scripts twice.

$is_preview = isset( $_GET['preview'] ) && 'true' === sanitize_text_field( wp_unslash( $_GET['preview'] ) );

if ( ! current_user_can( 'manage_options' ) || $is_preview ) {
return;
}

$version = filemtime( __DIR__ . '/style.css' );
wp_enqueue_style( 'launch-banner', plugins_url( 'style.css', __FILE__ ), array(), $version );
$asset_file = include Jetpack_Mu_Wpcom::BASE_DIR . 'build/adminbar-launch-button/adminbar-launch-button.asset.php';
Expand All @@ -94,6 +98,26 @@ function wpcom_enqueue_launch_button_styles() {
$asset_file['version'] ?? filemtime( Jetpack_Mu_Wpcom::BASE_DIR . 'build/adminbar-launch-button/adminbar-launch-button.js' ),
true
);

$bundles = function_exists( 'wpcom_get_site_purchases' ) ? wp_list_filter( wpcom_get_site_purchases(), array( 'product_type' => 'bundle' ) ) : array();
$current_plan = array_pop( $bundles );

$launch_button_data = wp_json_encode(
array(
'siteUrl' => home_url(),
'siteDomain' => wp_parse_url( home_url(), PHP_URL_HOST ),
'sitePlan' => $current_plan,
'hasCustomDomain' => function_exists( 'wpcom_site_has_feature' ) && wpcom_site_has_feature( 'custom-domain' ),
),
JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP
);

wp_add_inline_script(
'adminbar-launch-button',
"var JETPACK_LAUNCH_BUTTON_DATA = $launch_button_data;",
'before'
);

Common\wpcom_enqueue_tracking_scripts( 'adminbar-launch-button' );
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import { useExperimentWithAuth } from '@automattic/jetpack-explat';
import { getSiteData } from '@automattic/jetpack-script-data';
import { __ } from '@wordpress/i18n';
import { addQueryArgs } from '@wordpress/url';
import { useState } from 'react';
import CelebrateLaunchModal from '../../common/celebrate-launch-modal';
import { useLaunchSiteMutation } from '../../common/hooks';
import { wpcomTrackEvent } from '../../common/tracks';

const icon = (
<svg viewBox="0 0 15 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
fillRule="evenodd"
clipRule="evenodd"
d="M10.6242 9.74354L7.62419 12.1261V13.2995C7.62419 13.4418 7.77653 13.5322 7.90147 13.4641L10.5265 12.0322C10.5867 11.9994 10.6242 11.9363 10.6242 11.8676V9.74354ZM6.49919 12.0875L3.91203 9.50037H2.7001C1.70383 9.50037 1.07079 8.43399 1.54786 7.55937L2.97968 4.93437C3.20967 4.51272 3.65161 4.25036 4.13191 4.25036H7.17569C9.1325 2.16798 11.3176 0.754637 14.1427 0.531305C14.9004 0.471402 15.5282 1.09911 15.4682 1.85687C15.2449 4.68199 13.8316 6.86706 11.7492 8.82386V11.8676C11.7492 12.3479 11.4868 12.7899 11.0652 13.0199L8.44018 14.4517C7.56557 14.9288 6.49919 14.2957 6.49919 13.2995V12.0875ZM6.25602 5.37536H4.13191C4.0633 5.37536 4.00017 5.41284 3.96731 5.47308L2.53549 8.09808C2.46734 8.22303 2.55777 8.37536 2.7001 8.37536H3.87344L6.25602 5.37536Z"
/>
<path d="M0.498047 13.3962C0.498047 12.2341 1.44011 11.2921 2.60221 11.2921C3.76431 11.2921 4.70638 12.2341 4.70638 13.3962C4.70638 14.5583 3.76431 15.5004 2.60221 15.5004H1.06055C0.749887 15.5004 0.498047 15.2486 0.498047 14.9379V13.3962Z" />
</svg>
);

const Content = () => (
<>
<span className="ab-icon">{ icon }</span>
<span className="ab-label">{ __( 'Launch site', 'jetpack-mu-wpcom' ) }</span>
</>
);

const launchButtonData = typeof window === 'object' ? window.JETPACK_LAUNCH_BUTTON_DATA : {};

/**
* The LaunchButton component.
* @param {object} props - Props
* @param {Function} props.onCelebrationModalClose - Callback on celebration modal close.
* @return {React.ReactNode} The LaunchButton component.
*/
export function LaunchButton( { onCelebrationModalClose } ) {
const [ , data ] = useExperimentWithAuth( 'calypso_standardized_site_launch_gating' );
const [ showCelebrateLaunchModal, setShowCelebrateLaunchModal ] = useState( false );

const siteData = getSiteData();

const { mutate: launchSite } = useLaunchSiteMutation( () => setShowCelebrateLaunchModal( true ) );

// Default experience. Markup should match what's coming from the back-end.
if ( ! data || data.variationName !== 'ungated_site_launch' ) {
// Maybe this data can come from the server.
const launchUrl = addQueryArgs( 'https://wordpress.com/start/launch-site', {
siteSlug: siteData.suffix,
ref: 'wp-admin',
} );

return (
<a
className="ab-item"
role="menuitem"
href={ launchUrl }
onClick={ () => wpcomTrackEvent( 'wpcom_adminbar_launch_site' ) }
>
<Content />
</a>
);
}

const handleLaunchClick = e => {
e.preventDefault();
wpcomTrackEvent( 'wpcom_adminbar_launch_site' );
launchSite();
};

return (
<>
<a className="ab-item" role="menuitem" href="#" onClick={ handleLaunchClick }>
<Content />
</a>
{ showCelebrateLaunchModal && (
<CelebrateLaunchModal { ...launchButtonData } onRequestClose={ onCelebrationModalClose } />
) }
</>
);
}
Loading
Loading