Skip to content

Commit 96bc85a

Browse files
Refactor PaymentMethodsUseCase to use a stateful state, instead of be totally reactive.
1 parent 9adade5 commit 96bc85a

File tree

1 file changed

+99
-64
lines changed

1 file changed

+99
-64
lines changed

Library/Sources/Library/Library/ViewModels/PaymentMethodsUseCase.swift

Lines changed: 99 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -39,61 +39,42 @@ public protocol PaymentMethodsUseCaseDataOutputs {
3939
* `selectedPaymentSource` - The currently selected credit card, or `nil` if no card is selected. Sent at least once after `initialData` is sent.
4040
* `paymentMethodChangedAndValid` - Whether or not the payment method is valid for the current pledge type. Sends an event after `initialData` and potentially more after `creditCardSelected(with:)` has happened.
4141
*/
42+
4243
public final class PaymentMethodsUseCase: PaymentMethodsUseCaseType, PaymentMethodsUseCaseUIInputs,
4344
PaymentMethodsUseCaseUIOutputs, PaymentMethodsUseCaseDataOutputs {
44-
init(initialData: Signal<PledgeViewData, Never>, isLoggedIn isLoggedInChanged: Signal<Bool, Never>) {
45-
let project = initialData.map(\.project)
46-
let baseReward = initialData.map(\.rewards).map(\.first).skipNil()
47-
let refTag = initialData.map(\.refTag)
48-
let context = initialData.map(\.context)
49-
50-
let initialDataUnpacked = Signal.zip(project, baseReward, refTag, context)
51-
let initialLoggedIn = initialData.map { _ in AppEnvironment.current.currentUser != nil }
52-
53-
let isLoggedIn = Signal.merge(
54-
initialLoggedIn,
55-
isLoggedInChanged
56-
).skipRepeats()
57-
58-
let configurePaymentMethodsViewController = Signal.merge(
59-
initialDataUnpacked,
60-
initialDataUnpacked.takeWhen(isLoggedIn.filter { $0 == true })
61-
)
45+
private var state: MutableProperty<PaymentMethodsUseCaseState?>
6246

63-
self.configurePaymentMethodsViewControllerWithValue = configurePaymentMethodsViewController
64-
.filter { !$3.paymentMethodsViewHidden }
65-
.compactMap { project, reward, refTag, context -> PledgePaymentMethodsValue? in
66-
guard let user = AppEnvironment.current.currentUser else { return nil }
67-
return (user, project, "", reward, context, refTag)
68-
}
69-
70-
self.paymentMethodsViewHidden = Signal.combineLatest(isLoggedIn, context)
71-
.map { !$0 || $1.paymentMethodsViewHidden }
47+
init(initialData: Signal<PledgeViewData, Never>, isLoggedIn isLoggedInChanged: Signal<Bool, Never>) {
48+
self.state = MutableProperty(nil)
7249

73-
self.selectedPaymentSource = Signal.merge(
74-
initialData.mapConst(nil),
75-
self.creditCardSelectedSignal.wrapInOptional()
50+
self.state <~ Signal.combineLatest(
51+
initialData,
52+
Signal.merge(initialData.map { _ in AppEnvironment.current.currentUser != nil }, isLoggedInChanged),
53+
Signal.merge(initialData.mapConst(nil), self.creditCardSelectedSignal.wrapInOptional())
7654
)
55+
.map { data, loggedIn, paymentSource in
56+
PaymentMethodsUseCaseState(
57+
data: data,
58+
isLoggedIn: loggedIn,
59+
paymentSourceSelected: paymentSource
60+
)
61+
}
7762

78-
let notChangingPaymentMethod = context.map { context in
79-
if context.isUpdating {
80-
return context == .updateReward || context == .editPledgeOverTime
81-
}
63+
self.paymentMethodsViewHidden = self.state.signal
64+
.skipNil()
65+
.map { $0.isPaymentMethodViewHidden }
66+
.skipRepeats()
8267

83-
return false
84-
}
68+
self.configurePaymentMethodsViewControllerWithValue = self.state.signal
69+
.map { $0?.configurePaymentMethodsViewControllerValue }
70+
.skipNil()
8571

86-
/// The `paymentMethodChangedAndValid` compares against the existing backing payment source id.
87-
self.paymentMethodChangedAndValid = Signal.merge(
88-
notChangingPaymentMethod,
89-
Signal.combineLatest(
90-
project,
91-
baseReward,
92-
self.creditCardSelectedSignal,
93-
context
94-
)
95-
.map(paymentMethodValid)
96-
)
72+
self.selectedPaymentSource = self.state.signal
73+
.map { $0?.paymentSourceSelected }
74+
75+
self.paymentMethodChangedAndValid = self.state.signal
76+
.map { $0?.paymentMethodChangedAndValid }
77+
.skipNil()
9778
}
9879

9980
public let paymentMethodsViewHidden: Signal<Bool, Never>
@@ -112,25 +93,79 @@ public final class PaymentMethodsUseCase: PaymentMethodsUseCaseType, PaymentMeth
11293
public var dataOutputs: PaymentMethodsUseCaseDataOutputs { return self }
11394
}
11495

115-
private func paymentMethodValid(
116-
project: Project,
117-
reward: Reward,
118-
paymentSource: PaymentSourceSelected,
119-
context: PledgeViewContext
120-
) -> Bool {
121-
guard
122-
let backedPaymentSourceId = project.personalization.backing?.paymentSource?.id,
123-
context.isUpdating,
124-
userIsBacking(reward: reward, inProject: project)
125-
else {
126-
return true
96+
private struct PaymentMethodsUseCaseState {
97+
let data: PledgeViewData
98+
var isLoggedIn: Bool
99+
var paymentSourceSelected: PaymentSourceSelected?
100+
101+
var isPaymentMethodViewHidden: Bool {
102+
if !self.isLoggedIn {
103+
return true
104+
}
105+
106+
return self.data.context.paymentMethodsViewHidden
107+
}
108+
109+
var baseReward: Reward? {
110+
return self.data.rewards.first
111+
}
112+
113+
var configurePaymentMethodsViewControllerValue: PledgePaymentMethodsValue? {
114+
if !self.isLoggedIn || self.isPaymentMethodViewHidden {
115+
return nil
116+
}
117+
118+
guard let user = AppEnvironment.current.currentUser,
119+
let reward = self.baseReward else {
120+
return nil
121+
}
122+
123+
return (user, self.data.project, "", reward, self.data.context, self.data.refTag)
127124
}
128125

129-
if project.personalization.backing?.status == .errored {
130-
return true
131-
} else if backedPaymentSourceId != paymentSource.savedCreditCardId {
132-
return true
126+
var notChangingPaymentMethod: Bool {
127+
let context = self.data.context
128+
129+
if context.isUpdating {
130+
return context == .updateReward || context == .editPledgeOverTime
131+
}
132+
133+
return false
133134
}
134135

135-
return false
136+
/// The `paymentMethodChangedAndValid` compares against the existing backing payment source id.
137+
var paymentMethodChangedAndValid: Bool {
138+
guard let reward = self.baseReward,
139+
let source = self.paymentSourceSelected else { return self.notChangingPaymentMethod }
140+
141+
return self.paymentMethodValid(
142+
project: self.data.project,
143+
reward: reward,
144+
paymentSource: source,
145+
context: self.data.context
146+
)
147+
}
148+
149+
private func paymentMethodValid(
150+
project: Project,
151+
reward: Reward,
152+
paymentSource: PaymentSourceSelected,
153+
context: PledgeViewContext
154+
) -> Bool {
155+
guard
156+
let backedPaymentSourceId = project.personalization.backing?.paymentSource?.id,
157+
context.isUpdating,
158+
userIsBacking(reward: reward, inProject: project)
159+
else {
160+
return true
161+
}
162+
163+
if project.personalization.backing?.status == .errored {
164+
return true
165+
} else if backedPaymentSourceId != paymentSource.savedCreditCardId {
166+
return true
167+
}
168+
169+
return false
170+
}
136171
}

0 commit comments

Comments
 (0)