- previous profile screen connected to the receive ZEC button - receive ZEC is now simplified to show only QR code + UA with small "i" icon leading to address details - profile's UA address copy to pasteboard added - home's settings button connected to settings screen - settings screen updated, test crash report and rescan blockchain moved to debug menu - root reducer's debug code move to a separate file - unit tests updated + debug tests provided
This commit is contained in:
parent
f1c9b06123
commit
49d858d22a
|
@ -447,6 +447,9 @@
|
|||
9E7FE0F628327F6F00C374E8 /* ScanUIView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0F528327F6F00C374E8 /* ScanUIView.swift */; };
|
||||
9E7FE0F92832824C00C374E8 /* QRCodeScanView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0F82832824C00C374E8 /* QRCodeScanView.swift */; };
|
||||
9E852D5C29AF8EB200CF4AC1 /* RecoveryPhraseValidationFlowFeatureFlagTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E852D5B29AF8EB200CF4AC1 /* RecoveryPhraseValidationFlowFeatureFlagTests.swift */; };
|
||||
9E852D6129B098F400CF4AC1 /* RootDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E852D6029B098F400CF4AC1 /* RootDebug.swift */; };
|
||||
9E852D6229B098F400CF4AC1 /* RootDebug.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E852D6029B098F400CF4AC1 /* RootDebug.swift */; };
|
||||
9E852D6529B0A86300CF4AC1 /* DebugTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E852D6429B0A86300CF4AC1 /* DebugTests.swift */; };
|
||||
9E92AF0828530EBF007367AD /* View+UIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E92AF0728530EBF007367AD /* View+UIImage.swift */; };
|
||||
9E94C62028AA7DEE008256E9 /* BalanceBreakdownTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E94C61F28AA7DEE008256E9 /* BalanceBreakdownTests.swift */; };
|
||||
9E94C62328AA7EE0008256E9 /* BalanceBreakdownSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E94C62228AA7EE0008256E9 /* BalanceBreakdownSnapshotTests.swift */; };
|
||||
|
@ -770,6 +773,8 @@
|
|||
9E7FE0F528327F6F00C374E8 /* ScanUIView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanUIView.swift; sourceTree = "<group>"; };
|
||||
9E7FE0F82832824C00C374E8 /* QRCodeScanView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeScanView.swift; sourceTree = "<group>"; };
|
||||
9E852D5B29AF8EB200CF4AC1 /* RecoveryPhraseValidationFlowFeatureFlagTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecoveryPhraseValidationFlowFeatureFlagTests.swift; sourceTree = "<group>"; };
|
||||
9E852D6029B098F400CF4AC1 /* RootDebug.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootDebug.swift; sourceTree = "<group>"; };
|
||||
9E852D6429B0A86300CF4AC1 /* DebugTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugTests.swift; sourceTree = "<group>"; };
|
||||
9E92AF0728530EBF007367AD /* View+UIImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+UIImage.swift"; sourceTree = "<group>"; };
|
||||
9E94C61F28AA7DEE008256E9 /* BalanceBreakdownTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BalanceBreakdownTests.swift; sourceTree = "<group>"; };
|
||||
9E94C62228AA7EE0008256E9 /* BalanceBreakdownSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BalanceBreakdownSnapshotTests.swift; sourceTree = "<group>"; };
|
||||
|
@ -1852,6 +1857,7 @@
|
|||
children = (
|
||||
9EAFEB812805793200199FC9 /* RootTests.swift */,
|
||||
9E391131284644580073DD9A /* AppInitializationTests.swift */,
|
||||
9E852D6429B0A86300CF4AC1 /* DebugTests.swift */,
|
||||
);
|
||||
path = RootTests;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2104,6 +2110,7 @@
|
|||
F9971A4C27680DC400A2DB75 /* RootView.swift */,
|
||||
9E9ADA7E2938F5EC0071767B /* RootDestination.swift */,
|
||||
9E9ADA7C2938F4C00071767B /* RootInitialization.swift */,
|
||||
9E852D6029B098F400CF4AC1 /* RootDebug.swift */,
|
||||
);
|
||||
path = Root;
|
||||
sourceTree = "<group>";
|
||||
|
@ -2686,6 +2693,7 @@
|
|||
0D26AF01299E8196005260EE /* RecoveryPhraseDisplayView.swift in Sources */,
|
||||
0D26AF02299E8196005260EE /* URIParser.swift in Sources */,
|
||||
0D26AF03299E8196005260EE /* URIParserLive.swift in Sources */,
|
||||
9E852D6229B098F400CF4AC1 /* RootDebug.swift in Sources */,
|
||||
34F682ED29A763FD0022C079 /* WalletConfigProvider.swift in Sources */,
|
||||
0D26AF04299E8196005260EE /* LocalAuthenticationTestKey.swift in Sources */,
|
||||
0D26AF05299E8196005260EE /* ScanView.swift in Sources */,
|
||||
|
@ -2915,6 +2923,7 @@
|
|||
0D3D04082728B3440032ABC1 /* RecoveryPhraseDisplayView.swift in Sources */,
|
||||
9EB863A2292398A8003D0F8B /* URIParser.swift in Sources */,
|
||||
9EB863C12923C779003D0F8B /* URIParserLive.swift in Sources */,
|
||||
9E852D6129B098F400CF4AC1 /* RootDebug.swift in Sources */,
|
||||
34F682EC29A763FD0022C079 /* WalletConfigProvider.swift in Sources */,
|
||||
9EBDF987291F91EF000A1A05 /* LocalAuthenticationTestKey.swift in Sources */,
|
||||
F9971A5F27680DF600A2DB75 /* ScanView.swift in Sources */,
|
||||
|
@ -3053,6 +3062,7 @@
|
|||
9EAFEB862805A23100199FC9 /* SecItemClientTests.swift in Sources */,
|
||||
3448CB3728E485CB006ADEDB /* NotEnoughFeeSpaceSnapshots.swift in Sources */,
|
||||
9E9ECC9828589E150099D5A2 /* WelcomeSnapshotTests.swift in Sources */,
|
||||
9E852D6529B0A86300CF4AC1 /* DebugTests.swift in Sources */,
|
||||
9E7CB6122869882D00A02233 /* WalletEventsSnapshotTests.swift in Sources */,
|
||||
9E5BF644281FEC9900BA3F17 /* SendTests.swift in Sources */,
|
||||
9E852D5C29AF8EB200CF4AC1 /* RecoveryPhraseValidationFlowFeatureFlagTests.swift in Sources */,
|
||||
|
|
|
@ -4,9 +4,11 @@
|
|||
//
|
||||
// Created by Francisco Gindre on 2/2/23.
|
||||
//
|
||||
|
||||
import ComposableArchitecture
|
||||
import FirebaseCore
|
||||
import FirebaseCrashlytics
|
||||
|
||||
extension CrashReporterClient: DependencyKey {
|
||||
static let liveValue = CrashReporterClient(
|
||||
configure: { canConfigure in
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
//
|
||||
|
||||
import ComposableArchitecture
|
||||
|
||||
extension CrashReporterClient: TestDependencyKey {
|
||||
static let testValue = CrashReporterClient(
|
||||
configure: { _ in },
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
//
|
||||
// Created by Francisco Gindre on 2/2/23.
|
||||
//
|
||||
|
||||
import ComposableArchitecture
|
||||
import Foundation
|
||||
|
||||
|
|
|
@ -16,8 +16,8 @@ struct HomeReducer: ReducerProtocol {
|
|||
case balanceBreakdown
|
||||
case notEnoughFreeDiskSpace
|
||||
case profile
|
||||
case request
|
||||
case send
|
||||
case settings
|
||||
case transactionHistory
|
||||
}
|
||||
|
||||
|
@ -25,10 +25,10 @@ struct HomeReducer: ReducerProtocol {
|
|||
var balanceBreakdownState: BalanceBreakdownReducer.State
|
||||
var destination: Destination?
|
||||
var profileState: ProfileReducer.State
|
||||
var requestState: RequestReducer.State
|
||||
var requiredTransactionConfirmations = 0
|
||||
var scanState: ScanReducer.State
|
||||
var sendState: SendFlowReducer.State
|
||||
var settingsState: SettingsReducer.State
|
||||
var shieldedBalance: Balance
|
||||
var synchronizerStatusSnapshot: SyncStatusSnapshot
|
||||
var walletEventsState: WalletEventsFlowReducer.State
|
||||
|
@ -61,9 +61,8 @@ struct HomeReducer: ReducerProtocol {
|
|||
case onAppear
|
||||
case onDisappear
|
||||
case profile(ProfileReducer.Action)
|
||||
case request(RequestReducer.Action)
|
||||
case rewindDone(String?, SettingsReducer.Action)
|
||||
case send(SendFlowReducer.Action)
|
||||
case settings(SettingsReducer.Action)
|
||||
case synchronizerStateChanged(SDKSynchronizerState)
|
||||
case walletEvents(WalletEventsFlowReducer.Action)
|
||||
case updateDestination(HomeReducer.State.Destination?)
|
||||
|
@ -86,6 +85,10 @@ struct HomeReducer: ReducerProtocol {
|
|||
SendFlowReducer()
|
||||
}
|
||||
|
||||
Scope(state: \.settingsState, action: /Action.settings) {
|
||||
SettingsReducer()
|
||||
}
|
||||
|
||||
Scope(state: \.profileState, action: /Action.profile) {
|
||||
ProfileReducer()
|
||||
}
|
||||
|
@ -137,45 +140,12 @@ struct HomeReducer: ReducerProtocol {
|
|||
case .profile(.back):
|
||||
state.destination = nil
|
||||
return .none
|
||||
|
||||
case .profile(.settings(.quickRescan)):
|
||||
state.destination = nil
|
||||
return .run { send in
|
||||
do {
|
||||
try await sdkSynchronizer.rewind(.quick)
|
||||
await send(.rewindDone(nil, .quickRescan))
|
||||
} catch {
|
||||
await send(.rewindDone(error.localizedDescription, .quickRescan))
|
||||
}
|
||||
}
|
||||
|
||||
case .profile(.settings(.fullRescan)):
|
||||
state.destination = nil
|
||||
return .run { send in
|
||||
do {
|
||||
try await sdkSynchronizer.rewind(.birthday)
|
||||
await send(.rewindDone(nil, .fullRescan))
|
||||
} catch {
|
||||
await send(.rewindDone(error.localizedDescription, .fullRescan))
|
||||
}
|
||||
}
|
||||
|
||||
case .settings:
|
||||
return .none
|
||||
|
||||
case .profile:
|
||||
return .none
|
||||
|
||||
case .request:
|
||||
return .none
|
||||
|
||||
case let .rewindDone(errorDescription, _):
|
||||
if let errorDescription {
|
||||
// TODO: [#221] Handle error more properly (https://github.com/zcash/secant-ios-wallet/issues/221)
|
||||
state.alert = AlertState(
|
||||
title: TextState("Rewind failed"),
|
||||
message: TextState("Error: \(errorDescription)"),
|
||||
dismissButton: .default(TextState("Ok"), action: .send(.dismissAlert))
|
||||
)
|
||||
}
|
||||
return .none
|
||||
|
||||
case .walletEvents:
|
||||
return .none
|
||||
|
@ -221,13 +191,6 @@ extension HomeStore {
|
|||
)
|
||||
}
|
||||
|
||||
func requestStore() -> RequestStore {
|
||||
self.scope(
|
||||
state: \.requestState,
|
||||
action: HomeReducer.Action.request
|
||||
)
|
||||
}
|
||||
|
||||
func sendStore() -> SendFlowStore {
|
||||
self.scope(
|
||||
state: \.sendState,
|
||||
|
@ -235,6 +198,13 @@ extension HomeStore {
|
|||
)
|
||||
}
|
||||
|
||||
func settingsStore() -> SettingsStore {
|
||||
self.scope(
|
||||
state: \.settingsState,
|
||||
action: HomeReducer.Action.settings
|
||||
)
|
||||
}
|
||||
|
||||
func balanceBreakdownStore() -> BalanceBreakdownStore {
|
||||
self.scope(
|
||||
state: \.balanceBreakdownState,
|
||||
|
@ -263,9 +233,9 @@ extension HomeReducer.State {
|
|||
.init(
|
||||
balanceBreakdownState: .placeholder,
|
||||
profileState: .placeholder,
|
||||
requestState: .placeholder,
|
||||
scanState: .placeholder,
|
||||
sendState: .placeholder,
|
||||
settingsState: .placeholder,
|
||||
shieldedBalance: Balance.zero,
|
||||
synchronizerStatusSnapshot: .default,
|
||||
walletEventsState: .emptyPlaceHolder
|
||||
|
|
|
@ -8,9 +8,9 @@ struct HomeView: View {
|
|||
WithViewStore(store) { viewStore in
|
||||
VStack {
|
||||
HStack {
|
||||
profileButton(viewStore)
|
||||
|
||||
Spacer()
|
||||
|
||||
settingsButton(viewStore)
|
||||
}
|
||||
|
||||
balance(viewStore)
|
||||
|
@ -18,6 +18,8 @@ struct HomeView: View {
|
|||
Spacer()
|
||||
|
||||
sendButton(viewStore)
|
||||
|
||||
receiveButton(viewStore)
|
||||
|
||||
Button {
|
||||
viewStore.send(.updateDestination(.transactionHistory))
|
||||
|
@ -48,21 +50,21 @@ struct HomeView: View {
|
|||
// MARK: - Buttons
|
||||
|
||||
extension HomeView {
|
||||
func profileButton(_ viewStore: HomeViewStore) -> some View {
|
||||
func settingsButton(_ viewStore: HomeViewStore) -> some View {
|
||||
Image(Asset.Assets.Icons.profile.name)
|
||||
.resizable()
|
||||
.frame(width: 60, height: 60)
|
||||
.padding(.trailing, 15)
|
||||
.navigationLink(
|
||||
isActive: viewStore.bindingForDestination(.profile),
|
||||
isActive: viewStore.bindingForDestination(.settings),
|
||||
destination: {
|
||||
ProfileView(store: store.profileStore())
|
||||
SettingsView(store: store.settingsStore())
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func sendButton(_ viewStore: HomeViewStore) -> some View {
|
||||
Text("Send")
|
||||
Text("Send ZEC")
|
||||
.shadow(color: Asset.Colors.Buttons.buttonsTitleShadow.color, radius: 2, x: 0, y: 2)
|
||||
.frame(
|
||||
minWidth: 0,
|
||||
|
@ -85,6 +87,30 @@ extension HomeView {
|
|||
.padding(.bottom, 30)
|
||||
}
|
||||
|
||||
func receiveButton(_ viewStore: HomeViewStore) -> some View {
|
||||
Text("Receive ZEC")
|
||||
.shadow(color: Asset.Colors.Buttons.buttonsTitleShadow.color, radius: 2, x: 0, y: 2)
|
||||
.frame(
|
||||
minWidth: 0,
|
||||
maxWidth: .infinity,
|
||||
minHeight: 0,
|
||||
maxHeight: .infinity
|
||||
)
|
||||
.foregroundColor(Asset.Colors.Text.activeButtonText.color)
|
||||
.background(Asset.Colors.Buttons.activeButton.color)
|
||||
.cornerRadius(12)
|
||||
.frame(height: 60)
|
||||
.padding(.horizontal, 50)
|
||||
.neumorphicButton()
|
||||
.navigationLink(
|
||||
isActive: viewStore.bindingForDestination(.profile),
|
||||
destination: {
|
||||
ProfileView(store: store.profileStore())
|
||||
}
|
||||
)
|
||||
.padding(.bottom, 30)
|
||||
}
|
||||
|
||||
func balance(_ viewStore: HomeViewStore) -> some View {
|
||||
Group {
|
||||
Button {
|
||||
|
|
|
@ -9,7 +9,6 @@ struct ProfileReducer: ReducerProtocol {
|
|||
struct State: Equatable {
|
||||
enum Destination {
|
||||
case addressDetails
|
||||
case settings
|
||||
}
|
||||
|
||||
var addressDetailsState: AddressDetailsReducer.State
|
||||
|
@ -17,7 +16,6 @@ struct ProfileReducer: ReducerProtocol {
|
|||
var appVersion = ""
|
||||
var destination: Destination?
|
||||
var sdkVersion = ""
|
||||
var settingsState: SettingsReducer.State
|
||||
|
||||
var unifiedAddress: String {
|
||||
addressDetailsState.uAddress?.stringEncoded ?? "could not extract UA"
|
||||
|
@ -27,12 +25,13 @@ struct ProfileReducer: ReducerProtocol {
|
|||
enum Action: Equatable {
|
||||
case addressDetails(AddressDetailsReducer.Action)
|
||||
case back
|
||||
case copyUnifiedAddressToPastboard
|
||||
case onAppear
|
||||
case settings(SettingsReducer.Action)
|
||||
case updateDestination(ProfileReducer.State.Destination?)
|
||||
}
|
||||
|
||||
@Dependency(\.appVersion) var appVersion
|
||||
@Dependency(\.pasteboard) var pasteboard
|
||||
@Dependency(\.sdkSynchronizer) var sdkSynchronizer
|
||||
@Dependency(\.zcashSDKEnvironment) var zcashSDKEnvironment
|
||||
|
||||
|
@ -41,10 +40,6 @@ struct ProfileReducer: ReducerProtocol {
|
|||
AddressDetailsReducer()
|
||||
}
|
||||
|
||||
Scope(state: \.settingsState, action: /Action.settings) {
|
||||
SettingsReducer()
|
||||
}
|
||||
|
||||
Reduce { state, action in
|
||||
switch action {
|
||||
case .onAppear:
|
||||
|
@ -56,6 +51,10 @@ struct ProfileReducer: ReducerProtocol {
|
|||
|
||||
case .back:
|
||||
return .none
|
||||
|
||||
case .copyUnifiedAddressToPastboard:
|
||||
pasteboard.setString(state.unifiedAddress.redacted)
|
||||
return .none
|
||||
|
||||
case let .updateDestination(destination):
|
||||
state.destination = destination
|
||||
|
@ -63,9 +62,6 @@ struct ProfileReducer: ReducerProtocol {
|
|||
|
||||
case .addressDetails:
|
||||
return .none
|
||||
|
||||
case .settings:
|
||||
return .none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,13 +76,6 @@ extension ProfileStore {
|
|||
action: ProfileReducer.Action.addressDetails
|
||||
)
|
||||
}
|
||||
|
||||
func settingsStore() -> SettingsStore {
|
||||
self.scope(
|
||||
state: \.settingsState,
|
||||
action: ProfileReducer.Action.settings
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - ViewStore
|
||||
|
@ -105,13 +94,6 @@ extension ProfileViewStore {
|
|||
embed: { $0 ? .addressDetails : nil }
|
||||
)
|
||||
}
|
||||
|
||||
var bindingForSettings: Binding<Bool> {
|
||||
self.destinationBinding.map(
|
||||
extract: { $0 == .settings },
|
||||
embed: { $0 ? .settings : nil }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Placeholders
|
||||
|
@ -120,8 +102,7 @@ extension ProfileReducer.State {
|
|||
static var placeholder: Self {
|
||||
.init(
|
||||
addressDetailsState: .placeholder,
|
||||
destination: nil,
|
||||
settingsState: .placeholder
|
||||
destination: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,62 +6,32 @@ struct ProfileView: View {
|
|||
|
||||
var body: some View {
|
||||
WithViewStore(store) { viewStore in
|
||||
ScrollView {
|
||||
VStack {
|
||||
qrCodeUA(viewStore.unifiedAddress)
|
||||
.padding(.top, 30)
|
||||
|
||||
Text("Your UA address \(viewStore.unifiedAddress)")
|
||||
.truncationMode(.middle)
|
||||
.multilineTextAlignment(.center)
|
||||
.lineLimit(2)
|
||||
.padding(30)
|
||||
.padding(.vertical, 50)
|
||||
|
||||
Button(
|
||||
action: { viewStore.send(.updateDestination(.addressDetails)) },
|
||||
label: { Text("See address details") }
|
||||
)
|
||||
.activeButtonStyle
|
||||
.frame(height: 50)
|
||||
.padding(EdgeInsets(top: 0, leading: 30, bottom: 50, trailing: 30))
|
||||
|
||||
Rectangle()
|
||||
.frame(height: 1.5)
|
||||
.padding(EdgeInsets(top: 0, leading: 100, bottom: 20, trailing: 100))
|
||||
.foregroundColor(Asset.Colors.TextField.Underline.purple.color)
|
||||
|
||||
Button(
|
||||
action: { viewStore.send(.updateDestination(.settings)) },
|
||||
label: { Text("Settings") }
|
||||
)
|
||||
.primaryButtonStyle
|
||||
.frame(height: 50)
|
||||
.padding(EdgeInsets(top: 30, leading: 30, bottom: 20, trailing: 30))
|
||||
|
||||
Spacer()
|
||||
|
||||
HStack {
|
||||
VStack {
|
||||
Text("secant v\(viewStore.appVersion)(\(viewStore.appBuild))")
|
||||
Text("sdk v\(viewStore.sdkVersion)")
|
||||
}
|
||||
Spacer()
|
||||
Button(
|
||||
action: { },
|
||||
label: {
|
||||
Text("More info")
|
||||
.foregroundColor(Asset.Colors.Text.moreInfoText.color)
|
||||
Text("Your UA")
|
||||
.fontWeight(.bold)
|
||||
.onTapGesture {
|
||||
viewStore.send(.copyUnifiedAddressToPastboard)
|
||||
}
|
||||
)
|
||||
|
||||
Button {
|
||||
viewStore.send(.updateDestination(.addressDetails))
|
||||
} label: {
|
||||
Image(systemName: "info.circle")
|
||||
.offset(x: -10, y: -10)
|
||||
.tint(.black)
|
||||
}
|
||||
}
|
||||
.padding(30)
|
||||
|
||||
Text("\(viewStore.unifiedAddress)")
|
||||
.padding(30)
|
||||
|
||||
Spacer()
|
||||
}
|
||||
.onAppear(perform: { viewStore.send(.onAppear) })
|
||||
.navigationLinkEmpty(
|
||||
isActive: viewStore.bindingForSettings,
|
||||
destination: {
|
||||
SettingsView(store: store.settingsStore())
|
||||
}
|
||||
)
|
||||
.navigationLinkEmpty(
|
||||
isActive: viewStore.bindingForAddressDetails,
|
||||
destination: {
|
||||
|
@ -104,14 +74,11 @@ struct ProfileView_Previews: PreviewProvider {
|
|||
NavigationView {
|
||||
ProfileView(
|
||||
store: .init(
|
||||
initialState: .init(
|
||||
addressDetailsState: .placeholder,
|
||||
settingsState: .placeholder
|
||||
),
|
||||
initialState: .init(addressDetailsState: .placeholder),
|
||||
reducer: ProfileReducer()
|
||||
)
|
||||
)
|
||||
}
|
||||
.preferredColorScheme(.dark)
|
||||
.preferredColorScheme(.light)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
//
|
||||
// RootDebug.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Lukáš Korba on 02.03.2023.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ComposableArchitecture
|
||||
import ZcashLightClientKit
|
||||
|
||||
/// In this file is a collection of helpers that control all state and action related operations
|
||||
/// for the `RootReducer` with a connection to the UI navigation.
|
||||
extension RootReducer {
|
||||
struct DebugState: Equatable {
|
||||
var rescanDialog: ConfirmationDialogState<RootReducer.Action>?
|
||||
}
|
||||
|
||||
indirect enum DebugAction: Equatable {
|
||||
case cancelRescan
|
||||
case flagUpdated
|
||||
case fullRescan
|
||||
case quickRescan
|
||||
case rescanBlockchain
|
||||
case rewindDone(String?, RootReducer.Action)
|
||||
case testCrashReporter // this will crash the app if live.
|
||||
case updateFlag(FeatureFlag, Bool)
|
||||
case walletConfigLoaded(WalletConfig)
|
||||
}
|
||||
|
||||
// swiftlint:disable:next cyclomatic_complexity
|
||||
func debugReduce() -> Reduce<RootReducer.State, RootReducer.Action> {
|
||||
Reduce { state, action in
|
||||
switch action {
|
||||
case .debug(.testCrashReporter):
|
||||
crashReporter.testCrash()
|
||||
return .none
|
||||
|
||||
case .debug(.rescanBlockchain):
|
||||
state.debugState.rescanDialog = .init(
|
||||
title: TextState("Rescan"),
|
||||
message: TextState("Select the rescan you want"),
|
||||
buttons: [
|
||||
.default(TextState("Quick rescan"), action: .send(.debug(.quickRescan))),
|
||||
.default(TextState("Full rescan"), action: .send(.debug(.fullRescan))),
|
||||
.cancel(TextState("Cancel"))
|
||||
]
|
||||
)
|
||||
return .none
|
||||
|
||||
case .debug(.cancelRescan):
|
||||
state.debugState.rescanDialog = nil
|
||||
return .none
|
||||
|
||||
case .debug(.quickRescan):
|
||||
state.destinationState.destination = .home
|
||||
return .run { send in
|
||||
do {
|
||||
try await sdkSynchronizer.rewind(.quick)
|
||||
await send(.debug(.rewindDone(nil, .debug(.quickRescan))))
|
||||
} catch {
|
||||
await send(.debug(.rewindDone(error.localizedDescription, .debug(.quickRescan))))
|
||||
}
|
||||
}
|
||||
|
||||
case .debug(.fullRescan):
|
||||
state.destinationState.destination = .home
|
||||
return .run { send in
|
||||
do {
|
||||
try await sdkSynchronizer.rewind(.birthday)
|
||||
await send(.debug(.rewindDone(nil, .debug(.fullRescan))))
|
||||
} catch {
|
||||
await send(.debug(.rewindDone(error.localizedDescription, .debug(.fullRescan))))
|
||||
}
|
||||
}
|
||||
|
||||
case let .debug(.rewindDone(errorDescription, _)):
|
||||
if let errorDescription {
|
||||
// TODO: [#221] Handle error more properly (https://github.com/zcash/secant-ios-wallet/issues/221)
|
||||
state.alert = AlertState(
|
||||
title: TextState("Rewind failed"),
|
||||
message: TextState("Error: \(errorDescription)"),
|
||||
dismissButton: .default(TextState("Ok"), action: .send(.dismissAlert))
|
||||
)
|
||||
}
|
||||
return .none
|
||||
|
||||
case let .debug(.updateFlag(flag, isEnabled)):
|
||||
return walletConfigProvider.update(flag, !isEnabled)
|
||||
.receive(on: mainQueue)
|
||||
.map { _ in return Action.debug(.flagUpdated) }
|
||||
.eraseToEffect()
|
||||
.cancellable(id: WalletConfigCancelId.self, cancelInFlight: true)
|
||||
|
||||
case .debug(.flagUpdated):
|
||||
return walletConfigProvider.load()
|
||||
.receive(on: mainQueue)
|
||||
.map { Action.debug(.walletConfigLoaded($0)) }
|
||||
.eraseToEffect()
|
||||
.cancellable(id: WalletConfigCancelId.self, cancelInFlight: true)
|
||||
|
||||
case let .debug(.walletConfigLoaded(walletConfig)):
|
||||
return EffectTask(value: .updateStateAfterConfigUpdate(walletConfig))
|
||||
|
||||
default: return .none
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Placeholders
|
||||
|
||||
extension RootReducer.DebugState {
|
||||
static var placeholder: Self {
|
||||
.init()
|
||||
}
|
||||
}
|
|
@ -134,8 +134,8 @@ extension RootReducer {
|
|||
state.destinationState.alert = nil
|
||||
return .none
|
||||
|
||||
case .home, .initialization, .onboarding, .phraseDisplay, .phraseValidation,
|
||||
.sandbox, .welcome, .binding, .nukeWalletFailed, .nukeWalletSucceeded, .debug, .walletConfigLoaded, .dismissAlert:
|
||||
case .home, .initialization, .onboarding, .phraseDisplay, .phraseValidation, .sandbox, .updateStateAfterConfigUpdate,
|
||||
.welcome, .binding, .nukeWalletFailed, .nukeWalletSucceeded, .debug, .walletConfigLoaded, .dismissAlert:
|
||||
return .none
|
||||
}
|
||||
|
||||
|
|
|
@ -26,12 +26,6 @@ extension RootReducer {
|
|||
case walletConfigChanged(WalletConfig)
|
||||
}
|
||||
|
||||
enum DebugAction: Equatable {
|
||||
case updateFlag(FeatureFlag, Bool)
|
||||
case flagUpdated
|
||||
case walletConfigLoaded(WalletConfig)
|
||||
}
|
||||
|
||||
// swiftlint:disable:next cyclomatic_complexity function_body_length
|
||||
func initializationReduce() -> Reduce<RootReducer.State, RootReducer.Action> {
|
||||
Reduce { state, action in
|
||||
|
@ -56,8 +50,10 @@ extension RootReducer {
|
|||
}
|
||||
|
||||
case .initialization(.walletConfigChanged(let walletConfig)):
|
||||
updateStateAfterConfigUpdate(state: &state, config: walletConfig)
|
||||
return EffectTask(value: .initialization(.initialSetups))
|
||||
return .concatenate(
|
||||
EffectTask(value: .updateStateAfterConfigUpdate(walletConfig)),
|
||||
EffectTask(value: .initialization(.initialSetups))
|
||||
)
|
||||
|
||||
case .initialization(.initialSetups):
|
||||
// TODO: [#524] finish all the wallet events according to definition, https://github.com/zcash/secant-ios-wallet/issues/524
|
||||
|
@ -307,43 +303,26 @@ extension RootReducer {
|
|||
|
||||
case .onboarding(.createNewWallet):
|
||||
return EffectTask(value: .initialization(.createNewWallet))
|
||||
|
||||
case .home, .destination, .onboarding, .phraseDisplay, .phraseValidation, .sandbox, .welcome, .binding:
|
||||
return .none
|
||||
|
||||
case .initialization(.configureCrashReporter):
|
||||
crashReporter.configure(
|
||||
!userStoredPreferences.isUserOptedOutOfCrashReporting()
|
||||
)
|
||||
return .none
|
||||
|
||||
case .updateStateAfterConfigUpdate(let walletConfig):
|
||||
state.walletConfig = walletConfig
|
||||
state.onboardingState.walletConfig = walletConfig
|
||||
return .none
|
||||
|
||||
case .dismissAlert:
|
||||
state.alert = nil
|
||||
return .none
|
||||
|
||||
case let .debug(.updateFlag(flag, isEnabled)):
|
||||
return walletConfigProvider.update(flag, !isEnabled)
|
||||
.receive(on: mainQueue)
|
||||
.map { _ in return Action.debug(.flagUpdated) }
|
||||
.eraseToEffect()
|
||||
.cancellable(id: WalletConfigCancelId.self, cancelInFlight: true)
|
||||
|
||||
case .debug(.flagUpdated):
|
||||
return walletConfigProvider.load()
|
||||
.receive(on: mainQueue)
|
||||
.map { Action.debug(.walletConfigLoaded($0)) }
|
||||
.eraseToEffect()
|
||||
.cancellable(id: WalletConfigCancelId.self, cancelInFlight: true)
|
||||
|
||||
case let .debug(.walletConfigLoaded(walletConfig)):
|
||||
updateStateAfterConfigUpdate(state: &state, config: walletConfig)
|
||||
case .home, .destination, .onboarding, .phraseDisplay, .phraseValidation, .sandbox,
|
||||
.welcome, .binding, .debug:
|
||||
return .none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func updateStateAfterConfigUpdate(state: inout RootReducer.State, config: WalletConfig) {
|
||||
state.walletConfig = config
|
||||
state.onboardingState.walletConfig = config
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@ struct RootReducer: ReducerProtocol {
|
|||
struct State: Equatable {
|
||||
@BindingState var alert: AlertState<RootReducer.Action>?
|
||||
var appInitializationState: InitializationState = .uninitialized
|
||||
var debugState: DebugState
|
||||
var destinationState: DestinationState
|
||||
var homeState: HomeReducer.State
|
||||
var onboardingState: OnboardingFlowReducer.State
|
||||
|
@ -36,6 +37,7 @@ struct RootReducer: ReducerProtocol {
|
|||
case phraseDisplay(RecoveryPhraseDisplayReducer.Action)
|
||||
case phraseValidation(RecoveryPhraseValidationFlowReducer.Action)
|
||||
case sandbox(SandboxReducer.Action)
|
||||
case updateStateAfterConfigUpdate(WalletConfig)
|
||||
case walletConfigLoaded(WalletConfig)
|
||||
case welcome(WelcomeReducer.Action)
|
||||
}
|
||||
|
@ -83,6 +85,8 @@ struct RootReducer: ReducerProtocol {
|
|||
initializationReduce()
|
||||
|
||||
destinationReduce()
|
||||
|
||||
debugReduce()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -172,6 +176,7 @@ extension RootReducer {
|
|||
extension RootReducer.State {
|
||||
static var placeholder: Self {
|
||||
.init(
|
||||
debugState: .placeholder,
|
||||
destinationState: .placeholder,
|
||||
homeState: .placeholder,
|
||||
onboardingState: .init(
|
||||
|
|
|
@ -126,6 +126,14 @@ private extension RootView {
|
|||
viewStore.goToDestination(.welcome)
|
||||
}
|
||||
|
||||
Button("Test Crash Reporter") {
|
||||
viewStore.send(.debug(.testCrashReporter))
|
||||
}
|
||||
|
||||
Button("Rescan Blockchain") {
|
||||
viewStore.send(.debug(.rescanBlockchain))
|
||||
}
|
||||
|
||||
Button("[Be careful] Nuke Wallet") {
|
||||
viewStore.send(.initialization(.nukeWalletRequest))
|
||||
}
|
||||
|
@ -156,6 +164,10 @@ private extension RootView {
|
|||
}
|
||||
}
|
||||
.alert(self.store.scope(state: \.destinationState.alert), dismiss: .destination(.dismissAlert))
|
||||
.confirmationDialog(
|
||||
store.scope(state: \.debugState.rescanDialog),
|
||||
dismiss: .debug(.cancelRescan)
|
||||
)
|
||||
}
|
||||
.navigationBarTitle("Startup")
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ struct SettingsReducer: ReducerProtocol {
|
|||
@BindingState var isCrashReportingOn: Bool
|
||||
var isSharingLogs = false
|
||||
var phraseDisplayState: RecoveryPhraseDisplayReducer.State
|
||||
var rescanDialog: ConfirmationDialogState<SettingsReducer.Action>?
|
||||
var supportData: SupportData?
|
||||
|
||||
var tempSDKDir: URL {
|
||||
|
@ -43,20 +42,15 @@ struct SettingsReducer: ReducerProtocol {
|
|||
case backupWallet
|
||||
case backupWalletAccessRequest
|
||||
case binding(BindingAction<SettingsReducer.State>)
|
||||
case cancelRescan
|
||||
case dismissAlert
|
||||
case exportLogs
|
||||
case fullRescan
|
||||
case logsExported
|
||||
case logsExportFailed(String)
|
||||
case logsShareFinished
|
||||
case onAppear
|
||||
case phraseDisplay(RecoveryPhraseDisplayReducer.Action)
|
||||
case quickRescan
|
||||
case rescanBlockchain
|
||||
case sendSupportMail
|
||||
case sendSupportMailFinished
|
||||
case testCrashReporter // this will crash the app if live.
|
||||
case updateDestination(SettingsReducer.State.Destination?)
|
||||
}
|
||||
|
||||
|
@ -108,10 +102,6 @@ struct SettingsReducer: ReducerProtocol {
|
|||
return .run { [state] _ in
|
||||
await userStoredPreferences.setIsUserOptedOutOfCrashReporting(state.isCrashReportingOn)
|
||||
}
|
||||
|
||||
case .cancelRescan, .quickRescan, .fullRescan:
|
||||
state.rescanDialog = nil
|
||||
return .none
|
||||
|
||||
case .dismissAlert:
|
||||
state.alert = nil
|
||||
|
@ -146,18 +136,6 @@ struct SettingsReducer: ReducerProtocol {
|
|||
state.isSharingLogs = false
|
||||
return .none
|
||||
|
||||
case .rescanBlockchain:
|
||||
state.rescanDialog = .init(
|
||||
title: TextState("Rescan"),
|
||||
message: TextState("Select the rescan you want"),
|
||||
buttons: [
|
||||
.default(TextState("Quick rescan"), action: .send(.quickRescan)),
|
||||
.default(TextState("Full rescan"), action: .send(.fullRescan)),
|
||||
.cancel(TextState("Cancel"))
|
||||
]
|
||||
)
|
||||
return .none
|
||||
|
||||
case .phraseDisplay:
|
||||
state.destination = nil
|
||||
return .none
|
||||
|
@ -166,10 +144,6 @@ struct SettingsReducer: ReducerProtocol {
|
|||
state.destination = destination
|
||||
return .none
|
||||
|
||||
case .testCrashReporter:
|
||||
crashReporter.testCrash()
|
||||
return .none
|
||||
|
||||
case .binding:
|
||||
return .none
|
||||
|
||||
|
|
|
@ -18,13 +18,6 @@ struct SettingsView: View {
|
|||
.activeButtonStyle
|
||||
.frame(height: 50)
|
||||
|
||||
Button(
|
||||
action: { viewStore.send(.rescanBlockchain) },
|
||||
label: { Text("Rescan Blockchain") }
|
||||
)
|
||||
.primaryButtonStyle
|
||||
.frame(height: 50)
|
||||
|
||||
Button(
|
||||
action: { viewStore.send(.exportLogs) },
|
||||
label: {
|
||||
|
@ -42,13 +35,6 @@ struct SettingsView: View {
|
|||
.frame(height: 50)
|
||||
.disabled(viewStore.exportLogsDisabled)
|
||||
|
||||
Button(
|
||||
action: { viewStore.send(.testCrashReporter) },
|
||||
label: { Text("Test Crash Reporter") }
|
||||
)
|
||||
.primaryButtonStyle
|
||||
.frame(height: 50)
|
||||
|
||||
Button(
|
||||
action: { viewStore.send(.sendSupportMail) },
|
||||
label: { Text("Send us feedback!") }
|
||||
|
@ -61,10 +47,6 @@ struct SettingsView: View {
|
|||
.padding(.horizontal, 30)
|
||||
.navigationTitle("Settings")
|
||||
.applyScreenBackground()
|
||||
.confirmationDialog(
|
||||
store.scope(state: \.rescanDialog),
|
||||
dismiss: .cancelRescan
|
||||
)
|
||||
.navigationLinkEmpty(
|
||||
isActive: viewStore.bindingForBackupPhrase,
|
||||
destination: {
|
||||
|
|
|
@ -87,54 +87,4 @@ class HomeTests: XCTestCase {
|
|||
// the .onDisappear action cancles the observer of the synchronizer status change.
|
||||
store.send(.onDisappear)
|
||||
}
|
||||
|
||||
@MainActor func testQuickRescan_ResetToHomeScreen() async throws {
|
||||
let homeState = HomeReducer.State(
|
||||
balanceBreakdownState: .placeholder,
|
||||
destination: .profile,
|
||||
profileState: .placeholder,
|
||||
requestState: .placeholder,
|
||||
scanState: .placeholder,
|
||||
sendState: .placeholder,
|
||||
shieldedBalance: Balance.zero,
|
||||
synchronizerStatusSnapshot: .default,
|
||||
walletEventsState: .emptyPlaceHolder
|
||||
)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: homeState,
|
||||
reducer: HomeReducer()
|
||||
)
|
||||
|
||||
await store.send(.profile(.settings(.quickRescan))) { state in
|
||||
state.destination = nil
|
||||
}
|
||||
|
||||
await store.receive(.rewindDone(nil, .quickRescan))
|
||||
}
|
||||
|
||||
@MainActor func testFullRescan_ResetToHomeScreen() async throws {
|
||||
let homeState = HomeReducer.State(
|
||||
balanceBreakdownState: .placeholder,
|
||||
destination: .profile,
|
||||
profileState: .placeholder,
|
||||
requestState: .placeholder,
|
||||
scanState: .placeholder,
|
||||
sendState: .placeholder,
|
||||
shieldedBalance: Balance.zero,
|
||||
synchronizerStatusSnapshot: .default,
|
||||
walletEventsState: .emptyPlaceHolder
|
||||
)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: homeState,
|
||||
reducer: HomeReducer()
|
||||
)
|
||||
|
||||
await store.send(.profile(.settings(.fullRescan))) { state in
|
||||
state.destination = nil
|
||||
}
|
||||
|
||||
await store.receive(.rewindDone(nil, .fullRescan))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,9 @@ import ComposableArchitecture
|
|||
import ZcashLightClientKit
|
||||
|
||||
class ProfileTests: XCTestCase {
|
||||
// swiftlint:disable line_length
|
||||
let uAddressEncoding = "utest1zkkkjfxkamagznjr6ayemffj2d2gacdwpzcyw669pvg06xevzqslpmm27zjsctlkstl2vsw62xrjktmzqcu4yu9zdhdxqz3kafa4j2q85y6mv74rzjcgjg8c0ytrg7dwyzwtgnuc76h"
|
||||
|
||||
@MainActor func testSynchronizerStateChanged_AnyButSynced() async throws {
|
||||
let store = TestStore(
|
||||
initialState: .placeholder,
|
||||
|
@ -20,9 +23,8 @@ class ProfileTests: XCTestCase {
|
|||
dependencies.sdkSynchronizer = SDKSynchronizerDependency.mock
|
||||
}
|
||||
|
||||
// swiftlint:disable line_length
|
||||
let uAddress = try UnifiedAddress(
|
||||
encoding: "utest1zkkkjfxkamagznjr6ayemffj2d2gacdwpzcyw669pvg06xevzqslpmm27zjsctlkstl2vsw62xrjktmzqcu4yu9zdhdxqz3kafa4j2q85y6mv74rzjcgjg8c0ytrg7dwyzwtgnuc76h",
|
||||
encoding: uAddressEncoding,
|
||||
network: .testnet
|
||||
)
|
||||
|
||||
|
@ -33,4 +35,26 @@ class ProfileTests: XCTestCase {
|
|||
state.sdkVersion = "0.18.1-beta"
|
||||
}
|
||||
}
|
||||
|
||||
func testCopyUnifiedAddressToPasteboard() throws {
|
||||
let testPasteboard = PasteboardClient.testPasteboard
|
||||
let uAddress = try UnifiedAddress(encoding: uAddressEncoding, network: .testnet)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: ProfileReducer.State(
|
||||
addressDetailsState: AddressDetailsReducer.State(uAddress: uAddress)
|
||||
),
|
||||
reducer: ProfileReducer()
|
||||
) {
|
||||
$0.pasteboard = testPasteboard
|
||||
}
|
||||
|
||||
store.send(.copyUnifiedAddressToPastboard)
|
||||
|
||||
XCTAssertEqual(
|
||||
testPasteboard.getString()?.data,
|
||||
uAddress.stringEncoded,
|
||||
"AddressDetails: `testCopyUnifiedAddressToPasteboard` is expected to match the input `\(uAddress.stringEncoded)`"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -161,6 +161,7 @@ class RecoveryPhraseValidationFlowFeatureFlagTests: XCTestCase {
|
|||
)
|
||||
|
||||
let appState = RootReducer.State(
|
||||
debugState: .placeholder,
|
||||
destinationState: .placeholder,
|
||||
homeState: .placeholder,
|
||||
onboardingState: .init(
|
||||
|
|
|
@ -68,6 +68,7 @@ class AppInitializationTests: XCTestCase {
|
|||
let walletConfig = WalletConfig(flags: defaultRawFlags)
|
||||
|
||||
let appState = RootReducer.State(
|
||||
debugState: .placeholder,
|
||||
destinationState: .placeholder,
|
||||
homeState: .placeholder,
|
||||
onboardingState: .init(
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
//
|
||||
// DebugTests.swift
|
||||
// secantTests
|
||||
//
|
||||
// Created by Lukáš Korba on 02.03.2023.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import secant_testnet
|
||||
import ComposableArchitecture
|
||||
|
||||
@MainActor
|
||||
class DebugTests: XCTestCase {
|
||||
func testRescanBlockchain() async throws {
|
||||
let store = TestStore(
|
||||
initialState: .placeholder,
|
||||
reducer: RootReducer()
|
||||
)
|
||||
|
||||
await store.send(.debug(.rescanBlockchain)) { state in
|
||||
state.debugState.rescanDialog = .init(
|
||||
title: TextState("Rescan"),
|
||||
message: TextState("Select the rescan you want"),
|
||||
buttons: [
|
||||
.default(TextState("Quick rescan"), action: .send(.debug(.quickRescan))),
|
||||
.default(TextState("Full rescan"), action: .send(.debug(.fullRescan))),
|
||||
.cancel(TextState("Cancel"))
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func testRescanBlockchain_Cancelling() async throws {
|
||||
var mockState = RootReducer.State.placeholder
|
||||
|
||||
mockState.debugState.rescanDialog = .init(
|
||||
title: TextState("Rescan"),
|
||||
message: TextState("Select the rescan you want"),
|
||||
buttons: [
|
||||
.default(TextState("Quick rescan"), action: .send(.debug(.quickRescan))),
|
||||
.default(TextState("Full rescan"), action: .send(.debug(.fullRescan))),
|
||||
.cancel(TextState("Cancel"))
|
||||
]
|
||||
)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: mockState,
|
||||
reducer: RootReducer()
|
||||
)
|
||||
|
||||
await store.send(.debug(.cancelRescan)) { state in
|
||||
state.debugState.rescanDialog = nil
|
||||
}
|
||||
}
|
||||
|
||||
func testRescanBlockchain_QuickRescanClearance() async throws {
|
||||
var mockState = RootReducer.State.placeholder
|
||||
|
||||
mockState.debugState.rescanDialog = .init(
|
||||
title: TextState("Rescan"),
|
||||
message: TextState("Select the rescan you want"),
|
||||
buttons: [
|
||||
.default(TextState("Quick rescan"), action: .send(.debug(.quickRescan))),
|
||||
.default(TextState("Full rescan"), action: .send(.debug(.fullRescan))),
|
||||
.cancel(TextState("Cancel"))
|
||||
]
|
||||
)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: mockState,
|
||||
reducer: RootReducer()
|
||||
)
|
||||
|
||||
await store.send(.debug(.quickRescan)) { state in
|
||||
state.destinationState.internalDestination = .home
|
||||
state.destinationState.previousDestination = .welcome
|
||||
}
|
||||
|
||||
await store.receive(.debug(.rewindDone(nil, .debug(.quickRescan))))
|
||||
}
|
||||
|
||||
func testRescanBlockchain_FullRescanClearance() async throws {
|
||||
var mockState = RootReducer.State.placeholder
|
||||
|
||||
mockState.debugState.rescanDialog = .init(
|
||||
title: TextState("Rescan"),
|
||||
message: TextState("Select the rescan you want"),
|
||||
buttons: [
|
||||
.default(TextState("Quick rescan"), action: .send(.debug(.quickRescan))),
|
||||
.default(TextState("Full rescan"), action: .send(.debug(.fullRescan))),
|
||||
.cancel(TextState("Cancel"))
|
||||
]
|
||||
)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: mockState,
|
||||
reducer: RootReducer()
|
||||
)
|
||||
|
||||
await store.send(.debug(.fullRescan)) { state in
|
||||
state.destinationState.internalDestination = .home
|
||||
state.destinationState.previousDestination = .welcome
|
||||
}
|
||||
|
||||
await store.receive(.debug(.rewindDone(nil, .debug(.fullRescan))))
|
||||
}
|
||||
}
|
|
@ -80,112 +80,12 @@ class SettingsTests: XCTestCase {
|
|||
await store.finish()
|
||||
}
|
||||
|
||||
func testRescanBlockchain() async throws {
|
||||
let store = TestStore(
|
||||
initialState: .placeholder,
|
||||
reducer: SettingsReducer()
|
||||
)
|
||||
|
||||
await store.send(.rescanBlockchain) { state in
|
||||
state.rescanDialog = .init(
|
||||
title: TextState("Rescan"),
|
||||
message: TextState("Select the rescan you want"),
|
||||
buttons: [
|
||||
.default(TextState("Quick rescan"), action: .send(.quickRescan)),
|
||||
.default(TextState("Full rescan"), action: .send(.fullRescan)),
|
||||
.cancel(TextState("Cancel"))
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func testRescanBlockchain_Cancelling() async throws {
|
||||
let store = TestStore(
|
||||
initialState: SettingsReducer.State(
|
||||
destination: nil,
|
||||
isCrashReportingOn: false,
|
||||
phraseDisplayState: .init(),
|
||||
rescanDialog: .init(
|
||||
title: TextState("Rescan"),
|
||||
message: TextState("Select the rescan you want"),
|
||||
buttons: [
|
||||
.default(TextState("Quick rescan"), action: .send(.quickRescan)),
|
||||
.default(TextState("Full rescan"), action: .send(.fullRescan)),
|
||||
.cancel(TextState("Cancel"))
|
||||
]
|
||||
)
|
||||
),
|
||||
reducer: SettingsReducer()
|
||||
)
|
||||
|
||||
await store.send(.cancelRescan) { state in
|
||||
state.rescanDialog = nil
|
||||
}
|
||||
}
|
||||
|
||||
func testRescanBlockchain_QuickRescanClearance() async throws {
|
||||
let store = TestStore(
|
||||
initialState: SettingsReducer.State(
|
||||
destination: nil,
|
||||
isCrashReportingOn: false,
|
||||
phraseDisplayState: .init(),
|
||||
rescanDialog: .init(
|
||||
title: TextState("Rescan"),
|
||||
message: TextState("Select the rescan you want"),
|
||||
buttons: [
|
||||
.default(TextState("Quick rescan"), action: .send(.quickRescan)),
|
||||
.default(TextState("Full rescan"), action: .send(.fullRescan)),
|
||||
.cancel(TextState("Cancel"))
|
||||
]
|
||||
)
|
||||
),
|
||||
reducer: SettingsReducer()
|
||||
)
|
||||
|
||||
await store.send(.quickRescan) { state in
|
||||
state.rescanDialog = nil
|
||||
}
|
||||
}
|
||||
|
||||
func testRescanBlockchain_FullRescanClearance() async throws {
|
||||
let store = TestStore(
|
||||
initialState: SettingsReducer.State(
|
||||
destination: nil,
|
||||
isCrashReportingOn: false,
|
||||
phraseDisplayState: .init(),
|
||||
rescanDialog: .init(
|
||||
title: TextState("Rescan"),
|
||||
message: TextState("Select the rescan you want"),
|
||||
buttons: [
|
||||
.default(TextState("Quick rescan"), action: .send(.quickRescan)),
|
||||
.default(TextState("Full rescan"), action: .send(.fullRescan)),
|
||||
.cancel(TextState("Cancel"))
|
||||
]
|
||||
)
|
||||
),
|
||||
reducer: SettingsReducer()
|
||||
)
|
||||
|
||||
await store.send(.fullRescan) { state in
|
||||
state.rescanDialog = nil
|
||||
}
|
||||
}
|
||||
|
||||
func testExportLogs_ButtonDisableShareEnable() async throws {
|
||||
let store = TestStore(
|
||||
initialState: SettingsReducer.State(
|
||||
destination: nil,
|
||||
isCrashReportingOn: false,
|
||||
phraseDisplayState: .init(),
|
||||
rescanDialog: .init(
|
||||
title: TextState("Rescan"),
|
||||
message: TextState("Select the rescan you want"),
|
||||
buttons: [
|
||||
.default(TextState("Quick rescan"), action: .send(.quickRescan)),
|
||||
.default(TextState("Full rescan"), action: .send(.fullRescan)),
|
||||
.cancel(TextState("Cancel"))
|
||||
]
|
||||
)
|
||||
phraseDisplayState: .init()
|
||||
),
|
||||
reducer: SettingsReducer()
|
||||
)
|
||||
|
@ -208,16 +108,7 @@ class SettingsTests: XCTestCase {
|
|||
destination: nil,
|
||||
isCrashReportingOn: false,
|
||||
isSharingLogs: true,
|
||||
phraseDisplayState: .init(),
|
||||
rescanDialog: .init(
|
||||
title: TextState("Rescan"),
|
||||
message: TextState("Select the rescan you want"),
|
||||
buttons: [
|
||||
.default(TextState("Quick rescan"), action: .send(.quickRescan)),
|
||||
.default(TextState("Full rescan"), action: .send(.fullRescan)),
|
||||
.cancel(TextState("Cancel"))
|
||||
]
|
||||
)
|
||||
phraseDisplayState: .init()
|
||||
),
|
||||
reducer: SettingsReducer()
|
||||
)
|
||||
|
|
|
@ -39,9 +39,9 @@ class HomeSnapshotTests: XCTestCase {
|
|||
initialState: .init(
|
||||
balanceBreakdownState: .placeholder,
|
||||
profileState: .placeholder,
|
||||
requestState: .placeholder,
|
||||
scanState: .placeholder,
|
||||
sendState: .placeholder,
|
||||
settingsState: .placeholder,
|
||||
shieldedBalance: balance.redacted,
|
||||
synchronizerStatusSnapshot: .default,
|
||||
walletEventsState: .init(walletEvents: IdentifiedArrayOf(uniqueElements: walletEvents))
|
||||
|
|
|
@ -54,9 +54,9 @@ class WalletEventsSnapshotTests: XCTestCase {
|
|||
initialState: .init(
|
||||
balanceBreakdownState: .placeholder,
|
||||
profileState: .placeholder,
|
||||
requestState: .placeholder,
|
||||
scanState: .placeholder,
|
||||
sendState: .placeholder,
|
||||
settingsState: .placeholder,
|
||||
shieldedBalance: balance.redacted,
|
||||
synchronizerStatusSnapshot: .default,
|
||||
walletEventsState: .init(walletEvents: IdentifiedArrayOf(uniqueElements: [walletEvent]))
|
||||
|
@ -106,9 +106,9 @@ class WalletEventsSnapshotTests: XCTestCase {
|
|||
initialState: .init(
|
||||
balanceBreakdownState: .placeholder,
|
||||
profileState: .placeholder,
|
||||
requestState: .placeholder,
|
||||
scanState: .placeholder,
|
||||
sendState: .placeholder,
|
||||
settingsState: .placeholder,
|
||||
shieldedBalance: balance.redacted,
|
||||
synchronizerStatusSnapshot: .default,
|
||||
walletEventsState: .init(walletEvents: IdentifiedArrayOf(uniqueElements: [walletEvent]))
|
||||
|
@ -158,9 +158,9 @@ class WalletEventsSnapshotTests: XCTestCase {
|
|||
initialState: .init(
|
||||
balanceBreakdownState: .placeholder,
|
||||
profileState: .placeholder,
|
||||
requestState: .placeholder,
|
||||
scanState: .placeholder,
|
||||
sendState: .placeholder,
|
||||
settingsState: .placeholder,
|
||||
shieldedBalance: balance.redacted,
|
||||
synchronizerStatusSnapshot: .default,
|
||||
walletEventsState: .init(walletEvents: IdentifiedArrayOf(uniqueElements: [walletEvent]))
|
||||
|
@ -216,9 +216,9 @@ class WalletEventsSnapshotTests: XCTestCase {
|
|||
initialState: .init(
|
||||
balanceBreakdownState: .placeholder,
|
||||
profileState: .placeholder,
|
||||
requestState: .placeholder,
|
||||
scanState: .placeholder,
|
||||
sendState: .placeholder,
|
||||
settingsState: .placeholder,
|
||||
shieldedBalance: balance.redacted,
|
||||
synchronizerStatusSnapshot: .default,
|
||||
walletEventsState: .init(walletEvents: IdentifiedArrayOf(uniqueElements: [walletEvent]))
|
||||
|
|
|
@ -127,8 +127,10 @@ class WalletConfigProviderTests: XCTestCase {
|
|||
defaultRawFlags[.onboardingFlow] = true
|
||||
let flags = WalletConfig(flags: defaultRawFlags)
|
||||
|
||||
store.send(.debug(.walletConfigLoaded(flags)))
|
||||
|
||||
// The new flag's value has to be propagated to all `walletConfig` instances
|
||||
store.send(.debug(.walletConfigLoaded(flags))) { state in
|
||||
store.receive(.updateStateAfterConfigUpdate(flags)) { state in
|
||||
state.walletConfig = flags
|
||||
state.onboardingState.walletConfig = flags
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue