From 0f03a636b23c2f935da2d9609d21afdd2d4618a7 Mon Sep 17 00:00:00 2001
From: Philipp Stracker
Date: Mon, 18 Nov 2024 18:58:54 +0100
Subject: [PATCH] =?UTF-8?q?=F0=9F=9A=A7=20Initial=20store-module=20for=20c?=
=?UTF-8?q?ommon=20details?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../resources/js/data/common/action-types.js | 19 ++++
.../resources/js/data/common/actions.js | 100 ++++++++++++++++++
.../resources/js/data/common/constants.js | 26 +++++
.../resources/js/data/common/controls.js | 32 ++++++
.../resources/js/data/common/hooks.js | 9 ++
.../resources/js/data/common/index.js | 8 ++
.../resources/js/data/common/reducer.js | 43 ++++++++
.../resources/js/data/common/resolvers.js | 37 +++++++
.../resources/js/data/common/selectors.js | 34 ++++++
.../resources/js/data/onboarding/reducer.js | 1 -
10 files changed, 308 insertions(+), 1 deletion(-)
create mode 100644 modules/ppcp-settings/resources/js/data/common/action-types.js
create mode 100644 modules/ppcp-settings/resources/js/data/common/actions.js
create mode 100644 modules/ppcp-settings/resources/js/data/common/constants.js
create mode 100644 modules/ppcp-settings/resources/js/data/common/controls.js
create mode 100644 modules/ppcp-settings/resources/js/data/common/hooks.js
create mode 100644 modules/ppcp-settings/resources/js/data/common/index.js
create mode 100644 modules/ppcp-settings/resources/js/data/common/reducer.js
create mode 100644 modules/ppcp-settings/resources/js/data/common/resolvers.js
create mode 100644 modules/ppcp-settings/resources/js/data/common/selectors.js
diff --git a/modules/ppcp-settings/resources/js/data/common/action-types.js b/modules/ppcp-settings/resources/js/data/common/action-types.js
new file mode 100644
index 000000000..1645547d9
--- /dev/null
+++ b/modules/ppcp-settings/resources/js/data/common/action-types.js
@@ -0,0 +1,19 @@
+/**
+ * Action Types: Define unique identifiers for actions across all store modules.
+ *
+ * Keys are module-internal and can have any value.
+ * Values must be unique across all store modules to avoid collisions.
+ *
+ * @file
+ */
+
+export default {
+ // Transient data.
+ SET_TRANSIENT: 'COMMON:SET_TRANSIENT',
+
+ // Persistent data.
+ SET_PERSISTENT: 'COMMON:SET_PERSISTENT',
+
+ // Controls - always start with "DO_".
+ DO_PERSIST_DATA: 'COMMON:DO_PERSIST_DATA',
+};
diff --git a/modules/ppcp-settings/resources/js/data/common/actions.js b/modules/ppcp-settings/resources/js/data/common/actions.js
new file mode 100644
index 000000000..3992e0ff5
--- /dev/null
+++ b/modules/ppcp-settings/resources/js/data/common/actions.js
@@ -0,0 +1,100 @@
+/**
+ * Action Creators: Define functions to create action objects.
+ *
+ * These functions update state or trigger side effects (e.g., async operations).
+ * Exported functions must have unique names across all store modules.
+ * Actions are categorized as Transient, Persistent, or Side effect.
+ *
+ * @file
+ */
+
+import ACTION_TYPES from './action-types';
+
+/**
+ * Transient. Marks the onboarding details as "ready", i.e., fully initialized.
+ *
+ * @param {boolean} isReady
+ * @return {{type: string, isReady: boolean}} The action.
+ */
+export const setIsReady = ( isReady ) => {
+ return {
+ type: ACTION_TYPES.SET_TRANSIENT,
+ isReady,
+ };
+};
+
+/**
+ * Transient. Changes the "saving" flag.
+ *
+ * @param {boolean} isSaving
+ * @return {{type: string, isSaving: boolean}} The action.
+ */
+export const setIsSaving = ( isSaving ) => {
+ return {
+ type: ACTION_TYPES.SET_TRANSIENT,
+ isSaving,
+ };
+};
+
+/**
+ * Persistent. Sets the sandbox mode on or off.
+ *
+ * @param {boolean} useSandbox
+ * @return {{type: string, useSandbox: boolean}} An action.
+ */
+export const setSandboxMode = ( useSandbox ) => {
+ return {
+ type: ACTION_TYPES.SET_PERSISTENT,
+ useSandbox,
+ };
+};
+
+/**
+ * Persistent. Toggles the "Manual Connection" mode on or off.
+ *
+ * @param {boolean} useManualConnection
+ * @return {{type: string, useManualConnection: boolean}} An action.
+ */
+export const setManualConnectionMode = ( useManualConnection ) => {
+ return {
+ type: ACTION_TYPES.SET_PERSISTENT,
+ useManualConnection,
+ };
+};
+
+/**
+ * Persistent. Changes the "client ID" value.
+ *
+ * @param {string} clientId
+ * @return {{type: string, clientId: string}} The action.
+ */
+export const setClientId = ( clientId ) => {
+ return {
+ type: ACTION_TYPES.SET_PERSISTENT,
+ clientId,
+ };
+};
+
+/**
+ * Persistent. Changes the "client secret" value.
+ *
+ * @param {string} clientSecret
+ * @return {{type: string, clientSecret: string}} The action.
+ */
+export const setClientSecret = ( clientSecret ) => {
+ return {
+ type: ACTION_TYPES.SET_PERSISTENT,
+ clientSecret,
+ };
+};
+
+/**
+ * Side effect. Saves the persistent details to the WP database.
+ *
+ * @return {{type: string}} The action.
+ */
+export function* commonPersist() {
+ return {
+ type: ACTION_TYPES.DO_PERSIST_DATA,
+ };
+}
diff --git a/modules/ppcp-settings/resources/js/data/common/constants.js b/modules/ppcp-settings/resources/js/data/common/constants.js
new file mode 100644
index 000000000..365bfe305
--- /dev/null
+++ b/modules/ppcp-settings/resources/js/data/common/constants.js
@@ -0,0 +1,26 @@
+/**
+ * Name of the module-store in the main Redux store.
+ *
+ * Helps to isolate data, used by reducer and selectors.
+ *
+ * @type {string}
+ */
+export const STORE_KEY = 'common';
+
+/**
+ * REST path to hydrate data of this module by loading data from the WP DB..
+ *
+ * Used by resolvers.
+ *
+ * @type {string}
+ */
+export const REST_HYDRATE_PATH = 'common';
+
+/**
+ * REST path to persist data of this module to the WP DB.
+ *
+ * Used by controls.
+ *
+ * @type {string}
+ */
+export const REST_PERSIST_PATH = 'common';
diff --git a/modules/ppcp-settings/resources/js/data/common/controls.js b/modules/ppcp-settings/resources/js/data/common/controls.js
new file mode 100644
index 000000000..45b3e6ea5
--- /dev/null
+++ b/modules/ppcp-settings/resources/js/data/common/controls.js
@@ -0,0 +1,32 @@
+/**
+ * Controls: Implement side effects, typically asynchronous operations.
+ *
+ * Controls use ACTION_TYPES keys as identifiers to ensure uniqueness.
+ * They are triggered by corresponding actions and handle external interactions.
+ *
+ * @file
+ */
+
+import { select } from '@wordpress/data';
+import { apiFetch } from '@wordpress/api-fetch';
+
+import { NAMESPACE, STORE_NAME } from '../constants';
+import { REST_PERSIST_PATH } from './constants';
+import ACTION_TYPES from './action-types';
+export const controls = {
+ [ ACTION_TYPES.DO_PERSIST_DATA ]: async () => {
+ const path = `${ NAMESPACE }/${ REST_PERSIST_PATH }`;
+ const data = select( STORE_NAME ).getPersistentData();
+
+ try {
+ return await apiFetch( {
+ path,
+ method: 'post',
+ data,
+ } );
+ } catch ( error ) {
+ console.error( 'Error saving progress.', error );
+ throw error;
+ }
+ },
+};
diff --git a/modules/ppcp-settings/resources/js/data/common/hooks.js b/modules/ppcp-settings/resources/js/data/common/hooks.js
new file mode 100644
index 000000000..a201c6d37
--- /dev/null
+++ b/modules/ppcp-settings/resources/js/data/common/hooks.js
@@ -0,0 +1,9 @@
+/**
+ * Hooks: Provide the main API for components to interact with the store.
+ *
+ * These encapsulate store interactions, offering a consistent interface.
+ * Hooks simplify data access and manipulation for components.
+ * Exported hooks must have unique names across all store modules.
+ *
+ * @file
+ */
diff --git a/modules/ppcp-settings/resources/js/data/common/index.js b/modules/ppcp-settings/resources/js/data/common/index.js
new file mode 100644
index 000000000..a8c685d37
--- /dev/null
+++ b/modules/ppcp-settings/resources/js/data/common/index.js
@@ -0,0 +1,8 @@
+import { STORE_KEY } from './constants';
+import reducer from './reducer';
+import * as selectors from './selectors';
+import * as actions from './actions';
+import * as resolvers from './resolvers';
+import { controls } from './controls';
+
+export { reducer, selectors, actions, resolvers, controls, STORE_KEY };
diff --git a/modules/ppcp-settings/resources/js/data/common/reducer.js b/modules/ppcp-settings/resources/js/data/common/reducer.js
new file mode 100644
index 000000000..583fd7540
--- /dev/null
+++ b/modules/ppcp-settings/resources/js/data/common/reducer.js
@@ -0,0 +1,43 @@
+/**
+ * Reducer: Defines store structure and state updates for this module.
+ *
+ * Manages both transient (temporary) and persistent (saved) state.
+ * Each module uses isolated memory objects to prevent conflicts.
+ * The initial state must define all properties, as dynamic additions are not supported.
+ *
+ * @file
+ */
+
+import { createReducer, createSetters } from '../utils';
+import ACTION_TYPES from './action-types';
+
+// Store structure.
+
+const defaultTransient = {
+ isReady: false,
+ isSaving: false,
+};
+
+const defaultPersistent = {
+ useSandbox: false,
+ useManualConnection: false,
+ clientId: '',
+ clientSecret: '',
+};
+
+// Reducer logic.
+
+const [ setTransient, setPersistent ] = createSetters(
+ defaultTransient,
+ defaultPersistent
+);
+
+const commonReducer = createReducer( defaultTransient, defaultPersistent, {
+ [ ACTION_TYPES.SET_TRANSIENT ]: ( state, action ) =>
+ setTransient( state, action ),
+
+ [ ACTION_TYPES.SET_PERSISTENT ]: ( state, action ) =>
+ setPersistent( state, action ),
+} );
+
+export default commonReducer;
diff --git a/modules/ppcp-settings/resources/js/data/common/resolvers.js b/modules/ppcp-settings/resources/js/data/common/resolvers.js
new file mode 100644
index 000000000..2358496be
--- /dev/null
+++ b/modules/ppcp-settings/resources/js/data/common/resolvers.js
@@ -0,0 +1,37 @@
+/**
+ * Resolvers: Handle asynchronous data fetching for the store.
+ *
+ * These functions update store state with data from external sources.
+ * Each resolver corresponds to a specific selector but must have a unique name.
+ * Resolvers are called automatically when selectors request unavailable data.
+ *
+ * @file
+ */
+
+import { dispatch } from '@wordpress/data';
+import { __ } from '@wordpress/i18n';
+import { apiFetch } from '@wordpress/data-controls';
+
+import { NAMESPACE } from '../constants';
+import { setIsReady, setCommonDetails } from './actions';
+import { REST_HYDRATE_PATH } from './constants';
+
+/**
+ * Retrieve settings from the site's REST API.
+ */
+export function* commonPersistentData() {
+ const path = `${ NAMESPACE }/${ REST_HYDRATE_PATH }`;
+
+ try {
+ const result = yield apiFetch( { path } );
+ yield setCommonDetails( result );
+ yield setIsReady( true );
+ } catch ( e ) {
+ yield dispatch( 'core/notices' ).createErrorNotice(
+ __(
+ 'Error retrieving plugin details.',
+ 'woocommerce-paypal-payments'
+ )
+ );
+ }
+}
diff --git a/modules/ppcp-settings/resources/js/data/common/selectors.js b/modules/ppcp-settings/resources/js/data/common/selectors.js
new file mode 100644
index 000000000..4ea6e86a7
--- /dev/null
+++ b/modules/ppcp-settings/resources/js/data/common/selectors.js
@@ -0,0 +1,34 @@
+/**
+ * Selectors: Extract specific pieces of state from the store.
+ *
+ * These functions provide a consistent interface for accessing store data.
+ * They allow components to retrieve data without knowing the store structure.
+ * Exported functions must have unique names across all store modules.
+ *
+ * @file
+ */
+
+import { STORE_KEY } from './constants';
+
+const EMPTY_OBJ = Object.freeze( {} );
+
+const getState = ( state ) => {
+ if ( ! state ) {
+ return EMPTY_OBJ;
+ }
+
+ return state[ STORE_KEY ] || EMPTY_OBJ;
+};
+
+export const commonPersistentData = ( state ) => {
+ return getState( state ).data || EMPTY_OBJ;
+};
+
+export const commonTransientData = ( state ) => {
+ const { data, flags, ...transientState } = getState( state );
+ return transientState || EMPTY_OBJ;
+};
+
+export const commonFlags = ( state ) => {
+ return getState( state ).flags || EMPTY_OBJ;
+};
diff --git a/modules/ppcp-settings/resources/js/data/onboarding/reducer.js b/modules/ppcp-settings/resources/js/data/onboarding/reducer.js
index 41a57c578..0f253fc02 100644
--- a/modules/ppcp-settings/resources/js/data/onboarding/reducer.js
+++ b/modules/ppcp-settings/resources/js/data/onboarding/reducer.js
@@ -4,7 +4,6 @@
* Manages both transient (temporary) and persistent (saved) state.
* Each module uses isolated memory objects to prevent conflicts.
* The initial state must define all properties, as dynamic additions are not supported.
- * Reducers are separated per module to avoid conflicts in state management.
*
* @file
*/