-
Notifications
You must be signed in to change notification settings - Fork 111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Woo POS][Cash & Receipts] Render cash button and navigate to cash view #14724
Changes from all commits
64707e8
0061a43
ab37226
6144cf7
6eb26ef
714dfe5
c9178c0
71547ba
5f9bc07
c5923ef
e656b3b
8d723fa
c8a00b2
57a66b6
b840317
4e5a727
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
import SwiftUI | ||
|
||
struct PointOfSaleCollectCashView: View { | ||
@Environment(\.dismiss) private var dismiss | ||
@EnvironmentObject private var posModel: PointOfSaleAggregateModel | ||
|
||
@State private var textFieldAmountInput: String = "" | ||
@State private var isLoading: Bool = false | ||
@State private var errorMessage: String? | ||
|
||
let orderTotal: String | ||
|
||
private var formattedOrderTotal: String { | ||
String.localizedStringWithFormat(Localization.backNavigationSubtitle, orderTotal) | ||
} | ||
|
||
var body: some View { | ||
VStack(alignment: .center, spacing: 20) { | ||
HStack { | ||
Button(action: { | ||
dismiss() | ||
}, label: { | ||
VStack { | ||
HStack { | ||
Image(systemName: "chevron.left") | ||
Text(Localization.backNavigationTitle) | ||
} | ||
.font(.posTitleRegular) | ||
.bold() | ||
.foregroundColor(.primary) | ||
|
||
Text(formattedOrderTotal) | ||
.font(.posBodyRegular) | ||
.foregroundColor(.primary) | ||
} | ||
}) | ||
Spacer() | ||
} | ||
.padding() | ||
|
||
TextField("$0.00", text: $textFieldAmountInput) | ||
.keyboardType(.numbersAndPunctuation) | ||
.textInputAutocapitalization(.none) | ||
.autocorrectionDisabled() | ||
.multilineTextAlignment(.center) | ||
.font(POSFontStyle.posTitleRegular) | ||
.focused() | ||
.padding() | ||
.onSubmit { | ||
Task { @MainActor in | ||
await markComplete() | ||
} | ||
} | ||
|
||
if let errorMessage = errorMessage { | ||
Text(errorMessage) | ||
.font(POSFontStyle.posBodyRegular) | ||
.foregroundColor(.red) | ||
} | ||
|
||
Button(action: { | ||
Task { @MainActor in | ||
await markComplete() | ||
} | ||
}, label: { | ||
HStack(spacing: Constants.buttonSpacing) { | ||
if isLoading { | ||
ProgressView() | ||
.progressViewStyle(CircularProgressViewStyle()) | ||
.tint(Color.posPrimaryTextInverted) | ||
} else { | ||
Text(Localization.markPaymentCompletedButtonTitle) | ||
.font(Constants.buttonFont) | ||
} | ||
} | ||
.frame(maxWidth: .infinity) | ||
}) | ||
.padding(Constants.buttonPadding) | ||
.frame(maxWidth: .infinity) | ||
.foregroundColor(Color.posPrimaryTextInverted) | ||
.background(Color.posOverlayFillInverted) | ||
.cornerRadius(Constants.buttonCornerRadius) | ||
.contentShape(Rectangle()) | ||
Comment on lines
+78
to
+83
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm guessing some of these are shared with other buttons? It'd be good to put them in a button style for sharing and to make the view easier to read. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, we have an existing issue to make the async button a reusable component, I've logged it along: #14626 |
||
.disabled(isLoading) | ||
|
||
Spacer() | ||
} | ||
.padding() | ||
.animation(.easeInOut, value: errorMessage) | ||
.onChange(of: textFieldAmountInput) { amount in | ||
errorMessage = nil | ||
} | ||
} | ||
|
||
private func markComplete() async { | ||
// TODO: | ||
// https://github.com/woocommerce/woocommerce-ios/issues/14602 | ||
} | ||
} | ||
|
||
private extension PointOfSaleCollectCashView { | ||
enum Constants { | ||
static let buttonSpacing: CGFloat = 12 | ||
static let buttonPadding: CGFloat = 32 | ||
static let buttonFont: POSFontStyle = .posBodyEmphasized | ||
static let buttonCornerRadius: CGFloat = 8 | ||
} | ||
|
||
enum Localization { | ||
static let backNavigationTitle = NSLocalizedString( | ||
"pointOfSale.cashview.back.navigation.title", | ||
value: "Cash payment", | ||
comment: "Title for the cash payment view navigation back button" | ||
) | ||
static let backNavigationSubtitle = NSLocalizedString( | ||
"pointOfSale.cashview.back.navigation.subtitle", | ||
value: "Total: %1$@", | ||
comment: "Subtitle for the cash payment view navigation back button" + | ||
"Reads as 'Total: $1.23'" | ||
) | ||
static let markPaymentCompletedButtonTitle = NSLocalizedString( | ||
"pointOfSale.cashview.button.markpaymentcompleted.title", | ||
value: "Mark payment as complete", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a weird way to word it... I can see it's in the designs, but maybe it's worth discussing some more? It feels very programmery, I think the merchant action is more like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I'll raise it with Wagner along the navigation icons for each platform |
||
comment: "Button to mark a cash payment as completed" | ||
) | ||
} | ||
} | ||
|
||
#if DEBUG | ||
#Preview { | ||
let posModel = PointOfSaleAggregateModel( | ||
itemsController: PointOfSalePreviewItemsController(), | ||
cardPresentPaymentService: CardPresentPaymentPreviewService(), | ||
orderController: PointOfSalePreviewOrderController()) | ||
PointOfSaleCollectCashView(orderTotal: "$1.23") | ||
.environmentObject(posModel) | ||
} | ||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,9 +17,17 @@ struct TotalsView: View { | |
viewHelper.shouldShowTotalsFields(for: posModel.paymentState) | ||
} | ||
|
||
private var shouldShowCollectCashPaymentButton: Bool { | ||
ServiceLocator.featureFlagService.isFeatureFlagEnabled(.acceptCashForPointOfSale) && | ||
posModel.orderState != .syncing && | ||
(posModel.paymentState == .idle || posModel.paymentState == .acceptingCard) | ||
} | ||
|
||
@Environment(\.dynamicTypeSize) var dynamicTypeSize | ||
@Environment(\.colorScheme) var colorScheme | ||
|
||
@State private var shouldShowCollectCashPayment: Bool = false | ||
|
||
var body: some View { | ||
HStack { | ||
switch posModel.orderState { | ||
|
@@ -52,6 +60,17 @@ struct TotalsView: View { | |
.opacity(viewHelper.shouldShowTotalsFields(for: posModel.paymentState) ? 1 : 0) | ||
.layoutPriority(2) | ||
} | ||
Button(action: { | ||
shouldShowCollectCashPayment = true | ||
}, label: { | ||
Text(Localization.cashPaymentButtonTitle) | ||
.font(POSFontStyle.posBodyEmphasized) | ||
.foregroundColor(.posPrimaryText) | ||
.frame(height: Constants.buttonHeight) | ||
}) | ||
.buttonStyle(SecondaryButtonStyle()) | ||
.padding(.horizontal, Constants.buttonHorizontalPadding) | ||
.renderedIf(shouldShowCollectCashPaymentButton) | ||
} | ||
.animation(.default, value: posModel.cardPresentPaymentInlineMessage) | ||
Spacer() | ||
|
@@ -69,6 +88,13 @@ struct TotalsView: View { | |
isShowingTotalsFields = shouldShowTotalsFields | ||
} | ||
.onChange(of: shouldShowTotalsFields, perform: hideTotalsFieldsWithDelay) | ||
.fullScreenCover(isPresented: $shouldShowCollectCashPayment) { | ||
if case .loaded(let total) = posModel.orderState { | ||
PointOfSaleCollectCashView(orderTotal: total.orderTotal) | ||
.matchedGeometryEffect(id: Constants.matchedGeometryCashId, | ||
in: totalsFieldAnimation) | ||
} | ||
} | ||
.geometryGroupIfSupported() | ||
} | ||
|
||
|
@@ -291,6 +317,8 @@ private extension TotalsView { | |
enum Constants { | ||
static let pricesIdealWidth: CGFloat = 382 | ||
static let verticalSpacing: CGFloat = 56 | ||
static let buttonHeight: CGFloat = 56 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: fixed button height might result in text being clipped with a large dynamic font size, and/or multiline text. If a fixed height is needed, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
static let buttonHorizontalPadding: CGFloat = 48 | ||
|
||
static let totalsLineViewPadding: EdgeInsets = .init(top: 20, leading: 24, bottom: 20, trailing: 24) | ||
static let subtotalsVerticalSpacing: CGFloat = 8 | ||
|
@@ -311,6 +339,7 @@ private extension TotalsView { | |
static let matchedGeometrySubtotalId: String = "pos_totals_view_subtotal_matched_geometry_id" | ||
static let matchedGeometryTaxId: String = "pos_totals_view_tax_matched_geometry_id" | ||
static let matchedGeometryTotalId: String = "pos_totals_view_total_matched_geometry_id" | ||
static let matchedGeometryCashId: String = "pos_totals_view_cash_matched_geometry_id" | ||
|
||
static let totalsFieldsHideAnimationDelay: CGFloat = 0.3 | ||
} | ||
|
@@ -328,6 +357,10 @@ private extension TotalsView { | |
"pos.totalsView.taxes", | ||
value: "Taxes", | ||
comment: "Title for taxes amount field") | ||
static let cashPaymentButtonTitle = NSLocalizedString( | ||
"pos.totalsView.cash.button.title", | ||
value: "Cash payment", | ||
comment: "Title for the cash payment button title") | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps another place where an AsyncButton would be good? It'd need a binding so that the text field's
onSubmit
also triggered the progress view, but that seems like a useful option.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree! I'll make the change with #14602