Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -26,26 +26,51 @@ final class HIDBarcodeParser {
/// Process a key press event
/// - Parameter key: The key that was pressed
func processKeyPress(_ key: UIKey) {
let currentTime = timeProvider.now()

// If characters are entered too slowly, it's probably typing and we should ignore it
if let lastTime = lastKeyPressTime,
currentTime.timeIntervalSince(lastTime) > configuration.maximumInterCharacterTime {
onScan(.failure(HIDBarcodeParserError.timedOut(barcode: buffer)))
resetScan()
guard shouldRecogniseAsScanKeystroke(key) else {
return
}

lastKeyPressTime = currentTime

let character = key.characters
if configuration.terminatingStrings.contains(character) {
processScan()
} else {
guard !excludedKeys.contains(key.keyCode) else { return }
checkForTimeoutBetweenKeystrokes()
buffer.append(character)
}
}

private func shouldRecogniseAsScanKeystroke(_ key: UIKey) -> Bool {
guard key.characters.isNotEmpty else {
// This prevents a double-trigger-pull on a Star scanner from adding an error row –
// Star use this as a shortcut to switch to the software keyboard. They send keycode 174 0xAE, which is
// undefined and reserved in UIKeyboardHIDUsage. The scanner doesn't send a character with the code.
// There seems to be no reason to handle empty input when considering scans.
return false
}

if buffer.isEmpty && configuration.terminatingStrings.contains(key.characters) {
// We prefer to show all partial scans, but if we just get an enter with no numbers, ignoring it makes testing easier
return false
}

return true
}

private func checkForTimeoutBetweenKeystrokes() {
// If characters are entered too slowly, it's probably typing and we should ignore the old input.
// The key we just received is still considered for adding to the buffer – we may simply reset the buffer first.
let currentTime = timeProvider.now()

if let lastTime = lastKeyPressTime,
currentTime.timeIntervalSince(lastTime) > configuration.maximumInterCharacterTime {
onScan(.failure(HIDBarcodeParserError.timedOut(barcode: buffer)))
resetScan()
}

lastKeyPressTime = currentTime
}

private let excludedKeys: [UIKeyboardHIDUsage] = [
.keyboardCapsLock,
.keyboardF1,
Expand Down Expand Up @@ -138,6 +163,7 @@ final class HIDBarcodeParser {
}

private func processScan() {
checkForTimeoutBetweenKeystrokes()
if buffer.count >= configuration.minimumBarcodeLength {
onScan(.success(buffer))
} else {
Expand Down
113 changes: 79 additions & 34 deletions WooCommerce/Classes/POS/Presentation/ItemRowView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,6 @@ struct ItemRowView: View {

var body: some View {
itemRow
.background(Color.posSurfaceContainerLowest)
.frame(maxWidth: .infinity, idealHeight: dynamicTypeSize.isAccessibilitySize ? nil : dimension)
.posItemCardBorderStyles()
.padding(.horizontal, Constants.horizontalPadding)
.geometryGroup()
.accessibilityLabel(accessibilityLabel)
Expand Down Expand Up @@ -86,6 +83,9 @@ struct ItemRowView: View {
}
}
.padding(.trailing, Constants.cardContentHorizontalPadding)
.frame(maxWidth: .infinity, minHeight: dimension, alignment: .leading)
.background(Color.posSurfaceContainerLowest)
.posItemCardBorderStyles()
}

@ViewBuilder
Expand Down Expand Up @@ -152,39 +152,84 @@ private extension ItemRowView {

#if DEBUG
@available(iOS 17.0, *)
#Preview(traits: .sizeThatFitsLayout) {
ItemRowView(cartItem: Cart.PurchasableItem(id: UUID(),
item: PointOfSalePreviewItemService().providePointOfSaleItem(),
title: "Item Title",
subtitle: "Item Subtitle",
quantity: 2),
onItemRemoveTapped: { })
}
#Preview {
ScrollView {
ItemRowView(
cartItem: Cart.PurchasableItem(
id: UUID(),
item: PointOfSalePreviewItemService().providePointOfSaleItem(),
title: "Item Title",
subtitle: "Item Subtitle",
quantity: 2
),
onItemRemoveTapped: { }
)

@available(iOS 17.0, *)
#Preview(traits: .sizeThatFitsLayout) {
ItemRowView(cartItem: Cart.PurchasableItem(id: UUID(),
item: PointOfSalePreviewItemService().providePointOfSaleItem(),
title: "Item Title",
subtitle: nil,
quantity: 2),
onItemRemoveTapped: { })
}
ItemRowView(
cartItem: Cart.PurchasableItem(
id: UUID(),
item: PointOfSalePreviewItemService().providePointOfSaleItem(),
title: "Item Title With incredible long title that goes incredibly far and beyond",
subtitle: "Item Subtitle",
quantity: 2
),
onItemRemoveTapped: { }
)

@available(iOS 17.0, *)
#Preview(traits: .sizeThatFitsLayout) {
ItemRowView(cartItem: Cart.PurchasableItem.loading(id: UUID()),
onCancelLoading: { })
}
ItemRowView(
cartItem: Cart.PurchasableItem(
id: UUID(),
item: PointOfSalePreviewItemService().providePointOfSaleItem(),
title: "Item Title",
subtitle: nil,
quantity: 2
),
onItemRemoveTapped: { }
)

@available(iOS 17.0, *)
#Preview(traits: .sizeThatFitsLayout) {
ItemRowView.init(cartItem: Cart.PurchasableItem(
id: UUID(),
title: "123-123-123",
subtitle: "Unspported product type",
quantity: 1,
state: .error
))
ItemRowView(
cartItem: Cart.PurchasableItem.loading(id: UUID()),
onCancelLoading: { }
)

ItemRowView(
cartItem: Cart.PurchasableItem(
id: UUID(),
title: "123-123-123",
subtitle: "Unspported product type",
quantity: 1,
state: .error
)
)

ItemRowView(
cartItem: Cart.PurchasableItem(
id: UUID(),
title: "123-123-123",
subtitle: "Unspported product type with an incredibly long error message that goes on and on",
quantity: 1,
state: .error
)
)

ItemRowView(
cartItem: Cart.PurchasableItem(
id: UUID(),
item: PointOfSalePreviewItemService().providePointOfSaleItem(),
title: "Item Title",
subtitle: nil,
quantity: 2
),
showImage: .constant(false),
onItemRemoveTapped: { }
)

ItemRowView(
cartItem: Cart.PurchasableItem.loading(id: UUID()),
showImage: .constant(false),
onCancelLoading: { }
)
}
.frame(width: 400)
}
#endif
Loading
Loading