Improve the dismissal behavior of the incompatible gateways notice (https://github.com/woocommerce/woocommerce-blocks/pull/8299)
* Fix notice persistence after dismissal - This fix applied to the `incompatible payment gateway notice`. - We used the same dismissal logic in the `sidebar compatibility notice` * Get incompatible payments when initialized We initially get the list of `globalPaymentMethods` shared from the back-end as incompatible payments, because the front-end `availablePaymentMethods` is empty before the `paymentMethodsInitialized` state * Introduce advanced notice dismissal handling We want to display a dismissed incompatible gateways notice, when the list of incompatible gateways is updated (e.g., a new incompatible gateway is enabled) * Use the full block name for the `Cart` & `Checkout` * Update variable name for comprehension * Fix TS errors * Remove unused imports
This commit is contained in:
parent
c915123afc
commit
39b6c1c320
|
@ -2,11 +2,12 @@
|
|||
* External dependencies
|
||||
*/
|
||||
import { useEffect, useState } from '@wordpress/element';
|
||||
import { Dispatch, SetStateAction } from 'react';
|
||||
|
||||
export const useLocalStorageState = < T >(
|
||||
key: string,
|
||||
initialValue: T
|
||||
): [ T, ( arg0: T ) => void ] => {
|
||||
): [ T, Dispatch< SetStateAction< T > > ] => {
|
||||
const [ state, setState ] = useState< T >( () => {
|
||||
const valueInLocalStorage = window.localStorage.getItem( key );
|
||||
if ( valueInLocalStorage ) {
|
||||
|
|
|
@ -98,6 +98,11 @@ const withSidebarNotices = createHigherOrderComponent(
|
|||
toggleDismissedStatus={
|
||||
toggleIncompatiblePaymentGatewaysNoticeDismissedStatus
|
||||
}
|
||||
block={
|
||||
isCheckout
|
||||
? 'woocommerce/checkout'
|
||||
: 'woocommerce/cart'
|
||||
}
|
||||
/>
|
||||
|
||||
{ isIncompatiblePaymentGatewaysNoticeDismissed ? (
|
||||
|
|
|
@ -111,13 +111,24 @@ export const getPaymentMethodData = ( state: PaymentState ) => {
|
|||
};
|
||||
|
||||
export const getIncompatiblePaymentMethods = ( state: PaymentState ) => {
|
||||
const {
|
||||
availablePaymentMethods,
|
||||
availableExpressPaymentMethods,
|
||||
paymentMethodsInitialized,
|
||||
expressPaymentMethodsInitialized,
|
||||
} = state;
|
||||
|
||||
if ( ! paymentMethodsInitialized || ! expressPaymentMethodsInitialized ) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return Object.fromEntries(
|
||||
Object.entries( globalPaymentMethods ).filter( ( [ k ] ) => {
|
||||
return ! (
|
||||
k in
|
||||
{
|
||||
...state.availablePaymentMethods,
|
||||
...state.availableExpressPaymentMethods,
|
||||
...availablePaymentMethods,
|
||||
...availableExpressPaymentMethods,
|
||||
}
|
||||
);
|
||||
} )
|
||||
|
|
|
@ -3,48 +3,36 @@
|
|||
*/
|
||||
import { _n } from '@wordpress/i18n';
|
||||
import { Notice, ExternalLink } from '@wordpress/components';
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import {
|
||||
useState,
|
||||
createInterpolateElement,
|
||||
useEffect,
|
||||
} from '@wordpress/element';
|
||||
import { createInterpolateElement, useEffect } from '@wordpress/element';
|
||||
import { Alert } from '@woocommerce/icons';
|
||||
import { Icon } from '@wordpress/icons';
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { STORE_KEY as PAYMENT_STORE_KEY } from '../../data/payment/constants';
|
||||
import { useIncompatiblePaymentGatewaysNotice } from './use-incompatible-payment-gateways-notice';
|
||||
import './editor.scss';
|
||||
|
||||
interface PaymentGatewaysNoticeProps {
|
||||
toggleDismissedStatus: ( status: boolean ) => void;
|
||||
block: 'woocommerce/cart' | 'woocommerce/checkout';
|
||||
}
|
||||
|
||||
export function IncompatiblePaymentGatewaysNotice( {
|
||||
toggleDismissedStatus,
|
||||
block,
|
||||
}: PaymentGatewaysNoticeProps ) {
|
||||
// Everything below works the same for Cart/Checkout
|
||||
const { incompatiblePaymentMethods } = useSelect( ( select ) => {
|
||||
const { getIncompatiblePaymentMethods } = select( PAYMENT_STORE_KEY );
|
||||
return {
|
||||
incompatiblePaymentMethods: getIncompatiblePaymentMethods(),
|
||||
};
|
||||
}, [] );
|
||||
const [ settingStatus, setStatus ] = useState( 'pristine' );
|
||||
|
||||
const numberOfIncompatiblePaymentMethods = Object.keys(
|
||||
incompatiblePaymentMethods
|
||||
).length;
|
||||
const isNoticeDismissed =
|
||||
numberOfIncompatiblePaymentMethods === 0 ||
|
||||
settingStatus === 'dismissed';
|
||||
const [
|
||||
isVisible,
|
||||
dismissNotice,
|
||||
incompatiblePaymentMethods,
|
||||
numberOfIncompatiblePaymentMethods,
|
||||
] = useIncompatiblePaymentGatewaysNotice( block );
|
||||
|
||||
useEffect( () => {
|
||||
toggleDismissedStatus( isNoticeDismissed );
|
||||
}, [ isNoticeDismissed, toggleDismissedStatus ] );
|
||||
toggleDismissedStatus( ! isVisible );
|
||||
}, [ isVisible, toggleDismissedStatus ] );
|
||||
|
||||
if ( isNoticeDismissed ) {
|
||||
if ( ! isVisible ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -68,7 +56,7 @@ export function IncompatiblePaymentGatewaysNotice( {
|
|||
<Notice
|
||||
className="wc-blocks-incompatible-extensions-notice"
|
||||
status={ 'warning' }
|
||||
onRemove={ () => setStatus( 'dismissed' ) }
|
||||
onRemove={ dismissNotice }
|
||||
spokenMessage={ noticeContent }
|
||||
>
|
||||
<div className="wc-blocks-incompatible-extensions-notice__content">
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
/**
|
||||
* External dependencies
|
||||
*/
|
||||
import { useSelect } from '@wordpress/data';
|
||||
import { useEffect, useState } from '@wordpress/element';
|
||||
import { useLocalStorageState } from '@woocommerce/base-hooks';
|
||||
|
||||
/**
|
||||
* Internal dependencies
|
||||
*/
|
||||
import { STORE_KEY as PAYMENT_STORE_KEY } from '../../data/payment/constants';
|
||||
|
||||
type StoredIncompatibleGateway = { [ k: string ]: string[] };
|
||||
const initialDismissedNotices: React.SetStateAction<
|
||||
StoredIncompatibleGateway[]
|
||||
> = [];
|
||||
|
||||
const areEqual = ( array1: string[], array2: string[] ) => {
|
||||
if ( array1.length !== array2.length ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const uniqueCollectionValues = new Set( [ ...array1, ...array2 ] );
|
||||
|
||||
return uniqueCollectionValues.size === array1.length;
|
||||
};
|
||||
|
||||
export const useIncompatiblePaymentGatewaysNotice = (
|
||||
blockName: string
|
||||
): [ boolean, () => void, { [ k: string ]: string }, number ] => {
|
||||
const [ dismissedNotices, setDismissedNotices ] = useLocalStorageState<
|
||||
StoredIncompatibleGateway[]
|
||||
>(
|
||||
`wc-blocks_dismissed_incompatible_payment_gateways_notices`,
|
||||
initialDismissedNotices
|
||||
);
|
||||
const [ isVisible, setIsVisible ] = useState( false );
|
||||
|
||||
const { incompatiblePaymentMethods } = useSelect( ( select ) => {
|
||||
const { getIncompatiblePaymentMethods } = select( PAYMENT_STORE_KEY );
|
||||
return {
|
||||
incompatiblePaymentMethods: getIncompatiblePaymentMethods(),
|
||||
};
|
||||
}, [] );
|
||||
const incompatiblePaymentMethodsIDs = Object.keys(
|
||||
incompatiblePaymentMethods
|
||||
);
|
||||
const numberOfIncompatiblePaymentMethods =
|
||||
incompatiblePaymentMethodsIDs.length;
|
||||
|
||||
const isDismissedNoticeUpToDate = dismissedNotices.some(
|
||||
( notice ) =>
|
||||
Object.keys( notice ).includes( blockName ) &&
|
||||
areEqual(
|
||||
notice[ blockName as keyof object ],
|
||||
incompatiblePaymentMethodsIDs
|
||||
)
|
||||
);
|
||||
|
||||
const shouldBeDismissed =
|
||||
numberOfIncompatiblePaymentMethods === 0 || isDismissedNoticeUpToDate;
|
||||
const dismissNotice = () => {
|
||||
const dismissedNoticesSet = new Set( dismissedNotices );
|
||||
dismissedNoticesSet.add( {
|
||||
[ blockName ]: incompatiblePaymentMethodsIDs,
|
||||
} );
|
||||
setDismissedNotices( [ ...dismissedNoticesSet ] );
|
||||
};
|
||||
|
||||
// This ensures the modal is not loaded on first render. This is required so
|
||||
// Gutenberg doesn't steal the focus from the Guide and focuses the block.
|
||||
useEffect( () => {
|
||||
setIsVisible( ! shouldBeDismissed );
|
||||
|
||||
if ( ! shouldBeDismissed && ! isDismissedNoticeUpToDate ) {
|
||||
setDismissedNotices( ( previousDismissedNotices ) =>
|
||||
previousDismissedNotices.reduce(
|
||||
( acc: StoredIncompatibleGateway[], curr ) => {
|
||||
if ( Object.keys( curr ).includes( blockName ) ) {
|
||||
return acc;
|
||||
}
|
||||
acc.push( curr );
|
||||
|
||||
return acc;
|
||||
},
|
||||
[]
|
||||
)
|
||||
);
|
||||
}
|
||||
}, [
|
||||
shouldBeDismissed,
|
||||
isDismissedNoticeUpToDate,
|
||||
setDismissedNotices,
|
||||
blockName,
|
||||
] );
|
||||
|
||||
return [
|
||||
isVisible,
|
||||
dismissNotice,
|
||||
incompatiblePaymentMethods,
|
||||
numberOfIncompatiblePaymentMethods,
|
||||
];
|
||||
};
|
Loading…
Reference in New Issue