woocommerce/plugins/woocommerce-blocks/assets/js/editor-components/incompatible-extension-notice/use-combined-incompatibilit...

128 lines
3.2 KiB
TypeScript

/**
* External dependencies
*/
import { useState, useEffect } from '@wordpress/element';
import { useLocalStorageState } from '@woocommerce/base-hooks';
/**
* Internal dependencies
*/
import { useIncompatiblePaymentGatewaysNotice } from './use-incompatible-payment-gateways-notice';
import { useIncompatibleExtensionNotice } from './use-incompatible-extensions-notice';
type StoredIncompatibleExtension = { [ k: string ]: string[] };
const initialDismissedNotices: React.SetStateAction<
StoredIncompatibleExtension[]
> = [];
const areEqual = ( array1: string[], array2: string[] ) => {
if ( array1.length !== array2.length ) {
return false;
}
const uniqueCollectionValues = new Set( [ ...array1, ...array2 ] );
return uniqueCollectionValues.size === array1.length;
};
const sortAlphabetically = ( obj: {
[ key: string ]: string;
} ): { [ key: string ]: string } =>
Object.fromEntries(
Object.entries( obj ).sort( ( [ , a ], [ , b ] ) =>
a.localeCompare( b )
)
);
export const useCombinedIncompatibilityNotice = (
blockName: string
): [ boolean, () => void, { [ k: string ]: string }, number ] => {
const [
incompatibleExtensions,
incompatibleExtensionSlugs,
incompatibleExtensionCount,
] = useIncompatibleExtensionNotice();
const [
incompatiblePaymentMethods,
incompatiblePaymentMethodSlugs,
incompatiblePaymentMethodCount,
] = useIncompatiblePaymentGatewaysNotice();
const allIncompatibleItems = {
...incompatibleExtensions,
...incompatiblePaymentMethods,
};
const allIncompatibleItemSlugs = [
...incompatibleExtensionSlugs,
...incompatiblePaymentMethodSlugs,
];
const allIncompatibleItemCount =
incompatibleExtensionCount + incompatiblePaymentMethodCount;
const [ dismissedNotices, setDismissedNotices ] = useLocalStorageState<
StoredIncompatibleExtension[]
>(
`wc-blocks_dismissed_incompatible_extensions_notices`,
initialDismissedNotices
);
const [ isVisible, setIsVisible ] = useState( false );
const isDismissedNoticeUpToDate = dismissedNotices.some(
( notice ) =>
Object.keys( notice ).includes( blockName ) &&
areEqual(
notice[ blockName as keyof object ],
allIncompatibleItemSlugs
)
);
const shouldBeDismissed =
allIncompatibleItemCount === 0 || isDismissedNoticeUpToDate;
const dismissNotice = () => {
const dismissedNoticesSet = new Set( dismissedNotices );
dismissedNoticesSet.add( {
[ blockName ]: allIncompatibleItemSlugs,
} );
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: StoredIncompatibleExtension[], curr ) => {
if ( Object.keys( curr ).includes( blockName ) ) {
return acc;
}
acc.push( curr );
return acc;
},
[]
)
);
}
}, [
shouldBeDismissed,
isDismissedNoticeUpToDate,
setDismissedNotices,
blockName,
] );
return [
isVisible,
dismissNotice,
sortAlphabetically( allIncompatibleItems ),
allIncompatibleItemCount,
];
};