Skip to content

Commit 88974ef

Browse files
committed
POS Modularization: Use FormattableAmountTextField through type-erased adaptor
Provide access to FormattableAmountTextField and ViewModel for POS without making it as an explicit type dependency. FormattableAmountTextField cannot be easily moved and reused in a shared module due to multiple dependencies. This is used as a workaround to enable POS modularization without requiring a larger refactoring effort.
1 parent 9da0a71 commit 88974ef

File tree

6 files changed

+79
-21
lines changed

6 files changed

+79
-21
lines changed

WooCommerce/Classes/POS/Adaptors/POSServiceLocatorAdaptor.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,10 @@ private struct POSExternalViewAdaptor: POSExternalViewProviding {
8484
defaultSite: ServiceLocator.stores.sessionManager.defaultSite))
8585
)
8686
}
87+
88+
func createFormattableAmountTextField(preset: Decimal?,
89+
onSubmit: @escaping () -> Void,
90+
onChange: @escaping (String) -> Void) -> AnyView {
91+
AnyView(POSFormattableAmountTextFieldAdaptor(preset: preset, onSubmit: onSubmit, onChange: onChange))
92+
}
8793
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import SwiftUI
2+
3+
/// Provide access to FormattableAmountTextField and ViewModel for POS without making it as an explicit type dependency
4+
/// FormattableAmountTextField cannot be easily moved and reused in a shared module due to multiple dependencies
5+
/// This is used as a workaround to enable POS modularization without requiring a larger refactoring effort
6+
///
7+
struct POSFormattableAmountTextFieldAdaptor: View {
8+
@StateObject private var textFieldViewModel: FormattableAmountTextFieldViewModel
9+
let preset: Decimal?
10+
let onSubmit: () -> Void
11+
let onChange: (String) -> Void
12+
13+
init(preset: Decimal?, onSubmit: @escaping () -> Void, onChange: @escaping (String) -> Void) {
14+
self._textFieldViewModel = StateObject(wrappedValue: FormattableAmountTextFieldViewModel(
15+
size: .extraLarge,
16+
locale: Locale.autoupdatingCurrent,
17+
storeCurrencySettings: ServiceLocator.currencySettings,
18+
allowNegativeNumber: false)
19+
)
20+
self.preset = preset
21+
self.onSubmit = onSubmit
22+
self.onChange = onChange
23+
}
24+
25+
var body: some View {
26+
FormattableAmountTextField(viewModel: textFieldViewModel, style: .pos)
27+
.dynamicTypeSize(...DynamicTypeSize.accessibility1)
28+
.onSubmit {
29+
onSubmit()
30+
}
31+
.onChange(of: textFieldViewModel.amount) { _, newValue in
32+
onChange(newValue)
33+
}
34+
.onAppear {
35+
if let preset {
36+
textFieldViewModel.presetAmount(preset)
37+
}
38+
}
39+
}
40+
}

WooCommerce/Classes/POS/Presentation/PointOfSaleCollectCashView.swift

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import WooFoundation
55
struct PointOfSaleCollectCashView: View {
66
@Environment(\.dynamicTypeSize) var dynamicTypeSize
77
@Environment(\.posAnalytics) var analytics
8+
@Environment(\.posExternalViews) var externalViews
89
@Environment(\.floatingControlAreaSize) private var floatingControlAreaSize: CGSize
910
@Environment(PointOfSaleAggregateModel.self) private var posModel
1011
@FocusState private var isTextFieldFocused: Bool
@@ -32,13 +33,7 @@ struct PointOfSaleCollectCashView: View {
3233
isLoading: isLoading)
3334
}
3435

