diff --git a/plugins/woocommerce-admin/client/dashboard/customizable.js b/plugins/woocommerce-admin/client/dashboard/customizable.js
index bf85fe85a0d..4040f2d4c9d 100644
--- a/plugins/woocommerce-admin/client/dashboard/customizable.js
+++ b/plugins/woocommerce-admin/client/dashboard/customizable.js
@@ -4,7 +4,7 @@
import { __, sprintf } from '@wordpress/i18n';
import { Fragment, Suspense, lazy, useState } from '@wordpress/element';
import { compose } from '@wordpress/compose';
-import { partial, get } from 'lodash';
+import { partial } from 'lodash';
import { Dropdown, Button, Icon } from '@wordpress/components';
import { applyFilters } from '@wordpress/hooks';
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 {
SETTINGS_STORE_NAME,
+ OPTIONS_STORE_NAME,
useUserPreferences,
} from '@woocommerce/data';
@@ -329,8 +330,7 @@ const CustomizableDashboard = ( {
export default compose(
withSelect( ( select ) => {
- const { getOptions } = select( 'wc-api' );
-
+ const { getOption } = select( OPTIONS_STORE_NAME );
const { woocommerce_default_date_range: defaultDateRange } = select(
SETTINGS_STORE_NAME
).getSetting( 'wc_admin', 'wcAdminSettings' );
@@ -340,16 +340,13 @@ export default compose(
};
if ( isOnboardingEnabled() ) {
- const options = getOptions( [
- 'woocommerce_task_list_complete',
- 'woocommerce_task_list_hidden',
- ] );
+ withSelectData.homepageEnabled =
+ window.wcAdminFeatures.homepage &&
+ getOption( 'woocommerce_homescreen_enabled' ) === 'yes';
withSelectData.taskListHidden =
- get( options, [ 'woocommerce_task_list_hidden' ], 'no' ) ===
- 'yes';
+ getOption( 'woocommerce_task_list_hidden' ) === 'yes';
withSelectData.taskListComplete =
- get( options, [ 'woocommerce_task_list_complete' ], 'no' ) ===
- 'yes';
+ getOption( 'woocommerce_task_list_complete' ) === 'yes';
}
return withSelectData;
diff --git a/plugins/woocommerce-admin/client/dashboard/utils.js b/plugins/woocommerce-admin/client/dashboard/utils.js
index 173efc823c6..f93a6f468b2 100644
--- a/plugins/woocommerce-admin/client/dashboard/utils.js
+++ b/plugins/woocommerce-admin/client/dashboard/utils.js
@@ -51,6 +51,7 @@ export function getProductIdsForCart(
installedPlugins
) {
const onboarding = getSetting( 'onboarding', {} );
+ const productIds = [];
// The population of onboarding.productTypes only happens if the task list should be shown
// so bail early if it isn't present.
@@ -58,7 +59,6 @@ export function getProductIdsForCart(
return productIds;
}
- const productIds = [];
const productTypes = profileItems.product_types || [];
productTypes.forEach( ( productType ) => {
diff --git a/plugins/woocommerce-admin/client/homepage/layout.js b/plugins/woocommerce-admin/client/homepage/layout.js
index fce4e79463c..20912768a24 100644
--- a/plugins/woocommerce-admin/client/homepage/layout.js
+++ b/plugins/woocommerce-admin/client/homepage/layout.js
@@ -11,9 +11,13 @@ import {
} from '@wordpress/element';
import { compose } from '@wordpress/compose';
import classnames from 'classnames';
-import { get } from 'lodash';
import PropTypes from 'prop-types';
+/**
+ * WooCommerce dependencies
+ */
+import { OPTIONS_STORE_NAME } from '@woocommerce/data';
+
/**
* Internal dependencies
*/
@@ -21,9 +25,9 @@ import QuickLinks from '../quick-links';
import StatsOverview from './stats-overview';
import './style.scss';
import { isOnboardingEnabled } from 'dashboard/utils';
-import withSelect from 'wc-api/with-select';
import TaskListPlaceholder from '../task-list/placeholder';
import InboxPanel from '../header/activity-panel/panels/inbox';
+import withWCApiSelect from 'wc-api/with-select';
const TaskList = lazy( () =>
import( /* webpackChunkName: "task-list" */ '../task-list' )
@@ -138,32 +142,27 @@ Layout.propTypes = {
};
export default compose(
- withSelect( ( select ) => {
+ withWCApiSelect( ( select ) => {
const {
- getOptions,
getUndoDismissRequesting,
- isGetOptionsRequesting,
} = select( 'wc-api' );
+ const { isUndoRequesting } = getUndoDismissRequesting();
+ const { getOption, isResolving } = select( OPTIONS_STORE_NAME );
if ( isOnboardingEnabled() ) {
- const options = getOptions( [
- 'woocommerce_task_list_complete',
- 'woocommerce_task_list_hidden',
- ] );
- const { isUndoRequesting } = getUndoDismissRequesting();
-
return {
isUndoRequesting,
- requestingTaskList: isGetOptionsRequesting( [
- 'woocommerce_task_list_complete',
- 'woocommerce_task_list_hidden',
- ] ),
taskListComplete:
- get( options, [ 'woocommerce_task_list_complete' ] ) ===
- 'yes',
+ getOption( 'woocommerce_task_list_complete' ) === 'yes',
taskListHidden:
- get( options, [ 'woocommerce_task_list_hidden' ] ) ===
- 'yes',
+ getOption( 'woocommerce_task_list_hidden' ) === 'yes',
+ requestingTaskList:
+ isResolving( 'getOption', [
+ 'woocommerce_task_list_complete',
+ ] ) ||
+ isResolving( 'getOption', [
+ 'woocommerce_task_list_hidden',
+ ] ),
};
}
diff --git a/plugins/woocommerce-admin/client/layout/index.js b/plugins/woocommerce-admin/client/layout/index.js
index ef6f1a09477..ae4e1e03104 100644
--- a/plugins/woocommerce-admin/client/layout/index.js
+++ b/plugins/woocommerce-admin/client/layout/index.js
@@ -6,7 +6,7 @@ import { withSelect } from '@wordpress/data';
import { Component, lazy, Suspense } from '@wordpress/element';
import { Router, Route, Switch } from 'react-router-dom';
import PropTypes from 'prop-types';
-import { get, isFunction } from 'lodash';
+import { get, isFunction, identity } from 'lodash';
/**
* WooCommerce dependencies
@@ -14,7 +14,12 @@ import { get, isFunction } from 'lodash';
import { useFilters, Spinner } from '@woocommerce/components';
import { getHistory } from '@woocommerce/navigation';
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
@@ -25,7 +30,6 @@ import Header from 'header';
import Notices from './notices';
import { recordPageView } from 'lib/tracks';
import TransientNotices from './transient-notices';
-import withWCApiSelect from 'wc-api/with-select';
const StoreAlerts = lazy( () =>
import( /* webpackChunkName: "store-alerts" */ './store-alerts' )
);
@@ -211,13 +215,16 @@ class _PageLayout extends Component {
export const PageLayout = compose(
// Use the useFilters HoC so PageLayout is re-rendered when filters are used to add new pages or reports
useFilters( [ PAGES_FILTER, REPORTS_FILTER ] ),
- withWCApiSelect( ( select ) => {
- const { getOptions } = select( 'wc-api' );
- const options = getOptions( [ 'woocommerce_homescreen_enabled' ] );
+ window.wcSettings.preloadOptions
+ ? withOptionsHydration( {
+ ...window.wcSettings.preloadOptions,
+ } )
+ : identity,
+ withSelect( ( select ) => {
+ const { getOption } = select( OPTIONS_STORE_NAME );
const homepageEnabled =
window.wcAdminFeatures.homepage &&
- get( options, [ 'woocommerce_homescreen_enabled' ], false ) ===
- 'yes';
+ getOption( 'woocommerce_homescreen_enabled' ) === 'yes';
return { homepageEnabled };
} )
)( _PageLayout );
diff --git a/plugins/woocommerce-admin/client/marketing/overview/index.js b/plugins/woocommerce-admin/client/marketing/overview/index.js
index d790c9a9c9b..71ed80c4d80 100644
--- a/plugins/woocommerce-admin/client/marketing/overview/index.js
+++ b/plugins/woocommerce-admin/client/marketing/overview/index.js
@@ -3,6 +3,11 @@
*/
import { getSetting } from '@woocommerce/wc-admin-settings';
+/**
+ * WooCommerce dependencies
+ */
+import { withOptionsHydration } from '@woocommerce/data';
+
/**
* Internal dependencies
*/
@@ -26,4 +31,6 @@ const MarketingOverview = () => {
);
};
-export default MarketingOverview;
+export default withOptionsHydration( {
+ ...( window.wcSettings.preloadOptions || {} ),
+} )( MarketingOverview );
diff --git a/plugins/woocommerce-admin/client/marketing/overview/welcome-card/index.js b/plugins/woocommerce-admin/client/marketing/overview/welcome-card/index.js
index a4ce3f3d906..00fc4f40918 100644
--- a/plugins/woocommerce-admin/client/marketing/overview/welcome-card/index.js
+++ b/plugins/woocommerce-admin/client/marketing/overview/welcome-card/index.js
@@ -2,24 +2,23 @@
* External dependencies
*/
import { __ } from '@wordpress/i18n';
-import { get } from 'lodash';
import { Button } from '@wordpress/components';
import Gridicon from 'gridicons';
import { compose } from '@wordpress/compose';
-import { withDispatch } from '@wordpress/data';
+import { withDispatch, withSelect } from '@wordpress/data';
import PropTypes from 'prop-types';
/**
* WooCommerce dependencies
*/
import { Card } from '@woocommerce/components';
+import { OPTIONS_STORE_NAME } from '@woocommerce/data';
/**
* Internal dependencies
*/
import './style.scss';
import { recordEvent } from 'lib/tracks';
-import withSelect from 'wc-api/with-select';
import WelcomeImage from './images/welcome.svg';
const WelcomeCard = ( {
@@ -72,17 +71,17 @@ export { WelcomeCard }
// default export
export default compose(
withSelect( ( select ) => {
- const { getOptions, isUpdateOptionsRequesting } = select( 'wc-api' );
- const hideOptionName = 'woocommerce_marketing_overview_welcome_hidden';
- const options = getOptions( [ hideOptionName ] );
- const isHidden = get( options, [ hideOptionName ], 'no' ) === 'yes';
- const isUpdateRequesting = Boolean( isUpdateOptionsRequesting( [ hideOptionName ] ) );
+ const { getOption, isOptionsUpdating } = select( OPTIONS_STORE_NAME );
+ const isUpdateRequesting = isOptionsUpdating();
+
return {
- isHidden: isHidden || isUpdateRequesting,
+ isHidden:
+ getOption( 'woocommerce_marketing_overview_welcome_hidden' ) ===
+ 'yes' || isUpdateRequesting,
};
} ),
withDispatch( ( dispatch ) => {
- const { updateOptions } = dispatch( 'wc-api' );
+ const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
return {
updateOptions,
};
diff --git a/plugins/woocommerce-admin/client/profile-wizard/index.js b/plugins/woocommerce-admin/client/profile-wizard/index.js
index 0a7bad83191..0d7376631fe 100644
--- a/plugins/woocommerce-admin/client/profile-wizard/index.js
+++ b/plugins/woocommerce-admin/client/profile-wizard/index.js
@@ -247,7 +247,9 @@ const hydrateSettings =
export default compose(
withSelect( ( select ) => {
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 notesQuery = {
diff --git a/plugins/woocommerce-admin/client/profile-wizard/steps/benefits/index.js b/plugins/woocommerce-admin/client/profile-wizard/steps/benefits/index.js
index 3f6985d9e83..8559a1a415b 100644
--- a/plugins/woocommerce-admin/client/profile-wizard/steps/benefits/index.js
+++ b/plugins/woocommerce-admin/client/profile-wizard/steps/benefits/index.js
@@ -5,7 +5,7 @@ import { __, _n, sprintf } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
import { Component } from '@wordpress/element';
import { compose } from '@wordpress/compose';
-import { withDispatch } from '@wordpress/data';
+import { withDispatch, withSelect } from '@wordpress/data';
import { filter } from 'lodash';
/**
@@ -17,6 +17,7 @@ import {
pluginNames,
ONBOARDING_STORE_NAME,
PLUGINS_STORE_NAME,
+ OPTIONS_STORE_NAME
} from '@woocommerce/data';
/**
@@ -28,7 +29,6 @@ import ManagementIcon from './images/management';
import SalesTaxIcon from './images/sales_tax';
import ShippingLabels from './images/shipping_labels';
import SpeedIcon from './images/speed';
-import withSelect from 'wc-api/with-select';
import { recordEvent } from 'lib/tracks';
class Benefits extends Component {
@@ -108,12 +108,12 @@ class Benefits extends Component {
goToNextStep();
}
- async startPluginInstall() {
+ startPluginInstall() {
const { updateProfileItems, updateOptions } = this.props;
this.setState( { isInstalling: true } );
- await updateOptions( {
+ updateOptions( {
woocommerce_setup_jetpack_opted_in: true,
} );
@@ -328,7 +328,7 @@ export default compose(
} ),
withDispatch( ( dispatch ) => {
const { updateProfileItems } = dispatch( ONBOARDING_STORE_NAME );
- const { updateOptions } = dispatch( 'wc-api' );
+ const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
const { createNotice } = dispatch( 'core/notices' );
return {
diff --git a/plugins/woocommerce-admin/client/profile-wizard/steps/usage-modal.js b/plugins/woocommerce-admin/client/profile-wizard/steps/usage-modal.js
index b65d96728bd..32c9d5d73b0 100644
--- a/plugins/woocommerce-admin/client/profile-wizard/steps/usage-modal.js
+++ b/plugins/woocommerce-admin/client/profile-wizard/steps/usage-modal.js
@@ -4,8 +4,7 @@
import { __ } from '@wordpress/i18n';
import { Component } from '@wordpress/element';
import { compose } from '@wordpress/compose';
-import { withDispatch } from '@wordpress/data';
-import { get } from 'lodash';
+import { withDispatch, withSelect } from '@wordpress/data';
import interpolateComponents from 'interpolate-components';
import {
Button,
@@ -14,11 +13,15 @@ import {
Modal,
} from '@wordpress/components';
+/**
+ * WooCommerce dependencies
+ */
+import { OPTIONS_STORE_NAME } from '@woocommerce/data';
+
/**
* Internal dependencies
*/
import { Link } from '@woocommerce/components';
-import withSelect from 'wc-api/with-select';
class UsageModal extends Component {
constructor( props ) {
@@ -159,20 +162,15 @@ class UsageModal extends Component {
export default compose(
withSelect( ( select ) => {
const {
- getOptions,
- getOptionsError,
- isUpdateOptionsRequesting,
- } = select( 'wc-api' );
+ getOption,
+ getOptionsUpdatingError,
+ isOptionsUpdating,
+ } = select( OPTIONS_STORE_NAME );
- const options = getOptions( [ 'woocommerce_allow_tracking' ] );
const allowTracking =
- get( options, [ 'woocommerce_allow_tracking' ], false ) === 'yes';
- const isRequesting = Boolean(
- isUpdateOptionsRequesting( [ 'woocommerce_allow_tracking' ] )
- );
- const hasErrors = Boolean(
- getOptionsError( [ 'woocommerce_allow_tracking' ] )
- );
+ getOption( 'woocommerce_allow_tracking' ) === 'yes';
+ const isRequesting = Boolean( isOptionsUpdating() );
+ const hasErrors = Boolean( getOptionsUpdatingError() );
return {
allowTracking,
@@ -182,7 +180,7 @@ export default compose(
} ),
withDispatch( ( dispatch ) => {
const { createNotice } = dispatch( 'core/notices' );
- const { updateOptions } = dispatch( 'wc-api' );
+ const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
return {
createNotice,
diff --git a/plugins/woocommerce-admin/client/task-list/index.js b/plugins/woocommerce-admin/client/task-list/index.js
index 2777d8c6826..c0f35d8fb17 100644
--- a/plugins/woocommerce-admin/client/task-list/index.js
+++ b/plugins/woocommerce-admin/client/task-list/index.js
@@ -3,7 +3,7 @@
*/
import { __ } from '@wordpress/i18n';
import { Component, cloneElement, Fragment } from '@wordpress/element';
-import { get, isEqual } from 'lodash';
+import { isEqual } from 'lodash';
import { compose } from '@wordpress/compose';
import classNames from 'classnames';
import {
@@ -21,7 +21,11 @@ import { Icon, check, chevronRight } from '@wordpress/icons';
*/
import { H, List, EllipsisMenu } from '@woocommerce/components';
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
@@ -118,7 +122,7 @@ class TaskDashboard extends Component {
return getAllTasks( {
profileItems,
- options: taskListPayments,
+ taskListPayments,
query,
toggleCartModal: this.toggleCartModal.bind( this ),
installedPlugins,
@@ -383,7 +387,7 @@ class TaskDashboard extends Component {
export default compose(
withSelect( ( select, props ) => {
const { getProfileItems } = select( ONBOARDING_STORE_NAME );
- const { getOptions } = select( 'wc-api' );
+ const { getOption } = select( OPTIONS_STORE_NAME );
const {
getActivePlugins,
getInstalledPlugins,
@@ -391,28 +395,18 @@ export default compose(
} = select( PLUGINS_STORE_NAME );
const profileItems = getProfileItems();
- const options = getOptions( [
- 'woocommerce_task_list_welcome_modal_dismissed',
- 'woocommerce_task_list_hidden',
- 'woocommerce_task_list_tracked_completed_tasks',
- ] );
- const modalDismissed = get(
- options,
- [ '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 modalDismissed =
+ getOption( 'woocommerce_task_list_welcome_modal_dismissed' ) ||
+ false;
+ const taskListPayments = getOption( 'woocommerce_task_list_payments' );
+ const trackedCompletedTasks =
+ getOption( 'woocommerce_task_list_tracked_completed_tasks' ) || [];
+ const payments = getOption( 'woocommerce_task_list_payments' );
+
const installedPlugins = getInstalledPlugins();
const tasks = getAllTasks( {
profileItems,
- options: getOptions( [ 'woocommerce_task_list_payments' ] ),
+ options: payments,
query: props.query,
installedPlugins,
} );
@@ -437,7 +431,7 @@ export default compose(
};
} ),
withDispatch( ( dispatch ) => {
- const { updateOptions } = dispatch( 'wc-api' );
+ const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
return {
updateOptions,
};
diff --git a/plugins/woocommerce-admin/client/task-list/tasks.js b/plugins/woocommerce-admin/client/task-list/tasks.js
index d97432af28a..10ddc82fcf5 100644
--- a/plugins/woocommerce-admin/client/task-list/tasks.js
+++ b/plugins/woocommerce-admin/client/task-list/tasks.js
@@ -4,7 +4,6 @@
import { __ } from '@wordpress/i18n';
import { applyFilters } from '@wordpress/hooks';
-import { get } from 'lodash';
/**
* WooCommerce dependencies
@@ -25,7 +24,7 @@ import Payments from './tasks/payments';
export function getAllTasks( {
profileItems,
- options,
+ taskListPayments,
query,
toggleCartModal,
installedPlugins,
@@ -55,15 +54,11 @@ export function getAllTasks( {
installedPlugins
);
- const paymentsCompleted = get(
- options,
- [ 'woocommerce_task_list_payments', 'completed' ],
- false
+ const paymentsCompleted = Boolean(
+ taskListPayments && taskListPayments.completed
);
- const paymentsSkipped = get(
- options,
- [ 'woocommerce_task_list_payments', 'skipped' ],
- false
+ const paymentsSkipped = Boolean(
+ taskListPayments && taskListPayments.skipped
);
const tasks = [
diff --git a/plugins/woocommerce-admin/client/task-list/tasks/appearance.js b/plugins/woocommerce-admin/client/task-list/tasks/appearance.js
index 9dbdfd38020..d54881fbc82 100644
--- a/plugins/woocommerce-admin/client/task-list/tasks/appearance.js
+++ b/plugins/woocommerce-admin/client/task-list/tasks/appearance.js
@@ -6,8 +6,8 @@ import apiFetch from '@wordpress/api-fetch';
import { Button } from '@wordpress/components';
import { Component, Fragment } from '@wordpress/element';
import { compose } from '@wordpress/compose';
-import { difference, filter } from 'lodash';
-import { withDispatch } from '@wordpress/data';
+import { filter } from 'lodash';
+import { withDispatch, withSelect } from '@wordpress/data';
/**
* WooCommerce dependencies
@@ -20,13 +20,13 @@ import {
} from '@woocommerce/components';
import { getHistory, getNewPath } from '@woocommerce/navigation';
import { getSetting, setSetting } from '@woocommerce/wc-admin-settings';
+import { OPTIONS_STORE_NAME } from '@woocommerce/data';
/**
* Internal dependencies
*/
import { queueRecordEvent, recordEvent } from 'lib/tracks';
import { WC_ADMIN_NAMESPACE } from 'wc-api/constants';
-import withSelect from 'wc-api/with-select';
class Appearance extends Component {
constructor( props ) {
@@ -43,7 +43,9 @@ class Appearance extends Component {
isPending: false,
logo: null,
stepIndex: 0,
- storeNoticeText: props.options.woocommerce_demo_store_notice || '',
+ isUpdatingLogo: false,
+ isUpdatingNotice: false,
+ storeNoticeText: props.demoStoreNotice || '',
};
this.completeStep = this.completeStep.bind( this );
@@ -63,18 +65,9 @@ class Appearance extends Component {
}
}
- async componentDidUpdate( prevProps ) {
- const { isPending, logo, stepIndex } = this.state;
- const {
- createNotice,
- errors,
- hasErrors,
- isRequesting,
- options,
- } = this.props;
- const step = this.getSteps()[ stepIndex ].key;
- const isRequestSuccessful =
- ! isRequesting && prevProps.isRequesting && ! hasErrors;
+ componentDidUpdate( prevProps ) {
+ const { isPending, logo } = this.state;
+ const { demoStoreNotice } = this.props;
if ( logo && ! logo.url && ! isPending ) {
/* eslint-disable react/no-did-update-set-state */
@@ -93,38 +86,15 @@ class Appearance extends Component {
}
if (
- options.woocommerce_demo_store_notice &&
- prevProps.options.woocommerce_demo_store_notice !==
- options.woocommerce_demo_store_notice
+ demoStoreNotice &&
+ prevProps.demoStoreNotice !== demoStoreNotice
) {
/* eslint-disable react/no-did-update-set-state */
this.setState( {
- storeNoticeText: options.woocommerce_demo_store_notice,
+ storeNoticeText: demoStoreNotice,
} );
/* 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() {
@@ -222,8 +192,8 @@ class Appearance extends Component {
} );
}
- updateLogo() {
- const { updateOptions } = this.props;
+ async updateLogo() {
+ const { updateOptions, createNotice } = this.props;
const { logo } = this.state;
const { stylesheet, themeMods } = getSetting( 'onboarding', {} );
const updatedThemeMods = {
@@ -238,13 +208,25 @@ class Appearance extends Component {
themeMods: updatedThemeMods,
} );
- updateOptions( {
+ this.setState( { isUpdatingLogo: true } );
+ const update = await updateOptions( {
[ `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() {
- const { updateOptions } = this.props;
+ async updateNotice() {
+ const { updateOptions, createNotice } = this.props;
const { storeNoticeText } = this.state;
recordEvent( 'tasklist_appearance_set_store_notice', {
@@ -256,16 +238,36 @@ class Appearance extends Component {
isAppearanceComplete: true,
} );
- updateOptions( {
+ this.setState( { isUpdatingNotice: true } );
+ const update = await updateOptions( {
woocommerce_task_list_appearance_complete: true,
woocommerce_demo_store: storeNoticeText.length ? 'yes' : 'no',
woocommerce_demo_store_notice: storeNoticeText,
} );
+
+ if ( update.success ) {
+ this.setState( { isUpdatingNotice: false } );
+ createNotice(
+ 'success',
+ __(
+ "π¨ Your store is looking great! Don't forget to continue personalizing it.",
+ 'woocommerce-admin'
+ )
+ );
+ this.completeStep();
+ } else {
+ createNotice( 'error', update.message );
+ }
}
getSteps() {
- const { isDirty, isPending, logo, storeNoticeText } = this.state;
- const { isRequesting } = this.props;
+ const {
+ isDirty,
+ isPending,
+ logo,
+ storeNoticeText,
+ isUpdatingLogo,
+ } = this.state;
const steps = [
{
@@ -340,7 +342,7 @@ class Appearance extends Component {
) : (
-
@@ -291,23 +289,20 @@ PayPal.defaultProps = {
export default compose(
withSelect( ( select ) => {
- const { getOptions, isGetOptionsRequesting } = select( 'wc-api' );
+ const { getOption, isOptionsUpdating } = select( OPTIONS_STORE_NAME );
const { getActivePlugins } = select( PLUGINS_STORE_NAME );
- const options = getOptions( [ 'woocommerce_ppec_paypal_settings' ] );
- const isOptionsRequesting = Boolean(
- isGetOptionsRequesting( [ 'woocommerce_ppec_paypal_settings' ] )
- );
+ const options = getOption( 'woocommerce_ppec_paypal_settings' );
const activePlugins = getActivePlugins();
return {
activePlugins,
options,
- isOptionsRequesting,
+ isOptionsUpdating: isOptionsUpdating(),
};
} ),
withDispatch( ( dispatch ) => {
const { createNotice } = dispatch( 'core/notices' );
- const { updateOptions } = dispatch( 'wc-api' );
+ const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
return {
createNotice,
updateOptions,
diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/square.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/square.js
index a1eba4c43bd..f899d11f3a2 100644
--- a/plugins/woocommerce-admin/client/task-list/tasks/payments/square.js
+++ b/plugins/woocommerce-admin/client/task-list/tasks/payments/square.js
@@ -5,7 +5,7 @@ import { __ } from '@wordpress/i18n';
import { Component, Fragment } from '@wordpress/element';
import apiFetch from '@wordpress/api-fetch';
import { Button } from '@wordpress/components';
-import { withDispatch } from '@wordpress/data';
+import { withDispatch, withSelect } from '@wordpress/data';
import { compose } from '@wordpress/compose';
/**
@@ -13,9 +13,9 @@ import { compose } from '@wordpress/compose';
*/
import { getQuery } from '@woocommerce/navigation';
import { WC_ADMIN_NAMESPACE } from 'wc-api/constants';
-import withSelect from 'wc-api/with-select';
import { Stepper } from '@woocommerce/components';
import { getAdminLink } from '@woocommerce/wc-admin-settings';
+import { OPTIONS_STORE_NAME } from '@woocommerce/data';
class Square extends Component {
constructor( props ) {
@@ -145,15 +145,11 @@ class Square extends Component {
export default compose(
withSelect( ( select ) => {
- const { getOptions, isGetOptionsRequesting } = select( 'wc-api' );
- const options = getOptions( [
+ const { getOption, isResolving } = select( OPTIONS_STORE_NAME );
+ const options = getOption( 'woocommerce_square_credit_card_settings' );
+ const optionsIsRequesting = isResolving( 'getOption', [
'woocommerce_square_credit_card_settings',
] );
- const optionsIsRequesting = Boolean(
- isGetOptionsRequesting( [
- 'woocommerce_square_credit_card_settings',
- ] )
- );
return {
options,
@@ -162,7 +158,7 @@ export default compose(
} ),
withDispatch( ( dispatch ) => {
const { createNotice } = dispatch( 'core/notices' );
- const { updateOptions } = dispatch( 'wc-api' );
+ const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
return {
createNotice,
updateOptions,
diff --git a/plugins/woocommerce-admin/client/task-list/tasks/payments/stripe.js b/plugins/woocommerce-admin/client/task-list/tasks/payments/stripe.js
index e8c78f6c32c..438f1d049fe 100644
--- a/plugins/woocommerce-admin/client/task-list/tasks/payments/stripe.js
+++ b/plugins/woocommerce-admin/client/task-list/tasks/payments/stripe.js
@@ -5,10 +5,9 @@ import { __ } from '@wordpress/i18n';
import { Component, Fragment } from '@wordpress/element';
import { compose } from '@wordpress/compose';
import apiFetch from '@wordpress/api-fetch';
-import { withDispatch } from '@wordpress/data';
+import { withDispatch, withSelect } from '@wordpress/data';
import interpolateComponents from 'interpolate-components';
import { Button } from '@wordpress/components';
-import { get } from 'lodash';
/**
* WooCommerce dependencies
@@ -17,8 +16,7 @@ import { Form, Link, Stepper, TextControl } from '@woocommerce/components';
import { getAdminLink } from '@woocommerce/wc-admin-settings';
import { getQuery } from '@woocommerce/navigation';
import { WCS_NAMESPACE } from 'wc-api/constants';
-import withSelect from 'wc-api/with-select';
-import { PLUGINS_STORE_NAME } from '@woocommerce/data';
+import { PLUGINS_STORE_NAME, OPTIONS_STORE_NAME } from '@woocommerce/data';
class Stripe extends Component {
constructor( props ) {
@@ -54,26 +52,7 @@ class Stripe extends Component {
}
componentDidUpdate( prevProps ) {
- const {
- 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'
- )
- );
- }
- }
+ const { activePlugins } = this.props;
if (
! prevProps.activePlugins.includes(
@@ -154,10 +133,10 @@ class Stripe extends Component {
);
}
- updateSettings( values ) {
- const { updateOptions, stripeSettings } = this.props;
+ async updateSettings( values ) {
+ const { updateOptions, stripeSettings, createNotice } = this.props;
- updateOptions( {
+ const update = await updateOptions( {
woocommerce_stripe_settings: {
...stripeSettings,
publishable_key: values.publishable_key,
@@ -165,6 +144,18 @@ class Stripe extends Component {
enabled: 'yes',
},
} );
+
+ if ( update.success ) {
+ this.completeMethod();
+ } else {
+ createNotice(
+ 'error',
+ __(
+ 'There was a problem saving your payment setings',
+ 'woocommerce-admin'
+ )
+ );
+ }
}
getInitialConfigValues() {
@@ -194,7 +185,7 @@ class Stripe extends Component {
}
renderManualConfig() {
- const { isOptionsRequesting } = this.props;
+ const { isOptionsUpdating } = this.props;
const stripeHelp = interpolateComponents( {
mixedString: __(
'Your API details can be obtained from your {{docsLink}}Stripe account{{/docsLink}}. Donβt have a Stripe account? {{registerLink}}Create one.{{/registerLink}}',
@@ -246,7 +237,7 @@ class Stripe extends Component {
{ __( 'Proceed', 'woocommerce-admin' ) }
@@ -294,14 +285,14 @@ class Stripe extends Component {
}
render() {
- const { installStep, isOptionsRequesting } = this.props;
+ const { installStep, isOptionsUpdating } = this.props;
const { isPending } = this.state;
return (
{
- const {
- getOptions,
- getOptionsError,
- isUpdateOptionsRequesting,
- } = select( 'wc-api' );
+ const { getOption, isOptionsUpdating } = select( OPTIONS_STORE_NAME );
const { getActivePlugins, isJetpackConnected } = select(
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 {
activePlugins: getActivePlugins(),
- hasOptionsError,
isJetpackConnected: isJetpackConnected(),
- isOptionsRequesting,
- stripeSettings,
+ isOptionsUpdating: isOptionsUpdating(),
+ stripeSettings: getOption( 'woocommerce_stripe_settings' ) || [],
};
} ),
withDispatch( ( dispatch ) => {
const { createNotice } = dispatch( 'core/notices' );
- const { updateOptions } = dispatch( 'wc-api' );
+ const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
return {
createNotice,
updateOptions,
diff --git a/plugins/woocommerce-admin/client/task-list/tasks/tax.js b/plugins/woocommerce-admin/client/task-list/tasks/tax.js
index 0669fb89d87..763e206a67a 100644
--- a/plugins/woocommerce-admin/client/task-list/tasks/tax.js
+++ b/plugins/woocommerce-admin/client/task-list/tasks/tax.js
@@ -5,7 +5,7 @@ import { __ } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
import { Component, Fragment } from '@wordpress/element';
import { compose } from '@wordpress/compose';
-import { difference, filter, get } from 'lodash';
+import { difference, filter } from 'lodash';
import interpolateComponents from 'interpolate-components';
import { withDispatch, withSelect } from '@wordpress/data';
@@ -19,7 +19,11 @@ import {
getSetting,
setSetting,
} 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
@@ -27,7 +31,6 @@ import { SETTINGS_STORE_NAME, PLUGINS_STORE_NAME } from '@woocommerce/data';
import Connect from 'dashboard/components/connect';
import { getCountryCode } from 'dashboard/utils';
import StoreLocation from './steps/location';
-import withWCApiSelect from 'wc-api/with-select';
import { recordEvent, queueRecordEvent } from 'lib/tracks';
class Tax extends Component {
@@ -491,38 +494,16 @@ class Tax extends Component {
}
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 ) => {
const {
getSettings,
getSettingsError,
isGetSettingsRequesting,
} = select( SETTINGS_STORE_NAME );
+ const { getOption } = select( OPTIONS_STORE_NAME );
+ const { getActivePlugins, isJetpackConnected } = select(
+ PLUGINS_STORE_NAME
+ );
const { general: generalSettings = {} } = getSettings( 'general' );
const isGeneralSettingsError = Boolean( getSettingsError( 'general' ) );
@@ -537,6 +518,16 @@ export default compose(
const isTaxSettingsError = Boolean( getSettingsError( '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 {
isGeneralSettingsError,
isGeneralSettingsRequesting,
@@ -545,6 +536,9 @@ export default compose(
taxSettings,
isTaxSettingsError,
isTaxSettingsRequesting,
+ isJetpackConnected: isJetpackConnected(),
+ pluginsToActivate,
+ tosAccepted,
};
} ),
withDispatch( ( dispatch ) => {
diff --git a/plugins/woocommerce-admin/client/wc-api/options/index.js b/plugins/woocommerce-admin/client/wc-api/options/index.js
deleted file mode 100644
index 8d34b93de32..00000000000
--- a/plugins/woocommerce-admin/client/wc-api/options/index.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
- * Internal dependencies
- */
-import operations from './operations';
-import selectors from './selectors';
-import mutations from './mutations';
-
-export default {
- operations,
- selectors,
- mutations,
-};
diff --git a/plugins/woocommerce-admin/client/wc-api/options/mutations.js b/plugins/woocommerce-admin/client/wc-api/options/mutations.js
deleted file mode 100644
index c3c9c4e1354..00000000000
--- a/plugins/woocommerce-admin/client/wc-api/options/mutations.js
+++ /dev/null
@@ -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,
-};
diff --git a/plugins/woocommerce-admin/client/wc-api/options/operations.js b/plugins/woocommerce-admin/client/wc-api/options/operations.js
deleted file mode 100644
index 71e80b9282c..00000000000
--- a/plugins/woocommerce-admin/client/wc-api/options/operations.js
+++ /dev/null
@@ -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,
-};
diff --git a/plugins/woocommerce-admin/client/wc-api/options/selectors.js b/plugins/woocommerce-admin/client/wc-api/options/selectors.js
deleted file mode 100644
index 7b5198bec94..00000000000
--- a/plugins/woocommerce-admin/client/wc-api/options/selectors.js
+++ /dev/null
@@ -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,
-};
diff --git a/plugins/woocommerce-admin/client/wc-api/wc-api-spec.js b/plugins/woocommerce-admin/client/wc-api/wc-api-spec.js
index cf94f9749f0..c31530d8bf2 100644
--- a/plugins/woocommerce-admin/client/wc-api/wc-api-spec.js
+++ b/plugins/woocommerce-admin/client/wc-api/wc-api-spec.js
@@ -5,7 +5,6 @@ import reportExport from './export';
import items from './items';
import imports from './imports';
import notes from './notes';
-import options from './options';
import reportItems from './reports/items';
import reportStats from './reports/stats';
import reviews from './reviews';
@@ -17,13 +16,11 @@ function createWcApiSpec() {
...reportExport.mutations,
...items.mutations,
...notes.mutations,
- ...options.mutations,
},
selectors: {
...imports.selectors,
...items.selectors,
...notes.selectors,
- ...options.selectors,
...reportItems.selectors,
...reportStats.selectors,
...reviews.selectors,
@@ -39,7 +36,6 @@ function createWcApiSpec() {
...imports.operations.read( resourceNames ),
...items.operations.read( resourceNames ),
...notes.operations.read( resourceNames ),
- ...options.operations.read( resourceNames ),
...reportItems.operations.read( resourceNames ),
...reportStats.operations.read( resourceNames ),
...reviews.operations.read( resourceNames ),
@@ -50,7 +46,6 @@ function createWcApiSpec() {
...reportExport.operations.update( resourceNames, data ),
...items.operations.update( resourceNames, data ),
...notes.operations.update( resourceNames, data ),
- ...options.operations.update( resourceNames, data ),
];
},
remove( resourceNames, data ) {
diff --git a/plugins/woocommerce-admin/client/wp-admin-scripts/print-shipping-label-banner/dismiss-modal/index.js b/plugins/woocommerce-admin/client/wp-admin-scripts/print-shipping-label-banner/dismiss-modal/index.js
index 93073213a7b..5c34c44cce8 100644
--- a/plugins/woocommerce-admin/client/wp-admin-scripts/print-shipping-label-banner/dismiss-modal/index.js
+++ b/plugins/woocommerce-admin/client/wp-admin-scripts/print-shipping-label-banner/dismiss-modal/index.js
@@ -7,6 +7,11 @@ import { Button, Modal } from '@wordpress/components';
import { withDispatch } from '@wordpress/data';
import { compose } from '@wordpress/compose';
+/**
+ * WooCommerce dependencies
+ */
+import { OPTIONS_STORE_NAME } from '@woocommerce/data';
+
/**
* Internal dependencies
*/
@@ -75,7 +80,7 @@ export class DismissModal extends Component {
export default compose(
withDispatch( ( dispatch ) => {
- const { updateOptions } = dispatch( 'wc-api' );
+ const { updateOptions } = dispatch( OPTIONS_STORE_NAME );
return { updateOptions };
} )
)( DismissModal );
diff --git a/plugins/woocommerce-admin/packages/data/src/index.js b/plugins/woocommerce-admin/packages/data/src/index.js
index abec9adc87a..6ca9ab3c11e 100644
--- a/plugins/woocommerce-admin/packages/data/src/index.js
+++ b/plugins/woocommerce-admin/packages/data/src/index.js
@@ -12,3 +12,6 @@ export { withOnboardingHydration } from './onboarding/with-onboarding-hydration'
export { USER_STORE_NAME } from './user-preferences';
export { withCurrentUserHydration } from './user-preferences/with-current-user-hydration';
export { useUserPreferences } from './user-preferences/use-user-preferences';
+
+export { OPTIONS_STORE_NAME } from './options';
+export { withOptionsHydration } from './options/with-options-hydration';
diff --git a/plugins/woocommerce-admin/packages/data/src/options/action-types.js b/plugins/woocommerce-admin/packages/data/src/options/action-types.js
new file mode 100644
index 00000000000..e7765ef2e86
--- /dev/null
+++ b/plugins/woocommerce-admin/packages/data/src/options/action-types.js
@@ -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;
diff --git a/plugins/woocommerce-admin/packages/data/src/options/actions.js b/plugins/woocommerce-admin/packages/data/src/options/actions.js
new file mode 100644
index 00000000000..563bff3fe1b
--- /dev/null
+++ b/plugins/woocommerce-admin/packages/data/src/options/actions.js
@@ -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 };
+ }
+}
diff --git a/plugins/woocommerce-admin/packages/data/src/options/constants.js b/plugins/woocommerce-admin/packages/data/src/options/constants.js
new file mode 100644
index 00000000000..3b014ed7127
--- /dev/null
+++ b/plugins/woocommerce-admin/packages/data/src/options/constants.js
@@ -0,0 +1,3 @@
+
+
+export const STORE_NAME = 'wc/admin/options';
diff --git a/plugins/woocommerce-admin/packages/data/src/options/controls.js b/plugins/woocommerce-admin/packages/data/src/options/controls.js
new file mode 100644
index 00000000000..957150d28d2
--- /dev/null
+++ b/plugins/woocommerce-admin/packages/data/src/options/controls.js
@@ -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 );
+ } );
+ },
+};
diff --git a/plugins/woocommerce-admin/packages/data/src/options/index.js b/plugins/woocommerce-admin/packages/data/src/options/index.js
new file mode 100644
index 00000000000..8e017be45b3
--- /dev/null
+++ b/plugins/woocommerce-admin/packages/data/src/options/index.js
@@ -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;
diff --git a/plugins/woocommerce-admin/packages/data/src/options/reducer.js b/plugins/woocommerce-admin/packages/data/src/options/reducer.js
new file mode 100644
index 00000000000..759f3ab32ec
--- /dev/null
+++ b/plugins/woocommerce-admin/packages/data/src/options/reducer.js
@@ -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;
diff --git a/plugins/woocommerce-admin/packages/data/src/options/resolvers.js b/plugins/woocommerce-admin/packages/data/src/options/resolvers.js
new file mode 100644
index 00000000000..a1caf6c35b3
--- /dev/null
+++ b/plugins/woocommerce-admin/packages/data/src/options/resolvers.js
@@ -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 );
+ }
+}
diff --git a/plugins/woocommerce-admin/packages/data/src/options/selectors.js b/plugins/woocommerce-admin/packages/data/src/options/selectors.js
new file mode 100644
index 00000000000..c9e3d4f05ca
--- /dev/null
+++ b/plugins/woocommerce-admin/packages/data/src/options/selectors.js
@@ -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;
+};
diff --git a/plugins/woocommerce-admin/packages/data/src/options/test/reducer.js b/plugins/woocommerce-admin/packages/data/src/options/test/reducer.js
new file mode 100644
index 00000000000..27b38089d41
--- /dev/null
+++ b/plugins/woocommerce-admin/packages/data/src/options/test/reducer.js
@@ -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 );
+ } );
+
+} );
\ No newline at end of file
diff --git a/plugins/woocommerce-admin/packages/data/src/options/with-options-hydration.js b/plugins/woocommerce-admin/packages/data/src/options/with-options-hydration.js
new file mode 100644
index 00000000000..6c27bddd3f6
--- /dev/null
+++ b/plugins/woocommerce-admin/packages/data/src/options/with-options-hydration.js
@@ -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 ;
+ };
+};