diff --git a/assets/js/blocks/migration-products-to-product-collection/constants.ts b/assets/js/blocks/migration-products-to-product-collection/constants.ts new file mode 100644 index 00000000000..2f68b429ecf --- /dev/null +++ b/assets/js/blocks/migration-products-to-product-collection/constants.ts @@ -0,0 +1,19 @@ +/** + * Internal dependencies + */ +import type { UpgradeNoticeStatus, UpgradeNoticeStatuses } from './types'; + +export const AUTO_REPLACE_PRODUCTS_WITH_PRODUCT_COLLECTION = false; +export const MANUAL_REPLACE_PRODUCTS_WITH_PRODUCT_COLLECTION = false; +export const HOURS_TO_DISPLAY_UPGRADE_NOTICE = 72; +export const UPGRADE_NOTICE_DISPLAY_COUNT_THRESHOLD = 4; +export const MIGRATION_STATUS_LS_KEY = + 'wc-blocks_upgraded-products-to-product-collection'; +// Initial status used in the localStorage +export const INITIAL_STATUS_LS_VALUE: UpgradeNoticeStatuses = 'notseen'; + +export const getInitialStatusLSValue: () => UpgradeNoticeStatus = () => ( { + status: INITIAL_STATUS_LS_VALUE, + time: Date.now(), + displayCount: 0, +} ); diff --git a/assets/js/blocks/shared/scripts/index.tsx b/assets/js/blocks/migration-products-to-product-collection/index.ts similarity index 59% rename from assets/js/blocks/shared/scripts/index.tsx rename to assets/js/blocks/migration-products-to-product-collection/index.ts index b255b01a57d..9067c26005e 100644 --- a/assets/js/blocks/shared/scripts/index.tsx +++ b/assets/js/blocks/migration-products-to-product-collection/index.ts @@ -1,2 +1,5 @@ export * from './migration-from-products-to-product-collection'; export * from './migration-from-product-collection-to-products'; +export * from './migration-utils'; +export * from './constants'; +export * from './types'; diff --git a/assets/js/blocks/shared/scripts/migration-from-product-collection-to-products.tsx b/assets/js/blocks/migration-products-to-product-collection/migration-from-product-collection-to-products.ts similarity index 91% rename from assets/js/blocks/shared/scripts/migration-from-product-collection-to-products.tsx rename to assets/js/blocks/migration-products-to-product-collection/migration-from-product-collection-to-products.ts index 57bca80ad16..18444ae11ee 100644 --- a/assets/js/blocks/shared/scripts/migration-from-product-collection-to-products.tsx +++ b/assets/js/blocks/migration-products-to-product-collection/migration-from-product-collection-to-products.ts @@ -7,17 +7,21 @@ import { select, dispatch } from '@wordpress/data'; /** * Internal dependencies */ +import { disableAutoUpdate } from './migration-from-products-to-product-collection'; import { getProductCollectionBlockClientIds, checkIfBlockCanBeInserted, postTemplateHasSupportForGridView, - type TransformBlock, - type IsBlockType, - type ProductGridLayout, - type ProductGridLayoutTypes, - type PostTemplateLayout, - type PostTemplateLayoutTypes, + setUpgradeStatus, } from './migration-utils'; +import type { + TransformBlock, + IsBlockType, + ProductGridLayout, + ProductGridLayoutTypes, + PostTemplateLayout, + PostTemplateLayoutTypes, +} from './types'; const VARIATION_NAME = 'woocommerce/product-query'; @@ -45,6 +49,10 @@ const mapAttributes = ( attributes ) => { mappedQuery.__woocommerceOnSale = woocommerceOnSale; } + if ( taxQuery ) { + mappedQuery.taxQuery = taxQuery; + } + return { ...restAttributes, namespace: VARIATION_NAME, @@ -207,3 +215,12 @@ export const replaceProductCollectionWithProducts = () => { productCollectionBlockClientIds.map( replaceProductCollectionBlock ); }; + +export const revertMigration = () => { + disableAutoUpdate(); + setUpgradeStatus( { + status: 'reverted', + time: Date.now(), + } ); + replaceProductCollectionWithProducts(); +}; diff --git a/assets/js/blocks/shared/scripts/migration-from-products-to-product-collection.tsx b/assets/js/blocks/migration-products-to-product-collection/migration-from-products-to-product-collection.ts similarity index 82% rename from assets/js/blocks/shared/scripts/migration-from-products-to-product-collection.tsx rename to assets/js/blocks/migration-products-to-product-collection/migration-from-products-to-product-collection.ts index 2a803057442..0de18ef3e08 100644 --- a/assets/js/blocks/shared/scripts/migration-from-products-to-product-collection.tsx +++ b/assets/js/blocks/migration-products-to-product-collection/migration-from-products-to-product-collection.ts @@ -2,22 +2,31 @@ * External dependencies */ import { createBlock, BlockInstance } from '@wordpress/blocks'; -import { select, dispatch } from '@wordpress/data'; +import { select, dispatch, subscribe } from '@wordpress/data'; +import { isWpVersion } from '@woocommerce/settings'; /** * Internal dependencies */ +import { + AUTO_REPLACE_PRODUCTS_WITH_PRODUCT_COLLECTION, + getInitialStatusLSValue, +} from './constants'; import { getProductsBlockClientIds, checkIfBlockCanBeInserted, postTemplateHasSupportForGridView, - type TransformBlock, - type IsBlockType, - type ProductGridLayout, - type ProductGridLayoutTypes, - type PostTemplateLayout, - type PostTemplateLayoutTypes, + getUpgradeStatus, + setUpgradeStatus, } from './migration-utils'; +import type { + TransformBlock, + IsBlockType, + ProductGridLayout, + ProductGridLayoutTypes, + PostTemplateLayout, + PostTemplateLayoutTypes, +} from './types'; const mapAttributes = ( attributes: Record< string, unknown > ) => { const { query, namespace, ...restAttributes } = attributes; @@ -41,7 +50,7 @@ const mapAttributes = ( attributes: Record< string, unknown > ) => { isProductCollectionBlock: true, ...restQuery, }, - displayUpgradeNotice: true, + convertedFromProducts: true, }; }; @@ -194,9 +203,7 @@ const replaceProductsBlocks = ( productsBlockClientIds: string[] ) => { return !! results.length && results.every( ( result ) => !! result ); }; -export const replaceProductsWithProductCollection = ( - unsubscribe?: () => void -) => { +export const replaceProductsWithProductCollection = () => { const queryBlocksCount = select( 'core/block-editor' ).getGlobalBlockCount( 'core/query' ); if ( queryBlocksCount === 0 ) { @@ -211,10 +218,32 @@ export const replaceProductsWithProductCollection = ( return; } - const replaced = replaceProductsBlocks( productsBlockClientIds ); + replaceProductsBlocks( productsBlockClientIds ); +}; + +export const manualUpdate = () => { + setUpgradeStatus( getInitialStatusLSValue() ); + replaceProductsWithProductCollection(); +}; - if ( unsubscribe && replaced ) { - // @todo: unsubscribe on user reverting migration +let unsubscribe: ( () => void ) | undefined; +export const disableAutoUpdate = () => { + if ( unsubscribe ) { unsubscribe(); } }; +export const enableAutoUpdate = () => { + if ( isWpVersion( '6.1', '>=' ) ) { + const { status } = getUpgradeStatus(); + + if ( + AUTO_REPLACE_PRODUCTS_WITH_PRODUCT_COLLECTION && + status !== 'reverted' && + ! unsubscribe + ) { + unsubscribe = subscribe( () => { + replaceProductsWithProductCollection(); + }, 'core/block-editor' ); + } + } +}; diff --git a/assets/js/blocks/shared/scripts/migration-utils.tsx b/assets/js/blocks/migration-products-to-product-collection/migration-utils.ts similarity index 60% rename from assets/js/blocks/shared/scripts/migration-utils.tsx rename to assets/js/blocks/migration-products-to-product-collection/migration-utils.ts index 9748ab82de0..a88a0af297f 100644 --- a/assets/js/blocks/shared/scripts/migration-utils.tsx +++ b/assets/js/blocks/migration-products-to-product-collection/migration-utils.ts @@ -4,33 +4,25 @@ import { getSettingWithCoercion } from '@woocommerce/settings'; import { type BlockInstance } from '@wordpress/blocks'; import { select } from '@wordpress/data'; -import { isBoolean } from '@woocommerce/types'; +import { isBoolean, isNumber } from '@woocommerce/types'; -type GetBlocksClientIds = ( blocks: BlockInstance[] ) => string[]; -export type IsBlockType = ( block: BlockInstance ) => boolean; -export type TransformBlock = ( - block: BlockInstance, - innerBlock: BlockInstance[] -) => BlockInstance; -export type ProductGridLayoutTypes = 'flex' | 'list'; -export type PostTemplateLayoutTypes = 'grid' | 'default'; - -export type ProductGridLayout = { - type: ProductGridLayoutTypes; - columns: number; -}; - -export type PostTemplateLayout = { - type: PostTemplateLayoutTypes; - columnCount: number; -}; +/** + * Internal dependencies + */ +import { MIGRATION_STATUS_LS_KEY, getInitialStatusLSValue } from './constants'; +import type { + IsBlockType, + GetBlocksClientIds, + UpgradeNoticeStatus, +} from './types'; const isProductsBlock: IsBlockType = ( block ) => block.name === 'core/query' && block.attributes.namespace === 'woocommerce/product-query'; -const isProductCollectionBlock: IsBlockType = ( block ) => - block.name === 'woocommerce/product-collection'; +const isConvertedProductCollectionBlock: IsBlockType = ( block ) => + block.name === 'woocommerce/product-collection' && + block.attributes.convertedFromProducts; const getBlockClientIdsByPredicate = ( blocks: BlockInstance[], @@ -53,7 +45,7 @@ const getProductsBlockClientIds: GetBlocksClientIds = ( blocks ) => getBlockClientIdsByPredicate( blocks, isProductsBlock ); const getProductCollectionBlockClientIds: GetBlocksClientIds = ( blocks ) => - getBlockClientIdsByPredicate( blocks, isProductCollectionBlock ); + getBlockClientIdsByPredicate( blocks, isConvertedProductCollectionBlock ); const checkIfBlockCanBeInserted = ( clientId: string, @@ -78,9 +70,35 @@ const postTemplateHasSupportForGridView = getSettingWithCoercion( isBoolean ); +const getUpgradeStatus = (): UpgradeNoticeStatus => { + const status = window.localStorage.getItem( MIGRATION_STATUS_LS_KEY ); + return status ? JSON.parse( status ) : getInitialStatusLSValue(); +}; + +const setUpgradeStatus = ( newStatus: UpgradeNoticeStatus ) => { + window.localStorage.setItem( + MIGRATION_STATUS_LS_KEY, + JSON.stringify( newStatus ) + ); +}; + +const incrementUpgradeStatusDisplayCount = () => { + const status = getUpgradeStatus(); + const displayCount = isNumber( status.displayCount ) + ? status.displayCount + 1 + : 0; + setUpgradeStatus( { + ...status, + displayCount, + } ); +}; + export { getProductsBlockClientIds, getProductCollectionBlockClientIds, checkIfBlockCanBeInserted, postTemplateHasSupportForGridView, + getUpgradeStatus, + setUpgradeStatus, + incrementUpgradeStatusDisplayCount, }; diff --git a/assets/js/blocks/migration-products-to-product-collection/types.ts b/assets/js/blocks/migration-products-to-product-collection/types.ts new file mode 100644 index 00000000000..1fd795a6636 --- /dev/null +++ b/assets/js/blocks/migration-products-to-product-collection/types.ts @@ -0,0 +1,29 @@ +/** + * External dependencies + */ +import { type BlockInstance } from '@wordpress/blocks'; + +export type GetBlocksClientIds = ( blocks: BlockInstance[] ) => string[]; +export type IsBlockType = ( block: BlockInstance ) => boolean; +export type TransformBlock = ( + block: BlockInstance, + innerBlock: BlockInstance[] +) => BlockInstance; +export type ProductGridLayoutTypes = 'flex' | 'list'; +export type PostTemplateLayoutTypes = 'grid' | 'default'; + +export type ProductGridLayout = { + type: ProductGridLayoutTypes; + columns: number; +}; + +export type PostTemplateLayout = { + type: PostTemplateLayoutTypes; + columnCount: number; +}; +export type UpgradeNoticeStatuses = 'notseen' | 'seen' | 'reverted'; +export type UpgradeNoticeStatus = { + status: UpgradeNoticeStatuses; + time: number; + displayCount?: number; +}; diff --git a/assets/js/blocks/product-collection/block.json b/assets/js/blocks/product-collection/block.json index bf82ceb9b10..6c81a9f55a2 100644 --- a/assets/js/blocks/product-collection/block.json +++ b/assets/js/blocks/product-collection/block.json @@ -21,7 +21,7 @@ "displayLayout": { "type": "object" }, - "displayUpgradeNotice": { + "convertedFromProducts": { "type": "boolean", "default": false } diff --git a/assets/js/blocks/product-collection/inspector-controls/index.tsx b/assets/js/blocks/product-collection/inspector-controls/index.tsx index c75141c6113..0f7537fb219 100644 --- a/assets/js/blocks/product-collection/inspector-controls/index.tsx +++ b/assets/js/blocks/product-collection/inspector-controls/index.tsx @@ -1,14 +1,20 @@ /** * External dependencies */ -import type { ElementType } from 'react'; import type { BlockEditProps } from '@wordpress/blocks'; import { InspectorControls, BlockControls } from '@wordpress/block-editor'; import { __ } from '@wordpress/i18n'; -import { useMemo } from '@wordpress/element'; +import { type ElementType, useMemo } from '@wordpress/element'; import { EditorBlock } from '@woocommerce/types'; import { addFilter } from '@wordpress/hooks'; import { ProductCollectionFeedbackPrompt } from '@woocommerce/editor-components/feedback-prompt'; +import { + enableAutoUpdate, + revertMigration, + getUpgradeStatus, + HOURS_TO_DISPLAY_UPGRADE_NOTICE, + UPGRADE_NOTICE_DISPLAY_COUNT_THRESHOLD, +} from '@woocommerce/blocks/migration-products-to-product-collection'; import { // @ts-expect-error Using experimental features // eslint-disable-next-line @wordpress/no-unsafe-wp-apis @@ -34,7 +40,6 @@ import TaxonomyControls from './taxonomy-controls'; import HandPickedProductsControl from './hand-picked-products-control'; import AuthorControl from './author-control'; import DisplayLayoutControl from './display-layout-control'; -import { replaceProductCollectionWithProducts } from '../../shared/scripts'; const ProductCollectionInspectorControls = ( props: BlockEditProps< ProductCollectionAttributes > @@ -106,29 +111,92 @@ const ProductCollectionInspectorControls = ( export default ProductCollectionInspectorControls; -const isProductCollection = ( - block: EditorBlock< ProductCollectionAttributes > -) => block.name === metadata.name; +// Trigger Auto Upgrade of Products only once when module is loaded. +// This triggers subscription but only if: +// - auto update is enabled +// - user haven't reverted the migration +// - no other subscription is in place +enableAutoUpdate(); + +const isProductCollection = ( blockName: string ) => + blockName === metadata.name; + +const lessThanThresholdSinceUpdate = ( t: number ) => { + // Xh * 60m * 60s * 1000ms + const xHoursFromT = t + HOURS_TO_DISPLAY_UPGRADE_NOTICE * 60 * 60 * 1000; + return Date.now() < xHoursFromT; +}; + +const displayedLessThanThreshold = ( displayCount = 0 ) => { + return displayCount <= UPGRADE_NOTICE_DISPLAY_COUNT_THRESHOLD; +}; + +// Upgrade Notice should be displayed only if: +// - block is converted from Products +// - user haven't acknowledged seeing the notice +// - less than X hours since the notice was first displayed +// - notice was displayed less than X times +const shouldDisplayUpgradeNotice = ( props ) => { + const { attributes } = props; + const { convertedFromProducts } = attributes; + const { status, time, displayCount } = getUpgradeStatus(); + + return ( + convertedFromProducts && + status === 'notseen' && + lessThanThresholdSinceUpdate( time ) && + displayedLessThanThreshold( displayCount ) + ); +}; + +// Block should be unmarked as converted from Products if: +// block is converted from Products and either: +// - user acknowledged seeing the notice +// - it's more than X hours since the notice was first displayed +// - notice was displayed more than X times +// We do that to prevent showing the notice again after Products on +// other page were updated or local storage was cleared or user +// switched to another machine/browser etc. +const shouldBeUnmarkedAsConverted = ( props ) => { + const { attributes } = props; + const { convertedFromProducts } = attributes; + const { status, time, displayCount } = getUpgradeStatus(); + + return ( + convertedFromProducts && + ( status === 'seen' || + ! lessThanThresholdSinceUpdate( time ) || + ! displayedLessThanThreshold( displayCount ) ) + ); +}; export const withUpgradeNoticeControls = < T extends EditorBlock< T > >( BlockEdit: ElementType ) => - ( props: EditorBlock< ProductCollectionAttributes > ) => { - return isProductCollection( props ) ? ( + ( props: BlockEditProps< ProductCollectionAttributes > ) => { + if ( ! isProductCollection( props.name ) ) { + return ; + } + + const displayUpgradeNotice = shouldDisplayUpgradeNotice( props ); + const unmarkAsConverted = shouldBeUnmarkedAsConverted( props ); + + if ( unmarkAsConverted ) { + props.setAttributes( { convertedFromProducts: false } ); + } + + return ( <> - - { props.attributes.displayUpgradeNotice && ( - - ) } - + { displayUpgradeNotice && ( + + { + + } + + ) } - ) : ( - ); }; diff --git a/assets/js/blocks/product-collection/inspector-controls/upgrade-notice.tsx b/assets/js/blocks/product-collection/inspector-controls/upgrade-notice.tsx index d9fe5b9607e..df557e6a9f6 100644 --- a/assets/js/blocks/product-collection/inspector-controls/upgrade-notice.tsx +++ b/assets/js/blocks/product-collection/inspector-controls/upgrade-notice.tsx @@ -3,60 +3,81 @@ */ import { __ } from '@wordpress/i18n'; import { Notice, Button } from '@wordpress/components'; -import { BlockEditProps } from '@wordpress/blocks'; -import { createInterpolateElement } from '@wordpress/element'; +import { useLocalStorageState } from '@woocommerce/base-hooks'; +import { + createInterpolateElement, + useEffect, + useRef, +} from '@wordpress/element'; +import { + MIGRATION_STATUS_LS_KEY, + getInitialStatusLSValue, + incrementUpgradeStatusDisplayCount, +} from '@woocommerce/blocks/migration-products-to-product-collection'; -/** - * Internal dependencies - */ -import { ProductCollectionAttributes } from '../types'; - -const UpgradeNotice = ( - props: BlockEditProps< ProductCollectionAttributes > & { - revertMigration: () => void; - } -) => { - const { displayUpgradeNotice } = props.attributes; - const notice = createInterpolateElement( - __( - 'Products (Beta) block was upgraded to , an updated version with new features and simplified settings.', - 'woo-gutenberg-products-block' +const notice = createInterpolateElement( + __( + 'Products (Beta) block was upgraded to , an updated version with new features and simplified settings.', + 'woo-gutenberg-products-block' + ), + { + strongText: ( + + { __( `Product Collection`, 'woo-gutenberg-products-block' ) } + ), - { - strongText: ( - - { __( - `Product Collection`, - 'woo-gutenberg-products-block' - ) } - - ), - } - ); + } +); - const buttonLabel = __( - 'Revert to Products (Beta)', - 'woo-gutenberg-products-block' - ); +const buttonLabel = __( + 'Revert to Products (Beta)', + 'woo-gutenberg-products-block' +); + +type UpgradeNoticeProps = { + revertMigration: () => void; +}; + +const UpgradeNotice = ( { revertMigration }: UpgradeNoticeProps ) => { + const [ upgradeNoticeStatus, setUpgradeNoticeStatus ] = + useLocalStorageState( + MIGRATION_STATUS_LS_KEY, + getInitialStatusLSValue() + ); + + const canCountDisplays = useRef( true ); + const { status } = upgradeNoticeStatus; const handleRemove = () => { - // @todo: this logic needs to be extended to be hidden for all - // Product Collection blocks and whole store - props.setAttributes( { - displayUpgradeNotice: false, + setUpgradeNoticeStatus( { + status: 'seen', + time: Date.now(), } ); }; - const handleClick = () => { - props.revertMigration(); + const handleRevert = () => { + revertMigration(); }; - return displayUpgradeNotice ? ( + // Prevent the possibility to count displays multiple times when the + // block is selected and Inspector Controls are re-rendered multiple times. + useEffect( () => { + const countDisplay = () => { + if ( canCountDisplays.current ) { + incrementUpgradeStatusDisplayCount(); + canCountDisplays.current = false; + } + }; + + return countDisplay; + }, [ canCountDisplays ] ); + + return status === 'notseen' ? ( <>{ notice }

