@@ -13,14 +13,6 @@ final class ShippingLabelPackagesFormViewModel: ObservableObject {
1313 selectedPackages. count > 1
1414 }
1515
16- /// Message displayed on the Move Item action sheet.
17- ///
18- @Published private( set) var moveItemActionSheetMessage : String = " "
19-
20- /// Option buttons displayed on the Move Item action sheet.
21- ///
22- @Published private( set) var moveItemActionSheetButtons : [ ActionSheet . Button ] = [ ]
23-
2416 /// References of view models for child items.
2517 ///
2618 @Published private( set) var itemViewModels : [ ShippingLabelSinglePackageViewModel ] = [ ]
@@ -42,9 +34,9 @@ final class ShippingLabelPackagesFormViewModel: ObservableObject {
4234
4335 private var cancellables : Set < AnyCancellable > = [ ]
4436
45- /// Validation states of all items by index of each package.
37+ /// Validation states of all items by ID of each package.
4638 ///
47- private var packagesValidation : [ Int : Bool ] = [ : ] {
39+ private var packagesValidation : [ String : Bool ] = [ : ] {
4840 didSet {
4941 configureDoneButton ( )
5042 }
@@ -60,7 +52,11 @@ final class ShippingLabelPackagesFormViewModel: ObservableObject {
6052
6153 /// List of selected package with basic info.
6254 ///
63- @Published private var selectedPackages : [ ShippingLabelPackageAttributes ] = [ ]
55+ private var selectedPackages : [ ShippingLabelPackageAttributes ] = [ ] {
56+ didSet {
57+ configureItemViewModels ( order: order)
58+ }
59+ }
6460
6561 /// Products contained inside the Order and fetched from Core Data
6662 ///
@@ -120,88 +116,92 @@ private extension ShippingLabelPackagesFormViewModel {
120116 /// Set up item view models on change selected packages.
121117 ///
122118 func configureItemViewModels( order: Order ) {
123- $selectedPackages
124- . map { [ weak self] selectedPackages -> [ ShippingLabelSinglePackageViewModel ] in
125- guard let self = self else {
126- return [ ]
127- }
128- return selectedPackages. enumerated ( ) . map { index, details in
129- return . init( order: order,
130- orderItems: details. items,
131- packagesResponse: self . packagesResponse,
132- selectedPackageID: details. packageID,
133- totalWeight: details. totalWeight,
134- isOriginalPackaging: details. isOriginalPackaging,
135- onItemMoveRequest: { [ weak self] productOrVariationID, packageName in
136- self ? . updateMoveItemActionSheet ( for: productOrVariationID, from: details, packageIndex: index, packageName: packageName)
137- } ,
138- onPackageSwitch: { [ weak self] newPackage in
139- self ? . switchPackage ( currentPackage: details, newPackage: newPackage)
140- } ,
141- onPackagesSync: { [ weak self] packagesResponse in
142- self ? . packagesResponse = packagesResponse
143- self ? . onPackageSyncCompletion ( packagesResponse)
144- } )
119+ itemViewModels = selectedPackages. enumerated ( ) . map { index, details -> ShippingLabelSinglePackageViewModel in
120+ return . init( id: details. id,
121+ order: order,
122+ orderItems: details. items,
123+ packageNumber: index + 1 ,
124+ packagesResponse: self . packagesResponse,
125+ selectedPackageID: details. packageID,
126+ totalWeight: details. totalWeight,
127+ isOriginalPackaging: details. isOriginalPackaging,
128+ onItemMoveRequest: { [ weak self] in
129+ self ? . itemViewModels. forEach {
130+ $0. dismissPopover ( )
145131 }
132+ } ,
133+ onPackageSwitch: { [ weak self] newPackage in
134+ self ? . switchPackage ( currentPackage: details, newPackage: newPackage)
135+ } ,
136+ onPackagesSync: { [ weak self] packagesResponse in
137+ self ? . packagesResponse = packagesResponse
138+ self ? . onPackageSyncCompletion ( packagesResponse)
139+ } )
140+ }
141+
142+ // We need the updated `itemViewModels` to get package names for selection,
143+ // so we have to update buttons after creating the view models.
144+ itemViewModels. enumerated ( ) . forEach { index, model in
145+ guard let details = selectedPackages. first ( where: { $0. id == model. id } ) else {
146+ return
146147 }
147- . sink { [ weak self] viewModels in
148- self ? . itemViewModels = viewModels
149- self ? . observeItemViewModels ( )
150- }
151- . store ( in: & cancellables)
148+ let actionSheetButtons = moveItemActionButtons ( for: details, packageIndex: index)
149+ model. updateActionSheetButtons ( actionSheetButtons)
150+ }
151+ observeItemViewModels ( )
152152 }
153153
154154 /// Update title and buttons for the Move Item action sheet.
155155 ///
156- func updateMoveItemActionSheet( for productOrVariationID: Int64 ,
157- from currentPackage: ShippingLabelPackageAttributes ,
158- packageIndex: Int ,
159- packageName: String ) {
160- moveItemActionSheetMessage = String ( format: Localization . moveItemActionSheetMessage, packageIndex + 1 , packageName)
161- moveItemActionSheetButtons = {
162- var buttons : [ ActionSheet . Button ] = [ ]
163-
164- // Add options to move to other packages.
165- for (index, package ) in selectedPackages. enumerated ( ) {
166- guard !package . isOriginalPackaging else {
167- continue
168- }
169- if index != packageIndex {
170- guard let name = itemViewModels [ safe: index] ? . packageName else {
156+ func moveItemActionButtons( for currentPackage: ShippingLabelPackageAttributes ,
157+ packageIndex: Int ) -> [ String : [ ActionSheet . Button ] ] {
158+ var actionButtons : [ String : [ ActionSheet . Button ] ] = [ : ]
159+ currentPackage. items
160+ . forEach { item in
161+ var buttons : [ ActionSheet . Button ] = [ ]
162+
163+ // Add options to move to other packages.
164+ for (index, package ) in selectedPackages. enumerated ( ) {
165+ guard !package . isOriginalPackaging else {
171166 continue
172167 }
173- let packageTitle = String ( format: Localization . packageName, index + 1 , name)
174- buttons. append ( . default( Text ( packageTitle) , action: { [ weak self] in
175- ServiceLocator . analytics. track ( . shippingLabelItemMoved, withProperties: [ " destination " : " existing_package " ] )
176- self ? . moveItem ( productOrVariationID: productOrVariationID, currentPackageIndex: packageIndex, newPackageIndex: index)
177- } ) )
168+ if index != packageIndex {
169+ guard let name = itemViewModels. first ( where: { $0. id == package . id } ) ? . packageName else {
170+ continue
171+ }
172+ let packageTitle = String ( format: Localization . packageName, index + 1 , name)
173+ buttons. append ( . default( Text ( packageTitle) , action: { [ weak self] in
174+ ServiceLocator . analytics. track ( . shippingLabelItemMoved, withProperties: [ " destination " : " existing_package " ] )
175+ self ? . moveItem ( productOrVariationID: item. productOrVariationID, currentPackageIndex: packageIndex, newPackageIndex: index)
176+ } ) )
177+ }
178178 }
179- }
180179
181- if !currentPackage. isOriginalPackaging {
182- let hasMultipleItems = currentPackage. items. count > 1 || currentPackage. items. first ( where: { $0. quantity > 1 } ) != nil
183- if hasMultipleItems {
184- // Add option to add item to new package if current package has more than one item.
180+ if !currentPackage. isOriginalPackaging {
181+ let hasMultipleItems = currentPackage. items. count > 1 || currentPackage. items. first ( where: { $0. quantity > 1 } ) != nil
182+ if hasMultipleItems {
183+ // Add option to add item to new package if current package has more than one item.
184+ buttons. append ( . default( Text ( Localization . addToNewPackage) ) { [ weak self] in
185+ ServiceLocator . analytics. track ( . shippingLabelItemMoved, withProperties: [ " destination " : " new_package " ] )
186+ self ? . addItemToNewPackage ( productOrVariationID: item. productOrVariationID, packageIndex: packageIndex)
187+ } )
188+ }
189+
190+ // Add option to ship in original package
191+ buttons. append ( . default( Text ( Localization . shipInOriginalPackage) ) { [ weak self] in
192+ ServiceLocator . analytics. track ( . shippingLabelItemMoved, withProperties: [ " destination " : " original_packaging " ] )
193+ self ? . shipInOriginalPackage ( productOrVariationID: item. productOrVariationID, packageIndex: packageIndex)
194+ } )
195+ } else {
185196 buttons. append ( . default( Text ( Localization . addToNewPackage) ) { [ weak self] in
186197 ServiceLocator . analytics. track ( . shippingLabelItemMoved, withProperties: [ " destination " : " new_package " ] )
187- self ? . addItemToNewPackage ( productOrVariationID: productOrVariationID, packageIndex: packageIndex)
198+ self ? . addItemToNewPackage ( productOrVariationID: item . productOrVariationID, packageIndex: packageIndex)
188199 } )
189200 }
190-
191- // Add option to ship in original package
192- buttons. append ( . default( Text ( Localization . shipInOriginalPackage) ) { [ weak self] in
193- ServiceLocator . analytics. track ( . shippingLabelItemMoved, withProperties: [ " destination " : " original_packaging " ] )
194- self ? . shipInOriginalPackage ( productOrVariationID: productOrVariationID, packageIndex: packageIndex)
195- } )
196- } else {
197- buttons. append ( . default( Text ( Localization . addToNewPackage) ) { [ weak self] in
198- ServiceLocator . analytics. track ( . shippingLabelItemMoved, withProperties: [ " destination " : " new_package " ] )
199- self ? . addItemToNewPackage ( productOrVariationID: productOrVariationID, packageIndex: packageIndex)
200- } )
201+ buttons. append ( . cancel( ) )
202+ actionButtons [ item. id] = buttons
201203 }
202- buttons. append ( . cancel( ) )
203- return buttons
204- } ( )
204+ return actionButtons
205205 }
206206
207207 /// Move the item with `productOrVariationID` from `currentPackage` to a new package,
@@ -361,10 +361,10 @@ private extension ShippingLabelPackagesFormViewModel {
361361 ///
362362 func observeItemViewModels( ) {
363363 packagesValidation. removeAll ( )
364- itemViewModels. enumerated ( ) . forEach { ( index , item) in
364+ itemViewModels. forEach { item in
365365 item. $isValidPackage
366366 . sink { [ weak self] isValid in
367- self ? . packagesValidation [ index ] = isValid && item. selectedPackageID. isNotEmpty
367+ self ? . packagesValidation [ item . id ] = isValid && item. selectedPackageID. isNotEmpty
368368 }
369369 . store ( in: & cancellables)
370370 }
@@ -425,10 +425,6 @@ private extension ShippingLabelPackagesFormViewModel {
425425
426426private extension ShippingLabelPackagesFormViewModel {
427427 enum Localization {
428- static let moveItemActionSheetMessage = NSLocalizedString ( " This item is currently in Package %1$d: %2$@. Where would you like to move it? " ,
429- comment: " Message in action sheet when an order item is about to " +
430- " be moved on Package Details screen of Shipping Label flow. " +
431- " The package name reads like: Package 1: Custom Envelope. " )
432428 static let packageName = NSLocalizedString ( " Package %1$d: %2$@ " ,
433429 comment: " Name of package to be listed in Move Item action sheet " +
434430 " on Package Details screen of Shipping Label flow. " )
0 commit comments