* Show success screen based on conditions instead of step

* Use promise chain to handle updating tax settings

* Fix up error handling for failed setting updates

* Skip store location step if complete address exists

* Fix up pending/requesting state logic

* Allow opt in to TOS on tax task

* Don't complete task from completeStep

* Add caption styling to TOS text
This commit is contained in:
Joshua T Flowers 2020-07-01 15:19:15 +03:00 committed by GitHub
parent 1d5e7db33d
commit 8e5291e231
8 changed files with 238 additions and 265 deletions

View File

@ -696,11 +696,7 @@ BusinessDetails.contextType = CurrencyContext;
export default compose( export default compose(
withSelect( ( select ) => { withSelect( ( select ) => {
const { const { getSettings, getSettingsError } = select( SETTINGS_STORE_NAME );
getSettings,
getSettingsError,
isGetSettingsRequesting,
} = select( SETTINGS_STORE_NAME );
const { getProfileItems, getOnboardingError } = select( const { getProfileItems, getOnboardingError } = select(
ONBOARDING_STORE_NAME ONBOARDING_STORE_NAME
); );
@ -716,7 +712,6 @@ export default compose(
isError: Boolean( getOnboardingError( 'updateProfileItems' ) ), isError: Boolean( getOnboardingError( 'updateProfileItems' ) ),
profileItems: getProfileItems(), profileItems: getProfileItems(),
isSettingsError: Boolean( getSettingsError( 'general' ) ), isSettingsError: Boolean( getSettingsError( 'general' ) ),
isSettingsRequesting: isGetSettingsRequesting( 'general' ),
settings, settings,
isInstallingActivating: isInstallingActivating:
isPluginsRequesting( 'installPlugins' ) || isPluginsRequesting( 'installPlugins' ) ||

View File

@ -237,11 +237,7 @@ StoreDetails.contextType = CurrencyContext;
export default compose( export default compose(
withSelect( ( select ) => { withSelect( ( select ) => {
const { const { getSettings, getSettingsError } = select( SETTINGS_STORE_NAME );
getSettings,
getSettingsError,
isGetSettingsRequesting,
} = select( SETTINGS_STORE_NAME );
const { getOnboardingError, getProfileItems } = select( const { getOnboardingError, getProfileItems } = select(
ONBOARDING_STORE_NAME ONBOARDING_STORE_NAME
); );
@ -253,12 +249,10 @@ export default compose(
const { general: settings = {} } = getSettings( 'general' ); const { general: settings = {} } = getSettings( 'general' );
const isSettingsError = Boolean( getSettingsError( 'general' ) ); const isSettingsError = Boolean( getSettingsError( 'general' ) );
const isSettingsRequesting = isGetSettingsRequesting( 'general' );
return { return {
isProfileItemsError, isProfileItemsError,
isSettingsError, isSettingsError,
isSettingsRequesting,
profileItems, profileItems,
settings, settings,
}; };

View File

@ -584,3 +584,8 @@
border-top-right-radius: 2px; border-top-right-radius: 2px;
} }
} }
.woocommerce-task__caption {
color: $dark-gray-300;
margin-top: $gap;
}

View File

@ -302,13 +302,13 @@ class Shipping extends Component {
render() { render() {
const { isPending, step } = this.state; const { isPending, step } = this.state;
const { isSettingsRequesting } = this.props; const { isUpdateSettingsRequesting } = this.props;
return ( return (
<div className="woocommerce-task-shipping"> <div className="woocommerce-task-shipping">
<Card className="is-narrow"> <Card className="is-narrow">
<Stepper <Stepper
isPending={ isPending || isSettingsRequesting } isPending={ isPending || isUpdateSettingsRequesting }
isVertical isVertical
currentStep={ step } currentStep={ step }
steps={ this.getSteps() } steps={ this.getSteps() }
@ -321,19 +321,14 @@ class Shipping extends Component {
export default compose( export default compose(
withSelect( ( select ) => { withSelect( ( select ) => {
const { const { getSettings, isUpdateSettingsRequesting } = select(
getSettings, SETTINGS_STORE_NAME
getSettingsError, );
isGetSettingsRequesting,
} = select( SETTINGS_STORE_NAME );
const { getActivePlugins, isJetpackConnected } = select( const { getActivePlugins, isJetpackConnected } = select(
PLUGINS_STORE_NAME PLUGINS_STORE_NAME
); );
const { general: settings = {} } = getSettings( 'general' ); const { general: settings = {} } = getSettings( 'general' );
const isSettingsError = Boolean( getSettingsError( 'general' ) );
const isSettingsRequesting = isGetSettingsRequesting( 'general' );
const countryCode = getCountryCode( const countryCode = getCountryCode(
settings.woocommerce_default_country settings.woocommerce_default_country
); );
@ -348,8 +343,7 @@ export default compose(
return { return {
countryCode, countryCode,
countryName, countryName,
isSettingsError, isUpdateSettingsRequesting: isUpdateSettingsRequesting( 'general' ),
isSettingsRequesting,
settings, settings,
activePlugins, activePlugins,
isJetpackConnected: isJetpackConnected(), isJetpackConnected: isJetpackConnected(),

View File

@ -2,7 +2,7 @@
* External dependencies * External dependencies
*/ */
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { Button } from '@wordpress/components'; import { Button, __experimentalText as Text } from '@wordpress/components';
import { Component, Fragment } from '@wordpress/element'; import { Component, Fragment } from '@wordpress/element';
import { compose } from '@wordpress/compose'; import { compose } from '@wordpress/compose';
import { difference, filter } from 'lodash'; import { difference, filter } from 'lodash';
@ -29,6 +29,7 @@ import {
* Internal dependencies * Internal dependencies
*/ */
import Connect from 'dashboard/components/connect'; import Connect from 'dashboard/components/connect';
import { createNoticesFromResponse } from 'lib/notices';
import { getCountryCode } from 'dashboard/utils'; import { getCountryCode } from 'dashboard/utils';
import StoreLocation from './steps/location'; import StoreLocation from './steps/location';
import { recordEvent, queueRecordEvent } from 'lib/tracks'; import { recordEvent, queueRecordEvent } from 'lib/tracks';
@ -36,24 +37,18 @@ import { recordEvent, queueRecordEvent } from 'lib/tracks';
class Tax extends Component { class Tax extends Component {
constructor( props ) { constructor( props ) {
super( props ); super( props );
const { hasCompleteAddress, pluginsToActivate } = props;
this.initialState = { this.initialState = {
isPending: false, isPending: false,
stepIndex: 0, stepIndex: hasCompleteAddress ? 1 : 0,
automatedTaxEnabled: true, // Cache the value of pluginsToActivate so that we can
// Cache the value of pluginsToActivate so that we can show/hide tasks based on it, but not have them update mid task. // show/hide tasks based on it, but not have them update mid task.
pluginsToActivate: props.pluginsToActivate, cachedPluginsToActivate: pluginsToActivate,
}; };
this.state = this.initialState; this.state = this.initialState;
this.completeStep = this.completeStep.bind( this ); this.completeStep = this.completeStep.bind( this );
this.configureTaxRates = this.configureTaxRates.bind( this );
this.updateAutomatedTax = this.updateAutomatedTax.bind( this );
this.setIsPending = this.setIsPending.bind( this );
this.shouldShowSuccessScreen = this.shouldShowSuccessScreen.bind(
this
);
} }
componentDidMount() { componentDidMount() {
@ -65,87 +60,22 @@ class Tax extends Component {
} }
shouldShowSuccessScreen() { shouldShowSuccessScreen() {
const { stepIndex } = this.state;
const { const {
isJetpackConnected, isJetpackConnected,
hasCompleteAddress,
pluginsToActivate, pluginsToActivate,
generalSettings,
} = this.props; } = this.props;
const {
woocommerce_store_address: storeAddress,
woocommerce_default_country: defaultCountry,
woocommerce_store_postcode: storePostCode,
} = generalSettings;
const isCompleteAddress = Boolean(
storeAddress && defaultCountry && storePostCode
);
return ( return (
stepIndex !== null && hasCompleteAddress &&
isCompleteAddress &&
! pluginsToActivate.length && ! pluginsToActivate.length &&
isJetpackConnected && isJetpackConnected &&
this.isTaxJarSupported() this.isTaxJarSupported()
); );
} }
componentDidUpdate( prevProps ) {
const { generalSettings, isJetpackConnected, taxSettings } = this.props;
const {
woocommerce_calc_taxes: calcTaxes,
woocommerce_store_address: storeAddress,
woocommerce_default_country: defaultCountry,
woocommerce_store_postcode: storePostCode,
} = generalSettings;
const { stepIndex } = this.state;
const currentStep = this.getSteps()[ stepIndex ];
const currentStepKey = currentStep && currentStep.key;
if ( stepIndex !== null && this.shouldShowSuccessScreen() ) {
/* eslint-disable react/no-did-update-set-state */
this.setState( { stepIndex: null } );
/* eslint-enable react/no-did-update-set-state */
return;
}
if (
taxSettings.wc_connect_taxes_enabled &&
taxSettings.wc_connect_taxes_enabled !==
prevProps.taxSettings.wc_connect_taxes_enabled
) {
/* eslint-disable react/no-did-update-set-state */
this.setState( {
automatedTaxEnabled:
taxSettings.wc_connect_taxes_enabled === 'yes'
? true
: false,
} );
/* eslint-enable react/no-did-update-set-state */
}
if ( currentStepKey === 'connect' && isJetpackConnected ) {
this.completeStep();
}
const isCompleteAddress = Boolean(
storeAddress && defaultCountry && storePostCode
);
if ( currentStepKey === 'store_location' && isCompleteAddress ) {
this.completeStep();
}
const {
woocommerce_calc_taxes: prevCalcTaxes,
} = prevProps.generalSettings;
if ( prevCalcTaxes === 'no' && calcTaxes === 'yes' ) {
window.location = getAdminLink(
'admin.php?page=wc-settings&tab=tax&section=standard'
);
}
}
isTaxJarSupported() { isTaxJarSupported() {
const { countryCode, tosAccepted } = this.props; const { countryCode } = this.props;
const { const {
automatedTaxSupportedCountries = [], automatedTaxSupportedCountries = [],
taxJarActivated, taxJarActivated,
@ -153,7 +83,6 @@ class Tax extends Component {
return ( return (
! taxJarActivated && // WCS integration doesn't work with the official TaxJar plugin. ! taxJarActivated && // WCS integration doesn't work with the official TaxJar plugin.
tosAccepted &&
automatedTaxSupportedCountries.includes( countryCode ) automatedTaxSupportedCountries.includes( countryCode )
); );
} }
@ -165,12 +94,10 @@ class Tax extends Component {
if ( nextStep ) { if ( nextStep ) {
this.setState( { stepIndex: stepIndex + 1 } ); this.setState( { stepIndex: stepIndex + 1 } );
} else {
getHistory().push( getNewPath( {}, '/', {} ) );
} }
} }
async configureTaxRates() { async manuallyConfigureTaxRates() {
const { const {
generalSettings, generalSettings,
updateAndPersistSettingsForGroup, updateAndPersistSettingsForGroup,
@ -178,82 +105,87 @@ class Tax extends Component {
if ( generalSettings.woocommerce_calc_taxes !== 'yes' ) { if ( generalSettings.woocommerce_calc_taxes !== 'yes' ) {
this.setState( { isPending: true } ); this.setState( { isPending: true } );
await updateAndPersistSettingsForGroup( 'general', { updateAndPersistSettingsForGroup( 'general', {
general: { general: {
...generalSettings, ...generalSettings,
woocommerce_calc_taxes: 'yes', woocommerce_calc_taxes: 'yes',
}, },
} ); } )
.then( () => this.redirectToTaxSettings() )
.catch( ( error ) => createNoticesFromResponse( error ) );
} else {
this.redirectToTaxSettings();
} }
window.location = getAdminLink(
'admin.php?page=wc-settings&tab=tax&section=standard&wc_onboarding_active_task=tax'
);
} }
async updateAutomatedTax() { updateAutomatedTax( isEnabling ) {
const { const {
createNotice, createNotice,
updateAndPersistSettingsForGroup, updateAndPersistSettingsForGroup,
generalSettings, generalSettings,
taxSettings, taxSettings,
} = this.props; } = this.props;
const { automatedTaxEnabled } = this.state;
await updateAndPersistSettingsForGroup( 'tax', { Promise.all( [
tax: { updateAndPersistSettingsForGroup( 'tax', {
...taxSettings, tax: {
wc_connect_taxes_enabled: automatedTaxEnabled ? 'yes' : 'no', ...taxSettings,
}, wc_connect_taxes_enabled: isEnabling ? 'yes' : 'no',
} ); },
} ),
await updateAndPersistSettingsForGroup( 'general', { updateAndPersistSettingsForGroup( 'general', {
general: { general: {
...generalSettings, ...generalSettings,
woocommerce_calc_taxes: 'yes', woocommerce_calc_taxes: 'yes',
}, },
} ); } ),
] )
if ( .then( () => {
! this.props.isTaxSettingsError && // @todo This is a workaround to force the task to mark as complete.
! this.props.isGeneralSettingsError // This should probably be updated to use wc-api so we can fetch tax rates.
) { setSetting( 'onboarding', {
// @todo This is a workaround to force the task to mark as complete. ...getSetting( 'onboarding', {} ),
// This should probably be updated to use wc-api so we can fetch tax rates. isTaxComplete: true,
setSetting( 'onboarding', { } );
...getSetting( 'onboarding', {} ), if ( isEnabling ) {
isTaxComplete: true, createNotice(
'success',
__(
"You're awesome! One less item on your to-do list ✅",
'woocommerce-admin'
)
);
getHistory().push( getNewPath( {}, '/', {} ) );
} else {
this.redirectToTaxSettings();
}
} )
.catch( () => {
createNotice(
'error',
__(
'There was a problem updating your tax settings.',
'woocommerce-admin'
)
);
} ); } );
createNotice(
'success',
__(
"You're awesome! One less item on your to-do list ✅",
'woocommerce-admin'
)
);
if ( automatedTaxEnabled ) {
getHistory().push( getNewPath( {}, '/', {} ) );
} else {
this.configureTaxRates();
}
} else {
createNotice(
'error',
__(
'There was a problem updating your tax settings.',
'woocommerce-admin'
)
);
}
} }
setIsPending( value ) { redirectToTaxSettings() {
this.setState( { isPending: value } ); window.location = getAdminLink(
'admin.php?page=wc-settings&tab=tax&section=standard&wc_onboarding_active_task=tax'
);
} }
getSteps() { getSteps() {
const { generalSettings, isJetpackConnected } = this.props; const {
const { isPending, pluginsToActivate } = this.state; generalSettings,
isJetpackConnected,
isPending,
tosAccepted,
updateOptions,
} = this.props;
const { cachedPluginsToActivate } = this.state;
const steps = [ const steps = [
{ {
@ -273,12 +205,7 @@ class Tax extends Component {
recordEvent( 'tasklist_tax_set_location', { recordEvent( 'tasklist_tax_set_location', {
country, country,
} ); } );
if ( this.shouldShowSuccessScreen() ) { this.completeStep();
this.setState( { stepIndex: null } );
// Only complete step if another update hasn't already shown succes screen.
} else if ( this.state.stepIndex !== null ) {
this.completeStep();
}
} } } }
isSettingsRequesting={ false } isSettingsRequesting={ false }
settings={ generalSettings } settings={ generalSettings }
@ -297,31 +224,63 @@ class Tax extends Component {
'woocommerce-admin' 'woocommerce-admin'
), ),
content: ( content: (
<Plugins <Fragment>
onComplete={ () => { <Plugins
recordEvent( 'tasklist_tax_install_extensions', { onComplete={ () => {
install_extensions: true, recordEvent(
} ); 'tasklist_tax_install_extensions',
this.completeStep(); {
} } install_extensions: true,
onSkip={ () => { }
queueRecordEvent( );
'tasklist_tax_install_extensions', updateOptions( {
{ woocommerce_setup_jetpack_opted_in: true,
install_extensions: false, } );
} this.completeStep();
); } }
window.location.href = getAdminLink( onSkip={ () => {
'admin.php?page=wc-settings&tab=tax&section=standard' queueRecordEvent(
); 'tasklist_tax_install_extensions',
} } {
skipText={ __( install_extensions: false,
'Set up tax rates manually', }
'woocommerce-admin' );
this.redirectToTaxSettings();
} }
skipText={ __(
'Set up tax rates manually',
'woocommerce-admin'
) }
/>
{ ! tosAccepted && (
<Text
variant="caption"
className="woocommerce-task__caption"
>
{ interpolateComponents( {
mixedString: __(
'By installing Jetpack and WooCommerce Services you agree to the {{link}}Terms of Service{{/link}}.',
'woocommerce-admin'
),
components: {
link: (
<Link
href={
'https://wordpress.com/tos/'
}
target="_blank"
type="external"
/>
),
},
} ) }
</Text>
) } ) }
/> </Fragment>
), ),
visible: pluginsToActivate.length && this.isTaxJarSupported(), visible:
( cachedPluginsToActivate.length || ! tosAccepted ) &&
this.isTaxJarSupported(),
}, },
{ {
key: 'connect', key: 'connect',
@ -333,7 +292,6 @@ class Tax extends Component {
content: ( content: (
<Connect <Connect
{ ...this.props } { ...this.props }
setIsPending={ this.setIsPending }
onConnect={ () => { onConnect={ () => {
recordEvent( 'tasklist_tax_connect_store', { recordEvent( 'tasklist_tax_connect_store', {
connect: true, connect: true,
@ -343,9 +301,7 @@ class Tax extends Component {
queueRecordEvent( 'tasklist_tax_connect_store', { queueRecordEvent( 'tasklist_tax_connect_store', {
connect: false, connect: false,
} ); } );
window.location.href = getAdminLink( this.manuallyConfigureTaxRates();
'admin.php?page=wc-settings&tab=tax&section=standard'
);
} } } }
skipText={ __( skipText={ __(
'Set up tax rates manually', 'Set up tax rates manually',
@ -365,11 +321,12 @@ class Tax extends Component {
content: ( content: (
<Fragment> <Fragment>
<Button <Button
disabled={ isPending }
isPrimary isPrimary
isBusy={ isPending } isBusy={ isPending }
onClick={ () => { onClick={ () => {
recordEvent( 'tasklist_tax_config_rates' ); recordEvent( 'tasklist_tax_config_rates' );
this.configureTaxRates(); this.manuallyConfigureTaxRates();
} } } }
> >
{ __( 'Configure', 'woocommerce-admin' ) } { __( 'Configure', 'woocommerce-admin' ) }
@ -405,21 +362,14 @@ class Tax extends Component {
} }
render() { render() {
const { isPending, stepIndex } = this.state; const { stepIndex } = this.state;
const { isTaxSettingsRequesting, taxSettings } = this.props; const { isPending, isResolving } = this.props;
const step = this.getSteps()[ stepIndex ]; const step = this.getSteps()[ stepIndex ];
return ( return (
<div className="woocommerce-task-tax"> <div className="woocommerce-task-tax">
<Card className="is-narrow"> <Card className="is-narrow">
{ step ? ( { this.shouldShowSuccessScreen() ? (
<Stepper
isPending={ isPending || isTaxSettingsRequesting }
isVertical={ true }
currentStep={ step.key }
steps={ this.getSteps() }
/>
) : (
<div className="woocommerce-task-tax__success"> <div className="woocommerce-task-tax__success">
<span <span
className="woocommerce-task-tax__success-icon" className="woocommerce-task-tax__success-icon"
@ -444,11 +394,9 @@ class Tax extends Component {
} ) } } ) }
</p> </p>
<Button <Button
disabled={ isPending }
isPrimary isPrimary
isBusy={ isBusy={ isPending }
Object.keys( taxSettings ).length &&
isTaxSettingsRequesting
}
onClick={ () => { onClick={ () => {
recordEvent( recordEvent(
'tasklist_tax_setup_automated_proceed', 'tasklist_tax_setup_automated_proceed',
@ -456,15 +404,14 @@ class Tax extends Component {
setup_automatically: true, setup_automatically: true,
} }
); );
this.setState( this.updateAutomatedTax( true );
{ automatedTaxEnabled: true },
this.updateAutomatedTax
);
} } } }
> >
{ __( 'Yes please', 'woocommerce-admin' ) } { __( 'Yes please', 'woocommerce-admin' ) }
</Button> </Button>
<Button <Button
disabled={ isPending }
isBusy={ isPending }
onClick={ () => { onClick={ () => {
recordEvent( recordEvent(
'tasklist_tax_setup_automated_proceed', 'tasklist_tax_setup_automated_proceed',
@ -472,10 +419,7 @@ class Tax extends Component {
setup_automatically: false, setup_automatically: false,
} }
); );
this.setState( this.updateAutomatedTax( false );
{ automatedTaxEnabled: false },
this.updateAutomatedTax
);
} } } }
> >
{ __( { __(
@ -484,6 +428,13 @@ class Tax extends Component {
) } ) }
</Button> </Button>
</div> </div>
) : (
<Stepper
isPending={ isPending || isResolving }
isVertical={ true }
currentStep={ step.key }
steps={ this.getSteps() }
/>
) } ) }
</Card> </Card>
</div> </div>
@ -493,26 +444,30 @@ class Tax extends Component {
export default compose( export default compose(
withSelect( ( select ) => { withSelect( ( select ) => {
const { const { getSettings, isUpdateSettingsRequesting } = select(
getSettings, SETTINGS_STORE_NAME
getSettingsError,
isGetSettingsRequesting,
} = select( SETTINGS_STORE_NAME );
const { getOption } = select( OPTIONS_STORE_NAME );
const { getActivePlugins, isJetpackConnected } = select(
PLUGINS_STORE_NAME
); );
const { getOption } = select( OPTIONS_STORE_NAME );
const {
getActivePlugins,
isJetpackConnected,
isPluginsRequesting,
} = select( PLUGINS_STORE_NAME );
const { general: generalSettings = {} } = getSettings( 'general' ); const { general: generalSettings = {} } = getSettings( 'general' );
const isGeneralSettingsError = Boolean( getSettingsError( 'general' ) );
const countryCode = getCountryCode( const countryCode = getCountryCode(
generalSettings.woocommerce_default_country generalSettings.woocommerce_default_country
); );
const {
woocommerce_store_address: storeAddress,
woocommerce_default_country: defaultCountry,
woocommerce_store_postcode: storePostCode,
} = generalSettings;
const hasCompleteAddress = Boolean(
storeAddress && defaultCountry && storePostCode
);
const { tax: taxSettings = {} } = getSettings( 'tax' ); const { tax: taxSettings = {} } = getSettings( 'tax' );
const isTaxSettingsError = Boolean( getSettingsError( 'tax' ) );
const isTaxSettingsRequesting = isGetSettingsRequesting( 'tax' );
const activePlugins = getActivePlugins(); const activePlugins = getActivePlugins();
const pluginsToActivate = difference( const pluginsToActivate = difference(
[ 'jetpack', 'woocommerce-services' ], [ 'jetpack', 'woocommerce-services' ],
@ -523,20 +478,26 @@ export default compose(
connectOptions.tos_accepted || connectOptions.tos_accepted ||
getOption( 'woocommerce_setup_jetpack_opted_in' ); getOption( 'woocommerce_setup_jetpack_opted_in' );
const isPending =
isUpdateSettingsRequesting( 'tax' ) ||
isUpdateSettingsRequesting( 'general' );
const isResolving = isPluginsRequesting( 'getJetpackConnectUrl' );
return { return {
isGeneralSettingsError,
generalSettings,
countryCode, countryCode,
taxSettings, generalSettings,
isTaxSettingsError, hasCompleteAddress,
isTaxSettingsRequesting,
isJetpackConnected: isJetpackConnected(), isJetpackConnected: isJetpackConnected(),
isPending,
isResolving,
pluginsToActivate, pluginsToActivate,
taxSettings,
tosAccepted, tosAccepted,
}; };
} ), } ),
withDispatch( ( dispatch ) => { withDispatch( ( dispatch ) => {
const { createNotice } = dispatch( 'core/notices' ); const { createNotice } = dispatch( 'core/notices' );
const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
const { updateAndPersistSettingsForGroup } = dispatch( const { updateAndPersistSettingsForGroup } = dispatch(
SETTINGS_STORE_NAME SETTINGS_STORE_NAME
); );
@ -544,6 +505,7 @@ export default compose(
return { return {
createNotice, createNotice,
updateAndPersistSettingsForGroup, updateAndPersistSettingsForGroup,
updateOptions,
}; };
} ) } )
)( Tax ); )( Tax );

View File

@ -2,6 +2,7 @@
* External Dependencies * External Dependencies
*/ */
import { __ } from '@wordpress/i18n';
import { apiFetch, select } from '@wordpress/data-controls'; import { apiFetch, select } from '@wordpress/data-controls';
import { concat } from 'lodash'; import { concat } from 'lodash';
@ -65,11 +66,16 @@ export function* persistSettingsForGroup( group ) {
} }
// get data slice for keys // get data slice for keys
const dirtyData = yield select( STORE_NAME, 'getSettingsForGroup', group, dirtyKeys ); const dirtyData = yield select(
STORE_NAME,
'getSettingsForGroup',
group,
dirtyKeys
);
const url = `${ NAMESPACE }/settings/${ group }/batch`; const url = `${ NAMESPACE }/settings/${ group }/batch`;
const update = dirtyKeys.reduce( ( updates, key ) => { const update = dirtyKeys.reduce( ( updates, key ) => {
const u = Object.keys( dirtyData[ key ] ).map( k => { const u = Object.keys( dirtyData[ key ] ).map( ( k ) => {
return { id: k, value: dirtyData[ key ][ k ] }; return { id: k, value: dirtyData[ key ][ k ] };
} ); } );
return concat( updates, u ); return concat( updates, u );
@ -80,16 +86,25 @@ export function* persistSettingsForGroup( group ) {
method: 'POST', method: 'POST',
data: { update }, data: { update },
} ); } );
yield setIsRequesting( group, false );
if ( ! results ) { if ( ! results ) {
throw new Error( 'settings did not update' ); throw new Error(
__(
'There was a problem updating your settings.',
'woocommerce-admin'
)
);
} }
// remove dirtyKeys from map - note we're only doing this if there is no error. // remove dirtyKeys from map - note we're only doing this if there is no error.
yield clearIsDirty( group ); yield clearIsDirty( group );
} catch ( e ) { } catch ( e ) {
yield updateErrorForGroup( group, null, e ); yield updateErrorForGroup( group, null, e );
yield setIsRequesting( group, false );
throw e;
} }
// finally set the persisting state
yield setIsRequesting( group, false );
} }
export function clearSettings() { export function clearSettings() {

View File

@ -3,9 +3,9 @@
*/ */
import { getResourceName, getResourcePrefix } from '../utils'; import { getResourceName, getResourcePrefix } from '../utils';
export const getSettingsGroupNames = state => { export const getSettingsGroupNames = ( state ) => {
const groupNames = new Set( const groupNames = new Set(
Object.keys( state ).map( resourceName => { Object.keys( state ).map( ( resourceName ) => {
return getResourcePrefix( resourceName ); return getResourcePrefix( resourceName );
} ) } )
); );
@ -14,11 +14,11 @@ export const getSettingsGroupNames = state => {
export const getSettings = ( state, group ) => { export const getSettings = ( state, group ) => {
const settings = {}; const settings = {};
const settingIds = state[ group ] && state[ group ].data || []; const settingIds = ( state[ group ] && state[ group ].data ) || [];
if ( settingIds.length === 0 ) { if ( settingIds.length === 0 ) {
return settings; return settings;
} }
settingIds.forEach( id => { settingIds.forEach( ( id ) => {
settings[ id ] = state[ getResourceName( group, id ) ].data; settings[ id ] = state[ getResourceName( group, id ) ].data;
} ); } );
return settings; return settings;
@ -47,7 +47,7 @@ export const getSettingsForGroup = ( state, group, keys ) => {
}, {} ); }, {} );
}; };
export const isGetSettingsRequesting = ( state, group ) => { export const isUpdateSettingsRequesting = ( state, group ) => {
return state[ group ] && Boolean( state[ group ].isRequesting ); return state[ group ] && Boolean( state[ group ].isRequesting );
}; };
@ -70,9 +70,16 @@ export const isGetSettingsRequesting = ( state, group ) => {
* @return {*} The value present in the settings state for the given * @return {*} The value present in the settings state for the given
* name. * name.
*/ */
export function getSetting( state, group, name, fallback = false, filter = val => val ) { export function getSetting(
state,
group,
name,
fallback = false,
filter = ( val ) => val
) {
const resourceName = getResourceName( group, name ); const resourceName = getResourceName( group, name );
const value = ( state[ resourceName ] && state[ resourceName ].data ) || fallback; const value =
( state[ resourceName ] && state[ resourceName ].data ) || fallback;
return filter( value, fallback ); return filter( value, fallback );
} }
@ -86,7 +93,7 @@ export const getLastSettingsErrorForGroup = ( state, group ) => {
export const getSettingsError = ( state, group, id ) => { export const getSettingsError = ( state, group, id ) => {
if ( ! id ) { if ( ! id ) {
return state[ group ] && state[ group ].error || false; return ( state[ group ] && state[ group ].error ) || false;
} }
return state[ getResourceName( group, id ) ].error || false; return state[ getResourceName( group, id ) ].error || false;
}; };

View File

@ -1,4 +1,3 @@
/** /**
* External dependencies * External dependencies
*/ */
@ -7,18 +6,23 @@ import { STORE_NAME } from './constants';
import { useCallback } from '@wordpress/element'; import { useCallback } from '@wordpress/element';
export const useSettings = ( group, settingsKeys = [] ) => { export const useSettings = ( group, settingsKeys = [] ) => {
const { requestedSettings, settingsError, isRequesting, isDirty } = useSelect( const {
select => { requestedSettings,
settingsError,
isRequesting,
isDirty,
} = useSelect(
( select ) => {
const { const {
getLastSettingsErrorForGroup, getLastSettingsErrorForGroup,
getSettingsForGroup, getSettingsForGroup,
getIsDirty, getIsDirty,
isGetSettingsRequesting, isUpdateSettingsRequesting,
} = select( STORE_NAME ); } = select( STORE_NAME );
return { return {
requestedSettings: getSettingsForGroup( group, settingsKeys ), requestedSettings: getSettingsForGroup( group, settingsKeys ),
settingsError: Boolean( getLastSettingsErrorForGroup( group ) ), settingsError: Boolean( getLastSettingsErrorForGroup( group ) ),
isRequesting: isGetSettingsRequesting( group ), isRequesting: isUpdateSettingsRequesting( group ),
isDirty: getIsDirty( group, settingsKeys ), isDirty: getIsDirty( group, settingsKeys ),
}; };
}, },
@ -35,14 +39,11 @@ export const useSettings = ( group, settingsKeys = [] ) => {
}, },
[ group ] [ group ]
); );
const persistSettings = useCallback( const persistSettings = useCallback( () => {
() => { // this action would simply persist all settings marked as dirty in the
// this action would simply persist all settings marked as dirty in the // store state and then remove the dirty record in the isDirtyMap
// store state and then remove the dirty record in the isDirtyMap persistSettingsForGroup( group );
persistSettingsForGroup( group ); }, [ group ] );
},
[ group ]
);
const updateAndPersistSettings = useCallback( const updateAndPersistSettings = useCallback(
( name, data ) => { ( name, data ) => {
updateAndPersistSettingsForGroup( group, { [ name ]: data } ); updateAndPersistSettingsForGroup( group, { [ name ]: data } );