Skip to content

Commit a89afb4

Browse files
authored
[Woo POS][Barcodes] Partial setup flow for Star BSH-20B (#15893)
2 parents 9ce1249 + f29c579 commit a89afb4

File tree

14 files changed

+439
-35
lines changed

14 files changed

+439
-35
lines changed

WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetup.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ struct PointOfSaleBarcodeScannerSetup: View {
1818

1919
VStack {
2020
currentContent
21+
.frame(maxWidth: .infinity, maxHeight: .infinity)
2122
Spacer()
2223
}
2324
.scrollVerticallyIfNeeded()

WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupFlow.swift

Lines changed: 40 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,33 @@ class PointOfSaleBarcodeScannerSetupFlow {
8585
]
8686
case .starBSH20B:
8787
return [
88-
createWelcomeStep(title: "Star BSH-20B Setup")
89-
// TODO: Add more steps for Star BSH-20B WOOMOB-696
88+
PointOfSaleBarcodeScannerSetupStep(content: {
89+
PointOfSaleBarcodeScannerBarcodeView(
90+
title: String(format: Localization.starSetUpBarcodeStepTitleFormat, scannerType.name),
91+
instruction: Localization.setUpBarcodeStepInstruction,
92+
barcode: .starBsh20SetupBarcode)
93+
}),
94+
PointOfSaleBarcodeScannerSetupStep(content: {
95+
PointOfSaleBarcodeScannerPairingView(scanner: scannerType)
96+
}),
97+
PointOfSaleBarcodeScannerSetupStep(
98+
content: {
99+
PointOfSaleBarcodeScannerTestBarcodeView(
100+
scanTester: PointOfSaleBarcodeScannerSetupScanTester(
101+
onTestPass: { [weak self] in
102+
self?.nextStep()
103+
},
104+
onTestFailure: {},
105+
barcodeDefinition: .ean13)
106+
)
107+
},
108+
buttonCustomization: PointOfSaleBarcodeScannerBackOnlyButtonCustomization()
109+
),
110+
PointOfSaleBarcodeScannerSetupStep(
111+
content: {
112+
PointOfSaleBarcodeScannerSetupCompleteView()
113+
})
114+
// TODO: Add optional error step and documentation step for Star BSH-20B WOOMOB-696
90115
]
91116
case .tbcScanner:
92117
return [
@@ -112,24 +137,23 @@ class PointOfSaleBarcodeScannerSetupFlow {
112137
}
113138
}
114139

115-
// MARK: - Button Customizations
116140
@available(iOS 17.0, *)
117-
struct PointOfSaleBarcodeScannerWelcomeButtonCustomization: PointOfSaleBarcodeScannerButtonCustomization {
141+
struct PointOfSaleBarcodeScannerBackOnlyButtonCustomization: PointOfSaleBarcodeScannerButtonCustomization {
118142
func customizeButtons(for flow: PointOfSaleBarcodeScannerSetupFlow) -> PointOfSaleFlowButtonConfiguration {
119143
return PointOfSaleFlowButtonConfiguration(
120-
primaryButton: PointOfSaleFlowButtonConfiguration.ButtonConfig(
121-
title: Localization.doneButtonTitle,
122-
action: { flow.nextStep() }
123-
),
124-
secondaryButton: nil
144+
primaryButton: nil,
145+
secondaryButton: PointOfSaleFlowButtonConfiguration.ButtonConfig(
146+
title: Localization.backButtonTitle,
147+
action: { flow.previousStep() }
148+
)
125149
)
126150
}
127151

128152
private enum Localization {
129-
static let doneButtonTitle = NSLocalizedString(
130-
"pos.barcodeScannerSetup.done.button.title",
131-
value: "Done",
132-
comment: "Title for the done button in barcode scanner setup navigation"
153+
static let backButtonTitle = NSLocalizedString(
154+
"pos.barcodeScannerSetup.back.button.title",
155+
value: "Back",
156+
comment: "Title for the back button in barcode scanner setup navigation"
133157
)
134158
}
135159
}
@@ -153,5 +177,8 @@ private extension PointOfSaleBarcodeScannerSetupFlow {
153177
value: "Back",
154178
comment: "Title for the back button in barcode scanner setup navigation"
155179
)
180+
//TODO: WOOMOB-792
181+
static let starSetUpBarcodeStepTitleFormat = "%1$@ Setup"
182+
static let setUpBarcodeStepInstruction = "Scan the barcode to set up your scanner."
156183
}
157184
}

WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupModels.swift

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,29 @@ enum PointOfSaleBarcodeScannerType {
1313
case starBSH20B
1414
case tbcScanner
1515
case other
16+
17+
var name: String {
18+
switch self {
19+
case .socketS720:
20+
return Localization.socketS720Name
21+
case .starBSH20B:
22+
return Localization.starBsh20BName
23+
case .tbcScanner:
24+
return Localization.tbcScannerName
25+
case .other:
26+
return Localization.otherName
27+
}
28+
}
29+
}
30+
31+
private extension PointOfSaleBarcodeScannerType {
32+
//TODO: WOOMOB-792
33+
enum Localization {
34+
static let socketS720Name = "Socket S720"
35+
static let starBsh20BName = "Star BSH-20B"
36+
static let tbcScannerName = "TBC scanner"
37+
static let otherName = "Other scanner"
38+
}
1639
}
1740

1841
// MARK: - Flow State
@@ -35,7 +58,7 @@ struct PointOfSaleBarcodeScannerSetupStep {
3558
let buttonCustomization: PointOfSaleBarcodeScannerButtonCustomization?
3659

3760
init(
38-
title: String,
61+
title: String = "",
3962
@ViewBuilder content: () -> any View,
4063
buttonCustomization: PointOfSaleBarcodeScannerButtonCustomization? = nil
4164
) {
@@ -44,3 +67,22 @@ struct PointOfSaleBarcodeScannerSetupStep {
4467
self.buttonCustomization = buttonCustomization
4568
}
4669
}
70+
71+
// MARK: - Test Barcodes
72+
enum PointOfSaleBarcodeScannerTestBarcode {
73+
case ean13
74+
75+
var barcodeAsset: PointOfSaleAssets {
76+
switch self {
77+
case .ean13:
78+
return .testEan13Barcode
79+
}
80+
}
81+
82+
var expectedValue: String {
83+
switch self {
84+
case .ean13:
85+
return "1234567890128"
86+
}
87+
}
88+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import Foundation
2+
3+
struct PointOfSaleBarcodeScannerSetupScanTester {
4+
private let onTestPass: () -> Void
5+
private let onTestFailure: () -> Void
6+
private let barcodeDefinition: PointOfSaleBarcodeScannerTestBarcode
7+
8+
init(onTestPass: @escaping () -> Void, onTestFailure: @escaping () -> Void, barcodeDefinition: PointOfSaleBarcodeScannerTestBarcode) {
9+
self.onTestPass = onTestPass
10+
self.onTestFailure = onTestFailure
11+
self.barcodeDefinition = barcodeDefinition
12+
}
13+
14+
var barcode: PointOfSaleAssets {
15+
barcodeDefinition.barcodeAsset
16+
}
17+
18+
func handleScan(_ scanResult: Result<String, Error>) {
19+
switch scanResult {
20+
case .success(barcodeDefinition.expectedValue):
21+
onTestPass()
22+
case .success, .failure:
23+
onTestFailure()
24+
}
25+
}
26+
}
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
import SwiftUI
2+
3+
// TODO: Remove this view when all flows are complete
4+
struct PointOfSaleBarcodeScannerWelcomeView: View {
5+
let title: String
6+
7+
var body: some View {
8+
VStack(spacing: POSSpacing.medium) {
9+
Text(title)
10+
.font(.posBodyLargeBold)
11+
.foregroundColor(.posOnSurface)
12+
13+
Text("TODO: Implement \(title) setup flow")
14+
.font(.posBodyMediumRegular())
15+
.foregroundColor(.posOnSurfaceVariantHighest)
16+
.multilineTextAlignment(.center)
17+
}
18+
}
19+
}
20+
21+
struct PointOfSaleBarcodeScannerBarcodeView: View {
22+
let title: String
23+
let instruction: String
24+
let barcode: PointOfSaleAssets
25+
26+
var body: some View {
27+
VStack(spacing: POSSpacing.xLarge) {
28+
VStack(alignment: .center, spacing: POSSpacing.small) {
29+
Text(title)
30+
.font(.posHeadingBold)
31+
.foregroundColor(.posOnSurface)
32+
.accessibilityAddTraits(.isHeader)
33+
34+
Text(instruction)
35+
.font(.posBodyMediumRegular())
36+
.foregroundColor(.posOnSurfaceVariantHighest)
37+
.multilineTextAlignment(.center)
38+
}
39+
40+
Image(barcode.imageName)
41+
}
42+
}
43+
}
44+
45+
struct PointOfSaleBarcodeScannerPairingView: View {
46+
let scanner: PointOfSaleBarcodeScannerType
47+
48+
var body: some View {
49+
VStack(spacing: POSSpacing.xLarge) {
50+
// Temporary image until finalised assets are available
51+
Image(systemName: "gearshape")
52+
.font(.system(size: 78))
53+
.accessibilityHidden(true)
54+
55+
VStack(alignment: .center, spacing: POSSpacing.small) {
56+
Text(Localization.title)
57+
.font(.posHeadingBold)
58+
.foregroundColor(.posOnSurface)
59+
.accessibilityAddTraits(.isHeader)
60+
61+
Text(instruction)
62+
.font(.posBodyMediumRegular())
63+
.foregroundColor(.posOnSurfaceVariantHighest)
64+
.multilineTextAlignment(.center)
65+
}
66+
67+
Button {
68+
guard let targetURL = URL(string: UIApplication.openSettingsURLString) else {
69+
return
70+
}
71+
UIApplication.shared.open(targetURL)
72+
} label: {
73+
Text(Localization.settingsButtonTitle)
74+
}
75+
.buttonStyle(POSOutlinedButtonStyle(size: .extraSmall))
76+
}
77+
}
78+
79+
private var instruction: String {
80+
String(format: Localization.instructionFormat, scanner.name)
81+
}
82+
}
83+
84+
private extension PointOfSaleBarcodeScannerPairingView {
85+
//TODO: WOOMOB-792
86+
enum Localization {
87+
static let settingsButtonTitle = "Go to settings"
88+
static let title = "Pair your device"
89+
static let instructionFormat = "Enable Bluetooth and select your %1$@ scanner in iOS Settings."
90+
}
91+
}
92+
93+
@available(iOS 17.0, *)
94+
struct PointOfSaleBarcodeScannerTestBarcodeView: View {
95+
let scanTester: PointOfSaleBarcodeScannerSetupScanTester
96+
@State private var timerCompleted = false
97+
@State private var timer: Timer?
98+
99+
var body: some View {
100+
PointOfSaleBarcodeScannerBarcodeView(title: timerCompleted ? Localization.timeoutTitle : Localization.title,
101+
instruction: timerCompleted ? Localization.timeoutInstruction : Localization.instruction,
102+
barcode: scanTester.barcode)
103+
.barcodeScanning { result in
104+
scanTester.handleScan(result)
105+
}
106+
.onAppear {
107+
startTimer()
108+
}
109+
.onDisappear {
110+
timer?.invalidate()
111+
timer = nil
112+
}
113+
}
114+
115+
private func startTimer() {
116+
timer = Timer.scheduledTimer(withTimeInterval: 10.0, repeats: false) { _ in
117+
timerCompleted = true
118+
}
119+
}
120+
}
121+
122+
@available(iOS 17.0, *)
123+
private extension PointOfSaleBarcodeScannerTestBarcodeView {
124+
enum Localization {
125+
static let title = "Test your scanner"
126+
static let instruction = "Scan the barcode to test your scanner"
127+
static let timeoutTitle = "No scan data found yet"
128+
static let timeoutInstruction = "Scan the barcode to test your scanner. If the issue continues, please check Bluetooth settings and try again."
129+
}
130+
}
131+
132+
struct PointOfSaleBarcodeScannerSetupCompleteView: View {
133+
var body: some View {
134+
VStack(spacing: POSSpacing.xLarge) {
135+
// Temporary image until finalised assets are available
136+
successIcon
137+
.accessibilityHidden(true)
138+
139+
VStack(alignment: .center, spacing: POSSpacing.small) {
140+
Text(Localization.title)
141+
.font(.posHeadingBold)
142+
.foregroundColor(.posOnSurface)
143+
.accessibilityAddTraits(.isHeader)
144+
145+
Text(Localization.instruction)
146+
.font(.posBodyMediumRegular())
147+
.foregroundColor(.posOnSurfaceVariantHighest)
148+
.multilineTextAlignment(.center)
149+
}
150+
}
151+
}
152+
153+
@ViewBuilder private var successIcon: some View {
154+
ZStack {
155+
Circle()
156+
.frame(width: 104, height: 104)
157+
.foregroundColor(.posSuccess)
158+
Image(PointOfSaleAssets.successCheck.imageName)
159+
.renderingMode(.template)
160+
.resizable()
161+
.frame(width: 48, height: 48)
162+
.foregroundColor(.posOnSuccess)
163+
}
164+
}
165+
}
166+
167+
private extension PointOfSaleBarcodeScannerSetupCompleteView {
168+
enum Localization {
169+
//TODO: WOOMOB-792
170+
static let title = "Scanner set up!"
171+
static let instruction = "You are ready to start scanning products. \n" +
172+
"Read more about barcode and QR code scanner support."
173+
}
174+
}
175+
176+
// MARK: - Button Customizations
177+
@available(iOS 17.0, *)
178+
struct PointOfSaleBarcodeScannerWelcomeButtonCustomization: PointOfSaleBarcodeScannerButtonCustomization {
179+
func customizeButtons(for flow: PointOfSaleBarcodeScannerSetupFlow) -> PointOfSaleFlowButtonConfiguration {
180+
return PointOfSaleFlowButtonConfiguration(
181+
primaryButton: PointOfSaleFlowButtonConfiguration.ButtonConfig(
182+
title: Localization.doneButtonTitle,
183+
action: { flow.nextStep() }
184+
),
185+
secondaryButton: nil
186+
)
187+
}
188+
189+
private enum Localization {
190+
static let doneButtonTitle = NSLocalizedString(
191+
"pos.barcodeScannerSetup.done.button.title",
192+
value: "Done",
193+
comment: "Title for the done button in barcode scanner setup navigation"
194+
)
195+
}
196+
}

WooCommerce/Classes/POS/Presentation/Barcode Scanner Setup/PointOfSaleBarcodeScannerSetupViews.swift

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -56,25 +56,6 @@ struct PointOfSaleBarcodeScannerOptionView: View {
5656
}
5757
}
5858

59-
// MARK: - Step Views
60-
struct PointOfSaleBarcodeScannerWelcomeView: View {
61-
let title: String
62-
63-
var body: some View {
64-
VStack(spacing: POSSpacing.medium) {
65-
Text(title)
66-
.font(.posBodyLargeBold)
67-
.foregroundColor(.posOnSurface)
68-
69-
Text("TODO: Implement \(title) setup flow")
70-
.font(.posBodyMediumRegular())
71-
.foregroundColor(.posOnSurfaceVariantHighest)
72-
.multilineTextAlignment(.center)
73-
}
74-
.frame(maxWidth: .infinity, maxHeight: .infinity)
75-
}
76-
}
77-
7859
// MARK: - Private Localization Extensions
7960
private extension PointOfSaleBarcodeScannerSetupSelectionView {
8061
enum Localization {

0 commit comments

Comments
 (0)