Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ struct PointOfSaleBarcodeScannerSetup: View {

VStack {
currentContent
.frame(maxWidth: .infinity, maxHeight: .infinity)
Spacer()
}
.scrollVerticallyIfNeeded()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,33 @@ class PointOfSaleBarcodeScannerSetupFlow {
]
case .starBSH20B:
return [
createWelcomeStep(title: "Star BSH-20B Setup")
// TODO: Add more steps for Star BSH-20B WOOMOB-696
PointOfSaleBarcodeScannerSetupStep(content: {
PointOfSaleBarcodeScannerBarcodeView(
title: String(format: Localization.starSetUpBarcodeStepTitleFormat, scannerType.name),
instruction: Localization.setUpBarcodeStepInstruction,
barcode: .starBsh20SetupBarcode)
}),
PointOfSaleBarcodeScannerSetupStep(content: {
PointOfSaleBarcodeScannerPairingView(scanner: scannerType)
}),
PointOfSaleBarcodeScannerSetupStep(
content: {
PointOfSaleBarcodeScannerTestBarcodeView(
scanTester: PointOfSaleBarcodeScannerSetupScanTester(
onTestPass: { [weak self] in
self?.nextStep()
},
onTestFailure: {},
barcodeDefinition: .ean13)
)
},
buttonCustomization: PointOfSaleBarcodeScannerBackOnlyButtonCustomization()
),
PointOfSaleBarcodeScannerSetupStep(
content: {
PointOfSaleBarcodeScannerSetupCompleteView()
})
// TODO: Add optional error step and documentation step for Star BSH-20B WOOMOB-696
]
case .tbcScanner:
return [
Expand All @@ -112,24 +137,23 @@ class PointOfSaleBarcodeScannerSetupFlow {
}
}

// MARK: - Button Customizations
@available(iOS 17.0, *)
struct PointOfSaleBarcodeScannerWelcomeButtonCustomization: PointOfSaleBarcodeScannerButtonCustomization {
struct PointOfSaleBarcodeScannerBackOnlyButtonCustomization: PointOfSaleBarcodeScannerButtonCustomization {
func customizeButtons(for flow: PointOfSaleBarcodeScannerSetupFlow) -> PointOfSaleFlowButtonConfiguration {
return PointOfSaleFlowButtonConfiguration(
primaryButton: PointOfSaleFlowButtonConfiguration.ButtonConfig(
title: Localization.doneButtonTitle,
action: { flow.nextStep() }
),
secondaryButton: nil
primaryButton: nil,
secondaryButton: PointOfSaleFlowButtonConfiguration.ButtonConfig(
title: Localization.backButtonTitle,
action: { flow.previousStep() }
)
)
}

private enum Localization {
static let doneButtonTitle = NSLocalizedString(
"pos.barcodeScannerSetup.done.button.title",
value: "Done",
comment: "Title for the done button in barcode scanner setup navigation"
static let backButtonTitle = NSLocalizedString(
"pos.barcodeScannerSetup.back.button.title",
value: "Back",
comment: "Title for the back button in barcode scanner setup navigation"
)
}
}
Expand All @@ -153,5 +177,8 @@ private extension PointOfSaleBarcodeScannerSetupFlow {
value: "Back",
comment: "Title for the back button in barcode scanner setup navigation"
)
//TODO: WOOMOB-792
static let starSetUpBarcodeStepTitleFormat = "%1$@ Setup"
static let setUpBarcodeStepInstruction = "Scan the barcode to set up your scanner."
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,29 @@ enum PointOfSaleBarcodeScannerType {
case starBSH20B
case tbcScanner
case other

var name: String {
switch self {
case .socketS720:
return Localization.socketS720Name
case .starBSH20B:
return Localization.starBsh20BName
case .tbcScanner:
return Localization.tbcScannerName
case .other:
return Localization.otherName
}
}
}

private extension PointOfSaleBarcodeScannerType {
//TODO: WOOMOB-792
enum Localization {
static let socketS720Name = "Socket S720"
static let starBsh20BName = "Star BSH-20B"
static let tbcScannerName = "TBC scanner"
static let otherName = "Other scanner"
}
}

// MARK: - Flow State
Expand All @@ -35,7 +58,7 @@ struct PointOfSaleBarcodeScannerSetupStep {
let buttonCustomization: PointOfSaleBarcodeScannerButtonCustomization?

init(
title: String,
title: String = "",
@ViewBuilder content: () -> any View,
buttonCustomization: PointOfSaleBarcodeScannerButtonCustomization? = nil
) {
Expand All @@ -44,3 +67,22 @@ struct PointOfSaleBarcodeScannerSetupStep {
self.buttonCustomization = buttonCustomization
}
}

// MARK: - Test Barcodes
enum PointOfSaleBarcodeScannerTestBarcode {
case ean13

var barcodeAsset: PointOfSaleAssets {
switch self {
case .ean13:
return .testEan13Barcode
}
}

var expectedValue: String {
switch self {
case .ean13:
return "1234567890128"
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import Foundation

struct PointOfSaleBarcodeScannerSetupScanTester {
private let onTestPass: () -> Void
private let onTestFailure: () -> Void
private let barcodeDefinition: PointOfSaleBarcodeScannerTestBarcode

init(onTestPass: @escaping () -> Void, onTestFailure: @escaping () -> Void, barcodeDefinition: PointOfSaleBarcodeScannerTestBarcode) {
self.onTestPass = onTestPass
self.onTestFailure = onTestFailure
self.barcodeDefinition = barcodeDefinition
}

var barcode: PointOfSaleAssets {
barcodeDefinition.barcodeAsset
}

func handleScan(_ scanResult: Result<String, Error>) {
switch scanResult {
case .success(barcodeDefinition.expectedValue):
onTestPass()
case .success, .failure:
onTestFailure()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
import SwiftUI

// TODO: Remove this view when all flows are complete
struct PointOfSaleBarcodeScannerWelcomeView: View {
let title: String

var body: some View {
VStack(spacing: POSSpacing.medium) {
Text(title)
.font(.posBodyLargeBold)
.foregroundColor(.posOnSurface)

Text("TODO: Implement \(title) setup flow")
.font(.posBodyMediumRegular())
.foregroundColor(.posOnSurfaceVariantHighest)
.multilineTextAlignment(.center)
}
}
}

struct PointOfSaleBarcodeScannerBarcodeView: View {
let title: String
let instruction: String
let barcode: PointOfSaleAssets

var body: some View {
VStack(spacing: POSSpacing.xLarge) {
VStack(alignment: .center, spacing: POSSpacing.small) {
Text(title)
.font(.posHeadingBold)
.foregroundColor(.posOnSurface)
.accessibilityAddTraits(.isHeader)

Text(instruction)
.font(.posBodyMediumRegular())
.foregroundColor(.posOnSurfaceVariantHighest)
.multilineTextAlignment(.center)
}

Image(barcode.imageName)
}
}
}

struct PointOfSaleBarcodeScannerPairingView: View {
let scanner: PointOfSaleBarcodeScannerType

var body: some View {
VStack(spacing: POSSpacing.xLarge) {
// Temporary image until finalised assets are available
Image(systemName: "gearshape")
.font(.system(size: 78))
.accessibilityHidden(true)

VStack(alignment: .center, spacing: POSSpacing.small) {
Text(Localization.title)
.font(.posHeadingBold)
.foregroundColor(.posOnSurface)
.accessibilityAddTraits(.isHeader)

Text(instruction)
.font(.posBodyMediumRegular())
.foregroundColor(.posOnSurfaceVariantHighest)
.multilineTextAlignment(.center)
}

Button {
guard let targetURL = URL(string: UIApplication.openSettingsURLString) else {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit annoying because we want to open General or Bluetooth settings, but this URL opens the app's specific settings. I looked up, there's an option to use URL(string: "App-Prefs:root=General", but from some comments, it could be rejected, so maybe it's not worth risking.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that was my finding as well

return
}
UIApplication.shared.open(targetURL)
} label: {
Text(Localization.settingsButtonTitle)
}
.buttonStyle(POSOutlinedButtonStyle(size: .extraSmall))
}
}

private var instruction: String {
String(format: Localization.instructionFormat, scanner.name)
}
}

private extension PointOfSaleBarcodeScannerPairingView {
//TODO: WOOMOB-792
enum Localization {
static let settingsButtonTitle = "Go to settings"
static let title = "Pair your device"
static let instructionFormat = "Enable Bluetooth and select your %1$@ scanner in iOS Settings."
}
}

@available(iOS 17.0, *)
struct PointOfSaleBarcodeScannerTestBarcodeView: View {
let scanTester: PointOfSaleBarcodeScannerSetupScanTester
@State private var timerCompleted = false
@State private var timer: Timer?

var body: some View {
PointOfSaleBarcodeScannerBarcodeView(title: timerCompleted ? Localization.timeoutTitle : Localization.title,
instruction: timerCompleted ? Localization.timeoutInstruction : Localization.instruction,
barcode: scanTester.barcode)
.barcodeScanning { result in
scanTester.handleScan(result)
}
.onAppear {
startTimer()
}
.onDisappear {
timer?.invalidate()
timer = nil
}
}

private func startTimer() {
timer = Timer.scheduledTimer(withTimeInterval: 10.0, repeats: false) { _ in
timerCompleted = true
}
}
}

@available(iOS 17.0, *)
private extension PointOfSaleBarcodeScannerTestBarcodeView {
enum Localization {
static let title = "Test your scanner"
static let instruction = "Scan the barcode to test your scanner"
static let timeoutTitle = "No scan data found yet"
static let timeoutInstruction = "Scan the barcode to test your scanner. If the issue continues, please check Bluetooth settings and try again."
}
}

struct PointOfSaleBarcodeScannerSetupCompleteView: View {
var body: some View {
VStack(spacing: POSSpacing.xLarge) {
// Temporary image until finalised assets are available
successIcon
.accessibilityHidden(true)

VStack(alignment: .center, spacing: POSSpacing.small) {
Text(Localization.title)
.font(.posHeadingBold)
.foregroundColor(.posOnSurface)
.accessibilityAddTraits(.isHeader)

Text(Localization.instruction)
.font(.posBodyMediumRegular())
.foregroundColor(.posOnSurfaceVariantHighest)
.multilineTextAlignment(.center)
}
}
}

@ViewBuilder private var successIcon: some View {
ZStack {
Circle()
.frame(width: 104, height: 104)
.foregroundColor(.posSuccess)
Image(PointOfSaleAssets.successCheck.imageName)
.renderingMode(.template)
.resizable()
.frame(width: 48, height: 48)
.foregroundColor(.posOnSuccess)
}
}
}

private extension PointOfSaleBarcodeScannerSetupCompleteView {
enum Localization {
//TODO: WOOMOB-792
static let title = "Scanner set up!"
static let instruction = "You are ready to start scanning products. \n" +
"Read more about barcode and QR code scanner support."
}
}

// MARK: - Button Customizations
@available(iOS 17.0, *)
struct PointOfSaleBarcodeScannerWelcomeButtonCustomization: PointOfSaleBarcodeScannerButtonCustomization {
func customizeButtons(for flow: PointOfSaleBarcodeScannerSetupFlow) -> PointOfSaleFlowButtonConfiguration {
return PointOfSaleFlowButtonConfiguration(
primaryButton: PointOfSaleFlowButtonConfiguration.ButtonConfig(
title: Localization.doneButtonTitle,
action: { flow.nextStep() }
),
secondaryButton: nil
)
}

private enum Localization {
static let doneButtonTitle = NSLocalizedString(
"pos.barcodeScannerSetup.done.button.title",
value: "Done",
comment: "Title for the done button in barcode scanner setup navigation"
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,25 +56,6 @@ struct PointOfSaleBarcodeScannerOptionView: View {
}
}

// MARK: - Step Views
struct PointOfSaleBarcodeScannerWelcomeView: View {
let title: String

var body: some View {
VStack(spacing: POSSpacing.medium) {
Text(title)
.font(.posBodyLargeBold)
.foregroundColor(.posOnSurface)

Text("TODO: Implement \(title) setup flow")
.font(.posBodyMediumRegular())
.foregroundColor(.posOnSurfaceVariantHighest)
.multilineTextAlignment(.center)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}

// MARK: - Private Localization Extensions
private extension PointOfSaleBarcodeScannerSetupSelectionView {
enum Localization {
Expand Down
Loading