phrase 1: - project restructured - swifgen rewired - files renamed [180] Code inconsistency phase 2: - stores and views refactored phase 3: - models, dependencies, utils and UI components [180] Code inconsistency - tests fixed
|
@ -1,12 +0,0 @@
|
|||
//
|
||||
// AppError.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Francisco Gindre on 9/3/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum AppError: Error, Equatable {
|
||||
case failedToInitialize
|
||||
}
|
|
@ -116,72 +116,3 @@ struct DatabaseFiles {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DatabaseFilesInteractor {
|
||||
let documentsDirectory: () throws -> URL
|
||||
let cacheDbURLFor: (ZcashNetwork) throws -> URL
|
||||
let dataDbURLFor: (ZcashNetwork) throws -> URL
|
||||
let outputParamsURLFor: (ZcashNetwork) throws -> URL
|
||||
let pendingDbURLFor: (ZcashNetwork) throws -> URL
|
||||
let spendParamsURLFor: (ZcashNetwork) throws -> URL
|
||||
let areDbFilesPresentFor: (ZcashNetwork) throws -> Bool
|
||||
let nukeDbFilesFor: (ZcashNetwork) throws -> Void
|
||||
}
|
||||
|
||||
extension DatabaseFilesInteractor {
|
||||
static func live(databaseFiles: DatabaseFiles = DatabaseFiles(fileManager: .live)) -> Self {
|
||||
Self(
|
||||
documentsDirectory: {
|
||||
try databaseFiles.documentsDirectory()
|
||||
},
|
||||
cacheDbURLFor: { network in
|
||||
try databaseFiles.cacheDbURL(for: network)
|
||||
},
|
||||
dataDbURLFor: { network in
|
||||
try databaseFiles.dataDbURL(for: network)
|
||||
},
|
||||
outputParamsURLFor: { network in
|
||||
try databaseFiles.outputParamsURL(for: network)
|
||||
},
|
||||
pendingDbURLFor: { network in
|
||||
try databaseFiles.pendingDbURL(for: network)
|
||||
},
|
||||
spendParamsURLFor: { network in
|
||||
try databaseFiles.spendParamsURL(for: network)
|
||||
},
|
||||
areDbFilesPresentFor: { network in
|
||||
try databaseFiles.areDbFilesPresent(for: network)
|
||||
},
|
||||
nukeDbFilesFor: { network in
|
||||
try databaseFiles.nukeDbFiles(for: network)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
static var throwing = DatabaseFilesInteractor(
|
||||
documentsDirectory: {
|
||||
throw DatabaseFiles.DatabaseFilesError.getDocumentsURL
|
||||
},
|
||||
cacheDbURLFor: { _ in
|
||||
throw DatabaseFiles.DatabaseFilesError.getCacheURL
|
||||
},
|
||||
dataDbURLFor: { _ in
|
||||
throw DatabaseFiles.DatabaseFilesError.getDataURL
|
||||
},
|
||||
outputParamsURLFor: { _ in
|
||||
throw DatabaseFiles.DatabaseFilesError.getOutputParamsURL
|
||||
},
|
||||
pendingDbURLFor: { _ in
|
||||
throw DatabaseFiles.DatabaseFilesError.getPendingURL
|
||||
},
|
||||
spendParamsURLFor: { _ in
|
||||
throw DatabaseFiles.DatabaseFilesError.getSpendParamsURL
|
||||
},
|
||||
areDbFilesPresentFor: { _ in
|
||||
throw DatabaseFiles.DatabaseFilesError.filesPresentCheck
|
||||
},
|
||||
nukeDbFilesFor: { _ in
|
||||
throw DatabaseFiles.DatabaseFilesError.nukeFiles
|
||||
}
|
||||
)
|
||||
}
|
|
@ -1,6 +1,12 @@
|
|||
import ComposableArchitecture
|
||||
import ZcashLightClientKit
|
||||
|
||||
typealias AppReducer = Reducer<AppState, AppAction, AppEnvironment>
|
||||
typealias AppStore = Store<AppState, AppAction>
|
||||
typealias AppViewStore = ViewStore<AppState, AppAction>
|
||||
|
||||
// MARK: - State
|
||||
|
||||
struct AppState: Equatable {
|
||||
enum Route: Equatable {
|
||||
case welcome
|
||||
|
@ -14,8 +20,8 @@ struct AppState: Equatable {
|
|||
|
||||
var appInitializationState: InitializationState = .uninitialized
|
||||
var homeState: HomeState
|
||||
var onboardingState: OnboardingState
|
||||
var phraseValidationState: RecoveryPhraseValidationState
|
||||
var onboardingState: OnboardingFlowState
|
||||
var phraseValidationState: RecoveryPhraseValidationFlowState
|
||||
var phraseDisplayState: RecoveryPhraseDisplayState
|
||||
var route: Route = .welcome
|
||||
var sandboxState: SandboxState
|
||||
|
@ -23,6 +29,8 @@ struct AppState: Equatable {
|
|||
var welcomeState: WelcomeState
|
||||
}
|
||||
|
||||
// MARK: - Action
|
||||
|
||||
enum AppAction: Equatable {
|
||||
case appDelegate(AppDelegateAction)
|
||||
case checkBackupPhraseValidation
|
||||
|
@ -31,54 +39,54 @@ enum AppAction: Equatable {
|
|||
case home(HomeAction)
|
||||
case initializeSDK
|
||||
case nukeWallet
|
||||
case onboarding(OnboardingAction)
|
||||
case onboarding(OnboardingFlowAction)
|
||||
case phraseDisplay(RecoveryPhraseDisplayAction)
|
||||
case phraseValidation(RecoveryPhraseValidationAction)
|
||||
case phraseValidation(RecoveryPhraseValidationFlowAction)
|
||||
case respondToWalletInitializationState(InitializationState)
|
||||
case sandbox(SandboxAction)
|
||||
case updateRoute(AppState.Route)
|
||||
case welcome(WelcomeAction)
|
||||
}
|
||||
|
||||
// MARK: - Environment
|
||||
|
||||
struct AppEnvironment {
|
||||
let wrappedSDKSynchronizer: WrappedSDKSynchronizer
|
||||
let databaseFiles: DatabaseFilesInteractor
|
||||
let mnemonicSeedPhraseProvider: MnemonicSeedPhraseProvider
|
||||
let SDKSynchronizer: WrappedSDKSynchronizer
|
||||
let databaseFiles: WrappedDatabaseFiles
|
||||
let mnemonicSeedPhraseProvider: WrappedMnemonic
|
||||
let scheduler: AnySchedulerOf<DispatchQueue>
|
||||
let walletStorage: WalletStorageInteractor
|
||||
let wrappedDerivationTool: WrappedDerivationTool
|
||||
let walletStorage: WrappedWalletStorage
|
||||
let derivationTool: WrappedDerivationTool
|
||||
let zcashSDKEnvironment: ZCashSDKEnvironment
|
||||
}
|
||||
|
||||
extension AppEnvironment {
|
||||
static let live = AppEnvironment(
|
||||
wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer(),
|
||||
SDKSynchronizer: LiveWrappedSDKSynchronizer(),
|
||||
databaseFiles: .live(),
|
||||
mnemonicSeedPhraseProvider: .live,
|
||||
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
walletStorage: .live(),
|
||||
wrappedDerivationTool: .live(),
|
||||
derivationTool: .live(),
|
||||
zcashSDKEnvironment: .mainnet
|
||||
)
|
||||
|
||||
static let mock = AppEnvironment(
|
||||
wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer(),
|
||||
SDKSynchronizer: LiveWrappedSDKSynchronizer(),
|
||||
databaseFiles: .live(),
|
||||
mnemonicSeedPhraseProvider: .mock,
|
||||
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
walletStorage: .live(),
|
||||
wrappedDerivationTool: .live(derivationTool: DerivationTool(networkType: .mainnet)),
|
||||
derivationTool: .live(derivationTool: DerivationTool(networkType: .mainnet)),
|
||||
zcashSDKEnvironment: .mainnet
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - AppReducer
|
||||
|
||||
private struct ListenerId: Hashable {}
|
||||
|
||||
typealias AppReducer = Reducer<AppState, AppAction, AppEnvironment>
|
||||
// MARK: - Reducer
|
||||
|
||||
extension AppReducer {
|
||||
private struct ListenerId: Hashable {}
|
||||
|
||||
static let `default` = AppReducer.combine(
|
||||
[
|
||||
appReducer,
|
||||
|
@ -153,8 +161,8 @@ extension AppReducer {
|
|||
birthday: birthday,
|
||||
with: environment
|
||||
)
|
||||
try environment.wrappedSDKSynchronizer.prepareWith(initializer: initializer)
|
||||
try environment.wrappedSDKSynchronizer.start()
|
||||
try environment.SDKSynchronizer.prepareWith(initializer: initializer)
|
||||
try environment.SDKSynchronizer.start()
|
||||
} catch {
|
||||
state.appInitializationState = .failed
|
||||
// TODO: error we need to handle, issue #221 (https://github.com/zcash/secant-ios-wallet/issues/221)
|
||||
|
@ -176,7 +184,7 @@ extension AppReducer {
|
|||
|
||||
let recoveryPhrase = RecoveryPhrase(words: phraseWords)
|
||||
state.phraseDisplayState.phrase = recoveryPhrase
|
||||
state.phraseValidationState = RecoveryPhraseValidationState.random(phrase: recoveryPhrase)
|
||||
state.phraseValidationState = RecoveryPhraseValidationFlowState.random(phrase: recoveryPhrase)
|
||||
landingRoute = .phraseDisplay
|
||||
} catch {
|
||||
// TODO: - merge with issue 201 (https://github.com/zcash/secant-ios-wallet/issues/201) and its Error States
|
||||
|
@ -203,7 +211,7 @@ extension AppReducer {
|
|||
let randomPhraseWords = try environment.mnemonicSeedPhraseProvider.asWords(randomPhrase)
|
||||
let recoveryPhrase = RecoveryPhrase(words: randomPhraseWords)
|
||||
state.phraseDisplayState.phrase = recoveryPhrase
|
||||
state.phraseValidationState = RecoveryPhraseValidationState.random(phrase: recoveryPhrase)
|
||||
state.phraseValidationState = RecoveryPhraseValidationFlowState.random(phrase: recoveryPhrase)
|
||||
|
||||
return .concatenate(
|
||||
Effect(value: .initializeSDK),
|
||||
|
@ -294,17 +302,17 @@ extension AppReducer {
|
|||
mnemonicSeedPhraseProvider: environment.mnemonicSeedPhraseProvider,
|
||||
scheduler: environment.scheduler,
|
||||
walletStorage: environment.walletStorage,
|
||||
wrappedDerivationTool: environment.wrappedDerivationTool,
|
||||
wrappedSDKSynchronizer: environment.wrappedSDKSynchronizer
|
||||
derivationTool: environment.derivationTool,
|
||||
SDKSynchronizer: environment.SDKSynchronizer
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
private static let onboardingReducer: AppReducer = OnboardingReducer.default.pullback(
|
||||
private static let onboardingReducer: AppReducer = OnboardingFlowReducer.default.pullback(
|
||||
state: \AppState.onboardingState,
|
||||
action: /AppAction.onboarding,
|
||||
environment: { environment in
|
||||
OnboardingEnvironment(
|
||||
OnboardingFlowEnvironment(
|
||||
mnemonicSeedPhraseProvider: environment.mnemonicSeedPhraseProvider,
|
||||
walletStorage: environment.walletStorage,
|
||||
zcashSDKEnvironment: environment.zcashSDKEnvironment
|
||||
|
@ -312,33 +320,31 @@ extension AppReducer {
|
|||
}
|
||||
)
|
||||
|
||||
private static let phraseValidationReducer: AppReducer = RecoveryPhraseValidationReducer.default.pullback(
|
||||
private static let phraseValidationReducer: AppReducer = RecoveryPhraseValidationFlowReducer.default.pullback(
|
||||
state: \AppState.phraseValidationState,
|
||||
action: /AppAction.phraseValidation,
|
||||
environment: { _ in BackupPhraseEnvironment.demo }
|
||||
environment: { _ in RecoveryPhraseValidationFlowEnvironment.demo }
|
||||
)
|
||||
|
||||
private static let phraseDisplayReducer: AppReducer = RecoveryPhraseDisplayReducer.default.pullback(
|
||||
state: \AppState.phraseDisplayState,
|
||||
action: /AppAction.phraseDisplay,
|
||||
environment: { _ in BackupPhraseEnvironment.demo }
|
||||
environment: { _ in RecoveryPhraseDisplayEnvironment.demo }
|
||||
)
|
||||
|
||||
private static let sandboxReducer: AppReducer = SandboxReducer.default.pullback(
|
||||
state: \AppState.sandboxState,
|
||||
action: /AppAction.sandbox,
|
||||
environment: { _ in }
|
||||
environment: { _ in SandboxEnvironment() }
|
||||
)
|
||||
|
||||
private static let welcomeReducer: AppReducer = WelcomeReducer.default.pullback(
|
||||
state: \AppState.welcomeState,
|
||||
action: /AppAction.welcome,
|
||||
environment: { _ in }
|
||||
environment: { _ in WelcomeEnvironment() }
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - AppReducer Helper Functions
|
||||
|
||||
extension AppReducer {
|
||||
static func walletInitializationState(_ environment: AppEnvironment) -> InitializationState {
|
||||
var keysPresent = false
|
||||
|
@ -386,7 +392,7 @@ extension AppReducer {
|
|||
) throws -> Initializer {
|
||||
do {
|
||||
let seedBytes = try environment.mnemonicSeedPhraseProvider.toSeed(seedPhrase)
|
||||
let viewingKeys = try environment.wrappedDerivationTool.deriveUnifiedViewingKeysFromSeed(seedBytes, 1)
|
||||
let viewingKeys = try environment.derivationTool.deriveUnifiedViewingKeysFromSeed(seedBytes, 1)
|
||||
|
||||
let network = environment.zcashSDKEnvironment.network
|
||||
|
||||
|
@ -409,9 +415,24 @@ extension AppReducer {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - AppStore
|
||||
// MARK: Placeholders
|
||||
|
||||
typealias AppStore = Store<AppState, AppAction>
|
||||
extension AppState {
|
||||
static var placeholder: Self {
|
||||
.init(
|
||||
homeState: .placeholder,
|
||||
onboardingState: .init(
|
||||
importWalletState: .placeholder
|
||||
),
|
||||
phraseValidationState: RecoveryPhraseValidationFlowState.placeholder,
|
||||
phraseDisplayState: RecoveryPhraseDisplayState(
|
||||
phrase: .placeholder
|
||||
),
|
||||
sandboxState: .placeholder,
|
||||
welcomeState: .placeholder
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension AppStore {
|
||||
static var placeholder: AppStore {
|
||||
|
@ -422,29 +443,3 @@ extension AppStore {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - AppViewStore
|
||||
|
||||
typealias AppViewStore = ViewStore<AppState, AppAction>
|
||||
|
||||
extension AppViewStore {
|
||||
}
|
||||
|
||||
// MARK: PlaceHolders
|
||||
|
||||
extension AppState {
|
||||
static var placeholder: Self {
|
||||
.init(
|
||||
homeState: .placeholder,
|
||||
onboardingState: .init(
|
||||
importWalletState: .placeholder
|
||||
),
|
||||
phraseValidationState: RecoveryPhraseValidationState.placeholder,
|
||||
phraseDisplayState: RecoveryPhraseDisplayState(
|
||||
phrase: .placeholder
|
||||
),
|
||||
sandboxState: .placeholder,
|
||||
welcomeState: .placeholder
|
||||
)
|
||||
}
|
||||
}
|
|
@ -43,13 +43,13 @@ struct AppView: View {
|
|||
|
||||
case .startup:
|
||||
ZStack(alignment: .topTrailing) {
|
||||
StartupView(sendAction: viewStore.send)
|
||||
DebugView(sendAction: viewStore.send)
|
||||
.transition(.opacity)
|
||||
}
|
||||
|
||||
case .phraseValidation:
|
||||
NavigationView {
|
||||
RecoveryPhraseTestPreambleView(
|
||||
RecoveryPhraseValidationFlowView(
|
||||
store: store.scope(
|
||||
state: \.phraseValidationState,
|
||||
action: AppAction.phraseValidation
|
||||
|
@ -80,7 +80,8 @@ struct AppView: View {
|
|||
}
|
||||
}
|
||||
|
||||
private struct StartupView: View {
|
||||
private extension AppView {
|
||||
struct DebugView: View {
|
||||
var sendAction: (AppAction) -> Void
|
||||
|
||||
var body: some View {
|
||||
|
@ -110,6 +111,9 @@ private struct StartupView: View {
|
|||
.navigationBarTitle("Startup")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct AppView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
|
@ -2,6 +2,12 @@ import ComposableArchitecture
|
|||
import SwiftUI
|
||||
import ZcashLightClientKit
|
||||
|
||||
typealias HomeReducer = Reducer<HomeState, HomeAction, HomeEnvironment>
|
||||
typealias HomeStore = Store<HomeState, HomeAction>
|
||||
typealias HomeViewStore = ViewStore<HomeState, HomeAction>
|
||||
|
||||
// MARK: State
|
||||
|
||||
struct HomeState: Equatable {
|
||||
enum Route: Equatable {
|
||||
case profile
|
||||
|
@ -15,24 +21,26 @@ struct HomeState: Equatable {
|
|||
var drawerOverlay: DrawerOverlay
|
||||
var profileState: ProfileState
|
||||
var requestState: RequestState
|
||||
var sendState: SendState
|
||||
var sendState: SendFlowState
|
||||
var scanState: ScanState
|
||||
var synchronizerStatus: String
|
||||
var totalBalance: Int64
|
||||
var transactionHistoryState: TransactionHistoryState
|
||||
var transactionHistoryState: TransactionHistoryFlowState
|
||||
var verifiedBalance: Int64
|
||||
}
|
||||
|
||||
// MARK: Action
|
||||
|
||||
enum HomeAction: Equatable {
|
||||
case debugMenuStartup
|
||||
case onAppear
|
||||
case onDisappear
|
||||
case profile(ProfileAction)
|
||||
case request(RequestAction)
|
||||
case send(SendAction)
|
||||
case send(SendFlowAction)
|
||||
case scan(ScanAction)
|
||||
case synchronizerStateChanged(WrappedSDKSynchronizerState)
|
||||
case transactionHistory(TransactionHistoryAction)
|
||||
case transactionHistory(TransactionHistoryFlowAction)
|
||||
case updateBalance(Balance)
|
||||
case updateDrawer(DrawerOverlay)
|
||||
case updateRoute(HomeState.Route?)
|
||||
|
@ -40,21 +48,21 @@ enum HomeAction: Equatable {
|
|||
case updateTransactions([TransactionState])
|
||||
}
|
||||
|
||||
// MARK: Environment
|
||||
|
||||
struct HomeEnvironment {
|
||||
let mnemonicSeedPhraseProvider: MnemonicSeedPhraseProvider
|
||||
let mnemonicSeedPhraseProvider: WrappedMnemonic
|
||||
let scheduler: AnySchedulerOf<DispatchQueue>
|
||||
let walletStorage: WalletStorageInteractor
|
||||
let wrappedDerivationTool: WrappedDerivationTool
|
||||
let wrappedSDKSynchronizer: WrappedSDKSynchronizer
|
||||
let walletStorage: WrappedWalletStorage
|
||||
let derivationTool: WrappedDerivationTool
|
||||
let SDKSynchronizer: WrappedSDKSynchronizer
|
||||
}
|
||||
|
||||
// MARK: - HomeReducer
|
||||
|
||||
private struct ListenerId: Hashable {}
|
||||
|
||||
typealias HomeReducer = Reducer<HomeState, HomeAction, HomeEnvironment>
|
||||
// MARK: - Reducer
|
||||
|
||||
extension HomeReducer {
|
||||
private struct ListenerId: Hashable {}
|
||||
|
||||
static let `default` = HomeReducer.combine(
|
||||
[
|
||||
homeReducer,
|
||||
|
@ -67,7 +75,7 @@ extension HomeReducer {
|
|||
private static let homeReducer = HomeReducer { state, action, environment in
|
||||
switch action {
|
||||
case .onAppear:
|
||||
return environment.wrappedSDKSynchronizer.stateChanged
|
||||
return environment.SDKSynchronizer.stateChanged
|
||||
.map(HomeAction.synchronizerStateChanged)
|
||||
.eraseToEffect()
|
||||
.cancellable(id: ListenerId(), cancelInFlight: true)
|
||||
|
@ -77,12 +85,12 @@ extension HomeReducer {
|
|||
|
||||
case .synchronizerStateChanged(.synced):
|
||||
return .merge(
|
||||
environment.wrappedSDKSynchronizer.getAllClearedTransactions()
|
||||
environment.SDKSynchronizer.getAllClearedTransactions()
|
||||
.receive(on: environment.scheduler)
|
||||
.map(HomeAction.updateTransactions)
|
||||
.eraseToEffect(),
|
||||
|
||||
environment.wrappedSDKSynchronizer.getShieldedBalance()
|
||||
environment.SDKSynchronizer.getShieldedBalance()
|
||||
.receive(on: environment.scheduler)
|
||||
.map({ Balance(verified: $0.verified, total: $0.total) })
|
||||
.map(HomeAction.updateBalance)
|
||||
|
@ -108,7 +116,7 @@ extension HomeReducer {
|
|||
return .none
|
||||
|
||||
case .updateSynchronizerStatus:
|
||||
state.synchronizerStatus = environment.wrappedSDKSynchronizer.status()
|
||||
state.synchronizerStatus = environment.SDKSynchronizer.status()
|
||||
return .none
|
||||
|
||||
case .updateRoute(let route):
|
||||
|
@ -144,60 +152,36 @@ extension HomeReducer {
|
|||
}
|
||||
}
|
||||
|
||||
private static let historyReducer: HomeReducer = TransactionHistoryReducer.default.pullback(
|
||||
private static let historyReducer: HomeReducer = TransactionHistoryFlowReducer.default.pullback(
|
||||
state: \HomeState.transactionHistoryState,
|
||||
action: /HomeAction.transactionHistory,
|
||||
environment: { environment in
|
||||
TransactionHistoryEnvironment(
|
||||
TransactionHistoryFlowEnvironment(
|
||||
scheduler: environment.scheduler,
|
||||
wrappedSDKSynchronizer: environment.wrappedSDKSynchronizer
|
||||
SDKSynchronizer: environment.SDKSynchronizer
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
private static let sendReducer: HomeReducer = SendReducer.default.pullback(
|
||||
private static let sendReducer: HomeReducer = SendFlowReducer.default.pullback(
|
||||
state: \HomeState.sendState,
|
||||
action: /HomeAction.send,
|
||||
environment: { environment in
|
||||
SendEnvironment(
|
||||
SendFlowEnvironment(
|
||||
mnemonicSeedPhraseProvider: environment.mnemonicSeedPhraseProvider,
|
||||
scheduler: environment.scheduler,
|
||||
walletStorage: environment.walletStorage,
|
||||
wrappedDerivationTool: environment.wrappedDerivationTool,
|
||||
wrappedSDKSynchronizer: environment.wrappedSDKSynchronizer
|
||||
derivationTool: environment.derivationTool,
|
||||
SDKSynchronizer: environment.SDKSynchronizer
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - HomeViewStore
|
||||
|
||||
typealias HomeViewStore = ViewStore<HomeState, HomeAction>
|
||||
|
||||
extension HomeViewStore {
|
||||
func bindingForRoute(_ route: HomeState.Route) -> Binding<Bool> {
|
||||
self.binding(
|
||||
get: { $0.route == route },
|
||||
send: { isActive in
|
||||
return .updateRoute(isActive ? route : nil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func bindingForDrawer() -> Binding<DrawerOverlay> {
|
||||
self.binding(
|
||||
get: { $0.drawerOverlay },
|
||||
send: { .updateDrawer($0) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - HomeStore
|
||||
|
||||
typealias HomeStore = Store<HomeState, HomeAction>
|
||||
// MARK: - Store
|
||||
|
||||
extension HomeStore {
|
||||
func historyStore() -> TransactionHistoryStore {
|
||||
func historyStore() -> TransactionHistoryFlowStore {
|
||||
self.scope(
|
||||
state: \.transactionHistoryState,
|
||||
action: HomeAction.transactionHistory
|
||||
|
@ -218,7 +202,7 @@ extension HomeStore {
|
|||
)
|
||||
}
|
||||
|
||||
func sendStore() -> SendStore {
|
||||
func sendStore() -> SendFlowStore {
|
||||
self.scope(
|
||||
state: \.sendState,
|
||||
action: HomeAction.send
|
||||
|
@ -233,7 +217,27 @@ extension HomeStore {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: PlaceHolders
|
||||
// MARK: - ViewStore
|
||||
|
||||
extension HomeViewStore {
|
||||
func bindingForRoute(_ route: HomeState.Route) -> Binding<Bool> {
|
||||
self.binding(
|
||||
get: { $0.route == route },
|
||||
send: { isActive in
|
||||
return .updateRoute(isActive ? route : nil)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
func bindingForDrawer() -> Binding<DrawerOverlay> {
|
||||
self.binding(
|
||||
get: { $0.drawerOverlay },
|
||||
send: { .updateDrawer($0) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Placeholders
|
||||
|
||||
extension HomeState {
|
||||
static var placeholder: Self {
|
||||
|
@ -251,38 +255,18 @@ extension HomeState {
|
|||
}
|
||||
}
|
||||
|
||||
extension SDKSynchronizer {
|
||||
static func textFor(state: SyncStatus) -> String {
|
||||
switch state {
|
||||
case .downloading(let progress):
|
||||
return "Downloading \(progress.progressHeight)/\(progress.targetHeight)"
|
||||
|
||||
case .enhancing(let enhanceProgress):
|
||||
return "Enhancing tx \(enhanceProgress.enhancedTransactions) of \(enhanceProgress.totalTransactions)"
|
||||
|
||||
case .fetching:
|
||||
return "fetching UTXOs"
|
||||
|
||||
case .scanning(let scanProgress):
|
||||
return "Scanning: \(scanProgress.progressHeight)/\(scanProgress.targetHeight)"
|
||||
|
||||
case .disconnected:
|
||||
return "disconnected 💔"
|
||||
|
||||
case .stopped:
|
||||
return "Stopped 🚫"
|
||||
|
||||
case .synced:
|
||||
return "Synced 😎"
|
||||
|
||||
case .unprepared:
|
||||
return "Unprepared 😅"
|
||||
|
||||
case .validating:
|
||||
return "Validating"
|
||||
|
||||
case .error(let err):
|
||||
return "Error: \(err.localizedDescription)"
|
||||
}
|
||||
extension HomeStore {
|
||||
static var placeholder: HomeStore {
|
||||
HomeStore(
|
||||
initialState: .placeholder,
|
||||
reducer: .default.debug(),
|
||||
environment: HomeEnvironment(
|
||||
mnemonicSeedPhraseProvider: .live,
|
||||
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
walletStorage: .live(),
|
||||
derivationTool: .live(),
|
||||
SDKSynchronizer: LiveWrappedSDKSynchronizer()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ struct HomeView: View {
|
|||
if proxy.size.height > 0 {
|
||||
Drawer(overlay: viewStore.bindingForDrawer(), maxHeight: proxy.size.height) {
|
||||
VStack {
|
||||
TransactionHistoryView(store: store.historyStore())
|
||||
TransactionHistoryFlowView(store: store.historyStore())
|
||||
.padding(.top, 10)
|
||||
|
||||
Spacer()
|
||||
|
@ -93,7 +93,7 @@ extension HomeView {
|
|||
.navigationLink(
|
||||
isActive: viewStore.bindingForRoute(.send),
|
||||
destination: {
|
||||
SendView(store: store.sendStore())
|
||||
SendFlowView(store: store.sendStore())
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -126,22 +126,6 @@ extension HomeView {
|
|||
|
||||
// MARK: - Previews
|
||||
|
||||
extension HomeStore {
|
||||
static var placeholder: HomeStore {
|
||||
HomeStore(
|
||||
initialState: .placeholder,
|
||||
reducer: .default.debug(),
|
||||
environment: HomeEnvironment(
|
||||
mnemonicSeedPhraseProvider: .live,
|
||||
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
walletStorage: .live(),
|
||||
wrappedDerivationTool: .live(),
|
||||
wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct HomeView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NavigationView {
|
|
@ -8,13 +8,19 @@
|
|||
import ComposableArchitecture
|
||||
import ZcashLightClientKit
|
||||
|
||||
typealias ImportWalletReducer = Reducer<ImportWalletState, ImportWalletAction, ImportWalletEnvironment>
|
||||
typealias ImportWalletStore = Store<ImportWalletState, ImportWalletAction>
|
||||
typealias ImportWalletViewStore = ViewStore<ImportWalletState, ImportWalletAction>
|
||||
|
||||
// MARK: - State
|
||||
|
||||
struct ImportWalletState: Equatable {
|
||||
@BindableState var alert: AlertState<ImportWalletAction>?
|
||||
@BindableState var importedSeedPhrase: String = ""
|
||||
}
|
||||
|
||||
// MARK: - Action
|
||||
|
||||
enum ImportWalletAction: Equatable, BindableAction {
|
||||
case binding(BindingAction<ImportWalletState>)
|
||||
case dismissAlert
|
||||
|
@ -24,9 +30,11 @@ enum ImportWalletAction: Equatable, BindableAction {
|
|||
case successfullyRecovered
|
||||
}
|
||||
|
||||
// MARK: - Environment
|
||||
|
||||
struct ImportWalletEnvironment {
|
||||
let mnemonicSeedPhraseProvider: MnemonicSeedPhraseProvider
|
||||
let walletStorage: WalletStorageInteractor
|
||||
let mnemonicSeedPhraseProvider: WrappedMnemonic
|
||||
let walletStorage: WrappedWalletStorage
|
||||
let zcashSDKEnvironment: ZCashSDKEnvironment
|
||||
}
|
||||
|
||||
|
@ -44,7 +52,7 @@ extension ImportWalletEnvironment {
|
|||
)
|
||||
}
|
||||
|
||||
typealias ImportWalletReducer = Reducer<ImportWalletState, ImportWalletAction, ImportWalletEnvironment>
|
||||
// MARK: - Reducer
|
||||
|
||||
extension ImportWalletReducer {
|
||||
static let `default` = ImportWalletReducer { state, action, environment in
|
||||
|
@ -106,3 +114,19 @@ extension ImportWalletReducer {
|
|||
}
|
||||
.binding()
|
||||
}
|
||||
|
||||
// MARK: - Placeholders
|
||||
|
||||
extension ImportWalletState {
|
||||
static let placeholder = ImportWalletState(importedSeedPhrase: "")
|
||||
|
||||
static let live = ImportWalletState(importedSeedPhrase: "")
|
||||
}
|
||||
|
||||
extension ImportWalletStore {
|
||||
static let demo = Store(
|
||||
initialState: .placeholder,
|
||||
reducer: .default,
|
||||
environment: .demo
|
||||
)
|
||||
}
|
||||
|
|
|
@ -81,19 +81,7 @@ extension View {
|
|||
}
|
||||
}
|
||||
|
||||
extension ImportWalletStore {
|
||||
static let demo = Store(
|
||||
initialState: .placeholder,
|
||||
reducer: .default,
|
||||
environment: .demo
|
||||
)
|
||||
}
|
||||
|
||||
extension ImportWalletState {
|
||||
static let placeholder = ImportWalletState(importedSeedPhrase: "")
|
||||
|
||||
static let live = ImportWalletState(importedSeedPhrase: "")
|
||||
}
|
||||
// MARK: - Previews
|
||||
|
||||
struct ImportWalletView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
|
@ -1,105 +0,0 @@
|
|||
//
|
||||
// Onboarding.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Adam Stener on 10/12/21.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
|
||||
struct OnboardingView: View {
|
||||
let store: Store<OnboardingState, OnboardingAction>
|
||||
|
||||
var body: some View {
|
||||
WithViewStore(self.store) { viewStore in
|
||||
VStack(spacing: 50) {
|
||||
HStack(spacing: 50) {
|
||||
Button("Back") { viewStore.send(.back) }
|
||||
.disabled(viewStore.isInitialStep)
|
||||
|
||||
Spacer()
|
||||
Button("Next") { viewStore.send(.next) }
|
||||
|
||||
Button("Skip") { viewStore.send(.skip) }
|
||||
.disabled(viewStore.isFinalStep)
|
||||
}
|
||||
.frame(height: 100)
|
||||
.padding(.horizontal, 50)
|
||||
|
||||
Spacer()
|
||||
|
||||
Text(viewStore.currentStep.title)
|
||||
.frame(maxWidth: .infinity)
|
||||
.offset(y: viewStore.offset)
|
||||
.animation(.easeOut(duration: 0.4))
|
||||
|
||||
Spacer()
|
||||
|
||||
VStack {
|
||||
Text(viewStore.currentStep.description)
|
||||
|
||||
ProgressView(
|
||||
"Progress \(viewStore.progress)%",
|
||||
value: Double(viewStore.index + 1),
|
||||
total: Double(viewStore.steps.count)
|
||||
)
|
||||
.padding(.horizontal, 25)
|
||||
.padding(.vertical, 50)
|
||||
}
|
||||
.animation(.easeOut(duration: 0.2))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension OnboardingState {
|
||||
static let onboardingSteps = IdentifiedArray(
|
||||
uniqueElements: [
|
||||
Step(
|
||||
id: UUID(),
|
||||
title: "onboarding.step1.title",
|
||||
description: "onboarding.step1.description",
|
||||
background: Asset.Assets.Backgrounds.callout1.image,
|
||||
badge: .shield
|
||||
),
|
||||
Step(
|
||||
id: UUID(),
|
||||
title: "onboarding.step2.title",
|
||||
description: "onboarding.step2.description",
|
||||
background: Asset.Assets.Backgrounds.callout2.image,
|
||||
badge: .person
|
||||
),
|
||||
Step(
|
||||
id: UUID(),
|
||||
title: "onboarding.step3.title",
|
||||
description: "onboarding.step3.description",
|
||||
background: Asset.Assets.Backgrounds.callout3.image,
|
||||
badge: .list
|
||||
),
|
||||
Step(
|
||||
id: UUID(),
|
||||
title: "onboarding.step4.title",
|
||||
description: "onboarding.step4.description",
|
||||
background: Asset.Assets.Backgrounds.callout4.image,
|
||||
badge: .shield
|
||||
)
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
struct Onboarding_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
OnboardingView(
|
||||
store: Store(
|
||||
initialState: OnboardingState(
|
||||
importWalletState: .placeholder
|
||||
),
|
||||
reducer: .default,
|
||||
environment: (.demo)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,9 +9,13 @@ import Foundation
|
|||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
|
||||
typealias OnboardingViewStore = ViewStore<OnboardingState, OnboardingAction>
|
||||
typealias OnboardingFlowReducer = Reducer<OnboardingFlowState, OnboardingFlowAction, OnboardingFlowEnvironment>
|
||||
typealias OnboardingFlowStore = Store<OnboardingFlowState, OnboardingFlowAction>
|
||||
typealias OnboardingFlowViewStore = ViewStore<OnboardingFlowState, OnboardingFlowAction>
|
||||
|
||||
struct OnboardingState: Equatable {
|
||||
// MARK: - State
|
||||
|
||||
struct OnboardingFlowState: Equatable {
|
||||
enum Route: Equatable, CaseIterable {
|
||||
case createNewWallet
|
||||
case importExistingWallet
|
||||
|
@ -45,58 +49,86 @@ struct OnboardingState: Equatable {
|
|||
var importWalletState: ImportWalletState
|
||||
}
|
||||
|
||||
extension OnboardingViewStore {
|
||||
func bindingForRoute(_ route: OnboardingState.Route) -> Binding<Bool> {
|
||||
self.binding(
|
||||
get: { $0.route == route },
|
||||
send: { isActive in
|
||||
return .updateRoute(isActive ? route : nil)
|
||||
}
|
||||
extension OnboardingFlowState {
|
||||
static let onboardingSteps = IdentifiedArray(
|
||||
uniqueElements: [
|
||||
Step(
|
||||
id: UUID(),
|
||||
title: "onboarding.step1.title",
|
||||
description: "onboarding.step1.description",
|
||||
background: Asset.Assets.Backgrounds.callout1.image,
|
||||
badge: .shield
|
||||
),
|
||||
Step(
|
||||
id: UUID(),
|
||||
title: "onboarding.step2.title",
|
||||
description: "onboarding.step2.description",
|
||||
background: Asset.Assets.Backgrounds.callout2.image,
|
||||
badge: .person
|
||||
),
|
||||
Step(
|
||||
id: UUID(),
|
||||
title: "onboarding.step3.title",
|
||||
description: "onboarding.step3.description",
|
||||
background: Asset.Assets.Backgrounds.callout3.image,
|
||||
badge: .list
|
||||
),
|
||||
Step(
|
||||
id: UUID(),
|
||||
title: "onboarding.step4.title",
|
||||
description: "onboarding.step4.description",
|
||||
background: Asset.Assets.Backgrounds.callout4.image,
|
||||
badge: .shield
|
||||
)
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
enum OnboardingAction: Equatable {
|
||||
// MARK: - Action
|
||||
|
||||
enum OnboardingFlowAction: Equatable {
|
||||
case next
|
||||
case back
|
||||
case skip
|
||||
case updateRoute(OnboardingState.Route?)
|
||||
case updateRoute(OnboardingFlowState.Route?)
|
||||
case createNewWallet
|
||||
case importExistingWallet
|
||||
case importWallet(ImportWalletAction)
|
||||
}
|
||||
|
||||
struct OnboardingEnvironment {
|
||||
let mnemonicSeedPhraseProvider: MnemonicSeedPhraseProvider
|
||||
let walletStorage: WalletStorageInteractor
|
||||
// MARK: - Environment
|
||||
|
||||
struct OnboardingFlowEnvironment {
|
||||
let mnemonicSeedPhraseProvider: WrappedMnemonic
|
||||
let walletStorage: WrappedWalletStorage
|
||||
let zcashSDKEnvironment: ZCashSDKEnvironment
|
||||
}
|
||||
|
||||
extension OnboardingEnvironment {
|
||||
static let live = OnboardingEnvironment(
|
||||
extension OnboardingFlowEnvironment {
|
||||
static let live = OnboardingFlowEnvironment(
|
||||
mnemonicSeedPhraseProvider: .live,
|
||||
walletStorage: .live(),
|
||||
zcashSDKEnvironment: .mainnet
|
||||
)
|
||||
|
||||
static let demo = OnboardingEnvironment(
|
||||
static let demo = OnboardingFlowEnvironment(
|
||||
mnemonicSeedPhraseProvider: .mock,
|
||||
walletStorage: .live(),
|
||||
zcashSDKEnvironment: .testnet
|
||||
)
|
||||
}
|
||||
|
||||
typealias OnboardingReducer = Reducer<OnboardingState, OnboardingAction, OnboardingEnvironment>
|
||||
// MARK: - Reducer
|
||||
|
||||
extension OnboardingReducer {
|
||||
static let `default` = OnboardingReducer.combine(
|
||||
extension OnboardingFlowReducer {
|
||||
static let `default` = OnboardingFlowReducer.combine(
|
||||
[
|
||||
onboardingReducer,
|
||||
importWalletReducer
|
||||
]
|
||||
)
|
||||
|
||||
private static let onboardingReducer = OnboardingReducer { state, action, _ in
|
||||
private static let onboardingReducer = OnboardingFlowReducer { state, action, _ in
|
||||
switch action {
|
||||
case .back:
|
||||
guard state.index > 0 else { return .none }
|
||||
|
@ -136,9 +168,9 @@ extension OnboardingReducer {
|
|||
}
|
||||
}
|
||||
|
||||
private static let importWalletReducer: OnboardingReducer = ImportWalletReducer.default.pullback(
|
||||
state: \OnboardingState.importWalletState,
|
||||
action: /OnboardingAction.importWallet,
|
||||
private static let importWalletReducer: OnboardingFlowReducer = ImportWalletReducer.default.pullback(
|
||||
state: \OnboardingFlowState.importWalletState,
|
||||
action: /OnboardingFlowAction.importWallet,
|
||||
environment: { environment in
|
||||
ImportWalletEnvironment(
|
||||
mnemonicSeedPhraseProvider: environment.mnemonicSeedPhraseProvider,
|
||||
|
@ -148,3 +180,16 @@ extension OnboardingReducer {
|
|||
}
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - ViewStore
|
||||
|
||||
extension OnboardingFlowViewStore {
|
||||
func bindingForRoute(_ route: OnboardingFlowState.Route) -> Binding<Bool> {
|
||||
self.binding(
|
||||
get: { $0.route == route },
|
||||
send: { isActive in
|
||||
return .updateRoute(isActive ? route : nil)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ import SwiftUI
|
|||
import ComposableArchitecture
|
||||
|
||||
struct OnboardingScreen: View {
|
||||
let store: Store<OnboardingState, OnboardingAction>
|
||||
let store: Store<OnboardingFlowState, OnboardingFlowAction>
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { proxy in
|
||||
|
@ -46,14 +46,16 @@ struct OnboardingScreen: View {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct OnboardingScreen_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
OnboardingScreen(
|
||||
store: Store(
|
||||
initialState: OnboardingState(
|
||||
initialState: OnboardingFlowState(
|
||||
importWalletState: .placeholder
|
||||
),
|
||||
reducer: OnboardingReducer.default,
|
||||
reducer: OnboardingFlowReducer.default,
|
||||
environment: (.demo)
|
||||
)
|
||||
)
|
||||
|
@ -62,10 +64,10 @@ struct OnboardingScreen_Previews: PreviewProvider {
|
|||
|
||||
OnboardingScreen(
|
||||
store: Store(
|
||||
initialState: OnboardingState(
|
||||
initialState: OnboardingFlowState(
|
||||
importWalletState: .placeholder
|
||||
),
|
||||
reducer: OnboardingReducer.default,
|
||||
reducer: OnboardingFlowReducer.default,
|
||||
environment: (.demo)
|
||||
)
|
||||
)
|
||||
|
@ -74,10 +76,10 @@ struct OnboardingScreen_Previews: PreviewProvider {
|
|||
|
||||
OnboardingScreen(
|
||||
store: Store(
|
||||
initialState: OnboardingState(
|
||||
initialState: OnboardingFlowState(
|
||||
importWalletState: .placeholder
|
||||
),
|
||||
reducer: OnboardingReducer.default,
|
||||
reducer: OnboardingFlowReducer.default,
|
||||
environment: (.demo)
|
||||
)
|
||||
)
|
||||
|
@ -86,10 +88,10 @@ struct OnboardingScreen_Previews: PreviewProvider {
|
|||
|
||||
OnboardingScreen(
|
||||
store: Store(
|
||||
initialState: OnboardingState(
|
||||
initialState: OnboardingFlowState(
|
||||
importWalletState: .placeholder
|
||||
),
|
||||
reducer: OnboardingReducer.default,
|
||||
reducer: OnboardingFlowReducer.default,
|
||||
environment: (.demo)
|
||||
)
|
||||
)
|
||||
|
@ -98,10 +100,10 @@ struct OnboardingScreen_Previews: PreviewProvider {
|
|||
|
||||
OnboardingScreen(
|
||||
store: Store(
|
||||
initialState: OnboardingState(
|
||||
initialState: OnboardingFlowState(
|
||||
importWalletState: .placeholder
|
||||
),
|
||||
reducer: OnboardingReducer.default,
|
||||
reducer: OnboardingFlowReducer.default,
|
||||
environment: (.demo)
|
||||
)
|
||||
)
|
||||
|
@ -110,10 +112,10 @@ struct OnboardingScreen_Previews: PreviewProvider {
|
|||
|
||||
OnboardingScreen(
|
||||
store: Store(
|
||||
initialState: OnboardingState(
|
||||
initialState: OnboardingFlowState(
|
||||
importWalletState: .placeholder
|
||||
),
|
||||
reducer: OnboardingReducer.default,
|
||||
reducer: OnboardingFlowReducer.default,
|
||||
environment: (.demo)
|
||||
)
|
||||
)
|
|
@ -9,7 +9,7 @@ import SwiftUI
|
|||
import ComposableArchitecture
|
||||
|
||||
struct OnboardingContentView: View {
|
||||
let store: Store<OnboardingState, OnboardingAction>
|
||||
let store: Store<OnboardingFlowState, OnboardingFlowAction>
|
||||
let width: Double
|
||||
let height: Double
|
||||
|
||||
|
@ -127,11 +127,11 @@ extension OnboardingContentView {
|
|||
struct OnboardingContentView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let store = Store(
|
||||
initialState: OnboardingState(
|
||||
initialState: OnboardingFlowState(
|
||||
index: 0,
|
||||
importWalletState: .placeholder
|
||||
),
|
||||
reducer: OnboardingReducer.default,
|
||||
reducer: OnboardingFlowReducer.default,
|
||||
environment: (.demo)
|
||||
)
|
||||
|
||||
|
@ -146,8 +146,10 @@ struct OnboardingContentView_Previews: PreviewProvider {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
extension OnboardingContentView_Previews {
|
||||
static func example(_ store: Store<OnboardingState, OnboardingAction>) -> some View {
|
||||
static func example(_ store: Store<OnboardingFlowState, OnboardingFlowAction>) -> some View {
|
||||
GeometryReader { proxy in
|
||||
ZStack {
|
||||
OnboardingHeaderView(
|
|
@ -9,7 +9,7 @@ import SwiftUI
|
|||
import ComposableArchitecture
|
||||
|
||||
struct OnboardingFooterView: View {
|
||||
let store: Store<OnboardingState, OnboardingAction>
|
||||
let store: Store<OnboardingFlowState, OnboardingFlowAction>
|
||||
let animationDuration: CGFloat = 0.8
|
||||
|
||||
var body: some View {
|
||||
|
@ -58,7 +58,7 @@ struct OnboardingFooterView: View {
|
|||
ImportWalletView(
|
||||
store: store.scope(
|
||||
state: \.importWalletState,
|
||||
action: OnboardingAction.importWallet
|
||||
action: OnboardingFlowAction.importWallet
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -83,14 +83,16 @@ extension View {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct OnboardingFooterView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let store = Store<OnboardingState, OnboardingAction>(
|
||||
initialState: OnboardingState(
|
||||
let store = Store<OnboardingFlowState, OnboardingFlowAction>(
|
||||
initialState: OnboardingFlowState(
|
||||
index: 3,
|
||||
importWalletState: .placeholder
|
||||
),
|
||||
reducer: OnboardingReducer.default,
|
||||
reducer: OnboardingFlowReducer.default,
|
||||
environment: (.demo)
|
||||
)
|
||||
|
|
@ -60,14 +60,16 @@ struct OnboardingHeaderView: View {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct OnboardingHeaderView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let store = Store<OnboardingState, OnboardingAction>(
|
||||
initialState: OnboardingState(
|
||||
let store = Store<OnboardingFlowState, OnboardingFlowAction>(
|
||||
initialState: OnboardingFlowState(
|
||||
index: 0,
|
||||
importWalletState: .placeholder
|
||||
),
|
||||
reducer: OnboardingReducer.default,
|
||||
reducer: OnboardingFlowReducer.default,
|
||||
environment: (.demo)
|
||||
)
|
||||
|
|
@ -1,6 +1,12 @@
|
|||
import ComposableArchitecture
|
||||
import SwiftUI
|
||||
|
||||
typealias ProfileReducer = Reducer<ProfileState, ProfileAction, ProfileEnvironment>
|
||||
typealias ProfileStore = Store<ProfileState, ProfileAction>
|
||||
typealias ProfileViewStore = ViewStore<ProfileState, ProfileAction>
|
||||
|
||||
// MARK: - State
|
||||
|
||||
struct ProfileState: Equatable {
|
||||
enum Route {
|
||||
case settings
|
||||
|
@ -12,16 +18,17 @@ struct ProfileState: Equatable {
|
|||
var route: Route?
|
||||
}
|
||||
|
||||
// MARK: - Action
|
||||
|
||||
enum ProfileAction: Equatable {
|
||||
case updateRoute(ProfileState.Route?)
|
||||
}
|
||||
|
||||
struct ProfileEnvironment {
|
||||
}
|
||||
// MARK: - Environment
|
||||
|
||||
// MARK: - ProfileReducer
|
||||
struct ProfileEnvironment { }
|
||||
|
||||
typealias ProfileReducer = Reducer<ProfileState, ProfileAction, ProfileEnvironment>
|
||||
// MARK: - Reducer
|
||||
|
||||
extension ProfileReducer {
|
||||
static let `default` = ProfileReducer { state, action, _ in
|
||||
|
@ -33,16 +40,7 @@ extension ProfileReducer {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - ProfileStore
|
||||
|
||||
typealias ProfileStore = Store<ProfileState, ProfileAction>
|
||||
|
||||
extension ProfileStore {
|
||||
}
|
||||
|
||||
// MARK: - ProfileViewStore
|
||||
|
||||
typealias ProfileViewStore = ViewStore<ProfileState, ProfileAction>
|
||||
// MARK: - ViewStore
|
||||
|
||||
extension ProfileViewStore {
|
||||
var routeBinding: Binding<ProfileState.Route?> {
|
||||
|
@ -67,7 +65,7 @@ extension ProfileViewStore {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: PlaceHolders
|
||||
// MARK: Placeholders
|
||||
|
||||
extension ProfileState {
|
||||
static var placeholder: Self {
|
||||
|
|
|
@ -28,6 +28,8 @@ struct ProfileView: View {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct ProfileView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NavigationView {
|
|
@ -7,57 +7,37 @@
|
|||
|
||||
import Foundation
|
||||
import ComposableArchitecture
|
||||
import UIKit
|
||||
|
||||
enum RecoveryPhraseError: Error {
|
||||
/// This error is thrown then the Recovery Phrase can't be generated
|
||||
case unableToGeneratePhrase
|
||||
typealias RecoveryPhraseDisplayReducer = Reducer<RecoveryPhraseDisplayState, RecoveryPhraseDisplayAction, RecoveryPhraseDisplayEnvironment>
|
||||
typealias RecoveryPhraseDisplayStore = Store<RecoveryPhraseDisplayState, RecoveryPhraseDisplayAction>
|
||||
typealias RecoveryPhraseDisplayViewStore = ViewStore<RecoveryPhraseDisplayState, RecoveryPhraseDisplayAction>
|
||||
|
||||
// MARK: - State
|
||||
|
||||
struct RecoveryPhraseDisplayState: Equatable {
|
||||
var phrase: RecoveryPhrase?
|
||||
var showCopyToBufferAlert = false
|
||||
}
|
||||
|
||||
struct FeedbackGenerator {
|
||||
let generateFeedback: () -> Void
|
||||
// MARK: - Action
|
||||
|
||||
enum RecoveryPhraseDisplayAction: Equatable {
|
||||
case createPhrase
|
||||
case copyToBufferPressed
|
||||
case finishedPressed
|
||||
case phraseResponse(Result<RecoveryPhrase, RecoveryPhraseError>)
|
||||
}
|
||||
|
||||
extension FeedbackGenerator {
|
||||
static let haptic = FeedbackGenerator(
|
||||
generateFeedback: { UINotificationFeedbackGenerator().notificationOccurred(.error) }
|
||||
)
|
||||
// MARK: - Environment
|
||||
|
||||
static let silent = FeedbackGenerator(
|
||||
generateFeedback: { }
|
||||
)
|
||||
}
|
||||
|
||||
struct Pasteboard {
|
||||
let setString: (String) -> Void
|
||||
let getString: () -> String?
|
||||
}
|
||||
|
||||
extension Pasteboard {
|
||||
private struct TestPasteboard {
|
||||
static var general = TestPasteboard()
|
||||
var string: String?
|
||||
}
|
||||
|
||||
static let live = Pasteboard(
|
||||
setString: { UIPasteboard.general.string = $0 },
|
||||
getString: { UIPasteboard.general.string }
|
||||
)
|
||||
|
||||
static let test = Pasteboard(
|
||||
setString: { TestPasteboard.general.string = $0 },
|
||||
getString: { TestPasteboard.general.string }
|
||||
)
|
||||
}
|
||||
|
||||
struct BackupPhraseEnvironment {
|
||||
struct RecoveryPhraseDisplayEnvironment {
|
||||
let mainQueue: AnySchedulerOf<DispatchQueue>
|
||||
let newPhrase: () -> Effect<RecoveryPhrase, RecoveryPhraseError>
|
||||
let pasteboard: Pasteboard
|
||||
let feedbackGenerator: FeedbackGenerator
|
||||
let pasteboard: WrappedPasteboard
|
||||
let feedbackGenerator: WrappedFeedbackGenerator
|
||||
}
|
||||
|
||||
extension BackupPhraseEnvironment {
|
||||
extension RecoveryPhraseDisplayEnvironment {
|
||||
private struct DemoPasteboard {
|
||||
static var general = Self()
|
||||
var string: String?
|
||||
|
@ -78,51 +58,7 @@ extension BackupPhraseEnvironment {
|
|||
)
|
||||
}
|
||||
|
||||
typealias RecoveryPhraseDisplayStore = Store<RecoveryPhraseDisplayState, RecoveryPhraseDisplayAction>
|
||||
|
||||
struct RecoveryPhrase: Equatable {
|
||||
struct Group: Hashable {
|
||||
var startIndex: Int
|
||||
var words: [String]
|
||||
}
|
||||
|
||||
let words: [String]
|
||||
|
||||
private let groupSize = 6
|
||||
|
||||
func toGroups() -> [Group] {
|
||||
let chunks = words.count / groupSize
|
||||
return zip(0 ..< chunks, words.chunked(into: groupSize)).map {
|
||||
Group(startIndex: $0 * groupSize + 1, words: $1)
|
||||
}
|
||||
}
|
||||
|
||||
func toString() -> String {
|
||||
words.joined(separator: " ")
|
||||
}
|
||||
|
||||
func words(fromMissingIndices indices: [Int]) -> [PhraseChip.Kind] {
|
||||
assert((indices.count - 1) * groupSize <= self.words.count)
|
||||
|
||||
return indices.enumerated().map { index, position in
|
||||
.unassigned(word: self.words[(index * groupSize) + position])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct RecoveryPhraseDisplayState: Equatable {
|
||||
var phrase: RecoveryPhrase?
|
||||
var showCopyToBufferAlert = false
|
||||
}
|
||||
|
||||
enum RecoveryPhraseDisplayAction: Equatable {
|
||||
case createPhrase
|
||||
case copyToBufferPressed
|
||||
case finishedPressed
|
||||
case phraseResponse(Result<RecoveryPhrase, RecoveryPhraseError>)
|
||||
}
|
||||
|
||||
typealias RecoveryPhraseDisplayReducer = Reducer<RecoveryPhraseDisplayState, RecoveryPhraseDisplayAction, BackupPhraseEnvironment>
|
||||
// MARK: - Reducer
|
||||
|
||||
extension RecoveryPhraseDisplayReducer {
|
||||
static let `default` = RecoveryPhraseDisplayReducer { state, action, environment in
|
||||
|
@ -148,11 +84,3 @@ extension RecoveryPhraseDisplayReducer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Array {
|
||||
func chunked(into size: Int) -> [[Element]] {
|
||||
return stride(from: 0, to: count, by: size).map {
|
||||
Array(self[$0 ..< Swift.min($0 + size, count)])
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,19 +9,17 @@ import Foundation
|
|||
import ComposableArchitecture
|
||||
import SwiftUI
|
||||
|
||||
typealias RecoveryPhraseValidationStore = Store<RecoveryPhraseValidationState, RecoveryPhraseValidationAction>
|
||||
typealias RecoveryPhraseValidationViewStore = ViewStore<RecoveryPhraseValidationState, RecoveryPhraseValidationAction>
|
||||
|
||||
/// Represents the data of a word that has been placed into an empty position, that will be used
|
||||
/// to validate the completed phrase when all ValidationWords have been placed.
|
||||
struct ValidationWord: Equatable {
|
||||
var groupIndex: Int
|
||||
var word: String
|
||||
}
|
||||
typealias RecoveryPhraseValidationFlowReducer = Reducer<
|
||||
RecoveryPhraseValidationFlowState,
|
||||
RecoveryPhraseValidationFlowAction,
|
||||
RecoveryPhraseValidationFlowEnvironment
|
||||
>
|
||||
typealias RecoveryPhraseValidationFlowStore = Store<RecoveryPhraseValidationFlowState, RecoveryPhraseValidationFlowAction>
|
||||
typealias RecoveryPhraseValidationFlowViewStore = ViewStore<RecoveryPhraseValidationFlowState, RecoveryPhraseValidationFlowAction>
|
||||
|
||||
// MARK: - State
|
||||
|
||||
struct RecoveryPhraseValidationState: Equatable {
|
||||
struct RecoveryPhraseValidationFlowState: Equatable {
|
||||
enum Route: Equatable, CaseIterable {
|
||||
case validation
|
||||
case success
|
||||
|
@ -47,14 +45,14 @@ struct RecoveryPhraseValidationState: Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
extension RecoveryPhraseValidationState {
|
||||
extension RecoveryPhraseValidationFlowState {
|
||||
/// creates an initial `RecoveryPhraseValidationState` with no completions and random missing indices.
|
||||
/// - Note: Use this function to create a random validation puzzle for a given phrase.
|
||||
static func random(phrase: RecoveryPhrase) -> Self {
|
||||
let missingIndices = Self.randomIndices()
|
||||
let missingWordChipKind = phrase.words(fromMissingIndices: missingIndices).shuffled()
|
||||
|
||||
return RecoveryPhraseValidationState(
|
||||
return RecoveryPhraseValidationFlowState(
|
||||
phrase: phrase,
|
||||
missingIndices: missingIndices,
|
||||
missingWordChips: missingWordChipKind,
|
||||
|
@ -63,7 +61,7 @@ extension RecoveryPhraseValidationState {
|
|||
}
|
||||
}
|
||||
|
||||
extension RecoveryPhraseValidationState {
|
||||
extension RecoveryPhraseValidationFlowState {
|
||||
/// Given an array of RecoveryPhraseStepCompletion, missing indices, original phrase and the number of groups it was split into,
|
||||
/// assembly the resulting phrase. This comes up with the "proposed solution" for the recovery phrase validation challenge.
|
||||
/// - returns:an array of String containing the recovery phrase words ordered by the original phrase order, or `nil`
|
||||
|
@ -121,8 +119,8 @@ extension RecoveryPhrase.Group {
|
|||
|
||||
// MARK: - Action
|
||||
|
||||
enum RecoveryPhraseValidationAction: Equatable {
|
||||
case updateRoute(RecoveryPhraseValidationState.Route?)
|
||||
enum RecoveryPhraseValidationFlowAction: Equatable {
|
||||
case updateRoute(RecoveryPhraseValidationFlowState.Route?)
|
||||
case reset
|
||||
case move(wordChip: PhraseChip.Kind, intoGroup: Int)
|
||||
case succeed
|
||||
|
@ -132,15 +130,43 @@ enum RecoveryPhraseValidationAction: Equatable {
|
|||
case displayBackedUpPhrase
|
||||
}
|
||||
|
||||
// MARK: - Environment
|
||||
|
||||
struct RecoveryPhraseValidationFlowEnvironment {
|
||||
let mainQueue: AnySchedulerOf<DispatchQueue>
|
||||
let newPhrase: () -> Effect<RecoveryPhrase, RecoveryPhraseError>
|
||||
let pasteboard: WrappedPasteboard
|
||||
let feedbackGenerator: WrappedFeedbackGenerator
|
||||
}
|
||||
|
||||
extension RecoveryPhraseValidationFlowEnvironment {
|
||||
private struct DemoPasteboard {
|
||||
static var general = Self()
|
||||
var string: String?
|
||||
}
|
||||
|
||||
static let demo = Self(
|
||||
mainQueue: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
newPhrase: { Effect(value: .init(words: RecoveryPhrase.placeholder.words)) },
|
||||
pasteboard: .test,
|
||||
feedbackGenerator: .silent
|
||||
)
|
||||
|
||||
static let live = Self(
|
||||
mainQueue: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
newPhrase: { Effect(value: .init(words: RecoveryPhrase.placeholder.words)) },
|
||||
pasteboard: .live,
|
||||
feedbackGenerator: .haptic
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Reducer
|
||||
|
||||
typealias RecoveryPhraseValidationReducer = Reducer<RecoveryPhraseValidationState, RecoveryPhraseValidationAction, BackupPhraseEnvironment>
|
||||
|
||||
extension RecoveryPhraseValidationReducer {
|
||||
static let `default` = RecoveryPhraseValidationReducer { state, action, environment in
|
||||
extension RecoveryPhraseValidationFlowReducer {
|
||||
static let `default` = RecoveryPhraseValidationFlowReducer { state, action, environment in
|
||||
switch action {
|
||||
case .reset:
|
||||
state = RecoveryPhraseValidationState.random(phrase: state.phrase)
|
||||
state = RecoveryPhraseValidationFlowState.random(phrase: state.phrase)
|
||||
state.route = .validation
|
||||
// FIXME: Resetting causes route to be nil = preamble screen, hence setting the .validation. The transition back is not animated though (issue 186)
|
||||
|
||||
|
@ -154,8 +180,8 @@ extension RecoveryPhraseValidationReducer {
|
|||
state.validationWords.append(ValidationWord(groupIndex: group, word: word))
|
||||
|
||||
if state.isComplete {
|
||||
let value: RecoveryPhraseValidationAction = state.isValid ? .succeed : .fail
|
||||
let effect = Effect<RecoveryPhraseValidationAction, Never>(value: value)
|
||||
let value: RecoveryPhraseValidationFlowAction = state.isValid ? .succeed : .fail
|
||||
let effect = Effect<RecoveryPhraseValidationFlowAction, Never>(value: value)
|
||||
.delay(for: 1, scheduler: environment.mainQueue)
|
||||
.eraseToEffect()
|
||||
|
||||
|
@ -181,7 +207,7 @@ extension RecoveryPhraseValidationReducer {
|
|||
|
||||
case .updateRoute(let route):
|
||||
guard let route = route else {
|
||||
state = RecoveryPhraseValidationState.random(phrase: state.phrase)
|
||||
state = RecoveryPhraseValidationFlowState.random(phrase: state.phrase)
|
||||
return .none
|
||||
}
|
||||
state.route = route
|
||||
|
@ -198,8 +224,8 @@ extension RecoveryPhraseValidationReducer {
|
|||
|
||||
// MARK: - ViewStore
|
||||
|
||||
extension RecoveryPhraseValidationViewStore {
|
||||
func bindingForRoute(_ route: RecoveryPhraseValidationState.Route) -> Binding<Bool> {
|
||||
extension RecoveryPhraseValidationFlowViewStore {
|
||||
func bindingForRoute(_ route: RecoveryPhraseValidationFlowState.Route) -> Binding<Bool> {
|
||||
self.binding(
|
||||
get: { $0.route == route },
|
||||
send: { isActive in
|
||||
|
@ -209,7 +235,7 @@ extension RecoveryPhraseValidationViewStore {
|
|||
}
|
||||
}
|
||||
|
||||
extension RecoveryPhraseValidationViewStore {
|
||||
extension RecoveryPhraseValidationFlowViewStore {
|
||||
var bindingForValidation: Binding<Bool> {
|
||||
self.binding(
|
||||
get: { $0.route != nil },
|
||||
|
@ -237,3 +263,109 @@ extension RecoveryPhraseValidationViewStore {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Placeholders
|
||||
|
||||
extension RecoveryPhraseValidationFlowState {
|
||||
static let placeholder = RecoveryPhraseValidationFlowState.random(phrase: .placeholder)
|
||||
|
||||
static let placeholderStep1 = RecoveryPhraseValidationFlowState(
|
||||
phrase: .placeholder,
|
||||
missingIndices: [2, 0, 3, 5],
|
||||
missingWordChips: [
|
||||
.unassigned(word: "thank"),
|
||||
.empty,
|
||||
.unassigned(word: "boil"),
|
||||
.unassigned(word: "garlic")
|
||||
],
|
||||
validationWords: [
|
||||
.init(groupIndex: 2, word: "morning")
|
||||
],
|
||||
route: nil
|
||||
)
|
||||
|
||||
static let placeholderStep2 = RecoveryPhraseValidationFlowState(
|
||||
phrase: .placeholder,
|
||||
missingIndices: [2, 0, 3, 5],
|
||||
missingWordChips: [
|
||||
.empty,
|
||||
.empty,
|
||||
.unassigned(word: "boil"),
|
||||
.unassigned(word: "garlic")
|
||||
],
|
||||
validationWords: [
|
||||
.init(groupIndex: 2, word: "morning"),
|
||||
.init(groupIndex: 0, word: "thank")
|
||||
],
|
||||
route: nil
|
||||
)
|
||||
|
||||
static let placeholderStep3 = RecoveryPhraseValidationFlowState(
|
||||
phrase: .placeholder,
|
||||
missingIndices: [2, 0, 3, 5],
|
||||
missingWordChips: [
|
||||
.empty,
|
||||
.empty,
|
||||
.unassigned(word: "boil"),
|
||||
.empty
|
||||
],
|
||||
validationWords: [
|
||||
.init(groupIndex: 2, word: "morning"),
|
||||
.init(groupIndex: 0, word: "thank"),
|
||||
.init(groupIndex: 3, word: "garlic")
|
||||
],
|
||||
route: nil
|
||||
)
|
||||
|
||||
static let placeholderStep4 = RecoveryPhraseValidationFlowState(
|
||||
phrase: .placeholder,
|
||||
missingIndices: [2, 0, 3, 5],
|
||||
missingWordChips: [
|
||||
.empty,
|
||||
.empty,
|
||||
.empty,
|
||||
.empty
|
||||
],
|
||||
validationWords: [
|
||||
.init(groupIndex: 2, word: "morning"),
|
||||
.init(groupIndex: 0, word: "thank"),
|
||||
.init(groupIndex: 3, word: "garlic"),
|
||||
.init(groupIndex: 1, word: "boil")
|
||||
],
|
||||
route: nil
|
||||
)
|
||||
}
|
||||
|
||||
extension RecoveryPhraseValidationFlowStore {
|
||||
private static let scheduler = DispatchQueue.main
|
||||
|
||||
static let demo = Store(
|
||||
initialState: .placeholder,
|
||||
reducer: .default,
|
||||
environment: .demo
|
||||
)
|
||||
|
||||
static let demoStep1 = Store(
|
||||
initialState: .placeholderStep1,
|
||||
reducer: .default,
|
||||
environment: .demo
|
||||
)
|
||||
|
||||
static let demoStep2 = Store(
|
||||
initialState: .placeholderStep1,
|
||||
reducer: .default,
|
||||
environment: .demo
|
||||
)
|
||||
|
||||
static let demoStep3 = Store(
|
||||
initialState: .placeholderStep3,
|
||||
reducer: .default,
|
||||
environment: .demo
|
||||
)
|
||||
|
||||
static let demoStep4 = Store(
|
||||
initialState: .placeholderStep4,
|
||||
reducer: .default,
|
||||
environment: .demo
|
||||
)
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
//
|
||||
// RecoveryPhraseTestPreambleView.swift
|
||||
// RecoveryPhraseValidationFlowView.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 03/01/22.
|
||||
|
@ -8,8 +8,8 @@
|
|||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
|
||||
struct RecoveryPhraseTestPreambleView: View {
|
||||
var store: RecoveryPhraseValidationStore
|
||||
struct RecoveryPhraseValidationFlowView: View {
|
||||
var store: RecoveryPhraseValidationFlowStore
|
||||
|
||||
var body: some View {
|
||||
WithViewStore(store) { viewStore in
|
||||
|
@ -77,7 +77,7 @@ struct RecoveryPhraseTestPreambleView: View {
|
|||
.navigationLinkEmpty(
|
||||
isActive: viewStore.bindingForValidation,
|
||||
destination: {
|
||||
RecoveryPhraseBackupValidationView(store: store)
|
||||
RecoveryPhraseBackupView(store: store)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
@ -91,7 +91,7 @@ struct RecoveryPhraseTestPreambleView: View {
|
|||
/// Following computations are necessary to handle properly sizing and positioning of elements
|
||||
/// on different devices (apects). iPhone SE and iPhone 8 are similar aspect family devices
|
||||
/// while iPhone X, 11, etc are different family devices, capable to use more of the space.
|
||||
extension RecoveryPhraseTestPreambleView {
|
||||
extension RecoveryPhraseValidationFlowView {
|
||||
func circularFrameUniformSize(width: CGFloat, height: CGFloat) -> CGFloat {
|
||||
var deviceMultiplier = 1.0
|
||||
|
||||
|
@ -108,16 +108,16 @@ struct RecoveryPhraseTestPreambleView_Previews: PreviewProvider {
|
|||
static var previews: some View {
|
||||
Group {
|
||||
NavigationView {
|
||||
RecoveryPhraseTestPreambleView(store: .demo)
|
||||
RecoveryPhraseValidationFlowView(store: .demo)
|
||||
}
|
||||
|
||||
RecoveryPhraseTestPreambleView(store: .demo)
|
||||
RecoveryPhraseValidationFlowView(store: .demo)
|
||||
.preferredColorScheme(.dark)
|
||||
|
||||
RecoveryPhraseTestPreambleView(store: .demo)
|
||||
RecoveryPhraseValidationFlowView(store: .demo)
|
||||
.previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)"))
|
||||
|
||||
RecoveryPhraseTestPreambleView(store: .demo)
|
||||
RecoveryPhraseValidationFlowView(store: .demo)
|
||||
.environment(\.sizeCategory, .accessibilityLarge)
|
||||
}
|
||||
}
|
|
@ -8,10 +8,10 @@
|
|||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
|
||||
struct ValidationFailedView: View {
|
||||
struct RecoveryPhraseBackupFailedView: View {
|
||||
@Environment(\.presentationMode) var presentationMode
|
||||
|
||||
var store: RecoveryPhraseValidationStore
|
||||
var store: RecoveryPhraseValidationFlowStore
|
||||
|
||||
var body: some View {
|
||||
WithViewStore(store) { viewStore in
|
||||
|
@ -82,7 +82,7 @@ struct ValidationFailedView: View {
|
|||
/// Following computations are necessary to handle properly sizing and positioning of elements
|
||||
/// on different devices (apects). iPhone SE and iPhone 8 are similar aspect family devices
|
||||
/// while iPhone X, 11, etc are different family devices, capable to use more of the space.
|
||||
extension ValidationFailedView {
|
||||
extension RecoveryPhraseBackupFailedView {
|
||||
func circularFrameUniformSize(width: CGFloat, height: CGFloat) -> CGFloat {
|
||||
var deviceMultiplier = 1.0
|
||||
|
||||
|
@ -95,23 +95,25 @@ extension ValidationFailedView {
|
|||
}
|
||||
}
|
||||
|
||||
struct ValidationFailed_Previews: PreviewProvider {
|
||||
// MARK: - Previews
|
||||
|
||||
struct RecoveryPhraseBackupValidationFailedView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
NavigationView {
|
||||
ValidationFailedView(store: .demo)
|
||||
RecoveryPhraseBackupFailedView(store: .demo)
|
||||
}
|
||||
|
||||
ValidationFailedView(store: .demo)
|
||||
RecoveryPhraseBackupFailedView(store: .demo)
|
||||
.preferredColorScheme(.dark)
|
||||
|
||||
ValidationFailedView(store: .demo)
|
||||
RecoveryPhraseBackupFailedView(store: .demo)
|
||||
.previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)"))
|
||||
|
||||
ValidationFailedView(store: .demo)
|
||||
RecoveryPhraseBackupFailedView(store: .demo)
|
||||
.environment(\.sizeCategory, .accessibilityLarge)
|
||||
|
||||
ValidationFailedView(store: .demo)
|
||||
RecoveryPhraseBackupFailedView(store: .demo)
|
||||
.environment(\.sizeCategory, .accessibilityLarge)
|
||||
.previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)"))
|
||||
}
|
|
@ -8,8 +8,8 @@
|
|||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
|
||||
struct ValidationSucceededView: View {
|
||||
var store: RecoveryPhraseValidationStore
|
||||
struct RecoveryPhraseBackupSucceededView: View {
|
||||
var store: RecoveryPhraseValidationFlowStore
|
||||
|
||||
var body: some View {
|
||||
WithViewStore(store) { viewStore in
|
||||
|
@ -53,7 +53,7 @@ struct ValidationSucceededView: View {
|
|||
}
|
||||
)
|
||||
.activeButtonStyle
|
||||
.validationSucceededViewLayout()
|
||||
.recoveryPhraseBackupValidationSucceededViewLayout()
|
||||
|
||||
Button(
|
||||
action: {
|
||||
|
@ -68,7 +68,7 @@ struct ValidationSucceededView: View {
|
|||
}
|
||||
)
|
||||
.secondaryButtonStyle
|
||||
.validationSucceededViewLayout()
|
||||
.recoveryPhraseBackupValidationSucceededViewLayout()
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
|
@ -83,7 +83,7 @@ struct ValidationSucceededView: View {
|
|||
/// Following computations are necessary to handle properly sizing and positioning of elements
|
||||
/// on different devices (apects). iPhone SE and iPhone 8 are similar aspect family devices
|
||||
/// while iPhone X, 11, etc are different family devices, capable to use more of the space.
|
||||
extension ValidationSucceededView {
|
||||
extension RecoveryPhraseBackupSucceededView {
|
||||
func circularFrameUniformSize(width: CGFloat, height: CGFloat) -> CGFloat {
|
||||
var deviceMultiplier = 1.0
|
||||
|
||||
|
@ -96,8 +96,8 @@ extension ValidationSucceededView {
|
|||
}
|
||||
}
|
||||
|
||||
// swiftlint:disable:next private_over_fileprivate strict_fileprivate
|
||||
fileprivate struct ValidationSucceededViewLayout: ViewModifier {
|
||||
// swiftlint:disable:next private_over_fileprivate strict_fileprivate type_name
|
||||
fileprivate struct RecoveryPhraseBackupValidationSucceededViewLayout: ViewModifier {
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.frame(
|
||||
|
@ -114,28 +114,31 @@ fileprivate struct ValidationSucceededViewLayout: ViewModifier {
|
|||
}
|
||||
|
||||
extension View {
|
||||
func validationSucceededViewLayout() -> some View {
|
||||
modifier(ValidationSucceededViewLayout())
|
||||
func recoveryPhraseBackupValidationSucceededViewLayout() -> some View {
|
||||
modifier(RecoveryPhraseBackupValidationSucceededViewLayout())
|
||||
}
|
||||
}
|
||||
|
||||
struct ValidationSuccededView_Previews: PreviewProvider {
|
||||
// MARK: - Previews
|
||||
|
||||
// swiftlint:disable:next type_name
|
||||
struct RecoveryPhraseBackupValidationSucceededView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
NavigationView {
|
||||
ValidationSucceededView(store: .demo)
|
||||
RecoveryPhraseBackupSucceededView(store: .demo)
|
||||
}
|
||||
|
||||
ValidationSucceededView(store: .demo)
|
||||
RecoveryPhraseBackupSucceededView(store: .demo)
|
||||
.preferredColorScheme(.dark)
|
||||
|
||||
ValidationSucceededView(store: .demo)
|
||||
RecoveryPhraseBackupSucceededView(store: .demo)
|
||||
.previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)"))
|
||||
|
||||
ValidationSucceededView(store: .demo)
|
||||
RecoveryPhraseBackupSucceededView(store: .demo)
|
||||
.environment(\.sizeCategory, .accessibilityLarge)
|
||||
|
||||
ValidationSucceededView(store: .demo)
|
||||
RecoveryPhraseBackupSucceededView(store: .demo)
|
||||
.environment(\.sizeCategory, .accessibilityLarge)
|
||||
.previewDevice(PreviewDevice(rawValue: "iPhone SE (2nd generation)"))
|
||||
}
|
|
@ -8,10 +8,10 @@
|
|||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
|
||||
struct RecoveryPhraseBackupValidationView: View {
|
||||
let store: RecoveryPhraseValidationStore
|
||||
struct RecoveryPhraseBackupView: View {
|
||||
let store: RecoveryPhraseValidationFlowStore
|
||||
|
||||
var viewStore: RecoveryPhraseValidationViewStore {
|
||||
var viewStore: RecoveryPhraseValidationFlowViewStore {
|
||||
ViewStore(store)
|
||||
}
|
||||
|
||||
|
@ -52,11 +52,11 @@ struct RecoveryPhraseBackupValidationView: View {
|
|||
.padding(.top, 0)
|
||||
.navigationLinkEmpty(
|
||||
isActive: viewStore.bindingForSuccess,
|
||||
destination: { ValidationSucceededView(store: store) }
|
||||
destination: { RecoveryPhraseBackupSucceededView(store: store) }
|
||||
)
|
||||
.navigationLinkEmpty(
|
||||
isActive: viewStore.bindingForFailure,
|
||||
destination: { ValidationFailedView(store: store) }
|
||||
destination: { RecoveryPhraseBackupFailedView(store: store) }
|
||||
)
|
||||
}
|
||||
.frame(alignment: .top)
|
||||
|
@ -66,8 +66,10 @@ struct RecoveryPhraseBackupValidationView: View {
|
|||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationTitle(Text("recoveryPhraseBackupValidation.title"))
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder func header(for viewStore: RecoveryPhraseValidationViewStore) -> some View {
|
||||
private extension RecoveryPhraseBackupView {
|
||||
@ViewBuilder func header(for viewStore: RecoveryPhraseValidationFlowViewStore) -> some View {
|
||||
VStack {
|
||||
if viewStore.isComplete {
|
||||
completeHeader(for: viewStore.state)
|
||||
|
@ -81,7 +83,7 @@ struct RecoveryPhraseBackupValidationView: View {
|
|||
.padding(.horizontal, 30)
|
||||
}
|
||||
|
||||
@ViewBuilder func completeHeader(for state: RecoveryPhraseValidationState) -> some View {
|
||||
@ViewBuilder func completeHeader(for state: RecoveryPhraseValidationFlowState) -> some View {
|
||||
if state.isValid {
|
||||
Text("recoveryPhraseBackupValidation.successResult")
|
||||
.bodyText()
|
||||
|
@ -92,7 +94,7 @@ struct RecoveryPhraseBackupValidationView: View {
|
|||
}
|
||||
}
|
||||
|
||||
private extension RecoveryPhraseValidationState {
|
||||
private extension RecoveryPhraseValidationFlowState {
|
||||
@ViewBuilder func missingWordGrid() -> some View {
|
||||
let columns = Array(
|
||||
repeating: GridItem(.flexible(minimum: 100, maximum: 120), spacing: 20),
|
||||
|
@ -114,7 +116,7 @@ private extension RecoveryPhraseValidationState {
|
|||
}
|
||||
}
|
||||
|
||||
extension RecoveryPhraseValidationState {
|
||||
extension RecoveryPhraseValidationFlowState {
|
||||
func wordsChips(
|
||||
for groupIndex: Int,
|
||||
groupSize: Int,
|
||||
|
@ -136,120 +138,16 @@ extension RecoveryPhraseValidationState {
|
|||
}
|
||||
}
|
||||
|
||||
extension RecoveryPhraseValidationState {
|
||||
static let placeholder = RecoveryPhraseValidationState.random(phrase: .placeholder)
|
||||
|
||||
static let placeholderStep1 = RecoveryPhraseValidationState(
|
||||
phrase: .placeholder,
|
||||
missingIndices: [2, 0, 3, 5],
|
||||
missingWordChips: [
|
||||
.unassigned(word: "thank"),
|
||||
.empty,
|
||||
.unassigned(word: "boil"),
|
||||
.unassigned(word: "garlic")
|
||||
],
|
||||
validationWords: [
|
||||
.init(groupIndex: 2, word: "morning")
|
||||
],
|
||||
route: nil
|
||||
)
|
||||
|
||||
static let placeholderStep2 = RecoveryPhraseValidationState(
|
||||
phrase: .placeholder,
|
||||
missingIndices: [2, 0, 3, 5],
|
||||
missingWordChips: [
|
||||
.empty,
|
||||
.empty,
|
||||
.unassigned(word: "boil"),
|
||||
.unassigned(word: "garlic")
|
||||
],
|
||||
validationWords: [
|
||||
.init(groupIndex: 2, word: "morning"),
|
||||
.init(groupIndex: 0, word: "thank")
|
||||
],
|
||||
route: nil
|
||||
)
|
||||
|
||||
static let placeholderStep3 = RecoveryPhraseValidationState(
|
||||
phrase: .placeholder,
|
||||
missingIndices: [2, 0, 3, 5],
|
||||
missingWordChips: [
|
||||
.empty,
|
||||
.empty,
|
||||
.unassigned(word: "boil"),
|
||||
.empty
|
||||
],
|
||||
validationWords: [
|
||||
.init(groupIndex: 2, word: "morning"),
|
||||
.init(groupIndex: 0, word: "thank"),
|
||||
.init(groupIndex: 3, word: "garlic")
|
||||
],
|
||||
route: nil
|
||||
)
|
||||
|
||||
static let placeholderStep4 = RecoveryPhraseValidationState(
|
||||
phrase: .placeholder,
|
||||
missingIndices: [2, 0, 3, 5],
|
||||
missingWordChips: [
|
||||
.empty,
|
||||
.empty,
|
||||
.empty,
|
||||
.empty
|
||||
],
|
||||
validationWords: [
|
||||
.init(groupIndex: 2, word: "morning"),
|
||||
.init(groupIndex: 0, word: "thank"),
|
||||
.init(groupIndex: 3, word: "garlic"),
|
||||
.init(groupIndex: 1, word: "boil")
|
||||
],
|
||||
route: nil
|
||||
)
|
||||
}
|
||||
|
||||
extension RecoveryPhraseValidationStore {
|
||||
private static let scheduler = DispatchQueue.main
|
||||
|
||||
static let demo = Store(
|
||||
initialState: .placeholder,
|
||||
reducer: .default,
|
||||
environment: .demo
|
||||
)
|
||||
|
||||
static let demoStep1 = Store(
|
||||
initialState: .placeholderStep1,
|
||||
reducer: .default,
|
||||
environment: .demo
|
||||
)
|
||||
|
||||
static let demoStep2 = Store(
|
||||
initialState: .placeholderStep1,
|
||||
reducer: .default,
|
||||
environment: .demo
|
||||
)
|
||||
|
||||
static let demoStep3 = Store(
|
||||
initialState: .placeholderStep3,
|
||||
reducer: .default,
|
||||
environment: .demo
|
||||
)
|
||||
|
||||
static let demoStep4 = Store(
|
||||
initialState: .placeholderStep4,
|
||||
reducer: .default,
|
||||
environment: .demo
|
||||
)
|
||||
}
|
||||
|
||||
private extension WordChipGrid {
|
||||
init(
|
||||
state: RecoveryPhraseValidationState,
|
||||
state: RecoveryPhraseValidationFlowState,
|
||||
groupIndex: Int,
|
||||
wordGroup: RecoveryPhrase.Group,
|
||||
misingIndex: Int
|
||||
) {
|
||||
let chips = state.wordsChips(
|
||||
for: groupIndex,
|
||||
groupSize: RecoveryPhraseValidationState.wordGroupSize,
|
||||
groupSize: RecoveryPhraseValidationFlowState.wordGroupSize,
|
||||
from: wordGroup
|
||||
)
|
||||
|
||||
|
@ -257,7 +155,7 @@ private extension WordChipGrid {
|
|||
}
|
||||
}
|
||||
|
||||
private extension RecoveryPhraseValidationState {
|
||||
private extension RecoveryPhraseValidationFlowState {
|
||||
var coloredChipColor: Color {
|
||||
if self.isComplete {
|
||||
return isValid ? Asset.Colors.Buttons.activeButton.color : Asset.Colors.BackgroundColors.red.color
|
||||
|
@ -267,18 +165,20 @@ private extension RecoveryPhraseValidationState {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct RecoveryPhraseBackupView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NavigationView {
|
||||
RecoveryPhraseBackupValidationView(store: .demoStep4)
|
||||
RecoveryPhraseValidationFlowView(store: .demoStep4)
|
||||
}
|
||||
|
||||
NavigationView {
|
||||
RecoveryPhraseBackupValidationView(store: .demoStep1)
|
||||
RecoveryPhraseValidationFlowView(store: .demoStep1)
|
||||
}
|
||||
|
||||
NavigationView {
|
||||
RecoveryPhraseBackupValidationView(store: .demoStep1)
|
||||
RecoveryPhraseValidationFlowView(store: .demoStep1)
|
||||
}
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
|
@ -1,18 +1,25 @@
|
|||
import ComposableArchitecture
|
||||
|
||||
typealias RequestReducer = Reducer<RequestState, RequestAction, RequestEnvironment>
|
||||
typealias RequestStore = Store<RequestState, RequestAction>
|
||||
typealias RequestViewStore = ViewStore<RequestState, RequestAction>
|
||||
|
||||
// MARK: - State
|
||||
|
||||
struct RequestState: Equatable {
|
||||
}
|
||||
|
||||
// MARK: - Action
|
||||
|
||||
enum RequestAction: Equatable {
|
||||
case noOp
|
||||
}
|
||||
|
||||
struct RequestEnvironment {
|
||||
}
|
||||
// MARK: - Environment
|
||||
|
||||
// MARK: - RequestReducer
|
||||
struct RequestEnvironment { }
|
||||
|
||||
typealias RequestReducer = Reducer<RequestState, RequestAction, RequestEnvironment>
|
||||
// MARK: - Reducer
|
||||
|
||||
extension RequestReducer {
|
||||
static let `default` = RequestReducer { _, action, _ in
|
||||
|
@ -23,9 +30,13 @@ extension RequestReducer {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - RequestStore
|
||||
// MARK: Placeholders
|
||||
|
||||
typealias RequestStore = Store<RequestState, RequestAction>
|
||||
extension RequestState {
|
||||
static var placeholder: Self {
|
||||
.init()
|
||||
}
|
||||
}
|
||||
|
||||
extension RequestStore {
|
||||
static let placeholder = RequestStore(
|
||||
|
@ -34,18 +45,3 @@ extension RequestStore {
|
|||
environment: RequestEnvironment()
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - RequestViewStore
|
||||
|
||||
typealias RequestViewStore = ViewStore<RequestState, RequestAction>
|
||||
|
||||
extension RequestViewStore {
|
||||
}
|
||||
|
||||
// MARK: PlaceHolders
|
||||
|
||||
extension RequestState {
|
||||
static var placeholder: Self {
|
||||
.init()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ struct RequestView: View {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct RequestView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
RequestView(store: .placeholder)
|
|
@ -1,6 +1,12 @@
|
|||
import ComposableArchitecture
|
||||
import SwiftUI
|
||||
|
||||
typealias SandboxReducer = Reducer<SandboxState, SandboxAction, SandboxEnvironment>
|
||||
typealias SandboxStore = Store<SandboxState, SandboxAction>
|
||||
typealias SandboxViewStore = ViewStore<SandboxState, SandboxAction>
|
||||
|
||||
// MARK: - State
|
||||
|
||||
struct SandboxState: Equatable {
|
||||
enum Route: Equatable, CaseIterable {
|
||||
case history
|
||||
|
@ -10,21 +16,25 @@ struct SandboxState: Equatable {
|
|||
case scan
|
||||
case request
|
||||
}
|
||||
var transactionHistoryState: TransactionHistoryState
|
||||
var transactionHistoryState: TransactionHistoryFlowState
|
||||
var profileState: ProfileState
|
||||
var route: Route?
|
||||
}
|
||||
|
||||
// MARK: - Action
|
||||
|
||||
enum SandboxAction: Equatable {
|
||||
case updateRoute(SandboxState.Route?)
|
||||
case transactionHistory(TransactionHistoryAction)
|
||||
case transactionHistory(TransactionHistoryFlowAction)
|
||||
case profile(ProfileAction)
|
||||
case reset
|
||||
}
|
||||
|
||||
// MARK: - SandboxReducer
|
||||
// MARK: - Environment
|
||||
|
||||
typealias SandboxReducer = Reducer<SandboxState, SandboxAction, Void>
|
||||
struct SandboxEnvironment { }
|
||||
|
||||
// MARK: - Reducer
|
||||
|
||||
extension SandboxReducer {
|
||||
static let `default` = SandboxReducer { state, action, environment in
|
||||
|
@ -33,14 +43,14 @@ extension SandboxReducer {
|
|||
state.route = route
|
||||
return .none
|
||||
case let .transactionHistory(transactionHistoryAction):
|
||||
return TransactionHistoryReducer
|
||||
return TransactionHistoryFlowReducer
|
||||
.default
|
||||
.run(
|
||||
&state.transactionHistoryState,
|
||||
transactionHistoryAction,
|
||||
TransactionHistoryEnvironment(
|
||||
TransactionHistoryFlowEnvironment(
|
||||
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer()
|
||||
SDKSynchronizer: LiveWrappedSDKSynchronizer()
|
||||
)
|
||||
)
|
||||
.map(SandboxAction.transactionHistory)
|
||||
|
@ -61,12 +71,10 @@ extension SandboxReducer {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - SandboxStore
|
||||
|
||||
typealias SandboxStore = Store<SandboxState, SandboxAction>
|
||||
// MARK: - Store
|
||||
|
||||
extension SandboxStore {
|
||||
func historyStore() -> TransactionHistoryStore {
|
||||
func historyStore() -> TransactionHistoryFlowStore {
|
||||
self.scope(
|
||||
state: \.transactionHistoryState,
|
||||
action: SandboxAction.transactionHistory
|
||||
|
@ -81,22 +89,20 @@ extension SandboxStore {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - SandboxViewStore
|
||||
|
||||
typealias SandboxViewStore = ViewStore<SandboxState, SandboxAction>
|
||||
// MARK: - ViewStore
|
||||
|
||||
extension SandboxViewStore {
|
||||
func toggleSelectedTransaction() {
|
||||
let isAlreadySelected = (self.selectedTranactionID != nil)
|
||||
let transcation = self.transactionHistoryState.transactions[5]
|
||||
let newRoute = isAlreadySelected ? nil : TransactionHistoryState.Route.showTransaction(transcation)
|
||||
let newRoute = isAlreadySelected ? nil : TransactionHistoryFlowState.Route.showTransaction(transcation)
|
||||
send(.transactionHistory(.updateRoute(newRoute)))
|
||||
}
|
||||
|
||||
var selectedTranactionID: String? {
|
||||
self.transactionHistoryState
|
||||
.route
|
||||
.flatMap(/TransactionHistoryState.Route.showTransaction)
|
||||
.flatMap(/TransactionHistoryFlowState.Route.showTransaction)
|
||||
.map(\.id)
|
||||
}
|
||||
|
||||
|
@ -110,7 +116,8 @@ extension SandboxViewStore {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: PlaceHolders
|
||||
// MARK: - PlaceHolders
|
||||
|
||||
extension SandboxState {
|
||||
static var placeholder: Self {
|
||||
.init(
|
||||
|
@ -120,3 +127,17 @@ extension SandboxState {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
extension SandboxStore {
|
||||
static var placeholder: SandboxStore {
|
||||
SandboxStore(
|
||||
initialState: SandboxState(
|
||||
transactionHistoryState: .placeHolder,
|
||||
profileState: .placeholder,
|
||||
route: nil
|
||||
),
|
||||
reducer: .default.debug(),
|
||||
environment: SandboxEnvironment()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,12 @@ import SwiftUI
|
|||
import ComposableArchitecture
|
||||
|
||||
struct SandboxView: View {
|
||||
let store: Store<SandboxState, SandboxAction>
|
||||
struct SandboxRouteValue: Identifiable {
|
||||
let id: Int
|
||||
let route: SandboxState.Route
|
||||
}
|
||||
|
||||
let store: SandboxStore
|
||||
|
||||
var navigationRouteValues: [SandboxRouteValue] = SandboxState.Route.allCases
|
||||
.enumerated()
|
||||
|
@ -17,21 +22,21 @@ struct SandboxView: View {
|
|||
@ViewBuilder func view(for route: SandboxState.Route) -> some View {
|
||||
switch route {
|
||||
case .history:
|
||||
TransactionHistoryView(store: store.historyStore())
|
||||
TransactionHistoryFlowView(store: store.historyStore())
|
||||
case .send:
|
||||
SendView(
|
||||
SendFlowView(
|
||||
store: .init(
|
||||
initialState: .placeholder,
|
||||
reducer: SendReducer.default(
|
||||
reducer: SendFlowReducer.default(
|
||||
whenDone: { SandboxViewStore(store).send(.updateRoute(nil)) }
|
||||
)
|
||||
.debug(),
|
||||
environment: SendEnvironment(
|
||||
environment: SendFlowEnvironment(
|
||||
mnemonicSeedPhraseProvider: .live,
|
||||
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
walletStorage: .live(),
|
||||
wrappedDerivationTool: .live(),
|
||||
wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer()
|
||||
derivationTool: .live(),
|
||||
SDKSynchronizer: LiveWrappedSDKSynchronizer()
|
||||
)
|
||||
)
|
||||
)
|
||||
|
@ -88,7 +93,7 @@ struct SandboxView: View {
|
|||
isPresented: viewStore.bindingForRoute(.history),
|
||||
content: {
|
||||
NavigationView {
|
||||
TransactionHistoryView(store: store.historyStore())
|
||||
TransactionHistoryFlowView(store: store.historyStore())
|
||||
.toolbar {
|
||||
ToolbarItem {
|
||||
Button("Done") { viewStore.send(.updateRoute(nil)) }
|
||||
|
@ -102,27 +107,8 @@ struct SandboxView: View {
|
|||
}
|
||||
}
|
||||
|
||||
struct SandboxRouteValue: Identifiable {
|
||||
let id: Int
|
||||
let route: SandboxState.Route
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
extension SandboxStore {
|
||||
static var placeholder: SandboxStore {
|
||||
SandboxStore(
|
||||
initialState: SandboxState(
|
||||
transactionHistoryState: .placeHolder,
|
||||
profileState: .placeholder,
|
||||
route: nil
|
||||
),
|
||||
reducer: .default.debug(),
|
||||
environment: ()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct SandboxView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NavigationView {
|
|
@ -1,18 +1,25 @@
|
|||
import ComposableArchitecture
|
||||
|
||||
typealias ScanReducer = Reducer<ScanState, ScanAction, ScanEnvironment>
|
||||
typealias ScanStore = Store<ScanState, ScanAction>
|
||||
typealias ScanViewStore = ViewStore<ScanState, ScanAction>
|
||||
|
||||
// MARK: - State
|
||||
|
||||
struct ScanState: Equatable {
|
||||
}
|
||||
|
||||
// MARK: - Action
|
||||
|
||||
enum ScanAction: Equatable {
|
||||
case noOp
|
||||
}
|
||||
|
||||
struct ScanEnvironment {
|
||||
}
|
||||
// MARK: - Environment
|
||||
|
||||
// MARK: - ScanReducer
|
||||
struct ScanEnvironment { }
|
||||
|
||||
typealias ScanReducer = Reducer<ScanState, ScanAction, ScanEnvironment>
|
||||
// MARK: - Reducer
|
||||
|
||||
extension ScanReducer {
|
||||
static let `default` = ScanReducer { _, action, _ in
|
||||
|
@ -23,9 +30,13 @@ extension ScanReducer {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - ScanStore
|
||||
// MARK: Placeholders
|
||||
|
||||
typealias ScanStore = Store<ScanState, ScanAction>
|
||||
extension ScanState {
|
||||
static var placeholder: Self {
|
||||
.init()
|
||||
}
|
||||
}
|
||||
|
||||
extension ScanStore {
|
||||
static let placeholder = ScanStore(
|
||||
|
@ -34,18 +45,3 @@ extension ScanStore {
|
|||
environment: ScanEnvironment()
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - ScanViewStore
|
||||
|
||||
typealias ScanViewStore = ViewStore<ScanState, ScanAction>
|
||||
|
||||
extension ScanViewStore {
|
||||
}
|
||||
|
||||
// MARK: PlaceHolders
|
||||
|
||||
extension ScanState {
|
||||
static var placeholder: Self {
|
||||
.init()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ struct ScanView: View {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct ScanView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ScanView(store: .placeholder)
|
|
@ -1,24 +1,21 @@
|
|||
//
|
||||
// SendFlowStore.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 04/25/2022.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
import ZcashLightClientKit
|
||||
|
||||
struct Transaction: Equatable {
|
||||
var amount: Int64
|
||||
var memo: String
|
||||
var toAddress: String
|
||||
}
|
||||
typealias SendFlowReducer = Reducer<SendFlowState, SendFlowAction, SendFlowEnvironment>
|
||||
typealias SendFlowStore = Store<SendFlowState, SendFlowAction>
|
||||
typealias SendFlowViewStore = ViewStore<SendFlowState, SendFlowAction>
|
||||
|
||||
extension Transaction {
|
||||
static var placeholder: Self {
|
||||
.init(
|
||||
amount: 0,
|
||||
memo: "",
|
||||
toAddress: ""
|
||||
)
|
||||
}
|
||||
}
|
||||
// MARK: - State
|
||||
|
||||
struct SendState: Equatable {
|
||||
struct SendFlowState: Equatable {
|
||||
enum Route: Equatable {
|
||||
case confirmation
|
||||
case success
|
||||
|
@ -31,9 +28,9 @@ struct SendState: Equatable {
|
|||
var isSendingTransaction = false
|
||||
var memo = ""
|
||||
var totalBalance: Int64 = 0
|
||||
var transaction: Transaction
|
||||
var transactionAddressInputState: TransactionAddressInputState
|
||||
var transactionAmountInputState: TransactionAmountInputState
|
||||
var transaction: SendFlowTransaction
|
||||
var transactionAddressInputState: TransactionAddressTextFieldState
|
||||
var transactionAmountInputState: TransactionAmountTextFieldState
|
||||
|
||||
var isInvalidAddressFormat: Bool {
|
||||
!transactionAddressInputState.isValidAddress
|
||||
|
@ -60,38 +57,38 @@ struct SendState: Equatable {
|
|||
}
|
||||
}
|
||||
|
||||
enum SendAction: Equatable {
|
||||
// MARK: - Action
|
||||
|
||||
enum SendFlowAction: Equatable {
|
||||
case onAppear
|
||||
case onDisappear
|
||||
case sendConfirmationPressed
|
||||
case sendTransactionResult(Result<TransactionState, NSError>)
|
||||
case synchronizerStateChanged(WrappedSDKSynchronizerState)
|
||||
case transactionAddressInput(TransactionAddressInputAction)
|
||||
case transactionAmountInput(TransactionAmountInputAction)
|
||||
case transactionAddressInput(TransactionAddressTextFieldAction)
|
||||
case transactionAmountInput(TransactionAmountTextFieldAction)
|
||||
case updateBalance(Int64)
|
||||
case updateMemo(String)
|
||||
case updateTransaction(Transaction)
|
||||
case updateRoute(SendState.Route?)
|
||||
case updateTransaction(SendFlowTransaction)
|
||||
case updateRoute(SendFlowState.Route?)
|
||||
}
|
||||
|
||||
struct SendEnvironment {
|
||||
let mnemonicSeedPhraseProvider: MnemonicSeedPhraseProvider
|
||||
// MARK: - Environment
|
||||
|
||||
struct SendFlowEnvironment {
|
||||
let mnemonicSeedPhraseProvider: WrappedMnemonic
|
||||
let scheduler: AnySchedulerOf<DispatchQueue>
|
||||
let walletStorage: WalletStorageInteractor
|
||||
let wrappedDerivationTool: WrappedDerivationTool
|
||||
let wrappedSDKSynchronizer: WrappedSDKSynchronizer
|
||||
let walletStorage: WrappedWalletStorage
|
||||
let derivationTool: WrappedDerivationTool
|
||||
let SDKSynchronizer: WrappedSDKSynchronizer
|
||||
}
|
||||
|
||||
// MARK: - SendReducer
|
||||
// MARK: - Reducer
|
||||
|
||||
private struct ListenerId: Hashable {}
|
||||
|
||||
typealias SendReducer = Reducer<SendState, SendAction, SendEnvironment>
|
||||
|
||||
extension SendReducer {
|
||||
extension SendFlowReducer {
|
||||
private struct SyncStatusUpdatesID: Hashable {}
|
||||
|
||||
static let `default` = SendReducer.combine(
|
||||
static let `default` = SendFlowReducer.combine(
|
||||
[
|
||||
sendReducer,
|
||||
transactionAddressInputReducer,
|
||||
|
@ -100,7 +97,7 @@ extension SendReducer {
|
|||
)
|
||||
.debug()
|
||||
|
||||
private static let sendReducer = SendReducer { state, action, environment in
|
||||
private static let sendReducer = SendFlowReducer { state, action, environment in
|
||||
switch action {
|
||||
case let .updateTransaction(transaction):
|
||||
state.transaction = transaction
|
||||
|
@ -128,13 +125,13 @@ extension SendReducer {
|
|||
do {
|
||||
let storedWallet = try environment.walletStorage.exportWallet()
|
||||
let seedBytes = try environment.mnemonicSeedPhraseProvider.toSeed(storedWallet.seedPhrase)
|
||||
guard let spendingKey = try environment.wrappedDerivationTool.deriveSpendingKeys(seedBytes, 1).first else {
|
||||
guard let spendingKey = try environment.derivationTool.deriveSpendingKeys(seedBytes, 1).first else {
|
||||
return Effect(value: .updateRoute(.failure))
|
||||
}
|
||||
|
||||
state.isSendingTransaction = true
|
||||
|
||||
return environment.wrappedSDKSynchronizer.sendTransaction(
|
||||
return environment.SDKSynchronizer.sendTransaction(
|
||||
with: spendingKey,
|
||||
zatoshi: Int64(state.transaction.amount),
|
||||
to: state.transaction.toAddress,
|
||||
|
@ -142,7 +139,7 @@ extension SendReducer {
|
|||
from: 0
|
||||
)
|
||||
.receive(on: environment.scheduler)
|
||||
.map(SendAction.sendTransactionResult)
|
||||
.map(SendFlowAction.sendTransactionResult)
|
||||
.eraseToEffect()
|
||||
} catch {
|
||||
return Effect(value: .updateRoute(.failure))
|
||||
|
@ -164,19 +161,19 @@ extension SendReducer {
|
|||
return .none
|
||||
|
||||
case .onAppear:
|
||||
return environment.wrappedSDKSynchronizer.stateChanged
|
||||
.map(SendAction.synchronizerStateChanged)
|
||||
return environment.SDKSynchronizer.stateChanged
|
||||
.map(SendFlowAction.synchronizerStateChanged)
|
||||
.eraseToEffect()
|
||||
.cancellable(id: ListenerId(), cancelInFlight: true)
|
||||
.cancellable(id: SyncStatusUpdatesID(), cancelInFlight: true)
|
||||
|
||||
case .onDisappear:
|
||||
return Effect.cancel(id: ListenerId())
|
||||
return Effect.cancel(id: SyncStatusUpdatesID())
|
||||
|
||||
case .synchronizerStateChanged(.synced):
|
||||
return environment.wrappedSDKSynchronizer.getShieldedBalance()
|
||||
return environment.SDKSynchronizer.getShieldedBalance()
|
||||
.receive(on: environment.scheduler)
|
||||
.map({ $0.total })
|
||||
.map(SendAction.updateBalance)
|
||||
.map(SendFlowAction.updateBalance)
|
||||
.eraseToEffect()
|
||||
|
||||
case .synchronizerStateChanged(let synchronizerState):
|
||||
|
@ -193,24 +190,24 @@ extension SendReducer {
|
|||
}
|
||||
}
|
||||
|
||||
private static let transactionAddressInputReducer: SendReducer = TransactionAddressInputReducer.default.pullback(
|
||||
state: \SendState.transactionAddressInputState,
|
||||
action: /SendAction.transactionAddressInput,
|
||||
private static let transactionAddressInputReducer: SendFlowReducer = TransactionAddressTextFieldReducer.default.pullback(
|
||||
state: \SendFlowState.transactionAddressInputState,
|
||||
action: /SendFlowAction.transactionAddressInput,
|
||||
environment: { environment in
|
||||
TransactionAddressInputEnvironment(
|
||||
wrappedDerivationTool: environment.wrappedDerivationTool
|
||||
TransactionAddressTextFieldEnvironment(
|
||||
derivationTool: environment.derivationTool
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
private static let transactionAmountInputReducer: SendReducer = TransactionAmountInputReducer.default.pullback(
|
||||
state: \SendState.transactionAmountInputState,
|
||||
action: /SendAction.transactionAmountInput,
|
||||
environment: { _ in TransactionAmountInputEnvironment() }
|
||||
private static let transactionAmountInputReducer: SendFlowReducer = TransactionAmountTextFieldReducer.default.pullback(
|
||||
state: \SendFlowState.transactionAmountInputState,
|
||||
action: /SendFlowAction.transactionAmountInput,
|
||||
environment: { _ in TransactionAmountTextFieldEnvironment() }
|
||||
)
|
||||
|
||||
static func `default`(whenDone: @escaping () -> Void) -> SendReducer {
|
||||
SendReducer { state, action, environment in
|
||||
static func `default`(whenDone: @escaping () -> Void) -> SendFlowReducer {
|
||||
SendFlowReducer { state, action, environment in
|
||||
switch action {
|
||||
case let .updateRoute(route) where route == .done:
|
||||
return Effect.fireAndForget(whenDone)
|
||||
|
@ -221,68 +218,62 @@ extension SendReducer {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - SendStore
|
||||
// MARK: - ViewStore
|
||||
|
||||
typealias SendStore = Store<SendState, SendAction>
|
||||
|
||||
// MARK: - SendViewStore
|
||||
|
||||
typealias SendViewStore = ViewStore<SendState, SendAction>
|
||||
|
||||
extension SendViewStore {
|
||||
var bindingForTransaction: Binding<Transaction> {
|
||||
extension SendFlowViewStore {
|
||||
var bindingForTransaction: Binding<SendFlowTransaction> {
|
||||
self.binding(
|
||||
get: \.transaction,
|
||||
send: SendAction.updateTransaction
|
||||
send: SendFlowAction.updateTransaction
|
||||
)
|
||||
}
|
||||
|
||||
var routeBinding: Binding<SendState.Route?> {
|
||||
var routeBinding: Binding<SendFlowState.Route?> {
|
||||
self.binding(
|
||||
get: \.route,
|
||||
send: SendAction.updateRoute
|
||||
send: SendFlowAction.updateRoute
|
||||
)
|
||||
}
|
||||
|
||||
var bindingForConfirmation: Binding<Bool> {
|
||||
self.routeBinding.map(
|
||||
extract: { $0 == .confirmation || self.bindingForSuccess.wrappedValue || self.bindingForFailure.wrappedValue },
|
||||
embed: { $0 ? SendState.Route.confirmation : nil }
|
||||
embed: { $0 ? SendFlowState.Route.confirmation : nil }
|
||||
)
|
||||
}
|
||||
|
||||
var bindingForSuccess: Binding<Bool> {
|
||||
self.routeBinding.map(
|
||||
extract: { $0 == .success || self.bindingForDone.wrappedValue },
|
||||
embed: { $0 ? SendState.Route.success : SendState.Route.confirmation }
|
||||
embed: { $0 ? SendFlowState.Route.success : SendFlowState.Route.confirmation }
|
||||
)
|
||||
}
|
||||
|
||||
var bindingForFailure: Binding<Bool> {
|
||||
self.routeBinding.map(
|
||||
extract: { $0 == .failure || self.bindingForDone.wrappedValue },
|
||||
embed: { $0 ? SendState.Route.failure : SendState.Route.confirmation }
|
||||
embed: { $0 ? SendFlowState.Route.failure : SendFlowState.Route.confirmation }
|
||||
)
|
||||
}
|
||||
|
||||
var bindingForDone: Binding<Bool> {
|
||||
self.routeBinding.map(
|
||||
extract: { $0 == .done },
|
||||
embed: { $0 ? SendState.Route.done : SendState.Route.confirmation }
|
||||
embed: { $0 ? SendFlowState.Route.done : SendFlowState.Route.confirmation }
|
||||
)
|
||||
}
|
||||
|
||||
var bindingForMemo: Binding<String> {
|
||||
self.binding(
|
||||
get: \.memo,
|
||||
send: SendAction.updateMemo
|
||||
send: SendFlowAction.updateMemo
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: PlaceHolders
|
||||
// MARK: Placeholders
|
||||
|
||||
extension SendState {
|
||||
extension SendFlowState {
|
||||
static var placeholder: Self {
|
||||
.init(
|
||||
route: nil,
|
||||
|
@ -305,3 +296,26 @@ extension SendState {
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
// #if DEBUG // FIX: Issue #306 - Release build is broken
|
||||
extension SendFlowStore {
|
||||
static var placeholder: SendFlowStore {
|
||||
return SendFlowStore(
|
||||
initialState: .init(
|
||||
route: nil,
|
||||
transaction: .placeholder,
|
||||
transactionAddressInputState: .placeholder,
|
||||
transactionAmountInputState: .placeholder
|
||||
),
|
||||
reducer: .default,
|
||||
environment: SendFlowEnvironment(
|
||||
mnemonicSeedPhraseProvider: .live,
|
||||
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
walletStorage: .live(),
|
||||
derivationTool: .live(),
|
||||
SDKSynchronizer: LiveWrappedSDKSynchronizer()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
// #endif
|
|
@ -1,8 +1,15 @@
|
|||
//
|
||||
// SendFlowView.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 04/25/2022.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
|
||||
struct SendView: View {
|
||||
let store: SendStore
|
||||
struct SendFlowView: View {
|
||||
let store: SendFlowStore
|
||||
|
||||
var body: some View {
|
||||
WithViewStore(store) { viewStore in
|
||||
|
@ -19,10 +26,12 @@ struct SendView: View {
|
|||
}
|
||||
}
|
||||
|
||||
struct SendView_Previews: PreviewProvider {
|
||||
// MARK: - Previews
|
||||
|
||||
struct SendFLowView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NavigationView {
|
||||
SendView(
|
||||
SendFlowView(
|
||||
store: .init(
|
||||
initialState: .init(
|
||||
route: nil,
|
||||
|
@ -31,12 +40,12 @@ struct SendView_Previews: PreviewProvider {
|
|||
transactionAmountInputState: .placeholder
|
||||
),
|
||||
reducer: .default,
|
||||
environment: SendEnvironment(
|
||||
environment: SendFlowEnvironment(
|
||||
mnemonicSeedPhraseProvider: .live,
|
||||
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
walletStorage: .live(),
|
||||
wrappedDerivationTool: .live(),
|
||||
wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer()
|
||||
derivationTool: .live(),
|
||||
SDKSynchronizer: LiveWrappedSDKSynchronizer()
|
||||
)
|
||||
)
|
||||
)
|
|
@ -2,7 +2,7 @@ import SwiftUI
|
|||
import ComposableArchitecture
|
||||
|
||||
struct CreateTransaction: View {
|
||||
let store: SendStore
|
||||
let store: SendFlowStore
|
||||
|
||||
var body: some View {
|
||||
UITextView.appearance().backgroundColor = .clear
|
||||
|
@ -21,7 +21,7 @@ struct CreateTransaction: View {
|
|||
TransactionAmountTextField(
|
||||
store: store.scope(
|
||||
state: \.transactionAmountInputState,
|
||||
action: SendAction.transactionAmountInput
|
||||
action: SendFlowAction.transactionAmountInput
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -49,7 +49,7 @@ struct CreateTransaction: View {
|
|||
TransactionAddressTextField(
|
||||
store: store.scope(
|
||||
state: \.transactionAddressInputState,
|
||||
action: SendAction.transactionAddressInput
|
||||
action: SendFlowAction.transactionAddressInput
|
||||
)
|
||||
)
|
||||
|
||||
|
@ -106,26 +106,3 @@ struct Create_Previews: PreviewProvider {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// #if DEBUG // FIX: Issue #306 - Release build is broken
|
||||
extension SendStore {
|
||||
static var placeholder: SendStore {
|
||||
return SendStore(
|
||||
initialState: .init(
|
||||
route: nil,
|
||||
transaction: .placeholder,
|
||||
transactionAddressInputState: .placeholder,
|
||||
transactionAmountInputState: .placeholder
|
||||
),
|
||||
reducer: .default,
|
||||
environment: SendEnvironment(
|
||||
mnemonicSeedPhraseProvider: .live,
|
||||
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
walletStorage: .live(),
|
||||
wrappedDerivationTool: .live(),
|
||||
wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
// #endif
|
|
@ -2,7 +2,7 @@ import SwiftUI
|
|||
import ComposableArchitecture
|
||||
|
||||
struct TransactionConfirmation: View {
|
||||
let viewStore: SendViewStore
|
||||
let viewStore: SendFlowViewStore
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
|
@ -36,12 +36,14 @@ struct TransactionConfirmation: View {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct Confirmation_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NavigationView {
|
||||
StateContainer(
|
||||
initialState: (
|
||||
Transaction.placeholder,
|
||||
SendFlowTransaction.placeholder,
|
||||
false
|
||||
)
|
||||
) { _ in
|
|
@ -2,7 +2,7 @@ import SwiftUI
|
|||
import ComposableArchitecture
|
||||
|
||||
struct TransactionFailed: View {
|
||||
let viewStore: SendViewStore
|
||||
let viewStore: SendFlowViewStore
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
|
@ -25,6 +25,8 @@ struct TransactionFailed: View {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct TransactionFailed_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
TransactionFailed(viewStore: ViewStore(.placeholder))
|
|
@ -2,7 +2,7 @@ import SwiftUI
|
|||
import ComposableArchitecture
|
||||
|
||||
struct TransactionSent: View {
|
||||
let viewStore: SendViewStore
|
||||
let viewStore: SendFlowViewStore
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
|
@ -27,6 +27,8 @@ struct TransactionSent: View {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct TransactionSent_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
TransactionSent(viewStore: ViewStore(.placeholder))
|
|
@ -1,18 +1,25 @@
|
|||
import ComposableArchitecture
|
||||
|
||||
typealias SettingsReducer = Reducer<SettingsState, SettingsAction, SettingsEnvironment>
|
||||
typealias SettingsStore = Store<SettingsState, SettingsAction>
|
||||
typealias SettingsViewStore = ViewStore<SettingsState, SettingsAction>
|
||||
|
||||
// MARK: - State
|
||||
|
||||
struct SettingsState: Equatable {
|
||||
}
|
||||
|
||||
// MARK: - Action
|
||||
|
||||
enum SettingsAction: Equatable {
|
||||
case noOp
|
||||
}
|
||||
|
||||
struct SettingsEnvironment: Equatable {
|
||||
}
|
||||
// MARK: - Environment
|
||||
|
||||
// MARK: - SettingsStateReducer
|
||||
struct SettingsEnvironment: Equatable { }
|
||||
|
||||
typealias SettingsReducer = Reducer<SettingsState, SettingsAction, SettingsEnvironment>
|
||||
// MARK: - Reducer
|
||||
|
||||
extension SettingsReducer {
|
||||
static let `default` = SettingsReducer { _, action, _ in
|
||||
|
@ -22,17 +29,3 @@ extension SettingsReducer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - SettingsStore
|
||||
|
||||
typealias SettingsStore = Store<SettingsState, SettingsAction>
|
||||
|
||||
extension SettingsStore {
|
||||
}
|
||||
|
||||
// MARK: - SettingsViewStore
|
||||
|
||||
typealias SettingsViewStore = ViewStore<SettingsState, SettingsAction>
|
||||
|
||||
extension SettingsViewStore {
|
||||
}
|
|
@ -6,6 +6,8 @@ struct SettingsView: View {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct SettingsView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
SettingsView()
|
|
@ -1,18 +1,13 @@
|
|||
import ComposableArchitecture
|
||||
import SwiftUI
|
||||
|
||||
extension Date {
|
||||
func asHumanReadable() -> String {
|
||||
let dateFormatter = DateFormatter()
|
||||
typealias TransactionHistoryFlowReducer = Reducer<TransactionHistoryFlowState, TransactionHistoryFlowAction, TransactionHistoryFlowEnvironment>
|
||||
typealias TransactionHistoryFlowStore = Store<TransactionHistoryFlowState, TransactionHistoryFlowAction>
|
||||
typealias TransactionHistoryFlowViewStore = ViewStore<TransactionHistoryFlowState, TransactionHistoryFlowAction>
|
||||
|
||||
dateFormatter.dateStyle = .short
|
||||
dateFormatter.timeStyle = .short
|
||||
// MARK: - State
|
||||
|
||||
return dateFormatter.string(from: self)
|
||||
}
|
||||
}
|
||||
|
||||
struct TransactionHistoryState: Equatable {
|
||||
struct TransactionHistoryFlowState: Equatable {
|
||||
enum Route: Equatable {
|
||||
case latest
|
||||
case all
|
||||
|
@ -25,31 +20,33 @@ struct TransactionHistoryState: Equatable {
|
|||
var transactions: IdentifiedArrayOf<TransactionState>
|
||||
}
|
||||
|
||||
enum TransactionHistoryAction: Equatable {
|
||||
// MARK: - Action
|
||||
|
||||
enum TransactionHistoryFlowAction: Equatable {
|
||||
case onAppear
|
||||
case onDisappear
|
||||
case updateRoute(TransactionHistoryState.Route?)
|
||||
case updateRoute(TransactionHistoryFlowState.Route?)
|
||||
case synchronizerStateChanged(WrappedSDKSynchronizerState)
|
||||
case updateTransactions([TransactionState])
|
||||
}
|
||||
|
||||
struct TransactionHistoryEnvironment {
|
||||
// MARK: - Environment
|
||||
|
||||
struct TransactionHistoryFlowEnvironment {
|
||||
let scheduler: AnySchedulerOf<DispatchQueue>
|
||||
let wrappedSDKSynchronizer: WrappedSDKSynchronizer
|
||||
let SDKSynchronizer: WrappedSDKSynchronizer
|
||||
}
|
||||
|
||||
// MARK: - TransactionHistoryReducer
|
||||
// MARK: - Reducer
|
||||
|
||||
extension TransactionHistoryFlowReducer {
|
||||
private struct ListenerId: Hashable {}
|
||||
|
||||
typealias TransactionHistoryReducer = Reducer<TransactionHistoryState, TransactionHistoryAction, TransactionHistoryEnvironment>
|
||||
|
||||
extension TransactionHistoryReducer {
|
||||
static let `default` = TransactionHistoryReducer { state, action, environment in
|
||||
static let `default` = TransactionHistoryFlowReducer { state, action, environment in
|
||||
switch action {
|
||||
case .onAppear:
|
||||
return environment.wrappedSDKSynchronizer.stateChanged
|
||||
.map(TransactionHistoryAction.synchronizerStateChanged)
|
||||
return environment.SDKSynchronizer.stateChanged
|
||||
.map(TransactionHistoryFlowAction.synchronizerStateChanged)
|
||||
.eraseToEffect()
|
||||
.cancellable(id: ListenerId(), cancelInFlight: true)
|
||||
|
||||
|
@ -57,9 +54,9 @@ extension TransactionHistoryReducer {
|
|||
return Effect.cancel(id: ListenerId())
|
||||
|
||||
case .synchronizerStateChanged(.synced):
|
||||
return environment.wrappedSDKSynchronizer.getAllTransactions()
|
||||
return environment.SDKSynchronizer.getAllTransactions()
|
||||
.receive(on: environment.scheduler)
|
||||
.map(TransactionHistoryAction.updateTransactions)
|
||||
.map(TransactionHistoryFlowAction.updateTransactions)
|
||||
.eraseToEffect()
|
||||
|
||||
case .synchronizerStateChanged(let synchronizerState):
|
||||
|
@ -80,28 +77,22 @@ extension TransactionHistoryReducer {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - TransactionHistoryStore
|
||||
// MARK: - ViewStore
|
||||
|
||||
typealias TransactionHistoryStore = Store<TransactionHistoryState, TransactionHistoryAction>
|
||||
|
||||
// MARK: - TransactionHistoryViewStore
|
||||
|
||||
typealias TransactionHistoryViewStore = ViewStore<TransactionHistoryState, TransactionHistoryAction>
|
||||
|
||||
extension TransactionHistoryViewStore {
|
||||
private typealias Route = TransactionHistoryState.Route
|
||||
extension TransactionHistoryFlowViewStore {
|
||||
private typealias Route = TransactionHistoryFlowState.Route
|
||||
|
||||
func bindingForSelectingTransaction(_ transaction: TransactionState) -> Binding<Bool> {
|
||||
self.binding(
|
||||
get: { $0.route.map(/TransactionHistoryState.Route.showTransaction) == transaction },
|
||||
get: { $0.route.map(/TransactionHistoryFlowState.Route.showTransaction) == transaction },
|
||||
send: { isActive in
|
||||
TransactionHistoryAction.updateRoute( isActive ? TransactionHistoryState.Route.showTransaction(transaction) : nil)
|
||||
TransactionHistoryFlowAction.updateRoute( isActive ? TransactionHistoryFlowState.Route.showTransaction(transaction) : nil)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: PlaceHolders
|
||||
// MARK: Placeholders
|
||||
|
||||
extension TransactionState {
|
||||
static var placeholder: Self {
|
||||
|
@ -115,7 +106,7 @@ extension TransactionState {
|
|||
}
|
||||
}
|
||||
|
||||
extension TransactionHistoryState {
|
||||
extension TransactionHistoryFlowState {
|
||||
static var placeHolder: Self {
|
||||
.init(transactions: .placeholder)
|
||||
}
|
||||
|
@ -125,29 +116,29 @@ extension TransactionHistoryState {
|
|||
}
|
||||
}
|
||||
|
||||
extension TransactionHistoryStore {
|
||||
static var placeholder: Store<TransactionHistoryState, TransactionHistoryAction> {
|
||||
extension TransactionHistoryFlowStore {
|
||||
static var placeholder: Store<TransactionHistoryFlowState, TransactionHistoryFlowAction> {
|
||||
return Store(
|
||||
initialState: .placeHolder,
|
||||
reducer: .default,
|
||||
environment: TransactionHistoryEnvironment(
|
||||
environment: TransactionHistoryFlowEnvironment(
|
||||
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer()
|
||||
SDKSynchronizer: LiveWrappedSDKSynchronizer()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
static var demoWithSelectedTransaction: Store<TransactionHistoryState, TransactionHistoryAction> {
|
||||
static var demoWithSelectedTransaction: Store<TransactionHistoryFlowState, TransactionHistoryFlowAction> {
|
||||
let transactions = IdentifiedArrayOf<TransactionState>.placeholder
|
||||
return Store(
|
||||
initialState: TransactionHistoryState(
|
||||
initialState: TransactionHistoryFlowState(
|
||||
route: .showTransaction(transactions[3]),
|
||||
transactions: transactions
|
||||
),
|
||||
reducer: .default.debug(),
|
||||
environment: TransactionHistoryEnvironment(
|
||||
environment: TransactionHistoryFlowEnvironment(
|
||||
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
wrappedSDKSynchronizer: LiveWrappedSDKSynchronizer()
|
||||
SDKSynchronizer: LiveWrappedSDKSynchronizer()
|
||||
)
|
||||
)
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
|
||||
struct TransactionHistoryView: View {
|
||||
let store: Store<TransactionHistoryState, TransactionHistoryAction>
|
||||
struct TransactionHistoryFlowView: View {
|
||||
let store: TransactionHistoryFlowStore
|
||||
|
||||
var body: some View {
|
||||
UITableView.appearance().backgroundColor = .clear
|
||||
|
@ -28,8 +28,8 @@ struct TransactionHistoryView: View {
|
|||
}
|
||||
}
|
||||
|
||||
extension TransactionHistoryView {
|
||||
func transactionsList(with viewStore: TransactionHistoryViewStore) -> some View {
|
||||
extension TransactionHistoryFlowView {
|
||||
func transactionsList(with viewStore: TransactionHistoryFlowViewStore) -> some View {
|
||||
ForEach(viewStore.transactions) { transaction in
|
||||
WithStateBinding(binding: viewStore.bindingForSelectingTransaction(transaction)) { active in
|
||||
VStack {
|
||||
|
@ -65,7 +65,7 @@ extension TransactionHistoryView {
|
|||
}
|
||||
}
|
||||
|
||||
func header(with viewStore: TransactionHistoryViewStore) -> some View {
|
||||
func header(with viewStore: TransactionHistoryFlowViewStore) -> some View {
|
||||
HStack(spacing: 0) {
|
||||
VStack {
|
||||
Button("Latest") {
|
||||
|
@ -90,10 +90,12 @@ extension TransactionHistoryView {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct TransactionView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NavigationView {
|
||||
TransactionHistoryView(store: .placeholder)
|
||||
TransactionHistoryFlowView(store: .placeholder)
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
||||
}
|
|
@ -9,6 +9,8 @@ struct TransactionDetailView: View {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct TransactionDetail_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NavigationView {
|
|
@ -1,18 +1,26 @@
|
|||
import ComposableArchitecture
|
||||
|
||||
typealias WalletInfoReducer = Reducer<WalletInfoState, WalletInfoAction, WalletInfoEnvironment>
|
||||
typealias WalletInfoStore = Store<WalletInfoState, WalletInfoAction>
|
||||
typealias WalletInfoViewStore = ViewStore<WalletInfoState, WalletInfoAction>
|
||||
|
||||
// MARK: - State
|
||||
|
||||
struct WalletInfoState: Equatable {
|
||||
}
|
||||
|
||||
// MARK: - Action
|
||||
|
||||
enum WalletInfoAction: Equatable {
|
||||
case noOp
|
||||
}
|
||||
|
||||
// MARK: - Environment
|
||||
|
||||
struct WalletInfoEnvironment: Equatable {
|
||||
}
|
||||
|
||||
// MARK: - WalletInfoReducer
|
||||
|
||||
typealias WalletInfoReducer = Reducer<WalletInfoState, WalletInfoAction, WalletInfoEnvironment>
|
||||
// MARK: - Reducer
|
||||
|
||||
extension WalletInfoReducer {
|
||||
static let `default` = WalletInfoReducer { _, action, _ in
|
||||
|
@ -22,17 +30,3 @@ extension WalletInfoReducer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - WalletInfoStore
|
||||
|
||||
typealias WalletInfoStore = Store<WalletInfoState, WalletInfoAction>
|
||||
|
||||
extension WalletInfoStore {
|
||||
}
|
||||
|
||||
// MARK: - WalletInfoViewStore
|
||||
|
||||
typealias WalletInfoViewStore = ViewStore<WalletInfoState, WalletInfoAction>
|
||||
|
||||
extension WalletInfoViewStore {
|
||||
}
|
|
@ -6,6 +6,8 @@ struct WalletInfoView: View {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Previews
|
||||
|
||||
struct WalletInfoView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
WalletInfoView()
|
|
@ -8,17 +8,25 @@
|
|||
import Foundation
|
||||
import ComposableArchitecture
|
||||
|
||||
typealias WelcomeReducer = Reducer<WelcomeState, WelcomeAction, WelcomeEnvironment>
|
||||
typealias WelcomeStore = Store<WelcomeState, WelcomeAction>
|
||||
typealias WelcomeViewStore = ViewStore<WelcomeState, WelcomeAction>
|
||||
|
||||
// MARK: - State
|
||||
|
||||
struct WelcomeState: Equatable {}
|
||||
|
||||
extension WelcomeState {
|
||||
static let placeholder = WelcomeState()
|
||||
}
|
||||
// MARK: - Action
|
||||
|
||||
enum WelcomeAction: Equatable {
|
||||
case debugMenuStartup
|
||||
}
|
||||
|
||||
typealias WelcomeReducer = Reducer<WelcomeState, WelcomeAction, Void>
|
||||
// MARK: - Environment
|
||||
|
||||
struct WelcomeEnvironment { }
|
||||
|
||||
// MARK: - Reducer
|
||||
|
||||
extension WelcomeReducer {
|
||||
static let `default` = WelcomeReducer { _, _, _ in
|
||||
|
@ -26,12 +34,18 @@ extension WelcomeReducer {
|
|||
}
|
||||
}
|
||||
|
||||
typealias WelcomeStore = Store<WelcomeState, WelcomeAction>
|
||||
// MARK: - Store
|
||||
|
||||
extension WelcomeStore {
|
||||
static var demo = WelcomeStore(
|
||||
initialState: .placeholder,
|
||||
reducer: .default,
|
||||
environment: ()
|
||||
environment: WelcomeEnvironment()
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Placeholders
|
||||
|
||||
extension WelcomeState {
|
||||
static let placeholder = WelcomeState()
|
||||
}
|
|
@ -41,57 +41,7 @@ struct WelcomeView: View {
|
|||
}
|
||||
}
|
||||
|
||||
struct ZcashBadge: View {
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
GeometryReader { proxy in
|
||||
let outterPadding = proxy.size.height * 0.015
|
||||
let firstPadding = proxy.size.height * 0.075 + outterPadding
|
||||
let secondRingPadding = firstPadding * 1.5
|
||||
let outerShadowDrop = proxy.size.height * 0.14
|
||||
let outerShadowOffset = proxy.size.height * 0.055
|
||||
|
||||
Circle()
|
||||
.fill(
|
||||
LinearGradient(
|
||||
colors: [
|
||||
Asset.Colors.ZcashBadge.outerRingGradientStart.color,
|
||||
Asset.Colors.ZcashBadge.outerRingGradientEnd.color
|
||||
],
|
||||
startPoint: UnitPoint(x: 0.5, y: 0),
|
||||
endPoint: UnitPoint(x: 0.5, y: 1)
|
||||
)
|
||||
)
|
||||
.if(colorScheme == .light) { view in
|
||||
view.shadow(
|
||||
color: Asset.Colors.ZcashBadge.shadowColor.color,
|
||||
radius: outerShadowDrop,
|
||||
x: outerShadowOffset,
|
||||
y: outerShadowOffset
|
||||
)
|
||||
}
|
||||
|
||||
Circle()
|
||||
.foregroundColor(Asset.Colors.ZcashBadge.thickRing.color)
|
||||
.padding(outterPadding)
|
||||
|
||||
Circle()
|
||||
.foregroundColor(Asset.Colors.ZcashBadge.thinRing.color)
|
||||
.padding(firstPadding)
|
||||
|
||||
Circle()
|
||||
.foregroundColor(Asset.Colors.ZcashBadge.innerCircle.color)
|
||||
.padding(secondRingPadding)
|
||||
|
||||
ZcashSymbol()
|
||||
.fill(Asset.Colors.ZcashBadge.zcashLogoFill.color)
|
||||
.padding(firstPadding + secondRingPadding)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// MARK: - Previews
|
||||
|
||||
struct WelcomeView_Previews: PreviewProvider {
|
||||
static let squarePreviewSize: CGFloat = 360
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
//
|
||||
// Services.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/6/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ZcashLightClientKit
|
||||
|
||||
protocol Services {
|
||||
var networkProvider: ZcashNetworkProvider { get }
|
||||
var seedHandler: MnemonicSeedPhraseProvider { get }
|
||||
var walletStorage: WalletStorageInteractor { get }
|
||||
}
|
||||
|
||||
protocol ZcashNetworkProvider {
|
||||
func currentNetwork() -> ZcashNetwork
|
||||
}
|
|
@ -1,108 +0,0 @@
|
|||
//
|
||||
// Balance.swift
|
||||
// secant
|
||||
//
|
||||
// Created by Francisco Gindre on 8/9/21.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/**
|
||||
Funds Expressed in Zatoshis
|
||||
*/
|
||||
protocol Funds {
|
||||
/**
|
||||
Confirmed, spendable funds
|
||||
*/
|
||||
var confirmed: Int64 { get }
|
||||
|
||||
/**
|
||||
Unconfirmed, not yet spendable funds.
|
||||
*/
|
||||
var unconfirmed: Int64 { get }
|
||||
|
||||
/**
|
||||
Represents a null value of Funds with Zero confirmed and Zero unconfirmed funds.
|
||||
*/
|
||||
static var noFunds: Funds { get }
|
||||
}
|
||||
|
||||
/**
|
||||
Wallet Balance that condenses transparent, Sapling and Orchard funds
|
||||
*/
|
||||
protocol WalletBalance {
|
||||
/**
|
||||
Transparent funds. This is the sum of the UTXOs of the user found at a given time
|
||||
*/
|
||||
var transparent: Funds { get }
|
||||
|
||||
/**
|
||||
Funds on the Sapling shielded pool for a given user.
|
||||
*/
|
||||
var sapling: Funds { get }
|
||||
|
||||
/**
|
||||
Funds on the Orchard shielded pool for a given user.
|
||||
*/
|
||||
var orchard: Funds { get }
|
||||
|
||||
/**
|
||||
The sum of all confirmed funds, transparent, sapling and orchard funds (calculated)
|
||||
*/
|
||||
var totalAvailableBalance: Int64 { get }
|
||||
|
||||
/**
|
||||
The sum of all unconfirmed funds: transparent, sapling, and orchard funds (calculated
|
||||
*/
|
||||
var totalUnconfirmedBalance: Int64 { get }
|
||||
|
||||
/**
|
||||
The sum of all funds confirmed and unconfirmed of all pools (transparent, sapling and orchard).
|
||||
*/
|
||||
var totalBalance: Int64 { get }
|
||||
|
||||
/**
|
||||
Represents a the value of Zero funds.
|
||||
*/
|
||||
static var nullBalance: WalletBalance { get }
|
||||
}
|
||||
|
||||
extension WalletBalance {
|
||||
static var nullBalance: WalletBalance {
|
||||
Balance(
|
||||
transparent: ZcashFunds.noFunds,
|
||||
sapling: ZcashFunds.noFunds,
|
||||
orchard: ZcashFunds.noFunds
|
||||
)
|
||||
}
|
||||
|
||||
var totalAvailableBalance: Int64 {
|
||||
transparent.confirmed + sapling.confirmed + orchard.confirmed
|
||||
}
|
||||
|
||||
var totalUnconfirmedBalance: Int64 {
|
||||
transparent.unconfirmed + sapling.unconfirmed + orchard.unconfirmed
|
||||
}
|
||||
|
||||
var totalBalance: Int64 {
|
||||
totalAvailableBalance + totalUnconfirmedBalance
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Concrete Wallet Balance.
|
||||
*/
|
||||
struct Balance: WalletBalance {
|
||||
var transparent: Funds
|
||||
var sapling: Funds
|
||||
var orchard: Funds
|
||||
}
|
||||
|
||||
struct ZcashFunds: Funds {
|
||||
static var noFunds: Funds {
|
||||
ZcashFunds(confirmed: 0, unconfirmed: 0)
|
||||
}
|
||||
|
||||
var confirmed: Int64
|
||||
var unconfirmed: Int64
|
||||
}
|
|
@ -36,7 +36,7 @@ struct WordChipDropDelegate: DropDelegate {
|
|||
}
|
||||
}
|
||||
|
||||
extension RecoveryPhraseValidationState {
|
||||
extension RecoveryPhraseValidationFlowState {
|
||||
func groupCompleted(index: Int) -> Bool {
|
||||
validationWords.first(where: { $0.groupIndex == index }) != nil
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
//
|
||||
// RecoveryPhrase.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 12.05.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum RecoveryPhraseError: Error {
|
||||
/// This error is thrown then the Recovery Phrase can't be generated
|
||||
case unableToGeneratePhrase
|
||||
}
|
||||
|
||||
struct RecoveryPhrase: Equatable {
|
||||
struct Group: Hashable {
|
||||
var startIndex: Int
|
||||
var words: [String]
|
||||
}
|
||||
|
||||
let words: [String]
|
||||
|
||||
private let groupSize = 6
|
||||
|
||||
func toGroups() -> [Group] {
|
||||
let chunks = words.count / groupSize
|
||||
return zip(0 ..< chunks, words.chunked(into: groupSize)).map {
|
||||
Group(startIndex: $0 * groupSize + 1, words: $1)
|
||||
}
|
||||
}
|
||||
|
||||
func toString() -> String {
|
||||
words.joined(separator: " ")
|
||||
}
|
||||
|
||||
func words(fromMissingIndices indices: [Int]) -> [PhraseChip.Kind] {
|
||||
assert((indices.count - 1) * groupSize <= self.words.count)
|
||||
|
||||
return indices.enumerated().map { index, position in
|
||||
.unassigned(word: self.words[(index * groupSize) + position])
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// Transaction.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 12.05.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Simple model that holds data throughout the `SendFlow` feature
|
||||
struct SendFlowTransaction: Equatable {
|
||||
var amount: Int64
|
||||
var memo: String
|
||||
var toAddress: String
|
||||
}
|
||||
|
||||
extension SendFlowTransaction {
|
||||
static var placeholder: Self {
|
||||
.init(
|
||||
amount: 0,
|
||||
memo: "",
|
||||
toAddress: ""
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
//
|
||||
// StoredWallet.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 13.05.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ZcashLightClientKit
|
||||
import MnemonicSwift
|
||||
|
||||
/// Representation of the wallet stored in the persistent storage (typically keychain, handled by `WalletStorage`).
|
||||
struct StoredWallet: Codable, Equatable {
|
||||
let language: MnemonicLanguageType
|
||||
let seedPhrase: String
|
||||
let version: Int
|
||||
|
||||
var birthday: BlockHeight?
|
||||
var hasUserPassedPhraseBackupTest: Bool
|
||||
}
|
|
@ -8,6 +8,7 @@
|
|||
import Foundation
|
||||
import ZcashLightClientKit
|
||||
|
||||
/// Representation of the transaction on the SDK side, used as a bridge to the TCA wallet side.
|
||||
struct TransactionState: Equatable, Identifiable {
|
||||
enum Status: Equatable {
|
||||
case paid(success: Bool)
|
|
@ -0,0 +1,15 @@
|
|||
//
|
||||
// ValidationWord.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 12.05.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
/// Represents the data of a word that has been placed into an empty position, that will be used
|
||||
/// to validate the completed phrase when all ValidationWords have been placed.
|
||||
struct ValidationWord: Equatable {
|
||||
var groupIndex: Int
|
||||
var word: String
|
||||
}
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 8.9 KiB |
Before Width: | Height: | Size: 288 KiB After Width: | Height: | Size: 288 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 699 B After Width: | Height: | Size: 699 B |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 21 KiB |
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
Before Width: | Height: | Size: 998 B After Width: | Height: | Size: 998 B |
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 34 KiB |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 3.6 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
Before Width: | Height: | Size: 4.3 KiB After Width: | Height: | Size: 4.3 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.5 KiB |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 4.8 KiB |
Before Width: | Height: | Size: 5.1 KiB After Width: | Height: | Size: 5.1 KiB |
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 6.4 KiB After Width: | Height: | Size: 6.4 KiB |
Before Width: | Height: | Size: 6.9 KiB After Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 7.5 KiB After Width: | Height: | Size: 7.5 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |
Before Width: | Height: | Size: 138 KiB After Width: | Height: | Size: 138 KiB |