diff --git a/WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift b/WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift index a2938574c95..3753e88e8c8 100644 --- a/WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift +++ b/WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift @@ -5,6 +5,7 @@ import WooFoundation @available(iOS 17.0, *) struct PointOfSaleCollectCashView: View { @Environment(\.dynamicTypeSize) var dynamicTypeSize + @Environment(\.floatingControlAreaSize) private var floatingControlAreaSize: CGSize @Environment(PointOfSaleAggregateModel.self) private var posModel @FocusState private var isTextFieldFocused: Bool @@ -31,79 +32,85 @@ struct PointOfSaleCollectCashView: View { allowNegativeNumber: false) var body: some View { - ScrollView { - VStack(alignment: .center, spacing: conditionalPadding(POSSpacing.medium)) { - POSPageHeaderView(title: Localization.backNavigationTitle, - subtitle: formattedOrderTotal, - backButtonConfiguration: .init(state: isLoading ? .disabled: .enabled, - action: { - Task { @MainActor in - await posModel.cancelCashPayment() - isTextFieldFocused = false - } - })) - + GeometryReader { geometry in + ScrollView { VStack(alignment: .center, spacing: conditionalPadding(POSSpacing.medium)) { - Spacer() - - VStack(alignment: .center, spacing: conditionalPadding(POSSpacing.xSmall)) { - FormattableAmountTextField(viewModel: textFieldViewModel, style: .pos) - .focused($isTextFieldFocused) - .dynamicTypeSize(...DynamicTypeSize.accessibility1) - .onSubmit { - Task { @MainActor in - await submitCashAmount() + POSPageHeaderView(title: Localization.backNavigationTitle, + subtitle: formattedOrderTotal, + backButtonConfiguration: .init(state: isLoading ? .disabled: .enabled, + action: { + Task { @MainActor in + await posModel.cancelCashPayment() + isTextFieldFocused = false + } + })) + + VStack(alignment: .center, spacing: conditionalPadding(POSSpacing.medium)) { + Spacer() + + VStack(alignment: .center, spacing: conditionalPadding(POSSpacing.xSmall)) { + FormattableAmountTextField(viewModel: textFieldViewModel, style: .pos) + .focused($isTextFieldFocused) + .dynamicTypeSize(...DynamicTypeSize.accessibility1) + .onSubmit { + Task { @MainActor in + await submitCashAmount() + } + } + .onChange(of: textFieldViewModel.amount) { newValue in + textFieldAmountInput = newValue + updateChangeDueMessage() } - } - .onChange(of: textFieldViewModel.amount) { newValue in - textFieldAmountInput = newValue - updateChangeDueMessage() - } - if let changeDue = changeDueMessage { - Text(changeDue) - .font(.posBodySmallRegular()) - .foregroundColor(.posOnSurfaceVariantLowest) - } + if let changeDue = changeDueMessage { + Text(changeDue) + .font(.posBodySmallRegular()) + .foregroundColor(.posOnSurfaceVariantLowest) + } - if let errorMessage = errorMessage { - Text(errorMessage) - .font(.posBodySmallRegular()) - .foregroundColor(.posError) + if let errorMessage = errorMessage { + Text(errorMessage) + .font(.posBodySmallRegular()) + .foregroundColor(.posError) + } } - } - Spacer() + Spacer() - Button(action: { - Task { @MainActor in - await submitCashAmount() + Button(action: { + Task { @MainActor in + await submitCashAmount() + } + }, label: { + Text(Localization.markPaymentCompletedButtonTitle) + }) + .measureFrame { + buttonFrame = $0 } - }, label: { - Text(Localization.markPaymentCompletedButtonTitle) - }) - .measureFrame { - buttonFrame = $0 + .buttonStyle(POSFilledButtonStyle(size: .normal, isLoading: isLoading)) + .frame(maxWidth: .infinity) + .dynamicTypeSize(...DynamicTypeSize.accessibility1) + .disabled(isLoading) } - .buttonStyle(POSFilledButtonStyle(size: .normal, isLoading: isLoading)) - .frame(maxWidth: .infinity) - .dynamicTypeSize(...DynamicTypeSize.accessibility1) - .disabled(isLoading) + .padding([.horizontal]) + .padding(.bottom, max(keyboardFrame.height - geometry.safeAreaInsets.bottom, + floatingControlAreaSize.height) + Constants.bottomPadding + ) } - .padding([.horizontal]) - .padding(.bottom, keyboardFrame.height) - } - .animation(.easeInOut, value: errorMessage) - .animation(.easeInOut, value: changeDueMessage) - .onChange(of: textFieldAmountInput) { _ in - errorMessage = nil - } - .onReceive(Publishers.keyboardFrame) { - keyboardFrame = $0 - shouldMinimizePadding = $0.intersects(buttonFrame) + .frame(minHeight: geometry.size.height) + .animation(.easeInOut, value: errorMessage) + .animation(.easeInOut, value: changeDueMessage) + .onChange(of: textFieldAmountInput) { _ in + errorMessage = nil + } + .onReceive(Publishers.keyboardFrame) { + keyboardFrame = $0 + shouldMinimizePadding = $0.intersects(buttonFrame) + } + .animation(.default, value: shouldMinimizePadding) } - .animation(.default, value: shouldMinimizePadding) } + .frame(maxWidth: .infinity, maxHeight: .infinity) } private func markComplete() async throws { @@ -147,6 +154,7 @@ private extension PointOfSaleCollectCashView { private extension PointOfSaleCollectCashView { enum Constants { static let minimumPadding: CGFloat = POSSpacing.xSmall + static let bottomPadding: CGFloat = POSPadding.medium } private func conditionalPadding(_ padding: CGFloat) -> CGFloat { diff --git a/WooCommerce/Classes/POS/ViewHelpers/TotalsViewHelper.swift b/WooCommerce/Classes/POS/ViewHelpers/TotalsViewHelper.swift index 9458a1e3825..3cdaa79a7b7 100644 --- a/WooCommerce/Classes/POS/ViewHelpers/TotalsViewHelper.swift +++ b/WooCommerce/Classes/POS/ViewHelpers/TotalsViewHelper.swift @@ -67,7 +67,7 @@ final class TotalsViewHelper { func shouldApplyPadding(paymentState: PointOfSalePaymentState) -> Bool { switch paymentState { - case .card(.cardPaymentSuccessful), .cash(.paymentSuccess): + case .card(.cardPaymentSuccessful), .cash(.paymentSuccess), .cash(.collectingCash): return false default: return true