Skip to content

Commit a41b455

Browse files
authored
Shipping Labels: Load shipments from storage on purchase form (#15919)
2 parents 233057b + 1bbf5da commit a41b455

File tree

9 files changed

+316
-386
lines changed

9 files changed

+316
-386
lines changed

RELEASE-NOTES.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
- [*] POS: a POS tab in the tab bar is now available in the app for stores in countries eligible for Point of Sale, instead of the tab is only shown when the store is eligible for POS. [https://github.com/woocommerce/woocommerce-ios/pull/15918]
1111
- [*] Shipping Labels: Display base rate on selected shipping service cards [https://github.com/woocommerce/woocommerce-ios/pull/15916]
1212
- [*] Shipping Labels: Update mark order completed toggle on purchase form [https://github.com/woocommerce/woocommerce-ios/pull/15917]
13+
- [*] Shipping Labels: Optimize data loading on purchase form [https://github.com/woocommerce/woocommerce-ios/pull/15919]
1314
- [internal] Optimized assets for app size reduction [https://github.com/woocommerce/woocommerce-ios/pull/15881]
1415

1516
22.8

WooCommerce/Classes/ViewModels/Order Details/OrderDetailsResultsControllers.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ private extension OrderDetailsResultsControllers {
217217
do {
218218
try shipmentResultsController.performFetch()
219219
} catch {
220-
DDLogError("⛔️ Unable to fetch Order Statuses: \(error)")
220+
DDLogError("⛔️ Unable to fetch shipments: \(error)")
221221
}
222222
}
223223

WooCommerce/Classes/ViewModels/Order Details/OrderDetailsViewModel.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,12 @@ final class OrderDetailsViewModel {
117117
///
118118
let dataSource: OrderDetailsDataSource
119119

120+
/// The eligibility check for Woo Shipping can be updated late due to being async
121+
/// So the additional check for shipments determines if the new form should be displayed.
122+
var shouldNavigateToNewShippingLabelFlow: Bool {
123+
dataSource.isEligibleForWooShipping || dataSource.shipments.isNotEmpty
124+
}
125+
120126
private(set) lazy var editNoteViewModel: EditCustomerNoteViewModel = {
121127
return EditCustomerNoteViewModel(order: order)
122128
}()

WooCommerce/Classes/ViewRelated/Orders/Order Details/OrderDetailsViewController.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ private extension OrderDetailsViewController {
427427
}
428428

429429
func navigateToCreateShippingLabelForm(preSelection: WooShippingCreateLabelSelection? = nil) {
430-
guard viewModel.dataSource.isEligibleForWooShipping else {
430+
guard viewModel.shouldNavigateToNewShippingLabelFlow else {
431431
// Navigate to legacy shipping label creation form if Woo Shipping extension is not supported.
432432
let shippingLabelFormVC = ShippingLabelFormViewController(order: viewModel.order)
433433
shippingLabelFormVC.onLabelPurchase = { [weak self] isOrderComplete in

WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/WooShippingSplitShipmentsView.swift

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -517,19 +517,21 @@ fileprivate extension WooShippingSplitShipmentsView {
517517

518518
#if DEBUG
519519
#Preview {
520-
WooShippingSplitShipmentsView(viewModel: WooShippingSplitShipmentsViewModel(order: ShippingLabelSampleData.sampleOrder(),
521-
config: ShippingLabelSampleData.sampleWooShippingConfig(),
522-
items: [ShippingLabelPackageItem(productOrVariationID: 1,
523-
orderItemID: 12,
524-
name: "Shirt",
525-
weight: 0.5,
526-
quantity: 2,
527-
value: 9.99,
528-
dimensions: ProductDimensions(length: "",
529-
width: "",
530-
height: ""),
531-
attributes: [],
532-
imageURL: nil)]),
533-
onShipmentUpdate: { _ in })
520+
WooShippingSplitShipmentsView(viewModel: WooShippingSplitShipmentsViewModel(
521+
order: ShippingLabelSampleData.sampleOrder(),
522+
remoteShipments: [],
523+
items: [ShippingLabelPackageItem(productOrVariationID: 1,
524+
orderItemID: 12,
525+
name: "Shirt",
526+
weight: 0.5,
527+
quantity: 2,
528+
value: 9.99,
529+
dimensions: ProductDimensions(length: "",
530+
width: "",
531+
height: ""),
532+
attributes: [],
533+
imageURL: nil)]),
534+
onShipmentUpdate: { _ in }
535+
)
534536
}
535537
#endif

WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShipping Split Shipments/WooShippingSplitShipmentsViewModel.swift

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ final class WooShippingSplitShipmentsViewModel: ObservableObject {
112112
}
113113

114114
init(order: Order,
115-
config: WooShippingConfig?,
115+
remoteShipments: [WooShippingShipment],
116116
items: [ShippingLabelPackageItem],
117117
stores: StoresManager = ServiceLocator.stores,
118118
currencySettings: CurrencySettings = ServiceLocator.currencySettings,
@@ -122,7 +122,7 @@ final class WooShippingSplitShipmentsViewModel: ObservableObject {
122122
self.currencySettings = currencySettings
123123
self.shippingSettingsService = shippingSettingsService
124124

125-
let shipments = Self.createShipments(with: config,
125+
let shipments = Self.createShipments(with: remoteShipments,
126126
packageItems: items,
127127
currency: order.currency,
128128
currencySettings: currencySettings,
@@ -528,12 +528,12 @@ private extension WooShippingSplitShipmentsViewModel {
528528

529529
extension WooShippingSplitShipmentsViewModel {
530530

531-
private static func createShipments(with config: WooShippingConfig?,
531+
private static func createShipments(with remoteShipments: [WooShippingShipment],
532532
packageItems: [ShippingLabelPackageItem],
533533
currency: String,
534534
currencySettings: CurrencySettings,
535535
shippingSettingsService: ShippingSettingsService) -> [Shipment] {
536-
guard let config, config.shipments.isEmpty == false else {
536+
guard remoteShipments.isEmpty == false else {
537537
let contents = packageItems.map { item in
538538
CollapsibleShipmentItemCardViewModel(item: item, currency: currency)
539539
}
@@ -544,7 +544,7 @@ extension WooShippingSplitShipmentsViewModel {
544544
return [shipment]
545545
}
546546

547-
let shipments = config.shipments
547+
let shipments = remoteShipments
548548
.sorted(by: { $0.index.localizedStandardCompare($1.index) == .orderedAscending })
549549
.map { shipment in
550550
var shipmentContents = ShipmentContents()

WooCommerce/Classes/ViewRelated/Orders/Order Details/Shipping Labels/WooShipping Create Shipping Labels/WooShippingCreateLabelsViewModel.swift

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import WooFoundation
44
import Combine
55
import struct Networking.WooShippingAccountSettings
66
import enum Networking.DotcomError
7+
import protocol Storage.StorageManagerType
78

89
enum WooShippingCreateLabelSelection {
910
case shipment(index: Int)
@@ -31,6 +32,7 @@ final class WooShippingCreateLabelsViewModel: ObservableObject {
3132
private let itemsDataSource: WooShippingItemsDataSource
3233
private var destinationEmail: String?
3334
private let stores: StoresManager
35+
private let storageManager: StorageManagerType
3436
private let currencySettings: CurrencySettings
3537
private var subscriptions: Set<AnyCancellable> = []
3638
private let initialNoticeDelay: RunLoop.SchedulerTimeType.Stride
@@ -207,12 +209,24 @@ final class WooShippingCreateLabelsViewModel: ObservableObject {
207209

208210
let isOrderCompleted: Bool
209211

212+
/// Shipments Results Controller.
213+
///
214+
private lazy var shipmentResultsController: ResultsController<StorageWooShippingShipment> = {
215+
let predicate = NSPredicate(format: "siteID = %ld AND orderID = %ld",
216+
self.order.siteID,
217+
self.order.orderID)
218+
let descriptor = NSSortDescriptor(keyPath: \StorageWooShippingShipment.index, ascending: true)
219+
220+
return ResultsController<StorageWooShippingShipment>(storageManager: storageManager, matching: predicate, sortedBy: [descriptor])
221+
}()
222+
210223
/// Initialize the view model with or without an existing shipping label.
211224
init(order: Order,
212225
preselection: WooShippingCreateLabelSelection? = nil,
213226
currencySettings: CurrencySettings = ServiceLocator.currencySettings,
214227
shippingSettingsService: ShippingSettingsService = ServiceLocator.shippingSettingsService,
215228
stores: StoresManager = ServiceLocator.stores,
229+
storageManager: StorageManagerType = ServiceLocator.storageManager,
216230
analytics: Analytics = ServiceLocator.analytics,
217231
initialNoticeDelay: RunLoop.SchedulerTimeType.Stride = .seconds(2),
218232
onLabelPurchase: ((Bool) -> Void)? = nil) {
@@ -224,13 +238,14 @@ final class WooShippingCreateLabelsViewModel: ObservableObject {
224238
self.destinationEmail = order.shippingAddress?.email ?? order.billingAddress?.email
225239
self.shippingLines = order.shippingLines.map({ WooShipping_ShippingLineViewModel(shippingLine: $0, currency: order.currency) })
226240
self.stores = stores
241+
self.storageManager = storageManager
227242
self.analytics = analytics
228243
self.shippingSettingsService = shippingSettingsService
229244
self.initialNoticeDelay = initialNoticeDelay
230245
self.isOrderCompleted = order.status == .completed
231246

232247
let splitShipmentsViewModel = WooShippingSplitShipmentsViewModel(order: order,
233-
config: nil,
248+
remoteShipments: [],
234249
items: itemsDataSource.items,
235250
stores: stores)
236251
self.splitShipmentsViewModel = splitShipmentsViewModel
@@ -251,6 +266,7 @@ final class WooShippingCreateLabelsViewModel: ObservableObject {
251266
observeDestinationAddress()
252267
observeViewStates()
253268
observePaymentMethod()
269+
configureShipmentResultsController()
254270

255271
Task { @MainActor in
256272
await loadRequiredData()
@@ -281,19 +297,15 @@ final class WooShippingCreateLabelsViewModel: ObservableObject {
281297
}
282298
}
283299

284-
if shipments.contains(where: { $0.purchasedLabel == nil }) {
300+
if hasUnfulfilledShipments {
285301
group.addTask {
286302
await self.loadOriginAddresses()
287303
}
288304
}
289-
290-
group.addTask {
291-
await self.loadShipmentsInfo()
292-
}
293305
}
294306

295307
if isMissingStoreSettings ||
296-
originAddress.isEmpty {
308+
(originAddress.isEmpty && hasUnfulfilledShipments) {
297309
state = .missingRequiredData
298310
} else {
299311
state = .ready
@@ -438,33 +450,6 @@ private extension WooShippingCreateLabelsViewModel {
438450
}
439451
}
440452

441-
/// Loads shipment info from remote and creates view model for split shipments.
442-
///
443-
@MainActor
444-
func loadShipmentsInfo() async {
445-
let config: WooShippingConfig? = await withCheckedContinuation { continuation in
446-
let action = WooShippingAction.loadConfig(siteID: order.siteID,
447-
orderID: order.orderID) { result in
448-
switch result {
449-
case .success(let shipmentResponse):
450-
continuation.resume(returning: shipmentResponse)
451-
case .failure(let error):
452-
DDLogError("⛔️ Error loading config for Woo Shipping labels: \(error)")
453-
continuation.resume(returning: nil)
454-
}
455-
}
456-
stores.dispatch(action)
457-
}
458-
459-
if let config {
460-
splitShipmentsViewModel = WooShippingSplitShipmentsViewModel(order: order,
461-
config: config,
462-
items: itemsDataSource.items,
463-
stores: stores)
464-
shipments = splitShipmentsViewModel.shipments
465-
}
466-
}
467-
468453
/// Loads destination address of the order from remote.
469454
///
470455
func loadDestinationAddress() {
@@ -661,6 +646,33 @@ private extension WooShippingCreateLabelsViewModel {
661646
return $0.paymentMethodID == accountSettings?.selectedPaymentMethodID
662647
}
663648
}
649+
650+
func configureShipmentResultsController() {
651+
let reloadShipments = { [weak self] in
652+
guard let self else { return }
653+
let fetchedShipments = shipmentResultsController.fetchedObjects
654+
splitShipmentsViewModel = WooShippingSplitShipmentsViewModel(order: order,
655+
remoteShipments: fetchedShipments,
656+
items: itemsDataSource.items,
657+
stores: stores)
658+
shipments = splitShipmentsViewModel.shipments
659+
}
660+
661+
shipmentResultsController.onDidChangeContent = {
662+
reloadShipments()
663+
}
664+
665+
shipmentResultsController.onDidResetContent = {
666+
reloadShipments()
667+
}
668+
669+
do {
670+
try shipmentResultsController.performFetch()
671+
reloadShipments()
672+
} catch {
673+
DDLogError("⛔️ Unable to fetch shipments: \(error)")
674+
}
675+
}
664676
}
665677

666678
private extension WooShippingCreateLabelsViewModel {

0 commit comments

Comments
 (0)