@@ -9,6 +9,10 @@ import protocol Storage.StorageManagerType
99///
1010final class ShippingLabelPackagesFormViewModel : ObservableObject {
1111
12+ /// Completion callback
13+ ///
14+ typealias Completion = ( _ selectedPackages: [ ShippingLabelPackageAttributes ] ) -> Void
15+
1216 var foundMultiplePackages : Bool {
1317 selectedPackages. count > 1
1418 }
@@ -17,13 +21,34 @@ final class ShippingLabelPackagesFormViewModel: ObservableObject {
1721 ///
1822 @Published private( set) var itemViewModels : [ ShippingLabelPackageItemViewModel ] = [ ]
1923
24+ /// Whether Done button on Package Details screen should be enabled.
25+ ///
26+ @Published private( set) var doneButtonEnabled : Bool = false
27+
2028 private let order : Order
2129 private let stores : StoresManager
2230 private let storageManager : StorageManagerType
2331 private var resultsControllers : ShippingLabelPackageDetailsResultsControllers ?
32+ private let onCompletion : Completion
2433
2534 private var cancellables : Set < AnyCancellable > = [ ]
2635
36+ /// Validation states of all items.
37+ ///
38+ private var packagesValidation : [ String : Bool ] = [ : ] {
39+ didSet {
40+ configureDoneButton ( )
41+ }
42+ }
43+
44+ /// List of packages that are validated.
45+ ///
46+ private var validatedPackages : [ ShippingLabelPackageAttributes ] {
47+ itemViewModels. compactMap {
48+ $0. validatedPackageAttributes
49+ }
50+ }
51+
2752 /// List of selected package with basic info.
2853 ///
2954 @Published private var selectedPackages : [ ShippingLabelPackageAttributes ] = [ ]
@@ -39,6 +64,7 @@ final class ShippingLabelPackagesFormViewModel: ObservableObject {
3964 init ( order: Order ,
4065 packagesResponse: ShippingLabelPackagesResponse ? ,
4166 selectedPackages: [ ShippingLabelPackageAttributes ] ,
67+ onCompletion: @escaping Completion ,
4268 formatter: CurrencyFormatter = CurrencyFormatter ( currencySettings: ServiceLocator . currencySettings) ,
4369 stores: StoresManager = ServiceLocator . stores,
4470 storageManager: StorageManagerType = ServiceLocator . storageManager,
@@ -47,6 +73,7 @@ final class ShippingLabelPackagesFormViewModel: ObservableObject {
4773 self . stores = stores
4874 self . storageManager = storageManager
4975 self . selectedPackages = selectedPackages
76+ self . onCompletion = onCompletion
5077
5178 configureResultsControllers ( )
5279 syncProducts ( )
@@ -55,9 +82,17 @@ final class ShippingLabelPackagesFormViewModel: ObservableObject {
5582 configureItemViewModels ( order: order, packageResponse: packagesResponse)
5683 }
5784
85+ func confirmPackageSelection( ) {
86+ onCompletion ( validatedPackages)
87+ }
88+ }
89+
90+ // MARK: - Helper methods
91+ //
92+ private extension ShippingLabelPackagesFormViewModel {
5893 /// If no initial packages was input, set up default package from last selected package ID and all order items.
5994 ///
60- private func configureDefaultPackage( ) {
95+ func configureDefaultPackage( ) {
6196 guard selectedPackages. isEmpty,
6297 let selectedPackageID = resultsControllers? . accountSettings? . lastSelectedPackageID else {
6398 return
@@ -69,7 +104,7 @@ final class ShippingLabelPackagesFormViewModel: ObservableObject {
69104
70105 /// Set up item view models on change of products and product variations.
71106 ///
72- private func configureItemViewModels( order: Order , packageResponse: ShippingLabelPackagesResponse ? ) {
107+ func configureItemViewModels( order: Order , packageResponse: ShippingLabelPackagesResponse ? ) {
73108 $selectedPackages. combineLatest ( $products, $productVariations)
74109 . map { selectedPackages, products, variations -> [ ShippingLabelPackageItemViewModel ] in
75110 return selectedPackages. map { details in
@@ -87,13 +122,14 @@ final class ShippingLabelPackagesFormViewModel: ObservableObject {
87122 }
88123 . sink { [ weak self] viewModels in
89124 self ? . itemViewModels = viewModels
125+ self ? . observeItemViewModels ( )
90126 }
91127 . store ( in: & cancellables)
92128 }
93129
94130 /// Update selected packages when user switch any package.
95131 ///
96- private func switchPackage( currentID: String , newPackage: ShippingLabelPackageAttributes ) {
132+ func switchPackage( currentID: String , newPackage: ShippingLabelPackageAttributes ) {
97133 selectedPackages = selectedPackages. map { package in
98134 if package . packageID == currentID {
99135 return newPackage
@@ -103,7 +139,25 @@ final class ShippingLabelPackagesFormViewModel: ObservableObject {
103139 }
104140 }
105141
106- private func configureResultsControllers( ) {
142+ /// Observe validation state of each package and save it by package ID.
143+ ///
144+ func observeItemViewModels( ) {
145+ itemViewModels. forEach { item in
146+ item. $isValidTotalWeight
147+ . sink { [ weak self] isValid in
148+ self ? . packagesValidation [ item. selectedPackageID] = isValid
149+ }
150+ . store ( in: & cancellables)
151+ }
152+ }
153+
154+ /// Disable Done button if any of the package validation fails.
155+ ///
156+ func configureDoneButton( ) {
157+ doneButtonEnabled = packagesValidation. first ( where: { $0. value == false } ) == nil
158+ }
159+
160+ func configureResultsControllers( ) {
107161 resultsControllers = ShippingLabelPackageDetailsResultsControllers ( siteID: order. siteID,
108162 orderItems: order. items,
109163 storageManager: storageManager,
0 commit comments