- qr code generator - qr, address and initial buttons in place [73] [Scaffold] Profile Screen - address details view placeholder - navigation to address details view - custom back - app build + version from environment [73] [Scaffold] Profile Screen - unit tests [73] [Scaffold] Profile Screen - snapshot of the profile view [73] [Scaffold] Profile Screen (386) - comments resolved - AppVersionHandler implemented - sdkVersion also in the UI - updated profile view design according to MVP requirements [73] [Scaffold] Profile Screen (386) - AppVersionHandler added to the repo
This commit is contained in:
parent
bf6e9bfbb0
commit
59650d723d
|
@ -121,6 +121,12 @@
|
|||
9E5BF6502823E94900BA3F17 /* TransactionAddressTextFieldStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF64E2823E94900BA3F17 /* TransactionAddressTextFieldStore.swift */; };
|
||||
9E69A24D27FB002800A55317 /* WelcomeStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E69A24C27FB002800A55317 /* WelcomeStore.swift */; };
|
||||
9E7CB6122869882D00A02233 /* WalletEventsSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7CB6112869882D00A02233 /* WalletEventsSnapshotTests.swift */; };
|
||||
9E7CB61A287310EC00A02233 /* QRCodeGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7CB619287310EC00A02233 /* QRCodeGenerator.swift */; };
|
||||
9E7CB6202874143800A02233 /* AddressDetailsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7CB61F2874143800A02233 /* AddressDetailsStore.swift */; };
|
||||
9E7CB6212874143800A02233 /* AddressDetailsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7CB61E2874143800A02233 /* AddressDetailsView.swift */; };
|
||||
9E7CB6242874246800A02233 /* ProfileTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7CB6232874246800A02233 /* ProfileTests.swift */; };
|
||||
9E7CB6272874269F00A02233 /* ProfileSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7CB6262874269F00A02233 /* ProfileSnapshotTests.swift */; };
|
||||
9E7CB6292875AC2D00A02233 /* AppVersionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7CB6282875AC2D00A02233 /* AppVersionHandler.swift */; };
|
||||
9E7FE0CF282D257400C374E8 /* SDKSynchronizer+SyncStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0CE282D257400C374E8 /* SDKSynchronizer+SyncStatus.swift */; };
|
||||
9E7FE0D3282D274E00C374E8 /* Date+Readable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0D2282D274E00C374E8 /* Date+Readable.swift */; };
|
||||
9E7FE0D5282D281800C374E8 /* Array+Chunked.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7FE0D4282D281800C374E8 /* Array+Chunked.swift */; };
|
||||
|
@ -331,6 +337,12 @@
|
|||
9E5BF64E2823E94900BA3F17 /* TransactionAddressTextFieldStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAddressTextFieldStore.swift; sourceTree = "<group>"; };
|
||||
9E69A24C27FB002800A55317 /* WelcomeStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeStore.swift; sourceTree = "<group>"; };
|
||||
9E7CB6112869882D00A02233 /* WalletEventsSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletEventsSnapshotTests.swift; sourceTree = "<group>"; };
|
||||
9E7CB619287310EC00A02233 /* QRCodeGenerator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCodeGenerator.swift; sourceTree = "<group>"; };
|
||||
9E7CB61E2874143800A02233 /* AddressDetailsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressDetailsView.swift; sourceTree = "<group>"; };
|
||||
9E7CB61F2874143800A02233 /* AddressDetailsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressDetailsStore.swift; sourceTree = "<group>"; };
|
||||
9E7CB6232874246800A02233 /* ProfileTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileTests.swift; sourceTree = "<group>"; };
|
||||
9E7CB6262874269F00A02233 /* ProfileSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileSnapshotTests.swift; sourceTree = "<group>"; };
|
||||
9E7CB6282875AC2D00A02233 /* AppVersionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersionHandler.swift; sourceTree = "<group>"; };
|
||||
9E7FE0CE282D257400C374E8 /* SDKSynchronizer+SyncStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SDKSynchronizer+SyncStatus.swift"; sourceTree = "<group>"; };
|
||||
9E7FE0D2282D274E00C374E8 /* Date+Readable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Readable.swift"; sourceTree = "<group>"; };
|
||||
9E7FE0D4282D281800C374E8 /* Array+Chunked.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Chunked.swift"; sourceTree = "<group>"; };
|
||||
|
@ -516,8 +528,9 @@
|
|||
0D4E7A1926B364180058B01E /* secantTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9EAB4674285B5C68002904A0 /* DeeplinkTests */,
|
||||
9E391162284E3ECF0073DD9A /* SnapshotTests */,
|
||||
9E7CB6222874245400A02233 /* ProfileTests */,
|
||||
9EAB4674285B5C68002904A0 /* DeeplinkTests */,
|
||||
9E3911372848AD3A0073DD9A /* HomeTests */,
|
||||
9E391122283E4C970073DD9A /* ImportWalletTests */,
|
||||
9E01F8262833CD84000EFC57 /* ScanTests */,
|
||||
|
@ -676,6 +689,7 @@
|
|||
F9971A4927680DC400A2DB75 /* App */,
|
||||
F93874EC273C4DE200F0E875 /* Home */,
|
||||
F9971A4F27680DD000A2DB75 /* Profile */,
|
||||
9E7CB61B2874140900A02233 /* AddressDetails */,
|
||||
F9971A5527680DDE00A2DB75 /* Request */,
|
||||
F9971A5B27680DF600A2DB75 /* Scan */,
|
||||
F9C165B62740403600592F76 /* SendFlow */,
|
||||
|
@ -812,6 +826,7 @@
|
|||
9E391162284E3ECF0073DD9A /* SnapshotTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9E7CB6252874267B00A02233 /* ProfileSnapshotTests */,
|
||||
9E7CB6102869881300A02233 /* WalletEventsSnapshotTests */,
|
||||
9E9ECC8B28589E150099D5A2 /* HomeSnapshotTests */,
|
||||
9E9ECC9328589E150099D5A2 /* ImportWalletSnapshotTests */,
|
||||
|
@ -869,6 +884,31 @@
|
|||
path = WalletEventsSnapshotTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9E7CB61B2874140900A02233 /* AddressDetails */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9E7CB61F2874143800A02233 /* AddressDetailsStore.swift */,
|
||||
9E7CB61E2874143800A02233 /* AddressDetailsView.swift */,
|
||||
);
|
||||
path = AddressDetails;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9E7CB6222874245400A02233 /* ProfileTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9E7CB6232874246800A02233 /* ProfileTests.swift */,
|
||||
);
|
||||
path = ProfileTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9E7CB6252874267B00A02233 /* ProfileSnapshotTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9E7CB6262874269F00A02233 /* ProfileSnapshotTests.swift */,
|
||||
);
|
||||
path = ProfileSnapshotTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9E7FE0B6282D1D9800C374E8 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -908,6 +948,7 @@
|
|||
2EDA07A327EDE2A900D6F09B /* DebugFrame.swift */,
|
||||
9E2F1C832809B606004E65FE /* DebugMenu.swift */,
|
||||
9E391128283F74590073DD9A /* Zatoshi.swift */,
|
||||
9E7CB619287310EC00A02233 /* QRCodeGenerator.swift */,
|
||||
);
|
||||
path = Utils;
|
||||
sourceTree = "<group>";
|
||||
|
@ -922,6 +963,7 @@
|
|||
9E3911472848EEB90073DD9A /* WalletStorage.swift */,
|
||||
9E3911452848EEB90073DD9A /* ZCashSDKEnvironment.swift */,
|
||||
9EAB4670285A1C77002904A0 /* DeeplinkHandler.swift */,
|
||||
9E7CB6282875AC2D00A02233 /* AppVersionHandler.swift */,
|
||||
);
|
||||
path = Dependencies;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1423,6 +1465,7 @@
|
|||
F96B41E8273B501F0021B49A /* TransactionDetailView.swift in Sources */,
|
||||
9E02B56A27FED43E005B809B /* WrappedFileManager.swift in Sources */,
|
||||
663FABA2271D876C00E495F8 /* SecondaryButton.swift in Sources */,
|
||||
9E7CB6202874143800A02233 /* AddressDetailsStore.swift in Sources */,
|
||||
0DC487C32772574C00BE6A63 /* RecoveryPhraseBackupSucceededView.swift in Sources */,
|
||||
2EB1C5E827D77F6100BC43D7 /* TCATextFieldStore.swift in Sources */,
|
||||
9E5BF648282277BE00BA3F17 /* WrappedNotificationCenter.swift in Sources */,
|
||||
|
@ -1430,6 +1473,7 @@
|
|||
9E5BF641281FD7B600BA3F17 /* TransactionFailedView.swift in Sources */,
|
||||
9E7FE0CF282D257400C374E8 /* SDKSynchronizer+SyncStatus.swift in Sources */,
|
||||
9E4DC6E027C409A100E657F4 /* NeumorphicDesignModifier.swift in Sources */,
|
||||
9E7CB6292875AC2D00A02233 /* AppVersionHandler.swift in Sources */,
|
||||
0DACFA7F27208CE00039EEA5 /* Clamped.swift in Sources */,
|
||||
9EAB467A2861EA6A002904A0 /* TransactionRowView.swift in Sources */,
|
||||
0DFE93E3272CA1AA000FCCA5 /* RecoveryPhraseValidationFlowStore.swift in Sources */,
|
||||
|
@ -1487,6 +1531,7 @@
|
|||
9E7FE0E6282E7B1100C374E8 /* StoredWallet.swift in Sources */,
|
||||
9EAFEB9128081E9400199FC9 /* HomeStore.swift in Sources */,
|
||||
F9971A5A27680DDE00A2DB75 /* RequestView.swift in Sources */,
|
||||
9E7CB61A287310EC00A02233 /* QRCodeGenerator.swift in Sources */,
|
||||
9E7FE0D5282D281800C374E8 /* Array+Chunked.swift in Sources */,
|
||||
0DACFA8127208D940039EEA5 /* UInt+SuperscriptText.swift in Sources */,
|
||||
0DF2DC51272344E400FA31E2 /* EmptyChip.swift in Sources */,
|
||||
|
@ -1534,6 +1579,7 @@
|
|||
9EF8139C27F47AED0075AF48 /* InitializationState.swift in Sources */,
|
||||
0D0781C9278776D20083ACD7 /* ZcashSymbol.swift in Sources */,
|
||||
2E8719CB27FB09990082C926 /* TransactionAmountTextField.swift in Sources */,
|
||||
9E7CB6212874143800A02233 /* AddressDetailsView.swift in Sources */,
|
||||
F9C165C42740403600592F76 /* TransactionSentView.swift in Sources */,
|
||||
F9971A5927680DDE00A2DB75 /* RequestStore.swift in Sources */,
|
||||
);
|
||||
|
@ -1547,11 +1593,13 @@
|
|||
9EDDEAA22829610D00B4100C /* CurrencySelectionTests.swift in Sources */,
|
||||
9E01F8282833CDA0000EFC57 /* ScanTests.swift in Sources */,
|
||||
9EDDEAA42829610D00B4100C /* TransactionAddressInputTests.swift in Sources */,
|
||||
9E7CB6272874269F00A02233 /* ProfileSnapshotTests.swift in Sources */,
|
||||
9E92AF0828530EBF007367AD /* View+UIImage.swift in Sources */,
|
||||
6654C7442715A4AC00901167 /* OnboardingStoreTests.swift in Sources */,
|
||||
9E39112E283F91600073DD9A /* ZatoshiTests.swift in Sources */,
|
||||
9E9ECC9B28589E150099D5A2 /* ImportWalletSnapshotTests.swift in Sources */,
|
||||
9EDDEAA32829610D00B4100C /* TransactionAmountInputTests.swift in Sources */,
|
||||
9E7CB6242874246800A02233 /* ProfileTests.swift in Sources */,
|
||||
9EAFEB862805A23100199FC9 /* WrappedSecItemTests.swift in Sources */,
|
||||
9E9ECC9828589E150099D5A2 /* WelcomeSnapshotTests.swift in Sources */,
|
||||
9E7CB6122869882D00A02233 /* WalletEventsSnapshotTests.swift in Sources */,
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// AppVersionHandler.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 06.07.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct AppVersionHandler {
|
||||
let appVersion: () -> String
|
||||
let appBuild: () -> String
|
||||
}
|
||||
|
||||
extension AppVersionHandler {
|
||||
static let live = AppVersionHandler(
|
||||
appVersion: { Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "" },
|
||||
appBuild: { Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "" }
|
||||
)
|
||||
|
||||
static let test = AppVersionHandler(
|
||||
appVersion: { "0.0.1" },
|
||||
appBuild: { "31" }
|
||||
)
|
||||
}
|
|
@ -26,6 +26,7 @@ struct ZCashSDKEnvironment {
|
|||
let mnemonicWordsMaxCount: Int
|
||||
let network: ZcashNetwork
|
||||
let requiredTransactionConfirmations: Int
|
||||
let sdkVersion: String
|
||||
}
|
||||
|
||||
extension ZCashSDKEnvironment {
|
||||
|
@ -38,7 +39,8 @@ extension ZCashSDKEnvironment {
|
|||
),
|
||||
mnemonicWordsMaxCount: ZcashSDKConstants.mnemonicWordsMaxCount,
|
||||
network: ZcashNetworkBuilder.network(for: .mainnet),
|
||||
requiredTransactionConfirmations: ZcashSDKConstants.requiredTransactionConfirmations
|
||||
requiredTransactionConfirmations: ZcashSDKConstants.requiredTransactionConfirmations,
|
||||
sdkVersion: "0.14.0-beta"
|
||||
)
|
||||
|
||||
static let testnet = ZCashSDKEnvironment(
|
||||
|
@ -50,6 +52,7 @@ extension ZCashSDKEnvironment {
|
|||
),
|
||||
mnemonicWordsMaxCount: ZcashSDKConstants.mnemonicWordsMaxCount,
|
||||
network: ZcashNetworkBuilder.network(for: .testnet),
|
||||
requiredTransactionConfirmations: ZcashSDKConstants.requiredTransactionConfirmations
|
||||
requiredTransactionConfirmations: ZcashSDKConstants.requiredTransactionConfirmations,
|
||||
sdkVersion: "0.14.0-beta"
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// AddressDetailsStore.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 05.07.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import ComposableArchitecture
|
||||
|
||||
typealias AddressDetailsReducer = Reducer<AddressDetailsState, AddressDetailsAction, AddressDetailsEnvironment>
|
||||
typealias AddressDetailsStore = Store<AddressDetailsState, AddressDetailsAction>
|
||||
typealias AddressDetailsViewStore = ViewStore<AddressDetailsState, AddressDetailsAction>
|
||||
|
||||
// MARK: - State
|
||||
|
||||
struct AddressDetailsState: Equatable {
|
||||
}
|
||||
|
||||
// MARK: - Action
|
||||
|
||||
enum AddressDetailsAction: Equatable {
|
||||
case copyToPastboard(String)
|
||||
}
|
||||
|
||||
// MARK: - Environment
|
||||
|
||||
struct AddressDetailsEnvironment {
|
||||
let pasteboard: WrappedPasteboard
|
||||
}
|
||||
|
||||
extension AddressDetailsEnvironment {
|
||||
static let live = AddressDetailsEnvironment(
|
||||
pasteboard: .live
|
||||
)
|
||||
|
||||
static let mock = AddressDetailsEnvironment(
|
||||
pasteboard: .test
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Reducer
|
||||
|
||||
extension AddressDetailsReducer {
|
||||
static let `default` = AddressDetailsReducer { _, action, environment in
|
||||
switch action {
|
||||
case .copyToPastboard(let value):
|
||||
environment.pasteboard.setString(value)
|
||||
}
|
||||
|
||||
return .none
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Store
|
||||
|
||||
extension AddressDetailsStore {
|
||||
}
|
||||
|
||||
// MARK: - ViewStore
|
||||
|
||||
extension AddressDetailsViewStore {
|
||||
}
|
||||
|
||||
// MARK: - Placeholders
|
||||
|
||||
extension AddressDetailsState {
|
||||
static let placeholder = AddressDetailsState(
|
||||
)
|
||||
}
|
||||
|
||||
extension AddressDetailsStore {
|
||||
static let placeholder = AddressDetailsStore(
|
||||
initialState: .placeholder,
|
||||
reducer: .default,
|
||||
environment: AddressDetailsEnvironment(
|
||||
pasteboard: .test
|
||||
)
|
||||
)
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
//
|
||||
// AddressDetailsView.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 05.07.2022.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
|
||||
struct AddressDetails: View {
|
||||
let store: AddressDetailsStore
|
||||
|
||||
var body: some View {
|
||||
WithViewStore(store) { _ in
|
||||
Text("Address Details")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AddressDetails_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
AddressDetails(store: .placeholder)
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ struct AppView: View {
|
|||
)
|
||||
)
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
.navigationViewStyle(.stack)
|
||||
|
||||
case .sandbox:
|
||||
NavigationView {
|
||||
|
@ -29,7 +29,7 @@ struct AppView: View {
|
|||
)
|
||||
)
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
.navigationViewStyle(.stack)
|
||||
|
||||
case .onboarding:
|
||||
NavigationView {
|
||||
|
@ -40,7 +40,7 @@ struct AppView: View {
|
|||
)
|
||||
)
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
.navigationViewStyle(.stack)
|
||||
|
||||
case .startup:
|
||||
ZStack(alignment: .topTrailing) {
|
||||
|
@ -57,7 +57,7 @@ struct AppView: View {
|
|||
)
|
||||
)
|
||||
}
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
.navigationViewStyle(.stack)
|
||||
|
||||
case .phraseDisplay:
|
||||
NavigationView {
|
||||
|
|
|
@ -144,6 +144,10 @@ extension HomeReducer {
|
|||
state.route = route
|
||||
return .none
|
||||
|
||||
case .profile(.back):
|
||||
state.route = nil
|
||||
return .none
|
||||
|
||||
case .profile(let action):
|
||||
return .none
|
||||
|
||||
|
@ -171,7 +175,7 @@ extension HomeReducer {
|
|||
|
||||
case .scan(let action):
|
||||
return .none
|
||||
|
||||
|
||||
case .debugMenuStartup:
|
||||
return .none
|
||||
}
|
||||
|
@ -222,8 +226,11 @@ extension HomeReducer {
|
|||
action: /HomeAction.profile,
|
||||
environment: { environment in
|
||||
ProfileEnvironment(
|
||||
appVersionHandler: .live,
|
||||
mnemonic: environment.mnemonic,
|
||||
walletStorage: environment.walletStorage
|
||||
SDKSynchronizer: environment.SDKSynchronizer,
|
||||
walletStorage: environment.walletStorage,
|
||||
zcashSDKEnvironment: environment.zcashSDKEnvironment
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -9,74 +9,97 @@ typealias ProfileViewStore = ViewStore<ProfileState, ProfileAction>
|
|||
|
||||
struct ProfileState: Equatable {
|
||||
enum Route {
|
||||
case phraseDisplay
|
||||
case addressDetails
|
||||
case settings
|
||||
case walletInfo
|
||||
}
|
||||
|
||||
var phraseDisplayState: RecoveryPhraseDisplayState
|
||||
var address = ""
|
||||
var addressDetailsState: AddressDetailsState
|
||||
var appBuild = ""
|
||||
var appVersion = ""
|
||||
var route: Route?
|
||||
var sdkVersion = ""
|
||||
var settingsState: SettingsState
|
||||
var walletInfoState: WalletInfoState
|
||||
}
|
||||
|
||||
// MARK: - Action
|
||||
|
||||
enum ProfileAction: Equatable {
|
||||
case phraseDisplay(RecoveryPhraseDisplayAction)
|
||||
case addressDetails(AddressDetailsAction)
|
||||
case back
|
||||
case onAppear
|
||||
case updateRoute(ProfileState.Route?)
|
||||
}
|
||||
|
||||
// MARK: - Environment
|
||||
|
||||
struct ProfileEnvironment {
|
||||
let appVersionHandler: AppVersionHandler
|
||||
let mnemonic: WrappedMnemonic
|
||||
let SDKSynchronizer: WrappedSDKSynchronizer
|
||||
let walletStorage: WrappedWalletStorage
|
||||
let zcashSDKEnvironment: ZCashSDKEnvironment
|
||||
}
|
||||
|
||||
extension ProfileEnvironment {
|
||||
static let live = ProfileEnvironment(
|
||||
appVersionHandler: .live,
|
||||
mnemonic: .live,
|
||||
walletStorage: .live()
|
||||
SDKSynchronizer: LiveWrappedSDKSynchronizer(),
|
||||
walletStorage: .live(),
|
||||
zcashSDKEnvironment: .mainnet
|
||||
)
|
||||
|
||||
static let mock = ProfileEnvironment(
|
||||
appVersionHandler: .test,
|
||||
mnemonic: .mock,
|
||||
walletStorage: .live()
|
||||
SDKSynchronizer: MockWrappedSDKSynchronizer(),
|
||||
walletStorage: .live(),
|
||||
zcashSDKEnvironment: .testnet
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Reducer
|
||||
|
||||
extension ProfileReducer {
|
||||
static let `default` = ProfileReducer { state, action, environment in
|
||||
switch action {
|
||||
case .updateRoute(.phraseDisplay):
|
||||
do {
|
||||
let storedWallet = try environment.walletStorage.exportWallet()
|
||||
let phraseWords = try environment.mnemonic.asWords(storedWallet.seedPhrase)
|
||||
|
||||
let recoveryPhrase = RecoveryPhrase(words: phraseWords)
|
||||
state.phraseDisplayState.phrase = recoveryPhrase
|
||||
state.route = .phraseDisplay
|
||||
} catch {
|
||||
// TODO: - merge with issue 201 (https://github.com/zcash/secant-ios-wallet/issues/201) and its Error States
|
||||
return .none
|
||||
}
|
||||
return .none
|
||||
static let `default` = ProfileReducer.combine(
|
||||
[
|
||||
profileReducer,
|
||||
addressDetailsReducer
|
||||
]
|
||||
)
|
||||
.debug()
|
||||
|
||||
private static let profileReducer = ProfileReducer { state, action, environment in
|
||||
switch action {
|
||||
case .onAppear:
|
||||
state.address = environment.SDKSynchronizer.getShieldedAddress() ?? ""
|
||||
state.appBuild = environment.appVersionHandler.appBuild()
|
||||
state.appVersion = environment.appVersionHandler.appVersion()
|
||||
state.sdkVersion = environment.zcashSDKEnvironment.sdkVersion
|
||||
return .none
|
||||
|
||||
case .back:
|
||||
return .none
|
||||
|
||||
case let .updateRoute(route):
|
||||
state.route = route
|
||||
return .none
|
||||
|
||||
case .phraseDisplay(.finishedPressed):
|
||||
state.route = nil
|
||||
return .none
|
||||
|
||||
case .phraseDisplay:
|
||||
case .addressDetails:
|
||||
return .none
|
||||
}
|
||||
}
|
||||
|
||||
private static let addressDetailsReducer: ProfileReducer = AddressDetailsReducer.default.pullback(
|
||||
state: \ProfileState.addressDetailsState,
|
||||
action: /ProfileAction.addressDetails,
|
||||
environment: { _ in
|
||||
AddressDetailsEnvironment(
|
||||
pasteboard: .live
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - ViewStore
|
||||
|
@ -89,10 +112,10 @@ extension ProfileViewStore {
|
|||
)
|
||||
}
|
||||
|
||||
var bindingForWalletInfo: Binding<Bool> {
|
||||
var bindingForAddressDetails: Binding<Bool> {
|
||||
self.routeBinding.map(
|
||||
extract: { $0 == .walletInfo },
|
||||
embed: { $0 ? .walletInfo : nil }
|
||||
extract: { $0 == .addressDetails },
|
||||
embed: { $0 ? .addressDetails : nil }
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -102,13 +125,6 @@ extension ProfileViewStore {
|
|||
embed: { $0 ? .settings : nil }
|
||||
)
|
||||
}
|
||||
|
||||
var bindingForPhraseDisplay: Binding<Bool> {
|
||||
self.routeBinding.map(
|
||||
extract: { $0 == .phraseDisplay },
|
||||
embed: { $0 ? .phraseDisplay : nil }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Placeholders
|
||||
|
@ -116,10 +132,9 @@ extension ProfileViewStore {
|
|||
extension ProfileState {
|
||||
static var placeholder: Self {
|
||||
.init(
|
||||
phraseDisplayState: .init(),
|
||||
addressDetailsState: .placeholder,
|
||||
route: nil,
|
||||
settingsState: .init(),
|
||||
walletInfoState: .init()
|
||||
settingsState: .init()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,37 +6,101 @@ struct ProfileView: View {
|
|||
|
||||
var body: some View {
|
||||
WithViewStore(store) { viewStore in
|
||||
List {
|
||||
Text("Go To Wallet Info")
|
||||
.navigationLink(
|
||||
isActive: viewStore.bindingForWalletInfo,
|
||||
destination: {
|
||||
Text("Wallet")
|
||||
}
|
||||
)
|
||||
VStack {
|
||||
qrCodeUA(viewStore.address)
|
||||
.padding(.top, 30)
|
||||
|
||||
Text("Your UA address \(viewStore.address)")
|
||||
.truncationMode(.middle)
|
||||
.multilineTextAlignment(.center)
|
||||
.lineLimit(2)
|
||||
.padding(30)
|
||||
|
||||
Text("Show me backup phrase")
|
||||
.navigationLink(
|
||||
isActive: viewStore.bindingForPhraseDisplay,
|
||||
destination: {
|
||||
RecoveryPhraseDisplayView(
|
||||
store: store.scope(
|
||||
state: \.phraseDisplayState,
|
||||
action: ProfileAction.phraseDisplay
|
||||
)
|
||||
)
|
||||
}
|
||||
Button(
|
||||
action: { viewStore.send(.updateRoute(.addressDetails)) },
|
||||
label: { Text("See address details") }
|
||||
)
|
||||
.activeButtonStyle
|
||||
.frame(height: 50)
|
||||
.padding(EdgeInsets(top: 0, leading: 30, bottom: 50, trailing: 30))
|
||||
|
||||
Text("Go To Settings")
|
||||
.navigationLink(
|
||||
isActive: viewStore.bindingForSettings,
|
||||
destination: {
|
||||
Text("Settings")
|
||||
}
|
||||
Rectangle()
|
||||
.frame(height: 1.5)
|
||||
.padding(EdgeInsets(top: 0, leading: 100, bottom: 20, trailing: 100))
|
||||
.foregroundColor(Asset.Colors.TextField.Underline.purple.color)
|
||||
|
||||
Button(
|
||||
action: { viewStore.send(.updateRoute(.settings)) },
|
||||
label: { Text("Settings") }
|
||||
)
|
||||
.primaryButtonStyle
|
||||
.frame(height: 50)
|
||||
.padding(EdgeInsets(top: 30, leading: 30, bottom: 20, trailing: 30))
|
||||
|
||||
Button(
|
||||
action: { },
|
||||
label: { Text("Support") }
|
||||
)
|
||||
.primaryButtonStyle
|
||||
.frame(height: 50)
|
||||
.padding(EdgeInsets(top: 0, leading: 30, bottom: 20, trailing: 30))
|
||||
|
||||
Spacer()
|
||||
|
||||
HStack {
|
||||
VStack {
|
||||
Text("secant v\(viewStore.appVersion)(\(viewStore.appBuild))")
|
||||
Text("sdk v\(viewStore.sdkVersion)")
|
||||
}
|
||||
Spacer()
|
||||
Button(
|
||||
action: { },
|
||||
label: {
|
||||
Text("More info")
|
||||
.foregroundColor(Asset.Colors.Text.moreInfoText.color)
|
||||
}
|
||||
)
|
||||
}
|
||||
.padding(30)
|
||||
}
|
||||
.onAppear(perform: { viewStore.send(.onAppear) })
|
||||
.navigationLinkEmpty(
|
||||
isActive: viewStore.bindingForSettings,
|
||||
destination: {
|
||||
SettingsView()
|
||||
}
|
||||
)
|
||||
.navigationLinkEmpty(
|
||||
isActive: viewStore.bindingForAddressDetails,
|
||||
destination: {
|
||||
AddressDetails(store: .placeholder)
|
||||
}
|
||||
)
|
||||
}
|
||||
.applyScreenBackground()
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
}
|
||||
}
|
||||
|
||||
extension ProfileView {
|
||||
func qrCodeUA(_ qrText: String) -> some View {
|
||||
Group {
|
||||
if let img = QRCodeGenerator.generate(from: qrText) {
|
||||
Image(img, scale: 1, label: Text(String(format: NSLocalizedString("QR Code for %@", comment: ""), "\(qrText)") ))
|
||||
.cornerRadius(20)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 20)
|
||||
.stroke(Color.white, lineWidth: 25)
|
||||
.scaleEffect(1.1)
|
||||
)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 20)
|
||||
.stroke(Color.black, lineWidth: 8)
|
||||
.scaleEffect(1.1)
|
||||
)
|
||||
} else {
|
||||
Image(systemName: "qrcode")
|
||||
}
|
||||
.navigationTitle(Text("\(String(describing: Self.self))"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -49,14 +113,14 @@ struct ProfileView_Previews: PreviewProvider {
|
|||
ProfileView(
|
||||
store: .init(
|
||||
initialState: .init(
|
||||
phraseDisplayState: .init(),
|
||||
settingsState: .init(),
|
||||
walletInfoState: .init()
|
||||
addressDetailsState: .placeholder,
|
||||
settingsState: .init()
|
||||
),
|
||||
reducer: .default,
|
||||
environment: .live
|
||||
)
|
||||
)
|
||||
}
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ struct SendFLowView_Previews: PreviewProvider {
|
|||
)
|
||||
)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationViewStyle(StackNavigationViewStyle())
|
||||
.navigationViewStyle(.stack)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x00",
|
||||
"green" : "0xB8",
|
||||
"red" : "0xFF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x00",
|
||||
"green" : "0xB8",
|
||||
"red" : "0xFF"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -116,6 +116,7 @@ internal enum Asset {
|
|||
internal static let captionText = ColorAsset(name: "captionText")
|
||||
internal static let captionTextShadow = ColorAsset(name: "captionTextShadow")
|
||||
internal static let highlightedSuperscriptText = ColorAsset(name: "highlightedSuperscriptText")
|
||||
internal static let moreInfoText = ColorAsset(name: "moreInfoText")
|
||||
}
|
||||
internal enum TextField {
|
||||
internal static let titleAccessoryButton = ColorAsset(name: "TitleAccessoryButton")
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
//
|
||||
// QRCodeGenerator.swift
|
||||
// secant-testnet
|
||||
//
|
||||
// Created by Lukáš Korba on 04.07.2022.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Combine
|
||||
import CoreImage.CIFilterBuiltins
|
||||
import SwiftUI
|
||||
|
||||
enum QRCodeGenerator {
|
||||
enum QRCodeError: Error {
|
||||
case failedToGenerate
|
||||
}
|
||||
|
||||
static func generate(from string: String) -> Future<CGImage, QRCodeError> {
|
||||
Future<CGImage, QRCodeError> { promise in
|
||||
DispatchQueue.global().async {
|
||||
guard let image = generate(from: string) else {
|
||||
promise(.failure(QRCodeGenerator.QRCodeError.failedToGenerate))
|
||||
return
|
||||
}
|
||||
|
||||
return promise(.success(image))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static func generate(from string: String, scale: CGFloat = 5) -> CGImage? {
|
||||
let data = string.data(using: String.Encoding.utf8)
|
||||
|
||||
let context = CIContext()
|
||||
let filter = CoreImage.CIFilter.qrCodeGenerator()
|
||||
filter.setValue(data, forKey: "inputMessage")
|
||||
let transform = CGAffineTransform(scaleX: scale, y: scale)
|
||||
|
||||
guard let output = filter.outputImage?.transformed(by: transform) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
return context.createCGImage(output, from: output.extent)
|
||||
}
|
||||
}
|
|
@ -469,7 +469,7 @@ class TestWrappedSDKSynchronizer: WrappedSDKSynchronizer {
|
|||
|
||||
func getTransparentAddress(account: Int) -> TransparentAddress? { nil }
|
||||
|
||||
func getShieldedAddress(account: Int) -> SaplingShieldedAddress? { nil }
|
||||
func getShieldedAddress(account: Int) -> SaplingShieldedAddress? { "ff3927e1f83df9b1b0dc75540ddc59ee435eecebae914d2e6dfe8576fbedc9a8" }
|
||||
|
||||
func sendTransaction(
|
||||
with spendingKey: String,
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// ProfileTests.swift
|
||||
// secantTests
|
||||
//
|
||||
// Created by Lukáš Korba on 05.07.2022.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import secant_testnet
|
||||
import ComposableArchitecture
|
||||
|
||||
class ProfileTests: XCTestCase {
|
||||
func testSynchronizerStateChanged_AnyButSynced() throws {
|
||||
let testEnvironment = ProfileEnvironment(
|
||||
appVersionHandler: .test,
|
||||
mnemonic: .mock,
|
||||
SDKSynchronizer: TestWrappedSDKSynchronizer(),
|
||||
walletStorage: .throwing,
|
||||
zcashSDKEnvironment: .testnet
|
||||
)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: .placeholder,
|
||||
reducer: ProfileReducer.default,
|
||||
environment: testEnvironment
|
||||
)
|
||||
|
||||
store.send(.onAppear) { state in
|
||||
state.address = "ff3927e1f83df9b1b0dc75540ddc59ee435eecebae914d2e6dfe8576fbedc9a8"
|
||||
state.appVersion = "0.0.1"
|
||||
state.appBuild = "31"
|
||||
state.sdkVersion = "0.14.0-beta"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
//
|
||||
// ProfileSnapshotTests.swift
|
||||
// secantTests
|
||||
//
|
||||
// Created by Lukáš Korba on 05.07.2022.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import secant_testnet
|
||||
import ComposableArchitecture
|
||||
import SwiftUI
|
||||
|
||||
class ProfileSnapshotTests: XCTestCase {
|
||||
func testProfileSnapshot_sent() throws {
|
||||
let testEnvironment = ProfileEnvironment(
|
||||
appVersionHandler: .test,
|
||||
mnemonic: .mock,
|
||||
SDKSynchronizer: TestWrappedSDKSynchronizer(),
|
||||
walletStorage: .throwing,
|
||||
zcashSDKEnvironment: .testnet
|
||||
)
|
||||
|
||||
let store = Store(
|
||||
initialState: .placeholder,
|
||||
reducer: ProfileReducer.default,
|
||||
environment: testEnvironment
|
||||
)
|
||||
|
||||
ViewStore(store).send(.onAppear)
|
||||
addAttachments(ProfileView(store: store))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue