diff --git a/modules/ppcp-axo-block/resources/js/components/Card/Card.js b/modules/ppcp-axo-block/resources/js/components/Card/Card.js index c1046384e..51dfa15ae 100644 --- a/modules/ppcp-axo-block/resources/js/components/Card/Card.js +++ b/modules/ppcp-axo-block/resources/js/components/Card/Card.js @@ -1,5 +1,7 @@ import { useMemo } from '@wordpress/element'; +import { useSelect } from '@wordpress/data'; import { Watermark } from '../Watermark'; +import { STORE_NAME } from '../../stores/axoStore'; const cardIcons = { VISA: 'visa-light.svg', @@ -11,7 +13,14 @@ const cardIcons = { UNIONPAY: 'unionpay-light.svg', }; -const Card = ( { card, fastlaneSdk, showWatermark = true } ) => { +const Card = ( { fastlaneSdk, showWatermark = true } ) => { + const { card } = useSelect( + ( select ) => ( { + card: select( STORE_NAME ).getCardDetails(), + } ), + [] + ); + const { brand, lastDigits, expiry, name } = card?.paymentSource?.card ?? {}; const cardLogo = useMemo( () => { diff --git a/modules/ppcp-axo-block/resources/js/components/Payment/Payment.js b/modules/ppcp-axo-block/resources/js/components/Payment/Payment.js index b5a29cb44..7ff3f1b15 100644 --- a/modules/ppcp-axo-block/resources/js/components/Payment/Payment.js +++ b/modules/ppcp-axo-block/resources/js/components/Payment/Payment.js @@ -3,13 +3,14 @@ import { useSelect } from '@wordpress/data'; import { Card } from '../Card'; import { STORE_NAME } from '../../stores/axoStore'; -export const Payment = ( { fastlaneSdk, card, onPaymentLoad } ) => { - const isGuest = useSelect( ( select ) => - select( STORE_NAME ).getIsGuest() - ); - - const isEmailLookupCompleted = useSelect( ( select ) => - select( STORE_NAME ).getIsEmailLookupCompleted() +export const Payment = ( { fastlaneSdk, onPaymentLoad } ) => { + const { isGuest, isEmailLookupCompleted } = useSelect( + ( select ) => ( { + isGuest: select( STORE_NAME ).getIsGuest(), + isEmailLookupCompleted: + select( STORE_NAME ).getIsEmailLookupCompleted(), + } ), + [] ); const loadPaymentComponent = useCallback( async () => { @@ -36,11 +37,5 @@ export const Payment = ( { fastlaneSdk, card, onPaymentLoad } ) => { ); } - return ( - - ); + return ; }; diff --git a/modules/ppcp-axo-block/resources/js/hooks/useAxoSetup.js b/modules/ppcp-axo-block/resources/js/hooks/useAxoSetup.js index 319e390cf..90df3f3fa 100644 --- a/modules/ppcp-axo-block/resources/js/hooks/useAxoSetup.js +++ b/modules/ppcp-axo-block/resources/js/hooks/useAxoSetup.js @@ -5,18 +5,14 @@ import usePayPalScript from './usePayPalScript'; import { setupWatermark } from '../components/Watermark'; import { setupEmailFunctionality } from '../components/EmailButton'; import { createEmailLookupHandler } from '../events/emailLookupManager'; -import { usePhoneSyncHandler } from './usePhoneSyncHandler'; +import usePhoneSyncHandler from './usePhoneSyncHandler'; import { initializeClassToggles } from '../helpers/classnamesManager'; import { snapshotFields } from '../helpers/fieldHelpers'; import useCustomerData from './useCustomerData'; import useShippingAddressChange from './useShippingAddressChange'; +import useCardChange from './useCardChange'; -const useAxoSetup = ( - ppcpConfig, - fastlaneSdk, - paymentComponent, - onChangeCardButtonClick -) => { +const useAxoSetup = ( ppcpConfig, fastlaneSdk, paymentComponent ) => { const { setIsAxoActive, setIsAxoScriptLoaded, @@ -24,7 +20,7 @@ const useAxoSetup = ( setCardDetails, } = useDispatch( STORE_NAME ); const paypalLoaded = usePayPalScript( ppcpConfig ); - + const onChangeCardButtonClick = useCardChange( fastlaneSdk ); const onChangeShippingAddressClick = useShippingAddressChange( fastlaneSdk, setShippingAddress diff --git a/modules/ppcp-axo-block/resources/js/hooks/usePhoneSyncHandler.js b/modules/ppcp-axo-block/resources/js/hooks/usePhoneSyncHandler.js index 676a76abf..627425e74 100644 --- a/modules/ppcp-axo-block/resources/js/hooks/usePhoneSyncHandler.js +++ b/modules/ppcp-axo-block/resources/js/hooks/usePhoneSyncHandler.js @@ -1,8 +1,9 @@ -import { useEffect, useRef } from 'react'; -import { useSelect } from '@wordpress/data'; +import { useEffect, useRef, useCallback } from '@wordpress/element'; +import { useSelect, useDispatch } from '@wordpress/data'; import { debounce } from '../../../../ppcp-blocks/resources/js/Helper/debounce'; +import { STORE_NAME } from '../stores/axoStore'; +import useCustomerData from './useCustomerData'; -const WOO_STORE_NAME = 'wc/store/cart'; const PHONE_DEBOUNCE_DELAY = 250; /** @@ -15,23 +16,9 @@ const PHONE_DEBOUNCE_DELAY = 250; const sanitizePhoneNumber = ( phoneNumber = '' ) => { const localNumber = phoneNumber.replace( /^\+?[01]+/, '' ); const cleanNumber = localNumber.replace( /[^0-9]/g, '' ); - return cleanNumber.length === 10 ? cleanNumber : ''; }; -/** - * Retrieves and sanitizes the phone number from WooCommerce customer data. - * - * @param {Function} select - The select function from @wordpress/data. - * @return {string} The sanitized phone number. - */ -const getSanitizedPhoneNumber = ( select ) => { - const data = select( WOO_STORE_NAME ).getCustomerData() || {}; - const billingPhone = sanitizePhoneNumber( data.billingAddress?.phone ); - const shippingPhone = sanitizePhoneNumber( data.shippingAddress?.phone ); - return billingPhone || shippingPhone || ''; -}; - /** * Updates the prefilled phone number in the Fastlane CardField component. * @@ -48,17 +35,37 @@ const updatePrefills = ( paymentComponent, phoneNumber ) => { * * @param {Object} paymentComponent - The CardField component from Fastlane. */ -export const usePhoneSyncHandler = ( paymentComponent ) => { - // Fetch and sanitize phone number from WooCommerce. - const phoneNumber = useSelect( ( select ) => - getSanitizedPhoneNumber( select ) - ); +const usePhoneSyncHandler = ( paymentComponent ) => { + const { setPhoneNumber } = useDispatch( STORE_NAME ); + + const { phoneNumber } = useSelect( ( select ) => ( { + phoneNumber: select( STORE_NAME ).getPhoneNumber(), + } ) ); + + const { shippingAddress, billingAddress } = useCustomerData(); - // Create a debounced function that updates the prefilled phone-number. const debouncedUpdatePhone = useRef( debounce( updatePrefills, PHONE_DEBOUNCE_DELAY ) ).current; + // Fetch and update the phone number from the billing or shipping address. + const fetchAndUpdatePhoneNumber = useCallback( () => { + const billingPhone = billingAddress?.phone || ''; + const shippingPhone = shippingAddress?.phone || ''; + const sanitizedPhoneNumber = sanitizePhoneNumber( + billingPhone || shippingPhone + ); + + if ( sanitizedPhoneNumber && sanitizedPhoneNumber !== phoneNumber ) { + setPhoneNumber( sanitizedPhoneNumber ); + } + }, [ billingAddress, shippingAddress, phoneNumber, setPhoneNumber ] ); + + // Fetch and update the phone number from the billing or shipping address. + useEffect( () => { + fetchAndUpdatePhoneNumber(); + }, [ fetchAndUpdatePhoneNumber ] ); + // Invoke debounced function when paymentComponent or phoneNumber changes. useEffect( () => { if ( paymentComponent && phoneNumber ) { @@ -75,3 +82,5 @@ export const usePhoneSyncHandler = ( paymentComponent ) => { }; }, [ debouncedUpdatePhone ] ); }; + +export default usePhoneSyncHandler; diff --git a/modules/ppcp-axo-block/resources/js/index.js b/modules/ppcp-axo-block/resources/js/index.js index 8a718f085..b72875534 100644 --- a/modules/ppcp-axo-block/resources/js/index.js +++ b/modules/ppcp-axo-block/resources/js/index.js @@ -1,21 +1,16 @@ import { useState } from '@wordpress/element'; -import { useSelect } from '@wordpress/data'; import { registerPaymentMethod } from '@woocommerce/blocks-registry'; // Hooks import useFastlaneSdk from './hooks/useFastlaneSdk'; import useTokenizeCustomerData from './hooks/useTokenizeCustomerData'; -import useCardChange from './hooks/useCardChange'; import useAxoSetup from './hooks/useAxoSetup'; import useAxoCleanup from './hooks/useAxoCleanup'; import useHandlePaymentSetup from './hooks/useHandlePaymentSetup'; +import usePaymentSetupEffect from './hooks/usePaymentSetupEffect'; // Components import { Payment } from './components/Payment/Payment'; -import usePaymentSetupEffect from './hooks/usePaymentSetupEffect'; - -// Store -import { STORE_NAME } from './stores/axoStore'; const gatewayHandle = 'ppcp-axo-gateway'; const ppcpConfig = wc.wcSettings.getSetting( `${ gatewayHandle }_data` ); @@ -31,28 +26,15 @@ const Axo = ( props ) => { const { onPaymentSetup } = eventRegistration; const [ paymentComponent, setPaymentComponent ] = useState( null ); - const { cardDetails } = useSelect( - ( select ) => ( { - cardDetails: select( STORE_NAME ).getCardDetails(), - } ), - [] - ); - const fastlaneSdk = useFastlaneSdk( axoConfig, ppcpConfig ); const tokenizedCustomerData = useTokenizeCustomerData(); - const onChangeCardButtonClick = useCardChange( fastlaneSdk ); const handlePaymentSetup = useHandlePaymentSetup( emitResponse, paymentComponent, tokenizedCustomerData ); - useAxoSetup( - ppcpConfig, - fastlaneSdk, - paymentComponent, - onChangeCardButtonClick - ); + useAxoSetup( ppcpConfig, fastlaneSdk, paymentComponent ); const { handlePaymentLoad } = usePaymentSetupEffect( onPaymentSetup, @@ -62,17 +44,10 @@ const Axo = ( props ) => { useAxoCleanup(); - console.log( 'Rendering Axo component', { - fastlaneSdk, - } ); - return fastlaneSdk ? ( ) : (
Loading Fastlane...
diff --git a/modules/ppcp-axo-block/resources/js/stores/axoStore.js b/modules/ppcp-axo-block/resources/js/stores/axoStore.js index 908946fb8..04323c6e1 100644 --- a/modules/ppcp-axo-block/resources/js/stores/axoStore.js +++ b/modules/ppcp-axo-block/resources/js/stores/axoStore.js @@ -2,7 +2,7 @@ import { createReduxStore, register, dispatch } from '@wordpress/data'; export const STORE_NAME = 'woocommerce-paypal-payments/axo-block'; -// Initial state. +// Initial state const DEFAULT_STATE = { isGuest: true, isAxoActive: false, @@ -11,9 +11,10 @@ const DEFAULT_STATE = { isEmailLookupCompleted: false, shippingAddress: null, cardDetails: null, + phoneNumber: '', }; -// Actions. +// Actions const actions = { setIsGuest: ( isGuest ) => ( { type: 'SET_IS_GUEST', @@ -43,9 +44,13 @@ const actions = { type: 'SET_CARD_DETAILS', payload: cardDetails, } ), + setPhoneNumber: ( phoneNumber ) => ( { + type: 'SET_PHONE_NUMBER', + payload: phoneNumber, + } ), }; -// Reducer. +// Reducer const reducer = ( state = DEFAULT_STATE, action ) => { switch ( action.type ) { case 'SET_IS_GUEST': @@ -62,12 +67,14 @@ const reducer = ( state = DEFAULT_STATE, action ) => { return { ...state, shippingAddress: action.payload }; case 'SET_CARD_DETAILS': return { ...state, cardDetails: action.payload }; + case 'SET_PHONE_NUMBER': + return { ...state, phoneNumber: action.payload }; default: return state; } }; -// Selectors. +// Selectors const selectors = { getIsGuest: ( state ) => state.isGuest, getIsAxoActive: ( state ) => state.isAxoActive, @@ -76,9 +83,10 @@ const selectors = { getIsEmailLookupCompleted: ( state ) => state.isEmailLookupCompleted, getShippingAddress: ( state ) => state.shippingAddress, getCardDetails: ( state ) => state.cardDetails, + getPhoneNumber: ( state ) => state.phoneNumber, }; -// Create and register the store. +// Create and register the store const store = createReduxStore( STORE_NAME, { reducer, actions, @@ -87,7 +95,7 @@ const store = createReduxStore( STORE_NAME, { register( store ); -// Action dispatchers. +// Action dispatchers export const setIsGuest = ( isGuest ) => { dispatch( STORE_NAME ).setIsGuest( isGuest ); }; @@ -103,3 +111,7 @@ export const setShippingAddress = ( shippingAddress ) => { export const setCardDetails = ( cardDetails ) => { dispatch( STORE_NAME ).setCardDetails( cardDetails ); }; + +export const setPhoneNumber = ( phoneNumber ) => { + dispatch( STORE_NAME ).setPhoneNumber( phoneNumber ); +};