-
Notifications
You must be signed in to change notification settings - Fork 121
[Woo POS][Barcodes] Show barcode scanning information from the menu #15750
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
Changes from all commits
939e2e0
15a674a
9702df1
f5ba153
d24c03b
40d9174
9260aa9
a2af267
210cc08
cd1e7ef
0f523d5
ccac2ee
f99c050
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,95 @@ | ||
| import Foundation | ||
| import SwiftUI | ||
|
|
||
| struct PointOfSaleBarcodeScannerInformationModal: View { | ||
| @Binding var isPresented: Bool | ||
|
|
||
| init(isPresented: Binding<Bool>) { | ||
| self._isPresented = isPresented | ||
| } | ||
|
|
||
| var body: some View { | ||
| PointOfSaleInformationModal(isPresented: $isPresented, title: AttributedString(Localization.barcodeInfoHeading)) { | ||
| PointOfSaleInformationModalParagraphView { | ||
| Text(AttributedString(Localization.barcodeInfoIntroMessage)) | ||
| } | ||
|
|
||
| PointOfSaleInformationModalParagraphView { | ||
| Text(bulletPointWithLink) | ||
| Text(AttributedString(Localization.barcodeInfoSecondaryMessage)) | ||
| Text(AttributedString(Localization.barcodeInfoTertiaryMessage)) | ||
| Text(AttributedString(Localization.barcodeInfoQuaternaryMessage)) | ||
| } | ||
| .padding(.leading, POSSpacing.medium) | ||
|
|
||
| PointOfSaleInformationModalParagraphView(style: .outlined) { | ||
| Text(AttributedString(Localization.barcodeInfoQuinaryMessage)) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private var bulletPointWithLink: AttributedString { | ||
| var secondary = AttributedString(Localization.barcodeInfoPrimaryMessage + " ") | ||
| var moreDetails = AttributedString(Localization.barcodeInfoMoreDetailsLink) | ||
| moreDetails.link = Constants.detailsLink | ||
| moreDetails.foregroundColor = .posPrimary | ||
| moreDetails.underlineStyle = .single | ||
| secondary.append(moreDetails) | ||
| return secondary | ||
| } | ||
| } | ||
|
|
||
| private extension PointOfSaleBarcodeScannerInformationModal { | ||
| enum Constants { | ||
| static let detailsLink = URL(string: "https://woocommerce.com/document/barcode-and-qr-code-scanner/") | ||
| } | ||
|
|
||
| enum Localization { | ||
| static let barcodeInfoHeading = NSLocalizedString( | ||
| "pos.barcodeInfoModal.heading", | ||
| value: "Barcode scanning", | ||
| comment: "Heading for the barcode info modal in POS, introducing barcode scanning feature" | ||
| ) | ||
| static let barcodeInfoIntroMessage = NSLocalizedString( | ||
| "pos.barcodeInfoModal.introMessage", | ||
| value: "You can scan barcodes using an external scanner to quickly build a cart.", | ||
| comment: "Introductory message in the barcode info modal in POS, explaining the use of external barcode scanners" | ||
| ) | ||
| static let barcodeInfoPrimaryMessage = NSLocalizedString( | ||
| "pos.barcodeInfoModal.primaryMessage", | ||
| value: "• Set up barcodes in the \"GTIN, UPC, EAN, ISBN\" field in Products > Product Details > Inventory. ", | ||
| comment: "Primary bullet point in the barcode info modal in POS, instructing where to set up barcodes in product details" | ||
| ) | ||
| static let barcodeInfoMoreDetailsLink = NSLocalizedString( | ||
| "pos.barcodeInfoModal.moreDetailsLink", | ||
| value: "More details.", | ||
| comment: "Link text in the barcode info modal in POS, leading to more details about barcode setup" | ||
| ) | ||
| static let barcodeInfoSecondaryMessage = NSLocalizedString( | ||
| "pos.barcodeInfoModal.secondaryMessage", | ||
| value: "• Refer to your Bluetooth barcode scanner's instructions to set HID mode.", | ||
| comment: "Secondary bullet point in the barcode info modal in POS, instructing to set scanner to HID mode" | ||
| ) | ||
| static let barcodeInfoTertiaryMessage = NSLocalizedString( | ||
| "pos.barcodeInfoModal.tertiaryMessage", | ||
| value: "• Connect your barcode scanner in iOS Bluetooth settings.", | ||
| comment: "Tertiary bullet point in the barcode info modal in POS, instructing to connect scanner via Bluetooth settings" | ||
| ) | ||
| static let barcodeInfoQuaternaryMessage = NSLocalizedString( | ||
| "pos.barcodeInfoModal.quaternaryMessage", | ||
| value: "• Scan barcodes while on the item list to add products to the cart.", | ||
| comment: "Quaternary bullet point in the barcode info modal in POS, instructing to scan barcodes on item list to add to cart" | ||
| ) | ||
| static let barcodeInfoQuinaryMessage = NSLocalizedString( | ||
| "pos.barcodeInfoModal.quinaryMessage", | ||
| value: "The scanner emulates a keyboard, so sometimes it will prevent the software keyboard from showing, e.g. in search. " + | ||
| "Tap on the keyboard icon to show it again.", | ||
| comment: "Quinary message in the barcode info modal in POS, explaining scanner keyboard emulation and how to show software keyboard again" | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| @available(iOS 17.0, *) | ||
| #Preview { | ||
| PointOfSaleBarcodeScannerInformationModal(isPresented: .constant(true)) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| import SwiftUI | ||
|
|
||
| // Container view for displaying information modals in the POS. | ||
| // | ||
| struct PointOfSaleInformationModal<Content: View>: View { | ||
| @Binding var isPresented: Bool | ||
| let title: AttributedString | ||
| let content: Content | ||
|
|
||
| // Used to make ScrollView height increase together with the content height. | ||
| @State private var contentHeight: CGFloat = 0 | ||
|
Contributor
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 think that this (or something else about this struct) makes the modals grow as they transition for presentation/dismissal as well. That didn't happen in the past, they just faded in. It would be good if they could already have the correct size when we try to present them... I'm not sure the best way to do it though...
Contributor
Author
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. It's only an iOS 17 animation issue, not for the first time. Yes, it's likely related to
Contributor
Author
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 found a quick workaround, applying a 0-second Simulator.Screen.Recording.-.iPad.Air.11-inch.17.5.-.2025-06-18.at.17.45.30.mp4
Contributor
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. Nice one, and thanks for commenting about it too. I checked on device and it looks good. |
||
|
|
||
| init( | ||
| isPresented: Binding<Bool>, | ||
| title: AttributedString, | ||
| @ViewBuilder content: () -> Content | ||
| ) { | ||
| self._isPresented = isPresented | ||
| self.title = title | ||
| self.content = content() | ||
| } | ||
|
|
||
| var body: some View { | ||
| VStack(spacing: POSSpacing.xxLarge) { | ||
| HStack { | ||
| Text(title) | ||
| .font(.posHeadingBold) | ||
| Spacer() | ||
| Button { | ||
| isPresented = false | ||
| } label: { | ||
| Text(Image(systemName: "xmark")) | ||
| .font(.posButtonSymbolLarge) | ||
| } | ||
| } | ||
| .foregroundColor(Color.posOnSurface) | ||
|
|
||
| ScrollView { | ||
| VStack { | ||
| content | ||
| } | ||
| .measureHeight { height in | ||
| // Workaround for ScrollView not updating its height immediately on iOS 17 | ||
| withAnimation(.easeIn(duration: 0)) { | ||
| contentHeight = height | ||
| } | ||
| } | ||
| } | ||
| .frame(maxHeight: contentHeight) | ||
|
|
||
| Button(action: { | ||
| isPresented = false | ||
| }) { | ||
| Text(Localization.okButtonTitle) | ||
| } | ||
| .buttonStyle(POSOutlinedButtonStyle(size: .normal)) | ||
| } | ||
| .padding(POSPadding.xxLarge) | ||
| .background(Color.posSurfaceBright) | ||
| .frame(width: Constants.modalFrameWidth) | ||
| } | ||
| } | ||
|
|
||
| struct PointOfSaleInformationModalParagraphView<Content: View>: View { | ||
| enum Style { | ||
| case `default` | ||
| case outlined | ||
| } | ||
|
|
||
| let content: Content | ||
| let style: Style | ||
|
|
||
| init(style: Style = .default, @ViewBuilder content: () -> Content) { | ||
| self.content = content() | ||
| self.style = style | ||
| } | ||
|
|
||
| var body: some View { | ||
| VStack(alignment: style == .default ? .leading : .center) { | ||
| content | ||
| } | ||
| .if(style == .default, transform: { view in | ||
| view.modifier(PointOfSaleInformationModalDefaultParagraphStyle()) | ||
| }) | ||
| .if(style == .outlined, transform: { view in | ||
| view.modifier(PointOfSaleInformationModalOutlinedParagraphStyle()) | ||
| }) | ||
| } | ||
| } | ||
|
|
||
| private struct PointOfSaleInformationModalDefaultParagraphStyle: ViewModifier { | ||
| func body(content: Content) -> some View { | ||
| content | ||
| .frame(maxWidth: .infinity, alignment: .leading) | ||
| .font(.posBodyLargeRegular()) | ||
| .foregroundStyle(Color.posOnSurface) | ||
| .multilineTextAlignment(.leading) | ||
| .fixedSize(horizontal: false, vertical: true) | ||
| } | ||
| } | ||
|
|
||
| private struct PointOfSaleInformationModalOutlinedParagraphStyle: ViewModifier { | ||
| func body(content: Content) -> some View { | ||
| content | ||
| .frame(maxWidth: .infinity, alignment: .center) | ||
| .font(.posBodySmallRegular()) | ||
| .foregroundStyle(Color.posOnSurface) | ||
| .padding(POSPadding.medium) | ||
| .background(Color.posSurfaceDim) | ||
| .clipShape(RoundedRectangle(cornerRadius: POSCornerRadiusStyle.medium.value)) | ||
| .multilineTextAlignment(.center) | ||
| .fixedSize(horizontal: false, vertical: true) | ||
| } | ||
| } | ||
|
|
||
| private extension PointOfSaleInformationModal { | ||
| enum Constants { | ||
| static var modalFrameWidth: CGFloat { 896 } | ||
| } | ||
| } | ||
|
|
||
| private enum Localization { | ||
| static let okButtonTitle = NSLocalizedString( | ||
| "pos.posInformationModal.ok.button.title", | ||
| value: "OK", | ||
| comment: "Title for the OK button on the pos information modal" | ||
| ) | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -12,60 +12,26 @@ struct SimpleProductsOnlyInformation: View { | |
| } | ||
|
|
||
| var body: some View { | ||
| VStack(spacing: Constants.contentBlockSpacing) { | ||
| HStack { | ||
| Spacer() | ||
| Button { | ||
| isPresented = false | ||
| } label: { | ||
| Text(Image(systemName: "xmark")) | ||
| .font(.posButtonSymbolLarge) | ||
| } | ||
| .padding(Constants.dismissIconPadding) | ||
| .foregroundColor(Color.posOnSurfaceVariantLowest) | ||
| } | ||
|
|
||
| VStack(spacing: Constants.textSpacing) { | ||
| Text(Localization.modalTitle) | ||
| .font(.posHeadingBold) | ||
|
|
||
| Group { | ||
| Text(issueMessage) | ||
| Text(futureMessage) | ||
| } | ||
| .font(.posBodyLargeRegular()) | ||
| PointOfSaleInformationModal(isPresented: $isPresented, title: AttributedString(Localization.modalTitle)) { | ||
| PointOfSaleInformationModalParagraphView { | ||
| Text(issueMessage) | ||
|
Comment on lines
+15
to
+17
Contributor
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. Nice one for sharing this code 👍 Looks better all round |
||
| Text(futureMessage) | ||
| } | ||
| .foregroundStyle(Color.posOnSurface) | ||
| .multilineTextAlignment(.center) | ||
|
|
||
| VStack(spacing: Constants.textSpacing) { | ||
| PointOfSaleInformationModalParagraphView(style: .outlined) { | ||
| Text(hintMessage) | ||
| .font(.posBodySmallRegular()) | ||
| .foregroundStyle(Color.posOnSurface) | ||
|
|
||
| Spacer().frame(height: POSSpacing.small) | ||
|
|
||
| Button { | ||
| deepLinkNavigator?.navigate(to: OrdersDestination.createOrder) | ||
| } label: { | ||
| Label(Localization.modalAction, systemImage: "plus") | ||
| .font(.posBodySmallRegular()) | ||
| } | ||
| .foregroundStyle(Color.posPrimary) | ||
| } | ||
| .frame(maxWidth: .infinity) | ||
| .padding(.vertical, Constants.hintVerticalPadding) | ||
| .padding(.horizontal, Constants.hintHorizontalPadding) | ||
| .background(Color(.posSurfaceDim)) | ||
| .clipShape(RoundedRectangle(cornerRadius: Constants.hintBackgroundCornerRadius)) | ||
| .multilineTextAlignment(.center) | ||
|
|
||
| Button(action: { | ||
| isPresented = false | ||
| }) { | ||
| Text(Localization.okButtonTitle) | ||
| } | ||
| .buttonStyle(POSOutlinedButtonStyle(size: .normal)) | ||
| } | ||
| .padding(Constants.modalContentPadding) | ||
| .frame(width: Constants.modalFrameWidth) | ||
| } | ||
|
|
||
| private var issueMessage: String { | ||
|
|
@@ -84,17 +50,6 @@ struct SimpleProductsOnlyInformation: View { | |
| // Constants and Localization enums | ||
| @available(iOS 17.0, *) | ||
| private extension SimpleProductsOnlyInformation { | ||
| enum Constants { | ||
| static let modalFrameWidth: CGFloat = 896 | ||
| static let modalContentPadding: CGFloat = POSSpacing.medium | ||
| static let hintVerticalPadding: CGFloat = POSSpacing.medium | ||
| static let hintHorizontalPadding: CGFloat = POSSpacing.medium | ||
| static let hintBackgroundCornerRadius: CGFloat = POSCornerRadiusStyle.medium.value | ||
| static let contentBlockSpacing: CGFloat = POSSpacing.xxLarge | ||
| static let textSpacing: CGFloat = POSSpacing.small | ||
| static let dismissIconPadding: EdgeInsets = .init(top: 8, leading: 8, bottom: 8, trailing: 8) | ||
| } | ||
|
|
||
| enum Localization { | ||
| static let modalTitle = NSLocalizedString( | ||
| "pos.simpleProductsModal.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.
This link relates primarily to setting up barcodes. I mentioned to Andrey that it should be on the first item in the list, not the second, and I believe he changed it there, the screenshot from Android is probably out of date.
Could you move this to the first bullet point please?
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.
Yes, moved.