-
diff --git a/assets/js/blocks/product-collection/types.ts b/assets/js/blocks/product-collection/types.ts index 80fe339e80c..5d01793d5c7 100644 --- a/assets/js/blocks/product-collection/types.ts +++ b/assets/js/blocks/product-collection/types.ts @@ -14,7 +14,7 @@ export interface ProductCollectionAttributes { templateSlug: string; displayLayout: ProductCollectionDisplayLayout; tagName: string; - displayUpgradeNotice: boolean; + convertedFromProducts: boolean; } export interface ProductCollectionDisplayLayout { diff --git a/assets/js/blocks/product-query/constants.ts b/assets/js/blocks/product-query/constants.ts index e017460906e..a5d0fc5c8d4 100644 --- a/assets/js/blocks/product-query/constants.ts +++ b/assets/js/blocks/product-query/constants.ts @@ -13,9 +13,6 @@ import { VARIATION_NAME as PRODUCT_TITLE_ID } from './variations/elements/produc import { VARIATION_NAME as PRODUCT_TEMPLATE_ID } from './variations/elements/product-template'; import { ImageSizing } from '../../atomic/blocks/product-elements/image/types'; -export const AUTO_REPLACE_PRODUCTS_WITH_PRODUCT_COLLECTION = false; -export const MANUAL_REPLACE_PRODUCTS_WITH_PRODUCT_COLLECTION = false; - export const PRODUCT_QUERY_VARIATION_NAME = 'woocommerce/product-query'; export const EDIT_ATTRIBUTES_URL = diff --git a/assets/js/blocks/product-query/inspector-controls.tsx b/assets/js/blocks/product-query/inspector-controls.tsx index 05ea211b4f7..0ae16dd1287 100644 --- a/assets/js/blocks/product-query/inspector-controls.tsx +++ b/assets/js/blocks/product-query/inspector-controls.tsx @@ -1,15 +1,19 @@ /** * External dependencies */ -import type { ElementType } from 'react'; import { __ } from '@wordpress/i18n'; import { InspectorControls } from '@wordpress/block-editor'; -import { useSelect, subscribe } from '@wordpress/data'; +import { useSelect } from '@wordpress/data'; import { addFilter } from '@wordpress/hooks'; +import { type ElementType } from '@wordpress/element'; import { ProductQueryFeedbackPrompt } from '@woocommerce/editor-components/feedback-prompt'; import { EditorBlock, isNumber } from '@woocommerce/types'; import { usePrevious } from '@woocommerce/base-hooks'; -import { isWpVersion, getSettingWithCoercion } from '@woocommerce/settings'; +import { + manualUpdate, + MANUAL_REPLACE_PRODUCTS_WITH_PRODUCT_COLLECTION, +} from '@woocommerce/blocks/migration-products-to-product-collection'; +import { getSettingWithCoercion } from '@woocommerce/settings'; import { ProductQueryBlockQuery } from '@woocommerce/blocks/product-query/types'; import { FormTokenField, @@ -39,14 +43,11 @@ import { QUERY_DEFAULT_ATTRIBUTES, QUERY_LOOP_ID, STOCK_STATUS_OPTIONS, - AUTO_REPLACE_PRODUCTS_WITH_PRODUCT_COLLECTION, - MANUAL_REPLACE_PRODUCTS_WITH_PRODUCT_COLLECTION, } from './constants'; import { AttributesFilter } from './inspector-controls/attributes-filter'; import { PopularPresets } from './inspector-controls/popular-presets'; import { ProductSelector } from './inspector-controls/product-selector'; import { UpgradeNotice } from './inspector-controls/upgrade-notice'; -import { replaceProductsWithProductCollection } from '../shared/scripts'; import './editor.scss'; @@ -237,9 +238,7 @@ const ProductQueryControls = ( props: ProductQueryBlock ) => { <> { MANUAL_REPLACE_PRODUCTS_WITH_PRODUCT_COLLECTION && ( - + ) } { allowedControls?.includes( 'presets' ) && ( @@ -289,16 +288,3 @@ export const withProductQueryControls = }; addFilter( 'editor.BlockEdit', QUERY_LOOP_ID, withProductQueryControls ); - -if ( isWpVersion( '6.1', '>=' ) ) { - let unsubscribe: ( () => void ) | undefined; - if ( AUTO_REPLACE_PRODUCTS_WITH_PRODUCT_COLLECTION && ! unsubscribe ) { - unsubscribe = subscribe( () => { - replaceProductsWithProductCollection( () => { - if ( unsubscribe ) { - unsubscribe(); - } - } ); - }, 'core/block-editor' ); - } -}