From 160740ca20c4e94f95647f8e900c7e6096274029 Mon Sep 17 00:00:00 2001 From: RafaelKayumov Date: Wed, 24 Sep 2025 17:34:32 +0300 Subject: [PATCH 1/3] Workaround geometry reading by PreferenceKey observing --- .../Orders/Order Creation/OrderForm.swift | 221 ++++++++++-------- 1 file changed, 120 insertions(+), 101 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Creation/OrderForm.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Creation/OrderForm.swift index f2445a721a2..6cc2a0c1d67 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/Order Creation/OrderForm.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/Order Creation/OrderForm.swift @@ -222,6 +222,8 @@ struct OrderForm: View { @State private var shouldShowGiftCardForm = false + @State private var bannerWidth: CGFloat = 0 + @Environment(\.adaptiveModalContainerPresentationStyle) var presentationStyle @Environment(\.horizontalSizeClass) var horizontalSizeClass @@ -251,137 +253,145 @@ struct OrderForm: View { } @ViewBuilder private func orderFormSummary(_ presentProductSelector: (() -> Void)?) -> some View { - GeometryReader { geometry in - ScrollViewReader { scroll in - ScrollView { - Group { - VStack(spacing: Layout.noSpacing) { - Spacer(minLength: Layout.sectionSpacing) + ScrollViewReader { scroll in + ScrollView { + Group { + VStack(spacing: Layout.noSpacing) { + Spacer(minLength: Layout.sectionSpacing) + if viewModel.shouldShowNonEditableIndicators { Group { Divider() // Needed because `NonEditableOrderBanner` does not have a top divider - NonEditableOrderBanner(width: geometry.size.width) + NonEditableOrderBanner(width: bannerWidth) } - .renderedIf(viewModel.shouldShowNonEditableIndicators) - - Group { - OrderStatusSection(viewModel: viewModel, topDivider: !viewModel.shouldShowNonEditableIndicators) - Spacer(minLength: Layout.sectionSpacing) - } - .renderedIf(flow == .editing) - - ProductsSection(scroll: scroll, - flow: flow, - presentProductSelector: presentProductSelector, - viewModel: viewModel, - navigationButtonID: $navigationButtonID, - isLoading: isLoading) - .disabled(viewModel.shouldShowNonEditableIndicators) + } - Group { - Divider() - Spacer(minLength: Layout.sectionSpacing) - Divider() - } - .renderedIf(viewModel.shouldSplitProductsAndCustomAmountsSections) + Group { + OrderStatusSection(viewModel: viewModel, topDivider: !viewModel.shouldShowNonEditableIndicators) + Spacer(minLength: Layout.sectionSpacing) + } + .renderedIf(flow == .editing) - OrderCustomAmountsSection(viewModel: viewModel, sectionViewModel: viewModel.customAmountsSectionViewModel) - .disabled(viewModel.shouldShowNonEditableIndicators) + ProductsSection(scroll: scroll, + flow: flow, + presentProductSelector: presentProductSelector, + viewModel: viewModel, + navigationButtonID: $navigationButtonID, + isLoading: isLoading) + .disabled(viewModel.shouldShowNonEditableIndicators) + Group { Divider() - Spacer(minLength: Layout.sectionSpacing) + Divider() + } + .renderedIf(viewModel.shouldSplitProductsAndCustomAmountsSections) - Group { - OrderShippingSection(viewModel: viewModel.shippingLineViewModel) - .disabled(viewModel.shouldShowNonEditableIndicators) - Spacer(minLength: Layout.sectionSpacing) - } - .renderedIf(viewModel.shippingLineViewModel.shippingLineRows.isNotEmpty) - - Group { - OrderCouponSectionView(viewModel: viewModel, couponViewModel: viewModel.couponLineViewModel) - .disabled(viewModel.shouldShowNonEditableIndicators) - Spacer(minLength: Layout.sectionSpacing) - } - .renderedIf(viewModel.couponLineViewModel.couponLineRows.isNotEmpty) - - AddOrderComponentsSection( - viewModel: viewModel.paymentDataViewModel, - shippingLineViewModel: viewModel.shippingLineViewModel, - couponLineViewModel: viewModel.couponLineViewModel, - shouldShowCouponsInfoTooltip: $shouldShowInformationalCouponTooltip, - shouldShowGiftCardForm: $shouldShowGiftCardForm) - .addingTopAndBottomDividers() + OrderCustomAmountsSection(viewModel: viewModel, sectionViewModel: viewModel.customAmountsSectionViewModel) .disabled(viewModel.shouldShowNonEditableIndicators) + Divider() + + Spacer(minLength: Layout.sectionSpacing) + + Group { + OrderShippingSection(viewModel: viewModel.shippingLineViewModel) + .disabled(viewModel.shouldShowNonEditableIndicators) Spacer(minLength: Layout.sectionSpacing) } + .renderedIf(viewModel.shippingLineViewModel.shippingLineRows.isNotEmpty) - VStack(spacing: Layout.noSpacing) { - Group { - NewTaxRateSection(text: viewModel.taxRateRowText) { - viewModel.onSetNewTaxRateTapped() - switch viewModel.taxRateRowAction { - case .storedTaxRateSheet: - shouldShowStoredTaxRateSheet = true - viewModel.onStoredTaxRateBottomSheetAppear() - case .taxSelector: - shouldShowNewTaxRateSelector = true - } + Group { + OrderCouponSectionView(viewModel: viewModel, couponViewModel: viewModel.couponLineViewModel) + .disabled(viewModel.shouldShowNonEditableIndicators) + Spacer(minLength: Layout.sectionSpacing) + } + .renderedIf(viewModel.couponLineViewModel.couponLineRows.isNotEmpty) + + AddOrderComponentsSection( + viewModel: viewModel.paymentDataViewModel, + shippingLineViewModel: viewModel.shippingLineViewModel, + couponLineViewModel: viewModel.couponLineViewModel, + shouldShowCouponsInfoTooltip: $shouldShowInformationalCouponTooltip, + shouldShowGiftCardForm: $shouldShowGiftCardForm) + .addingTopAndBottomDividers() + .disabled(viewModel.shouldShowNonEditableIndicators) + + Spacer(minLength: Layout.sectionSpacing) + } - } - .sheet(isPresented: $shouldShowNewTaxRateSelector) { - NewTaxRateSelectorView(viewModel: NewTaxRateSelectorViewModel(siteID: viewModel.siteID, - onTaxRateSelected: { taxRate in - viewModel.onTaxRateSelected(taxRate) - }), - taxEducationalDialogViewModel: viewModel.paymentDataViewModel.taxEducationalDialogViewModel, - onDismissWpAdminWebView: viewModel.paymentDataViewModel.onDismissWpAdminWebViewClosure, - storeSelectedTaxRate: viewModel.shouldStoreTaxRateInSelectorByDefault) - } - .sheet(isPresented: $shouldShowStoredTaxRateSheet) { - if #available(iOS 16.0, *) { - storedTaxRateBottomSheetContent - .presentationDetents([.medium]) - .presentationDragIndicator(.visible) - } else { - storedTaxRateBottomSheetContent - } + VStack(spacing: Layout.noSpacing) { + Group { + NewTaxRateSection(text: viewModel.taxRateRowText) { + viewModel.onSetNewTaxRateTapped() + switch viewModel.taxRateRowAction { + case .storedTaxRateSheet: + shouldShowStoredTaxRateSheet = true + viewModel.onStoredTaxRateBottomSheetAppear() + case .taxSelector: + shouldShowNewTaxRateSelector = true } - Spacer(minLength: Layout.sectionSpacing) } - .renderedIf(viewModel.shouldShowNewTaxRateSection) - - Divider() - - if ServiceLocator.featureFlagService.isFeatureFlagEnabled(.subscriptionsInOrderCreationCustomers) { - OrderCustomerSection(viewModel: viewModel.customerSectionViewModel) - } else { - LegacyOrderCustomerSection(viewModel: viewModel, addressFormViewModel: viewModel.addressFormViewModel) + .sheet(isPresented: $shouldShowNewTaxRateSelector) { + NewTaxRateSelectorView(viewModel: NewTaxRateSelectorViewModel(siteID: viewModel.siteID, + onTaxRateSelected: { taxRate in + viewModel.onTaxRateSelected(taxRate) + }), + taxEducationalDialogViewModel: viewModel.paymentDataViewModel.taxEducationalDialogViewModel, + onDismissWpAdminWebView: viewModel.paymentDataViewModel.onDismissWpAdminWebViewClosure, + storeSelectedTaxRate: viewModel.shouldStoreTaxRateInSelectorByDefault) + } + .sheet(isPresented: $shouldShowStoredTaxRateSheet) { + if #available(iOS 16.0, *) { + storedTaxRateBottomSheetContent + .presentationDetents([.medium]) + .presentationDragIndicator(.visible) + } else { + storedTaxRateBottomSheetContent + } } - Group { - Divider() + Spacer(minLength: Layout.sectionSpacing) + } + .renderedIf(viewModel.shouldShowNewTaxRateSection) - Spacer(minLength: Layout.sectionSpacing) + Divider() - Divider() - } - .renderedIf(viewModel.shouldSplitCustomerAndNoteSections) + if ServiceLocator.featureFlagService.isFeatureFlagEnabled(.subscriptionsInOrderCreationCustomers) { + OrderCustomerSection(viewModel: viewModel.customerSectionViewModel) + } else { + LegacyOrderCustomerSection(viewModel: viewModel, addressFormViewModel: viewModel.addressFormViewModel) + } - CustomerNoteSection(viewModel: viewModel) + Group { + Divider() + + Spacer(minLength: Layout.sectionSpacing) Divider() } + .renderedIf(viewModel.shouldSplitCustomerAndNoteSections) + + CustomerNoteSection(viewModel: viewModel) + + Divider() } - .disabled(viewModel.disabled) } - .accessibilityIdentifier(Accessibility.orderFormScrollViewIdentifier) - .background(Color(.listBackground).ignoresSafeArea()) - .ignoresSafeArea(.container, edges: [.horizontal]) + .disabled(viewModel.disabled) } + .accessibilityIdentifier(Accessibility.orderFormScrollViewIdentifier) + .background(Color(.listBackground).ignoresSafeArea()) + .ignoresSafeArea(.container, edges: [.horizontal]) + .background( + GeometryReader { geometryProxy in + Color.clear + .preference(key: WidthPreferenceKey.self, value: geometryProxy.size.width) + } + ) + } + .onPreferenceChange(WidthPreferenceKey.self) { newWidth in + bannerWidth = newWidth } .safeAreaInset(edge: .bottom) { VStack { @@ -863,6 +873,15 @@ private extension OrderForm { } } +private extension OrderForm { + struct WidthPreferenceKey: PreferenceKey { + static var defaultValue: CGFloat = .zero + static func reduce(value: inout CGFloat, nextValue: () -> CGFloat) { + value = max(value, nextValue()) + } + } +} + struct OrderForm_Previews: PreviewProvider { static var previews: some View { let viewModel = EditableOrderViewModel(siteID: 123) From bb1f8e1f85ec04f008ed488a1bd4f76b92b04fa2 Mon Sep 17 00:00:00 2001 From: RafaelKayumov Date: Wed, 24 Sep 2025 17:47:18 +0300 Subject: [PATCH 2/3] Move geometry reader outside of the scroll view --- .../Orders/Order Creation/OrderForm.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/WooCommerce/Classes/ViewRelated/Orders/Order Creation/OrderForm.swift b/WooCommerce/Classes/ViewRelated/Orders/Order Creation/OrderForm.swift index 6cc2a0c1d67..4e0c9e79d32 100644 --- a/WooCommerce/Classes/ViewRelated/Orders/Order Creation/OrderForm.swift +++ b/WooCommerce/Classes/ViewRelated/Orders/Order Creation/OrderForm.swift @@ -241,6 +241,12 @@ struct OrderForm: View { viewModel.saveInFlightOrderNotes() viewModel.saveInflightCustomerDetails() } + .background( + GeometryReader { geometryProxy in + Color.clear + .preference(key: WidthPreferenceKey.self, value: geometryProxy.size.width) + } + ) } private func updateSelectionSyncApproach(for presentationStyle: AdaptiveModalContainerPresentationStyle?) { @@ -383,12 +389,6 @@ struct OrderForm: View { .accessibilityIdentifier(Accessibility.orderFormScrollViewIdentifier) .background(Color(.listBackground).ignoresSafeArea()) .ignoresSafeArea(.container, edges: [.horizontal]) - .background( - GeometryReader { geometryProxy in - Color.clear - .preference(key: WidthPreferenceKey.self, value: geometryProxy.size.width) - } - ) } .onPreferenceChange(WidthPreferenceKey.self) { newWidth in bannerWidth = newWidth From 328e4aa5ce8632fbf6e7c854801c4a8fb3a22cdd Mon Sep 17 00:00:00 2001 From: RafaelKayumov Date: Wed, 24 Sep 2025 19:09:48 +0300 Subject: [PATCH 3/3] Update release notes --- RELEASE-NOTES.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.txt index 90b8740f147..558975895ff 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.txt @@ -3,7 +3,7 @@ 23.4 ----- - +- [*] Order details: Fixes broken country selector navigation and "Non editable banner" width. [https://github.com/woocommerce/woocommerce-ios/pull/16171] 23.3 -----