128 lines
3.2 KiB
TypeScript
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,
|
|
];
|
|
};
|