35-
@StateObject private var textFieldViewModel: FormattableAmountTextFieldViewModel
36-
3736
init(orderTotal: String, currencySettings: CurrencySettings) {
38-
self._textFieldViewModel = StateObject(wrappedValue: FormattableAmountTextFieldViewModel(size: .extraLarge,
39-
locale: Locale.autoupdatingCurrent,
40-
storeCurrencySettings: currencySettings,
41-
allowNegativeNumber: false))
4237
self.viewHelper = CollectCashViewHelper(currencySettings: currencySettings)
4338
self.orderTotal = orderTotal
4439
}
@@ -61,18 +56,19 @@ struct PointOfSaleCollectCashView: View {
6156
Spacer()
6257

6358
VStack(alignment: .center, spacing: conditionalPadding(POSSpacing.xSmall)) {
64-
FormattableAmountTextField(viewModel: textFieldViewModel, style: .pos)
65-
.focused($isTextFieldFocused)
66-
.dynamicTypeSize(...DynamicTypeSize.accessibility1)
67-
.onSubmit {
59+
externalViews.createFormattableAmountTextField(
60+
preset: viewHelper.parseCurrency(orderTotal),
61+
onSubmit: {
6862
Task { @MainActor in
6963
await submitCashAmount()
7064
}
71-
}
72-
.onChange(of: textFieldViewModel.amount) { _, newValue in
65+
},
66+
onChange: { newValue in
7367
textFieldAmountInput = newValue
7468
updateChangeDueMessage()
7569
}
70+
)
71+
.focused($isTextFieldFocused)
7672

7773
if let changeDue = changeDueMessage {
7874
Text(changeDue)
@@ -124,7 +120,7 @@ struct PointOfSaleCollectCashView: View {
124120
}
125121
.frame(maxWidth: .infinity, maxHeight: .infinity)
126122
.onAppear {
127-
prefillOrderTotal()
123+
isTextFieldFocused = true
128124
}
129125
}
130126

@@ -153,13 +149,6 @@ private extension PointOfSaleCollectCashView {
153149
orderTotal: orderTotal,
154150
textFieldAmountInput: textFieldAmountInput)
155151
}
156-
157-
private func prefillOrderTotal() {
158-
if let orderDecimal = viewHelper.parseCurrency(orderTotal) {
159-
textFieldViewModel.presetAmount(orderDecimal)
160-
}
161-
isTextFieldFocused = true
162-
}
163152
}
164153

165154
private extension PointOfSaleCollectCashView {

WooCommerce/Classes/POS/Protocols/POSDependencyProviding.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,15 @@ protocol POSExternalNavigationProviding {
3939
func navigateToCreateOrder()
4040
}
4141

42-
/// Protocol that provides external view creation capabilities for POS
42+
/// Protocol that provides access to complex Woo application views that depend on a lot of Woo app target dependencies
43+
/// and cannot be easily moved and reused in a shared module
44+
/// This is used as a workaround to enable POS modularization without requiring a larger refactoring effort
45+
///
4346
protocol POSExternalViewProviding {
4447
func createSupportFormView(isPresented: Binding<Bool>, sourceTag: String) -> AnyView
48+
func createFormattableAmountTextField(preset: Decimal?,
49+
onSubmit: @escaping () -> Void,
50+
onChange: @escaping (String) -> Void) -> AnyView
4551
}
4652

4753
/// Main protocol that combines all POS dependency providers

WooCommerce/Classes/POS/Utils/POSEnvironmentKeys.swift

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,5 +122,10 @@ struct EmptyPOSAnalytics: POSAnalyticsProviding {
122122

123123
struct EmptyPOSExternalView: POSExternalViewProviding {
124124
func createSupportFormView(isPresented: Binding<Bool>, sourceTag: String) -> AnyView { AnyView(EmptyView()) }
125+
func createFormattableAmountTextField(preset: Decimal?,
126+
onSubmit: @escaping () -> Void,
127+
onChange: @escaping (String) -> Void) -> AnyView {
128+
AnyView(EmptyView())
129+
}
125130
init() {}
126131
}

WooCommerce/WooCommerce.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
0105865E2E426FDC002FADD1 /* UIKitBarcodeObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0105865D2E426FDB002FADD1 /* UIKitBarcodeObserver.swift */; };
2626
01058DD02E42716A002FADD1 /* BarcodeScannerContainerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01058DCF2E42716A002FADD1 /* BarcodeScannerContainerTests.swift */; };
2727
01058DD22E4273F2002FADD1 /* UIKitBarcodeObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01058DD12E4273F2002FADD1 /* UIKitBarcodeObserverTests.swift */; };
28+
010F7D872E79B39E002B02EA /* POSFormattableAmountTextFieldAdaptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 010F7D862E79B39E002B02EA /* POSFormattableAmountTextFieldAdaptor.swift */; };
2829
011D396F2D09FCD200DB1445 /* CardPresentModalLocationRequired.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D396E2D09FCCB00DB1445 /* CardPresentModalLocationRequired.swift */; };
2930
011D39712D0A324200DB1445 /* LocationServiceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D39702D0A324100DB1445 /* LocationServiceTests.swift */; };
3031
011D7A332CEC877A0007C187 /* CardPresentModalNonRetryableErrorEmailSent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 011D7A322CEC87770007C187 /* CardPresentModalNonRetryableErrorEmailSent.swift */; };
@@ -3234,6 +3235,7 @@
32343235
0105865D2E426FDB002FADD1 /* UIKitBarcodeObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitBarcodeObserver.swift; sourceTree = "<group>"; };
32353236
01058DCF2E42716A002FADD1 /* BarcodeScannerContainerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BarcodeScannerContainerTests.swift; sourceTree = "<group>"; };
32363237
01058DD12E4273F2002FADD1 /* UIKitBarcodeObserverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIKitBarcodeObserverTests.swift; sourceTree = "<group>"; };
3238+
010F7D862E79B39E002B02EA /* POSFormattableAmountTextFieldAdaptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = POSFormattableAmountTextFieldAdaptor.swift; sourceTree = "<group>"; };
32373239
011D396E2D09FCCB00DB1445 /* CardPresentModalLocationRequired.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalLocationRequired.swift; sourceTree = "<group>"; };
32383240
011D39702D0A324100DB1445 /* LocationServiceTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocationServiceTests.swift; sourceTree = "<group>"; };
32393241
011D7A322CEC87770007C187 /* CardPresentModalNonRetryableErrorEmailSent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CardPresentModalNonRetryableErrorEmailSent.swift; sourceTree = "<group>"; };
@@ -6475,6 +6477,14 @@
64756477
/* End PBXFrameworksBuildPhase section */
64766478

