Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Save and display Pay Later Messaging configuration (4102) #3037

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public function handle_request(): bool {
*
* @param array $config The configurator config.
*/
private function save_config( array $config ): void {
public function save_config( array $config ): void {
$this->settings->set( 'pay_later_enable_styling_per_messaging_location', true );
$this->settings->set( 'pay_later_messaging_enabled', true );

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import React, { useEffect } from 'react';
import { PayLaterMessagingHooks } from '../../../data';

const TabPayLaterMessaging = () => {
const config = {}; // Replace with the appropriate/saved configuration.
const {
config,
setCart,
setCheckout,
setProduct,
setShop,
setHome,
setCustom_placement,
} = PayLaterMessagingHooks.usePayLaterMessaging();
const PcpPayLaterConfigurator =
window.ppcpSettings?.PcpPayLaterConfigurator;

Expand All @@ -27,17 +36,16 @@ const TabPayLaterMessaging = () => {
subheader: 'ppcp-r-paylater-configurator__subheader',
},
onSave: ( data ) => {
/*
TODO:
- The saving will be handled in a separate PR.
- One option could be:
- When saving the settings, programmatically click on the configurator's
"Save Changes" button and send the request to PHP.
*/
setCart( data.config.cart );
setCheckout( data.config.checkout );
setProduct( data.config.product );
setShop( data.config.shop );
setHome( data.config.home );
setCustom_placement( data.config.custom_placement );
},
} );
}
}, [ PcpPayLaterConfigurator ] );
}, [ PcpPayLaterConfigurator, config ] );

return (
<div
Expand Down
2 changes: 1 addition & 1 deletion modules/ppcp-settings/resources/js/data/_example/hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const useHooks = () => {
};
};

