This commit is contained in:
Paul Sealock 2020-06-11 11:49:27 +12:00 committed by GitHub
parent 9be6bdd841
commit 41eeb4f9f9
36 changed files with 688 additions and 661 deletions

View File

@ -4,7 +4,7 @@
import { __, sprintf } from '@wordpress/i18n'; import { __, sprintf } from '@wordpress/i18n';
import { Fragment, Suspense, lazy, useState } from '@wordpress/element'; import { Fragment, Suspense, lazy, useState } from '@wordpress/element';
import { compose } from '@wordpress/compose'; import { compose } from '@wordpress/compose';
import { partial, get } from 'lodash'; import { partial } from 'lodash';
import { Dropdown, Button, Icon } from '@wordpress/components'; import { Dropdown, Button, Icon } from '@wordpress/components';
import { applyFilters } from '@wordpress/hooks'; import { applyFilters } from '@wordpress/hooks';
import { Icon as WPIcon, plusCircleFilled } from '@wordpress/icons'; import { Icon as WPIcon, plusCircleFilled } from '@wordpress/icons';
@ -15,6 +15,7 @@ import { Icon as WPIcon, plusCircleFilled } from '@wordpress/icons';
import { H, Spinner } from '@woocommerce/components'; import { H, Spinner } from '@woocommerce/components';
import { import {
SETTINGS_STORE_NAME, SETTINGS_STORE_NAME,
OPTIONS_STORE_NAME,
useUserPreferences, useUserPreferences,
} from '@woocommerce/data'; } from '@woocommerce/data';
@ -329,8 +330,7 @@ const CustomizableDashboard = ( {
export default compose( export default compose(
withSelect( ( select ) => { withSelect( ( select ) => {
const { getOptions } = select( 'wc-api' ); const { getOption } = select( OPTIONS_STORE_NAME );
const { woocommerce_default_date_range: defaultDateRange } = select( const { woocommerce_default_date_range: defaultDateRange } = select(
SETTINGS_STORE_NAME SETTINGS_STORE_NAME
).getSetting( 'wc_admin', 'wcAdminSettings' ); ).getSetting( 'wc_admin', 'wcAdminSettings' );
@ -340,16 +340,13 @@ export default compose(
}; };
if ( isOnboardingEnabled() ) { if ( isOnboardingEnabled() ) {
const options = getOptions( [ withSelectData.homepageEnabled =
'woocommerce_task_list_complete', window.wcAdminFeatures.homepage &&
'woocommerce_task_list_hidden', getOption( 'woocommerce_homescreen_enabled' ) === 'yes';
] );
withSelectData.taskListHidden = withSelectData.taskListHidden =
get( options, [ 'woocommerce_task_list_hidden' ], 'no' ) === getOption( 'woocommerce_task_list_hidden' ) === 'yes';
'yes';
withSelectData.taskListComplete = withSelectData.taskListComplete =
get( options, [ 'woocommerce_task_list_complete' ], 'no' ) === getOption( 'woocommerce_task_list_complete' ) === 'yes';
'yes';
} }
return withSelectData; return withSelectData;

View File

@ -51,6 +51,7 @@ export function getProductIdsForCart(
installedPlugins installedPlugins
) { ) {
const onboarding = getSetting( 'onboarding', {} ); const onboarding = getSetting( 'onboarding', {} );
const productIds = [];
// The population of onboarding.productTypes only happens if the task list should be shown // The population of onboarding.productTypes only happens if the task list should be shown
// so bail early if it isn't present. // so bail early if it isn't present.
@ -58,7 +59,6 @@ export function getProductIdsForCart(
return productIds; return productIds;
} }
const productIds = [];
const productTypes = profileItems.product_types || []; const productTypes = profileItems.product_types || [];
productTypes.forEach( ( productType ) => { productTypes.forEach( ( productType ) => {

View File

@ -11,9 +11,13 @@ import {
} from '@wordpress/element'; } from '@wordpress/element';
import { compose } from '@wordpress/compose'; import { compose } from '@wordpress/compose';
import classnames from 'classnames'; import classnames from 'classnames';
import { get } from 'lodash';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
/**
* WooCommerce dependencies
*/
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
/** /**
* Internal dependencies * Internal dependencies
*/ */
@ -21,9 +25,9 @@ import QuickLinks from '../quick-links';
import StatsOverview from './stats-overview'; import StatsOverview from './stats-overview';
import './style.scss'; import './style.scss';
import { isOnboardingEnabled } from 'dashboard/utils'; import { isOnboardingEnabled } from 'dashboard/utils';
import withSelect from 'wc-api/with-select';
import TaskListPlaceholder from '../task-list/placeholder'; import TaskListPlaceholder from '../task-list/placeholder';
import InboxPanel from '../header/activity-panel/panels/inbox'; import InboxPanel from '../header/activity-panel/panels/inbox';
import withWCApiSelect from 'wc-api/with-select';
const TaskList = lazy( () => const TaskList = lazy( () =>
import( /* webpackChunkName: "task-list" */ '../task-list' ) import( /* webpackChunkName: "task-list" */ '../task-list' )
@ -138,32 +142,27 @@ Layout.propTypes = {
}; };
export default compose( export default compose(
withSelect( ( select ) => { withWCApiSelect( ( select ) => {
const { const {
getOptions,
getUndoDismissRequesting, getUndoDismissRequesting,
isGetOptionsRequesting,
} = select( 'wc-api' ); } = select( 'wc-api' );
const { isUndoRequesting } = getUndoDismissRequesting();
const { getOption, isResolving } = select( OPTIONS_STORE_NAME );
if ( isOnboardingEnabled() ) { if ( isOnboardingEnabled() ) {
const options = getOptions( [
'woocommerce_task_list_complete',
'woocommerce_task_list_hidden',
] );
const { isUndoRequesting } = getUndoDismissRequesting();
return { return {
isUndoRequesting, isUndoRequesting,
requestingTaskList: isGetOptionsRequesting( [
'woocommerce_task_list_complete',
'woocommerce_task_list_hidden',
] ),
taskListComplete: taskListComplete:
get( options, [ 'woocommerce_task_list_complete' ] ) === getOption( 'woocommerce_task_list_complete' ) === 'yes',
'yes',
taskListHidden: taskListHidden:
get( options, [ 'woocommerce_task_list_hidden' ] ) === getOption( 'woocommerce_task_list_hidden' ) === 'yes',
'yes', requestingTaskList:
isResolving( 'getOption', [
'woocommerce_task_list_complete',
] ) ||
isResolving( 'getOption', [
'woocommerce_task_list_hidden',
] ),
}; };
} }

View File

@ -6,7 +6,7 @@ import { withSelect } from '@wordpress/data';
import { Component, lazy, Suspense } from '@wordpress/element'; import { Component, lazy, Suspense } from '@wordpress/element';
import { Router, Route, Switch } from 'react-router-dom'; import { Router, Route, Switch } from 'react-router-dom';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { get, isFunction } from 'lodash'; import { get, isFunction, identity } from 'lodash';
/** /**
* WooCommerce dependencies * WooCommerce dependencies
@ -14,7 +14,12 @@ import { get, isFunction } from 'lodash';
import { useFilters, Spinner } from '@woocommerce/components'; import { useFilters, Spinner } from '@woocommerce/components';
import { getHistory } from '@woocommerce/navigation'; import { getHistory } from '@woocommerce/navigation';
import { getSetting } from '@woocommerce/wc-admin-settings'; import { getSetting } from '@woocommerce/wc-admin-settings';
import { PLUGINS_STORE_NAME, withPluginsHydration } from '@woocommerce/data'; import {
OPTIONS_STORE_NAME,
PLUGINS_STORE_NAME,
withPluginsHydration,
withOptionsHydration,
} from '@woocommerce/data';
/** /**
* Internal dependencies * Internal dependencies
@ -25,7 +30,6 @@ import Header from 'header';
import Notices from './notices'; import Notices from './notices';
import { recordPageView } from 'lib/tracks'; import { recordPageView } from 'lib/tracks';
import TransientNotices from './transient-notices'; import TransientNotices from './transient-notices';
import withWCApiSelect from 'wc-api/with-select';
const StoreAlerts = lazy( () => const StoreAlerts = lazy( () =>
import( /* webpackChunkName: "store-alerts" */ './store-alerts' ) import( /* webpackChunkName: "store-alerts" */ './store-alerts' )
); );
@ -211,13 +215,16 @@ class _PageLayout extends Component {
export const PageLayout = compose( export const PageLayout = compose(
// Use the useFilters HoC so PageLayout is re-rendered when filters are used to add new pages or reports // Use the useFilters HoC so PageLayout is re-rendered when filters are used to add new pages or reports
useFilters( [ PAGES_FILTER, REPORTS_FILTER ] ), useFilters( [ PAGES_FILTER, REPORTS_FILTER ] ),
withWCApiSelect( ( select ) => { window.wcSettings.preloadOptions
const { getOptions } = select( 'wc-api' ); ? withOptionsHydration( {
const options = getOptions( [ 'woocommerce_homescreen_enabled' ] ); ...window.wcSettings.preloadOptions,
} )
: identity,
withSelect( ( select ) => {
const { getOption } = select( OPTIONS_STORE_NAME );
const homepageEnabled = const homepageEnabled =
window.wcAdminFeatures.homepage && window.wcAdminFeatures.homepage &&
get( options, [ 'woocommerce_homescreen_enabled' ], false ) === getOption( 'woocommerce_homescreen_enabled' ) === 'yes';
'yes';
return { homepageEnabled }; return { homepageEnabled };
} ) } )
)( _PageLayout ); )( _PageLayout );

View File

@ -3,6 +3,11 @@
*/ */
import { getSetting } from '@woocommerce/wc-admin-settings'; import { getSetting } from '@woocommerce/wc-admin-settings';
/**
* WooCommerce dependencies
*/
import { withOptionsHydration } from '@woocommerce/data';
/** /**
* Internal dependencies * Internal dependencies
*/ */
@ -26,4 +31,6 @@ const MarketingOverview = () => {
); );
}; };
export default MarketingOverview; export default withOptionsHydration( {
...( window.wcSettings.preloadOptions || {} ),
} )( MarketingOverview );

View File

@ -2,24 +2,23 @@
* External dependencies * External dependencies
*/ */
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { get } from 'lodash';
import { Button } from '@wordpress/components'; import { Button } from '@wordpress/components';
import Gridicon from 'gridicons'; import Gridicon from 'gridicons';
import { compose } from '@wordpress/compose'; import { compose } from '@wordpress/compose';
import { withDispatch } from '@wordpress/data'; import { withDispatch, withSelect } from '@wordpress/data';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
/** /**
* WooCommerce dependencies * WooCommerce dependencies
*/ */
import { Card } from '@woocommerce/components'; import { Card } from '@woocommerce/components';
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import './style.scss'; import './style.scss';
import { recordEvent } from 'lib/tracks'; import { recordEvent } from 'lib/tracks';
import withSelect from 'wc-api/with-select';
import WelcomeImage from './images/welcome.svg'; import WelcomeImage from './images/welcome.svg';
const WelcomeCard = ( { const WelcomeCard = ( {
@ -72,17 +71,17 @@ export { WelcomeCard }
// default export // default export
export default compose( export default compose(
withSelect( ( select ) => { withSelect( ( select ) => {
const { getOptions, isUpdateOptionsRequesting } = select( 'wc-api' ); const { getOption, isOptionsUpdating } = select( OPTIONS_STORE_NAME );
const hideOptionName = 'woocommerce_marketing_overview_welcome_hidden'; const isUpdateRequesting = isOptionsUpdating();
const options = getOptions( [ hideOptionName ] );
const isHidden = get( options, [ hideOptionName ], 'no' ) === 'yes';
const isUpdateRequesting = Boolean( isUpdateOptionsRequesting( [ hideOptionName ] ) );
return { return {
isHidden: isHidden || isUpdateRequesting, isHidden:
getOption( 'woocommerce_marketing_overview_welcome_hidden' ) ===
'yes' || isUpdateRequesting,
}; };
} ), } ),
withDispatch( ( dispatch ) => { withDispatch( ( dispatch ) => {
const { updateOptions } = dispatch( 'wc-api' ); const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
return { return {
updateOptions, updateOptions,
}; };

View File

@ -247,7 +247,9 @@ const hydrateSettings =
export default compose( export default compose(
withSelect( ( select ) => { withSelect( ( select ) => {
const { getNotes } = select( 'wc-api' ); const { getNotes } = select( 'wc-api' );
const { getProfileItems, getOnboardingError } = select( ONBOARDING_STORE_NAME ); const { getProfileItems, getOnboardingError } = select(
ONBOARDING_STORE_NAME
);
const { getActivePlugins } = select( PLUGINS_STORE_NAME ); const { getActivePlugins } = select( PLUGINS_STORE_NAME );
const notesQuery = { const notesQuery = {

View File

@ -5,7 +5,7 @@ import { __, _n, sprintf } from '@wordpress/i18n';
import { Button } from '@wordpress/components'; import { Button } from '@wordpress/components';
import { Component } from '@wordpress/element'; import { Component } from '@wordpress/element';
import { compose } from '@wordpress/compose'; import { compose } from '@wordpress/compose';
import { withDispatch } from '@wordpress/data'; import { withDispatch, withSelect } from '@wordpress/data';
import { filter } from 'lodash'; import { filter } from 'lodash';
/** /**
@ -17,6 +17,7 @@ import {
pluginNames, pluginNames,
ONBOARDING_STORE_NAME, ONBOARDING_STORE_NAME,
PLUGINS_STORE_NAME, PLUGINS_STORE_NAME,
OPTIONS_STORE_NAME
} from '@woocommerce/data'; } from '@woocommerce/data';
/** /**
@ -28,7 +29,6 @@ import ManagementIcon from './images/management';
import SalesTaxIcon from './images/sales_tax'; import SalesTaxIcon from './images/sales_tax';
import ShippingLabels from './images/shipping_labels'; import ShippingLabels from './images/shipping_labels';
import SpeedIcon from './images/speed'; import SpeedIcon from './images/speed';
import withSelect from 'wc-api/with-select';
import { recordEvent } from 'lib/tracks'; import { recordEvent } from 'lib/tracks';
class Benefits extends Component { class Benefits extends Component {
@ -108,12 +108,12 @@ class Benefits extends Component {
goToNextStep(); goToNextStep();
} }
async startPluginInstall() { startPluginInstall() {
const { updateProfileItems, updateOptions } = this.props; const { updateProfileItems, updateOptions } = this.props;
this.setState( { isInstalling: true } ); this.setState( { isInstalling: true } );
await updateOptions( { updateOptions( {
woocommerce_setup_jetpack_opted_in: true, woocommerce_setup_jetpack_opted_in: true,
} ); } );
@ -328,7 +328,7 @@ export default compose(
} ), } ),
withDispatch( ( dispatch ) => { withDispatch( ( dispatch ) => {
const { updateProfileItems } = dispatch( ONBOARDING_STORE_NAME ); const { updateProfileItems } = dispatch( ONBOARDING_STORE_NAME );
const { updateOptions } = dispatch( 'wc-api' ); const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
const { createNotice } = dispatch( 'core/notices' ); const { createNotice } = dispatch( 'core/notices' );
return { return {

View File

@ -4,8 +4,7 @@
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { Component } from '@wordpress/element'; import { Component } from '@wordpress/element';
import { compose } from '@wordpress/compose'; import { compose } from '@wordpress/compose';
import { withDispatch } from '@wordpress/data'; import { withDispatch, withSelect } from '@wordpress/data';
import { get } from 'lodash';
import interpolateComponents from 'interpolate-components'; import interpolateComponents from 'interpolate-components';
import { import {
Button, Button,
@ -14,11 +13,15 @@ import {
Modal, Modal,
} from '@wordpress/components'; } from '@wordpress/components';
/**
* WooCommerce dependencies
*/
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { Link } from '@woocommerce/components'; import { Link } from '@woocommerce/components';
import withSelect from 'wc-api/with-select';
class UsageModal extends Component { class UsageModal extends Component {
constructor( props ) { constructor( props ) {
@ -159,20 +162,15 @@ class UsageModal extends Component {
export default compose( export default compose(
withSelect( ( select ) => { withSelect( ( select ) => {
const { const {
getOptions, getOption,
getOptionsError, getOptionsUpdatingError,
isUpdateOptionsRequesting, isOptionsUpdating,
} = select( 'wc-api' ); } = select( OPTIONS_STORE_NAME );
const options = getOptions( [ 'woocommerce_allow_tracking' ] );
const allowTracking = const allowTracking =
get( options, [ 'woocommerce_allow_tracking' ], false ) === 'yes'; getOption( 'woocommerce_allow_tracking' ) === 'yes';
const isRequesting = Boolean( const isRequesting = Boolean( isOptionsUpdating() );
isUpdateOptionsRequesting( [ 'woocommerce_allow_tracking' ] ) const hasErrors = Boolean( getOptionsUpdatingError() );
);
const hasErrors = Boolean(
getOptionsError( [ 'woocommerce_allow_tracking' ] )
);
return { return {
allowTracking, allowTracking,
@ -182,7 +180,7 @@ export default compose(
} ), } ),
withDispatch( ( dispatch ) => { withDispatch( ( dispatch ) => {
const { createNotice } = dispatch( 'core/notices' ); const { createNotice } = dispatch( 'core/notices' );
const { updateOptions } = dispatch( 'wc-api' ); const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
return { return {
createNotice, createNotice,

View File

@ -3,7 +3,7 @@
*/ */
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { Component, cloneElement, Fragment } from '@wordpress/element'; import { Component, cloneElement, Fragment } from '@wordpress/element';
import { get, isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { compose } from '@wordpress/compose'; import { compose } from '@wordpress/compose';
import classNames from 'classnames'; import classNames from 'classnames';
import { import {
@ -21,7 +21,11 @@ import { Icon, check, chevronRight } from '@wordpress/icons';
*/ */
import { H, List, EllipsisMenu } from '@woocommerce/components'; import { H, List, EllipsisMenu } from '@woocommerce/components';
import { updateQueryString } from '@woocommerce/navigation'; import { updateQueryString } from '@woocommerce/navigation';
import { ONBOARDING_STORE_NAME, PLUGINS_STORE_NAME } from '@woocommerce/data'; import {
PLUGINS_STORE_NAME,
OPTIONS_STORE_NAME,
ONBOARDING_STORE_NAME,
} from '@woocommerce/data';
/** /**
* Internal dependencies * Internal dependencies
@ -118,7 +122,7 @@ class TaskDashboard extends Component {
return getAllTasks( { return getAllTasks( {
profileItems, profileItems,
options: taskListPayments, taskListPayments,
query, query,
toggleCartModal: this.toggleCartModal.bind( this ), toggleCartModal: this.toggleCartModal.bind( this ),
installedPlugins, installedPlugins,
@ -383,7 +387,7 @@ class TaskDashboard extends Component {
export default compose( export default compose(
withSelect( ( select, props ) => { withSelect( ( select, props ) => {
const { getProfileItems } = select( ONBOARDING_STORE_NAME ); const { getProfileItems } = select( ONBOARDING_STORE_NAME );
const { getOptions } = select( 'wc-api' ); const { getOption } = select( OPTIONS_STORE_NAME );
const { const {
getActivePlugins, getActivePlugins,
getInstalledPlugins, getInstalledPlugins,
@ -391,28 +395,18 @@ export default compose(
} = select( PLUGINS_STORE_NAME ); } = select( PLUGINS_STORE_NAME );
const profileItems = getProfileItems(); const profileItems = getProfileItems();
const options = getOptions( [ const modalDismissed =
'woocommerce_task_list_welcome_modal_dismissed', getOption( 'woocommerce_task_list_welcome_modal_dismissed' ) ||
'woocommerce_task_list_hidden', false;
'woocommerce_task_list_tracked_completed_tasks', const taskListPayments = getOption( 'woocommerce_task_list_payments' );
] ); const trackedCompletedTasks =
const modalDismissed = get( getOption( 'woocommerce_task_list_tracked_completed_tasks' ) || [];
options, const payments = getOption( 'woocommerce_task_list_payments' );
[ 'woocommerce_task_list_welcome_modal_dismissed' ],
false
);
const taskListPayments = getOptions( [
'woocommerce_task_list_payments',
] );
const trackedCompletedTasks = get(
options,
[ 'woocommerce_task_list_tracked_completed_tasks' ],
[]
);
const installedPlugins = getInstalledPlugins(); const installedPlugins = getInstalledPlugins();
const tasks = getAllTasks( { const tasks = getAllTasks( {
profileItems, profileItems,
options: getOptions( [ 'woocommerce_task_list_payments' ] ), options: payments,
query: props.query, query: props.query,
installedPlugins, installedPlugins,
} ); } );
@ -437,7 +431,7 @@ export default compose(
}; };
} ), } ),
withDispatch( ( dispatch ) => { withDispatch( ( dispatch ) => {
const { updateOptions } = dispatch( 'wc-api' ); const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
return { return {
updateOptions, updateOptions,
}; };

View File

@ -4,7 +4,6 @@
import { __ } from '@wordpress/i18n'; import { __ } from '@wordpress/i18n';
import { applyFilters } from '@wordpress/hooks'; import { applyFilters } from '@wordpress/hooks';
import { get } from 'lodash';
/** /**
* WooCommerce dependencies * WooCommerce dependencies
@ -25,7 +24,7 @@ import Payments from './tasks/payments';
export function getAllTasks( { export function getAllTasks( {
profileItems, profileItems,
options, taskListPayments,
query, query,
toggleCartModal, toggleCartModal,
installedPlugins, installedPlugins,
@ -55,15 +54,11 @@ export function getAllTasks( {
installedPlugins installedPlugins
); );
const paymentsCompleted = get( const paymentsCompleted = Boolean(
options, taskListPayments && taskListPayments.completed
[ 'woocommerce_task_list_payments', 'completed' ],
false
); );
const paymentsSkipped = get( const paymentsSkipped = Boolean(
options, taskListPayments && taskListPayments.skipped
[ 'woocommerce_task_list_payments', 'skipped' ],
false
); );
const tasks = [ const tasks = [

View File

@ -6,8 +6,8 @@ import apiFetch from '@wordpress/api-fetch';
import { Button } from '@wordpress/components'; import { Button } 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 { filter } from 'lodash';
import { withDispatch } from '@wordpress/data'; import { withDispatch, withSelect } from '@wordpress/data';
/** /**
* WooCommerce dependencies * WooCommerce dependencies
@ -20,13 +20,13 @@ import {
} from '@woocommerce/components'; } from '@woocommerce/components';
import { getHistory, getNewPath } from '@woocommerce/navigation'; import { getHistory, getNewPath } from '@woocommerce/navigation';
import { getSetting, setSetting } from '@woocommerce/wc-admin-settings'; import { getSetting, setSetting } from '@woocommerce/wc-admin-settings';
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
/** /**
* Internal dependencies * Internal dependencies
*/ */
import { queueRecordEvent, recordEvent } from 'lib/tracks'; import { queueRecordEvent, recordEvent } from 'lib/tracks';
import { WC_ADMIN_NAMESPACE } from 'wc-api/constants'; import { WC_ADMIN_NAMESPACE } from 'wc-api/constants';
import withSelect from 'wc-api/with-select';
class Appearance extends Component { class Appearance extends Component {
constructor( props ) { constructor( props ) {
@ -43,7 +43,9 @@ class Appearance extends Component {
isPending: false, isPending: false,
logo: null, logo: null,
stepIndex: 0, stepIndex: 0,
storeNoticeText: props.options.woocommerce_demo_store_notice || '', isUpdatingLogo: false,
isUpdatingNotice: false,
storeNoticeText: props.demoStoreNotice || '',
}; };
this.completeStep = this.completeStep.bind( this ); this.completeStep = this.completeStep.bind( this );
@ -63,18 +65,9 @@ class Appearance extends Component {
} }
} }
async componentDidUpdate( prevProps ) { componentDidUpdate( prevProps ) {
const { isPending, logo, stepIndex } = this.state; const { isPending, logo } = this.state;
const { const { demoStoreNotice } = this.props;
createNotice,
errors,
hasErrors,
isRequesting,
options,
} = this.props;
const step = this.getSteps()[ stepIndex ].key;
const isRequestSuccessful =
! isRequesting && prevProps.isRequesting && ! hasErrors;
if ( logo && ! logo.url && ! isPending ) { if ( logo && ! logo.url && ! isPending ) {
/* eslint-disable react/no-did-update-set-state */ /* eslint-disable react/no-did-update-set-state */
@ -93,38 +86,15 @@ class Appearance extends Component {
} }
if ( if (
options.woocommerce_demo_store_notice && demoStoreNotice &&
prevProps.options.woocommerce_demo_store_notice !== prevProps.demoStoreNotice !== demoStoreNotice
options.woocommerce_demo_store_notice
) { ) {
/* eslint-disable react/no-did-update-set-state */ /* eslint-disable react/no-did-update-set-state */
this.setState( { this.setState( {
storeNoticeText: options.woocommerce_demo_store_notice, storeNoticeText: demoStoreNotice,
} ); } );
/* eslint-enable react/no-did-update-set-state */ /* eslint-enable react/no-did-update-set-state */
} }
if ( step === 'logo' && isRequestSuccessful ) {
createNotice(
'success',
__( 'Store logo updated sucessfully.', 'woocommerce-admin' )
);
this.completeStep();
}
if ( step === 'notice' && isRequestSuccessful ) {
createNotice(
'success',
__(
"🎨 Your store is looking great! Don't forget to continue personalizing it.",
'woocommerce-admin'
)
);
this.completeStep();
}
const newErrors = difference( errors, prevProps.errors );
newErrors.map( ( error ) => createNotice( 'error', error ) );
} }
completeStep() { completeStep() {
@ -222,8 +192,8 @@ class Appearance extends Component {
} ); } );
} }
updateLogo() { async updateLogo() {
const { updateOptions } = this.props; const { updateOptions, createNotice } = this.props;
const { logo } = this.state; const { logo } = this.state;
const { stylesheet, themeMods } = getSetting( 'onboarding', {} ); const { stylesheet, themeMods } = getSetting( 'onboarding', {} );
const updatedThemeMods = { const updatedThemeMods = {
@ -238,13 +208,25 @@ class Appearance extends Component {
themeMods: updatedThemeMods, themeMods: updatedThemeMods,
} ); } );
updateOptions( { this.setState( { isUpdatingLogo: true } );
const update = await updateOptions( {
[ `theme_mods_${ stylesheet }` ]: updatedThemeMods, [ `theme_mods_${ stylesheet }` ]: updatedThemeMods,
} ); } );
if ( update.success ) {
this.setState( { isUpdatingLogo: false } );
createNotice(
'success',
__( 'Store logo updated sucessfully.', 'woocommerce-admin' )
);
this.completeStep();
} else {
createNotice( 'error', update.message );
}
} }
updateNotice() { async updateNotice() {
const { updateOptions } = this.props; const { updateOptions, createNotice } = this.props;
const { storeNoticeText } = this.state; const { storeNoticeText } = this.state;
recordEvent( 'tasklist_appearance_set_store_notice', { recordEvent( 'tasklist_appearance_set_store_notice', {
@ -256,16 +238,36 @@ class Appearance extends Component {
isAppearanceComplete: true, isAppearanceComplete: true,
} ); } );
updateOptions( { this.setState( { isUpdatingNotice: true } );
const update = await updateOptions( {
woocommerce_task_list_appearance_complete: true, woocommerce_task_list_appearance_complete: true,
woocommerce_demo_store: storeNoticeText.length ? 'yes' : 'no', woocommerce_demo_store: storeNoticeText.length ? 'yes' : 'no',
woocommerce_demo_store_notice: storeNoticeText, 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-admin'
)
);
this.completeStep();
} else {
createNotice( 'error', update.message );
}
} }
getSteps() { getSteps() {
const { isDirty, isPending, logo, storeNoticeText } = this.state; const {
const { isRequesting } = this.props; isDirty,
isPending,
logo,
storeNoticeText,
isUpdatingLogo,
} = this.state;
const steps = [ const steps = [
{ {
@ -340,7 +342,7 @@ class Appearance extends Component {
<Button <Button
disabled={ ! logo && ! isDirty } disabled={ ! logo && ! isDirty }
onClick={ this.updateLogo } onClick={ this.updateLogo }
isBusy={ isRequesting } isBusy={ isUpdatingLogo }
isPrimary isPrimary
> >
{ __( 'Proceed', 'woocommerce-admin' ) } { __( 'Proceed', 'woocommerce-admin' ) }
@ -388,8 +390,12 @@ class Appearance extends Component {
} }
render() { render() {
const { isPending, stepIndex } = this.state; const {
const { isRequesting, hasErrors } = this.props; isPending,
stepIndex,
isUpdatingLogo,
isUpdatingNotice,
} = this.state;
const currentStep = this.getSteps()[ stepIndex ].key; const currentStep = this.getSteps()[ stepIndex ].key;
return ( return (
@ -397,7 +403,7 @@ class Appearance extends Component {
<Card className="is-narrow"> <Card className="is-narrow">
<Stepper <Stepper
isPending={ isPending={
( isRequesting && ! hasErrors ) || isPending isUpdatingNotice || isUpdatingLogo || isPending
} }
isVertical isVertical
currentStep={ currentStep } currentStep={ currentStep }
@ -411,49 +417,15 @@ class Appearance extends Component {
export default compose( export default compose(
withSelect( ( select ) => { withSelect( ( select ) => {
const { const { getOption } = select( OPTIONS_STORE_NAME );
getOptions,
getOptionsError,
isUpdateOptionsRequesting,
} = select( 'wc-api' );
const { stylesheet } = getSetting( 'onboarding', {} );
const options = getOptions( [ return {
'woocommerce_demo_store', demoStoreNotice: getOption( 'woocommerce_demo_store_notice' ),
'woocommerce_demo_store_notice', };
] );
const errors = [];
const uploadLogoError = getOptionsError( [
`theme_mods_${ stylesheet }`,
] );
const storeNoticeError = getOptionsError( [
'woocommerce_demo_store',
'woocommerce_demo_store_notice',
] );
if ( uploadLogoError ) {
errors.push( uploadLogoError.message );
}
if ( storeNoticeError ) {
errors.push( storeNoticeError.message );
}
const hasErrors = Boolean( errors.length );
const isRequesting =
Boolean(
isUpdateOptionsRequesting( [ `theme_mods_${ stylesheet }` ] )
) ||
Boolean(
isUpdateOptionsRequesting( [
'woocommerce_task_list_appearance_complete',
'woocommerce_demo_store',
'woocommerce_demo_store_notice',
] )
);
return { errors, getOptionsError, hasErrors, isRequesting, options };
} ), } ),
withDispatch( ( dispatch ) => { withDispatch( ( dispatch ) => {
const { createNotice } = dispatch( 'core/notices' ); const { createNotice } = dispatch( 'core/notices' );
const { updateOptions } = dispatch( 'wc-api' ); const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
return { return {
createNotice, createNotice,

View File

@ -5,17 +5,13 @@ import { __ } from '@wordpress/i18n';
import { Component, Fragment } from '@wordpress/element'; import { Component, Fragment } from '@wordpress/element';
import { Button } from '@wordpress/components'; import { Button } from '@wordpress/components';
import { compose } from '@wordpress/compose'; import { compose } from '@wordpress/compose';
import { withDispatch } from '@wordpress/data'; import { withDispatch, withSelect } from '@wordpress/data';
/** /**
* WooCommerce dependencies * WooCommerce dependencies
*/ */
import { Form, H, TextControl } from '@woocommerce/components'; import { Form, H, TextControl } from '@woocommerce/components';
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
/**
* Internal dependencies
*/
import withSelect from 'wc-api/with-select';
class PayFast extends Component { class PayFast extends Component {
getInitialConfigValues = () => { getInitialConfigValues = () => {
@ -42,45 +38,34 @@ class PayFast extends Component {
return errors; return errors;
}; };
componentDidUpdate( prevProps ) { updateSettings = async ( values ) => {
const { const { updateOptions, createNotice, markConfigured } = this.props;
createNotice,
isOptionsRequesting,
hasOptionsError,
markConfigured,
} = this.props;
if ( prevProps.isOptionsRequesting && ! isOptionsRequesting ) { const update = await updateOptions( {
if ( ! hasOptionsError ) {
markConfigured( 'bacs' );
createNotice(
'success',
__(
'Direct bank transfer details added successfully',
'woocommerce-admin'
)
);
} else {
createNotice(
'error',
__(
'There was a problem saving your payment setings',
'woocommerce-admin'
)
);
}
}
}
updateSettings = ( values ) => {
const { updateOptions } = this.props;
updateOptions( {
woocommerce_bacs_settings: { woocommerce_bacs_settings: {
enabled: 'yes', enabled: 'yes',
}, },
woocommerce_bacs_accounts: [ values ], woocommerce_bacs_accounts: [ values ],
} ); } );
if ( update.success ) {
markConfigured( 'bacs' );
createNotice(
'success',
__(
'Direct bank transfer details added successfully',
'woocommerce-admin'
)
);
} else {
createNotice(
'error',
__(
'There was a problem saving your payment setings',
'woocommerce-admin'
)
);
}
}; };
render() { render() {
@ -171,28 +156,16 @@ class PayFast extends Component {
export default compose( export default compose(
withSelect( ( select ) => { withSelect( ( select ) => {
const { getOptionsError, isUpdateOptionsRequesting } = select( const { isOptionsUpdating } = select( OPTIONS_STORE_NAME );
'wc-api' const isOptionsRequesting = isOptionsUpdating();
);
const isOptionsRequesting = Boolean(
isUpdateOptionsRequesting( [
'woocommerce_bacs_settings',
'woocommerce_bacs_accounts',
] )
);
const hasOptionsError = getOptionsError( [
'woocommerce_bacs_settings',
'woocommerce_bacs_accounts',
] );
return { return {
hasOptionsError,
isOptionsRequesting, isOptionsRequesting,
}; };
} ), } ),
withDispatch( ( dispatch ) => { withDispatch( ( dispatch ) => {
const { createNotice } = dispatch( 'core/notices' ); const { createNotice } = dispatch( 'core/notices' );
const { updateOptions } = dispatch( 'wc-api' ); const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
return { return {
createNotice, createNotice,

View File

@ -19,8 +19,9 @@ import {
} from '@woocommerce/navigation'; } from '@woocommerce/navigation';
import { import {
ONBOARDING_STORE_NAME, ONBOARDING_STORE_NAME,
pluginNames, OPTIONS_STORE_NAME,
PLUGINS_STORE_NAME, PLUGINS_STORE_NAME,
pluginNames,
} from '@woocommerce/data'; } from '@woocommerce/data';
/** /**
@ -53,32 +54,15 @@ class Payments extends Component {
if ( prevProps === this.props ) { if ( prevProps === this.props ) {
return; return;
} }
const { createNotice, errors, methods, requesting } = this.props; const { methods } = this.props;
let recommendedMethod = 'stripe'; let recommendedMethod = 'stripe';
methods.forEach( ( method ) => { methods.forEach( ( method ) => {
const { key, title, visible } = method; const { key, visible } = method;
if ( key === 'wcpay' && visible ) { if ( key === 'wcpay' && visible ) {
recommendedMethod = 'wcpay'; recommendedMethod = 'wcpay';
} }
if (
prevProps.requesting[ key ] &&
! requesting[ key ] &&
errors[ key ]
) {
createNotice(
'error',
sprintf(
__(
'There was a problem updating settings for %s',
'woocommerce-admin'
),
title
)
);
}
} ); } );
if ( this.state.recommendedMethod !== recommendedMethod ) { if ( this.state.recommendedMethod !== recommendedMethod ) {
@ -88,10 +72,10 @@ class Payments extends Component {
} }
} }
completeTask() { async completeTask() {
const { createNotice, methods, updateOptions } = this.props; const { createNotice, methods, updateOptions } = this.props;
updateOptions( { const update = await updateOptions( {
woocommerce_task_list_payments: { woocommerce_task_list_payments: {
completed: 1, completed: 1,
timestamp: Math.floor( Date.now() / 1000 ), timestamp: Math.floor( Date.now() / 1000 ),
@ -104,15 +88,25 @@ class Payments extends Component {
.map( ( method ) => method.key ), .map( ( method ) => method.key ),
} ); } );
createNotice( if ( update.success ) {
'success', createNotice(
__( 'success',
'💰 Ka-ching! Your store can now accept payments 💳', __(
'woocommerce-admin' '💰 Ka-ching! Your store can now accept payments 💳',
) 'woocommerce-admin'
); )
);
getHistory().push( getNewPath( {}, '/', {} ) ); getHistory().push( getNewPath( {}, '/', {} ) );
} else {
createNotice(
'error',
__(
'There was a problem updating settings',
'woocommerce-admin'
)
);
}
} }
skipTask() { skipTask() {
@ -218,8 +212,8 @@ class Payments extends Component {
render() { render() {
const currentMethod = this.getCurrentMethod(); const currentMethod = this.getCurrentMethod();
const { methods, query } = this.props;
const { enabledMethods, recommendedMethod } = this.state; const { enabledMethods, recommendedMethod } = this.state;
const { methods, query, requesting } = this.props;
const configuredMethods = methods.filter( const configuredMethods = methods.filter(
( method ) => method.isConfigured ( method ) => method.isConfigured
).length; ).length;
@ -344,7 +338,11 @@ class Payments extends Component {
) } ) }
</Button> </Button>
) : ( ) : (
<Button isPrimary onClick={ this.completeTask }> <Button
isPrimary
isBusy={ requesting }
onClick={ this.completeTask }
>
{ __( 'Done', 'woocommerce-admin' ) } { __( 'Done', 'woocommerce-admin' ) }
</Button> </Button>
) } ) }
@ -357,18 +355,15 @@ class Payments extends Component {
export default compose( export default compose(
withSelect( ( select ) => { withSelect( ( select ) => {
const { getProfileItems } = select( ONBOARDING_STORE_NAME ); const { getProfileItems } = select( ONBOARDING_STORE_NAME );
const { const { getOption, isOptionsUpdating } = select( OPTIONS_STORE_NAME );
getOptions,
getUpdateOptionsError,
isUpdateOptionsRequesting,
} = select( 'wc-api' );
const { getActivePlugins, isJetpackConnected } = select( const { getActivePlugins, isJetpackConnected } = select(
PLUGINS_STORE_NAME PLUGINS_STORE_NAME
); );
const activePlugins = getActivePlugins(); const activePlugins = getActivePlugins();
const profileItems = getProfileItems(); const profileItems = getProfileItems();
const options = getOptions( [
const optionNames = [
'woocommerce_default_country', 'woocommerce_default_country',
'woocommerce_woocommerce_payments_settings', 'woocommerce_woocommerce_payments_settings',
'woocommerce_stripe_settings', 'woocommerce_stripe_settings',
@ -381,7 +376,12 @@ export default compose(
'woocommerce_cod_settings', 'woocommerce_cod_settings',
'woocommerce_bacs_settings', 'woocommerce_bacs_settings',
'woocommerce_bacs_accounts', 'woocommerce_bacs_accounts',
] ); ];
const options = optionNames.reduce( ( result, name ) => {
result[ name ] = getOption( name );
return result;
}, {} );
const countryCode = getCountryCode( const countryCode = getCountryCode(
options.woocommerce_default_country options.woocommerce_default_country
); );
@ -394,20 +394,10 @@ export default compose(
profileItems, profileItems,
} ); } );
const errors = {}; const requesting = isOptionsUpdating();
const requesting = {};
methods.forEach( ( method ) => {
errors[ method.key ] = Boolean(
getUpdateOptionsError( [ method.optionName ] )
);
requesting[ method.key ] = Boolean(
isUpdateOptionsRequesting( [ method.optionName ] )
);
} );
return { return {
countryCode, countryCode,
errors,
profileItems, profileItems,
activePlugins, activePlugins,
options, options,
@ -417,7 +407,7 @@ export default compose(
} ), } ),
withDispatch( ( dispatch ) => { withDispatch( ( dispatch ) => {
const { createNotice } = dispatch( 'core/notices' ); const { createNotice } = dispatch( 'core/notices' );
const { updateOptions } = dispatch( 'wc-api' ); const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
return { return {
createNotice, createNotice,
updateOptions, updateOptions,

View File

@ -6,17 +6,13 @@ import { Component, Fragment } from '@wordpress/element';
import { Button } from '@wordpress/components'; import { Button } from '@wordpress/components';
import interpolateComponents from 'interpolate-components'; import interpolateComponents from 'interpolate-components';
import { compose } from '@wordpress/compose'; import { compose } from '@wordpress/compose';
import { withDispatch } from '@wordpress/data'; import { withDispatch, withSelect } from '@wordpress/data';
/** /**
* WooCommerce dependencies * WooCommerce dependencies
*/ */
import { Form, Link, Stepper, TextControl } from '@woocommerce/components'; import { Form, Link, Stepper, TextControl } from '@woocommerce/components';
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
/**
* Internal dependencies
*/
import withSelect from 'wc-api/with-select';
class PayFast extends Component { class PayFast extends Component {
getInitialConfigValues = () => { getInitialConfigValues = () => {
@ -54,39 +50,12 @@ class PayFast extends Component {
return errors; return errors;
}; };
componentDidUpdate( prevProps ) { updateSettings = async ( values ) => {
const { const { updateOptions, createNotice, markConfigured } = this.props;
createNotice,
isOptionsRequesting,
hasOptionsError,
markConfigured,
} = this.props;
if ( prevProps.isOptionsRequesting && ! isOptionsRequesting ) {
if ( ! hasOptionsError ) {
markConfigured( 'payfast' );
createNotice(
'success',
__( 'PayFast connected successfully', 'woocommerce-admin' )
);
} else {
createNotice(
'error',
__(
'There was a problem saving your payment setings',
'woocommerce-admin'
)
);
}
}
}
updateSettings = ( values ) => {
const { updateOptions } = this.props;
// Because the PayFast extension only works with the South African Rand // Because the PayFast extension only works with the South African Rand
// currency, force the store to use it while setting the PayFast settings // currency, force the store to use it while setting the PayFast settings
updateOptions( { const update = await updateOptions( {
woocommerce_currency: 'ZAR', woocommerce_currency: 'ZAR',
woocommerce_payfast_settings: { woocommerce_payfast_settings: {
merchant_id: values.merchant_id, merchant_id: values.merchant_id,
@ -95,6 +64,22 @@ class PayFast extends Component {
enabled: 'yes', enabled: 'yes',
}, },
} ); } );
if ( update.success ) {
markConfigured( 'payfast' );
createNotice(
'success',
__( 'PayFast connected successfully', 'woocommerce-admin' )
);
} else {
createNotice(
'error',
__(
'There was a problem saving your payment setings',
'woocommerce-admin'
)
);
}
}; };
renderConnectStep() { renderConnectStep() {
@ -190,28 +175,16 @@ class PayFast extends Component {
export default compose( export default compose(
withSelect( ( select ) => { withSelect( ( select ) => {
const { getOptionsError, isUpdateOptionsRequesting } = select( const { isOptionsUpdating } = select( OPTIONS_STORE_NAME );
'wc-api' const isOptionsRequesting = isOptionsUpdating();
);
const isOptionsRequesting = Boolean(
isUpdateOptionsRequesting( [
'woocommerce_currency',
'woocommerce_payfast_settings',
] )
);
const hasOptionsError = getOptionsError( [
'woocommerce_currency',
'woocommerce_payfast_settings',
] );
return { return {
hasOptionsError,
isOptionsRequesting, isOptionsRequesting,
}; };
} ), } ),
withDispatch( ( dispatch ) => { withDispatch( ( dispatch ) => {
const { createNotice } = dispatch( 'core/notices' ); const { createNotice } = dispatch( 'core/notices' );
const { updateOptions } = dispatch( 'wc-api' ); const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
return { return {
createNotice, createNotice,

View File

@ -7,7 +7,7 @@ import { Button } 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 interpolateComponents from 'interpolate-components'; import interpolateComponents from 'interpolate-components';
import { withDispatch } from '@wordpress/data'; import { withDispatch, withSelect } from '@wordpress/data';
/** /**
* WooCommerce dependencies * WooCommerce dependencies
@ -15,8 +15,7 @@ import { withDispatch } from '@wordpress/data';
import { Form, Link, Stepper, TextControl } from '@woocommerce/components'; import { Form, Link, Stepper, TextControl } from '@woocommerce/components';
import { getQuery } from '@woocommerce/navigation'; import { getQuery } from '@woocommerce/navigation';
import { WC_ADMIN_NAMESPACE } from 'wc-api/constants'; import { WC_ADMIN_NAMESPACE } from 'wc-api/constants';
import withSelect from 'wc-api/with-select'; import { PLUGINS_STORE_NAME, OPTIONS_STORE_NAME } from '@woocommerce/data';
import { PLUGINS_STORE_NAME } from '@woocommerce/data';
class PayPal extends Component { class PayPal extends Component {
constructor( props ) { constructor( props ) {
@ -119,13 +118,12 @@ class PayPal extends Component {
async updateSettings( values ) { async updateSettings( values ) {
const { const {
createNotice, createNotice,
isSettingsError,
options, options,
updateOptions, updateOptions,
markConfigured, markConfigured,
} = this.props; } = this.props;
await updateOptions( { const update = await updateOptions( {
woocommerce_ppec_paypal_settings: { woocommerce_ppec_paypal_settings: {
...options.woocommerce_ppec_paypal_settings, ...options.woocommerce_ppec_paypal_settings,
api_username: values.api_username, api_username: values.api_username,
@ -134,7 +132,7 @@ class PayPal extends Component {
}, },
} ); } );
if ( ! isSettingsError ) { if ( update.success ) {
createNotice( createNotice(
'success', 'success',
__( 'PayPal connected successfully.', 'woocommerce-admin' ) __( 'PayPal connected successfully.', 'woocommerce-admin' )
@ -178,7 +176,7 @@ class PayPal extends Component {
} }
renderManualConfig() { renderManualConfig() {
const { isOptionsRequesting } = this.props; const { isOptionsUpdating } = this.props;
const link = ( const link = (
<Link <Link
href="https://docs.woocommerce.com/document/paypal-express-checkout/#section-8" href="https://docs.woocommerce.com/document/paypal-express-checkout/#section-8"
@ -225,7 +223,7 @@ class PayPal extends Component {
<Button <Button
onClick={ handleSubmit } onClick={ handleSubmit }
isPrimary isPrimary
disabled={ isOptionsRequesting } isBusy={ isOptionsUpdating }
> >
{ __( 'Proceed', 'woocommerce-admin' ) } { __( 'Proceed', 'woocommerce-admin' ) }
</Button> </Button>
@ -291,23 +289,20 @@ PayPal.defaultProps = {
export default compose( export default compose(
withSelect( ( select ) => { withSelect( ( select ) => {
const { getOptions, isGetOptionsRequesting } = select( 'wc-api' ); const { getOption, isOptionsUpdating } = select( OPTIONS_STORE_NAME );
const { getActivePlugins } = select( PLUGINS_STORE_NAME ); const { getActivePlugins } = select( PLUGINS_STORE_NAME );
const options = getOptions( [ 'woocommerce_ppec_paypal_settings' ] ); const options = getOption( 'woocommerce_ppec_paypal_settings' );
const isOptionsRequesting = Boolean(
isGetOptionsRequesting( [ 'woocommerce_ppec_paypal_settings' ] )
);
const activePlugins = getActivePlugins(); const activePlugins = getActivePlugins();
return { return {
activePlugins, activePlugins,
options, options,
isOptionsRequesting, isOptionsUpdating: isOptionsUpdating(),
}; };
} ), } ),
withDispatch( ( dispatch ) => { withDispatch( ( dispatch ) => {
const { createNotice } = dispatch( 'core/notices' ); const { createNotice } = dispatch( 'core/notices' );
const { updateOptions } = dispatch( 'wc-api' ); const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
return { return {
createNotice, createNotice,
updateOptions, updateOptions,

View File

@ -5,7 +5,7 @@ import { __ } from '@wordpress/i18n';
import { Component, Fragment } from '@wordpress/element'; import { Component, Fragment } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch'; import apiFetch from '@wordpress/api-fetch';
import { Button } from '@wordpress/components'; import { Button } from '@wordpress/components';
import { withDispatch } from '@wordpress/data'; import { withDispatch, withSelect } from '@wordpress/data';
import { compose } from '@wordpress/compose'; import { compose } from '@wordpress/compose';
/** /**
@ -13,9 +13,9 @@ import { compose } from '@wordpress/compose';
*/ */
import { getQuery } from '@woocommerce/navigation'; import { getQuery } from '@woocommerce/navigation';
import { WC_ADMIN_NAMESPACE } from 'wc-api/constants'; import { WC_ADMIN_NAMESPACE } from 'wc-api/constants';
import withSelect from 'wc-api/with-select';
import { Stepper } from '@woocommerce/components'; import { Stepper } from '@woocommerce/components';
import { getAdminLink } from '@woocommerce/wc-admin-settings'; import { getAdminLink } from '@woocommerce/wc-admin-settings';
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
class Square extends Component { class Square extends Component {
constructor( props ) { constructor( props ) {
@ -145,15 +145,11 @@ class Square extends Component {
export default compose( export default compose(
withSelect( ( select ) => { withSelect( ( select ) => {
const { getOptions, isGetOptionsRequesting } = select( 'wc-api' ); const { getOption, isResolving } = select( OPTIONS_STORE_NAME );
const options = getOptions( [ const options = getOption( 'woocommerce_square_credit_card_settings' );
const optionsIsRequesting = isResolving( 'getOption', [
'woocommerce_square_credit_card_settings', 'woocommerce_square_credit_card_settings',
] ); ] );
const optionsIsRequesting = Boolean(
isGetOptionsRequesting( [
'woocommerce_square_credit_card_settings',
] )
);
return { return {
options, options,
@ -162,7 +158,7 @@ export default compose(
} ), } ),
withDispatch( ( dispatch ) => { withDispatch( ( dispatch ) => {
const { createNotice } = dispatch( 'core/notices' ); const { createNotice } = dispatch( 'core/notices' );
const { updateOptions } = dispatch( 'wc-api' ); const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
return { return {
createNotice, createNotice,
updateOptions, updateOptions,

View File

@ -5,10 +5,9 @@ import { __ } from '@wordpress/i18n';
import { Component, Fragment } from '@wordpress/element'; import { Component, Fragment } from '@wordpress/element';
import { compose } from '@wordpress/compose'; import { compose } from '@wordpress/compose';
import apiFetch from '@wordpress/api-fetch'; import apiFetch from '@wordpress/api-fetch';
import { withDispatch } from '@wordpress/data'; import { withDispatch, withSelect } from '@wordpress/data';
import interpolateComponents from 'interpolate-components'; import interpolateComponents from 'interpolate-components';
import { Button } from '@wordpress/components'; import { Button } from '@wordpress/components';
import { get } from 'lodash';
/** /**
* WooCommerce dependencies * WooCommerce dependencies
@ -17,8 +16,7 @@ import { Form, Link, Stepper, TextControl } from '@woocommerce/components';
import { getAdminLink } from '@woocommerce/wc-admin-settings'; import { getAdminLink } from '@woocommerce/wc-admin-settings';
import { getQuery } from '@woocommerce/navigation'; import { getQuery } from '@woocommerce/navigation';
import { WCS_NAMESPACE } from 'wc-api/constants'; import { WCS_NAMESPACE } from 'wc-api/constants';
import withSelect from 'wc-api/with-select'; import { PLUGINS_STORE_NAME, OPTIONS_STORE_NAME } from '@woocommerce/data';
import { PLUGINS_STORE_NAME } from '@woocommerce/data';
class Stripe extends Component { class Stripe extends Component {
constructor( props ) { constructor( props ) {
@ -54,26 +52,7 @@ class Stripe extends Component {
} }
componentDidUpdate( prevProps ) { componentDidUpdate( prevProps ) {
const { const { activePlugins } = this.props;
activePlugins,
createNotice,
isOptionsRequesting,
hasOptionsError,
} = this.props;
if ( prevProps.isOptionsRequesting && ! isOptionsRequesting ) {
if ( ! hasOptionsError ) {
this.completeMethod();
} else {
createNotice(
'error',
__(
'There was a problem saving your payment setings',
'woocommerce-admin'
)
);
}
}
if ( if (
! prevProps.activePlugins.includes( ! prevProps.activePlugins.includes(
@ -154,10 +133,10 @@ class Stripe extends Component {
); );
} }
updateSettings( values ) { async updateSettings( values ) {
const { updateOptions, stripeSettings } = this.props; const { updateOptions, stripeSettings, createNotice } = this.props;
updateOptions( { const update = await updateOptions( {
woocommerce_stripe_settings: { woocommerce_stripe_settings: {
...stripeSettings, ...stripeSettings,
publishable_key: values.publishable_key, publishable_key: values.publishable_key,
@ -165,6 +144,18 @@ class Stripe extends Component {
enabled: 'yes', enabled: 'yes',
}, },
} ); } );
if ( update.success ) {
this.completeMethod();
} else {
createNotice(
'error',
__(
'There was a problem saving your payment setings',
'woocommerce-admin'
)
);
}
} }
getInitialConfigValues() { getInitialConfigValues() {
@ -194,7 +185,7 @@ class Stripe extends Component {
} }
renderManualConfig() { renderManualConfig() {
const { isOptionsRequesting } = this.props; const { isOptionsUpdating } = this.props;
const stripeHelp = interpolateComponents( { const stripeHelp = interpolateComponents( {
mixedString: __( mixedString: __(
'Your API details can be obtained from your {{docsLink}}Stripe account{{/docsLink}}. Dont have a Stripe account? {{registerLink}}Create one.{{/registerLink}}', 'Your API details can be obtained from your {{docsLink}}Stripe account{{/docsLink}}. Dont have a Stripe account? {{registerLink}}Create one.{{/registerLink}}',
@ -246,7 +237,7 @@ class Stripe extends Component {
<Button <Button
isPrimary isPrimary
isBusy={ isOptionsRequesting } isBusy={ isOptionsUpdating }
onClick={ handleSubmit } onClick={ handleSubmit }
> >
{ __( 'Proceed', 'woocommerce-admin' ) } { __( 'Proceed', 'woocommerce-admin' ) }
@ -294,14 +285,14 @@ class Stripe extends Component {
} }
render() { render() {
const { installStep, isOptionsRequesting } = this.props; const { installStep, isOptionsUpdating } = this.props;
const { isPending } = this.state; const { isPending } = this.state;
return ( return (
<Stepper <Stepper
isVertical isVertical
isPending={ isPending={
! installStep.isComplete || isOptionsRequesting || isPending ! installStep.isComplete || isOptionsUpdating || isPending
} }
currentStep={ installStep.isComplete ? 'connect' : 'install' } currentStep={ installStep.isComplete ? 'connect' : 'install' }
steps={ [ installStep, this.getConnectStep() ] } steps={ [ installStep, this.getConnectStep() ] }
@ -312,38 +303,21 @@ class Stripe extends Component {
export default compose( export default compose(
withSelect( ( select ) => { withSelect( ( select ) => {
const { const { getOption, isOptionsUpdating } = select( OPTIONS_STORE_NAME );
getOptions,
getOptionsError,
isUpdateOptionsRequesting,
} = select( 'wc-api' );
const { getActivePlugins, isJetpackConnected } = select( const { getActivePlugins, isJetpackConnected } = select(
PLUGINS_STORE_NAME PLUGINS_STORE_NAME
); );
const options = getOptions( [ 'woocommerce_stripe_settings' ] );
const stripeSettings = get(
options,
[ 'woocommerce_stripe_settings' ],
[]
);
const isOptionsRequesting = Boolean(
isUpdateOptionsRequesting( [ 'woocommerce_stripe_settings' ] )
);
const hasOptionsError = getOptionsError( [
'woocommerce_stripe_settings',
] );
return { return {
activePlugins: getActivePlugins(), activePlugins: getActivePlugins(),
hasOptionsError,
isJetpackConnected: isJetpackConnected(), isJetpackConnected: isJetpackConnected(),
isOptionsRequesting, isOptionsUpdating: isOptionsUpdating(),
stripeSettings, stripeSettings: getOption( 'woocommerce_stripe_settings' ) || [],
}; };
} ), } ),
withDispatch( ( dispatch ) => { withDispatch( ( dispatch ) => {
const { createNotice } = dispatch( 'core/notices' ); const { createNotice } = dispatch( 'core/notices' );
const { updateOptions } = dispatch( 'wc-api' ); const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
return { return {
createNotice, createNotice,
updateOptions, updateOptions,

View File

@ -5,7 +5,7 @@ import { __ } from '@wordpress/i18n';
import { Button } from '@wordpress/components'; import { Button } 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, get } from 'lodash'; import { difference, filter } from 'lodash';
import interpolateComponents from 'interpolate-components'; import interpolateComponents from 'interpolate-components';
import { withDispatch, withSelect } from '@wordpress/data'; import { withDispatch, withSelect } from '@wordpress/data';
@ -19,7 +19,11 @@ import {
getSetting, getSetting,
setSetting, setSetting,
} from '@woocommerce/wc-admin-settings'; } from '@woocommerce/wc-admin-settings';
import { SETTINGS_STORE_NAME, PLUGINS_STORE_NAME } from '@woocommerce/data'; import {
SETTINGS_STORE_NAME,
PLUGINS_STORE_NAME,
OPTIONS_STORE_NAME,
} from '@woocommerce/data';
/** /**
* Internal dependencies * Internal dependencies
@ -27,7 +31,6 @@ import { SETTINGS_STORE_NAME, PLUGINS_STORE_NAME } from '@woocommerce/data';
import Connect from 'dashboard/components/connect'; import Connect from 'dashboard/components/connect';
import { getCountryCode } from 'dashboard/utils'; import { getCountryCode } from 'dashboard/utils';
import StoreLocation from './steps/location'; import StoreLocation from './steps/location';
import withWCApiSelect from 'wc-api/with-select';
import { recordEvent, queueRecordEvent } from 'lib/tracks'; import { recordEvent, queueRecordEvent } from 'lib/tracks';
class Tax extends Component { class Tax extends Component {
@ -491,38 +494,16 @@ class Tax extends Component {
} }
export default compose( export default compose(
withWCApiSelect( ( select ) => {
const { getOptions } = select( 'wc-api' );
const { getActivePlugins, isJetpackConnected } = select(
PLUGINS_STORE_NAME
);
const activePlugins = getActivePlugins();
const pluginsToActivate = difference(
[ 'jetpack', 'woocommerce-services' ],
activePlugins
);
const options = getOptions( [
'wc_connect_options',
'woocommerce_setup_jetpack_opted_in',
] );
const connectOptions = get( options, 'wc_connect_options', {} );
const tosAccepted =
connectOptions.tos_accepted ||
options.woocommerce_setup_jetpack_opted_in;
return {
isJetpackConnected: isJetpackConnected(),
pluginsToActivate,
tosAccepted,
};
} ),
withSelect( ( select ) => { withSelect( ( select ) => {
const { const {
getSettings, getSettings,
getSettingsError, getSettingsError,
isGetSettingsRequesting, isGetSettingsRequesting,
} = select( SETTINGS_STORE_NAME ); } = select( SETTINGS_STORE_NAME );
const { getOption } = select( OPTIONS_STORE_NAME );
const { getActivePlugins, isJetpackConnected } = select(
PLUGINS_STORE_NAME
);
const { general: generalSettings = {} } = getSettings( 'general' ); const { general: generalSettings = {} } = getSettings( 'general' );
const isGeneralSettingsError = Boolean( getSettingsError( 'general' ) ); const isGeneralSettingsError = Boolean( getSettingsError( 'general' ) );
@ -537,6 +518,16 @@ export default compose(
const isTaxSettingsError = Boolean( getSettingsError( 'tax' ) ); const isTaxSettingsError = Boolean( getSettingsError( 'tax' ) );
const isTaxSettingsRequesting = isGetSettingsRequesting( 'tax' ); const isTaxSettingsRequesting = isGetSettingsRequesting( 'tax' );
const activePlugins = getActivePlugins();
const pluginsToActivate = difference(
[ 'jetpack', 'woocommerce-services' ],
activePlugins
);
const connectOptions = getOption( 'wc_connect_options' ) || {};
const tosAccepted =
connectOptions.tos_accepted ||
getOption( 'woocommerce_setup_jetpack_opted_in' );
return { return {
isGeneralSettingsError, isGeneralSettingsError,
isGeneralSettingsRequesting, isGeneralSettingsRequesting,
@ -545,6 +536,9 @@ export default compose(
taxSettings, taxSettings,
isTaxSettingsError, isTaxSettingsError,
isTaxSettingsRequesting, isTaxSettingsRequesting,
isJetpackConnected: isJetpackConnected(),
pluginsToActivate,
tosAccepted,
}; };
} ), } ),
withDispatch( ( dispatch ) => { withDispatch( ( dispatch ) => {

View File

@ -1,12 +0,0 @@
/**
* Internal dependencies
*/
import operations from './operations';
import selectors from './selectors';
import mutations from './mutations';
export default {
operations,
selectors,
mutations,
};

View File

@ -1,16 +0,0 @@
/**
* Internal dependencies
*/
import { getResourceName } from '../utils';
const updateOptions = ( operations ) => ( options ) => {
const resourceName = getResourceName(
'options-update',
Object.keys( options )
);
operations.update( [ resourceName ], { [ resourceName ]: options } );
};
export default {
updateOptions,
};

View File

@ -1,84 +0,0 @@
/**
* External dependencies
*/
import apiFetch from '@wordpress/api-fetch';
/**
* Internal dependencies
*/
import { getResourceIdentifier, getResourceName } from '../utils';
import { WC_ADMIN_NAMESPACE } from '../constants';
function read( resourceNames, fetch = apiFetch ) {
return [ ...readOptions( resourceNames, fetch ) ];
}
function update( resourceNames, data, fetch = apiFetch ) {
return [ ...updateOptions( resourceNames, data, fetch ) ];
}
function readOptions( resourceNames, fetch ) {
const filteredNames = resourceNames.filter( ( name ) => {
return name.startsWith( 'options' );
} );
return filteredNames.map( async ( resourceName ) => {
const optionNames = getResourceIdentifier( resourceName );
const url =
WC_ADMIN_NAMESPACE + '/options?options=' + optionNames.join( ',' );
return fetch( { path: url } )
.then( optionsToResource )
.catch( ( error ) => {
return { [ resourceName ]: { error: String( error.message ) } };
} );
} );
}
function updateOptions( resourceNames, data, fetch ) {
const url = WC_ADMIN_NAMESPACE + '/options';
const filteredNames = resourceNames.filter( ( name ) => {
return name.startsWith( 'options-update' );
} );
return filteredNames.map( async ( resourceName ) => {
return fetch( {
path: url,
method: 'POST',
data: data[ resourceName ],
} )
.then( () => optionsToResource( data[ resourceName ], true ) )
.catch( ( error ) => {
return { [ resourceName ]: { data: {}, error } };
} );
} );
}
function optionsToResource( options, updateResource = false ) {
const optionNames = Object.keys( options );
const resourceName = getResourceName(
updateResource ? 'options-update' : 'options',
optionNames
);
const resources = {};
optionNames.forEach(
( optionName ) =>
( resources[ getResourceName( 'options', optionName ) ] = {
data: options[ optionName ],
} )
);
return {
[ resourceName ]: {
data: optionNames,
},
...resources,
};
}
export default {
read,
update,
};

View File

@ -1,81 +0,0 @@
/**
* External dependencies
*/
import { isNil } from 'lodash';
/**
* WooCommerce dependencies
*/
import { getSetting } from '@woocommerce/wc-admin-settings';
/**
* Internal dependencies
*/
import { DEFAULT_REQUIREMENT } from '../constants';
import { getResourceName } from '../utils';
const getOptions = ( getResource, requireResource ) => (
optionNames,
requirement = DEFAULT_REQUIREMENT
) => {
const resourceName = getResourceName( 'options', optionNames );
const options = {};
const names =
requireResource( requirement, resourceName ).data || optionNames;
names.forEach( ( name ) => {
const data = getSetting(
'preloadOptions',
{},
( po ) =>
getResource( getResourceName( 'options', name ) ).data ||
po[ name ]
);
if ( data ) {
options[ name ] = data;
}
} );
return options;
};
const getOptionsError = ( getResource ) => ( optionNames ) => {
return getResource( getResourceName( 'options', optionNames ) ).error;
};
const getUpdateOptionsError = ( getResource ) => ( optionNames ) => {
return getResource( getResourceName( 'options-update', optionNames ) )
.error;
};
const isGetOptionsRequesting = ( getResource ) => ( optionNames ) => {
const { lastReceived, lastRequested } = getResource(
getResourceName( 'options', optionNames )
);
if ( isNil( lastRequested ) || isNil( lastReceived ) ) {
return true;
}
return lastRequested > lastReceived;
};
const isUpdateOptionsRequesting = ( getResource ) => ( optionNames ) => {
const { lastReceived, lastRequested } = getResource(
getResourceName( 'options-update', optionNames )
);
if ( ! isNil( lastRequested ) && isNil( lastReceived ) ) {
return true;
}
return lastRequested > lastReceived;
};
export default {
getOptions,
getOptionsError,
getUpdateOptionsError,
isGetOptionsRequesting,
isUpdateOptionsRequesting,
};

View File

@ -5,7 +5,6 @@ import reportExport from './export';
import items from './items'; import items from './items';
import imports from './imports'; import imports from './imports';
import notes from './notes'; import notes from './notes';
import options from './options';
import reportItems from './reports/items'; import reportItems from './reports/items';
import reportStats from './reports/stats'; import reportStats from './reports/stats';
import reviews from './reviews'; import reviews from './reviews';
@ -17,13 +16,11 @@ function createWcApiSpec() {
...reportExport.mutations, ...reportExport.mutations,
...items.mutations, ...items.mutations,
...notes.mutations, ...notes.mutations,
...options.mutations,
}, },
selectors: { selectors: {
...imports.selectors, ...imports.selectors,
...items.selectors, ...items.selectors,
...notes.selectors, ...notes.selectors,
...options.selectors,
...reportItems.selectors, ...reportItems.selectors,
...reportStats.selectors, ...reportStats.selectors,
...reviews.selectors, ...reviews.selectors,
@ -39,7 +36,6 @@ function createWcApiSpec() {
...imports.operations.read( resourceNames ), ...imports.operations.read( resourceNames ),
...items.operations.read( resourceNames ), ...items.operations.read( resourceNames ),
...notes.operations.read( resourceNames ), ...notes.operations.read( resourceNames ),
...options.operations.read( resourceNames ),
...reportItems.operations.read( resourceNames ), ...reportItems.operations.read( resourceNames ),
...reportStats.operations.read( resourceNames ), ...reportStats.operations.read( resourceNames ),
...reviews.operations.read( resourceNames ), ...reviews.operations.read( resourceNames ),
@ -50,7 +46,6 @@ function createWcApiSpec() {
...reportExport.operations.update( resourceNames, data ), ...reportExport.operations.update( resourceNames, data ),
...items.operations.update( resourceNames, data ), ...items.operations.update( resourceNames, data ),
...notes.operations.update( resourceNames, data ), ...notes.operations.update( resourceNames, data ),
...options.operations.update( resourceNames, data ),
]; ];
}, },
remove( resourceNames, data ) { remove( resourceNames, data ) {

View File

@ -7,6 +7,11 @@ import { Button, Modal } from '@wordpress/components';
import { withDispatch } from '@wordpress/data'; import { withDispatch } from '@wordpress/data';
import { compose } from '@wordpress/compose'; import { compose } from '@wordpress/compose';
/**
* WooCommerce dependencies
*/
import { OPTIONS_STORE_NAME } from '@woocommerce/data';
/** /**
* Internal dependencies * Internal dependencies
*/ */
@ -75,7 +80,7 @@ export class DismissModal extends Component {
export default compose( export default compose(
withDispatch( ( dispatch ) => { withDispatch( ( dispatch ) => {
const { updateOptions } = dispatch( 'wc-api' ); const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
return { updateOptions }; return { updateOptions };
} ) } )
)( DismissModal ); )( DismissModal );

View File

@ -12,3 +12,6 @@ export { withOnboardingHydration } from './onboarding/with-onboarding-hydration'
export { USER_STORE_NAME } from './user-preferences'; export { USER_STORE_NAME } from './user-preferences';
export { withCurrentUserHydration } from './user-preferences/with-current-user-hydration'; export { withCurrentUserHydration } from './user-preferences/with-current-user-hydration';
export { useUserPreferences } from './user-preferences/use-user-preferences'; export { useUserPreferences } from './user-preferences/use-user-preferences';
export { OPTIONS_STORE_NAME } from './options';
export { withOptionsHydration } from './options/with-options-hydration';

View File

@ -0,0 +1,9 @@
const TYPES = {
RECEIVE_OPTIONS: 'RECEIVE_OPTIONS',
SET_IS_REQUESTING: 'SET_IS_REQUESTING',
SET_IS_UPDATING: 'SET_IS_UPDATING',
SET_REQUESTING_ERROR: 'SET_REQUESTING_ERROR',
SET_UPDATING_ERROR: 'SET_UPDATING_ERROR',
};
export default TYPES;

View File

@ -0,0 +1,59 @@
/**
* External Dependencies
*/
import { apiFetch } from '@wordpress/data-controls';
/**
* Internal Dependencies
*/
import TYPES from './action-types';
import { WC_ADMIN_NAMESPACE } from '../constants';
export function receiveOptions( options ) {
return {
type: TYPES.RECEIVE_OPTIONS,
options,
};
}
export function setRequestingError( error, name ) {
return {
type: TYPES.SET_REQUESTING_ERROR,
error,
name,
};
}
export function setUpdatingError( error ) {
return {
type: TYPES.SET_UPDATING_ERROR,
error,
};
}
export function setIsUpdating( isUpdating ) {
return {
type: TYPES.SET_IS_UPDATING,
isUpdating,
};
}
export function* updateOptions( data ) {
yield setIsUpdating( true );
yield receiveOptions( data );
try {
const results = yield apiFetch( {
path: WC_ADMIN_NAMESPACE + '/options',
method: 'POST',
data,
} );
yield setIsUpdating( false );
return { success: true, ...results };
} catch ( error ) {
yield setUpdatingError( error );
return { success: false, ...error };
}
}

View File

@ -0,0 +1,3 @@
export const STORE_NAME = 'wc/admin/options';

View File

@ -0,0 +1,50 @@
/**
* External dependencies
*/
import { controls as dataControls } from '@wordpress/data-controls';
import apiFetch from '@wordpress/api-fetch';
/**
* Internal dependencies
*/
import { WC_ADMIN_NAMESPACE } from '../constants';
let optionNames = [];
const fetches = {};
export const batchFetch = ( optionName ) => {
return {
type: 'BATCH_FETCH',
optionName,
};
};
export const controls = {
...dataControls,
BATCH_FETCH( { optionName } ) {
optionNames.push( optionName );
return new Promise( resolve => {
setTimeout( function() {
const names = optionNames.join(',');
if ( fetches[ names ] ) {
return fetches[ names ].then( ( result ) => {
resolve( result[ optionName ] );
} );
}
const url = WC_ADMIN_NAMESPACE + '/options?options=' + names;
fetches[ names ] = apiFetch( { path: url } );
fetches[names].then( ( result ) => resolve( result ) )
// Clear option names after all resolved;
setTimeout( () => {
optionNames = [];
// Delete the fetch after to allow wp data to handle cache invalidation.
delete fetches[ names ];
}, 1 )
}, 1 );
} );
},
};

View File

@ -0,0 +1,25 @@
/**
* External dependencies
*/
import { registerStore } from '@wordpress/data';
/**
* Internal dependencies
*/
import { STORE_NAME } from './constants';
import * as selectors from './selectors';
import * as actions from './actions';
import * as resolvers from './resolvers';
import { controls } from './controls';
import reducer from './reducer';
registerStore( STORE_NAME, {
reducer,
actions,
controls,
selectors,
resolvers,
} );
export const OPTIONS_STORE_NAME = STORE_NAME;

View File

@ -0,0 +1,43 @@
/**
* Internal dependencies
*/
import TYPES from './action-types';
const optionsReducer = (
state = { isUpdating: false, requestingErrors: {} },
{ type, options, error, isUpdating, name }
) => {
switch ( type ) {
case TYPES.RECEIVE_OPTIONS:
state = {
...state,
...options,
};
break;
case TYPES.SET_IS_UPDATING:
state = {
...state,
isUpdating,
};
break;
case TYPES.SET_REQUESTING_ERROR:
state = {
...state,
requestingErrors: {
[ name ]: error,
},
};
break;
case TYPES.SET_UPDATING_ERROR:
state = {
...state,
error,
updatingError: error,
isUpdating: false,
};
break;
}
return state;
};
export default optionsReducer;

View File

@ -0,0 +1,23 @@
/**
* External Dependencies
*/
import { batchFetch } from './controls';
/**
* Internal dependencies
*/
import { receiveOptions, setRequestingError } from './actions';
/**
* Request an option value.
*
* @param {string} name - Option name
*/
export function* getOption( name ) {
try {
const result = yield batchFetch( name );
yield receiveOptions( result );
} catch ( error ) {
yield setRequestingError( error, name );
}
}

View File

@ -0,0 +1,37 @@
/**
* Get option from state tree.
*
* @param {Object} state - Reducer state
* @param {Array} name - Option name
*/
export const getOption = ( state, name ) => {
return state[ name ];
};
/**
* Determine if an options request resulted in an error.
*
* @param {Object} state - Reducer state
* @param {string} name - Option name
*/
export const getOptionsRequestingError = ( state, name ) => {
return state.requestingErrors[ name ] || false;
};
/**
* Determine if options are being updated.
*
* @param {Object} state - Reducer state
*/
export const isOptionsUpdating = ( state ) => {
return state.isUpdating || false;
};
/**
* Determine if an options update resulted in an error.
*
* @param {Object} state - Reducer state
*/
export const getOptionsUpdatingError = ( state ) => {
return state.updatingError || false;
};

View File

@ -0,0 +1,60 @@
/**
* Internal dependencies
*/
import reducer from '../reducer';
import TYPES from '../action-types';
const defaultState = { isUpdating: false, requestingErrors: {} };
describe( 'options reducer', () => {
it( 'should return a default state', () => {
const state = reducer( undefined, {} );
expect( state ).toEqual( defaultState );
expect( state ).not.toBe( defaultState );
} );
it( 'should handle RECEIVE_OPTIONS', () => {
const state = reducer( defaultState, {
type: TYPES.RECEIVE_OPTIONS,
options: { test_option: 'abc' },
} );
/* eslint-disable dot-notation */
expect( state.requestingErrors[ 'test_option' ] ).toBeUndefined();
expect( state[ 'test_option' ] ).toBe( 'abc' );
/* eslint-enable dot-notation */
} );
it( 'should handle SET_REQUESTING_ERROR', () => {
const state = reducer( defaultState, {
type: TYPES.SET_REQUESTING_ERROR,
error: 'My bad',
name: 'test_option'
} );
/* eslint-disable dot-notation */
expect( state.requestingErrors[ 'test_option' ] ).toBe( 'My bad' );
expect( state[ 'test_option' ] ).toBeUndefined();
/* eslint-enable dot-notation */
} );
it( 'should handle SET_UPDATING_ERROR', () => {
const state = reducer( defaultState, {
type: TYPES.SET_UPDATING_ERROR,
error: 'My bad',
} );
expect( state.updatingError ).toBe( 'My bad' );
expect( state.isUpdating ).toBe( false );
} );
it( 'should handle SET_IS_UPDATING', () => {
const state = reducer( defaultState, {
type: TYPES.SET_IS_UPDATING,
isUpdating: true,
} );
expect( state.isUpdating ).toBe( true );
} );
} );

View File

@ -0,0 +1,43 @@
/**
* External dependencies
*/
import { useRef } from '@wordpress/element';
import { useSelect } from '@wordpress/data';
/**
* Internal dependencies
*/
import { STORE_NAME } from './constants';
export const withOptionsHydration = ( data ) => ( OriginalComponent ) => {
return ( props ) => {
const dataRef = useRef( data );
useSelect( ( select, registry ) => {
if ( ! dataRef.current ) {
return;
}
const { isResolving, hasFinishedResolution } = select( STORE_NAME );
const {
startResolution,
finishResolution,
receiveOptions,
} = registry.dispatch( STORE_NAME );
const names = Object.keys( dataRef.current );
names.forEach( ( name ) => {
if (
! isResolving( 'getOption', [ name ] ) &&
! hasFinishedResolution( 'getOption', [ name ] )
) {
startResolution( 'getOption', [ name ] );
receiveOptions( { [ name ]: dataRef.current[ name ] } );
finishResolution( 'getOption', [ name ] );
}
} );
}, [] );
return <OriginalComponent { ...props } />;
};
};