- AlertRequest + Reducer + States removed - Alerts decoupled and living within the features - tests fixed
This commit is contained in:
parent
9b77a57a5f
commit
e3a312ee1a
|
@ -192,12 +192,6 @@
|
|||
9E2DF99E27CF704D00649636 /* ImportWalletView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DF99B27CF704D00649636 /* ImportWalletView.swift */; };
|
||||
9E2F1C8C280ED6A7004E65FE /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9E2F1C8B280ED6A7004E65FE /* LaunchScreen.storyboard */; };
|
||||
9E2F1C8F280EDE09004E65FE /* Drawer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2F1C8E280EDE09004E65FE /* Drawer.swift */; };
|
||||
9E33ECD429D5D99000708DE4 /* AlertRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E33ECD029D4CCB600708DE4 /* AlertRequest.swift */; };
|
||||
9E33ECD529D5D99700708DE4 /* AlertReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9CEA3229D32D5100599DF5 /* AlertReducer.swift */; };
|
||||
9E33ECD629D5D99A00708DE4 /* AlertStates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E33ECD229D4D1FB00708DE4 /* AlertStates.swift */; };
|
||||
9E33ECD729D5E30200708DE4 /* AlertReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9CEA3229D32D5100599DF5 /* AlertReducer.swift */; };
|
||||
9E33ECD829D5E30200708DE4 /* AlertStates.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E33ECD229D4D1FB00708DE4 /* AlertStates.swift */; };
|
||||
9E33ECD929D5E30200708DE4 /* AlertRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E33ECD029D4CCB600708DE4 /* AlertRequest.swift */; };
|
||||
9E33ECDA29D5E30700708DE4 /* OnChangeReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9CEA3D29D47BE000599DF5 /* OnChangeReducer.swift */; };
|
||||
9E34519529C4A4BF00177D16 /* AddressDetailsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E207C382966EF87003E2C9B /* AddressDetailsTests.swift */; };
|
||||
9E34519629C4A4D800177D16 /* secantUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D4E7A2526B364180058B01E /* secantUITests.swift */; };
|
||||
|
@ -406,8 +400,6 @@
|
|||
9E2DF99B27CF704D00649636 /* ImportWalletView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportWalletView.swift; sourceTree = "<group>"; };
|
||||
9E2F1C8B280ED6A7004E65FE /* LaunchScreen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
9E2F1C8E280EDE09004E65FE /* Drawer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Drawer.swift; sourceTree = "<group>"; };
|
||||
9E33ECD029D4CCB600708DE4 /* AlertRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertRequest.swift; sourceTree = "<group>"; };
|
||||
9E33ECD229D4D1FB00708DE4 /* AlertStates.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertStates.swift; sourceTree = "<group>"; };
|
||||
9E391123283E4CAC0073DD9A /* ImportWalletTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImportWalletTests.swift; sourceTree = "<group>"; };
|
||||
9E39112D283F91600073DD9A /* ZatoshiTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZatoshiTests.swift; sourceTree = "<group>"; };
|
||||
9E391131284644580073DD9A /* AppInitializationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppInitializationTests.swift; sourceTree = "<group>"; };
|
||||
|
@ -449,7 +441,6 @@
|
|||
9E94C62228AA7EE0008256E9 /* BalanceBreakdownSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BalanceBreakdownSnapshotTests.swift; sourceTree = "<group>"; };
|
||||
9E9ADA7C2938F4C00071767B /* RootInitialization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootInitialization.swift; sourceTree = "<group>"; };
|
||||
9E9ADA7E2938F5EC0071767B /* RootDestination.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootDestination.swift; sourceTree = "<group>"; };
|
||||
9E9CEA3229D32D5100599DF5 /* AlertReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertReducer.swift; sourceTree = "<group>"; };
|
||||
9E9CEA3D29D47BE000599DF5 /* OnChangeReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OnChangeReducer.swift; sourceTree = "<group>"; };
|
||||
9E9ECC8C28589E150099D5A2 /* HomeSnapshotTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeSnapshotTests.swift; sourceTree = "<group>"; };
|
||||
9E9ECC8E28589E150099D5A2 /* WelcomeSnapshotTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WelcomeSnapshotTests.swift; sourceTree = "<group>"; };
|
||||
|
@ -598,13 +589,6 @@
|
|||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
0D0781C2278750C00083ACD7 /* Welcome */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
path = Welcome;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0D4E79FC26B364170058B01E = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -822,20 +806,17 @@
|
|||
isa = PBXGroup;
|
||||
children = (
|
||||
9E0031BD2A28B21A003DFCEB /* BalanceBreakdown */,
|
||||
9E33ECCF29D4CC9900708DE4 /* Alerts */,
|
||||
34C5657F29B60BDF002F3A7C /* ExportLogs */,
|
||||
F93874EC273C4DE200F0E875 /* Home */,
|
||||
9E2DF99727CF704D00649636 /* ImportWallet */,
|
||||
3448CB3028E4764E006ADEDB /* NotEnoughFreeSpace */,
|
||||
6654C73C2715A3FA00901167 /* OnboardingFlow */,
|
||||
9E7FE0E4282E753700C374E8 /* RecoveryPhraseDisplay */,
|
||||
F9971A4927680DC400A2DB75 /* Root */,
|
||||
9EAFEB8B2808174900199FC9 /* Sandbox */,
|
||||
F9971A5B27680DF600A2DB75 /* Scan */,
|
||||
F9C165B62740403600592F76 /* SendFlow */,
|
||||
F9971A6127680DFE00A2DB75 /* Settings */,
|
||||
F96B41E2273B501F0021B49A /* WalletEventsFlow */,
|
||||
0D0781C2278750C00083ACD7 /* Welcome */,
|
||||
);
|
||||
path = Features;
|
||||
sourceTree = "<group>";
|
||||
|
@ -938,16 +919,6 @@
|
|||
path = Drawer;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9E33ECCF29D4CC9900708DE4 /* Alerts */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9E33ECD029D4CCB600708DE4 /* AlertRequest.swift */,
|
||||
9E9CEA3229D32D5100599DF5 /* AlertReducer.swift */,
|
||||
9E33ECD229D4D1FB00708DE4 /* AlertStates.swift */,
|
||||
);
|
||||
path = Alerts;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9E391122283E4C970073DD9A /* ImportWalletTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1180,13 +1151,6 @@
|
|||
path = "UI Components";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9E7FE0E4282E753700C374E8 /* RecoveryPhraseDisplay */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
path = RecoveryPhraseDisplay;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9E7FE0E9282E7CF800C374E8 /* ImportSeedEditor */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1878,7 +1842,6 @@
|
|||
0D26AEDA299E8196005260EE /* ImportSeedEditor.swift in Sources */,
|
||||
9E486DE629B637AF003E6945 /* ImportBirthdayView.swift in Sources */,
|
||||
0D26AEDC299E8196005260EE /* CheckCircle.swift in Sources */,
|
||||
9E33ECD929D5E30200708DE4 /* AlertRequest.swift in Sources */,
|
||||
0D26AEE4299E8196005260EE /* CurrencySelectionView.swift in Sources */,
|
||||
0D26AEE7299E8196005260EE /* TransactionAddressTextFieldStore.swift in Sources */,
|
||||
34C5658629B60C8B002F3A7C /* ExportLogsStore.swift in Sources */,
|
||||
|
@ -1919,7 +1882,6 @@
|
|||
0D26AF3E299E8196005260EE /* SingleLineTextField.swift in Sources */,
|
||||
0D26AF40299E8196005260EE /* RootDestination.swift in Sources */,
|
||||
0D26AF41299E8196005260EE /* OnboardingProgressIndicator.swift in Sources */,
|
||||
9E33ECD829D5E30200708DE4 /* AlertStates.swift in Sources */,
|
||||
0D26AF44299E8196005260EE /* Memo+toString.swift in Sources */,
|
||||
0D26AF46299E8196005260EE /* CheckCircleStore.swift in Sources */,
|
||||
0D26AF47299E8196005260EE /* CreateTransactionView.swift in Sources */,
|
||||
|
@ -1932,7 +1894,6 @@
|
|||
9E0031C72A28BD54003DFCEB /* BalanceBreakdownView.swift in Sources */,
|
||||
0D26AF56299E8196005260EE /* ScanStore.swift in Sources */,
|
||||
0D26AF5F299E8196005260EE /* SendFlowView.swift in Sources */,
|
||||
9E33ECD729D5E30200708DE4 /* AlertReducer.swift in Sources */,
|
||||
0D26AF64299E8196005260EE /* SettingsStore.swift in Sources */,
|
||||
0D26AF65299E8196005260EE /* InitializationState.swift in Sources */,
|
||||
9E486DF429B9EEC4003E6945 /* UIResponder+Current.swift in Sources */,
|
||||
|
@ -1946,14 +1907,12 @@
|
|||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9E33ECD629D5D99A00708DE4 /* AlertStates.swift in Sources */,
|
||||
2EB660E02747EAB900A06A07 /* OnboardingFlowView.swift in Sources */,
|
||||
0D261040298C406F00CC9DE9 /* CrashReporterTestKey.swift in Sources */,
|
||||
9EAFEB902808183D00199FC9 /* SandboxStore.swift in Sources */,
|
||||
34DA414728E4385800F8CC61 /* TransactionSendingView.swift in Sources */,
|
||||
F96B41E9273B501F0021B49A /* WalletEventsFlowView.swift in Sources */,
|
||||
9E4AA4F829BF76BB00752BB3 /* About.swift in Sources */,
|
||||
9E33ECD429D5D99000708DE4 /* AlertRequest.swift in Sources */,
|
||||
0D26103E298C3FA600CC9DE9 /* CrashReporterLiveKey.swift in Sources */,
|
||||
2EDA07A027EDE18C00D6F09B /* TCATextField.swift in Sources */,
|
||||
2EB7758727FC67FD00269373 /* TransactionAmountTextFieldStore.swift in Sources */,
|
||||
|
@ -1970,7 +1929,6 @@
|
|||
9E9ADA7D2938F4C00071767B /* RootInitialization.swift in Sources */,
|
||||
6654C73E2715A41300901167 /* OnboardingFlowStore.swift in Sources */,
|
||||
2E6CF8DD27D78319004DCD7A /* CurrencySelectionStore.swift in Sources */,
|
||||
9E33ECD529D5D99700708DE4 /* AlertReducer.swift in Sources */,
|
||||
9E66122C2877188700C75B70 /* SyncStatusSnapshot.swift in Sources */,
|
||||
9E2DF99D27CF704D00649636 /* ImportSeedEditor.swift in Sources */,
|
||||
9E486DE529B637AF003E6945 /* ImportBirthdayView.swift in Sources */,
|
||||
|
@ -2576,7 +2534,7 @@
|
|||
repositoryURL = "https://github.com/pointfreeco/swift-composable-architecture";
|
||||
requirement = {
|
||||
kind = exactVersion;
|
||||
version = 0.53.2;
|
||||
version = 0.54.0;
|
||||
};
|
||||
};
|
||||
9E2AC0FD27D8EC120042AA47 /* XCRemoteSwiftPackageReference "MnemonicSwift" */ = {
|
||||
|
|
|
@ -185,8 +185,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/swift-composable-architecture",
|
||||
"state" : {
|
||||
"revision" : "b6559103c7867821b3848afe29afc1a386ae83f1",
|
||||
"version" : "0.53.2"
|
||||
"revision" : "8b98ba40a2bc8e70579397f906ceb325e7c04b2f",
|
||||
"version" : "0.54.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -212,8 +212,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/swift-dependencies",
|
||||
"state" : {
|
||||
"revision" : "25c9b6789b4b7ada649a3808e6d8de1489082a33",
|
||||
"version" : "0.5.0"
|
||||
"revision" : "de1a984a71e51f6e488e98ce3652035563eb8acb",
|
||||
"version" : "0.5.1"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -311,8 +311,8 @@
|
|||
"kind" : "remoteSourceControl",
|
||||
"location" : "https://github.com/pointfreeco/swiftui-navigation",
|
||||
"state" : {
|
||||
"revision" : "47dd574b900ba5ba679f56ea00d4d282fc7305a6",
|
||||
"version" : "0.7.1"
|
||||
"revision" : "db81007362f998654239021ca9308a264e59d3e2",
|
||||
"version" : "0.7.2"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
@ -1,118 +0,0 @@
|
|||
//
|
||||
// AlertReducer.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Lukáš Korba on 28.03.2023.
|
||||
//
|
||||
|
||||
import ComposableArchitecture
|
||||
|
||||
extension ReducerProtocol<RootReducer.State, RootReducer.Action> {
|
||||
func alerts() -> some ReducerProtocol<RootReducer.State, RootReducer.Action> {
|
||||
UniAlert(base: self)
|
||||
}
|
||||
}
|
||||
|
||||
private struct UniAlert<Base: ReducerProtocol<RootReducer.State, RootReducer.Action>>: ReducerProtocol {
|
||||
let base: Base
|
||||
|
||||
var body: some ReducerProtocol<RootReducer.State, RootReducer.Action> {
|
||||
base
|
||||
catchAlertRequests
|
||||
passAlertActions
|
||||
}
|
||||
|
||||
// Catching the alert side effects
|
||||
@ReducerBuilder<State, Action>
|
||||
var catchAlertRequests: some ReducerProtocol<State, Action> {
|
||||
Reduce { state, action in
|
||||
switch action {
|
||||
case .alert(let alert):
|
||||
state.uniAlert = alert.alertState()
|
||||
return .none
|
||||
|
||||
case .exportLogs(.alert(let alert)):
|
||||
state.uniAlert = alert.alertState()
|
||||
return .none
|
||||
case .home(.settings(.exportLogs(.alert(let alert)))):
|
||||
state.uniAlert = alert.alertState()
|
||||
return .none
|
||||
|
||||
case .home(.alert(let alert)):
|
||||
state.uniAlert = alert.alertState()
|
||||
return .none
|
||||
|
||||
case .home(.balanceBreakdown(.alert(let alert))):
|
||||
state.uniAlert = alert.alertState()
|
||||
return .none
|
||||
|
||||
case .home(.send(.scan(.alert(let alert)))):
|
||||
state.uniAlert = alert.alertState()
|
||||
return .none
|
||||
|
||||
case .onboarding(.importWallet(.alert(let alert))):
|
||||
state.uniAlert = alert.alertState()
|
||||
return .none
|
||||
|
||||
case .home(.settings(.alert(let alert))):
|
||||
state.uniAlert = alert.alertState()
|
||||
return .none
|
||||
|
||||
case .home(.walletEvents(.alert(let alert))):
|
||||
state.uniAlert = alert.alertState()
|
||||
return .none
|
||||
|
||||
default: return .none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Passing alert actions back to the children
|
||||
@ReducerBuilder<State, Action>
|
||||
var passAlertActions: some ReducerProtocol<State, Action> {
|
||||
Reduce { state, action in
|
||||
switch action {
|
||||
case .dismissAlert:
|
||||
state.uniAlert = nil
|
||||
return .none
|
||||
|
||||
case .uniAlert(.balanceBreakdown(let action)):
|
||||
guard let action else { return .none }
|
||||
return EffectTask(value: .home(.balanceBreakdown(action)))
|
||||
|
||||
case .uniAlert(.exportLogs(let action)):
|
||||
guard let action else { return .none }
|
||||
return .concatenate(
|
||||
EffectTask(value: .exportLogs(action)),
|
||||
EffectTask(value: .home(.settings(.exportLogs(action))))
|
||||
)
|
||||
|
||||
case .uniAlert(.home(let action)):
|
||||
guard let action else { return .none }
|
||||
return EffectTask(value: .home(action))
|
||||
|
||||
case .uniAlert(.importWallet(let action)):
|
||||
guard let action else { return .none }
|
||||
return EffectTask(value: .onboarding(.importWallet(action)))
|
||||
|
||||
case .uniAlert(.root(let action)):
|
||||
guard let action else { return .none }
|
||||
return EffectTask(value: action)
|
||||
|
||||
case .uniAlert(.scan(let action)):
|
||||
guard let action else { return .none }
|
||||
return EffectTask(value: .home(.send(.scan(action))))
|
||||
|
||||
case .uniAlert(.settings(let action)):
|
||||
guard let action else { return .none }
|
||||
return EffectTask(value: .home(.settings(action)))
|
||||
|
||||
case .uniAlert(.walletEvents(let action)):
|
||||
guard let action else { return .none }
|
||||
return EffectTask(value: .home(.walletEvents(action)))
|
||||
|
||||
default: return .none
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,99 +0,0 @@
|
|||
//
|
||||
// AlertRequest.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 29.03.2023.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ComposableArchitecture
|
||||
import ZcashLightClientKit
|
||||
|
||||
extension RootReducer {
|
||||
indirect enum AlertAction: Equatable {
|
||||
case balanceBreakdown(BalanceBreakdownReducer.Action?)
|
||||
case exportLogs(ExportLogsReducer.Action?)
|
||||
case home(HomeReducer.Action?)
|
||||
case importWallet(ImportWalletReducer.Action?)
|
||||
case root(RootReducer.Action?)
|
||||
case scan(ScanReducer.Action?)
|
||||
case settings(SettingsReducer.Action?)
|
||||
case walletEvents(WalletEventsFlowReducer.Action?)
|
||||
}
|
||||
}
|
||||
|
||||
enum AlertRequest: Equatable {
|
||||
enum BalanceBreakdown: Equatable {
|
||||
case shieldFundsSuccess
|
||||
case shieldFundsFailure(ZcashError)
|
||||
}
|
||||
|
||||
enum ExportLogs: Equatable {
|
||||
case failed(ZcashError)
|
||||
}
|
||||
|
||||
enum Home: Equatable {
|
||||
case syncFailed(ZcashError, String)
|
||||
}
|
||||
|
||||
enum ImportWallet: Equatable {
|
||||
case succeed
|
||||
case failed(ZcashError)
|
||||
}
|
||||
|
||||
enum Root: Equatable {
|
||||
case cantCreateNewWallet(ZcashError)
|
||||
case cantLoadSeedPhrase
|
||||
case cantStartSync(ZcashError)
|
||||
case cantStoreThatUserPassedPhraseBackupTest(ZcashError)
|
||||
case failedToProcessDeeplink(URL, ZcashError)
|
||||
case initializationFailed(ZcashError)
|
||||
case rewindFailed(ZcashError)
|
||||
case walletStateFailed(InitializationState)
|
||||
case wipeFailed
|
||||
case wipeRequest
|
||||
}
|
||||
|
||||
enum Scan: Equatable {
|
||||
case cantInitializeCamera(ZcashError)
|
||||
}
|
||||
|
||||
enum Settings: Equatable {
|
||||
case cantBackupWallet(ZcashError)
|
||||
case sendSupportMail
|
||||
}
|
||||
|
||||
enum WalletEvents: Equatable {
|
||||
case warnBeforeLeavingApp(URL?)
|
||||
}
|
||||
|
||||
case balanceBreakdown(BalanceBreakdown)
|
||||
case exportLogs(ExportLogs)
|
||||
case home(Home)
|
||||
case importWallet(ImportWallet)
|
||||
case root(Root)
|
||||
case scan(Scan)
|
||||
case settings(Settings)
|
||||
case walletEvents(WalletEvents)
|
||||
|
||||
func alertState() -> AlertState<RootReducer.Action> {
|
||||
switch self {
|
||||
case .balanceBreakdown(let balanceBreakdown):
|
||||
return balanceBreakdownAlertState(balanceBreakdown)
|
||||
case .exportLogs(let exportLogs):
|
||||
return exportLogsAlertState(exportLogs)
|
||||
case .home(let home):
|
||||
return homeAlertState(home)
|
||||
case .importWallet(let importWallet):
|
||||
return importWalletAlertState(importWallet)
|
||||
case .root(let root):
|
||||
return rootAlertState(root)
|
||||
case .scan(let scan):
|
||||
return scanAlertState(scan)
|
||||
case .settings(let settings):
|
||||
return settingsAlertState(settings)
|
||||
case .walletEvents(let walletEvents):
|
||||
return walletEventsAlertState(walletEvents)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,214 +0,0 @@
|
|||
//
|
||||
// AlertStates.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 29.03.2023.
|
||||
//
|
||||
|
||||
import ComposableArchitecture
|
||||
import Generated
|
||||
|
||||
// MARK: - Balance Breakdown
|
||||
|
||||
extension AlertRequest {
|
||||
func balanceBreakdownAlertState(_ balanceBreakdown: BalanceBreakdown) -> AlertState<RootReducer.Action> {
|
||||
switch balanceBreakdown {
|
||||
case .shieldFundsFailure(let error):
|
||||
return AlertState(
|
||||
title: TextState(L10n.BalanceBreakdown.Alert.ShieldFunds.Failure.title),
|
||||
message: TextState(L10n.BalanceBreakdown.Alert.ShieldFunds.Failure.message(error.message, error.code.rawValue)),
|
||||
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
|
||||
)
|
||||
case .shieldFundsSuccess:
|
||||
return AlertState(
|
||||
title: TextState(L10n.BalanceBreakdown.Alert.ShieldFunds.Success.title),
|
||||
message: TextState(L10n.BalanceBreakdown.Alert.ShieldFunds.Success.message),
|
||||
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Export Logs
|
||||
|
||||
extension AlertRequest {
|
||||
func exportLogsAlertState(_ exportLogs: ExportLogs) -> AlertState<RootReducer.Action> {
|
||||
switch exportLogs {
|
||||
case .failed(let error):
|
||||
return AlertState(
|
||||
title: TextState(L10n.ExportLogs.Alert.Failed.title),
|
||||
message: TextState(L10n.ExportLogs.Alert.Failed.message(error.message, error.code.rawValue)),
|
||||
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Home
|
||||
|
||||
extension AlertRequest {
|
||||
func homeAlertState(_ home: Home) -> AlertState<RootReducer.Action> {
|
||||
switch home {
|
||||
case let .syncFailed(error, secondaryButtonTitle):
|
||||
return AlertState(
|
||||
title: TextState(L10n.Home.SyncFailed.title),
|
||||
message: TextState("\(error.message) (code: \(error.code.rawValue))"),
|
||||
primaryButton: .default(TextState(L10n.Home.SyncFailed.retry), action: .send(.uniAlert(.home(.retrySync)))),
|
||||
secondaryButton: .default(TextState(secondaryButtonTitle), action: .send(.dismissAlert))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Import Wallet
|
||||
|
||||
extension AlertRequest {
|
||||
func importWalletAlertState(_ importWallet: ImportWallet) -> AlertState<RootReducer.Action> {
|
||||
switch importWallet {
|
||||
case .succeed:
|
||||
return AlertState(
|
||||
title: TextState(L10n.General.success),
|
||||
message: TextState(L10n.ImportWallet.Alert.Success.message),
|
||||
dismissButton: .default(TextState(L10n.General.ok), action: .send(.uniAlert(.importWallet(.successfullyRecovered))))
|
||||
)
|
||||
case .failed(let error):
|
||||
return AlertState(
|
||||
title: TextState(L10n.ImportWallet.Alert.Failed.title),
|
||||
message: TextState(L10n.ImportWallet.Alert.Failed.message(error.message, error.code.rawValue)),
|
||||
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Root
|
||||
|
||||
extension AlertRequest {
|
||||
func rootAlertState(_ root: Root) -> AlertState<RootReducer.Action> {
|
||||
switch root {
|
||||
case .cantCreateNewWallet(let error):
|
||||
return AlertState(
|
||||
title: TextState(L10n.Root.Initialization.Alert.Failed.title),
|
||||
message: TextState(L10n.Root.Initialization.Alert.CantCreateNewWallet.message(error.message, error.code.rawValue)),
|
||||
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
|
||||
)
|
||||
case .cantLoadSeedPhrase:
|
||||
return AlertState(
|
||||
title: TextState(L10n.Root.Initialization.Alert.Failed.title),
|
||||
message: TextState(L10n.Root.Initialization.Alert.CantLoadSeedPhrase.message),
|
||||
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
|
||||
)
|
||||
case .cantStartSync(let error):
|
||||
return AlertState(
|
||||
title: TextState(L10n.Root.Debug.Alert.Rewind.CantStartSync.title),
|
||||
message: TextState(L10n.Root.Debug.Alert.Rewind.CantStartSync.message(error.message, error.code.rawValue)),
|
||||
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
|
||||
)
|
||||
case .cantStoreThatUserPassedPhraseBackupTest(let error):
|
||||
return AlertState(
|
||||
title: TextState(L10n.Root.Initialization.Alert.Failed.title),
|
||||
message: TextState(
|
||||
L10n.Root.Initialization.Alert.CantStoreThatUserPassedPhraseBackupTest.message(error.message, error.code.rawValue)
|
||||
),
|
||||
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
|
||||
)
|
||||
case let .failedToProcessDeeplink(url, error):
|
||||
return AlertState(
|
||||
title: TextState(L10n.Root.Destination.Alert.FailedToProcessDeeplink.title),
|
||||
message: TextState(L10n.Root.Destination.Alert.FailedToProcessDeeplink.message(url, error.message, error.code.rawValue)),
|
||||
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
|
||||
)
|
||||
case .initializationFailed(let error):
|
||||
return AlertState(
|
||||
title: TextState(L10n.Root.Initialization.Alert.SdkInitFailed.title),
|
||||
message: TextState(L10n.Root.Initialization.Alert.Error.message(error.message, error.code.rawValue)),
|
||||
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
|
||||
)
|
||||
case .rewindFailed(let error):
|
||||
return AlertState(
|
||||
title: TextState(L10n.Root.Debug.Alert.Rewind.Failed.title),
|
||||
message: TextState(L10n.Root.Debug.Alert.Rewind.Failed.message(error.message, error.code.rawValue)),
|
||||
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
|
||||
)
|
||||
case .walletStateFailed(let walletState):
|
||||
return AlertState(
|
||||
title: TextState(L10n.Root.Initialization.Alert.Failed.title),
|
||||
message: TextState(L10n.Root.Initialization.Alert.WalletStateFailed.message(walletState)),
|
||||
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
|
||||
)
|
||||
case .wipeFailed:
|
||||
return AlertState(
|
||||
title: TextState(L10n.Root.Initialization.Alert.WipeFailed.title),
|
||||
message: TextState(""),
|
||||
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
|
||||
)
|
||||
case .wipeRequest:
|
||||
return AlertState(
|
||||
title: TextState(L10n.Root.Initialization.Alert.Wipe.title),
|
||||
message: TextState(L10n.Root.Initialization.Alert.Wipe.message),
|
||||
buttons: [
|
||||
.destructive(TextState(L10n.General.yes), action: .send(.initialization(.nukeWallet))),
|
||||
.cancel(TextState(L10n.General.no), action: .send(.dismissAlert))
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Scan
|
||||
|
||||
extension AlertRequest {
|
||||
func scanAlertState(_ scan: Scan) -> AlertState<RootReducer.Action> {
|
||||
switch scan {
|
||||
case .cantInitializeCamera(let error):
|
||||
return AlertState(
|
||||
title: TextState(L10n.Scan.Alert.CantInitializeCamera.title),
|
||||
message: TextState(L10n.Scan.Alert.CantInitializeCamera.message(error.message, error.code.rawValue)),
|
||||
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Settings
|
||||
|
||||
extension AlertRequest {
|
||||
func settingsAlertState(_ settings: Settings) -> AlertState<RootReducer.Action> {
|
||||
switch settings {
|
||||
case .cantBackupWallet(let error):
|
||||
return AlertState<RootReducer.Action>(
|
||||
title: TextState(L10n.Settings.Alert.CantBackupWallet.title),
|
||||
message: TextState(L10n.Settings.Alert.CantBackupWallet.message(error.message, error.code.rawValue)),
|
||||
dismissButton: .default(TextState(L10n.General.ok), action: .send(.dismissAlert))
|
||||
)
|
||||
case .sendSupportMail:
|
||||
return AlertState<RootReducer.Action>(
|
||||
title: TextState(L10n.Settings.Alert.CantSendEmail.title),
|
||||
message: TextState(L10n.Settings.Alert.CantSendEmail.message),
|
||||
dismissButton: .default(TextState(L10n.General.ok), action: .send(.uniAlert(.settings(.sendSupportMailFinished))))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Wallet Events
|
||||
|
||||
extension AlertRequest {
|
||||
func walletEventsAlertState(_ walletEvents: WalletEvents) -> AlertState<RootReducer.Action> {
|
||||
switch walletEvents {
|
||||
case .warnBeforeLeavingApp(let blockExplorerURL):
|
||||
return AlertState(
|
||||
title: TextState(L10n.WalletEvent.Alert.LeavingApp.title),
|
||||
message: TextState(L10n.WalletEvent.Alert.LeavingApp.message),
|
||||
primaryButton: .cancel(
|
||||
TextState(L10n.WalletEvent.Alert.LeavingApp.Button.nevermind),
|
||||
action: .send(.dismissAlert)
|
||||
),
|
||||
secondaryButton: .default(
|
||||
TextState(L10n.WalletEvent.Alert.LeavingApp.Button.seeOnline),
|
||||
action: .send(.uniAlert(.walletEvents(.openBlockExplorer(blockExplorerURL))))
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -23,6 +23,7 @@ struct BalanceBreakdownReducer: ReducerProtocol {
|
|||
private enum CancelId { case timer }
|
||||
|
||||
struct State: Equatable {
|
||||
@PresentationState var alert: AlertState<Action>?
|
||||
var autoShieldingThreshold: Zatoshi
|
||||
var latestBlock: String
|
||||
var shieldedBalance: Balance
|
||||
|
@ -43,7 +44,7 @@ struct BalanceBreakdownReducer: ReducerProtocol {
|
|||
}
|
||||
|
||||
enum Action: Equatable {
|
||||
case alert(AlertRequest)
|
||||
case alert(PresentationAction<Action>)
|
||||
case onAppear
|
||||
case onDisappear
|
||||
case shieldFunds
|
||||
|
@ -94,11 +95,13 @@ struct BalanceBreakdownReducer: ReducerProtocol {
|
|||
|
||||
case .shieldFundsSuccess:
|
||||
state.shieldingFunds = false
|
||||
return EffectTask(value: .alert(.balanceBreakdown(.shieldFundsSuccess)))
|
||||
state.alert = AlertState.shieldFundsSuccess()
|
||||
return .none
|
||||
|
||||
case .shieldFundsFailure(let error):
|
||||
state.shieldingFunds = false
|
||||
return EffectTask(value: .alert(.balanceBreakdown(.shieldFundsFailure(error))))
|
||||
state.alert = AlertState.shieldFundsFailure(error)
|
||||
return .none
|
||||
|
||||
case .synchronizerStateChanged(let latestState):
|
||||
state.shieldedBalance = latestState.shieldedBalance.redacted
|
||||
|
@ -108,13 +111,33 @@ struct BalanceBreakdownReducer: ReducerProtocol {
|
|||
case .updateLatestBlock:
|
||||
let latestBlockNumber = sdkSynchronizer.latestScannedHeight()
|
||||
let latestBlock = numberFormatter.string(NSDecimalNumber(value: latestBlockNumber))
|
||||
state.latestBlock = "\(String(describing: latestBlock))"
|
||||
state.latestBlock = "\(String(describing: latestBlock ?? ""))"
|
||||
return .none
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Alerts
|
||||
|
||||
extension AlertState where Action == BalanceBreakdownReducer.Action {
|
||||
static func shieldFundsFailure(_ error: ZcashError) -> AlertState<BalanceBreakdownReducer.Action> {
|
||||
AlertState<BalanceBreakdownReducer.Action> {
|
||||
TextState(L10n.BalanceBreakdown.Alert.ShieldFunds.Failure.title)
|
||||
} message: {
|
||||
TextState(L10n.BalanceBreakdown.Alert.ShieldFunds.Failure.message(error.message, error.code.rawValue))
|
||||
}
|
||||
}
|
||||
|
||||
static func shieldFundsSuccess() -> AlertState<BalanceBreakdownReducer.Action> {
|
||||
AlertState<BalanceBreakdownReducer.Action> {
|
||||
TextState(L10n.BalanceBreakdown.Alert.ShieldFunds.Success.title)
|
||||
} message: {
|
||||
TextState(L10n.BalanceBreakdown.Alert.ShieldFunds.Success.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Placeholders
|
||||
|
||||
extension BalanceBreakdownReducer.State {
|
||||
|
|
|
@ -52,6 +52,10 @@ struct BalanceBreakdownView: View {
|
|||
.onDisappear { viewStore.send(.onDisappear) }
|
||||
}
|
||||
.applyScreenBackground()
|
||||
.alert(store: store.scope(
|
||||
state: \.$alert,
|
||||
action: { .alert($0) }
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,19 +11,21 @@ import Foundation
|
|||
import ZcashLightClientKit
|
||||
import LogsHandler
|
||||
import Utils
|
||||
import Generated
|
||||
|
||||
typealias ExportLogsStore = Store<ExportLogsReducer.State, ExportLogsReducer.Action>
|
||||
typealias ExportLogsViewStore = ViewStore<ExportLogsReducer.State, ExportLogsReducer.Action>
|
||||
|
||||
struct ExportLogsReducer: ReducerProtocol {
|
||||
struct State: Equatable {
|
||||
@PresentationState var alert: AlertState<Action>?
|
||||
var exportLogsDisabled = false
|
||||
var isSharingLogs = false
|
||||
var zippedLogsURLs: [URL] = []
|
||||
}
|
||||
|
||||
indirect enum Action: Equatable {
|
||||
case alert(AlertRequest)
|
||||
case alert(PresentationAction<Action>)
|
||||
case start
|
||||
case finished(URL?)
|
||||
case failed(ZcashError)
|
||||
|
@ -64,7 +66,8 @@ struct ExportLogsReducer: ReducerProtocol {
|
|||
case let .failed(error):
|
||||
state.exportLogsDisabled = false
|
||||
state.isSharingLogs = false
|
||||
return EffectTask(value: .alert(.exportLogs(.failed(error))))
|
||||
state.alert = AlertState.failed(error)
|
||||
return .none
|
||||
|
||||
case .shareFinished:
|
||||
state.isSharingLogs = false
|
||||
|
@ -74,6 +77,18 @@ struct ExportLogsReducer: ReducerProtocol {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: Alerts
|
||||
|
||||
extension AlertState where Action == ExportLogsReducer.Action {
|
||||
static func failed(_ error: ZcashError) -> AlertState<ExportLogsReducer.Action> {
|
||||
AlertState<ExportLogsReducer.Action> {
|
||||
TextState(L10n.ExportLogs.Alert.Failed.title)
|
||||
} message: {
|
||||
TextState(L10n.ExportLogs.Alert.Failed.message(error.message, error.code.rawValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Placeholders
|
||||
|
||||
extension ExportLogsReducer.State {
|
||||
|
|
|
@ -27,6 +27,7 @@ struct HomeReducer: ReducerProtocol {
|
|||
case transactionHistory
|
||||
}
|
||||
|
||||
@PresentationState var alert: AlertState<Action>?
|
||||
var balanceBreakdownState: BalanceBreakdownReducer.State
|
||||
var destination: Destination?
|
||||
var canRequestReview = false
|
||||
|
@ -68,24 +69,25 @@ struct HomeReducer: ReducerProtocol {
|
|||
}
|
||||
|
||||
enum Action: Equatable {
|
||||
case alert(AlertRequest)
|
||||
case alert(PresentationAction<Action>)
|
||||
case balanceBreakdown(BalanceBreakdownReducer.Action)
|
||||
case debugMenuStartup
|
||||
case dismissAlert
|
||||
case foundTransactions
|
||||
case onAppear
|
||||
case onDisappear
|
||||
case profile(ProfileReducer.Action)
|
||||
case resolveReviewRequest
|
||||
case retrySync
|
||||
case reviewRequestFinished
|
||||
case send(SendFlowReducer.Action)
|
||||
case settings(SettingsReducer.Action)
|
||||
case syncFailed(ZcashError)
|
||||
case foundTransactions
|
||||
case synchronizerStateChanged(SynchronizerState)
|
||||
case walletEvents(WalletEventsFlowReducer.Action)
|
||||
case updateDestination(HomeReducer.State.Destination?)
|
||||
case showSynchronizerErrorAlert(ZcashError)
|
||||
case retrySync
|
||||
case synchronizerStateChanged(SynchronizerState)
|
||||
case syncFailed(ZcashError)
|
||||
case updateDestination(HomeReducer.State.Destination?)
|
||||
case updateWalletEvents([WalletEvent])
|
||||
case walletEvents(WalletEventsFlowReducer.Action)
|
||||
}
|
||||
|
||||
@Dependency(\.audioServices) var audioServices
|
||||
|
@ -214,7 +216,8 @@ struct HomeReducer: ReducerProtocol {
|
|||
}
|
||||
|
||||
case .showSynchronizerErrorAlert(let error):
|
||||
return EffectTask(value: .alert(.home(.syncFailed(error, L10n.Home.SyncFailed.dismiss))))
|
||||
state.alert = AlertState.syncFailed(error, L10n.Home.SyncFailed.dismiss)
|
||||
return .none
|
||||
|
||||
case .balanceBreakdown(.onDisappear):
|
||||
state.destination = nil
|
||||
|
@ -227,10 +230,14 @@ struct HomeReducer: ReducerProtocol {
|
|||
return .none
|
||||
|
||||
case .syncFailed(let error):
|
||||
return EffectTask(value: .alert(.home(.syncFailed(error, L10n.General.ok))))
|
||||
|
||||
state.alert = AlertState.syncFailed(error, L10n.General.ok)
|
||||
return .none
|
||||
|
||||
case .alert:
|
||||
return .none
|
||||
|
||||
case .dismissAlert:
|
||||
return .none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -288,6 +295,25 @@ extension HomeViewStore {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: Alerts
|
||||
|
||||
extension AlertState where Action == HomeReducer.Action {
|
||||
static func syncFailed(_ error: ZcashError, _ secondaryButtonTitle: String) -> AlertState<HomeReducer.Action> {
|
||||
AlertState<HomeReducer.Action> {
|
||||
TextState(L10n.Home.SyncFailed.title)
|
||||
} actions: {
|
||||
ButtonState(action: .retrySync) {
|
||||
TextState(L10n.Home.SyncFailed.retry)
|
||||
}
|
||||
ButtonState(action: .dismissAlert) {
|
||||
TextState(secondaryButtonTitle)
|
||||
}
|
||||
} message: {
|
||||
TextState("\(error.message) (code: \(error.code.rawValue))")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Placeholders
|
||||
|
||||
extension HomeReducer.State {
|
||||
|
|
|
@ -42,6 +42,10 @@ struct HomeView: View {
|
|||
}
|
||||
}
|
||||
.onDisappear { viewStore.send(.onDisappear) }
|
||||
.alert(store: store.scope(
|
||||
state: \.$alert,
|
||||
action: { .alert($0) }
|
||||
))
|
||||
.navigationLinkEmpty(
|
||||
isActive: viewStore.bindingForDestination(.balanceBreakdown),
|
||||
destination: { BalanceBreakdownView(store: store.balanceBreakdownStore()) }
|
||||
|
|
|
@ -21,6 +21,7 @@ struct ImportWalletReducer: ReducerProtocol {
|
|||
case birthday
|
||||
}
|
||||
|
||||
@PresentationState var alert: AlertState<Action>?
|
||||
var birthdayHeight = "".redacted
|
||||
var birthdayHeightValue: RedactableBlockHeight?
|
||||
var destination: Destination?
|
||||
|
@ -46,8 +47,9 @@ struct ImportWalletReducer: ReducerProtocol {
|
|||
}
|
||||
|
||||
enum Action: Equatable {
|
||||
case alert(AlertRequest)
|
||||
case alert(PresentationAction<Action>)
|
||||
case birthdayInputChanged(RedactableString)
|
||||
case dismissAlert
|
||||
case restoreWallet
|
||||
case importPrivateOrViewingKey
|
||||
case initializeSDK
|
||||
|
@ -111,15 +113,15 @@ struct ImportWalletReducer: ReducerProtocol {
|
|||
// update the backup phrase validation flag
|
||||
try walletStorage.markUserPassedPhraseBackupTest(true)
|
||||
|
||||
state.alert = AlertState.succeed()
|
||||
|
||||
// notify user
|
||||
return .concatenate(
|
||||
EffectTask(value: .alert(.importWallet(.succeed))),
|
||||
EffectTask(value: .initializeSDK)
|
||||
)
|
||||
return EffectTask(value: .initializeSDK)
|
||||
} catch {
|
||||
return EffectTask(value: .alert(.importWallet(.failed(error.toZcashError()))))
|
||||
state.alert = AlertState.failed(error.toZcashError())
|
||||
}
|
||||
|
||||
return .none
|
||||
|
||||
case .updateDestination(let destination):
|
||||
state.destination = destination
|
||||
return .none
|
||||
|
@ -132,6 +134,9 @@ struct ImportWalletReducer: ReducerProtocol {
|
|||
|
||||
case .initializeSDK:
|
||||
return .none
|
||||
|
||||
case .dismissAlert:
|
||||
return .none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -164,6 +169,34 @@ extension ImportWalletViewStore {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: Alerts
|
||||
|
||||
extension AlertState where Action == ImportWalletReducer.Action {
|
||||
static func succeed() -> AlertState<ImportWalletReducer.Action> {
|
||||
AlertState<ImportWalletReducer.Action> {
|
||||
TextState(L10n.General.success)
|
||||
} actions: {
|
||||
ButtonState(action: .successfullyRecovered) {
|
||||
TextState(L10n.General.ok)
|
||||
}
|
||||
} message: {
|
||||
TextState(L10n.ImportWallet.Alert.Success.message)
|
||||
}
|
||||
}
|
||||
|
||||
static func failed(_ error: ZcashError) -> AlertState<ImportWalletReducer.Action> {
|
||||
AlertState<ImportWalletReducer.Action> {
|
||||
TextState(L10n.ImportWallet.Alert.Failed.title)
|
||||
} actions: {
|
||||
ButtonState(action: .dismissAlert) {
|
||||
TextState(L10n.General.ok)
|
||||
}
|
||||
} message: {
|
||||
TextState(L10n.ImportWallet.Alert.Failed.message(error.message, error.code.rawValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Placeholders
|
||||
|
||||
extension ImportWalletReducer.State {
|
||||
|
|
|
@ -44,6 +44,10 @@ struct ImportWalletView: View {
|
|||
isActive: viewStore.bindingForDestination(.birthday),
|
||||
destination: { ImportBirthdayView(store: store) }
|
||||
)
|
||||
.alert(store: store.scope(
|
||||
state: \.$alert,
|
||||
action: { .alert($0) }
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,8 @@ extension RootReducer {
|
|||
|
||||
case let .debug(.rewindDone(error, _)):
|
||||
if let error {
|
||||
return EffectTask(value: .alert(.root(.rewindFailed(error.toZcashError()))))
|
||||
state.alert = AlertState.rewindFailed(error.toZcashError())
|
||||
return .none
|
||||
} else {
|
||||
return .run { send in
|
||||
do {
|
||||
|
@ -96,7 +97,8 @@ extension RootReducer {
|
|||
return EffectTask(value: .updateStateAfterConfigUpdate(walletConfig))
|
||||
|
||||
case .debug(.cantStartSync(let error)):
|
||||
return EffectTask(value: .alert(.root(.cantStartSync(error))))
|
||||
state.alert = AlertState.cantStartSync(error)
|
||||
return .none
|
||||
|
||||
case .debug(.rateTheApp):
|
||||
return .none
|
||||
|
|
|
@ -113,7 +113,8 @@ extension RootReducer {
|
|||
return .none
|
||||
|
||||
case let .destination(.deeplinkFailed(url, error)):
|
||||
return EffectTask(value: .alert(.root(.failedToProcessDeeplink(url, error))))
|
||||
state.alert = AlertState.failedToProcessDeeplink(url, error)
|
||||
return .none
|
||||
|
||||
case .home(.walletEvents(.replyTo(let address))):
|
||||
guard let url = URL(string: "zcash:\(address)") else {
|
||||
|
@ -122,7 +123,7 @@ extension RootReducer {
|
|||
return EffectTask(value: .destination(.deeplink(url)))
|
||||
|
||||
case .home, .initialization, .onboarding, .phraseDisplay, .phraseValidation, .sandbox, .updateStateAfterConfigUpdate, .alert,
|
||||
.welcome, .binding, .nukeWalletFailed, .nukeWalletSucceeded, .debug, .walletConfigLoaded, .dismissAlert, .exportLogs, .uniAlert:
|
||||
.welcome, .binding, .nukeWalletFailed, .nukeWalletSucceeded, .debug, .walletConfigLoaded, .dismissAlert, .exportLogs:
|
||||
return .none
|
||||
}
|
||||
|
||||
|
|
|
@ -85,10 +85,12 @@ extension RootReducer {
|
|||
switch walletState {
|
||||
case .failed:
|
||||
state.appInitializationState = .failed
|
||||
return EffectTask(value: .alert(.root(.walletStateFailed(walletState))))
|
||||
state.alert = AlertState.walletStateFailed(walletState)
|
||||
return .none
|
||||
case .keysMissing:
|
||||
state.appInitializationState = .keysMissing
|
||||
return EffectTask(value: .alert(.root(.walletStateFailed(walletState))))
|
||||
state.alert = AlertState.walletStateFailed(walletState)
|
||||
return .none
|
||||
case .initialized, .filesMissing:
|
||||
if walletState == .filesMissing {
|
||||
state.appInitializationState = .filesMissing
|
||||
|
@ -113,7 +115,8 @@ extension RootReducer {
|
|||
|
||||
guard let storedWallet = state.storedWallet else {
|
||||
state.appInitializationState = .failed
|
||||
return EffectTask(value: .alert(.root(.cantLoadSeedPhrase)))
|
||||
state.alert = AlertState.cantLoadSeedPhrase()
|
||||
return .none
|
||||
}
|
||||
|
||||
let birthday = state.storedWallet?.birthday?.value() ?? zcashSDKEnvironment.latestCheckpoint(TargetConstants.zcashNetwork)
|
||||
|
@ -138,7 +141,8 @@ extension RootReducer {
|
|||
case .initialization(.checkBackupPhraseValidation):
|
||||
guard let storedWallet = state.storedWallet else {
|
||||
state.appInitializationState = .failed
|
||||
return EffectTask(value: .alert(.root(.cantLoadSeedPhrase)))
|
||||
state.alert = AlertState.cantLoadSeedPhrase()
|
||||
return .none
|
||||
}
|
||||
|
||||
var landingDestination = RootReducer.DestinationState.Destination.home
|
||||
|
@ -179,19 +183,21 @@ extension RootReducer {
|
|||
EffectTask(value: .phraseValidation(.displayBackedUpPhrase))
|
||||
)
|
||||
} catch {
|
||||
return EffectTask(value: .alert(.root(.cantCreateNewWallet(error.toZcashError()))))
|
||||
state.alert = AlertState.cantCreateNewWallet(error.toZcashError())
|
||||
}
|
||||
return .none
|
||||
|
||||
case .phraseValidation(.succeed):
|
||||
do {
|
||||
try walletStorage.markUserPassedPhraseBackupTest(true)
|
||||
return .none
|
||||
} catch {
|
||||
return EffectTask(value: .alert(.root(.cantStoreThatUserPassedPhraseBackupTest(error.toZcashError()))))
|
||||
state.alert = AlertState.cantStoreThatUserPassedPhraseBackupTest(error.toZcashError())
|
||||
}
|
||||
return .none
|
||||
|
||||
case .initialization(.nukeWalletRequest):
|
||||
return EffectTask(value: .alert(.root(.wipeRequest)))
|
||||
state.alert = AlertState.wipeRequest()
|
||||
return .none
|
||||
|
||||
case .initialization(.nukeWallet):
|
||||
guard let wipePublisher = sdkSynchronizer.wipe() else {
|
||||
|
@ -221,8 +227,8 @@ extension RootReducer {
|
|||
} else {
|
||||
backDestination = EffectTask(value: .destination(.updateDestination(state.destinationState.destination)))
|
||||
}
|
||||
state.alert = AlertState.wipeFailed()
|
||||
return .concatenate(
|
||||
EffectTask(value: .alert(.root(.wipeFailed))),
|
||||
.cancel(id: SynchronizerCancelId.timer),
|
||||
backDestination
|
||||
)
|
||||
|
@ -256,10 +262,11 @@ extension RootReducer {
|
|||
|
||||
case .initialization(.initializationFailed(let error)):
|
||||
state.appInitializationState = .failed
|
||||
return EffectTask(value: .alert(.root(.initializationFailed(error))))
|
||||
state.alert = AlertState.initializationFailed(error)
|
||||
return .none
|
||||
|
||||
case .home, .destination, .onboarding, .phraseDisplay, .phraseValidation, .sandbox,
|
||||
.welcome, .binding, .debug, .exportLogs, .uniAlert, .dismissAlert, .alert:
|
||||
.welcome, .binding, .debug, .exportLogs, .dismissAlert, .alert:
|
||||
return .none
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ import UserPreferencesStorage
|
|||
import Models
|
||||
import RecoveryPhraseDisplay
|
||||
import Welcome
|
||||
import Generated
|
||||
import Foundation
|
||||
|
||||
typealias RootStore = Store<RootReducer.State, RootReducer.Action>
|
||||
typealias RootViewStore = ViewStore<RootReducer.State, RootReducer.Action>
|
||||
|
@ -20,6 +22,7 @@ struct RootReducer: ReducerProtocol {
|
|||
enum WalletConfigCancelId { case timer }
|
||||
|
||||
struct State: Equatable {
|
||||
@PresentationState var alert: AlertState<Action>?
|
||||
var appInitializationState: InitializationState = .uninitialized
|
||||
var debugState: DebugState
|
||||
var destinationState: DestinationState
|
||||
|
@ -30,13 +33,12 @@ struct RootReducer: ReducerProtocol {
|
|||
var phraseDisplayState: RecoveryPhraseDisplayReducer.State
|
||||
var sandboxState: SandboxReducer.State
|
||||
var storedWallet: StoredWallet?
|
||||
@BindingState var uniAlert: AlertState<RootReducer.Action>?
|
||||
var walletConfig: WalletConfig
|
||||
var welcomeState: WelcomeReducer.State
|
||||
}
|
||||
|
||||
enum Action: Equatable, BindableAction {
|
||||
case alert(AlertRequest)
|
||||
enum Action: Equatable {
|
||||
case alert(PresentationAction<Action>)
|
||||
case binding(BindingAction<RootReducer.State>)
|
||||
case debug(DebugAction)
|
||||
case dismissAlert
|
||||
|
@ -50,7 +52,6 @@ struct RootReducer: ReducerProtocol {
|
|||
case phraseDisplay(RecoveryPhraseDisplayReducer.Action)
|
||||
case phraseValidation(RecoveryPhraseValidationFlowReducer.Action)
|
||||
case sandbox(SandboxReducer.Action)
|
||||
case uniAlert(AlertAction)
|
||||
case updateStateAfterConfigUpdate(WalletConfig)
|
||||
case walletConfigLoaded(WalletConfig)
|
||||
case welcome(WelcomeReducer.Action)
|
||||
|
@ -71,8 +72,6 @@ struct RootReducer: ReducerProtocol {
|
|||
|
||||
@ReducerBuilder<State, Action>
|
||||
var core: some ReducerProtocol<State, Action> {
|
||||
BindingReducer()
|
||||
|
||||
Scope(state: \.homeState, action: /Action.home) {
|
||||
HomeReducer()
|
||||
}
|
||||
|
@ -110,7 +109,6 @@ struct RootReducer: ReducerProtocol {
|
|||
|
||||
var body: some ReducerProtocol<State, Action> {
|
||||
self.core
|
||||
.alerts()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -148,6 +146,97 @@ extension RootReducer {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: Alerts
|
||||
|
||||
extension AlertState where Action == RootReducer.Action {
|
||||
static func cantCreateNewWallet(_ error: ZcashError) -> AlertState<RootReducer.Action> {
|
||||
AlertState<RootReducer.Action> {
|
||||
TextState(L10n.Root.Initialization.Alert.Failed.title)
|
||||
} message: {
|
||||
TextState(L10n.Root.Initialization.Alert.CantCreateNewWallet.message(error.message, error.code.rawValue))
|
||||
}
|
||||
}
|
||||
|
||||
static func cantLoadSeedPhrase() -> AlertState<RootReducer.Action> {
|
||||
AlertState<RootReducer.Action> {
|
||||
TextState(L10n.Root.Initialization.Alert.Failed.title)
|
||||
} message: {
|
||||
TextState(L10n.Root.Initialization.Alert.CantLoadSeedPhrase.message)
|
||||
}
|
||||
}
|
||||
|
||||
static func cantStartSync(_ error: ZcashError) -> AlertState<RootReducer.Action> {
|
||||
AlertState<RootReducer.Action> {
|
||||
TextState(L10n.Root.Debug.Alert.Rewind.CantStartSync.title)
|
||||
} message: {
|
||||
TextState(L10n.Root.Debug.Alert.Rewind.CantStartSync.message(error.message, error.code.rawValue))
|
||||
}
|
||||
}
|
||||
|
||||
static func cantStoreThatUserPassedPhraseBackupTest(_ error: ZcashError) -> AlertState<RootReducer.Action> {
|
||||
AlertState<RootReducer.Action> {
|
||||
TextState(L10n.Root.Initialization.Alert.Failed.title)
|
||||
} message: {
|
||||
TextState(
|
||||
L10n.Root.Initialization.Alert.CantStoreThatUserPassedPhraseBackupTest.message(error.message, error.code.rawValue)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
static func failedToProcessDeeplink(_ url: URL, _ error: ZcashError) -> AlertState<RootReducer.Action> {
|
||||
AlertState<RootReducer.Action> {
|
||||
TextState(L10n.Root.Destination.Alert.FailedToProcessDeeplink.title)
|
||||
} message: {
|
||||
TextState(L10n.Root.Destination.Alert.FailedToProcessDeeplink.message(url, error.message, error.code.rawValue))
|
||||
}
|
||||
}
|
||||
|
||||
static func initializationFailed(_ error: ZcashError) -> AlertState<RootReducer.Action> {
|
||||
AlertState<RootReducer.Action> {
|
||||
TextState(L10n.Root.Initialization.Alert.SdkInitFailed.title)
|
||||
} message: {
|
||||
TextState(L10n.Root.Initialization.Alert.Error.message(error.message, error.code.rawValue))
|
||||
}
|
||||
}
|
||||
|
||||
static func rewindFailed(_ error: ZcashError) -> AlertState<RootReducer.Action> {
|
||||
AlertState<RootReducer.Action> {
|
||||
TextState(L10n.Root.Debug.Alert.Rewind.Failed.title)
|
||||
} message: {
|
||||
TextState(L10n.Root.Debug.Alert.Rewind.Failed.message(error.message, error.code.rawValue))
|
||||
}
|
||||
}
|
||||
|
||||
static func walletStateFailed(_ walletState: InitializationState) -> AlertState<RootReducer.Action> {
|
||||
AlertState<RootReducer.Action> {
|
||||
TextState(L10n.Root.Initialization.Alert.Failed.title)
|
||||
} message: {
|
||||
TextState(L10n.Root.Initialization.Alert.WalletStateFailed.message(walletState))
|
||||
}
|
||||
}
|
||||
|
||||
static func wipeFailed() -> AlertState<RootReducer.Action> {
|
||||
AlertState<RootReducer.Action> {
|
||||
TextState(L10n.Root.Initialization.Alert.WipeFailed.title)
|
||||
}
|
||||
}
|
||||
|
||||
static func wipeRequest() -> AlertState<RootReducer.Action> {
|
||||
AlertState<RootReducer.Action> {
|
||||
TextState(L10n.Root.Initialization.Alert.Wipe.title)
|
||||
} actions: {
|
||||
ButtonState(role: .destructive, action: .initialization(.nukeWallet)) {
|
||||
TextState(L10n.General.yes)
|
||||
}
|
||||
ButtonState(role: .cancel, action: .dismissAlert) {
|
||||
TextState(L10n.General.no)
|
||||
}
|
||||
} message: {
|
||||
TextState(L10n.Root.Initialization.Alert.Wipe.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Placeholders
|
||||
|
||||
extension RootReducer.State {
|
||||
|
|
|
@ -114,11 +114,15 @@ private extension RootView {
|
|||
}
|
||||
}
|
||||
.onOpenURL(perform: { viewStore.goToDeeplink($0) })
|
||||
.alert(store.scope(
|
||||
state: \.uniAlert,
|
||||
action: { _ in RootReducer.Action.dismissAlert }
|
||||
), dismiss: .dismissAlert)
|
||||
|
||||
.alert(store: store.scope(
|
||||
state: \.$alert,
|
||||
action: { .alert($0) }
|
||||
))
|
||||
.alert(store: store.scope(
|
||||
state: \.exportLogsState.$alert,
|
||||
action: { .exportLogs(.alert($0)) }
|
||||
))
|
||||
|
||||
shareLogsView(viewStore)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,8 @@ import Foundation
|
|||
import CaptureDevice
|
||||
import Utils
|
||||
import URIParser
|
||||
import ZcashLightClientKit
|
||||
import Generated
|
||||
|
||||
typealias ScanStore = Store<ScanReducer.State, ScanReducer.Action>
|
||||
typealias ScanViewStore = ViewStore<ScanReducer.State, ScanReducer.Action>
|
||||
|
@ -24,6 +26,7 @@ struct ScanReducer: ReducerProtocol {
|
|||
case unknown
|
||||
}
|
||||
|
||||
@PresentationState var alert: AlertState<Action>?
|
||||
var isTorchAvailable = false
|
||||
var isTorchOn = false
|
||||
var scanStatus: ScanStatus = .unknown
|
||||
|
@ -49,7 +52,7 @@ struct ScanReducer: ReducerProtocol {
|
|||
@Dependency(\.uriParser) var uriParser
|
||||
|
||||
enum Action: Equatable {
|
||||
case alert(AlertRequest)
|
||||
case alert(PresentationAction<Action>)
|
||||
case onAppear
|
||||
case onDisappear
|
||||
case found(RedactableString)
|
||||
|
@ -59,63 +62,78 @@ struct ScanReducer: ReducerProtocol {
|
|||
}
|
||||
|
||||
// swiftlint:disable:next cyclomatic_complexity
|
||||
func reduce(into state: inout State, action: Action) -> ComposableArchitecture.EffectTask<Action> {
|
||||
switch action {
|
||||
case .alert:
|
||||
return .none
|
||||
|
||||
case .onAppear:
|
||||
// reset the values
|
||||
state.scanStatus = .unknown
|
||||
state.isTorchOn = false
|
||||
// check the torch availability
|
||||
do {
|
||||
state.isTorchAvailable = try captureDevice.isTorchAvailable()
|
||||
var body: some ReducerProtocolOf<Self> {
|
||||
Reduce { state, action in
|
||||
switch action {
|
||||
case .alert:
|
||||
return .none
|
||||
} catch {
|
||||
return EffectTask(value: .alert(.scan(.cantInitializeCamera(error.toZcashError()))))
|
||||
}
|
||||
|
||||
case .onDisappear:
|
||||
return .cancel(id: CancelId.timer)
|
||||
|
||||
case .found:
|
||||
return .none
|
||||
|
||||
case .scanFailed:
|
||||
state.scanStatus = .failed
|
||||
return .none
|
||||
|
||||
case .scan(let code):
|
||||
// the logic for the same scanned code is skipped until some new code
|
||||
if let prevCode = state.scannedValue, prevCode == code.data {
|
||||
|
||||
case .onAppear:
|
||||
// reset the values
|
||||
state.scanStatus = .unknown
|
||||
state.isTorchOn = false
|
||||
// check the torch availability
|
||||
do {
|
||||
state.isTorchAvailable = try captureDevice.isTorchAvailable()
|
||||
} catch {
|
||||
state.alert = AlertState.cantInitializeCamera(error.toZcashError())
|
||||
}
|
||||
return .none
|
||||
}
|
||||
if uriParser.isValidURI(code.data, TargetConstants.zcashNetwork.networkType) {
|
||||
state.scanStatus = .value(code)
|
||||
// once valid URI is scanned we want to start the timer to deliver the code
|
||||
// any new code cancels the schedule and fires new one
|
||||
return .concatenate(
|
||||
EffectTask.cancel(id: CancelId.timer),
|
||||
EffectTask(value: .found(code))
|
||||
.delay(for: 1.0, scheduler: mainQueue)
|
||||
.eraseToEffect()
|
||||
.cancellable(id: CancelId.timer, cancelInFlight: true)
|
||||
)
|
||||
} else {
|
||||
|
||||
case .onDisappear:
|
||||
return .cancel(id: CancelId.timer)
|
||||
|
||||
case .found:
|
||||
return .none
|
||||
|
||||
case .scanFailed:
|
||||
state.scanStatus = .failed
|
||||
}
|
||||
return .cancel(id: CancelId.timer)
|
||||
|
||||
case .torchPressed:
|
||||
do {
|
||||
try captureDevice.torch(!state.isTorchOn)
|
||||
state.isTorchOn.toggle()
|
||||
return .none
|
||||
} catch {
|
||||
return EffectTask(value: .alert(.scan(.cantInitializeCamera(error.toZcashError()))))
|
||||
|
||||
case .scan(let code):
|
||||
// the logic for the same scanned code is skipped until some new code
|
||||
if let prevCode = state.scannedValue, prevCode == code.data {
|
||||
return .none
|
||||
}
|
||||
if uriParser.isValidURI(code.data, TargetConstants.zcashNetwork.networkType) {
|
||||
state.scanStatus = .value(code)
|
||||
// once valid URI is scanned we want to start the timer to deliver the code
|
||||
// any new code cancels the schedule and fires new one
|
||||
return .concatenate(
|
||||
EffectTask.cancel(id: CancelId.timer),
|
||||
EffectTask(value: .found(code))
|
||||
.delay(for: 1.0, scheduler: mainQueue)
|
||||
.eraseToEffect()
|
||||
.cancellable(id: CancelId.timer, cancelInFlight: true)
|
||||
)
|
||||
} else {
|
||||
state.scanStatus = .failed
|
||||
}
|
||||
return .cancel(id: CancelId.timer)
|
||||
|
||||
case .torchPressed:
|
||||
do {
|
||||
try captureDevice.torch(!state.isTorchOn)
|
||||
state.isTorchOn.toggle()
|
||||
} catch {
|
||||
state.alert = AlertState.cantInitializeCamera(error.toZcashError())
|
||||
}
|
||||
return .none
|
||||
}
|
||||
}
|
||||
.ifLet(\.$alert, action: /Action.alert)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Alerts
|
||||
|
||||
extension AlertState where Action == ScanReducer.Action {
|
||||
static func cantInitializeCamera(_ error: ZcashError) -> AlertState<ScanReducer.Action> {
|
||||
AlertState<ScanReducer.Action> {
|
||||
TextState(L10n.Scan.Alert.CantInitializeCamera.title)
|
||||
} message: {
|
||||
TextState(L10n.Scan.Alert.CantInitializeCamera.message(error.message, error.code.rawValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,6 +54,10 @@ struct ScanView: View {
|
|||
}
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
.alert(store: store.scope(
|
||||
state: \.$alert,
|
||||
action: { .alert($0) }
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,8 @@ import LocalAuthenticationHandler
|
|||
import SupportDataGenerator
|
||||
import Models
|
||||
import RecoveryPhraseDisplay
|
||||
import ZcashLightClientKit
|
||||
import Generated
|
||||
|
||||
typealias SettingsStore = Store<SettingsReducer.State, SettingsReducer.Action>
|
||||
typealias SettingsViewStore = ViewStore<SettingsReducer.State, SettingsReducer.Action>
|
||||
|
@ -19,6 +21,7 @@ struct SettingsReducer: ReducerProtocol {
|
|||
case backupPhrase
|
||||
}
|
||||
|
||||
@PresentationState var alert: AlertState<Action>?
|
||||
var appVersion = ""
|
||||
var appBuild = ""
|
||||
var destination: Destination?
|
||||
|
@ -29,7 +32,7 @@ struct SettingsReducer: ReducerProtocol {
|
|||
}
|
||||
|
||||
enum Action: BindableAction, Equatable {
|
||||
case alert(AlertRequest)
|
||||
case alert(PresentationAction<Action>)
|
||||
case backupWallet
|
||||
case backupWalletAccessRequest
|
||||
case binding(BindingAction<SettingsReducer.State>)
|
||||
|
@ -73,9 +76,10 @@ struct SettingsReducer: ReducerProtocol {
|
|||
state.phraseDisplayState.phrase = recoveryPhrase
|
||||
return EffectTask(value: .updateDestination(.backupPhrase))
|
||||
} catch {
|
||||
return EffectTask(value: .alert(.settings(.cantBackupWallet(error.toZcashError()))))
|
||||
state.alert = AlertState.cantBackupWallet(error.toZcashError())
|
||||
}
|
||||
|
||||
return .none
|
||||
|
||||
case .binding(\.$isCrashReportingOn):
|
||||
if state.isCrashReportingOn {
|
||||
crashReporter.optOut()
|
||||
|
@ -104,10 +108,10 @@ struct SettingsReducer: ReducerProtocol {
|
|||
case .sendSupportMail:
|
||||
if MFMailComposeViewController.canSendMail() {
|
||||
state.supportData = SupportDataGenerator.generate()
|
||||
return .none
|
||||
} else {
|
||||
return EffectTask(value: .alert(.settings(.sendSupportMail)))
|
||||
state.alert = AlertState.sendSupportMail()
|
||||
}
|
||||
return .none
|
||||
|
||||
case .sendSupportMailFinished:
|
||||
state.supportData = nil
|
||||
|
@ -117,6 +121,7 @@ struct SettingsReducer: ReducerProtocol {
|
|||
return .none
|
||||
}
|
||||
}
|
||||
.ifLet(\.$alert, action: /Action.alert)
|
||||
|
||||
Scope(state: \.phraseDisplayState, action: /Action.phraseDisplay) {
|
||||
RecoveryPhraseDisplayReducer()
|
||||
|
@ -164,6 +169,30 @@ extension SettingsStore {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: Alerts
|
||||
|
||||
extension AlertState where Action == SettingsReducer.Action {
|
||||
static func cantBackupWallet(_ error: ZcashError) -> AlertState<SettingsReducer.Action> {
|
||||
AlertState<SettingsReducer.Action> {
|
||||
TextState(L10n.Settings.Alert.CantBackupWallet.title)
|
||||
} message: {
|
||||
TextState(L10n.Settings.Alert.CantBackupWallet.message(error.message, error.code.rawValue))
|
||||
}
|
||||
}
|
||||
|
||||
static func sendSupportMail() -> AlertState<SettingsReducer.Action> {
|
||||
AlertState<SettingsReducer.Action> {
|
||||
TextState(L10n.Settings.Alert.CantSendEmail.title)
|
||||
} actions: {
|
||||
ButtonState(action: .sendSupportMailFinished) {
|
||||
TextState(L10n.General.ok)
|
||||
}
|
||||
} message: {
|
||||
TextState(L10n.Settings.Alert.CantSendEmail.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Placeholders
|
||||
|
||||
extension SettingsReducer.State {
|
||||
|
|
|
@ -88,6 +88,14 @@ struct SettingsView: View {
|
|||
.frame(width: 0, height: 0)
|
||||
}
|
||||
}
|
||||
.alert(store: store.scope(
|
||||
state: \.$alert,
|
||||
action: { .alert($0) }
|
||||
))
|
||||
.alert(store: store.scope(
|
||||
state: \.exportLogsState.$alert,
|
||||
action: { .exportLogs(.alert($0)) }
|
||||
))
|
||||
}
|
||||
|
||||
@ViewBuilder func shareLogsView(_ viewStore: SettingsViewStore) -> some View {
|
||||
|
|
|
@ -3,6 +3,7 @@ import SwiftUI
|
|||
import ZcashLightClientKit
|
||||
import Utils
|
||||
import Models
|
||||
import Generated
|
||||
|
||||
typealias WalletEventsFlowStore = Store<WalletEventsFlowReducer.State, WalletEventsFlowReducer.Action>
|
||||
typealias WalletEventsFlowViewStore = ViewStore<WalletEventsFlowReducer.State, WalletEventsFlowReducer.Action>
|
||||
|
@ -17,8 +18,8 @@ struct WalletEventsFlowReducer: ReducerProtocol {
|
|||
case showWalletEvent(WalletEvent)
|
||||
}
|
||||
|
||||
@PresentationState var alert: AlertState<Action>?
|
||||
var destination: Destination?
|
||||
|
||||
var latestMinedHeight: BlockHeight?
|
||||
var isScrollable = true
|
||||
var requiredTransactionConfirmations = 0
|
||||
|
@ -27,8 +28,9 @@ struct WalletEventsFlowReducer: ReducerProtocol {
|
|||
}
|
||||
|
||||
enum Action: Equatable {
|
||||
case alert(AlertRequest)
|
||||
case alert(PresentationAction<Action>)
|
||||
case copyToPastboard(RedactableString)
|
||||
case dismissAlert
|
||||
case onAppear
|
||||
case onDisappear
|
||||
case openBlockExplorer(URL?)
|
||||
|
@ -99,9 +101,13 @@ struct WalletEventsFlowReducer: ReducerProtocol {
|
|||
|
||||
case .alert:
|
||||
return .none
|
||||
|
||||
case .dismissAlert:
|
||||
return .none
|
||||
|
||||
case .warnBeforeLeavingApp(let blockExplorerURL):
|
||||
return EffectTask(value: .alert(.walletEvents(.warnBeforeLeavingApp(blockExplorerURL))))
|
||||
state.alert = AlertState.warnBeforeLeavingApp(blockExplorerURL)
|
||||
return .none
|
||||
|
||||
case .openBlockExplorer(let blockExplorerURL):
|
||||
if let url = blockExplorerURL {
|
||||
|
@ -139,6 +145,25 @@ extension WalletEventsFlowViewStore {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: Alerts
|
||||
|
||||
extension AlertState where Action == WalletEventsFlowReducer.Action {
|
||||
static func warnBeforeLeavingApp(_ blockExplorerURL: URL?) -> AlertState<WalletEventsFlowReducer.Action> {
|
||||
AlertState<WalletEventsFlowReducer.Action> {
|
||||
TextState(L10n.WalletEvent.Alert.LeavingApp.title)
|
||||
} actions: {
|
||||
ButtonState(action: .openBlockExplorer(blockExplorerURL)) {
|
||||
TextState(L10n.WalletEvent.Alert.LeavingApp.Button.seeOnline)
|
||||
}
|
||||
ButtonState(role: .cancel, action: .dismissAlert) {
|
||||
TextState(L10n.WalletEvent.Alert.LeavingApp.Button.nevermind)
|
||||
}
|
||||
} message: {
|
||||
TextState(L10n.WalletEvent.Alert.LeavingApp.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Placeholders
|
||||
|
||||
extension TransactionState {
|
||||
|
|
|
@ -6,7 +6,7 @@ struct WalletEventsFlowView: View {
|
|||
let store: WalletEventsFlowStore
|
||||
|
||||
var body: some View {
|
||||
return WithViewStore(store) { viewStore in
|
||||
WithViewStore(store) { viewStore in
|
||||
List {
|
||||
walletEventsList(with: viewStore)
|
||||
}
|
||||
|
@ -18,6 +18,10 @@ struct WalletEventsFlowView: View {
|
|||
viewStore.selectedWalletEvent?.detailView(store)
|
||||
}
|
||||
}
|
||||
.alert(store: store.scope(
|
||||
state: \.$alert,
|
||||
action: { .alert($0) }
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ class BalanceBreakdownTests: XCTestCase {
|
|||
// expected side effects as a result of .onAppear registration
|
||||
store.receive(.synchronizerStateChanged(.zero))
|
||||
store.receive(.updateLatestBlock) { state in
|
||||
state.latestBlock = "nil"
|
||||
state.latestBlock = ""
|
||||
}
|
||||
|
||||
// long-living (cancelable) effects need to be properly canceled.
|
||||
|
@ -54,9 +54,8 @@ class BalanceBreakdownTests: XCTestCase {
|
|||
}
|
||||
await store.receive(.shieldFundsSuccess) { state in
|
||||
state.shieldingFunds = false
|
||||
state.alert = AlertState.shieldFundsSuccess()
|
||||
}
|
||||
|
||||
await store.receive(.alert(.balanceBreakdown(.shieldFundsSuccess)))
|
||||
|
||||
// long-living (cancelable) effects need to be properly canceled.
|
||||
// the .onDisappear action cancels the observer of the synchronizer status change.
|
||||
|
@ -80,16 +79,9 @@ class BalanceBreakdownTests: XCTestCase {
|
|||
}
|
||||
await store.receive(.shieldFundsFailure(ZcashError.synchronizerNotPrepared)) { state in
|
||||
state.shieldingFunds = false
|
||||
state.alert = AlertState.shieldFundsFailure(ZcashError.synchronizerNotPrepared)
|
||||
}
|
||||
|
||||
await store.receive(
|
||||
.alert(
|
||||
.balanceBreakdown(
|
||||
.shieldFundsFailure(ZcashError.synchronizerNotPrepared)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
// long-living (cancelable) effects need to be properly canceled.
|
||||
// the .onDisappear action cancels the observer of the synchronizer status change.
|
||||
await store.send(.onDisappear)
|
||||
|
|
|
@ -9,6 +9,7 @@ import Combine
|
|||
import XCTest
|
||||
import ComposableArchitecture
|
||||
import Utils
|
||||
import Generated
|
||||
@testable import secant_testnet
|
||||
@testable import ZcashLightClientKit
|
||||
|
||||
|
@ -106,14 +107,8 @@ class HomeTests: XCTestCase {
|
|||
state.synchronizerStatusSnapshot = errorSnapshot
|
||||
}
|
||||
|
||||
store.receive(.showSynchronizerErrorAlert(testError))
|
||||
|
||||
store.receive(
|
||||
.alert(
|
||||
.home(
|
||||
.syncFailed(ZcashError.synchronizerNotPrepared, "Dismiss")
|
||||
)
|
||||
)
|
||||
)
|
||||
store.receive(.showSynchronizerErrorAlert(testError)) { state in
|
||||
state.alert = AlertState.syncFailed(ZcashError.synchronizerNotPrepared, L10n.Home.SyncFailed.dismiss)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -287,9 +287,9 @@ class ImportWalletTests: XCTestCase {
|
|||
store.dependencies.mnemonic = .noOp
|
||||
store.dependencies.walletStorage = .noOp
|
||||
|
||||
store.send(.restoreWallet)
|
||||
|
||||
store.receive(.alert(.importWallet(.succeed)))
|
||||
store.send(.restoreWallet) { state in
|
||||
state.alert = AlertState.succeed()
|
||||
}
|
||||
|
||||
store.receive(.initializeSDK)
|
||||
}
|
||||
|
|
|
@ -174,14 +174,7 @@ class AppInitializationTests: XCTestCase {
|
|||
|
||||
await store.receive(.initialization(.respondToWalletInitializationState(.keysMissing))) { state in
|
||||
state.appInitializationState = .keysMissing
|
||||
}
|
||||
|
||||
await store.receive(.alert(.root(.walletStateFailed(.keysMissing)))) { state in
|
||||
state.uniAlert = AlertState(
|
||||
title: TextState("Wallet initialisation failed."),
|
||||
message: TextState("App initialisation state: keysMissing."),
|
||||
dismissButton: .default(TextState("Ok"), action: .send(.dismissAlert))
|
||||
)
|
||||
state.alert = AlertState.walletStateFailed(.keysMissing)
|
||||
}
|
||||
|
||||
await store.finish()
|
||||
|
|
|
@ -118,14 +118,7 @@ class RootTests: XCTestCase {
|
|||
|
||||
store.send(.initialization(.respondToWalletInitializationState(.keysMissing))) { state in
|
||||
state.appInitializationState = .keysMissing
|
||||
}
|
||||
|
||||
store.receive(.alert(.root(.walletStateFailed(.keysMissing)))) { state in
|
||||
state.uniAlert = AlertState(
|
||||
title: TextState("Wallet initialisation failed."),
|
||||
message: TextState("App initialisation state: keysMissing."),
|
||||
dismissButton: .default(TextState("Ok"), action: .send(.dismissAlert))
|
||||
)
|
||||
state.alert = AlertState.walletStateFailed(.keysMissing)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -150,24 +143,11 @@ class RootTests: XCTestCase {
|
|||
store.receive(.initialization(.checkBackupPhraseValidation)) { state in
|
||||
// failed is expected because environment is throwing errors
|
||||
state.appInitializationState = .failed
|
||||
state.alert = AlertState.cantLoadSeedPhrase()
|
||||
}
|
||||
|
||||
store.receive(.initialization(.initializationFailed(zcashError)))
|
||||
|
||||
store.receive(.alert(.root(.cantLoadSeedPhrase))) { state in
|
||||
state.uniAlert = AlertState(
|
||||
title: TextState("Wallet initialisation failed."),
|
||||
message: TextState("Can't load seed phrase from local storage."),
|
||||
dismissButton: .default(TextState("Ok"), action: .send(.dismissAlert))
|
||||
)
|
||||
}
|
||||
|
||||
store.receive(.alert(.root(.initializationFailed(zcashError)))) { state in
|
||||
state.uniAlert = AlertState(
|
||||
title: TextState("Failed to initialize the SDK"),
|
||||
message: TextState("Error: \(zcashError.message) (code: \(zcashError.code.rawValue))"),
|
||||
dismissButton: .default(TextState("Ok"), action: .send(.dismissAlert))
|
||||
)
|
||||
store.receive(.initialization(.initializationFailed(zcashError))) { state in
|
||||
state.alert = AlertState.initializationFailed(zcashError)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,24 +170,11 @@ class RootTests: XCTestCase {
|
|||
store.receive(.initialization(.checkBackupPhraseValidation)) { state in
|
||||
// failed is expected because environment is throwing errors
|
||||
state.appInitializationState = .failed
|
||||
state.alert = AlertState.cantLoadSeedPhrase()
|
||||
}
|
||||
|
||||
store.receive(.initialization(.initializationFailed(zcashError)))
|
||||
|
||||
store.receive(.alert(.root(.cantLoadSeedPhrase))) { state in
|
||||
state.uniAlert = AlertState(
|
||||
title: TextState("Wallet initialisation failed."),
|
||||
message: TextState("Can't load seed phrase from local storage."),
|
||||
dismissButton: .default(TextState("Ok"), action: .send(.dismissAlert))
|
||||
)
|
||||
}
|
||||
|
||||
store.receive(.alert(.root(.initializationFailed(zcashError)))) { state in
|
||||
state.uniAlert = AlertState(
|
||||
title: TextState("Failed to initialize the SDK"),
|
||||
message: TextState("Error: \(zcashError.message) (code: \(zcashError.code.rawValue))"),
|
||||
dismissButton: .default(TextState("Ok"), action: .send(.dismissAlert))
|
||||
)
|
||||
store.receive(.initialization(.initializationFailed(zcashError))) { state in
|
||||
state.alert = AlertState.initializationFailed(zcashError)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue