context['postId'] ) ? absint( $block->context['postId'] ) : 0; if ( ! $post_id ) { return ''; } $product = wc_get_product( $post_id ); if ( ! $product instanceof \WC_Product ) { return ''; } wp_enqueue_script_module( $this->get_full_block_name() ); $consent = 'I acknowledge that using private APIs means my theme or plugin will inevitably break in the next version of WooCommerce'; BlocksSharedState::load_store_config( $consent ); $items = $this->prefetch_items(); // Seed the shared shopper-lists store the same way the Wishlist // block does — restUrl + starter nonce + prefetched items. The // two blocks may both render on the same page (e.g. the merchant // drops the Wishlist block into a sidebar of single-product); iAPI's // deep-merge keeps the first writer's payload, so seeding identical // values here is a no-op when Wishlist already ran. wp_interactivity_state( 'woocommerce/shopper-lists', array( 'restUrl' => get_rest_url(), 'nonce' => wp_create_nonce( 'wc_store_api' ), 'lists' => array( self::LIST_SLUG => array( 'items' => $items, 'isLoading' => false, ), ), ) ); // Visible labels flow through `wp_interactivity_config` so the // JS-side getter can pick the right one based on // `state.isInWishlist`. PHP renders the matching one as the // initial server-side label. wp_interactivity_config( 'woocommerce/add-to-wishlist-button', array( 'addLabel' => $this->get_add_label(), 'savedLabel' => $this->get_saved_label(), 'selectOptionsLabel' => $this->get_select_options_label(), ) ); $is_variable = $product->is_type( 'variable' ); $initial_is_in_wishlist = $this->is_initial_in_wishlist( $items, $product ); $initial_disabled = $is_variable; $initial_label = $is_variable ? $this->get_select_options_label() : ( $initial_is_in_wishlist ? $this->get_saved_label() : $this->get_add_label() ); $wrapper_attributes = array( 'class' => 'wc-block-add-to-wishlist-button', 'data-wp-interactive' => 'woocommerce/add-to-wishlist-button', 'data-wp-context' => (string) wp_json_encode( array( 'productId' => $product->get_id(), 'isVariableType' => $is_variable, 'isPending' => false, ) ), ); ob_start(); ?>
>
> Items in the schema response shape. */ private function prefetch_items(): array { if ( ! is_user_logged_in() ) { return array(); } $request = new \WP_REST_Request( 'GET', '/wc/store/v1/shopper-lists/' . self::LIST_SLUG . '/items' ); $response = rest_do_request( $request ); if ( $response->is_error() ) { $error = $response->as_error(); $message = $error instanceof \WP_Error ? $error->get_error_message() : 'Unknown error'; wc_get_logger()->debug( sprintf( 'Add to Wishlist button prefetch failed: %s', $message ), array( 'source' => 'add-to-wishlist-button', 'data' => array( 'slug' => self::LIST_SLUG ), ) ); return array(); } $data = $response->get_data(); if ( ! is_array( $data ) && ! is_object( $data ) ) { return array(); } $decoded = json_decode( (string) wp_json_encode( $data ), true ); return is_array( $decoded ) ? $decoded : array(); } /** * Whether the current product (or its parent, for a variable parent * with no selection yet) is already in the prefetched wishlist. For * variable products the SSR star is always empty — we can't know which * variation the shopper will pick before JS hydrates. * * @param array> $items Schema-shape items. * @param \WC_Product $product The product being viewed. */ private function is_initial_in_wishlist( array $items, \WC_Product $product ): bool { if ( $product->is_type( 'variable' ) ) { return false; } $product_id = $product->get_id(); foreach ( $items as $item ) { if ( isset( $item['id'] ) && (int) $item['id'] === $product_id ) { return true; } } return false; } /** * Visible label when the product is not in the wishlist. */ private function get_add_label(): string { return __( 'Add to wishlist', 'woocommerce' ); } /** * Visible label when the product is already in the wishlist. */ private function get_saved_label(): string { return __( 'Saved to wishlist', 'woocommerce' ); } /** * Visible label when the shopper still needs to pick variation * attributes before the wishlist toggle can resolve to a specific * variation. */ private function get_select_options_label(): string { return __( 'Select options first', 'woocommerce' ); } /** * Get the frontend script handle for this block type. Scripts are * loaded via `viewScriptModule` in block.json. * * @param string|null $key The key of the script to get. * @return null */ protected function get_block_type_script( $key = null ) { return null; } /** * Get the frontend style handle for this block type. Returning null * lets WP use the `style` array from block.json. * * @return null */ protected function get_block_type_style() { return null; } /** * Disable the editor style handle for this block type. * * @return null */ protected function get_block_type_editor_style() { return null; } }