Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add redirect to shop on order cycle change #5748

Merged
merged 6 commits into from Sep 4, 2020

Conversation

mbudm
Copy link

@mbudm mbudm commented Jul 8, 2020

What? Why?

Closes #5072

Adds a location change to the shop path when an order cycle is changed.

Not sure if this is the nicest way to do it. I was looking around for something to use other than a straight 'shop' string. I thought there may be a possibility of using the whitelist loaded in app/assets/javascripts/darkswarm/controllers/page_selection_ctrl.js.coffee but that relies on knowing the index order of the shop tab, which is also brittle as it changes depending on the shop settings.

Any ideas on a pattern for providing some authoritative constants in angularJs that are generated by rails?

What should we test?

  • Navigate to a shop, that has a home page message it will default to the home page.
  • Select an order cycle
  • The tabs should change to the shop tab

This should also behave the same if an item has been added to a checkout and the shopper is prompted about clearing their cart.

  • Navigate to a shop, that has a home page message it will default to the home page.
  • Select an order cycle
  • The tabs should change to the shop tab
  • add an item
  • navigate to the shop home (or any other tab)
  • Select another order cycle
  • confirm the prompt to clear your cart
  • The tabs should change to the shop tab

Release notes

Redirect to shop when order cycle selected

Changelog Category: Changed

@mbudm mbudm self-assigned this Jul 8, 2020
# Track previous order cycle id for use with revertOrderCycle()
$scope.previous_order_cycle_id = OrderCycle.order_cycle.order_cycle_id
$scope.$watch 'order_cycle.order_cycle_id', (newValue, oldValue)->
$scope.previous_order_cycle_id = oldValue

$scope.changeOrderCycle = ->
OrderCycle.push_order_cycle $scope.orderCycleChanged
$location.path("shop")
Copy link
Contributor

Choose a reason for hiding this comment

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

Spree does it with app/views/spree/admin/shared/_routes.html.erb
I dont think we should do routing in angular. The main reason is that we don't really need it and it can get complicated if we go for it.
In this case, I'd do this on the server. For example, making push_order_cycle return a 301 and follow it on the callback?

Copy link
Author

Choose a reason for hiding this comment

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

This changes the hash only, that's done client side no?

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 in this case we already are handling it in Angular, and we update the browser path when different tabs are clicked...

Copy link
Contributor

Choose a reason for hiding this comment

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

@mbudm it's a call to the server, see app/assets/javascripts/darkswarm/services/order_cycle.js.coffee
that goes to app/controllers/shop_controller.rb#order_cycle

Copy link
Contributor

Choose a reason for hiding this comment

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

we already are handling it in Angular

Matt, where? I think the actual route is on the server side, isnt it?

Copy link
Author

Choose a reason for hiding this comment

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

@luisramos0 the shop is essentially a SPA. Changing tabs is done with an anchor URL. See: https://github.com/openfoodfoundation/openfoodnetwork/blob/master/app/views/shopping_shared/_tabs.html.haml#L12

So changing the hash via javascript is the same as a click on <a href="#/shop">.

The network traffic shows this when changing tabs - it's all XHR (and one PNG). This network traffic is initiated from javascript by the OrderCycleCtrl when the shop tab is rendered. Those calls every time are probably not necessary most of the time as I doubt shops product change that quickly but thats a future optimisation.
ofn-shop-network-calls-720

What I did find in my travels is that in the admin we are using a tab directive, that has a method for selecting tabs, and broadcasts this change to any listeners. Perhaps this should also be used in the shop as it's nicer than changing the location directly. As an aside this directive uses the same $location.path('tabname') approach (as do a few other examples).

Copy link
Contributor

Choose a reason for hiding this comment

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

Hey Steve, that tabset directive is used in the frontoffice, it's used in the tabs on the user account page.

That tbaset code is a good example where I dont think we should go. Even on this shopfront page, I think it's best to avoid SPA altogether and keep with rails routes. Simple, it works, we dont need the extra complication.

But I understand that the current routing is done through the fragment/hash already. Because of that I dont think this is a major problem. I think the routing needs to be done at least in an isolated manner though, for example, in a specific service? Having the OrderCyycleChangeController know about routes looks wrong.

Not to mention the fact that we are talking about an angular file with two controllers defined in it...

I am just sharing my concerns and why I think we should make it a little better as we change it.

Copy link
Author

Choose a reason for hiding this comment

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

OK I found a nicer way - using the existing broadcast event of an orderCycleChange, which is picked by by the shop tab controller - generically called PageSelectionCtrl (rename?). To make this work I also removed what seems to be a redundant piece of code that fires this event whenever the OrderCycleCtrl is instantiated.

I suspect that this was a way to open the select box (clue is in the "openTrigger" trigger argument) to prompt users to select an order, which has been superceded by the red style applied to the order cycle selector:

Screen Shot 2020-07-15 at 3 36 48 pm

The commits around this area in git blame seem to cover functionality that still works with this code removed:
Screen Shot 2020-07-15 at 3 59 06 pm

There is defs a lot more refactoring potential in the shop, and probably a lot more redundant code that is hard to spot at first - it is also high value given this is the crucial customer facing experience. Possibly a candidate if we look at a library upgrade/change.

Copy link
Contributor

@luisramos0 luisramos0 left a comment

Choose a reason for hiding this comment

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

Nice, thanks for investigating Steve. Much better 👍

