Skip to content
This repository has been archived by the owner on Feb 23, 2024. It is now read-only.

Commit

Permalink
Combine country asset data to reduce the amount of data consumed by t…
Browse files Browse the repository at this point in the history
…he cart and checkout blocks (#9552)

* Combine countryData using shared util

* Update tests for shared util

* Update client to use countryData

* Avoid duplication of country names

* Use cart version of deep_sort_with_accents

* Update assets/js/settings/blocks/constants.ts

Co-authored-by: Thomas Roberts <[email protected]>

* Update LocaleSpecificAddressField type

* Support nested arrays

---------

Co-authored-by: Thomas Roberts <[email protected]>
  • Loading branch information
mikejolley and opr authored May 31, 2023
1 parent fd5af1c commit 4bb57c1
Show file tree
Hide file tree
Showing 11 changed files with 192 additions and 242 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,12 @@ import {
AddressFields,
CountryAddressFields,
defaultAddressFields,
getSetting,
KeyedAddressField,
LocaleSpecificAddressField,
} from '@woocommerce/settings';
import { __, sprintf } from '@wordpress/i18n';
import { isNumber, isString } from '@woocommerce/types';

/**
* This is locale data from WooCommerce countries class. This doesn't match the shape of the new field data blocks uses,
* but we can import part of it to set which fields are required.
*
* This supports new properties such as optionalLabel which are not used by core (yet).
*/
const coreLocale = getSetting< CountryAddressFields >( 'countryLocale', {} );
import { COUNTRY_LOCALE } from '@woocommerce/block-settings';

/**
* Gets props from the core locale, then maps them to the shape we require in the client.
Expand Down Expand Up @@ -72,7 +64,15 @@ const getSupportedCoreLocaleProps = (
return fields;
};

const countryAddressFields: CountryAddressFields = Object.entries( coreLocale )
/**
* COUNTRY_LOCALE is locale data from WooCommerce countries class. This doesn't match the shape of the new field data blocks uses,
* but we can import part of it to set which fields are required.
*
* This supports new properties such as optionalLabel which are not used by core (yet).
*/
const countryAddressFields: CountryAddressFields = Object.entries(
COUNTRY_LOCALE
)
.map( ( [ country, countryLocale ] ) => [
country,
Object.entries( countryLocale )
Expand Down
23 changes: 9 additions & 14 deletions assets/js/base/utils/address.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ import {
defaultAddressFields,
ShippingAddress,
BillingAddress,
getSetting,
} from '@woocommerce/settings';
import { decodeEntities } from '@wordpress/html-entities';
import {
SHIPPING_COUNTRIES,
SHIPPING_STATES,
} from '@woocommerce/block-settings';

/**
* Compare two addresses and see if they are the same.
Expand Down Expand Up @@ -116,24 +119,16 @@ export const formatShippingAddress = (
if ( Object.values( address ).length === 0 ) {
return null;
}
const shippingCountries = getSetting< Record< string, string > >(
'shippingCountries',
{}
);
const shippingStates = getSetting< Record< string, string > >(
'shippingStates',
{}
);
const formattedCountry =
typeof shippingCountries[ address.country ] === 'string'
? decodeEntities( shippingCountries[ address.country ] )
typeof SHIPPING_COUNTRIES[ address.country ] === 'string'
? decodeEntities( SHIPPING_COUNTRIES[ address.country ] )
: '';

const formattedState =
typeof shippingStates[ address.country ] === 'object' &&
typeof shippingStates[ address.country ][ address.state ] === 'string'
typeof SHIPPING_STATES[ address.country ] === 'object' &&
typeof SHIPPING_STATES[ address.country ][ address.state ] === 'string'
? decodeEntities(
shippingStates[ address.country ][ address.state ]
SHIPPING_STATES[ address.country ][ address.state ]
)
: address.state;

Expand Down
81 changes: 66 additions & 15 deletions assets/js/settings/blocks/constants.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
/**
* External dependencies
*/
import { getSetting, STORE_PAGES } from '@woocommerce/settings';
import {
getSetting,
STORE_PAGES,
LocaleSpecificAddressField,
} from '@woocommerce/settings';

export type WordCountType =
| 'words'
Expand Down Expand Up @@ -41,22 +45,69 @@ export const CART_URL = STORE_PAGES.cart.permalink;
export const LOGIN_URL = STORE_PAGES.myaccount.permalink
? STORE_PAGES.myaccount.permalink
: getSetting( 'wpLoginUrl', '/wp-login.php' );
export const SHIPPING_COUNTRIES = getSetting< Record< string, string > >(
'shippingCountries',
{}
export const LOCAL_PICKUP_ENABLED = getSetting< boolean >(
'localPickupEnabled',
false
);
export const ALLOWED_COUNTRIES = getSetting< Record< string, string > >(
'allowedCountries',

type CountryData = {
allowBilling: boolean;
allowShipping: boolean;
states: Record< string, string >;
locale: Record< string, LocaleSpecificAddressField >;
};

// Contains country names.
const countries = getSetting< Record< string, string > >( 'countries', {} );

// Contains country settings.
const countryData = getSetting< Record< string, CountryData > >(
'countryData',
{}
);
export const SHIPPING_STATES = getSetting<
Record< string, Record< string, string > >
>( 'shippingStates', {} );
export const ALLOWED_STATES = getSetting< Record< string, string > >(
'allowedStates',
{}

export const ALLOWED_COUNTRIES = Object.fromEntries(
Object.keys( countryData )
.filter( ( countryCode ) => {
return countryData[ countryCode ].allowBilling === true;
} )
.map( ( countryCode ) => {
return [ countryCode, countries[ countryCode ] || '' ];
} )
);
export const LOCAL_PICKUP_ENABLED = getSetting< boolean >(
'localPickupEnabled',
false

export const ALLOWED_STATES = Object.fromEntries(
Object.keys( countryData )
.filter( ( countryCode ) => {
return countryData[ countryCode ].allowBilling === true;
} )
.map( ( countryCode ) => {
return [ countryCode, countryData[ countryCode ].states || [] ];
} )
);

export const SHIPPING_COUNTRIES = Object.fromEntries(
Object.keys( countryData )
.filter( ( countryCode ) => {
return countryData[ countryCode ].allowShipping === true;
} )
.map( ( countryCode ) => {
return [ countryCode, countries[ countryCode ] || '' ];
} )
);

export const SHIPPING_STATES = Object.fromEntries(
Object.keys( countryData )
.filter( ( countryCode ) => {
return countryData[ countryCode ].allowShipping === true;
} )
.map( ( countryCode ) => {
return [ countryCode, countryData[ countryCode ].states || [] ];
} )
);

export const COUNTRY_LOCALE = Object.fromEntries(
Object.keys( countryData ).map( ( countryCode ) => {
return [ countryCode, countryData[ countryCode ].locale || [] ];
} )
);
4 changes: 2 additions & 2 deletions assets/js/settings/shared/default-address-fields.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ export interface AddressField {
index: number;
}

export interface LocaleSpecificAddressField extends AddressField {
priority: number;
export interface LocaleSpecificAddressField extends Partial< AddressField > {
priority?: number | undefined;
}

export interface AddressFields {
Expand Down
60 changes: 3 additions & 57 deletions src/BlockTypes/Cart.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
<?php
namespace Automattic\WooCommerce\Blocks\BlockTypes;

use Automattic\WooCommerce\Blocks\Utils\CartCheckoutUtils;

/**
* Cart class.
*
Expand Down Expand Up @@ -156,40 +158,8 @@ protected function render( $attributes, $content, $block ) {
*/
protected function enqueue_data( array $attributes = [] ) {
parent::enqueue_data( $attributes );
if ( wc_shipping_enabled() ) {
$this->asset_data_registry->add(
'shippingCountries',
function() {
return $this->deep_sort_with_accents( WC()->countries->get_shipping_countries() );
},
true
);
$this->asset_data_registry->add(
'shippingStates',
function() {
return $this->deep_sort_with_accents( WC()->countries->get_shipping_country_states() );
},
true
);
}

$this->asset_data_registry->add(
'countryLocale',
function() {
// Merge country and state data to work around https://github.com/woocommerce/woocommerce/issues/28944.
$country_locale = wc()->countries->get_country_locale();
$states = wc()->countries->get_states();

foreach ( $states as $country => $states ) {
if ( empty( $states ) ) {
$country_locale[ $country ]['state']['required'] = false;
$country_locale[ $country ]['state']['hidden'] = true;
}
}
return $country_locale;
},
true
);
$this->asset_data_registry->add( 'countryData', CartCheckoutUtils::get_country_data(), true );
$this->asset_data_registry->add( 'baseLocation', wc_get_base_location(), true );
$this->asset_data_registry->add( 'isShippingCalculatorEnabled', filter_var( get_option( 'woocommerce_enable_shipping_calc' ), FILTER_VALIDATE_BOOLEAN ), true );
$this->asset_data_registry->add( 'displayItemizedTaxes', 'itemized' === get_option( 'woocommerce_tax_total_display' ), true );
Expand All @@ -216,30 +186,6 @@ function() {
do_action( 'woocommerce_blocks_cart_enqueue_data' );
}

/**
* Removes accents from an array of values, sorts by the values, then returns the original array values sorted.
*
* @param array $array Array of values to sort.
* @return array Sorted array.
*/
protected function deep_sort_with_accents( $array ) {
if ( ! is_array( $array ) || empty( $array ) ) {
return $array;
}

$array_without_accents = array_map(
function( $value ) {
return is_array( $value )
? $this->deep_sort_with_accents( $value )
: remove_accents( wc_strtolower( html_entity_decode( $value ) ) );
},
$array
);

asort( $array_without_accents );
return array_replace( $array_without_accents, $array );
}

/**
* Hydrate the cart block with data from the API.
*/
Expand Down
70 changes: 2 additions & 68 deletions src/BlockTypes/Checkout.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
namespace Automattic\WooCommerce\Blocks\BlockTypes;

use Automattic\WooCommerce\StoreApi\Utilities\LocalPickupUtils;
use Automattic\WooCommerce\Blocks\Utils\CartCheckoutUtils;

/**
* Checkout class.
Expand Down Expand Up @@ -184,54 +185,7 @@ protected function is_checkout_endpoint() {
protected function enqueue_data( array $attributes = [] ) {
parent::enqueue_data( $attributes );

$this->asset_data_registry->add(
'allowedCountries',
function() {
return $this->deep_sort_with_accents( WC()->countries->get_allowed_countries() );
},
true
);
$this->asset_data_registry->add(
'allowedStates',
function() {
return $this->deep_sort_with_accents( WC()->countries->get_allowed_country_states() );
},
true
);
if ( wc_shipping_enabled() ) {
$this->asset_data_registry->add(
'shippingCountries',
function() {
return $this->deep_sort_with_accents( WC()->countries->get_shipping_countries() );
},
true
);
$this->asset_data_registry->add(
'shippingStates',
function() {
return $this->deep_sort_with_accents( WC()->countries->get_shipping_country_states() );
},
true
);
}

$this->asset_data_registry->add(
'countryLocale',
function() {
// Merge country and state data to work around https://github.com/woocommerce/woocommerce/issues/28944.
$country_locale = wc()->countries->get_country_locale();
$states = wc()->countries->get_states();

foreach ( $states as $country => $states ) {
if ( empty( $states ) ) {
$country_locale[ $country ]['state']['required'] = false;
$country_locale[ $country ]['state']['hidden'] = true;
}
}
return $country_locale;
},
true
);
$this->asset_data_registry->add( 'countryData', CartCheckoutUtils::get_country_data(), true );
$this->asset_data_registry->add( 'baseLocation', wc_get_base_location(), true );
$this->asset_data_registry->add(
'checkoutAllowsGuest',
Expand Down Expand Up @@ -373,26 +327,6 @@ protected function is_block_editor() {
return $screen && $screen->is_block_editor();
}

/**
* Removes accents from an array of values, sorts by the values, then returns the original array values sorted.
*
* @param array $array Array of values to sort.
* @return array Sorted array.
*/
protected function deep_sort_with_accents( $array ) {
if ( ! is_array( $array ) || empty( $array ) ) {
return $array;
}

if ( is_array( reset( $array ) ) ) {
return array_map( [ $this, 'deep_sort_with_accents' ], $array );
}

$array_without_accents = array_map( 'remove_accents', array_map( 'wc_strtolower', array_map( 'html_entity_decode', $array ) ) );
asort( $array_without_accents );
return array_replace( $array_without_accents, $array );
}

/**
* Get saved customer payment methods for use in checkout.
*/
Expand Down
Loading

0 comments on commit 4bb57c1

Please sign in to comment.