-
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
Changes from 21 commits
ba8743a
a18d6f1
a037029
a8368a2
cc3ec75
55cf241
187c375
6e8ea7c
ac1f13d
893a098
4d4499c
c046426
f592b08
9d0a13d
38ceb16
94a57cb
e12987f
21c746d
5433fc5
7327882
1aaccc0
bc134ba
5c3fbfb
7eeb80e
a910f78
c17af3e
b500b78
66705df
103ba97
191901b
31b1e6f
64c956b
04fcea8
7a41f8c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,6 +4,7 @@ import Yosemite | |
final class WooShippingAddCustomPackageViewModel: ObservableObject { | ||
private let stores: StoresManager | ||
private let siteID: Int64 | ||
let storeOptions: ShippingLabelStoreOptions | ||
|
||
// Holds values for all dimension input fields. | ||
// Using a dictionary so we can easily add/remove new types | ||
|
@@ -14,24 +15,15 @@ final class WooShippingAddCustomPackageViewModel: ObservableObject { | |
// Holds value for toggle that determines if we are showing button for saving the template | ||
@Published var showSaveTemplate: Bool = false | ||
@Published var packageTemplateName: String = "" | ||
// The dimension unit used in the store (e.g. "in") | ||
let dimensionsUnit: String | ||
// The weight unit used in the store (e.g. "kg") | ||
let weightUnit: String | ||
@Published var packagesRepository: WooShippingPackagesRepositoryProtocol | ||
|
||
// MARK: Initialization | ||
|
||
init(siteID: Int64 = ServiceLocator.stores.sessionManager.defaultStoreID ?? 0, | ||
dimensionsUnit: String? = ServiceLocator.shippingSettingsService.dimensionUnit, | ||
weightUnit: String? = ServiceLocator.shippingSettingsService.weightUnit, | ||
stores: StoresManager = ServiceLocator.stores, | ||
packagesRepository: WooShippingPackagesRepositoryProtocol) { | ||
self.dimensionsUnit = dimensionsUnit ?? "" | ||
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 commentThe reason will be displayed to describe this comment to others. Learn more. nit: What do you think of keeping the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. After reading through the changes in |
||
self.stores = stores | ||
self.siteID = siteID | ||
self.packagesRepository = packagesRepository | ||
} | ||
|
||
// Field values are invalid if one of them is empty | ||
|
@@ -52,15 +44,15 @@ final class WooShippingAddCustomPackageViewModel: ObservableObject { | |
return validFieldsCount != keysToCheck.count | ||
} | ||
|
||
private var packageDataFromCurrentData: WooShippingPackageDataRepresentable { | ||
private var packageDataFromCurrentData: WooShippingPackageDataRepresentable? { | ||
return WooShippingPackageData(id: UUID().uuidString, | ||
name: packageTemplateName, | ||
length: fieldValues[.length] ?? "", | ||
width: fieldValues[.width] ?? "", | ||
height: fieldValues[.height] ?? "", | ||
dimensionsUnit: dimensionsUnit, | ||
dimensionsUnit: storeOptions.dimensionUnit, | ||
weight: fieldValues[.weight] ?? "", | ||
weightUnit: weightUnit, | ||
weightUnit: storeOptions.weightUnit, | ||
source: .custom, | ||
packageType: packageType.rawValue) | ||
} | ||
|
@@ -97,11 +89,39 @@ final class WooShippingAddCustomPackageViewModel: ObservableObject { | |
return .failure(WooShippingAddCustomPackageViewModel.Error.packageDataNotValid) | ||
} | ||
|
||
let result = await packagesRepository.saveCustomPackage(packageData, | ||
dimensionsUnit: dimensionsUnit, | ||
weightUnit: weightUnit, | ||
siteID: siteID, | ||
stores: stores) | ||
let customPackage = WooShippingCustomPackage(id: "", | ||
name: packageData.name, | ||
rawType: packageData.packageType, | ||
dimensions: "\(packageData.length) x \(packageData.width) x \(packageData.height)", | ||
boxWeight: Double(packageData.weight) ?? 0) | ||
let result: Result<WooShippingPackageDataRepresentable, Error> = await withCheckedContinuation { continuation in | ||
let action = WooShippingAction.createPackage(siteID: siteID, | ||
customPackage: customPackage, | ||
predefinedOption: nil) { [weak self] result in | ||
switch result { | ||
case let .success(packages): | ||
guard let self, let savedPackage = packages.customPackages.first(where: { $0.name == customPackage.name }) else { | ||
return continuation.resume(returning: .failure(WooShippingAddCustomPackageViewModel.Error.failedSavingTemplate)) | ||
} | ||
let packageData = WooShippingPackageData(id: savedPackage.id, | ||
name: savedPackage.name, | ||
length: savedPackage.getLength().description, | ||
width: savedPackage.getWidth().description, | ||
height: savedPackage.getHeight().description, | ||
dimensionsUnit: storeOptions.dimensionUnit, | ||
weight: savedPackage.boxWeight.description, | ||
weightUnit: storeOptions.weightUnit, | ||
source: .custom, | ||
packageType: savedPackage.rawType) | ||
continuation.resume(returning: .success(packageData)) | ||
case let .failure(error): | ||
DDLogError("⛔️ Error saving custom package with WCShip: \(error)") | ||
continuation.resume(returning: .failure(WooShippingAddCustomPackageViewModel.Error.failedSavingTemplate)) | ||
} | ||
} | ||
stores.dispatch(action) | ||
} | ||
|
||
switch result { | ||
case .success(let success): | ||
return .success(success) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 commentThe reason will be displayed to describe this comment to others. Learn more. This is now much cleaner and easier to review 🥳 |
||
WooShippingPackageAndRatePlaceholder(storeOptions: storeOptions) | ||
} | ||
else { | ||
loadingView | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 This would also allow us to use the fallbacks from There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 commentThe 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 commentThe reason will be displayed to describe this comment to others. Learn more.
Why not use the dimension and weight units from |
||
} | ||
} | ||
} | ||
.padding(16) | ||
|
@@ -151,6 +156,28 @@ struct WooShippingCreateLabelsView: View { | |
} | ||
} | ||
} | ||
.onAppear() { | ||
guard viewModel.storeOptions == nil else { return } | ||
viewModel.loadStoreOptions() | ||
} | ||
} | ||
|
||
private var loadingView: some View { | ||
HStack { | ||
Spacer() | ||
if viewModel.isLoadingStoreOptions { | ||
ActivityIndicator(isAnimating: .constant(true), style: .large) | ||
} | ||
else { | ||
Button { | ||
viewModel.loadStoreOptions() | ||
} label: { | ||
Image(systemName: "arrow.trianglehead.counterclockwise") | ||
} | ||
} | ||
Spacer() | ||
} | ||
.padding() | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -97,6 +97,40 @@ final class WooShippingCreateLabelsViewModel: ObservableObject { | |
/// If the label purchase is in progress. | ||
@Published private(set) var isPurchasingLabel: Bool = false | ||
|
||
@Published var storeOptions: ShippingLabelStoreOptions? | ||
@Published var isLoadingStoreOptions: Bool = false | ||
|
||
func loadStoreOptions() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||
guard isLoadingStoreOptions == false, | ||
let siteID = ServiceLocator.stores.sessionManager.defaultStoreID else { return } | ||
|
||
isLoadingStoreOptions = true | ||
|
||
let action = WooShippingAction.loadAccountSettings(siteID: siteID) { result in | ||
switch result { | ||
case .success(let settings): | ||
self.storeOptions = settings.storeOptions | ||
case .failure(let error): | ||
DDLogError("⛔️ Error loading account settings: \(error)") | ||
// fallback to store settings | ||
let shippingSettingsService = ServiceLocator.shippingSettingsService | ||
let currencySettings = ServiceLocator.currencySettings | ||
let currencySymbol = currencySettings.symbol(from: currencySettings.currencyCode) | ||
let originCountry = SiteAddress().countryCode.rawValue | ||
if let dimensionUnit = shippingSettingsService.dimensionUnit, | ||
let weightUnit = shippingSettingsService.weightUnit { | ||
let fallbackStoreOptions = ShippingLabelStoreOptions(currencySymbol: currencySymbol, | ||
dimensionUnit: dimensionUnit, | ||
weightUnit: weightUnit, | ||
originCountry: originCountry) | ||
self.storeOptions = fallbackStoreOptions | ||
} | ||
} | ||
self.isLoadingStoreOptions = false | ||
} | ||
ServiceLocator.stores.dispatch(action) | ||
} | ||
|
||
/// Closure to execute after the label is successfully purchased. | ||
let onLabelPurchase: ((_ markOrderComplete: Bool) -> Void)? | ||
|
||
|
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