$timeout =>
$rootScope.$broadcast 'orderCycleSelected'
if !$scope.OrderCycle.selected()
$("#order_cycle_id").trigger("openTrigger")
Copy link
Contributor

Choose a reason for hiding this comment

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

there's something related to tooltips with this openTrigger, I dont know what is it...
here: /javascripts/darkswarm/darkswarm.js.coffee

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm pretty sure the OpenTrigger bit relates to a popover message that has now been removed, so let's ditch that part 👍

I think we still need the timeout and $rootScope.$broadcast 'orderCycleSelected' bit here though, it's used elsewhere. It triggers the shop's list of product filters to be loaded or refreshed for example (which is causing the 2 failing specs in the current build).

See: app/assets/javascripts/darkswarm/controllers/products_controller.js.coffee:16. Maybe it could be adjusted to fit your needs here?

Copy link
Contributor

Choose a reason for hiding this comment

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

ok, so lets remove that line in javascripts/darkswarm/darkswarm.js.coffee as well?

Copy link
Author

Choose a reason for hiding this comment

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

@Matt-Yorkley The problem with broadcasting 'orderCycleSelected' every time the OrderCycleCtrl is initiated is that this occurs every time a tab is loaded, so the user is always navigated to /shop.

So I've pushed an alternate way of ensuring update_filters() is run after a product load, using a watcher on the Product.loading flag.

Alternatively I could leave this timeout and use a different event, (naming is hard) and fire update_filter() from that event:

  # Timeout forces this to be evaluated after everything is loaded
  # This is a hack. We should probably write our own "popover" directive
  # That takes an expression instead of a trigger, and binds to that
  $timeout =>
    $rootScope.$broadcast 'orderCycleChangeReady'

@luisramos0 I also removed the other redundant tooltipProvider reference you mentioned.

Copy link
Author

@mbudm mbudm Jul 16, 2020

Choose a reason for hiding this comment

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

Also I'm getting different results with tests locally so will see how this lates commit goes, in theory it should pass as the filters are now updating again.

EDIT: they passed, just the variant_override problem spec failing

Copy link
Contributor

Choose a reason for hiding this comment

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

I've pushed an alternate way of ensuring update_filters() is run...

I think this is moving in the right direction, but in this case the value of Product.loading can change many times while the user is scrolling through the product list, submitting a search, selecting filters etc, but we need to be calling update_filters() only once, when an order cycle is selected...

Copy link
Author

Choose a reason for hiding this comment

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

Yes I was afraid of that, hence the alternate suggestion. My question on update_filters being triggered too often is, can. the filters change as more products are loaded? It seems likely as I assume the filters shown are only relevant to what is loaded so far...?

Nonetheless.

  • Update_filters() is called when the order cycle changes, via the existing broadcast.
  • The remaining problem is when the page is loaded and an order_cycle is already selected.

I'll look at this next week, there must be some safe/more appropriate hook for calling update_filters, my alternate solution, reinstating the timeout in the unrelated OrderCycleCtrl doesn't feel like the best option - it smells like potential edge case race condition.

Copy link
Contributor

Choose a reason for hiding this comment

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

The filters are per order cycle, so they won't change when more products are loaded for the same OC.

I'm sure there's a nice way to resolve it 👍

Also remove the other redundant trigger
@daniellemoorhead
Copy link
Contributor

Hey @mbudm any movement on this PR?

@mbudm
Copy link
Author

mbudm commented Aug 27, 2020

Keeping it simple, using a $timeout to trigger the update filters after the Products Controller is loaded.

@daniellemoorhead
Copy link
Contributor

daniellemoorhead commented Aug 28, 2020

@Matt-Yorkley @luisramos0 @filipefurtad0 would be great to get this in the next release if we can - any chance of getting the code reviews and testing done before then? Isn't difficult to test and this is the second round of code review so hopefully will be quick 🤞

Filipe, I'll be asleep by the time this gets reviewed (if that's today) thus why I'm hoping you can do it 🙏

@luisramos0
Copy link
Contributor

I am ok with this if it works 👍

@daniellemoorhead
Copy link
Contributor

@luisramos0 do we need to wait for @Matt-Yorkley to re-review or is it a small enough change to come straight back to testing?

Copy link
Member

@mkllnk mkllnk left a comment

Choose a reason for hiding this comment

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

This seems to do the trick. 👍

@daniellemoorhead daniellemoorhead added pr-staged-fr staging.coopcircuits.fr pr-staged-es and removed pr-staged-fr staging.coopcircuits.fr labels Sep 4, 2020
@daniellemoorhead daniellemoorhead self-assigned this Sep 4, 2020
@daniellemoorhead
Copy link
Contributor

daniellemoorhead commented Sep 4, 2020

Testing notes:

Tested on:

  • Mac / Chrome + Safari
  • Windows 10 / Chrome

Works a treat:

  • When on another tab, I select the order cycle for the first time. It takes me to the shop tab.
  • I then go to another tab, and then select the different order cycle option. It takes me to the shop tab.
  • Same happens when I add products, go to another tab, change order cycle, then confirm the change will remove the products from my cart. It takes me to the shop tab.

Good to go! @luisramos0 perhaps this can go into the release for next week?

@luisramos0 luisramos0 merged commit 4fe24da into openfoodfoundation:master Sep 4, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Auto redirect from Home to Shop when Order Cycle is selected
6 participants