[#450] Migrate WalletEvents to ReducerProtocol (#457)

- WalletEvents migrated to ReducerProtocol
- unit and snapshot tests fixed
- some code cleanup, refactor and re-ordered
- WalletEvents refactored to WalletEventsReducer
This commit is contained in:
Lukas Korba 2022-11-05 07:14:44 +01:00 committed by GitHub
parent d6cb429372
commit 53011ff4c8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 103 additions and 178 deletions

View File

@ -25,9 +25,6 @@ struct BalanceBreakdownReducer: ReducerProtocol {
}
}
@Dependency(\.numberFormatter) var numberFormatter
@Dependency(\.sdkSynchronizer) var sdkSynchronizer
enum Action: Equatable {
case onAppear
case onDisappear
@ -35,6 +32,9 @@ struct BalanceBreakdownReducer: ReducerProtocol {
case updateLatestBlock
case updateSynchronizerStatus
}
@Dependency(\.numberFormatter) var numberFormatter
@Dependency(\.sdkSynchronizer) var sdkSynchronizer
func reduce(into state: inout State, action: Action) -> ComposableArchitecture.EffectTask<Action> {
switch action {

View File

@ -11,6 +11,7 @@ typealias HomeViewStore = ViewStore<HomeState, HomeAction>
typealias AnyBalanceBreakdownReducer = AnyReducer<BalanceBreakdownReducer.State, BalanceBreakdownReducer.Action, HomeEnvironment>
typealias AnyScanReducer = AnyReducer<ScanReducer.State, ScanReducer.Action, HomeEnvironment>
typealias AnyWalletEventsFlowReducer = AnyReducer<WalletEventsFlowReducer.State, WalletEventsFlowReducer.Action, HomeEnvironment>
// MARK: State
@ -35,7 +36,7 @@ struct HomeState: Equatable {
var sendState: SendFlowState
var shieldedBalance: WalletBalance
var synchronizerStatusSnapshot: SyncStatusSnapshot
var walletEventsState: WalletEventsFlowState
var walletEventsState: WalletEventsFlowReducer.State
// TODO [#311]: - Get the ZEC price from the SDK, https://github.com/zcash/secant-ios-wallet/issues/311
var zecPrice = Decimal(140.0)
@ -70,7 +71,7 @@ enum HomeAction: Equatable {
case send(SendFlowAction)
case scan(ScanReducer.Action)
case synchronizerStateChanged(WrappedSDKSynchronizerState)
case walletEvents(WalletEventsFlowAction)
case walletEvents(WalletEventsFlowReducer.Action)
case updateDrawer(DrawerOverlay)
case updateRoute(HomeState.Route?)
case updateSynchronizerStatus
@ -113,7 +114,7 @@ extension HomeReducer {
static let `default` = HomeReducer.combine(
[
homeReducer,
historyReducer,
walletEventsFlowReducer,
sendReducer,
scanReducer,
profileReducer,
@ -233,17 +234,13 @@ extension HomeReducer {
}
}
private static let historyReducer: HomeReducer = WalletEventsFlowReducer.default.pullback(
private static let walletEventsFlowReducer: HomeReducer = AnyWalletEventsFlowReducer { _ in
WalletEventsFlowReducer()
}
.pullback(
state: \HomeState.walletEventsState,
action: /HomeAction.walletEvents,
environment: { environment in
WalletEventsFlowEnvironment(
pasteboard: .live,
scheduler: environment.scheduler,
SDKSynchronizer: environment.SDKSynchronizer,
zcashSDKEnvironment: environment.zcashSDKEnvironment
)
}
environment: { $0 }
)
private static let sendReducer: HomeReducer = SendFlowReducer.default.pullback(

View File

@ -37,10 +37,6 @@ struct ImportWalletReducer: ReducerProtocol {
}
}
@Dependency(\.zcashSDKEnvironment) var zcashSDKEnvironment
@Dependency(\.mnemonic) var mnemonic
@Dependency(\.walletStorage) var walletStorage
enum Action: Equatable, BindableAction {
case binding(BindingAction<ImportWalletReducer.State>)
case dismissAlert
@ -50,7 +46,11 @@ struct ImportWalletReducer: ReducerProtocol {
case onAppear
case successfullyRecovered
}
@Dependency(\.zcashSDKEnvironment) var zcashSDKEnvironment
@Dependency(\.mnemonic) var mnemonic
@Dependency(\.walletStorage) var walletStorage
var body: some ReducerProtocol<State, Action> {
BindingReducer()

View File

@ -38,11 +38,6 @@ struct RecoveryPhraseValidationFlowReducer: ReducerProtocol {
return resultingPhrase == phrase.words
}
}
@Dependency(\.randomPhrase) var randomPhrase
@Dependency(\.mainQueue) var mainQueue
@Dependency(\.pasteboard) var pasteboard
@Dependency(\.feedbackGenerator) var feedbackGenerator
enum Action: Equatable {
case updateRoute(RecoveryPhraseValidationFlowReducer.State.Route?)
@ -55,6 +50,12 @@ struct RecoveryPhraseValidationFlowReducer: ReducerProtocol {
case displayBackedUpPhrase
}
@Dependency(\.randomPhrase) var randomPhrase
@Dependency(\.mainQueue) var mainQueue
@Dependency(\.pasteboard) var pasteboard
@Dependency(\.feedbackGenerator) var feedbackGenerator
// swiftlint:disable:next cyclomatic_complexity
func reduce(into state: inout State, action: Action) -> ComposableArchitecture.EffectTask<Action> {
switch action {

View File

@ -16,7 +16,7 @@ struct SandboxState: Equatable {
case scan
case request
}
var walletEventsState: WalletEventsFlowState
var walletEventsState: WalletEventsFlowReducer.State
var profileState: ProfileState
var route: Route?
}
@ -25,7 +25,7 @@ struct SandboxState: Equatable {
enum SandboxAction: Equatable {
case updateRoute(SandboxState.Route?)
case walletEvents(WalletEventsFlowAction)
case walletEvents(WalletEventsFlowReducer.Action)
case profile(ProfileAction)
case reset
}
@ -42,20 +42,12 @@ extension SandboxReducer {
case let .updateRoute(route):
state.route = route
return .none
case let .walletEvents(walletEventsAction):
return WalletEventsFlowReducer
.default
.run(
&state.walletEventsState,
walletEventsAction,
WalletEventsFlowEnvironment(
pasteboard: .live,
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
SDKSynchronizer: LiveWrappedSDKSynchronizer(),
zcashSDKEnvironment: .testnet
)
)
return WalletEventsFlowReducer()
.reduce(into: &state.walletEventsState, action: walletEventsAction)
.map(SandboxAction.walletEvents)
case let .profile(profileAction):
return ProfileReducer
.default
@ -65,6 +57,7 @@ extension SandboxReducer {
environment: { _ in ProfileEnvironment.live }
)
.run(&state, action, ())
case .reset:
return .none
}
@ -95,14 +88,14 @@ extension SandboxViewStore {
func toggleSelectedTransaction() {
let isAlreadySelected = (self.selectedTranactionID != nil)
let walletEvent = self.walletEventsState.walletEvents[5]
let newRoute = isAlreadySelected ? nil : WalletEventsFlowState.Route.showWalletEvent(walletEvent)
let newRoute = isAlreadySelected ? nil : WalletEventsFlowReducer.State.Route.showWalletEvent(walletEvent)
send(.walletEvents(.updateRoute(newRoute)))
}
var selectedTranactionID: String? {
self.walletEventsState
.route
.flatMap(/WalletEventsFlowState.Route.showWalletEvent)
.flatMap(/WalletEventsFlowReducer.State.Route.showWalletEvent)
.map(\.id)
}

View File

@ -2,80 +2,70 @@ import ComposableArchitecture
import SwiftUI
import ZcashLightClientKit
typealias WalletEventsFlowReducer = Reducer<WalletEventsFlowState, WalletEventsFlowAction, WalletEventsFlowEnvironment>
typealias WalletEventsFlowStore = Store<WalletEventsFlowState, WalletEventsFlowAction>
typealias WalletEventsFlowViewStore = ViewStore<WalletEventsFlowState, WalletEventsFlowAction>
typealias WalletEventsFlowStore = Store<WalletEventsFlowReducer.State, WalletEventsFlowReducer.Action>
typealias WalletEventsFlowViewStore = ViewStore<WalletEventsFlowReducer.State, WalletEventsFlowReducer.Action>
// MARK: - State
struct WalletEventsFlowReducer: ReducerProtocol {
private enum CancelId {}
struct WalletEventsFlowState: Equatable {
enum Route: Equatable {
case latest
case all
case showWalletEvent(WalletEvent)
struct State: Equatable {
enum Route: Equatable {
case latest
case all
case showWalletEvent(WalletEvent)
}
var route: Route?
@BindableState var alert: AlertState<WalletEventsFlowReducer.Action>?
var latestMinedHeight: BlockHeight?
var isScrollable = false
var requiredTransactionConfirmations = 0
var walletEvents = IdentifiedArrayOf<WalletEvent>.placeholder
var selectedWalletEvent: WalletEvent?
}
var route: Route?
@BindableState var alert: AlertState<WalletEventsFlowAction>?
var latestMinedHeight: BlockHeight?
var isScrollable = false
var requiredTransactionConfirmations = 0
var walletEvents = IdentifiedArrayOf<WalletEvent>.placeholder
var selectedWalletEvent: WalletEvent?
}
// MARK: - Action
enum WalletEventsFlowAction: Equatable {
case copyToPastboard(String)
case dismissAlert
case onAppear
case onDisappear
case openBlockExplorer(URL?)
case updateRoute(WalletEventsFlowState.Route?)
case replyTo(String)
case synchronizerStateChanged(WrappedSDKSynchronizerState)
case updateWalletEvents([WalletEvent])
case warnBeforeLeavingApp(URL?)
}
// MARK: - Environment
struct WalletEventsFlowEnvironment {
let pasteboard: WrappedPasteboard
let scheduler: AnySchedulerOf<DispatchQueue>
let SDKSynchronizer: WrappedSDKSynchronizer
let zcashSDKEnvironment: ZCashSDKEnvironment
}
// MARK: - Reducer
extension WalletEventsFlowReducer {
private struct CancelId: Hashable {}
enum Action: Equatable {
case copyToPastboard(String)
case dismissAlert
case onAppear
case onDisappear
case openBlockExplorer(URL?)
case updateRoute(WalletEventsFlowReducer.State.Route?)
case replyTo(String)
case synchronizerStateChanged(WrappedSDKSynchronizerState)
case updateWalletEvents([WalletEvent])
case warnBeforeLeavingApp(URL?)
}
static let `default` = WalletEventsFlowReducer { state, action, environment in
@Dependency(\.pasteboard) var pasteboard
@Dependency(\.mainQueue) var mainQueue
@Dependency(\.sdkSynchronizer) var sdkSynchronizer
@Dependency(\.zcashSDKEnvironment) var zcashSDKEnvironment
// swiftlint:disable:next cyclomatic_complexity
func reduce(into state: inout State, action: Action) -> ComposableArchitecture.EffectTask<Action> {
switch action {
case .onAppear:
state.requiredTransactionConfirmations = environment.zcashSDKEnvironment.requiredTransactionConfirmations
return environment.SDKSynchronizer.stateChanged
.map(WalletEventsFlowAction.synchronizerStateChanged)
state.requiredTransactionConfirmations = zcashSDKEnvironment.requiredTransactionConfirmations
return sdkSynchronizer.stateChanged
.map(WalletEventsFlowReducer.Action.synchronizerStateChanged)
.eraseToEffect()
.cancellable(id: CancelId(), cancelInFlight: true)
.cancellable(id: CancelId.self, cancelInFlight: true)
case .onDisappear:
return Effect.cancel(id: CancelId())
return Effect.cancel(id: CancelId.self)
case .synchronizerStateChanged(.synced):
if let latestMinedHeight = environment.SDKSynchronizer.synchronizer?.latestScannedHeight {
if let latestMinedHeight = sdkSynchronizer.synchronizer?.latestScannedHeight {
state.latestMinedHeight = latestMinedHeight
}
return environment.SDKSynchronizer.getAllTransactions()
.receive(on: environment.scheduler)
.map(WalletEventsFlowAction.updateWalletEvents)
return sdkSynchronizer.getAllTransactions()
.receive(on: mainQueue)
.map(WalletEventsFlowReducer.Action.updateWalletEvents)
.eraseToEffect()
case .synchronizerStateChanged(let synchronizerState):
case .synchronizerStateChanged:
return .none
case .updateWalletEvents(let walletEvents):
@ -99,10 +89,10 @@ extension WalletEventsFlowReducer {
return .none
case .copyToPastboard(let value):
environment.pasteboard.setString(value)
pasteboard.setString(value)
return .none
case .replyTo(let address):
case .replyTo:
return .none
case .dismissAlert:
@ -140,7 +130,7 @@ extension WalletEventsFlowReducer {
// MARK: - ViewStore
extension WalletEventsFlowViewStore {
private typealias Route = WalletEventsFlowState.Route
private typealias Route = WalletEventsFlowReducer.State.Route
func bindingForSelectedWalletEvent(_ walletEvent: WalletEvent?) -> Binding<Bool> {
self.binding(
@ -149,14 +139,14 @@ extension WalletEventsFlowViewStore {
return false
}
return $0.route.map(/WalletEventsFlowState.Route.showWalletEvent) == walletEvent
return $0.route.map(/WalletEventsFlowReducer.State.Route.showWalletEvent) == walletEvent
},
send: { isActive in
guard let walletEvent = walletEvent else {
return WalletEventsFlowAction.updateRoute(nil)
return WalletEventsFlowReducer.Action.updateRoute(nil)
}
return WalletEventsFlowAction.updateRoute( isActive ? WalletEventsFlowState.Route.showWalletEvent(walletEvent) : nil)
return WalletEventsFlowReducer.Action.updateRoute( isActive ? WalletEventsFlowReducer.State.Route.showWalletEvent(walletEvent) : nil)
}
)
}
@ -188,7 +178,7 @@ extension TransactionState {
}
}
extension WalletEventsFlowState {
extension WalletEventsFlowReducer.State {
static var placeHolder: Self {
.init(walletEvents: .placeholder)
}
@ -199,16 +189,11 @@ extension WalletEventsFlowState {
}
extension WalletEventsFlowStore {
static var placeholder: Store<WalletEventsFlowState, WalletEventsFlowAction> {
static var placeholder: Store<WalletEventsFlowReducer.State, WalletEventsFlowReducer.Action> {
return Store(
initialState: .placeHolder,
reducer: .default,
environment: WalletEventsFlowEnvironment(
pasteboard: .live,
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
SDKSynchronizer: LiveWrappedSDKSynchronizer(),
zcashSDKEnvironment: .testnet
)
reducer: WalletEventsFlowReducer()
.dependency(\.zcashSDKEnvironment, .testnet)
)
}
}

View File

@ -99,19 +99,10 @@ class WalletEventsSnapshotTests: XCTestCase {
environment: .demo
)
// wallet event detail
let testEnvironment = WalletEventsFlowEnvironment(
pasteboard: .test,
scheduler: DispatchQueue.test.eraseToAnyScheduler(),
SDKSynchronizer: TestWrappedSDKSynchronizer(),
zcashSDKEnvironment: .testnet
)
ViewStore(store).send(.walletEvents(.updateRoute(.showWalletEvent(walletEvent))))
let walletEventsStore = WalletEventsFlowStore(
initialState: .placeHolder,
reducer: .default,
environment: testEnvironment
reducer: WalletEventsFlowReducer()
)
addAttachments(
@ -156,19 +147,10 @@ class WalletEventsSnapshotTests: XCTestCase {
environment: .demo
)
// wallet event detail
let testEnvironment = WalletEventsFlowEnvironment(
pasteboard: .test,
scheduler: DispatchQueue.test.eraseToAnyScheduler(),
SDKSynchronizer: TestWrappedSDKSynchronizer(),
zcashSDKEnvironment: .testnet
)
ViewStore(store).send(.walletEvents(.updateRoute(.showWalletEvent(walletEvent))))
let walletEventsStore = WalletEventsFlowStore(
initialState: .placeHolder,
reducer: .default,
environment: testEnvironment
reducer: WalletEventsFlowReducer()
)
addAttachments(
@ -213,15 +195,7 @@ class WalletEventsSnapshotTests: XCTestCase {
environment: .demo
)
// wallet event detail
let testEnvironment = WalletEventsFlowEnvironment(
pasteboard: .test,
scheduler: DispatchQueue.test.eraseToAnyScheduler(),
SDKSynchronizer: TestWrappedSDKSynchronizer(),
zcashSDKEnvironment: .testnet
)
let walletEventsState = WalletEventsFlowState(
let walletEventsState = WalletEventsFlowReducer.State(
requiredTransactionConfirmations: 10,
walletEvents: .placeholder
)
@ -229,8 +203,7 @@ class WalletEventsSnapshotTests: XCTestCase {
ViewStore(store).send(.walletEvents(.updateRoute(.showWalletEvent(walletEvent))))
let walletEventsStore = WalletEventsFlowStore(
initialState: walletEventsState,
reducer: .default,
environment: testEnvironment
reducer: WalletEventsFlowReducer()
)
addAttachments(
@ -276,19 +249,10 @@ class WalletEventsSnapshotTests: XCTestCase {
environment: .demo
)
// wallet event detail
let testEnvironment = WalletEventsFlowEnvironment(
pasteboard: .test,
scheduler: DispatchQueue.test.eraseToAnyScheduler(),
SDKSynchronizer: TestWrappedSDKSynchronizer(),
zcashSDKEnvironment: .testnet
)
ViewStore(store).send(.walletEvents(.updateRoute(.showWalletEvent(walletEvent))))
let walletEventsStore = WalletEventsFlowStore(
initialState: .placeHolder,
reducer: .default,
environment: testEnvironment
reducer: WalletEventsFlowReducer()
)
addAttachments(

View File

@ -13,22 +13,14 @@ import ZcashLightClientKit
class WalletEventsTests: XCTestCase {
static let testScheduler = DispatchQueue.test
let testEnvironment = WalletEventsFlowEnvironment(
pasteboard: .test,
scheduler: testScheduler.eraseToAnyScheduler(),
SDKSynchronizer: TestWrappedSDKSynchronizer(),
zcashSDKEnvironment: .testnet
)
func testSynchronizerSubscription() throws {
let store = TestStore(
initialState: WalletEventsFlowState(
initialState: WalletEventsFlowReducer.State(
route: .latest,
isScrollable: true,
walletEvents: []
),
reducer: WalletEventsFlowReducer.default,
environment: testEnvironment
reducer: WalletEventsFlowReducer()
)
store.send(.onAppear) { state in
@ -78,13 +70,14 @@ class WalletEventsTests: XCTestCase {
let identifiedWalletEvents = IdentifiedArrayOf(uniqueElements: walletEvents)
let store = TestStore(
initialState: WalletEventsFlowState(
initialState: WalletEventsFlowReducer.State(
route: .latest,
isScrollable: true,
walletEvents: identifiedWalletEvents
),
reducer: WalletEventsFlowReducer.default,
environment: testEnvironment
reducer: WalletEventsFlowReducer()
.dependency(\.sdkSynchronizer, TestWrappedSDKSynchronizer())
.dependency(\.mainQueue, WalletEventsTests.testScheduler.eraseToAnyScheduler())
)
store.send(.synchronizerStateChanged(.synced))
@ -107,21 +100,13 @@ class WalletEventsTests: XCTestCase {
func testCopyToPasteboard() throws {
let pasteboard = WrappedPasteboard.test
let testEnvironment = WalletEventsFlowEnvironment(
pasteboard: pasteboard,
scheduler: DispatchQueue.test.eraseToAnyScheduler(),
SDKSynchronizer: TestWrappedSDKSynchronizer(),
zcashSDKEnvironment: .testnet
)
let store = TestStore(
initialState: WalletEventsFlowState(
initialState: WalletEventsFlowReducer.State(
route: .latest,
isScrollable: true,
walletEvents: []
),
reducer: WalletEventsFlowReducer.default,
environment: testEnvironment
reducer: WalletEventsFlowReducer()
)
let testText = "test text"