diff --git a/modules/ppcp-settings/resources/js/data/common/action-types.js b/modules/ppcp-settings/resources/js/data/common/action-types.js index f33e8a9ee..990ea8759 100644 --- a/modules/ppcp-settings/resources/js/data/common/action-types.js +++ b/modules/ppcp-settings/resources/js/data/common/action-types.js @@ -6,26 +6,26 @@ export default { // Transient data. - SET_TRANSIENT: 'COMMON:SET_TRANSIENT', + SET_TRANSIENT: 'ppcp/common/SET_TRANSIENT', // Persistent data. - SET_PERSISTENT: 'COMMON:SET_PERSISTENT', - RESET: 'COMMON:RESET', - HYDRATE: 'COMMON:HYDRATE', + SET_PERSISTENT: 'ppcp/common/SET_PERSISTENT', + RESET: 'ppcp/common/RESET', + HYDRATE: 'ppcp/common/HYDRATE', // Activity management (advanced solution that replaces the isBusy state). - START_ACTIVITY: 'COMMON:START_ACTIVITY', - STOP_ACTIVITY: 'COMMON:STOP_ACTIVITY', + START_ACTIVITY: 'ppcp/common/START_ACTIVITY', + STOP_ACTIVITY: 'ppcp/common/STOP_ACTIVITY', // Controls - always start with "DO_". - DO_PERSIST_DATA: 'COMMON:DO_PERSIST_DATA', - DO_DIRECT_API_AUTHENTICATION: 'COMMON:DO_DIRECT_API_AUTHENTICATION', - DO_OAUTH_AUTHENTICATION: 'COMMON:DO_OAUTH_AUTHENTICATION', - DO_DISCONNECT_MERCHANT: 'COMMON:DO_DISCONNECT_MERCHANT', - DO_GENERATE_ONBOARDING_URL: 'COMMON:DO_GENERATE_ONBOARDING_URL', - DO_REFRESH_MERCHANT: 'COMMON:DO_REFRESH_MERCHANT', - DO_REFRESH_FEATURES: 'COMMON:DO_REFRESH_FEATURES', - DO_RESUBSCRIBE_WEBHOOKS: 'COMMON:DO_RESUBSCRIBE_WEBHOOKS', - DO_START_WEBHOOK_SIMULATION: 'COMMON:DO_START_WEBHOOK_SIMULATION', - DO_CHECK_WEBHOOK_SIMULATION: 'COMMON:DO_CHECK_WEBHOOK_SIMULATION', + DO_PERSIST_DATA: 'ppcp/common/DO_PERSIST_DATA', + DO_DIRECT_API_AUTHENTICATION: 'ppcp/common/DO_DIRECT_API_AUTHENTICATION', + DO_OAUTH_AUTHENTICATION: 'ppcp/common/DO_OAUTH_AUTHENTICATION', + DO_DISCONNECT_MERCHANT: 'ppcp/common/DO_DISCONNECT_MERCHANT', + DO_GENERATE_ONBOARDING_URL: 'ppcp/common/DO_GENERATE_ONBOARDING_URL', + DO_REFRESH_MERCHANT: 'ppcp/common/DO_REFRESH_MERCHANT', + DO_REFRESH_FEATURES: 'ppcp/common/DO_REFRESH_FEATURES', + DO_RESUBSCRIBE_WEBHOOKS: 'ppcp/common/DO_RESUBSCRIBE_WEBHOOKS', + DO_START_WEBHOOK_SIMULATION: 'ppcp/common/DO_START_WEBHOOK_SIMULATION', + DO_CHECK_WEBHOOK_SIMULATION: 'ppcp/common/DO_CHECK_WEBHOOK_SIMULATION', }; diff --git a/modules/ppcp-settings/resources/js/data/common/actions-thunk.js b/modules/ppcp-settings/resources/js/data/common/actions-thunk.js index 240f78f21..6a8a9fc26 100644 --- a/modules/ppcp-settings/resources/js/data/common/actions-thunk.js +++ b/modules/ppcp-settings/resources/js/data/common/actions-thunk.js @@ -1,46 +1,83 @@ -import { select } from '@wordpress/data'; - -import ACTION_TYPES from './action-types'; -import { hydrate } from './actions'; -import { STORE_NAME } from './constants'; +import { + REST_CONNECTION_URL_PATH, + REST_DIRECT_AUTHENTICATION_PATH, + REST_DISCONNECT_MERCHANT_PATH, + REST_HYDRATE_MERCHANT_PATH, + REST_OAUTH_AUTHENTICATION_PATH, + REST_PERSIST_PATH, + REST_REFRESH_FEATURES_PATH, + REST_WEBHOOKS, + REST_WEBHOOKS_SIMULATE, +} from './constants'; +import apiFetch from '@wordpress/api-fetch'; /** * Side effect. Saves the persistent details to the WP database. * - * @return {Action} The action. + * @return {Function} The thunk function. */ -export const persist = function* () { - const data = yield select( STORE_NAME ).persistentData(); +export function persist() { + return async ( { select } ) => { + const data = select.persistentData(); - yield { type: ACTION_TYPES.DO_PERSIST_DATA, data }; -}; + await apiFetch( { + path: REST_PERSIST_PATH, + method: 'POST', + data, + } ); + }; +} /** * Side effect. Fetches the ISU-login URL for a sandbox account. * - * @return {Action} The action. + * @return {Function} The thunk function. */ -export const sandboxOnboardingUrl = function* () { - return yield { - type: ACTION_TYPES.DO_GENERATE_ONBOARDING_URL, - useSandbox: true, - products: [ 'EXPRESS_CHECKOUT' ], +export function sandboxOnboardingUrl() { + return async () => { + try { + return apiFetch( { + path: REST_CONNECTION_URL_PATH, + method: 'POST', + data: { + useSandbox: true, + products: [ 'EXPRESS_CHECKOUT' ], + }, + } ); + } catch ( e ) { + return { + success: false, + error: e, + }; + } }; -}; +} /** * Side effect. Fetches the ISU-login URL for a production account. * * @param {string[]} products Which products/features to display in the ISU popup. - * @return {Action} The action. + * @return {Function} The thunk function. */ -export const productionOnboardingUrl = function* ( products = [] ) { - return yield { - type: ACTION_TYPES.DO_GENERATE_ONBOARDING_URL, - useSandbox: false, - products, +export function productionOnboardingUrl( products = [] ) { + return async () => { + try { + return apiFetch( { + path: REST_CONNECTION_URL_PATH, + method: 'POST', + data: { + useSandbox: false, + products, + }, + } ); + } catch ( e ) { + return { + success: false, + error: e, + }; + } }; -}; +} /** * Side effect. Initiates a direct connection attempt using the provided client ID and secret. @@ -52,20 +89,32 @@ export const productionOnboardingUrl = function* ( products = [] ) { * @param {string} clientId - AP client ID (always 80-characters, starting with "A"). * @param {string} clientSecret - API client secret. * @param {boolean} useSandbox - Whether the credentials are for a sandbox account. - * @return {Action} The action. + * @return {Function} The thunk function. */ -export const authenticateWithCredentials = function* ( +export function authenticateWithCredentials( clientId, clientSecret, useSandbox ) { - return yield { - type: ACTION_TYPES.DO_DIRECT_API_AUTHENTICATION, - clientId, - clientSecret, - useSandbox, + return async () => { + try { + return await apiFetch( { + path: REST_DIRECT_AUTHENTICATION_PATH, + method: 'POST', + data: { + clientId, + clientSecret, + useSandbox, + }, + } ); + } catch ( e ) { + return { + success: false, + error: e, + }; + } }; -}; +} /** * Side effect. Completes the ISU login by authenticating the user via the one time sharedId and @@ -75,97 +124,156 @@ export const authenticateWithCredentials = function* ( * parameters are dynamically generated during the authentication process, and not managed by our * Redux store. * - * @param {string} sharedId - OAuth client ID; called "sharedId" to prevent confusion with the API client ID. + * @param {string} sharedId - OAuth client ID; called "sharedId" to prevent confusion with the + * API client ID. * @param {string} authCode - OAuth authorization code provided during onboarding. * @param {boolean} useSandbox - Whether the credentials are for a sandbox account. - * @return {Action} The action. + * @return {Function} The thunk function. */ -export const authenticateWithOAuth = function* ( - sharedId, - authCode, - useSandbox -) { - return yield { - type: ACTION_TYPES.DO_OAUTH_AUTHENTICATION, - sharedId, - authCode, - useSandbox, +export function authenticateWithOAuth( sharedId, authCode, useSandbox ) { + return async () => { + try { + return await apiFetch( { + path: REST_OAUTH_AUTHENTICATION_PATH, + method: 'POST', + data: { + sharedId, + authCode, + useSandbox, + }, + } ); + } catch ( e ) { + return { + success: false, + error: e, + }; + } }; -}; +} /** * Side effect. Checks webhook simulation. * - * @return {Action} The action. + * @return {Function} The thunk function. */ -export const disconnectMerchant = function* () { - return yield { type: ACTION_TYPES.DO_DISCONNECT_MERCHANT }; -}; +export function disconnectMerchant() { + return async () => { + return await apiFetch( { + path: REST_DISCONNECT_MERCHANT_PATH, + method: 'POST', + } ); + }; +} /** * Side effect. Clears and refreshes the merchant data via a REST request. * - * @return {Action} The action. + * @return {Function} The thunk function. */ -export const refreshMerchantData = function* () { - const result = yield { type: ACTION_TYPES.DO_REFRESH_MERCHANT }; +export function refreshMerchantData() { + return async ( { dispatch } ) => { + try { + const result = await apiFetch( { + path: REST_HYDRATE_MERCHANT_PATH, + } ); - if ( result.success && result.merchant ) { - yield hydrate( result ); - } + if ( result.success && result.merchant ) { + dispatch.hydrate( result ); + } - return result; -}; + return result; + } catch ( e ) { + return { + success: false, + error: e, + }; + } + }; +} /** * Side effect. * Purges all feature status data via a REST request. * Refreshes the merchant data via a REST request. * - * @return {Action} The action. + * @return {Function} The thunk function. */ -export const refreshFeatureStatuses = function* () { - const result = yield { type: ACTION_TYPES.DO_REFRESH_FEATURES }; +export function refreshFeatureStatuses() { + return async ( { dispatch } ) => { + try { + const result = await apiFetch( { + path: REST_REFRESH_FEATURES_PATH, + method: 'POST', + } ); - if ( result && result.success ) { - // TODO: Review if we can get the updated feature details in the result.data instead of - // doing a second refreshMerchantData() request. - yield refreshMerchantData(); - } + if ( result && result.success ) { + // TODO: Review if we can get the updated feature details in the result.data + // instead of doing a second refreshMerchantData() request. + await dispatch.refreshMerchantData(); + } - return result; -}; + return result; + } catch ( e ) { + return { + success: false, + error: e, + message: e.message, + }; + } + }; +} /** * Side effect * Refreshes subscribed webhooks via a REST request * - * @return {Action} The action. + * @return {Function} The thunk function. */ -export const resubscribeWebhooks = function* () { - const result = yield { type: ACTION_TYPES.DO_RESUBSCRIBE_WEBHOOKS }; +export function resubscribeWebhooks() { + return async ( { dispatch } ) => { + try { + const result = await apiFetch( { + method: 'POST', + path: REST_WEBHOOKS, + } ); - if ( result && result.success ) { - yield hydrate( result ); - } + if ( result.success && result.merchant ) { + dispatch.hydrate( result ); + } - return result; -}; + return result; + } catch ( e ) { + return { + success: false, + error: e, + }; + } + }; +} /** * Side effect. Starts webhook simulation. * - * @return {Action} The action. + * @return {Function} The thunk function. */ -export const startWebhookSimulation = function* () { - return yield { type: ACTION_TYPES.DO_START_WEBHOOK_SIMULATION }; -}; +export function startWebhookSimulation() { + return async () => { + return await apiFetch( { + method: 'POST', + path: REST_WEBHOOKS_SIMULATE, + } ); + }; +} /** * Side effect. Checks webhook simulation. * - * @return {Action} The action. + * @return {Function} The thunk function. */ -export const checkWebhookSimulationState = function* () { - return yield { type: ACTION_TYPES.DO_CHECK_WEBHOOK_SIMULATION }; -}; +export function checkWebhookSimulationState() { + return async () => { + return await apiFetch( { + path: REST_WEBHOOKS_SIMULATE, + } ); + }; +} diff --git a/modules/ppcp-settings/resources/js/data/common/controls.js b/modules/ppcp-settings/resources/js/data/common/controls.js deleted file mode 100644 index a318e2116..000000000 --- a/modules/ppcp-settings/resources/js/data/common/controls.js +++ /dev/null @@ -1,154 +0,0 @@ -/** - * Controls: Implement side effects, typically asynchronous operations. - * - * Controls use ACTION_TYPES keys as identifiers. - * They are triggered by corresponding actions and handle external interactions. - * - * @file - */ - -import apiFetch from '@wordpress/api-fetch'; - -import { - REST_PERSIST_PATH, - REST_CONNECTION_URL_PATH, - REST_HYDRATE_MERCHANT_PATH, - REST_REFRESH_FEATURES_PATH, - REST_DIRECT_AUTHENTICATION_PATH, - REST_OAUTH_AUTHENTICATION_PATH, - REST_DISCONNECT_MERCHANT_PATH, - REST_WEBHOOKS, - REST_WEBHOOKS_SIMULATE, -} from './constants'; -import ACTION_TYPES from './action-types'; - -export const controls = { - async [ ACTION_TYPES.DO_PERSIST_DATA ]( { data } ) { - try { - await apiFetch( { - path: REST_PERSIST_PATH, - method: 'POST', - data, - } ); - } catch ( error ) { - console.error( 'Error saving data.', error ); - } - }, - - async [ ACTION_TYPES.DO_GENERATE_ONBOARDING_URL ]( { - products, - useSandbox, - } ) { - try { - return apiFetch( { - path: REST_CONNECTION_URL_PATH, - method: 'POST', - data: { useSandbox, products }, - } ); - } catch ( e ) { - return { - success: false, - error: e, - }; - } - }, - - async [ ACTION_TYPES.DO_DIRECT_API_AUTHENTICATION ]( { - clientId, - clientSecret, - useSandbox, - } ) { - try { - return await apiFetch( { - path: REST_DIRECT_AUTHENTICATION_PATH, - method: 'POST', - data: { - clientId, - clientSecret, - useSandbox, - }, - } ); - } catch ( e ) { - return { - success: false, - error: e, - }; - } - }, - - async [ ACTION_TYPES.DO_OAUTH_AUTHENTICATION ]( { - sharedId, - authCode, - useSandbox, - } ) { - try { - return await apiFetch( { - path: REST_OAUTH_AUTHENTICATION_PATH, - method: 'POST', - data: { - sharedId, - authCode, - useSandbox, - }, - } ); - } catch ( e ) { - return { - success: false, - error: e, - }; - } - }, - - async [ ACTION_TYPES.DO_DISCONNECT_MERCHANT ]() { - return await apiFetch( { - path: REST_DISCONNECT_MERCHANT_PATH, - method: 'POST', - } ); - }, - - async [ ACTION_TYPES.DO_REFRESH_MERCHANT ]() { - try { - return await apiFetch( { path: REST_HYDRATE_MERCHANT_PATH } ); - } catch ( e ) { - return { - success: false, - error: e, - }; - } - }, - - async [ ACTION_TYPES.DO_REFRESH_FEATURES ]() { - try { - return await apiFetch( { - path: REST_REFRESH_FEATURES_PATH, - method: 'POST', - } ); - } catch ( e ) { - return { - success: false, - error: e, - message: e.message, - }; - } - }, - - async [ ACTION_TYPES.DO_RESUBSCRIBE_WEBHOOKS ]() { - return await apiFetch( { - method: 'POST', - path: REST_WEBHOOKS, - } ); - }, - - async [ ACTION_TYPES.DO_START_WEBHOOK_SIMULATION ]() { - return await apiFetch( { - method: 'POST', - path: REST_WEBHOOKS_SIMULATE, - } ); - }, - - async [ ACTION_TYPES.DO_CHECK_WEBHOOK_SIMULATION ]() { - return await apiFetch( { - path: REST_WEBHOOKS_SIMULATE, - } ); - }, -}; diff --git a/modules/ppcp-settings/resources/js/data/common/index.js b/modules/ppcp-settings/resources/js/data/common/index.js index 9e4f22782..89a554b0e 100644 --- a/modules/ppcp-settings/resources/js/data/common/index.js +++ b/modules/ppcp-settings/resources/js/data/common/index.js @@ -1,5 +1,4 @@ import { createReduxStore, register } from '@wordpress/data'; -import { controls as wpControls } from '@wordpress/data-controls'; import { STORE_NAME } from './constants'; import reducer from './reducer'; @@ -7,8 +6,7 @@ import * as selectors from './selectors'; import * as actions from './actions'; import * as thunkActions from './actions-thunk'; import * as hooks from './hooks'; -import { resolvers } from './resolvers'; -import { controls } from './controls'; +import * as resolvers from './resolvers'; /** * Initializes and registers the settings store with WordPress data layer. @@ -19,7 +17,6 @@ import { controls } from './controls'; export const initStore = () => { const store = createReduxStore( STORE_NAME, { reducer, - controls: { ...wpControls, ...controls }, actions: { ...actions, ...thunkActions }, selectors, resolvers, diff --git a/modules/ppcp-settings/resources/js/data/common/resolvers.js b/modules/ppcp-settings/resources/js/data/common/resolvers.js index 2c1f0d3c2..9fc8428c0 100644 --- a/modules/ppcp-settings/resources/js/data/common/resolvers.js +++ b/modules/ppcp-settings/resources/js/data/common/resolvers.js @@ -8,34 +8,37 @@ * @file */ -import { dispatch } from '@wordpress/data'; import { __ } from '@wordpress/i18n'; -import { apiFetch } from '@wordpress/data-controls'; +import apiFetch from '@wordpress/api-fetch'; -import { STORE_NAME, REST_HYDRATE_PATH, REST_WEBHOOKS } from './constants'; +import { REST_HYDRATE_PATH, REST_WEBHOOKS } from './constants'; -export const resolvers = { - /** - * Retrieve settings from the site's REST API. - */ - *persistentData() { +/** + * Retrieve settings from the site's REST API. + */ +export function persistentData() { + return async ( { dispatch, registry } ) => { try { - const result = yield apiFetch( { path: REST_HYDRATE_PATH } ); - const webhooks = yield apiFetch( { path: REST_WEBHOOKS } ); + const [ result, webhooks ] = await Promise.all( [ + apiFetch( { path: REST_HYDRATE_PATH } ), + apiFetch( { path: REST_WEBHOOKS } ), + ] ); - if ( webhooks.success && webhooks.data ) { + if ( result?.success && webhooks?.success && webhooks.data ) { result.webhooks = webhooks.data; } - yield dispatch( STORE_NAME ).hydrate( result ); - yield dispatch( STORE_NAME ).setIsReady( true ); + await dispatch.hydrate( result ); + await dispatch.setIsReady( true ); } catch ( e ) { - yield dispatch( 'core/notices' ).createErrorNotice( - __( - 'Error retrieving plugin details.', - 'woocommerce-paypal-payments' - ) - ); + await registry + .dispatch( 'core/notices' ) + .createErrorNotice( + __( + 'Error retrieving plugin details.', + 'woocommerce-paypal-payments' + ) + ); } - }, -}; + }; +}