From 3b945b4bba130389d3f41173a3139e8a7f05f207 Mon Sep 17 00:00:00 2001 From: Joshua T Flowers Date: Tue, 28 May 2019 22:45:52 +0800 Subject: [PATCH] Add profile onboarding mutators and selectors to wc-api (https://github.com/woocommerce/woocommerce-admin/pull/2310) * Add onboarding profile selectors and mutators to wc-api * Show onboarding profiler depending on API results * Add initial state hydration for onboarding profile * Add onboarding namespace constant --- .../client/dashboard/index.js | 18 +++- .../client/dashboard/profile-wizard/index.js | 3 +- .../dashboard/profile-wizard/steps/plugins.js | 5 +- .../client/wc-api/onboarding/constants.js | 3 + .../client/wc-api/onboarding/index.js | 13 +++ .../client/wc-api/onboarding/mutations.js | 12 +++ .../client/wc-api/onboarding/operations.js | 96 +++++++++++++++++++ .../client/wc-api/onboarding/selectors.js | 50 ++++++++++ .../client/wc-api/wc-api-spec.js | 5 + .../onboarding/class-wc-admin-onboarding.php | 13 +-- 10 files changed, 202 insertions(+), 16 deletions(-) create mode 100644 plugins/woocommerce-admin/client/wc-api/onboarding/constants.js create mode 100644 plugins/woocommerce-admin/client/wc-api/onboarding/index.js create mode 100644 plugins/woocommerce-admin/client/wc-api/onboarding/mutations.js create mode 100644 plugins/woocommerce-admin/client/wc-api/onboarding/operations.js create mode 100644 plugins/woocommerce-admin/client/wc-api/onboarding/selectors.js diff --git a/plugins/woocommerce-admin/client/dashboard/index.js b/plugins/woocommerce-admin/client/dashboard/index.js index ae1d68df711..133434c4632 100644 --- a/plugins/woocommerce-admin/client/dashboard/index.js +++ b/plugins/woocommerce-admin/client/dashboard/index.js @@ -4,6 +4,7 @@ */ import { __ } from '@wordpress/i18n'; import { Component, Fragment } from '@wordpress/element'; +import { compose } from '@wordpress/compose'; /** * Internal dependencies @@ -17,13 +18,13 @@ import { ReportFilters } from '@woocommerce/components'; import StorePerformance from './store-performance'; import TaskList from './task-list'; import ProfileWizard from './profile-wizard'; +import withSelect from 'wc-api/with-select'; -export default class Dashboard extends Component { +class Dashboard extends Component { renderDashboardOutput() { - const { query, path } = this.props; + const { path, profileItems, query } = this.props; - // @todo This should check a selector client side, with wcSettings.showProfiler as initial state. - if ( window.wcAdminFeatures.onboarding && wcSettings.showProfiler ) { + if ( window.wcAdminFeatures.onboarding && ! profileItems.skipped && ! profileItems.completed ) { return ; } @@ -58,3 +59,12 @@ export default class Dashboard extends Component { ); } } + +export default compose( + withSelect( select => { + const { getProfileItems } = select( 'wc-api' ); + const profileItems = getProfileItems(); + + return { profileItems }; + } ) +)( Dashboard ); diff --git a/plugins/woocommerce-admin/client/dashboard/profile-wizard/index.js b/plugins/woocommerce-admin/client/dashboard/profile-wizard/index.js index 0ee9cab45b3..9b1a4e6da62 100644 --- a/plugins/woocommerce-admin/client/dashboard/profile-wizard/index.js +++ b/plugins/woocommerce-admin/client/dashboard/profile-wizard/index.js @@ -16,6 +16,7 @@ import { updateQueryString } from '@woocommerce/navigation'; /** * Internal depdencies */ +import { NAMESPACE } from 'wc-api/onboarding/constants'; import ProfileWizardHeader from './header'; import Plugins from './steps/plugins'; import Start from './steps/start'; @@ -105,7 +106,7 @@ class ProfileWizard extends Component { const { addNotice } = this.props; return apiFetch( { - path: '/wc-admin/v1/onboarding/profile', + path: `${ NAMESPACE }/onboarding/profile`, method: 'POST', data: params, } ).catch( error => { diff --git a/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/plugins.js b/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/plugins.js index 7d5fe5c6b7d..26f1033beab 100644 --- a/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/plugins.js +++ b/plugins/woocommerce-admin/client/dashboard/profile-wizard/steps/plugins.js @@ -14,6 +14,7 @@ import { withDispatch } from '@wordpress/data'; * Internal depdencies */ import { H, Stepper, Card } from '@woocommerce/components'; +import { NAMESPACE } from 'wc-api/onboarding/constants'; const plugins = [ 'jetpack', 'woocommerce-services' ]; @@ -107,7 +108,7 @@ class Plugins extends Component { async doPluginAction( action, plugin ) { try { const pluginResponse = await apiFetch( { - path: `/wc-admin/v1/onboarding/plugins/${ action }`, + path: `${ NAMESPACE }/onboarding/plugins/${ action }`, method: 'POST', data: { plugin, @@ -129,7 +130,7 @@ class Plugins extends Component { async connectJetpack() { try { const connectResponse = await apiFetch( { - path: '/wc-admin/v1/onboarding/plugins/connect-jetpack', + path: `${ NAMESPACE }/onboarding/plugins/connect-jetpack`, } ); if ( connectResponse && connectResponse.connectAction ) { window.location = connectResponse.connectAction; diff --git a/plugins/woocommerce-admin/client/wc-api/onboarding/constants.js b/plugins/woocommerce-admin/client/wc-api/onboarding/constants.js new file mode 100644 index 00000000000..90ca8fbb450 --- /dev/null +++ b/plugins/woocommerce-admin/client/wc-api/onboarding/constants.js @@ -0,0 +1,3 @@ +/** @format */ + +export const NAMESPACE = '/wc-admin/v1'; diff --git a/plugins/woocommerce-admin/client/wc-api/onboarding/index.js b/plugins/woocommerce-admin/client/wc-api/onboarding/index.js new file mode 100644 index 00000000000..094ff3a82fa --- /dev/null +++ b/plugins/woocommerce-admin/client/wc-api/onboarding/index.js @@ -0,0 +1,13 @@ +/** @format */ +/** + * 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/onboarding/mutations.js b/plugins/woocommerce-admin/client/wc-api/onboarding/mutations.js new file mode 100644 index 00000000000..9f91400e29d --- /dev/null +++ b/plugins/woocommerce-admin/client/wc-api/onboarding/mutations.js @@ -0,0 +1,12 @@ +/** @format */ + +const updateProfileItems = operations => fields => { + const resourceKey = 'onboarding-profile'; + operations.update( [ resourceKey ], { + [ resourceKey ]: fields, + } ); +}; + +export default { + updateProfileItems, +}; diff --git a/plugins/woocommerce-admin/client/wc-api/onboarding/operations.js b/plugins/woocommerce-admin/client/wc-api/onboarding/operations.js new file mode 100644 index 00000000000..a3e21d1ff8d --- /dev/null +++ b/plugins/woocommerce-admin/client/wc-api/onboarding/operations.js @@ -0,0 +1,96 @@ +/** @format */ + +/** + * External dependencies + */ +import apiFetch from '@wordpress/api-fetch'; + +/** + * Internal dependencies + */ +import { getResourceName } from '../utils'; +import { NAMESPACE } from './constants'; + +function read( resourceNames, fetch = apiFetch ) { + return [ ...readProfileItems( resourceNames, fetch ) ]; +} + +function update( resourceNames, data, fetch = apiFetch ) { + return [ ...updateProfileItems( resourceNames, data, fetch ) ]; +} + +function readProfileItems( resourceNames, fetch ) { + const resourceName = 'onboarding-profile'; + + if ( resourceNames.includes( resourceName ) ) { + const url = NAMESPACE + '/onboarding/profile'; + + return [ + fetch( { path: url } ) + .then( profileItemsToResources ) + .catch( error => { + return { [ resourceName ]: { error: String( error.message ) } }; + } ), + ]; + } + + return []; +} + +function updateProfileItems( resourceNames, data, fetch ) { + const resourceName = 'onboarding-profile'; + + if ( resourceNames.includes( resourceName ) ) { + const url = NAMESPACE + '/onboarding/profile'; + + return [ + fetch( { + path: url, + method: 'POST', + data: data[ resourceName ], + } ) + .then( profileItemToResource.bind( null, data[ resourceName ] ) ) + .catch( error => { + return { [ resourceName ]: { error } }; + } ), + ]; + } + + return []; +} + +function profileItemsToResources( items ) { + const resourceName = 'onboarding-profile'; + + const itemKeys = Object.keys( items ); + + const resources = {}; + itemKeys.forEach( key => { + const item = items[ key ]; + resources[ getResourceName( resourceName, key ) ] = { data: item }; + } ); + + return { + [ resourceName ]: { + data: itemKeys, + }, + ...resources, + }; +} + +function profileItemToResource( items ) { + const resourceName = 'onboarding-profile'; + + const resources = {}; + Object.keys( items ).forEach( key => { + const item = items[ key ]; + resources[ getResourceName( resourceName, key ) ] = { data: item }; + } ); + + return resources; +} + +export default { + read, + update, +}; diff --git a/plugins/woocommerce-admin/client/wc-api/onboarding/selectors.js b/plugins/woocommerce-admin/client/wc-api/onboarding/selectors.js new file mode 100644 index 00000000000..13f29b39f24 --- /dev/null +++ b/plugins/woocommerce-admin/client/wc-api/onboarding/selectors.js @@ -0,0 +1,50 @@ +/** @format */ + +/** + * External dependencies + */ +import { isNil } from 'lodash'; + +/** + * Internal dependencies + */ +import { DEFAULT_REQUIREMENT } from '../constants'; +import { getResourceName } from '../utils'; + +const getProfileItems = ( getResource, requireResource ) => ( + requirement = DEFAULT_REQUIREMENT +) => { + const resourceName = 'onboarding-profile'; + const ids = requireResource( requirement, resourceName ).data || []; + + if ( ! ids.length ) { + return wcSettings.onboardingProfile; + } + + const items = {}; + ids.forEach( id => { + items[ id ] = getResource( getResourceName( resourceName, id ) ).data; + } ); + + return items; +}; + +const getProfileItemsError = getResource => () => { + return getResource( 'onboarding-profile' ).error; +}; + +const isGetProfileItemsRequesting = getResource => () => { + const { lastReceived, lastRequested } = getResource( 'onboarding-profile' ); + + if ( isNil( lastRequested ) || isNil( lastReceived ) ) { + return true; + } + + return lastRequested > lastReceived; +}; + +export default { + getProfileItems, + getProfileItemsError, + isGetProfileItemsRequesting, +}; 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 e5874351fc9..25182542b11 100644 --- a/plugins/woocommerce-admin/client/wc-api/wc-api-spec.js +++ b/plugins/woocommerce-admin/client/wc-api/wc-api-spec.js @@ -6,6 +6,7 @@ import items from './items'; import imports from './imports'; import notes from './notes'; +import onboarding from './onboarding'; import reportItems from './reports/items'; import reportStats from './reports/stats'; import reviews from './reviews'; @@ -18,6 +19,7 @@ function createWcApiSpec() { mutations: { ...items.mutations, ...notes.mutations, + ...onboarding.mutations, ...settings.mutations, ...user.mutations, }, @@ -25,6 +27,7 @@ function createWcApiSpec() { ...imports.selectors, ...items.selectors, ...notes.selectors, + ...onboarding.selectors, ...reportItems.selectors, ...reportStats.selectors, ...reviews.selectors, @@ -37,6 +40,7 @@ function createWcApiSpec() { ...imports.operations.read( resourceNames ), ...items.operations.read( resourceNames ), ...notes.operations.read( resourceNames ), + ...onboarding.operations.read( resourceNames ), ...reportItems.operations.read( resourceNames ), ...reportStats.operations.read( resourceNames ), ...reviews.operations.read( resourceNames ), @@ -48,6 +52,7 @@ function createWcApiSpec() { return [ ...items.operations.update( resourceNames, data ), ...notes.operations.update( resourceNames, data ), + ...onboarding.operations.update( resourceNames, data ), ...settings.operations.update( resourceNames, data ), ...user.operations.update( resourceNames, data ), ]; diff --git a/plugins/woocommerce-admin/includes/features/onboarding/class-wc-admin-onboarding.php b/plugins/woocommerce-admin/includes/features/onboarding/class-wc-admin-onboarding.php index 0eeef458b1d..204a02a9a7d 100644 --- a/plugins/woocommerce-admin/includes/features/onboarding/class-wc-admin-onboarding.php +++ b/plugins/woocommerce-admin/includes/features/onboarding/class-wc-admin-onboarding.php @@ -41,15 +41,10 @@ class WC_Admin_Onboarding { * @return bool */ public function should_show_profiler() { - // @todo Remove this once we have a proper way to dismiss the profiler. - if ( ! defined( 'WOOCOMMERCE_ADMIN_DEV_SHOW_PROFILER' ) || false === WOOCOMMERCE_ADMIN_DEV_SHOW_PROFILER ) { - return false; - } $onboarding_data = get_option( 'wc_onboarding_profile', array() ); - // @todo Update this to compare to proper bools (https://github.com/woocommerce/woocommerce-admin/issues/2299). - $is_completed = isset( $onboarding_data['completed'] ) && 'true' === $onboarding_data['completed']; - $is_skipped = isset( $onboarding_data['skipped'] ) && 'true' === $onboarding_data['skipped']; + $is_completed = isset( $onboarding_data['completed'] ) && true === $onboarding_data['completed']; + $is_skipped = isset( $onboarding_data['skipped'] ) && true === $onboarding_data['skipped']; // @todo When merging to WooCommerce Core, we should set the `completed` flag to true during the upgrade progress. // https://github.com/woocommerce/woocommerce-admin/pull/2300#discussion_r287237498. @@ -57,12 +52,12 @@ class WC_Admin_Onboarding { } /** - * Add profiler status to component settings. + * Add profiler items to component settings. * * @param array $settings Component settings. */ public function component_settings( $settings ) { - $settings['showProfiler'] = $this->should_show_profiler(); + $settings['onboardingProfile'] = get_option( 'wc_onboarding_profile', array() ); return $settings; }