export const useState = () => {
export const useStore = () => {
const { persist, isReady } = useHooks();
return { persist, isReady };
};
Expand Down
13 changes: 12 additions & 1 deletion modules/ppcp-settings/resources/js/data/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,17 @@ import * as Payment from './payment';
import * as Settings from './settings';
import * as Styling from './styling';
import * as Todos from './todos';
import * as PayLaterMessaging from './pay-later-messaging';

const stores = [ Onboarding, Common, Payment, Settings, Styling, Todos ];
const stores = [
Onboarding,
Common,
Payment,
Settings,
Styling,
Todos,
PayLaterMessaging,
];

stores.forEach( ( store ) => {
try {
Expand All @@ -30,13 +39,15 @@ export const PaymentHooks = Payment.hooks;
export const SettingsHooks = Settings.hooks;
export const StylingHooks = Styling.hooks;
export const TodosHooks = Todos.hooks;
export const PayLaterMessagingHooks = PayLaterMessaging.hooks;

export const OnboardingStoreName = Onboarding.STORE_NAME;
export const CommonStoreName = Common.STORE_NAME;
export const PaymentStoreName = Payment.STORE_NAME;
export const SettingsStoreName = Settings.STORE_NAME;
export const StylingStoreName = Styling.STORE_NAME;
export const TodosStoreName = Todos.STORE_NAME;
export const PayLaterMessagingStoreName = PayLaterMessaging.STORE_NAME;

export * from './configuration';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* Action Types: Define unique identifiers for actions across all store modules.
*
* @file
*/

export default {
// Transient data.
SET_TRANSIENT: 'PAY_LATER_MESSAGING:SET_TRANSIENT',

// Persistent data.
SET_PERSISTENT: 'PAY_LATER_MESSAGING:SET_PERSISTENT',
RESET: 'PAY_LATER_MESSAGING:RESET',
HYDRATE: 'PAY_LATER_MESSAGING:HYDRATE',

// Controls - always start with "DO_".
DO_PERSIST_DATA: 'PAY_LATER_MESSAGING:DO_PERSIST_DATA',
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/**
* Action Creators: Define functions to create action objects.
*
* These functions update state or trigger side effects (e.g., async operations).
* Actions are categorized as Transient, Persistent, or Side effect.
*
* @file
*/

import { select } from '@wordpress/data';

import ACTION_TYPES from './action-types';
import { STORE_NAME } from './constants';

/**
* @typedef {Object} Action An action object that is handled by a reducer or control.
* @property {string} type - The action type.
* @property {Object?} payload - Optional payload for the action.
*/

/**
* Special. Resets all values in the store to initial defaults.
*
* @return {Action} The action.
*/
export const reset = () => ( { type: ACTION_TYPES.RESET } );

/**
* Persistent. Set the full store details during app initialization.
*
* @param {{data: {}, flags?: {}}} payload
* @return {Action} The action.
*/
export const hydrate = ( payload ) => ( {
type: ACTION_TYPES.HYDRATE,
payload,
} );

/**
* Generic transient-data updater.
*
* @param {string} prop Name of the property to update.
* @param {any} value The new value of the property.
* @return {Action} The action.
*/
export const setTransient = ( prop, value ) => ( {
type: ACTION_TYPES.SET_TRANSIENT,
payload: { [ prop ]: value },
} );

/**
* Generic persistent-data updater.
*
* @param {string} prop Name of the property to update.
* @param {any} value The new value of the property.
* @return {Action} The action.
*/
export const setPersistent = ( prop, value ) => ( {
type: ACTION_TYPES.SET_PERSISTENT,
payload: { [ prop ]: value },
} );

/**
* Transient. Marks the store as "ready", i.e., fully initialized.
*
* @param {boolean} isReady
* @return {Action} The action.
*/
export const setIsReady = ( isReady ) => setTransient( 'isReady', isReady );

/**
* Side effect. Triggers the persistence of store data to the server.
*
* @return {Action} The action.
*/
export const persist = function* () {
const data = yield select( STORE_NAME ).persistentData();

yield { type: ACTION_TYPES.DO_PERSIST_DATA, data };
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* Name of the Redux store module.
*
* Used by: Reducer, Selector, Index
*
* @type {string}
*/
export const STORE_NAME = 'wc/paypal/pay_later_messaging';

/**
* REST path to hydrate data of this module by loading data from the WP DB.
*
* Used by: Resolvers
* See: PayLaterMessagingEndpoint.php
*
* @type {string}
*/
export const REST_HYDRATE_PATH = '/wc/v3/wc_paypal/pay_later_messaging';

/**
* REST path to persist data of this module to the WP DB.
*
* Used by: Controls
* See: PayLaterMessagingEndpoint.php
*
* @type {string}
*/
export const REST_PERSIST_PATH = '/wc/v3/wc_paypal/pay_later_messaging';
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/**
* 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 } from './constants';
import ACTION_TYPES from './action-types';

export const controls = {
async [ ACTION_TYPES.DO_PERSIST_DATA ]( { data } ) {
return await apiFetch( {
path: REST_PERSIST_PATH,
method: 'POST',
data,
} );
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* 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.
*
* @file
*/

import { useDispatch } from '@wordpress/data';

import { createHooksForStore } from '../utils';
import { STORE_NAME } from './constants';

const useHooks = () => {
const { useTransient, usePersistent } = createHooksForStore( STORE_NAME );
const { persist } = useDispatch( STORE_NAME );

// Read-only flags and derived state.
// Nothing here yet.

// Transient accessors.
const [ isReady ] = useTransient( 'isReady' );

// Persistent accessors.
const [ cart, setCart ] = usePersistent( 'cart' );
const [ checkout, setCheckout ] = usePersistent( 'checkout' );
const [ product, setProduct ] = usePersistent( 'product' );
const [ shop, setShop ] = usePersistent( 'shop' );
const [ home, setHome ] = usePersistent( 'home' );
const [ custom_placement, setCustom_placement ] =
usePersistent( 'custom_placement' );

return {
persist,
isReady,
cart,
setCart,
checkout,
setCheckout,
product,
setProduct,
shop,
setShop,
home,
setHome,
custom_placement,
setCustom_placement,
};
};

export const useStore = () => {
const { persist, isReady } = useHooks();
return { persist, isReady };
};

export const usePayLaterMessaging = () => {
const {
cart,
setCart,
checkout,
setCheckout,
product,
setProduct,
shop,
setShop,
home,
setHome,
custom_placement,
setCustom_placement,
} = useHooks();

return {
config: {
cart,
checkout,
product,
shop,
home,
custom_placement,
},
setCart,
setCheckout,
setProduct,
setShop,
setHome,
setCustom_placement,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { createReduxStore, register } from '@wordpress/data';
import { controls as wpControls } from '@wordpress/data-controls';

import { STORE_NAME } from './constants';
import reducer from './reducer';
import * as selectors from './selectors';
import * as actions from './actions';
import * as hooks from './hooks';
import { resolvers } from './resolvers';
import { controls } from './controls';

/**
* Initializes and registers the settings store with WordPress data layer.
* Combines custom controls with WordPress data controls.
*
* @return {boolean} True if initialization succeeded, false otherwise.
*/
export const initStore = () => {
const store = createReduxStore( STORE_NAME, {
reducer,
controls: { ...wpControls, ...controls },
actions,
selectors,
resolvers,
} );

register( store );

return Boolean( wp.data.select( STORE_NAME ) );
};

export { hooks, selectors, STORE_NAME };
Loading
Loading