64776479
/* Begin PBXGroup section */
6480+
010F7D852E79B39A002B02EA /* View Adaptors */ = {
6481+
isa = PBXGroup;
6482+
children = (
6483+
010F7D862E79B39E002B02EA /* POSFormattableAmountTextFieldAdaptor.swift */,
6484+
);
6485+
path = "View Adaptors";
6486+
sourceTree = "<group>";
6487+
};
64786488
014BD4B62C64E26B0011A66E /* Order Messages */ = {
64796489
isa = PBXGroup;
64806490
children = (
@@ -6522,6 +6532,7 @@
65226532
01654EB02E786223001DBB6F /* Adaptors */ = {
65236533
isa = PBXGroup;
65246534
children = (
6535+
010F7D852E79B39A002B02EA /* View Adaptors */,
65256536
016582D42E78715B001DBB6F /* Card Present Payments */,
65266537
016582D52E78715B001DBB6F /* POSCollectOrderPaymentAnalyticsAdaptor.swift */,
65276538
01654EAF2E786223001DBB6F /* POSServiceLocatorAdaptor.swift */,
@@ -16880,6 +16891,7 @@
1688016891
DA41043A2C247B6900E8456A /* PointOfSalePreviewOrderController.swift in Sources */,
1688116892
20F6A46C2DE5FCEF0066D8CB /* POSItemFetchAnalytics.swift in Sources */,
1688216893
B933CCB02AA6220E00938F3F /* TaxRateRow.swift in Sources */,
16894+
010F7D872E79B39E002B02EA /* POSFormattableAmountTextFieldAdaptor.swift in Sources */,
1688316895
57532CAC24BFF4DA0032B84E /* MessageComposerPresenter.swift in Sources */,
1688416896
DE4D23AE29B1B0EF003A4B5D /* WPCom2FALoginViewModel.swift in Sources */,
1688516897
0270F47824D006F60005210A /* ProductFormPresentationStyle.swift in Sources */,

0 commit comments

Comments
 (0)