-
Notifications
You must be signed in to change notification settings - Fork 115
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
[Shipping labels] Get account settings and use in UI custom packages #14499
[Shipping labels] Get account settings and use in UI custom packages #14499
Conversation
@@ -141,6 +139,24 @@ final class WooShippingAddCustomPackageViewModel: ObservableObject { | |||
} | |||
return true | |||
} | |||
|
|||
func loadStoreOptions() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Loading and using the store options so we get fresh/live weight and dimensions units.
What should we do if we do not get them, should we show some kind of error and ability to load again? I added a simple fresh button to trigger it again. In a separate PR I am preparing the logic for persisting that data in CoreData but I am wondering if we should use persisted/saved data or always fetch fresh?
|
ScrollViewReader { proxy in | ||
ScrollView { | ||
VStack(alignment: .leading, spacing: Constants.defaultVerticalSpacing) { | ||
HStack { | ||
Text(Localization.packageType) | ||
.font(.subheadline) | ||
Spacer() | ||
} | ||
packageTypeSelectionView | ||
VStack { | ||
AdaptiveStack(spacing: 8) { | ||
ForEach(WooShippingPackageUnitType.dimensionUnits, id: \.self) { dimensionUnit in | ||
unitInputView(for: dimensionUnit, unit: customPackageViewModel.dimensionsUnit) | ||
if let storeOptions = customPackageViewModel.storeOptions { | ||
ScrollViewReader { proxy in | ||
ScrollView { | ||
VStack(alignment: .leading, spacing: Constants.defaultVerticalSpacing) { | ||
HStack { | ||
Text(Localization.packageType) | ||
.font(.subheadline) | ||
Spacer() | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part looks like a lot of code change but basically if/else is added to show the content if the store options are loaded and show loading view if not.
From the discussion in Slack (p1732618862774319-slack-C02KUCFCSFP):
@bozidarsevo let me know when you're ready for me to take another look at this! Thanks for raising the question about storage. 👍 FWIW, I think if we fail to fetch them from remote at this point we should just fall back to the store settings, which are updated each time the app launches. |
Sure! I will do few updates regarding those couple of things and then reassign when PR is ready for another look. Thanks! |
…packages # Conflicts: # WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShippingCreateLabelsViewModel.swift
} | ||
|
||
@ViewBuilder | ||
private var expandableBottomSheet: some View { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Separated UI code into helper properties so it can be updated easier in the future
@Published var storeOptions: ShippingLabelStoreOptions? | ||
@Published var isLoadingStoreOptions: Bool = false | ||
|
||
func loadStoreOptions() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Loading and storing the store options so we get fresh/live weight and dimensions units.
That property is then being passed to all UI in shipping labels flow
@@ -37,109 +37,37 @@ struct WooShippingCreateLabelsView: View { | |||
var body: some View { | |||
NavigationStack { | |||
ScrollView { | |||
VStack(spacing: Layout.verticalSpacing) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part looks like a lot of code change but basically if/else is added to show the content if the store options are loaded and show loading view if not.
@bozidarsevo I'm having some trouble following all of the changes in this PR. In particular, I appreciate having |
I will try something. 👍 |
@@ -56,7 +56,12 @@ struct WooShippingCreateLabelsView: View { | |||
WooShippingServiceView(viewModel: shippingService) | |||
.padding(.horizontal, -16) | |||
} else { | |||
WooShippingPackageAndRatePlaceholder() | |||
if let storeOptions = viewModel.storeOptions { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now much cleaner and easier to review 🥳
I put the check for the storeOptions just here so that just this view is not showed before we get storeOptions so later on we can do other changes if needed 🕺 @rachelmcr
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm approving this PR because all of these changes work as intended and, given our sync discussion, I don't want to block us from moving forward.
However, I don't think the loading indicator is a great experience on the main screen here. I think it's worth taking a different approach that will allow us to immediately load the settings we already have for the store and refresh them in place. That said, I'm ok if you want to merge these changes now and revisit the behavior after M1 is complete and we do a full design review.
@@ -1,4 +1,5 @@ | |||
import SwiftUI | |||
import enum Yosemite.WooShippingAction |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: This import isn't needed in the view here
self.weightUnit = weightUnit ?? "" | ||
storeOptions: ShippingLabelStoreOptions, | ||
stores: StoresManager = ServiceLocator.stores) { | ||
self.storeOptions = storeOptions |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: What do you think of keeping the dimensionsUnit
and weightUnit
properties and just using the ShippingLabelStoreOptions
to set those values? No worries if you want to leave it like this, but the other approach felt a little clearer to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After reading through the changes in WooShippingCreateLabelsViewModel
I'm more inclined to keep these properties and the init parameters the same here. That way we only need to pass in the data that we're actually using, which can simplify the fallbacks we need to set if the request to load the store options fails. Just a thought, not a blocking suggestion!
WooShippingPackageAndRatePlaceholder(storeOptions: storeOptions) | ||
} | ||
else { | ||
loadingView |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having a loading view here feels a little odd to me, especially because the data isn't needed for this placeholder view — it just needs to be passed down to its child views.
What do you think about setting the store options as an environment variable, instead? That way we could initialize this view without the storeOptions
property necessarily being loaded yet, and once we have those options we can pass them to the views that need them. Or we could use some other approach to set/update the store options after initialization.
This would also allow us to use the fallbacks from ServiceLocator
immediately, and then update them in place if we get fresh data from the backend.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We will still need to do pick a place where we want to trigger the fetching/loading the data. And in theory that loading could take a longer time and user could get to a state in UI that needs this data and we need to show that something will be there.
For example, I do not show any loading indicators and user gets to Add Package screen and Custom tab. I still do not have the store options therefor I do not have the dimension and weight units. Should I then show some indicator that something is loading? Because at that state we do not have all the data needed to show the UI and we cannot just show blank white screen.
If I use environment variable again we need to decide when will we trigger fetching/loading so it would basically be the same just a different implementation.
I can do it in a way that I trigger the initial load when we enter label creation flow and then if some part of the app needs the store options and they are not loaded yet I will show some loading indication there, basically showing the loading at the last possible state to avoid blocking user as little as possible.
I will see if I will do that change here or in a separate PR when I see how big of a change would it be.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated:
Basically loading store options (account settings) when we enter the label creation screen, but showing loading indicator only if I get to the part of the UI that needs the store options and they are not loaded. That way in most cases users will not even see the loading indicator. 😊
Updated screen recordings in description
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For example, I do not show any loading indicators and user gets to Add Package screen and Custom tab. I still do not have the store options therefor I do not have the dimension and weight units. Should I then show some indicator that something is loading?
Why not use the dimension and weight units from ServiceLocator.shippingSettingsService
by default? We can still trigger fetching/loading the account settings when the shipping label flow begins, and update those units in place if they are different from the default we've used.
@@ -60,6 +73,18 @@ struct WooShippingAddPackageView: View { | |||
.task { | |||
packagesRepository.loadPackages() | |||
} | |||
.onAppear() { | |||
if let storeOptions = createLabelsViewModel.storeOptions { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If there is storeOptions at appear we use it, if not we check for updates for storeOptions and update when we receive changes for it.
Closes #14484
Description
Testing information
Screenshots
Flow when store options are not loaded yet and users taps fast thru UI and gets to UI part that needs it:
Simulator.Screen.Recording.-.iPhone.16.Pro.-.2024-11-29.at.11.10.27.mp4
Store options already loaded and UI has everything needed:
Simulator.Screen.Recording.-.iPhone.16.Pro.-.2024-11-29.at.11.10.37.mp4
RELEASE-NOTES.txt
if necessary.Reviewer (or Author, in the case of optional code reviews):
Please make sure these conditions are met before approving the PR, or request changes if the PR needs improvement: