Merge pull request #3202 from woocommerce/PCP-4300-fix-broken-toggle-sync-for-payment-and-setting-dependencies

Fix broken payment method toggle sync (4300)
This commit is contained in:
Emili Castells 2025-03-11 17:50:44 +01:00 committed by GitHub
commit bab13f2524
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 166 additions and 4 deletions

View file

@ -8,6 +8,7 @@ import * as hooks from './hooks';
import * as resolvers from './resolvers';
import { initTodoSync } from '../sync/todo-state-sync';
import { initPaymentDependencySync } from '../sync/payment-methods-sync';
import { initSettingBasedPaymentMethodsSync } from '../sync/setting-based-payment-methods-sync';
/**
* Initializes and registers the settings store with WordPress data layer.
@ -30,6 +31,7 @@ export const initStore = () => {
// Initialize payment method dependency sync.
initPaymentDependencySync();
initSettingBasedPaymentMethodsSync();
return Boolean( wp.data.select( STORE_NAME ) );
};

View file

@ -61,8 +61,10 @@ export const initPaymentDependencySync = () => {
( [ key, method ] ) =>
key !== '__meta' &&
method &&
method.depends_on &&
method.depends_on.includes( changedId )
method.depends_on_payment_methods &&
method.depends_on_payment_methods.includes(
changedId
)
)
.map( ( [ key ] ) => key );
@ -115,11 +117,11 @@ const handleRestoreDependents = ( dependentIds, methods ) => {
const checkAllDependenciesSatisfied = ( methodId, methods ) => {
const method = methods[ methodId ];
if ( ! method || ! method.depends_on ) {
if ( ! method || ! method.depends_on_payment_methods ) {
return true;
}
return ! method.depends_on.some( ( parentId ) => {
return ! method.depends_on_payment_methods.some( ( parentId ) => {
const parent = methods[ parentId ];
return ! parent || parent.enabled === false;
} );

View file

@ -0,0 +1,158 @@
import { subscribe, select } from '@wordpress/data';
// Store names
const PAYMENT_STORE = 'wc/paypal/payment';
const SETTINGS_STORE = 'wc/paypal/settings';
// Track original states of methods affected by settings
const settingDependentStates = {};
/**
* Initialize setting dependency synchronization
*/
export const initSettingBasedPaymentMethodsSync = () => {
let previousSettingsState = null;
let isProcessing = false;
const unsubscribe = subscribe( () => {
if ( isProcessing ) {
return;
}
isProcessing = true;
try {
// Get both settings and payment stores
const settingsHooks = select( SETTINGS_STORE );
const paymentHooks = select( PAYMENT_STORE );
if ( ! settingsHooks || ! paymentHooks ) {
isProcessing = false;
return;
}
const settings = settingsHooks.persistentData();
const methods = paymentHooks.persistentData();
if ( ! settings || ! methods ) {
isProcessing = false;
return;
}
if ( ! previousSettingsState ) {
previousSettingsState = { ...settings };
isProcessing = false;
return;
}
// Find which settings changed
const changedSettings = Object.keys( settings ).filter(
( key ) =>
previousSettingsState[ key ] !== undefined &&
settings[ key ] !== previousSettingsState[ key ]
);
if ( changedSettings.length > 0 ) {
// Process affected payment methods for each changed setting
for ( const methodId in methods ) {
if ( methodId === '__meta' || ! methods[ methodId ] ) {
continue;
}
const method = methods[ methodId ];
// Skip methods without setting dependencies
if ( ! method.depends_on_settings?.settings ) {
continue;
}
const { settings: dependencySettings } =
method.depends_on_settings;
// Check if any of the changed settings affects this method
const relevantSettings = Object.values(
dependencySettings
).filter( ( setting ) =>
changedSettings.includes( setting.id )
);
if ( relevantSettings.length > 0 ) {
// Determine if method should be disabled based on new setting values
const shouldBeDisabled = relevantSettings.some(
( setting ) =>
settings[ setting.id ] !== setting.value
);
if ( shouldBeDisabled ) {
// Store original state before disabling
if ( ! ( methodId in settingDependentStates ) ) {
settingDependentStates[ methodId ] =
method.enabled;
}
// Disable the method
methods[ methodId ].enabled = false;
methods[ methodId ].isDisabled = true;
} else {
// Check if all setting dependencies are now satisfied
const allSettingsSatisfied = Object.values(
dependencySettings
).every(
( setting ) =>
settings[ setting.id ] === setting.value
);
// Also check payment method dependencies
const paymentDependenciesSatisfied =
checkPaymentDependenciesSatisfied(
methodId,
methods
);
// If all dependencies are satisfied, restore the original state
if (
allSettingsSatisfied &&
paymentDependenciesSatisfied &&
methodId in settingDependentStates
) {
methods[ methodId ].enabled =
settingDependentStates[ methodId ];
methods[ methodId ].isDisabled = false;
delete settingDependentStates[ methodId ];
}
}
}
}
}
previousSettingsState = { ...settings };
} catch ( error ) {
// Silent error handling
} finally {
isProcessing = false;
}
} );
return unsubscribe;
};
/**
* Check if all payment method dependencies are satisfied for a method
*
* @param {string} methodId - ID of the method to check
* @param {Object} methods - All payment methods
* @return {boolean} True if all dependencies are satisfied
*/
const checkPaymentDependenciesSatisfied = ( methodId, methods ) => {
const method = methods[ methodId ];
if ( ! method || ! method.depends_on_payment_methods ) {
return true;
}
return ! method.depends_on_payment_methods.some( ( parentId ) => {
const parent = methods[ parentId ];
return ! parent || parent.enabled === false;
} );
};
export default initSettingBasedPaymentMethodsSync;