Add plugin and jetpack wc-api methods to shipping task (https://github.com/woocommerce/woocommerce-admin/pull/2778)
* Add shipping labels step * Add jetpack connection selectors to wc-api * Add plugin install and activation methods to wc-api * Add shipping connect step * Add busy cursor CSS to buttons
This commit is contained in:
parent
f4d7936b17
commit
234e4d513c
|
@ -2,20 +2,19 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
import { __, sprintf } from '@wordpress/i18n';
|
import { __ } from '@wordpress/i18n';
|
||||||
import { Button } from 'newspack-components';
|
import { Button } from 'newspack-components';
|
||||||
import { Component, Fragment } from '@wordpress/element';
|
import { Component, Fragment } from '@wordpress/element';
|
||||||
import apiFetch from '@wordpress/api-fetch';
|
|
||||||
import { forEach } from 'lodash';
|
|
||||||
import { compose } from '@wordpress/compose';
|
import { compose } from '@wordpress/compose';
|
||||||
|
import { difference } from 'lodash';
|
||||||
import { withDispatch } from '@wordpress/data';
|
import { withDispatch } from '@wordpress/data';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal depdencies
|
* Internal depdencies
|
||||||
*/
|
*/
|
||||||
import { H, Stepper, Card } from '@woocommerce/components';
|
import { H, Stepper, Card } from '@woocommerce/components';
|
||||||
import { NAMESPACE } from 'wc-api/onboarding/constants';
|
|
||||||
import { recordEvent } from 'lib/tracks';
|
import { recordEvent } from 'lib/tracks';
|
||||||
|
import withSelect from 'wc-api/with-select';
|
||||||
|
|
||||||
const plugins = [ 'jetpack', 'woocommerce-services' ];
|
const plugins = [ 'jetpack', 'woocommerce-services' ];
|
||||||
|
|
||||||
|
@ -25,138 +24,53 @@ class Plugins extends Component {
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
step: 'install',
|
step: 'install',
|
||||||
isPending: true,
|
|
||||||
isError: false,
|
|
||||||
pluginsInstalled: 0,
|
|
||||||
pluginsActivated: 0,
|
|
||||||
connectUrl: '',
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.activatePlugins = this.activatePlugins.bind( this );
|
this.activatePlugins = this.activatePlugins.bind( this );
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.installPlugins();
|
this.props.installPlugins( plugins );
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate( prevProps, prevState ) {
|
componentDidUpdate( prevProps ) {
|
||||||
|
const { createNotice, errors, installedPlugins, jetpackConnectUrl } = this.props;
|
||||||
|
|
||||||
|
if ( jetpackConnectUrl ) {
|
||||||
|
window.location = jetpackConnectUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
const newErrors = difference( errors, prevProps.errors );
|
||||||
|
newErrors.map( error => createNotice( 'error', error ) );
|
||||||
|
|
||||||
if (
|
if (
|
||||||
this.state.pluginsInstalled !== prevState.pluginsInstalled &&
|
prevProps.installedPlugins.length !== plugins.length &&
|
||||||
this.state.pluginsInstalled === plugins.length
|
installedPlugins.length === plugins.length
|
||||||
) {
|
) {
|
||||||
/* eslint-disable react/no-did-update-set-state */
|
/* eslint-disable react/no-did-update-set-state */
|
||||||
this.setState( {
|
this.setState( { step: 'activate' } );
|
||||||
step: 'activate',
|
|
||||||
isPending: false,
|
|
||||||
} );
|
|
||||||
/* eslint-enable react/no-did-update-set-state */
|
/* eslint-enable react/no-did-update-set-state */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
|
||||||
this.state.pluginsActivated !== prevState.pluginsActivated &&
|
|
||||||
this.state.pluginsActivated === plugins.length
|
|
||||||
) {
|
|
||||||
this.connectJetpack();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
installPlugins() {
|
async activatePlugins( event ) {
|
||||||
forEach( plugins, async plugin => {
|
|
||||||
const response = await this.doPluginAction( 'install', plugin );
|
|
||||||
if ( 'success' === response.status ) {
|
|
||||||
this.setState( state => ( {
|
|
||||||
pluginsInstalled: state.pluginsInstalled + 1,
|
|
||||||
} ) );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
activatePlugins( event ) {
|
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
// Avoid double activating.
|
// Avoid double activating.
|
||||||
const { isPending } = this.state;
|
const { isRequesting } = this.props;
|
||||||
if ( isPending ) {
|
if ( isRequesting ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState( {
|
|
||||||
isPending: true,
|
|
||||||
} );
|
|
||||||
|
|
||||||
recordEvent( 'storeprofiler_install_plugin' );
|
recordEvent( 'storeprofiler_install_plugin' );
|
||||||
|
|
||||||
forEach( plugins, async plugin => {
|
this.props.activatePlugins( plugins );
|
||||||
const response = await this.doPluginAction( 'activate', plugin );
|
|
||||||
if ( 'success' === response.status ) {
|
|
||||||
this.setState( state => ( {
|
|
||||||
pluginsActivated: state.pluginsActivated + 1,
|
|
||||||
} ) );
|
|
||||||
}
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
|
|
||||||
getErrorMessage( action, plugin ) {
|
|
||||||
return 'install' === action
|
|
||||||
? sprintf(
|
|
||||||
__( 'There was an error installing %s. Please try again.', 'woocommerce-admin' ),
|
|
||||||
this.getPluginName( plugin )
|
|
||||||
)
|
|
||||||
: sprintf(
|
|
||||||
__( 'There was an error activating %s. Please try again.', 'woocommerce-admin' ),
|
|
||||||
this.getPluginName( plugin )
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async doPluginAction( action, plugin ) {
|
|
||||||
try {
|
|
||||||
const pluginResponse = await apiFetch( {
|
|
||||||
path: `${ NAMESPACE }/onboarding/plugins/${ action }`,
|
|
||||||
method: 'POST',
|
|
||||||
data: {
|
|
||||||
plugin,
|
|
||||||
},
|
|
||||||
} );
|
|
||||||
return pluginResponse;
|
|
||||||
} catch ( err ) {
|
|
||||||
this.props.createNotice( 'error', this.getErrorMessage( action, plugin ) );
|
|
||||||
this.setState( {
|
|
||||||
isPending: false,
|
|
||||||
isError: true,
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async connectJetpack() {
|
|
||||||
try {
|
|
||||||
const connectResponse = await apiFetch( {
|
|
||||||
path: `${ NAMESPACE }/onboarding/plugins/connect-jetpack`,
|
|
||||||
} );
|
|
||||||
if ( connectResponse && connectResponse.connectAction ) {
|
|
||||||
window.location = connectResponse.connectAction;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
throw new Error();
|
|
||||||
} catch ( err ) {
|
|
||||||
this.props.createNotice( 'error', this.getErrorMessage( 'activate', 'jetpack' ) );
|
|
||||||
this.setState( {
|
|
||||||
isPending: false,
|
|
||||||
isError: true,
|
|
||||||
} );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
getPluginName( plugin ) {
|
|
||||||
switch ( plugin ) {
|
|
||||||
case 'jetpack':
|
|
||||||
return __( 'Jetpack', 'woocommerce-admin' );
|
|
||||||
case 'woocommerce-services':
|
|
||||||
return __( 'WooCommerce Services', 'woocommerce-admin' );
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { step, isPending, isError } = this.state;
|
const { hasErrors, isRequesting } = this.props;
|
||||||
|
const { step } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<H className="woocommerce-profile-wizard__header-title">
|
<H className="woocommerce-profile-wizard__header-title">
|
||||||
|
@ -167,7 +81,7 @@ class Plugins extends Component {
|
||||||
<Stepper
|
<Stepper
|
||||||
isVertical={ true }
|
isVertical={ true }
|
||||||
currentStep={ step }
|
currentStep={ step }
|
||||||
isPending={ isPending }
|
isPending={ isRequesting && ! hasErrors }
|
||||||
steps={ [
|
steps={ [
|
||||||
{
|
{
|
||||||
label: __( 'Install Jetpack and WooCommerce Services', 'woocommerce-admin' ),
|
label: __( 'Install Jetpack and WooCommerce Services', 'woocommerce-admin' ),
|
||||||
|
@ -181,18 +95,17 @@ class Plugins extends Component {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div className="woocommerce-profile-wizard__plugins-actions">
|
<div className="woocommerce-profile-wizard__plugins-actions">
|
||||||
{ isError && (
|
{ hasErrors && (
|
||||||
<Button isPrimary onClick={ () => location.reload() }>
|
<Button isPrimary onClick={ () => location.reload() }>
|
||||||
{ __( 'Retry', 'woocommerce-admin' ) }
|
{ __( 'Retry', 'woocommerce-admin' ) }
|
||||||
</Button>
|
</Button>
|
||||||
) }
|
) }
|
||||||
|
|
||||||
{ ! isError &&
|
{ ! ( hasErrors && 'activate' === step ) && (
|
||||||
'activate' === step && (
|
<Button isPrimary isBusy={ isRequesting } onClick={ this.activatePlugins }>
|
||||||
<Button isPrimary isBusy={ isPending } onClick={ this.activatePlugins }>
|
{ __( 'Activate & continue', 'woocommerce-admin' ) }
|
||||||
{ __( 'Activate & continue', 'woocommerce-admin' ) }
|
</Button>
|
||||||
</Button>
|
) }
|
||||||
) }
|
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
</Card>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
@ -201,10 +114,64 @@ class Plugins extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
export default compose(
|
export default compose(
|
||||||
|
withSelect( select => {
|
||||||
|
const {
|
||||||
|
getJetpackConnectUrl,
|
||||||
|
isGetJetpackConnectUrlRequesting,
|
||||||
|
getJetpackConnectUrlError,
|
||||||
|
getPluginInstallations,
|
||||||
|
getPluginInstallationErrors,
|
||||||
|
getPluginActivations,
|
||||||
|
getPluginActivationErrors,
|
||||||
|
isPluginActivateRequesting,
|
||||||
|
isPluginInstallRequesting,
|
||||||
|
} = select( 'wc-api' );
|
||||||
|
|
||||||
|
const isRequesting =
|
||||||
|
isPluginActivateRequesting() || isPluginInstallRequesting() || getJetpackConnectUrlError();
|
||||||
|
|
||||||
|
const activationErrors = getPluginActivationErrors( plugins );
|
||||||
|
const activatedPlugins = Object.keys( getPluginActivations( plugins ) );
|
||||||
|
const installationErrors = getPluginInstallationErrors( plugins );
|
||||||
|
const installedPlugins = Object.keys( getPluginInstallations( plugins ) );
|
||||||
|
|
||||||
|
const isJetpackConnectUrlRequesting = isGetJetpackConnectUrlRequesting();
|
||||||
|
const jetpackConnectUrlError = getJetpackConnectUrlError();
|
||||||
|
let jetpackConnectUrl = null;
|
||||||
|
if ( activatedPlugins.includes( 'jetpack' ) ) {
|
||||||
|
jetpackConnectUrl = getJetpackConnectUrl();
|
||||||
|
}
|
||||||
|
|
||||||
|
const errors = [];
|
||||||
|
Object.keys( activationErrors ).map( plugin =>
|
||||||
|
errors.push( activationErrors[ plugin ].message )
|
||||||
|
);
|
||||||
|
Object.keys( installationErrors ).map( plugin =>
|
||||||
|
errors.push( installationErrors[ plugin ].message )
|
||||||
|
);
|
||||||
|
if ( jetpackConnectUrlError ) {
|
||||||
|
errors.push( jetpackConnectUrlError );
|
||||||
|
}
|
||||||
|
const hasErrors = Boolean( errors.length );
|
||||||
|
|
||||||
|
return {
|
||||||
|
activatedPlugins,
|
||||||
|
installedPlugins,
|
||||||
|
jetpackConnectUrl,
|
||||||
|
isJetpackConnectUrlRequesting,
|
||||||
|
errors,
|
||||||
|
hasErrors,
|
||||||
|
isRequesting,
|
||||||
|
};
|
||||||
|
} ),
|
||||||
withDispatch( dispatch => {
|
withDispatch( dispatch => {
|
||||||
const { createNotice } = dispatch( 'core/notices' );
|
const { createNotice } = dispatch( 'core/notices' );
|
||||||
|
const { activatePlugins, installPlugins } = dispatch( 'wc-api' );
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
activatePlugins,
|
||||||
createNotice,
|
createNotice,
|
||||||
|
installPlugins,
|
||||||
};
|
};
|
||||||
} )
|
} )
|
||||||
)( Plugins );
|
)( Plugins );
|
||||||
|
|
|
@ -194,6 +194,10 @@
|
||||||
|
|
||||||
.muriel-button.is-button {
|
.muriel-button.is-button {
|
||||||
height: 48px;
|
height: 48px;
|
||||||
|
|
||||||
|
&.is-busy {
|
||||||
|
cursor: progress;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.muriel-checkbox input[type='checkbox']:checked {
|
.muriel-checkbox input[type='checkbox']:checked {
|
||||||
|
|
|
@ -16,9 +16,15 @@
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.muriel-button.is-button {
|
.muriel-button.components-button {
|
||||||
height: 40px;
|
height: 40px;
|
||||||
margin: 0;
|
min-width: 106px;
|
||||||
|
margin: $gap $gap-smaller 0 0;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&:not(.is-primary) {
|
||||||
|
color: $muriel-hot-pink-500;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-list {
|
.woocommerce-list {
|
||||||
|
@ -65,10 +71,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.woocommerce-shipping-rates {
|
|
||||||
margin-bottom: $gap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.woocommerce-shipping-rate {
|
.woocommerce-shipping-rate {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding-top: $gap-small;
|
padding-top: $gap-small;
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
/** @format */
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { Button } from 'newspack-components';
|
||||||
|
import { Component, Fragment } from '@wordpress/element';
|
||||||
|
import { compose } from '@wordpress/compose';
|
||||||
|
import { withDispatch } from '@wordpress/data';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import withSelect from 'wc-api/with-select';
|
||||||
|
|
||||||
|
class Connect extends Component {
|
||||||
|
constructor() {
|
||||||
|
super( ...arguments );
|
||||||
|
|
||||||
|
this.connectJetpack = this.connectJetpack.bind( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate( prevProps ) {
|
||||||
|
const { createNotice, error } = this.props;
|
||||||
|
|
||||||
|
if ( error && error !== prevProps.error ) {
|
||||||
|
createNotice( 'error', error );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async connectJetpack() {
|
||||||
|
const { jetpackConnectUrl } = this.props;
|
||||||
|
window.location = jetpackConnectUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { hasErrors, isRequesting } = this.props;
|
||||||
|
|
||||||
|
return hasErrors ? (
|
||||||
|
<Button isPrimary onClick={ () => location.reload() }>
|
||||||
|
{ __( 'Retry', 'woocommerce-admin' ) }
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Fragment>
|
||||||
|
<Button isBusy={ isRequesting } isPrimary onClick={ this.connectJetpack }>
|
||||||
|
{ __( 'Connect', 'woocommerce-admin' ) }
|
||||||
|
</Button>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withSelect( select => {
|
||||||
|
const {
|
||||||
|
getJetpackConnectUrl,
|
||||||
|
isGetJetpackConnectUrlRequesting,
|
||||||
|
getJetpackConnectUrlError,
|
||||||
|
} = select( 'wc-api' );
|
||||||
|
|
||||||
|
const isRequesting = isGetJetpackConnectUrlRequesting();
|
||||||
|
const error = getJetpackConnectUrlError();
|
||||||
|
const jetpackConnectUrl = getJetpackConnectUrl();
|
||||||
|
|
||||||
|
return {
|
||||||
|
error,
|
||||||
|
isRequesting,
|
||||||
|
jetpackConnectUrl,
|
||||||
|
};
|
||||||
|
} ),
|
||||||
|
withDispatch( dispatch => {
|
||||||
|
const { createNotice } = dispatch( 'core/notices' );
|
||||||
|
|
||||||
|
return {
|
||||||
|
createNotice,
|
||||||
|
};
|
||||||
|
} )
|
||||||
|
)( Connect );
|
|
@ -18,7 +18,9 @@ import { getHistory, getNewPath } from '@woocommerce/navigation';
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
|
import Connect from './connect';
|
||||||
import StoreLocation from './location';
|
import StoreLocation from './location';
|
||||||
|
import ShippingLabels from './labels';
|
||||||
import ShippingRates from './rates';
|
import ShippingRates from './rates';
|
||||||
import withSelect from 'wc-api/with-select';
|
import withSelect from 'wc-api/with-select';
|
||||||
|
|
||||||
|
@ -137,6 +139,8 @@ class Shipping extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
getSteps() {
|
getSteps() {
|
||||||
|
const { countryCode } = this.props;
|
||||||
|
|
||||||
const steps = [
|
const steps = [
|
||||||
{
|
{
|
||||||
key: 'store_location',
|
key: 'store_location',
|
||||||
|
@ -161,6 +165,27 @@ class Shipping extends Component {
|
||||||
),
|
),
|
||||||
visible: true,
|
visible: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'label_printing',
|
||||||
|
label: __( 'Enable shipping label printing', 'woocommerce-admin' ),
|
||||||
|
description: __(
|
||||||
|
'With WooCommerce Services and Jetpack you can save time at the' +
|
||||||
|
'Post Office by printing your shipping labels at home',
|
||||||
|
'woocommerce-admin'
|
||||||
|
),
|
||||||
|
content: <ShippingLabels completeStep={ this.completeStep } { ...this.props } />,
|
||||||
|
visible: [ 'US', 'GB', 'CA', 'AU' ].includes( countryCode ),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'connect',
|
||||||
|
label: __( 'Connect your store', 'woocommerce-admin' ),
|
||||||
|
description: __(
|
||||||
|
'Connect your store to WordPress.com to enable label printing',
|
||||||
|
'woocommerce-admin'
|
||||||
|
),
|
||||||
|
content: <Connect completeStep={ this.completeStep } { ...this.props } />,
|
||||||
|
visible: 'US' === countryCode,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return filter( steps, step => step.visible );
|
return filter( steps, step => step.visible );
|
||||||
|
|
|
@ -0,0 +1,142 @@
|
||||||
|
/** @format */
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
import { Button } from 'newspack-components';
|
||||||
|
import { Component, Fragment } from '@wordpress/element';
|
||||||
|
import { compose } from '@wordpress/compose';
|
||||||
|
import { difference } from 'lodash';
|
||||||
|
import { withDispatch } from '@wordpress/data';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WooCommerce dependencies
|
||||||
|
*/
|
||||||
|
import { getHistory, getNewPath } from '@woocommerce/navigation';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal dependencies
|
||||||
|
*/
|
||||||
|
import withSelect from 'wc-api/with-select';
|
||||||
|
|
||||||
|
const plugins = [ 'jetpack', 'woocommerce-services' ];
|
||||||
|
|
||||||
|
class ShippingLabels extends Component {
|
||||||
|
constructor() {
|
||||||
|
super( ...arguments );
|
||||||
|
|
||||||
|
this.installAndActivatePlugins = this.installAndActivatePlugins.bind( this );
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate( prevProps ) {
|
||||||
|
const {
|
||||||
|
activatedPlugins,
|
||||||
|
activatePlugins,
|
||||||
|
completeStep,
|
||||||
|
createNotice,
|
||||||
|
errors,
|
||||||
|
installedPlugins,
|
||||||
|
isRequesting,
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
|
const newErrors = difference( errors, prevProps.errors );
|
||||||
|
newErrors.map( error => createNotice( 'error', error ) );
|
||||||
|
|
||||||
|
if (
|
||||||
|
! isRequesting &&
|
||||||
|
installedPlugins.length === plugins.length &&
|
||||||
|
activatedPlugins.length !== plugins.length &&
|
||||||
|
prevProps.installedPlugins.length !== installedPlugins.length
|
||||||
|
) {
|
||||||
|
activatePlugins( plugins );
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( activatedPlugins.length === plugins.length ) {
|
||||||
|
createNotice(
|
||||||
|
'success',
|
||||||
|
__( 'Plugins were successfully installed and activated.', 'woocommerce-admin' )
|
||||||
|
);
|
||||||
|
completeStep();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async installAndActivatePlugins( event ) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
// Avoid double activating.
|
||||||
|
const { isRequesting, installPlugins } = this.props;
|
||||||
|
if ( isRequesting ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
installPlugins( plugins );
|
||||||
|
}
|
||||||
|
|
||||||
|
skipInstaller() {
|
||||||
|
getHistory().push( getNewPath( {}, '/', {} ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { hasErrors, isRequesting } = this.props;
|
||||||
|
|
||||||
|
return hasErrors ? (
|
||||||
|
<Button isPrimary onClick={ () => location.reload() }>
|
||||||
|
{ __( 'Retry', 'woocommerce-admin' ) }
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Fragment>
|
||||||
|
<Button isBusy={ isRequesting } isPrimary onClick={ this.installAndActivatePlugins }>
|
||||||
|
{ __( 'Install & enable', 'woocommerce-admin' ) }
|
||||||
|
</Button>
|
||||||
|
<Button onClick={ this.skipInstaller }>{ __( 'No thanks', 'woocommerce-admin' ) }</Button>
|
||||||
|
</Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default compose(
|
||||||
|
withSelect( select => {
|
||||||
|
const {
|
||||||
|
getPluginInstallations,
|
||||||
|
getPluginInstallationErrors,
|
||||||
|
getPluginActivations,
|
||||||
|
getPluginActivationErrors,
|
||||||
|
isPluginActivateRequesting,
|
||||||
|
isPluginInstallRequesting,
|
||||||
|
} = select( 'wc-api' );
|
||||||
|
|
||||||
|
const isRequesting = isPluginActivateRequesting() || isPluginInstallRequesting();
|
||||||
|
|
||||||
|
const activationErrors = getPluginActivationErrors( plugins );
|
||||||
|
const activatedPlugins = Object.keys( getPluginActivations( plugins ) );
|
||||||
|
const installationErrors = getPluginInstallationErrors( plugins );
|
||||||
|
const installedPlugins = Object.keys( getPluginInstallations( plugins ) );
|
||||||
|
|
||||||
|
const errors = [];
|
||||||
|
Object.keys( activationErrors ).map( plugin =>
|
||||||
|
errors.push( activationErrors[ plugin ].message )
|
||||||
|
);
|
||||||
|
Object.keys( installationErrors ).map( plugin =>
|
||||||
|
errors.push( installationErrors[ plugin ].message )
|
||||||
|
);
|
||||||
|
const hasErrors = Boolean( errors.length );
|
||||||
|
|
||||||
|
return {
|
||||||
|
activatedPlugins,
|
||||||
|
installedPlugins,
|
||||||
|
errors,
|
||||||
|
hasErrors,
|
||||||
|
isRequesting,
|
||||||
|
};
|
||||||
|
} ),
|
||||||
|
withDispatch( dispatch => {
|
||||||
|
const { createNotice } = dispatch( 'core/notices' );
|
||||||
|
const { activatePlugins, installPlugins } = dispatch( 'wc-api' );
|
||||||
|
|
||||||
|
return {
|
||||||
|
activatePlugins,
|
||||||
|
createNotice,
|
||||||
|
installPlugins,
|
||||||
|
};
|
||||||
|
} )
|
||||||
|
)( ShippingLabels );
|
|
@ -1,3 +1,18 @@
|
||||||
/** @format */
|
/** @format */
|
||||||
|
/**
|
||||||
|
* External dependencies
|
||||||
|
*/
|
||||||
|
import { __ } from '@wordpress/i18n';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Onboarding namespace.
|
||||||
|
*/
|
||||||
export const NAMESPACE = '/wc-admin/v1';
|
export const NAMESPACE = '/wc-admin/v1';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin slugs and names as key/value pairs.
|
||||||
|
*/
|
||||||
|
export const pluginNames = {
|
||||||
|
jetpack: __( 'Jetpack', 'woocommerce-admin' ),
|
||||||
|
'woocommerce-services': __( 'WooCommerce Services', 'woocommerce-admin' ),
|
||||||
|
};
|
||||||
|
|
|
@ -7,6 +7,22 @@ const updateProfileItems = operations => fields => {
|
||||||
} );
|
} );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const installPlugins = operations => plugins => {
|
||||||
|
const resourceKey = 'plugin-install';
|
||||||
|
operations.update( [ resourceKey ], {
|
||||||
|
[ resourceKey ]: plugins,
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
|
const activatePlugins = operations => plugins => {
|
||||||
|
const resourceKey = 'plugin-activate';
|
||||||
|
operations.update( [ resourceKey ], {
|
||||||
|
[ resourceKey ]: plugins,
|
||||||
|
} );
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
activatePlugins,
|
||||||
|
installPlugins,
|
||||||
updateProfileItems,
|
updateProfileItems,
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,20 +3,28 @@
|
||||||
/**
|
/**
|
||||||
* External dependencies
|
* External dependencies
|
||||||
*/
|
*/
|
||||||
|
import { __, sprintf } from '@wordpress/i18n';
|
||||||
import apiFetch from '@wordpress/api-fetch';
|
import apiFetch from '@wordpress/api-fetch';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
*/
|
*/
|
||||||
import { getResourceName } from '../utils';
|
import { getResourceName } from '../utils';
|
||||||
import { NAMESPACE } from './constants';
|
import { NAMESPACE, pluginNames } from './constants';
|
||||||
|
|
||||||
function read( resourceNames, fetch = apiFetch ) {
|
function read( resourceNames, fetch = apiFetch ) {
|
||||||
return [ ...readProfileItems( resourceNames, fetch ) ];
|
return [
|
||||||
|
...readProfileItems( resourceNames, fetch ),
|
||||||
|
...readJetpackConnectUrl( resourceNames, fetch ),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function update( resourceNames, data, fetch = apiFetch ) {
|
function update( resourceNames, data, fetch = apiFetch ) {
|
||||||
return [ ...updateProfileItems( resourceNames, data, fetch ) ];
|
return [
|
||||||
|
...activatePlugins( resourceNames, data, fetch ),
|
||||||
|
...installPlugins( resourceNames, data, fetch ),
|
||||||
|
...updateProfileItems( resourceNames, data, fetch ),
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
function readProfileItems( resourceNames, fetch ) {
|
function readProfileItems( resourceNames, fetch ) {
|
||||||
|
@ -90,6 +98,92 @@ function profileItemToResource( items ) {
|
||||||
return resources;
|
return resources;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function readJetpackConnectUrl( resourceNames, fetch ) {
|
||||||
|
const resourceName = 'jetpack-connect-url';
|
||||||
|
|
||||||
|
if ( resourceNames.includes( resourceName ) ) {
|
||||||
|
const url = NAMESPACE + '/onboarding/plugins/connect-jetpack';
|
||||||
|
|
||||||
|
return [
|
||||||
|
fetch( {
|
||||||
|
path: url,
|
||||||
|
} )
|
||||||
|
.then( response => {
|
||||||
|
return { [ resourceName ]: { data: response.connectAction } };
|
||||||
|
} )
|
||||||
|
.catch( error => {
|
||||||
|
error.message = getPluginErrorMessage( 'activate', 'jetpack' );
|
||||||
|
return { [ resourceName ]: { error } };
|
||||||
|
} ),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function doPluginActions( fetch, action, plugins ) {
|
||||||
|
const resourceName = [ `plugin-${ action }` ];
|
||||||
|
|
||||||
|
return plugins.map( async plugin => {
|
||||||
|
return fetch( {
|
||||||
|
path: `${ NAMESPACE }/onboarding/plugins/${ action }`,
|
||||||
|
method: 'POST',
|
||||||
|
data: {
|
||||||
|
plugin,
|
||||||
|
},
|
||||||
|
} )
|
||||||
|
.then( response => {
|
||||||
|
return {
|
||||||
|
[ resourceName ]: { data: plugins },
|
||||||
|
[ getResourceName( resourceName, plugin ) ]: { data: response },
|
||||||
|
};
|
||||||
|
} )
|
||||||
|
.catch( error => {
|
||||||
|
error.message = getPluginErrorMessage( action, plugin );
|
||||||
|
return {
|
||||||
|
[ resourceName ]: { data: plugins },
|
||||||
|
[ getResourceName( resourceName, plugin ) ]: { error },
|
||||||
|
};
|
||||||
|
} );
|
||||||
|
} );
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPluginErrorMessage( action, plugin ) {
|
||||||
|
const pluginName = pluginNames[ plugin ] || plugin;
|
||||||
|
|
||||||
|
return 'install' === action
|
||||||
|
? sprintf(
|
||||||
|
__( 'There was an error installing %s. Please try again.', 'woocommerce-admin' ),
|
||||||
|
pluginName
|
||||||
|
)
|
||||||
|
: sprintf(
|
||||||
|
__( 'There was an error activating %s. Please try again.', 'woocommerce-admin' ),
|
||||||
|
pluginName
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function installPlugins( resourceNames, data, fetch ) {
|
||||||
|
const resourceName = 'plugin-install';
|
||||||
|
|
||||||
|
if ( resourceNames.includes( resourceName ) ) {
|
||||||
|
const plugins = data[ resourceName ];
|
||||||
|
return doPluginActions( fetch, 'install', plugins );
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function activatePlugins( resourceNames, data, fetch ) {
|
||||||
|
const resourceName = 'plugin-activate';
|
||||||
|
|
||||||
|
if ( resourceNames.includes( resourceName ) ) {
|
||||||
|
const plugins = data[ resourceName ];
|
||||||
|
return doPluginActions( fetch, 'activate', plugins );
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
read,
|
read,
|
||||||
update,
|
update,
|
||||||
|
|
|
@ -43,8 +43,113 @@ const isGetProfileItemsRequesting = getResource => () => {
|
||||||
return lastRequested > lastReceived;
|
return lastRequested > lastReceived;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getJetpackConnectUrl = ( getResource, requireResource ) => (
|
||||||
|
requirement = DEFAULT_REQUIREMENT
|
||||||
|
) => {
|
||||||
|
return requireResource( requirement, 'jetpack-connect-url' ).data;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getJetpackConnectUrlError = getResource => () => {
|
||||||
|
return getResource( 'jetpack-connect-url' ).error;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isGetJetpackConnectUrlRequesting = getResource => () => {
|
||||||
|
const { lastReceived, lastRequested } = getResource( 'jetpack-connect-url' );
|
||||||
|
|
||||||
|
if ( isNil( lastRequested ) || isNil( lastReceived ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastRequested > lastReceived;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPluginInstallations = getResource => plugins => {
|
||||||
|
const resourceName = 'plugin-install';
|
||||||
|
|
||||||
|
const installations = {};
|
||||||
|
plugins.forEach( plugin => {
|
||||||
|
const data = getResource( getResourceName( resourceName, plugin ) ).data;
|
||||||
|
if ( data ) {
|
||||||
|
installations[ plugin ] = data;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
return installations;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPluginActivations = getResource => plugins => {
|
||||||
|
const resourceName = 'plugin-activate';
|
||||||
|
|
||||||
|
const activations = {};
|
||||||
|
plugins.forEach( plugin => {
|
||||||
|
const data = getResource( getResourceName( resourceName, plugin ) ).data;
|
||||||
|
if ( data ) {
|
||||||
|
activations[ plugin ] = data;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
return activations;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPluginActivationErrors = getResource => plugins => {
|
||||||
|
const resourceName = 'plugin-activate';
|
||||||
|
|
||||||
|
const errors = {};
|
||||||
|
plugins.forEach( plugin => {
|
||||||
|
const error = getResource( getResourceName( resourceName, plugin ) ).error;
|
||||||
|
if ( error ) {
|
||||||
|
errors[ plugin ] = error;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPluginInstallationErrors = getResource => plugins => {
|
||||||
|
const resourceName = 'plugin-install';
|
||||||
|
|
||||||
|
const errors = {};
|
||||||
|
plugins.forEach( plugin => {
|
||||||
|
const error = getResource( getResourceName( resourceName, plugin ) ).error;
|
||||||
|
if ( error ) {
|
||||||
|
errors[ plugin ] = error;
|
||||||
|
}
|
||||||
|
} );
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isPluginActivateRequesting = getResource => () => {
|
||||||
|
const { lastReceived, lastRequested } = getResource( 'plugin-activate' );
|
||||||
|
|
||||||
|
if ( ! isNil( lastRequested ) && isNil( lastReceived ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastRequested > lastReceived;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isPluginInstallRequesting = getResource => () => {
|
||||||
|
const { lastReceived, lastRequested } = getResource( 'plugin-install' );
|
||||||
|
|
||||||
|
if ( ! isNil( lastRequested ) && isNil( lastReceived ) ) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastRequested > lastReceived;
|
||||||
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
getProfileItems,
|
getProfileItems,
|
||||||
getProfileItemsError,
|
getProfileItemsError,
|
||||||
isGetProfileItemsRequesting,
|
isGetProfileItemsRequesting,
|
||||||
|
getJetpackConnectUrl,
|
||||||
|
getJetpackConnectUrlError,
|
||||||
|
isGetJetpackConnectUrlRequesting,
|
||||||
|
getPluginActivations,
|
||||||
|
getPluginInstallations,
|
||||||
|
getPluginInstallationErrors,
|
||||||
|
getPluginActivationErrors,
|
||||||
|
isPluginActivateRequesting,
|
||||||
|
isPluginInstallRequesting,
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue