mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-08-30 05:00:51 +08:00
Cart PayPal Pay Later messaging block: Change the default insert position to under the totals
This commit is contained in:
parent
d1b4d7721e
commit
287a4a2819
8 changed files with 205 additions and 13 deletions
|
@ -18,7 +18,6 @@
|
|||
"lock": {
|
||||
"type": "object",
|
||||
"default": {
|
||||
"remove": true,
|
||||
"move": false
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
(function(wp) {
|
||||
const { createBlock } = wp.blocks;
|
||||
const { select, dispatch, subscribe } = wp.data;
|
||||
const getBlocks = () => select('core/block-editor').getBlocks() || [];
|
||||
|
||||
// Store the initial list of block client IDs
|
||||
let blockList = getBlocks().map(block => block.clientId);
|
||||
|
||||
/**
|
||||
* Subscribes to changes in the block editor, specifically checking for the presence of 'woocommerce/cart'.
|
||||
*/
|
||||
subscribe(() => {
|
||||
const currentBlocks = getBlocks();
|
||||
|
||||
currentBlocks.forEach(block => {
|
||||
if (block.name === 'woocommerce/cart') {
|
||||
ensurePayLaterBlockExists(block);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Ensures the 'woocommerce-paypal-payments/cart-paylater-messages' block exists inside the 'woocommerce/cart' block.
|
||||
* @param {Object} cartBlock - The cart block instance.
|
||||
*/
|
||||
function ensurePayLaterBlockExists(cartBlock) {
|
||||
const payLaterBlock = findBlockByName(cartBlock.innerBlocks, 'woocommerce-paypal-payments/cart-paylater-messages');
|
||||
if (!payLaterBlock) {
|
||||
waitForBlock('woocommerce/cart-totals-block', 'woocommerce-paypal-payments/cart-paylater-messages', 'woocommerce/cart-order-summary-block');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits for a specific block to appear using async/await pattern before executing the insertBlockAfter function.
|
||||
* @param {string} targetBlockName - Name of the block to wait for.
|
||||
* @param {string} newBlockName - Name of the new block to insert after the target.
|
||||
* @param {string} anchorBlockName - Name of the anchor block to determine position.
|
||||
* @param {number} attempts - The number of attempts made to find the target block.
|
||||
*/
|
||||
async function waitForBlock(targetBlockName, newBlockName, anchorBlockName = '', attempts = 0) {
|
||||
const targetBlock = findBlockByName(getBlocks(), targetBlockName);
|
||||
if (targetBlock) {
|
||||
await delay(1000); // We need this to ensure the block is fully rendered
|
||||
insertBlockAfter(targetBlockName, newBlockName, anchorBlockName);
|
||||
} else if (attempts < 10) { // Poll up to 10 times
|
||||
await delay(1000); // Wait 1 second before retrying
|
||||
await waitForBlock(targetBlockName, newBlockName, anchorBlockName, attempts + 1);
|
||||
} else {
|
||||
console.log('Failed to find target block after several attempts.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delays execution by a given number of milliseconds.
|
||||
* @param {number} ms - Milliseconds to delay.
|
||||
* @return {Promise} A promise that resolves after the delay.
|
||||
*/
|
||||
function delay(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a block after a specified block if it doesn't already exist.
|
||||
* @param {string} targetBlockName - Name of the block to find.
|
||||
* @param {string} newBlockName - Name of the new block to insert.
|
||||
* @param {string} anchorBlockName - Name of the anchor block to determine position.
|
||||
*/
|
||||
function insertBlockAfter(targetBlockName, newBlockName, anchorBlockName = '') {
|
||||
const targetBlock = findBlockByName(getBlocks(), targetBlockName);
|
||||
if (!targetBlock) {
|
||||
// Target block not found
|
||||
return;
|
||||
}
|
||||
|
||||
const parentBlock = select('core/block-editor').getBlock(targetBlock.clientId);
|
||||
if (parentBlock.innerBlocks.some(block => block.name === newBlockName)) {
|
||||
// The block is already inserted next to the target block
|
||||
return;
|
||||
}
|
||||
|
||||
let offset = 0;
|
||||
if (anchorBlockName !== '') {
|
||||
// Find the anchor block and calculate the offset
|
||||
const anchorIndex = parentBlock.innerBlocks.findIndex(block => block.name === anchorBlockName);
|
||||
offset = parentBlock.innerBlocks.length - (anchorIndex + 1);
|
||||
}
|
||||
|
||||
const newBlock = createBlock(newBlockName);
|
||||
|
||||
// Insert the block at the correct position
|
||||
dispatch('core/block-editor').insertBlock(newBlock, parentBlock.innerBlocks.length - offset, parentBlock.clientId);
|
||||
|
||||
// Lock the block after it has been inserted
|
||||
setTimeout(() => {
|
||||
dispatch('core/block-editor').updateBlockAttributes(newBlock.clientId, {
|
||||
lock: { remove: true }
|
||||
});
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively searches for a block by name among all blocks.
|
||||
* @param {Array} blocks - The array of blocks to search.
|
||||
* @param {string} blockName - The name of the block to find.
|
||||
* @returns {Object|null} The found block, or null if not found.
|
||||
*/
|
||||
function findBlockByName(blocks, blockName) {
|
||||
for (const block of blocks) {
|
||||
if (block.name === blockName) {
|
||||
return block;
|
||||
}
|
||||
if (block.innerBlocks.length > 0) {
|
||||
const foundBlock = findBlockByName(block.innerBlocks, blockName);
|
||||
if (foundBlock) {
|
||||
return foundBlock;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
})(window.wp);
|
|
@ -9,6 +9,8 @@ import { registerBlockType } from '@wordpress/blocks';
|
|||
import Edit from './edit';
|
||||
import metadata from './block.json';
|
||||
|
||||
const { underTotalsPlacementEnabled } = window.PcpCartPayLaterBlock;
|
||||
|
||||
const paypalIcon = (
|
||||
<svg width="584.798" height="720" viewBox="0 0 154.728 190.5">
|
||||
<g transform="translate(898.192 276.071)">
|
||||
|
@ -31,6 +33,8 @@ const paypalIcon = (
|
|||
</svg>
|
||||
);
|
||||
|
||||
metadata.attributes.lock.default.remove = !Boolean(underTotalsPlacementEnabled);
|
||||
|
||||
registerBlockType(metadata, {
|
||||
icon: paypalIcon,
|
||||
edit: Edit,
|
||||
|
|
|
@ -6,7 +6,7 @@ import { PayPalScriptProvider, PayPalMessages } from '@paypal/react-paypal-js';
|
|||
import { useScriptParams } from '../../../../ppcp-paylater-block/resources/js/hooks/script-params';
|
||||
|
||||
export default function Edit({ attributes, clientId, setAttributes }) {
|
||||
const { id, ppcpId } = attributes;
|
||||
const { ppcpId } = attributes;
|
||||
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import { PayPalScriptProvider, PayPalMessages } from '@paypal/react-paypal-js';
|
|||
import { useScriptParams } from '../../../../ppcp-paylater-block/resources/js/hooks/script-params';
|
||||
|
||||
export default function Edit({ attributes, clientId, setAttributes }) {
|
||||
const { id, ppcpId } = attributes;
|
||||
const { ppcpId } = attributes;
|
||||
|
||||
const [loaded, setLoaded] = useState(false);
|
||||
|
||||
|
|
|
@ -59,6 +59,19 @@ class PayLaterWCBlocksModule implements ModuleInterface {
|
|||
return self::is_block_enabled( $settings_status, $location );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the under cart totals placement is enabled.
|
||||
*
|
||||
* @return bool true if the under cart totals placement is enabled, otherwise false.
|
||||
*/
|
||||
public function is_under_cart_totals_placement_enabled() : bool {
|
||||
return apply_filters(
|
||||
// phpcs:ignore WordPress.NamingConventions.ValidHookName.UseUnderscores
|
||||
'woocommerce.feature-flags.woocommerce_paypal_payments.paylater_wc_blocks_cart_under_totals_enabled',
|
||||
true
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
|
@ -103,16 +116,17 @@ class PayLaterWCBlocksModule implements ModuleInterface {
|
|||
$script_handle,
|
||||
'PcpCartPayLaterBlock',
|
||||
array(
|
||||
'ajax' => array(
|
||||
'ajax' => array(
|
||||
'cart_script_params' => array(
|
||||
'endpoint' => \WC_AJAX::get_endpoint( CartScriptParamsEndpoint::ENDPOINT ),
|
||||
),
|
||||
),
|
||||
'config' => $config_factory->from_settings( $settings ),
|
||||
'settingsUrl' => admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway' ),
|
||||
'vaultingEnabled' => $settings->has( 'vault_enabled' ) && $settings->get( 'vault_enabled' ),
|
||||
'placementEnabled' => self::is_placement_enabled( $c->get( 'wcgateway.settings.status' ), 'cart' ),
|
||||
'payLaterSettingsUrl' => admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway&ppcp-tab=ppcp-pay-later' ),
|
||||
'config' => $config_factory->from_settings( $settings ),
|
||||
'settingsUrl' => admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway' ),
|
||||
'vaultingEnabled' => $settings->has( 'vault_enabled' ) && $settings->get( 'vault_enabled' ),
|
||||
'placementEnabled' => self::is_placement_enabled( $c->get( 'wcgateway.settings.status' ), 'cart' ),
|
||||
'payLaterSettingsUrl' => admin_url( 'admin.php?page=wc-settings&tab=checkout§ion=ppcp-gateway&ppcp-tab=ppcp-pay-later' ),
|
||||
'underTotalsPlacementEnabled' => self::is_under_cart_totals_placement_enabled(),
|
||||
)
|
||||
);
|
||||
|
||||
|
@ -218,7 +232,8 @@ class PayLaterWCBlocksModule implements ModuleInterface {
|
|||
'woocommerce-paypal-payments/cart-paylater-messages',
|
||||
'ppcp-cart-paylater-messages',
|
||||
'cart',
|
||||
$c
|
||||
$c,
|
||||
self::is_under_cart_totals_placement_enabled()
|
||||
);
|
||||
}
|
||||
return $block_content;
|
||||
|
@ -245,6 +260,27 @@ class PayLaterWCBlocksModule implements ModuleInterface {
|
|||
10,
|
||||
1
|
||||
);
|
||||
|
||||
// Since there's no regular way we can place the Pay Later messaging block under the cart totals block, we need a custom script.
|
||||
if ( self::is_under_cart_totals_placement_enabled() ) {
|
||||
add_action(
|
||||
'enqueue_block_editor_assets',
|
||||
function () use ( $c, $settings ): void {
|
||||
$handle = 'ppcp-checkout-paylater-block-editor-inserter';
|
||||
$path = $c->get( 'paylater-wc-blocks.url' ) . 'assets/js/cart-paylater-block-inserter.js';
|
||||
|
||||
wp_register_script(
|
||||
$handle,
|
||||
$path,
|
||||
array( 'wp-blocks', 'wp-data', 'wp-element' ),
|
||||
$c->get( 'ppcp.asset-version' ),
|
||||
true
|
||||
);
|
||||
|
||||
wp_enqueue_script( $handle );
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,6 +31,24 @@ class PayLaterWCBlocksUtils {
|
|||
return $block_content;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts content after the closing div tag of a specific block.
|
||||
*
|
||||
* @param string $block_content The block content.
|
||||
* @param string $content_to_insert The content to insert.
|
||||
* @param string $reference_block The block markup to insert the content after.
|
||||
* @return string The block content with the content inserted.
|
||||
*/
|
||||
public static function insert_before_opening_div( string $block_content, string $content_to_insert, string $reference_block ): string {
|
||||
$reference_block_index = strpos( $block_content, $reference_block );
|
||||
|
||||
if ( false !== $reference_block_index ) {
|
||||
return substr_replace( $block_content, $content_to_insert, $reference_block_index, 0 );
|
||||
} else {
|
||||
return self::insert_before_last_div( $block_content, $content_to_insert );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a PayLater message block and inserts it before the last closing div tag if the block id is not already present.
|
||||
*
|
||||
|
@ -39,12 +57,19 @@ class PayLaterWCBlocksUtils {
|
|||
* @param string $ppcp_id ID for the PPCP component.
|
||||
* @param string $context Rendering context (cart or checkout).
|
||||
* @param mixed $container Dependency injection container.
|
||||
* @param bool $is_under_cart_totals_placement_enabled Whether the block should be placed under the cart totals.
|
||||
* @return string Updated block content.
|
||||
*/
|
||||
public static function render_and_insert_paylater_block( string $block_content, string $block_id, string $ppcp_id, string $context, $container ): string {
|
||||
$paylater_message_block = self::render_paylater_block( $block_id, $ppcp_id, $context, $container );
|
||||
public static function render_and_insert_paylater_block( string $block_content, string $block_id, string $ppcp_id, string $context, $container, bool $is_under_cart_totals_placement_enabled = false ): string {
|
||||
$paylater_message_block = self::render_paylater_block( $block_id, $ppcp_id, $context, $container );
|
||||
$cart_express_payment_block = '<div data-block-name="woocommerce/cart-express-payment-block" class="wp-block-woocommerce-cart-express-payment-block"></div>';
|
||||
|
||||
if ( false !== $paylater_message_block ) {
|
||||
return self::insert_before_last_div( $block_content, $paylater_message_block );
|
||||
if ( $is_under_cart_totals_placement_enabled && 'cart' === $context ) {
|
||||
return self::insert_before_opening_div( $block_content, $paylater_message_block, $cart_express_payment_block );
|
||||
} else {
|
||||
return self::insert_before_last_div( $block_content, $paylater_message_block );
|
||||
}
|
||||
}
|
||||
return $block_content;
|
||||
}
|
||||
|
|
|
@ -16,6 +16,13 @@ module.exports = {
|
|||
"CartPayLaterMessagesBlock",
|
||||
"cart-paylater-block.js"
|
||||
),
|
||||
"cart-paylater-block-inserter": path.resolve(
|
||||
process.cwd(),
|
||||
"resources",
|
||||
"js",
|
||||
"CartPayLaterMessagesBlock",
|
||||
"cart-paylater-block-inserter.js"
|
||||
),
|
||||
"checkout-paylater-block": path.resolve(
|
||||
process.cwd(),
|
||||
"resources",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue