Replace "Personalize Your Store" Task with "Choose Your Theme" (#40239)

* Update appearance task to choose a WP theme

* Add changelog

* Update redirect URL

Redirects to the main theme screen.

* Update plugins/woocommerce/src/Admin/Features/OnboardingTasks/Tasks/Appearance.php

Co-authored-by: Ilyas Foo <foo.ilyas@gmail.com>

* Wrap URL in getAdminLink

* Update position of task

* Change title

* Change redirect URL to be the site editor

* Revert to original task name

* Use inherited is_complete function

* Use PHP-based action tracking

* Fix lint issue

---------

Co-authored-by: Ilyas Foo <foo.ilyas@gmail.com>
This commit is contained in:
Adrian Duffell 2023-09-21 00:25:35 +08:00 committed by GitHub
parent 5bc0789921
commit 76b8042359
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 40 additions and 496 deletions

View File

@ -1,431 +1,36 @@
/**
* External dependencies
*/
import { __ } from '@wordpress/i18n';
import apiFetch from '@wordpress/api-fetch';
import { Button, Card, CardBody } from '@wordpress/components';
import { Component, Fragment } from '@wordpress/element';
import { compose } from '@wordpress/compose';
import { filter } from 'lodash';
import { withDispatch, withSelect } from '@wordpress/data';
import { Stepper, TextControl, ImageUpload } from '@woocommerce/components';
import {
OPTIONS_STORE_NAME,
ONBOARDING_STORE_NAME,
WC_ADMIN_NAMESPACE,
} from '@woocommerce/data';
import { queueRecordEvent, recordEvent } from '@woocommerce/tracks';
import React from 'react';
import { WooOnboardingTaskListItem } from '@woocommerce/onboarding';
import { registerPlugin } from '@wordpress/plugins';
import { WooOnboardingTask } from '@woocommerce/onboarding';
import { getAdminLink } from '@woocommerce/settings';
/**
* Internal dependencies
*/
class Appearance extends Component {
constructor( props ) {
super( props );
const { hasHomepage, hasProducts, supportCustomLogo } =
props.task.additionalData;
this.stepVisibility = {
homepage: ! hasHomepage,
import: ! hasProducts,
logo: supportCustomLogo,
};
this.state = {
isDirty: false,
isPending: false,
logo: null,
stepIndex: 0,
isUpdatingLogo: false,
isUpdatingNotice: false,
storeNoticeText: props.demoStoreNotice || '',
};
this.completeStep = this.completeStep.bind( this );
this.createHomepage = this.createHomepage.bind( this );
this.importProducts = this.importProducts.bind( this );
this.updateLogo = this.updateLogo.bind( this );
this.updateNotice = this.updateNotice.bind( this );
}
componentDidMount() {
const { themeMods } = this.props.task.additionalData;
if ( themeMods && themeMods.custom_logo ) {
/* eslint-disable react/no-did-mount-set-state */
this.setState( { logo: { id: themeMods.custom_logo } } );
/* eslint-enable react/no-did-mount-set-state */
}
}
componentDidUpdate( prevProps ) {
const { isPending, logo } = this.state;
const { demoStoreNotice } = this.props;
if ( logo && ! logo.url && ! isPending ) {
/* eslint-disable react/no-did-update-set-state */
this.setState( { isPending: true } );
wp.media
.attachment( logo.id )
.fetch()
.then( () => {
const logoUrl = wp.media.attachment( logo.id ).get( 'url' );
this.setState( {
isPending: false,
logo: { id: logo.id, url: logoUrl },
} );
} );
/* eslint-enable react/no-did-update-set-state */
}
if (
demoStoreNotice &&
prevProps.demoStoreNotice !== demoStoreNotice
) {
/* eslint-disable react/no-did-update-set-state */
this.setState( {
storeNoticeText: demoStoreNotice,
} );
/* eslint-enable react/no-did-update-set-state */
}
}
async completeStep() {
const { stepIndex } = this.state;
const { actionTask, onComplete } = this.props;
const nextStep = this.getSteps()[ stepIndex + 1 ];
if ( nextStep ) {
this.setState( { stepIndex: stepIndex + 1 } );
} else {
this.setState( { isPending: true } );
await actionTask( 'appearance' );
onComplete();
}
}
importProducts() {
const { createNotice } = this.props;
this.setState( { isPending: true } );
recordEvent( 'tasklist_appearance_import_demo', {} );
apiFetch( {
path: `${ WC_ADMIN_NAMESPACE }/onboarding/tasks/import_sample_products`,
method: 'POST',
} )
.then( ( result ) => {
if ( result.failed && result.failed.length ) {
createNotice(
'error',
__(
'There was an error importing some of the sample products',
'woocommerce'
)
);
} else {
createNotice(
'success',
__(
'All sample products have been imported',
'woocommerce'
)
);
}
this.setState( { isPending: false } );
this.completeStep();
} )
.catch( ( { message } ) => {
createNotice(
'error',
message ||
__(
'There was an error importing the sample products',
'woocommerce'
),
{ __unstableHTML: true }
);
this.setState( { isPending: false } );
} );
}
createHomepage() {
const { createNotice } = this.props;
this.setState( { isPending: true } );
recordEvent( 'tasklist_appearance_create_homepage', {
create_homepage: true,
} );
apiFetch( {
path: '/wc-admin/onboarding/tasks/create_homepage',
method: 'POST',
} )
.then( ( response ) => {
createNotice( response.status, response.message, {
actions: response.edit_post_link
? [
{
label: __( 'Customize', 'woocommerce' ),
onClick: () => {
queueRecordEvent(
'tasklist_appearance_customize_homepage',
{}
);
window.location = `${ response.edit_post_link }&wc_onboarding_active_task=appearance`;
},
},
]
: null,
} );
this.setState( { isPending: false } );
this.completeStep();
} )
.catch( ( error ) => {
createNotice( 'error', error.message );
this.setState( { isPending: false } );
} );
}
async updateLogo() {
const { createNotice, task, updateOptions } = this.props;
const { stylesheet, themeMods } = task.additionalData;
const { logo } = this.state;
const updatedThemeMods = {
...themeMods,
custom_logo: logo ? logo.id : null,
};
recordEvent( 'tasklist_appearance_upload_logo' );
this.setState( { isUpdatingLogo: true } );
const update = await updateOptions( {
[ `theme_mods_${ stylesheet }` ]: updatedThemeMods,
} );
if ( update.success ) {
this.setState( { isUpdatingLogo: false } );
createNotice(
'success',
__( 'Store logo updated successfully', 'woocommerce' )
);
this.completeStep();
} else {
createNotice( 'error', update.message );
}
}
async updateNotice() {
const { createNotice, updateOptions } = this.props;
const { storeNoticeText } = this.state;
recordEvent( 'tasklist_appearance_set_store_notice', {
added_text: Boolean( storeNoticeText.length ),
} );
this.setState( { isUpdatingNotice: true } );
const update = await updateOptions( {
woocommerce_demo_store: storeNoticeText.length ? 'yes' : 'no',
woocommerce_demo_store_notice: storeNoticeText,
} );
if ( update.success ) {
this.setState( { isUpdatingNotice: false } );
createNotice(
'success',
__(
"🎨 Your store is looking great! Don't forget to continue personalizing it",
'woocommerce'
)
);
this.completeStep();
} else {
createNotice( 'error', update.message );
}
}
getSteps() {
const { isDirty, isPending, logo, storeNoticeText, isUpdatingLogo } =
this.state;
const steps = [
{
key: 'import',
label: __( 'Import sample products', 'woocommerce' ),
description: __(
'Well add some products that will make it easier to see what your store looks like',
'woocommerce'
),
content: (
<Fragment>
<Button
onClick={ this.importProducts }
isBusy={ isPending }
isPrimary
>
{ __( 'Import products', 'woocommerce' ) }
</Button>
<Button onClick={ () => this.completeStep() }>
{ __( 'Skip', 'woocommerce' ) }
</Button>
</Fragment>
),
visible: this.stepVisibility.import,
},
{
key: 'homepage',
label: __( 'Create a custom homepage', 'woocommerce' ),
description: __(
'Create a new homepage and customize it to suit your needs',
'woocommerce'
),
content: (
<Fragment>
<Button
isPrimary
isBusy={ isPending }
onClick={ this.createHomepage }
>
{ __( 'Create homepage', 'woocommerce' ) }
</Button>
<Button
isTertiary
onClick={ () => {
recordEvent(
'tasklist_appearance_create_homepage',
{ create_homepage: false }
);
this.completeStep();
} }
>
{ __( 'Skip', 'woocommerce' ) }
</Button>
</Fragment>
),
visible: this.stepVisibility.homepage,
},
{
key: 'logo',
label: __( 'Upload a logo', 'woocommerce' ),
description: __(
'Ensure your store is on-brand by adding your logo',
'woocommerce'
),
content: isPending ? null : (
<Fragment>
<ImageUpload
image={ logo }
onChange={ ( image ) =>
this.setState( { isDirty: true, logo: image } )
}
/>
<Button
disabled={ ! logo && ! isDirty }
onClick={ this.updateLogo }
isBusy={ isUpdatingLogo }
isPrimary
>
{ __( 'Continue', 'woocommerce' ) }
</Button>
<Button
isTertiary
onClick={ () => this.completeStep() }
>
{ __( 'Skip', 'woocommerce' ) }
</Button>
</Fragment>
),
visible: this.stepVisibility.logo,
},
{
key: 'notice',
label: __( 'Set a store notice', 'woocommerce' ),
description: __(
'Optionally display a prominent notice across all pages of your store',
'woocommerce'
),
content: (
<Fragment>
<TextControl
label={ __( 'Store notice text', 'woocommerce' ) }
placeholder={ __(
'Store notice text',
'woocommerce'
) }
value={ storeNoticeText }
onChange={ ( value ) =>
this.setState( { storeNoticeText: value } )
}
/>
<Button onClick={ this.updateNotice } isPrimary>
{ __( 'Complete task', 'woocommerce' ) }
</Button>
</Fragment>
),
visible: true,
},
];
return filter( steps, ( step ) => step.visible );
}
render() {
const { isPending, stepIndex, isUpdatingLogo, isUpdatingNotice } =
this.state;
const currentStep = this.getSteps()[ stepIndex ].key;
return (
<div className="woocommerce-task-appearance">
<Card className="woocommerce-task-card">
<CardBody>
<Stepper
isPending={
isUpdatingNotice || isUpdatingLogo || isPending
}
isVertical
currentStep={ currentStep }
steps={ this.getSteps() }
/>
</CardBody>
</Card>
</div>
const useAppearanceClick = () => {
const onClick = () => {
window.location = getAdminLink(
'theme-install.php?browse=block-themes'
);
}
}
};
const AppearanceWrapper = compose(
withSelect( ( select ) => {
const { getOption } = select( OPTIONS_STORE_NAME );
return { onClick };
};
return {
demoStoreNotice: getOption( 'woocommerce_demo_store_notice' ),
};
} ),
withDispatch( ( dispatch ) => {
const { createNotice } = dispatch( 'core/notices' );
const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
const { actionTask } = dispatch( ONBOARDING_STORE_NAME );
return {
actionTask,
createNotice,
updateOptions,
};
} )
)( Appearance );
const AppearanceFill = () => {
const { onClick } = useAppearanceClick();
return (
<WooOnboardingTaskListItem id="appearance">
{ ( { defaultTaskItem: DefaultTaskItem } ) => (
<DefaultTaskItem
// Override task click so it doesn't navigate to a task component.
onClick={ onClick }
/>
) }
</WooOnboardingTaskListItem>
);
};
registerPlugin( 'wc-admin-onboarding-task-appearance', {
scope: 'woocommerce-tasks',
render: () => (
<WooOnboardingTask id="appearance">
{ ( { onComplete, task } ) => (
<AppearanceWrapper onComplete={ onComplete } task={ task } />
) }
</WooOnboardingTask>
),
render: () => <AppearanceFill />,
} );

View File

@ -0,0 +1,4 @@
Significance: minor
Type: update
Replace Personalize Your Store task with Choose Your Theme

View File

@ -112,12 +112,12 @@ class TaskLists {
'CustomizeStore',
'StoreDetails',
'Products',
'Appearance',
'WooCommercePayments',
'Payments',
'Tax',
'Shipping',
'Marketing',
'Appearance',
);
if ( Features::is_enabled( 'core-profiler' ) ) {

View File

@ -14,14 +14,12 @@ use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;
class Appearance extends Task {
/**
* Constructor
*
* @param TaskList $task_list Parent task list.
* Constructor.
*/
public function __construct( $task_list ) {
parent::__construct( $task_list );
add_action( 'admin_enqueue_scripts', array( $this, 'add_media_scripts' ) );
add_action( 'admin_enqueue_scripts', array( $this, 'possibly_add_return_notice_script' ) );
public function __construct() {
if ( ! $this->is_complete() ) {
add_action( 'load-theme-install.php', array( $this, 'mark_actioned' ) );
}
}
/**
@ -39,13 +37,7 @@ class Appearance extends Task {
* @return string
*/
public function get_title() {
if ( $this->get_parent_option( 'use_completed_title' ) === true ) {
if ( $this->is_complete() ) {
return __( 'You personalized your store', 'woocommerce' );
}
return __( 'Personalize your store', 'woocommerce' );
}
return __( 'Personalize my store', 'woocommerce' );
return __( 'Choose your theme', 'woocommerce' );
}
/**
@ -55,7 +47,7 @@ class Appearance extends Task {
*/
public function get_content() {
return __(
'Add your logo, create a homepage, and start designing your store.',
"Choose a theme that best fits your brand's look and feel, then make it your own. Change the colors, add your logo, and create pages.",
'woocommerce'
);
}
@ -70,68 +62,11 @@ class Appearance extends Task {
}
/**
* Addtional data.
* Action label.
*
* @return array
* @return string
*/
public function get_additional_data() {
return array(
'has_homepage' => self::has_homepage(),
'has_products' => Products::has_products(),
'stylesheet' => get_option( 'stylesheet' ),
'theme_mods' => get_theme_mods(),
'support_custom_logo' => false !== get_theme_support( 'custom-logo' ),
);
}
/**
* Add media scripts for image uploader.
*/
public function add_media_scripts() {
if ( ! PageController::is_admin_page() || ! $this->can_view() ) {
return;
}
wp_enqueue_media();
}
/**
* Adds a return to task list notice when completing the task.
*
* @param string $hook Page hook.
*/
public function possibly_add_return_notice_script( $hook ) {
global $post;
if ( $hook !== 'post.php' || $post->post_type !== 'page' ) {
return;
}
if ( $this->is_complete() || ! $this->is_active() ) {
return;
}
WCAdminAssets::register_script( 'wp-admin-scripts', 'onboarding-homepage-notice', true );
}
/**
* Check if the site has a homepage set up.
*/
public static function has_homepage() {
if ( get_option( 'classic-editor-replace' ) === 'classic' ) {
return true;
}
$homepage_id = get_option( 'woocommerce_onboarding_homepage_post_id', false );
if ( ! $homepage_id ) {
return false;
}
$post = get_post( $homepage_id );
$completed = $post && $post->post_status === 'publish';
return $completed;
public function get_action_label() {
return __( 'Choose theme', 'woocommerce' );
}
}