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

Update and select shipping rates dynamically #1794

Merged
merged 25 commits into from
Mar 5, 2020

Conversation

senadir
Copy link
Member

@senadir senadir commented Feb 21, 2020

REST API

Some changes were made to the store/cart/ endpoint, any endpoint in cart will also return the whole newly updated cart (except cart/shipping-rates, which is still independent), alongside that, some endpoints have been moved around, and changed, here is the summary:

  • /cart/shipping-rates works as usual, but it’s not currently used in the cart, it takes an address, it returns rates only.
  • /cart/ returns an array of shipping-rates for convenience.
  • shipping-rates resource have a selected property much now.
  • /cart/update-shipping takes an address, updates customer session, and returns the cart with new rates.
  • /cart/select-shipping-rate/<package_id> lets you set a rate id for a package by POSTing, it also returns the full cart.

(majority of changes were done in #1833).

GET /wc/store/cart

response

{
  "coupons": [],
  "shipping_rates": [
    {
      "package_id": 0,
      "name": "Shipping",
      "destination": {
        "address_1": "",
        "address_2": "",
        "city": "Oran",
        "state": "Oran",
        "postcode": "31085",
        "country": "DZ"
      },
      "items": [
        {
          "key": "33e75ff09dd601bbe69f351039152189",
          "name": "Beanie with Logo",
          "quantity": 1
        }
      ],
      "shipping_rates": [
        {
          "rate_id": "flat_rate:1",
          "name": "Flat rate &#8211; 1",
          "description": "",
          "delivery_time": "",
          "price": "2000",
          "instance_id": 1,
          "method_id": "flat_rate",
          "meta_data": [
            {
              "key": "Items",
              "value": "Beanie with Logo &times; 1"
            }
          ],
          "selected": true,
          "currency_code": "GBP",
          "currency_symbol": "£",
          "currency_minor_unit": 2,
          "currency_decimal_separator": ".",
          "currency_thousand_separator": ",",
          "currency_prefix": "£",
          "currency_suffix": ""
        },
        {
          "rate_id": "flat_rate:2",
          "name": "Flat rate &#8211; 2",
          "description": "",
          "delivery_time": "",
          "price": "6000",
          "instance_id": 2,
          "method_id": "flat_rate",
          "meta_data": [
            {
              "key": "Items",
              "value": "Beanie with Logo &times; 1"
            }
          ],
          "selected": false,
          "currency_code": "GBP",
          "currency_symbol": "£",
          "currency_minor_unit": 2,
          "currency_decimal_separator": ".",
          "currency_thousand_separator": ",",
          "currency_prefix": "£",
          "currency_suffix": ""
        }
      ]
    }
  ],
  "items": [
    {
      "key": "33e75ff09dd601bbe69f351039152189",
      "id": 28,
      "quantity": 1,
      "name": "Beanie with Logo",
      "summary": "<p>This is a simple product.<\/p>",
      "short_description": "<p>This is a simple product.<\/p>",
      "description": "<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.<\/p>",
      "sku": "Woo-beanie-logo",
      "low_stock_remaining": null,
      "sold_individually": false,
      "permalink": "http:\/\/localhost:8889\/product\/beanie-with-logo\/",
      "images": [
        {
          "id": 51,
          "src": "http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1.jpg",
          "thumbnail": "http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-450x450.jpg",
          "srcset": "http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1.jpg 800w, http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-300x300.jpg 300w, http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-150x150.jpg 150w, http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-768x768.jpg 768w, http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-450x450.jpg 450w, http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-600x600.jpg 600w, http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-100x100.jpg 100w",
          "sizes": "(max-width: 800px) 100vw, 800px",
          "name": "beanie-with-logo-1.jpg",
          "alt": ""
        }
      ],
      "variation": [],
      "prices": {
        "currency_code": "GBP",
        "currency_symbol": "£",
        "currency_minor_unit": 2,
        "currency_decimal_separator": ".",
        "currency_thousand_separator": ",",
        "currency_prefix": "£",
        "currency_suffix": "",
        "price": "1800",
        "regular_price": "2000",
        "sale_price": "1800",
        "price_range": null
      },
      "totals": {
        "currency_code": "GBP",
        "currency_symbol": "£",
        "currency_minor_unit": 2,
        "currency_decimal_separator": ".",
        "currency_thousand_separator": ",",
        "currency_prefix": "£",
        "currency_suffix": "",
        "line_subtotal": "1800",
        "line_subtotal_tax": "0",
        "line_total": "1800",
        "line_total_tax": "0"
      }
    }
  ],
  "items_count": 1,
  "items_weight": 0,
  "needs_shipping": true,
  "totals": {
    "currency_code": "GBP",
    "currency_symbol": "£",
    "currency_minor_unit": 2,
    "currency_decimal_separator": ".",
    "currency_thousand_separator": ",",
    "currency_prefix": "£",
    "currency_suffix": "",
    "total_items": "1800",
    "total_items_tax": "0",
    "total_fees": "0",
    "total_fees_tax": "0",
    "total_discount": "0",
    "total_discount_tax": "0",
    "total_shipping": "2000",
    "total_shipping_tax": "0",
    "total_price": "3800",
    "total_tax": "0",
    "tax_lines": []
  }
}

POST /wc/store/cart/update-shipping

{
	"city":"Oran",
	"state":"Oran",
	"postcode":"31085",
	"country":"DZ"
}
response

{
  "coupons": [],
  "shipping_rates": [
    {
      "package_id": 0,
      "name": "Shipping",
      "destination": {
        "address_1": "",
        "address_2": "",
        "city": "Oran",
        "state": "Oran",
        "postcode": "31085",
        "country": "DZ"
      },
      "items": [
        {
          "key": "33e75ff09dd601bbe69f351039152189",
          "name": "Beanie with Logo",
          "quantity": 1
        }
      ],
      "shipping_rates": [
        {
          "rate_id": "flat_rate:1",
          "name": "Flat rate &#8211; 1",
          "description": "",
          "delivery_time": "",
          "price": "2000",
          "instance_id": 1,
          "method_id": "flat_rate",
          "meta_data": [
            {
              "key": "Items",
              "value": "Beanie with Logo &times; 1"
            }
          ],
          "selected": true,
          "currency_code": "GBP",
          "currency_symbol": "£",
          "currency_minor_unit": 2,
          "currency_decimal_separator": ".",
          "currency_thousand_separator": ",",
          "currency_prefix": "£",
          "currency_suffix": ""
        },
        {
          "rate_id": "flat_rate:2",
          "name": "Flat rate &#8211; 2",
          "description": "",
          "delivery_time": "",
          "price": "6000",
          "instance_id": 2,
          "method_id": "flat_rate",
          "meta_data": [
            {
              "key": "Items",
              "value": "Beanie with Logo &times; 1"
            }
          ],
          "selected": false,
          "currency_code": "GBP",
          "currency_symbol": "£",
          "currency_minor_unit": 2,
          "currency_decimal_separator": ".",
          "currency_thousand_separator": ",",
          "currency_prefix": "£",
          "currency_suffix": ""
        }
      ]
    }
  ],
  "items": [
    {
      "key": "33e75ff09dd601bbe69f351039152189",
      "id": 28,
      "quantity": 1,
      "name": "Beanie with Logo",
      "summary": "<p>This is a simple product.<\/p>",
      "short_description": "<p>This is a simple product.<\/p>",
      "description": "<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.<\/p>",
      "sku": "Woo-beanie-logo",
      "low_stock_remaining": null,
      "sold_individually": false,
      "permalink": "http:\/\/localhost:8889\/product\/beanie-with-logo\/",
      "images": [
        {
          "id": 51,
          "src": "http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1.jpg",
          "thumbnail": "http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-450x450.jpg",
          "srcset": "http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1.jpg 800w, http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-300x300.jpg 300w, http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-150x150.jpg 150w, http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-768x768.jpg 768w, http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-450x450.jpg 450w, http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-600x600.jpg 600w, http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-100x100.jpg 100w",
          "sizes": "(max-width: 800px) 100vw, 800px",
          "name": "beanie-with-logo-1.jpg",
          "alt": ""
        }
      ],
      "variation": [],
      "prices": {
        "currency_code": "GBP",
        "currency_symbol": "£",
        "currency_minor_unit": 2,
        "currency_decimal_separator": ".",
        "currency_thousand_separator": ",",
        "currency_prefix": "£",
        "currency_suffix": "",
        "price": "1800",
        "regular_price": "2000",
        "sale_price": "1800",
        "price_range": null
      },
      "totals": {
        "currency_code": "GBP",
        "currency_symbol": "£",
        "currency_minor_unit": 2,
        "currency_decimal_separator": ".",
        "currency_thousand_separator": ",",
        "currency_prefix": "£",
        "currency_suffix": "",
        "line_subtotal": "1800",
        "line_subtotal_tax": "0",
        "line_total": "1800",
        "line_total_tax": "0"
      }
    }
  ],
  "items_count": 1,
  "items_weight": 0,
  "needs_shipping": true,
  "totals": {
    "currency_code": "GBP",
    "currency_symbol": "£",
    "currency_minor_unit": 2,
    "currency_decimal_separator": ".",
    "currency_thousand_separator": ",",
    "currency_prefix": "£",
    "currency_suffix": "",
    "total_items": "1800",
    "total_items_tax": "0",
    "total_fees": "0",
    "total_fees_tax": "0",
    "total_discount": "0",
    "total_discount_tax": "0",
    "total_shipping": "2000",
    "total_shipping_tax": "0",
    "total_price": "3800",
    "total_tax": "0",
    "tax_lines": []
  }
}

GET /wc/store/cart/shipping-rates

{
	"city":"Oran",
	"state":"Oran",
	"postcode":"31085",
	"country":"DZ"
}
response

[
  {
    "package_id": 0,
    "name": "Shipping",
    "destination": {
      "address_1": "",
      "address_2": "",
      "city": "Oran",
      "state": "Oran",
      "postcode": "31085",
      "country": "DZ"
    },
    "items": [
      {
        "key": "33e75ff09dd601bbe69f351039152189",
        "name": "Beanie with Logo",
        "quantity": 1
      }
    ],
    "shipping_rates": [
      {
        "rate_id": "flat_rate:1",
        "name": "Flat rate &#8211; 1",
        "description": "",
        "delivery_time": "",
        "price": "2000",
        "instance_id": 1,
        "method_id": "flat_rate",
        "meta_data": [
          {
            "key": "Items",
            "value": "Beanie with Logo &times; 1"
          }
        ],
        "selected": true,
        "currency_code": "GBP",
        "currency_symbol": "£",
        "currency_minor_unit": 2,
        "currency_decimal_separator": ".",
        "currency_thousand_separator": ",",
        "currency_prefix": "£",
        "currency_suffix": ""
      },
      {
        "rate_id": "flat_rate:2",
        "name": "Flat rate &#8211; 2",
        "description": "",
        "delivery_time": "",
        "price": "6000",
        "instance_id": 2,
        "method_id": "flat_rate",
        "meta_data": [
          {
            "key": "Items",
            "value": "Beanie with Logo &times; 1"
          }
        ],
        "selected": false,
        "currency_code": "GBP",
        "currency_symbol": "£",
        "currency_minor_unit": 2,
        "currency_decimal_separator": ".",
        "currency_thousand_separator": ",",
        "currency_prefix": "£",
        "currency_suffix": ""
      }
    ]
  }
]

GET /wc/store/cart/select-shipping-rate/0

{
	"rate_id": "flat_rate:2"
}
response

{
  "coupons": [],
  "shipping_rates": [
    {
      "package_id": 0,
      "name": "Shipping",
      "destination": {
        "address_1": "",
        "address_2": "",
        "city": "Oran",
        "state": "Oran",
        "postcode": "31085",
        "country": "DZ"
      },
      "items": [
        {
          "key": "33e75ff09dd601bbe69f351039152189",
          "name": "Beanie with Logo",
          "quantity": 1
        }
      ],
      "shipping_rates": [
        {
          "rate_id": "flat_rate:1",
          "name": "Flat rate &#8211; 1",
          "description": "",
          "delivery_time": "",
          "price": "2000",
          "instance_id": 1,
          "method_id": "flat_rate",
          "meta_data": [
            {
              "key": "Items",
              "value": "Beanie with Logo &times; 1"
            }
          ],
          "selected": false,
          "currency_code": "GBP",
          "currency_symbol": "£",
          "currency_minor_unit": 2,
          "currency_decimal_separator": ".",
          "currency_thousand_separator": ",",
          "currency_prefix": "£",
          "currency_suffix": ""
        },
        {
          "rate_id": "flat_rate:2",
          "name": "Flat rate &#8211; 2",
          "description": "",
          "delivery_time": "",
          "price": "6000",
          "instance_id": 2,
          "method_id": "flat_rate",
          "meta_data": [
            {
              "key": "Items",
              "value": "Beanie with Logo &times; 1"
            }
          ],
          "selected": true,
          "currency_code": "GBP",
          "currency_symbol": "£",
          "currency_minor_unit": 2,
          "currency_decimal_separator": ".",
          "currency_thousand_separator": ",",
          "currency_prefix": "£",
          "currency_suffix": ""
        }
      ]
    }
  ],
  "items": [
    {
      "key": "33e75ff09dd601bbe69f351039152189",
      "id": 28,
      "quantity": 1,
      "name": "Beanie with Logo",
      "summary": "<p>This is a simple product.<\/p>",
      "short_description": "<p>This is a simple product.<\/p>",
      "description": "<p>Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum tortor quam, feugiat vitae, ultricies eget, tempor sit amet, ante. Donec eu libero sit amet quam egestas semper. Aenean ultricies mi vitae est. Mauris placerat eleifend leo.<\/p>",
      "sku": "Woo-beanie-logo",
      "low_stock_remaining": null,
      "sold_individually": false,
      "permalink": "http:\/\/localhost:8889\/product\/beanie-with-logo\/",
      "images": [
        {
          "id": 51,
          "src": "http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1.jpg",
          "thumbnail": "http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-450x450.jpg",
          "srcset": "http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1.jpg 800w, http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-300x300.jpg 300w, http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-150x150.jpg 150w, http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-768x768.jpg 768w, http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-450x450.jpg 450w, http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-600x600.jpg 600w, http:\/\/localhost:8889\/wp-content\/uploads\/2020\/03\/beanie-with-logo-1-100x100.jpg 100w",
          "sizes": "(max-width: 800px) 100vw, 800px",
          "name": "beanie-with-logo-1.jpg",
          "alt": ""
        }
      ],
      "variation": [],
      "prices": {
        "currency_code": "GBP",
        "currency_symbol": "£",
        "currency_minor_unit": 2,
        "currency_decimal_separator": ".",
        "currency_thousand_separator": ",",
        "currency_prefix": "£",
        "currency_suffix": "",
        "price": "1800",
        "regular_price": "2000",
        "sale_price": "1800",
        "price_range": null
      },
      "totals": {
        "currency_code": "GBP",
        "currency_symbol": "£",
        "currency_minor_unit": 2,
        "currency_decimal_separator": ".",
        "currency_thousand_separator": ",",
        "currency_prefix": "£",
        "currency_suffix": "",
        "line_subtotal": "1800",
        "line_subtotal_tax": "0",
        "line_total": "1800",
        "line_total_tax": "0"
      }
    }
  ],
  "items_count": 1,
  "items_weight": 0,
  "needs_shipping": true,
  "totals": {
    "currency_code": "GBP",
    "currency_symbol": "£",
    "currency_minor_unit": 2,
    "currency_decimal_separator": ".",
    "currency_thousand_separator": ",",
    "currency_prefix": "£",
    "currency_suffix": "",
    "total_items": "1800",
    "total_items_tax": "0",
    "total_fees": "0",
    "total_fees_tax": "0",
    "total_discount": "0",
    "total_discount_tax": "0",
    "total_shipping": "6000",
    "total_shipping_tax": "0",
    "total_price": "7800",
    "total_tax": "0",
    "tax_lines": []
  }
}

wc/store/cart data store.

(a majority of changes were done in #1829).

actions

Some new actions have been added:

  • selectShippingRate selects a shipping rate from a package, updates the whole store.
  • updateShipping updates the shipping address, update the whole as well.
  • receiveShippingAddress used to track the act of updating the address.
selectShippingRate( rate_id<string>, package_id<number> );
updateShipping( address<object> );
receiveShippingAddress( isResolving<boolean> );

reducer

  • changing store keys to uppercase have been moved to the reducer.
  • UPDATING_SHIPPING to track the shipping update state

selectors

  • areShippingRatesLoading used to track the state of loading and updating rates.

Our ever-growing collection of hooks.

  • useSelectShippingRate returns the selectShippingRate action dispatcher, this used to have more things in it, but I think we can skip it and directly have useDispatch inside our component cc @nerrad
  • useShippingRates doesn’t use collections anymore, it has its own resolvers, it returns the same as before shippingRates shippingRatesLoading and also updateShipping an action dispatcher to update shipping address.
  • useStoreCart also returns shippingRates now, useShippingRates relies on it.

Misc

  • Preview data have a destination now.
  • We select the initial rate from the server now.
  • Address persist when you reload the page.

How to test this:

Set up a page with Cart, some product in it the cart, and some shipping rates for different zones.

1- Go the cart, see if there are any rates or not.
2- Update your shipping address to one that has rates.
3- Rates should show up and one of them should be initially selected.
4- Select a different rate, it will select immediately, and prices will changes.
5- Reload the page, your selection should persist, your address should persist as well.

ezgif com-video-to-gif (2)

fixes #1531 #1836 #1779

@senadir senadir added the focus: rest api Work impacting REST api routes. label Feb 21, 2020
@senadir senadir requested a review from Aljullu February 21, 2020 18:27
@senadir senadir self-assigned this Feb 21, 2020
@senadir senadir requested a review from mikejolley February 21, 2020 18:28
Copy link
Member

@mikejolley mikejolley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @senadir

So what you're done here is correct (good work on your first API addition!). Thinking about it more though, I worry this endpoint is going to be confusing to consume. The data returned is readonly, but we want to allow something to be selected. Selected/not selected doesn't really feel like part of the schema?

This is just an idea, and I think we should chat about it further (maybe over slack?) but I wonder if it would be more appropriate to either add a new endpoint for selection, or add a new property to the /cart/ endpoint itself.. That way the shipping rate endpoint here remains purely for getting rates, not for interacting with customer session data.

e.g. /cart/ already has needs_shipping property, and we could add a non-readonly chosen_shipping properly which just stores rate IDs.

Thoughts?

src/RestApi/StoreApi/Controllers/CartShippingRates.php Outdated Show resolved Hide resolved
src/RestApi/StoreApi/Controllers/CartShippingRates.php Outdated Show resolved Hide resolved
src/RestApi/StoreApi/Schemas/CartShippingRateSchema.php Outdated Show resolved Hide resolved
@nerrad
Copy link
Contributor

nerrad commented Feb 25, 2020

After some discussion in slack (p1582626470029500-slack-C8X6Q7XQU), here's what we arrived at:

  • I'll create an issue to address store/cart/shipping-rates coupling with the cart (and whether we change that at all). The issue will be addressed outside our current cycle (see Assess whether store/cart/shipping-rates should be decoupled from cart. #1805)
  • For now do something like POST store/cart/select-shipping for shipping rate selections (similar to coupons). Then include a selected_shipping_rates property on the cart response. Similar logic as what was used for coupons can be used to update cart totals in the cart response etc.

@senadir senadir force-pushed the add/put-method-on-shipping branch from bfaf350 to c1f1d43 Compare February 26, 2020 13:18
@senadir senadir marked this pull request as ready for review February 26, 2020 13:19
@senadir senadir requested a review from a team as a code owner February 26, 2020 13:19
src/RestApi/StoreApi/Schemas/CartSchema.php Outdated Show resolved Hide resolved
src/RestApi/StoreApi/Schemas/CartSchema.php Outdated Show resolved Hide resolved
src/RestApi/StoreApi/Utilities/CartController.php Outdated Show resolved Hide resolved
src/RestApi/StoreApi/Controllers/Cart.php Outdated Show resolved Hide resolved
@senadir
Copy link
Member Author

senadir commented Feb 26, 2020

so I did another push doing some refactors, fixed the tests errors as well.

src/RestApi/StoreApi/Controllers/Cart.php Outdated Show resolved Hide resolved
src/RestApi/StoreApi/Controllers/Cart.php Outdated Show resolved Hide resolved
src/RestApi/StoreApi/Controllers/Cart.php Show resolved Hide resolved
src/RestApi/StoreApi/Utilities/CartController.php Outdated Show resolved Hide resolved
@senadir senadir force-pushed the add/put-method-on-shipping branch from b7273b4 to 713d510 Compare March 3, 2020 11:12
@senadir senadir added the skip-changelog PRs that you don't want to appear in the changelog. label Mar 4, 2020
@senadir senadir changed the title Add endpoint to select a shipping rate Update and select shipping rates dynamically Mar 4, 2020
Copy link
Member

@mikejolley mikejolley left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code seems okay, and it tested okay too :) Maybe need someone else to review but happy to see this nearly merged.

Copy link
Contributor

@nerrad nerrad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work here Nadir (and Mike)! I have a few things I'd like addressed JavaScript side. I didn't look that closely at the php side of things as I'm trusting MIke more for that.

selected = [],
shippingRates,
shippingRates = [],
Copy link
Contributor

@nerrad nerrad Mar 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If shippingRates is being provided as props on this component, I think selectShippingRate should be too along with selectedShipping. It's not clear to me why this needs a setSelectedShipping and selectedShipping state maintained with the Packages component. Seems like this state can be lifted up? (note the suggestions I make later about modifying the hooks which will accomplish this).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well this is two folds, if we're going to go with the approach you’re suggesting, we will need to lift all the way up to FullCart, I don’t want this, I want to isolate hooks to appropriate components to avoid unnecessary full rerender (only packages need to rerender, not the full cart), having all hooks and all logic there will cause performance issues along the way, I would also keep shipping rates within this component if I can, but since we have preview data that is not coming from a hook, I have to pass it to full cart.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can move those to the top until we find a solution for mocking data store in the Editor so that we can rid of all top level hook usage.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well this is two folds, if we're going to go with the approach you’re suggesting, we will need to lift all the way up to FullCart,

Why does it have to go all the way to FullCart? I'm suggesting you lift state up to a hook. You can still pull in the useShippingRates hook here (or in <ShippingRatesControl /> - which is where I was thinking it would be added).

I want to isolate hooks to appropriate components to avoid unnecessary full rerender (only packages need to rerender, not the full cart), having all hooks and all logic there will cause performance issues along the way

I'm wary of assertive statements like "will cause performance issues along the way", unless it's very clear an approach will cause that. All hooks and logic don't need to get implemented in the FullCart and then all props get pushed down.

but since we have preview data that is not coming from a hook, I have to pass it to full cart.

I'm not sure I understand the relevance here? Why not just pass along an isEditor prop from the <Editor/> component to full cart and that can be used to determine whether shipping rates come from the preview data or not?

Copy link
Contributor

@nerrad nerrad Mar 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I'd avoid having preview handling in on this pull as it could block it from getting merged unnecessarily.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assuming we’re getting shipping rates usinguseShippingRates, we will have the mixed preview data thing, I think we can the useSelectShippingRates call in packages (the nearest place they’re used in), and refactor in the future?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wary of assertive statements like "will cause performance issues along the way", unless it's very clear an approach will cause that. All hooks and logic don't need to get implemented in the FullCart and then all props get pushed down.

While there is no direct implications that are obvious, having a lot of logic in the root is going to be problematic for slower devices, as a rule of thumb, we should always try to avoid unnecessary rerenders (our Cart block rerenders 3 or 4 times on initial load only)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should always try to avoid unnecessary rerenders

I agree, but premature optimization can be expensive too. Re-renders aren't necessarily bad, it's expensive re-renders that are bad. So let's avoid premature optimization unless it's clear expensive re-rendering is happening.

Comment on lines 13 to 37
export const useSelectShippingRate = () => {
const { selectShippingRate } = useDispatch( storeKey );
return selectShippingRate;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should receive shippingRates as an argument and then expose selectedShippingRates and selectShippingRate. Then you can include this in the useShippingRates hook. That way this particular hook contains the logic for extracting the selected rates from shippingRates. So something like:

export const useSelectShippingRate = ( shippingRates ) => {
	const initialSelected = shippingRates.map(
		( p ) => p.shipping_rates.find( ( rate ) => rate.selected )?.rate_id
	);
	const [ selectedShippingRates, setSelectedShipping ] = useState(
		initialSelected
	);
	const { selectShippingRate } = useDispatch( storeKey );
	const setRate = ( newShippingRate, packageId ) => {
		setSelectedShipping( [ newShippingRate ] );
		selectShippingRate( newShippingRate, packageId );
	};
	return {
		selectShippingRate: setRate,
		selectedShippingRates,
	};
};

Then in useShippingRates:

export const useShippingRates = () => {
	const { shippingRates } = useStoreCart();
	const { selectShippingRate, selectedShippingRates } = useSelectShippingRate( shippingRates );
    /** rest of existing logic and you return the extra interfaces **/
}

Copy link
Contributor

@nerrad nerrad Mar 4, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incidentally, this is where you could put preview data if you wanted in the editor context. useShippingRate could optionally receive shippingRates as an argument and if it's present then skip using what's returned from the cart (although it'd probably be better to just have any preview data just populate the store and turn off resolving via an isEditor flag maybe. Lot's of ways to do it).

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

although it'd probably be better to just have any preview data just populate the store and turn off resolving via an isEditor flag maybe.

I love this, but we still including preview data to the frontend, but I could argue that it has very minimal weight consideration, the idea was two have possibly two data stores (one registered in the editor and the other frontend, while one resolves to actual data, the other resolves to preview data, but this is a considerate change)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not have preview data be dealt with in this pull. How it's implemented may depend to a degree on what gets implemented from the work I've been doing as well.

assets/js/base/hooks/use-shipping-rates.js Outdated Show resolved Hide resolved
assets/js/data/cart/action-types.js Outdated Show resolved Hide resolved
assets/js/data/cart/actions.js Outdated Show resolved Hide resolved
assets/js/data/cart/selectors.js Outdated Show resolved Hide resolved
// Update customer session.
WC()->customer->set_props(
array(
'shipping_country' => isset( $request['country'] ) ? $request['country'] : null,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this check needed for $request['country']? It should be guaranteed to be set or it would have thrown an error when validated right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

keeping the same style as the rest? I agree that it’s not needed.

@senadir senadir force-pushed the add/put-method-on-shipping branch 2 times, most recently from eff7d7e to ac02b7e Compare March 5, 2020 09:08
Copy link
Contributor

@Aljullu Aljullu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good job with this @senadir! This was a complex task touching many different areas of the codebase. I did some testing and found some small issues.

When opening a page with the Cart and Checkout blocks, I see a warning in the devtools:
imatge

When writing into the State field of the Checkout block, I get some JS errors:
imatge

When loading the Checkout block without any address introduced yet, Shipping options are permanently loading:
imatge

assets/js/base/components/shipping-rates-control/index.js Outdated Show resolved Hide resolved
assets/js/base/hooks/use-shipping-rates.js Outdated Show resolved Hide resolved
assets/js/base/hooks/use-collection.js Outdated Show resolved Hide resolved
assets/js/blocks/cart-checkout/cart/full-cart/index.js Outdated Show resolved Hide resolved
assets/js/data/cart/actions.js Outdated Show resolved Hide resolved
assets/js/previews/shipping-rates.js Outdated Show resolved Hide resolved
@senadir
Copy link
Member Author

senadir commented Mar 5, 2020

#1883

senadir and others added 25 commits March 5, 2020 20:36
* Items should not have keys in API response

* Include package ID in response (this is just a basic index)

* /cart/select-shipping-rate/package_id

* Add package_id to package array

* Update responses and add shipping-rates to main cart endpoint

* update-shipping endpoint
* add selecting shipping to store

* directly call useSelectShippingRate

* refactor cart keys transformation to reducer

* remove selecting first result and accept selecting

* move update shipping to new endpoint

* pass selected rates down

* select shipping right directly and fix editor issues
@senadir senadir force-pushed the add/put-method-on-shipping branch from feb9b78 to 4ba8067 Compare March 5, 2020 19:44
@senadir senadir merged commit 4ddcf44 into master Mar 5, 2020
@senadir senadir deleted the add/put-method-on-shipping branch March 5, 2020 19:54
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
focus: rest api Work impacting REST api routes. skip-changelog PRs that you don't want to appear in the changelog.
Projects
None yet
5 participants