[#514] Adopt Unified Addresses (#515)

- UAs integrated to the Profile and Address details screen
- Snapshot tests for the AddressDetailsView
- Unit tests for the AddressDetailsStore
This commit is contained in:
Lukas Korba 2023-01-05 20:07:25 +01:00 committed by GitHub
parent 464d87bd25
commit 64d509aedb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 228 additions and 31 deletions

View File

@ -112,6 +112,8 @@
9E153A7529216EFB00112F41 /* UserDefaultsLiveKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A7229216EFB00112F41 /* UserDefaultsLiveKey.swift */; };
9E153A7629216EFB00112F41 /* UserDefaultsInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A7329216EFB00112F41 /* UserDefaultsInterface.swift */; };
9E153A7729216EFB00112F41 /* UserDefaultsTestKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E153A7429216EFB00112F41 /* UserDefaultsTestKey.swift */; };
9E207C362966EC77003E2C9B /* AddressDetailsSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E207C352966EC77003E2C9B /* AddressDetailsSnapshotTests.swift */; };
9E207C392966EF87003E2C9B /* AddressDetailsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E207C382966EF87003E2C9B /* AddressDetailsTests.swift */; };
9E2AC0FF27D8EC120042AA47 /* MnemonicSwift in Frameworks */ = {isa = PBXBuildFile; productRef = 9E2AC0FE27D8EC120042AA47 /* MnemonicSwift */; };
9E2DF99C27CF704D00649636 /* ImportWalletStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DF99827CF704D00649636 /* ImportWalletStore.swift */; };
9E2DF99D27CF704D00649636 /* ImportSeedEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E2DF99A27CF704D00649636 /* ImportSeedEditor.swift */; };
@ -406,6 +408,8 @@
9E153A7229216EFB00112F41 /* UserDefaultsLiveKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsLiveKey.swift; sourceTree = "<group>"; };
9E153A7329216EFB00112F41 /* UserDefaultsInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsInterface.swift; sourceTree = "<group>"; };
9E153A7429216EFB00112F41 /* UserDefaultsTestKey.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserDefaultsTestKey.swift; sourceTree = "<group>"; };
9E207C352966EC77003E2C9B /* AddressDetailsSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressDetailsSnapshotTests.swift; sourceTree = "<group>"; };
9E207C382966EF87003E2C9B /* AddressDetailsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressDetailsTests.swift; sourceTree = "<group>"; };
9E2DF99827CF704D00649636 /* ImportWalletStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportWalletStore.swift; sourceTree = "<group>"; };
9E2DF99A27CF704D00649636 /* ImportSeedEditor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportSeedEditor.swift; sourceTree = "<group>"; };
9E2DF99B27CF704D00649636 /* ImportWalletView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImportWalletView.swift; sourceTree = "<group>"; };
@ -687,6 +691,7 @@
isa = PBXGroup;
children = (
9E391162284E3ECF0073DD9A /* SnapshotTests */,
9E207C372966EF6E003E2C9B /* AddressDetailsTests */,
9E94C61E28AA7DD5008256E9 /* BalanceBreakdownTests */,
9E6713EF2897F80A00A6796F /* MultiLineTextFieldTests */,
9E7CB6222874245400A02233 /* ProfileTests */,
@ -998,6 +1003,22 @@
path = UserDefaults;
sourceTree = "<group>";
};
9E207C342966EC60003E2C9B /* AddressDetailsSnapshotTests */ = {
isa = PBXGroup;
children = (
9E207C352966EC77003E2C9B /* AddressDetailsSnapshotTests.swift */,
);
path = AddressDetailsSnapshotTests;
sourceTree = "<group>";
};
9E207C372966EF6E003E2C9B /* AddressDetailsTests */ = {
isa = PBXGroup;
children = (
9E207C382966EF87003E2C9B /* AddressDetailsTests.swift */,
);
path = AddressDetailsTests;
sourceTree = "<group>";
};
9E2DF99727CF704D00649636 /* ImportWallet */ = {
isa = PBXGroup;
children = (
@ -1034,6 +1055,7 @@
9E391162284E3ECF0073DD9A /* SnapshotTests */ = {
isa = PBXGroup;
children = (
9E207C342966EC60003E2C9B /* AddressDetailsSnapshotTests */,
9E94C62128AA7ECD008256E9 /* BalanceBreakdownSnapshotTests */,
9E9ECC8B28589E150099D5A2 /* HomeSnapshotTests */,
9E9ECC9328589E150099D5A2 /* ImportWalletSnapshotTests */,
@ -2193,9 +2215,11 @@
0DFE93E6272CB6F7000FCCA5 /* RecoveryPhraseValidationTests.swift in Sources */,
9E66122A287717A900C75B70 /* HomeCircularProgressSnapshotTests.swift in Sources */,
9EAB4676285B5C7C002904A0 /* DeeplinkTests.swift in Sources */,
9E207C362966EC77003E2C9B /* AddressDetailsSnapshotTests.swift in Sources */,
9E3911392848AD500073DD9A /* HomeTests.swift in Sources */,
9E9ECC9C28589E150099D5A2 /* OnboardingSnapshotTests.swift in Sources */,
9E9ECC9728589E150099D5A2 /* HomeSnapshotTests.swift in Sources */,
9E207C392966EF87003E2C9B /* AddressDetailsTests.swift in Sources */,
9EF8135C27ECC25E0075AF48 /* WalletStorageTests.swift in Sources */,
0DB4E0B12881F2DB00947B78 /* WalletBalance+testing.swift in Sources */,
9E02B56C27FED475005B809B /* DatabaseFilesTests.swift in Sources */,

View File

@ -63,6 +63,7 @@ protocol SDKSynchronizerClient {
func getAllPendingTransactions() -> Effect<[WalletEvent], Never>
func getAllTransactions() -> Effect<[WalletEvent], Never>
func getUnifiedAddress(account: Int) -> UnifiedAddress?
func getTransparentAddress(account: Int) -> TransparentAddress?
func getSaplingAddress(accountIndex: Int) async -> SaplingAddress?
@ -78,12 +79,4 @@ extension SDKSynchronizerClient {
func start() throws {
try start(retry: false)
}
func getTransparentAddress() -> TransparentAddress? {
getTransparentAddress(account: 0)
}
func getSaplingAddress() async -> SaplingAddress? {
await getSaplingAddress(accountIndex: 0)
}
}

View File

@ -166,6 +166,10 @@ class LiveSDKSynchronizerClient: SDKSynchronizerClient {
return .none
}
func getUnifiedAddress(account: Int) -> UnifiedAddress? {
synchronizer?.getUnifiedAddress(accountIndex: account)
}
func getTransparentAddress(account: Int) -> TransparentAddress? {
synchronizer?.getTransparentAddress(accountIndex: account)
}

View File

@ -111,6 +111,14 @@ class MockSDKSynchronizerClient: SDKSynchronizerClient {
.eraseToEffect()
}
func getUnifiedAddress(account: Int) -> UnifiedAddress? {
// swiftlint:disable line_length
try! UnifiedAddress(
encoding: "utest1zkkkjfxkamagznjr6ayemffj2d2gacdwpzcyw669pvg06xevzqslpmm27zjsctlkstl2vsw62xrjktmzqcu4yu9zdhdxqz3kafa4j2q85y6mv74rzjcgjg8c0ytrg7dwyzwtgnuc76h",
network: .testnet
)
}
func getTransparentAddress(account: Int) -> TransparentAddress? { nil }
func getSaplingAddress(accountIndex account: Int) -> SaplingAddress? {

View File

@ -48,6 +48,8 @@ class NoopSDKSynchronizer: SDKSynchronizerClient {
func getAllTransactions() -> Effect<[WalletEvent], Never> { Effect(value: []) }
func getUnifiedAddress(account: Int) -> UnifiedAddress? { nil }
func getTransparentAddress(account: Int) -> TransparentAddress? { nil }
func getSaplingAddress(accountIndex account: Int) -> SaplingAddress? { nil }
@ -159,6 +161,8 @@ class TestSDKSynchronizerClient: SDKSynchronizerClient {
.eraseToEffect()
}
func getUnifiedAddress(account: Int) -> UnifiedAddress? { nil }
func getTransparentAddress(account: Int) -> TransparentAddress? { nil }
func getSaplingAddress(accountIndex account: Int) -> SaplingAddress? {

View File

@ -7,22 +7,43 @@
import Foundation
import ComposableArchitecture
import ZcashLightClientKit
typealias AddressDetailsStore = Store<AddressDetailsReducer.State, AddressDetailsReducer.Action>
struct AddressDetailsReducer: ReducerProtocol {
struct State: Equatable { }
struct State: Equatable {
var uAddress: UnifiedAddress?
var unifiedAddress: String {
uAddress?.stringEncoded ?? "could not extract UA"
}
var transparentAddress: String {
uAddress?.transparentReceiver()?.stringEncoded ?? "could not extract transparent receiver from UA"
}
var saplingAddress: String {
uAddress?.saplingReceiver()?.stringEncoded ?? "could not extract sapling receiver from UA"
}
}
enum Action: Equatable {
case copyToPastboard(String)
case copySaplingAddressToPastboard
case copyTransparentAddressToPastboard
case copyUnifiedAddressToPastboard
}
@Dependency(\.pasteboard) var pasteboard
func reduce(into state: inout State, action: Action) -> ComposableArchitecture.EffectTask<Action> {
switch action {
case .copyToPastboard(let value):
pasteboard.setString(value)
case .copySaplingAddressToPastboard:
pasteboard.setString(state.saplingAddress)
case .copyTransparentAddressToPastboard:
pasteboard.setString(state.transparentAddress)
case .copyUnifiedAddressToPastboard:
pasteboard.setString(state.unifiedAddress)
}
return .none
}

View File

@ -8,18 +8,43 @@
import SwiftUI
import ComposableArchitecture
struct AddressDetails: View {
struct AddressDetailsView: View {
let store: AddressDetailsStore
var body: some View {
WithViewStore(store) { _ in
Text("Address Details")
WithViewStore(store) { viewStore in
VStack {
Text("Unified Address")
Text("\(viewStore.unifiedAddress)")
.onTapGesture {
viewStore.send(.copyUnifiedAddressToPastboard)
}
Text("Sapling Address")
.padding(.top, 20)
Text("\(viewStore.saplingAddress)")
.onTapGesture {
viewStore.send(.copySaplingAddressToPastboard)
}
Text("Transparent Address")
.padding(.top, 20)
Text("\(viewStore.transparentAddress)")
.onTapGesture {
viewStore.send(.copyTransparentAddressToPastboard)
}
}
.padding(20)
.applyScreenBackground()
}
}
}
struct AddressDetails_Previews: PreviewProvider {
static var previews: some View {
AddressDetails(store: .placeholder)
AddressDetailsView(store: .placeholder)
}
}

View File

@ -1,5 +1,6 @@
import ComposableArchitecture
import SwiftUI
import ZcashLightClientKit
typealias ProfileStore = Store<ProfileReducer.State, ProfileReducer.Action>
typealias ProfileViewStore = ViewStore<ProfileReducer.State, ProfileReducer.Action>
@ -11,20 +12,22 @@ struct ProfileReducer: ReducerProtocol {
case settings
}
var address = ""
var addressDetailsState: AddressDetailsReducer.State
var appBuild = ""
var appVersion = ""
var destination: Destination?
var sdkVersion = ""
var settingsState: SettingsReducer.State
var unifiedAddress: String {
addressDetailsState.uAddress?.stringEncoded ?? "could not extract UA"
}
}
enum Action: Equatable {
case addressDetails(AddressDetailsReducer.Action)
case back
case onAppear
case onAppearFinished(String)
case settings(SettingsReducer.Action)
case updateDestination(ProfileReducer.State.Destination?)
}
@ -45,13 +48,7 @@ struct ProfileReducer: ReducerProtocol {
Reduce { state, action in
switch action {
case .onAppear:
return Effect.task {
let saplingAddress = await self.sdkSynchronizer.getSaplingAddress()?.stringEncoded ?? ""
return .onAppearFinished(saplingAddress)
}
case let .onAppearFinished(saplingAddress):
state.address = saplingAddress
state.addressDetailsState.uAddress = self.sdkSynchronizer.getUnifiedAddress(account: 0)
state.appBuild = appVersion.appBuild()
state.appVersion = appVersion.appVersion()
state.sdkVersion = zcashSDKEnvironment.sdkVersion
@ -77,6 +74,13 @@ struct ProfileReducer: ReducerProtocol {
// MARK: - Store
extension ProfileStore {
func addressStore() -> AddressDetailsStore {
self.scope(
state: \.addressDetailsState,
action: ProfileReducer.Action.addressDetails
)
}
func settingsStore() -> SettingsStore {
self.scope(
state: \.settingsState,

View File

@ -7,10 +7,10 @@ struct ProfileView: View {
var body: some View {
WithViewStore(store) { viewStore in
VStack {
qrCodeUA(viewStore.address)
qrCodeUA(viewStore.unifiedAddress)
.padding(.top, 30)
Text("Your UA address \(viewStore.address)")
Text("Your UA address \(viewStore.unifiedAddress)")
.truncationMode(.middle)
.multilineTextAlignment(.center)
.lineLimit(2)
@ -73,7 +73,7 @@ struct ProfileView: View {
.navigationLinkEmpty(
isActive: viewStore.bindingForAddressDetails,
destination: {
AddressDetails(store: .placeholder)
AddressDetailsView(store: store.addressStore())
}
)
}

View File

@ -0,0 +1,80 @@
//
// AddressDetailsTests.swift
// secantTests
//
// Created by Lukáš Korba on 05.01.2023.
//
import XCTest
@testable import secant_testnet
import ComposableArchitecture
import ZcashLightClientKit
class AddressDetailsTests: XCTestCase {
// swiftlint:disable line_length
let uAddressEncoding = "utest1zkkkjfxkamagznjr6ayemffj2d2gacdwpzcyw669pvg06xevzqslpmm27zjsctlkstl2vsw62xrjktmzqcu4yu9zdhdxqz3kafa4j2q85y6mv74rzjcgjg8c0ytrg7dwyzwtgnuc76h"
func testCopySaplingAddressToPasteboard() throws {
let testPasteboard = PasteboardClient.testPasteboard
let uAddress = try UnifiedAddress(encoding: uAddressEncoding, network: .testnet)
let store = TestStore(
initialState: AddressDetailsReducer.State(uAddress: uAddress),
reducer: AddressDetailsReducer()
) {
$0.pasteboard = testPasteboard
}
store.send(.copySaplingAddressToPastboard)
let expectedAddress = uAddress.saplingReceiver()?.stringEncoded ?? "could not extract sapling receiver from UA"
XCTAssertEqual(
testPasteboard.getString(),
expectedAddress,
"AddressDetails: `testCopySaplingAddressToPasteboard` is expected to match the input `\(expectedAddress)`"
)
}
func testCopyTransparentAddressToPasteboard() throws {
let testPasteboard = PasteboardClient.testPasteboard
let uAddress = try UnifiedAddress(encoding: uAddressEncoding, network: .testnet)
let store = TestStore(
initialState: AddressDetailsReducer.State(uAddress: uAddress),
reducer: AddressDetailsReducer()
) {
$0.pasteboard = testPasteboard
}
store.send(.copyTransparentAddressToPastboard)
let expectedAddress = uAddress.transparentReceiver()?.stringEncoded ?? "could not extract transparent receiver from UA"
XCTAssertEqual(
testPasteboard.getString(),
expectedAddress,
"AddressDetails: `testCopyTransparentAddressToPasteboard` is expected to match the input `\(expectedAddress)`"
)
}
func testCopyUnifiedAddressToPasteboard() throws {
let testPasteboard = PasteboardClient.testPasteboard
let uAddress = try UnifiedAddress(encoding: uAddressEncoding, network: .testnet)
let store = TestStore(
initialState: AddressDetailsReducer.State(uAddress: uAddress),
reducer: AddressDetailsReducer()
) {
$0.pasteboard = testPasteboard
}
store.send(.copyUnifiedAddressToPastboard)
XCTAssertEqual(
testPasteboard.getString(),
uAddress.stringEncoded,
"AddressDetails: `testCopyUnifiedAddressToPasteboard` is expected to match the input `\(uAddress.stringEncoded)`"
)
}
}

View File

@ -8,6 +8,7 @@
import XCTest
@testable import secant_testnet
import ComposableArchitecture
import ZcashLightClientKit
class ProfileTests: XCTestCase {
@MainActor func testSynchronizerStateChanged_AnyButSynced() async throws {
@ -19,10 +20,14 @@ class ProfileTests: XCTestCase {
dependencies.sdkSynchronizer = SDKSynchronizerDependency.mock
}
_ = await store.send(.onAppear)
// swiftlint:disable line_length
let uAddress = try UnifiedAddress(
encoding: "utest1zkkkjfxkamagznjr6ayemffj2d2gacdwpzcyw669pvg06xevzqslpmm27zjsctlkstl2vsw62xrjktmzqcu4yu9zdhdxqz3kafa4j2q85y6mv74rzjcgjg8c0ytrg7dwyzwtgnuc76h",
network: .testnet
)
await store.receive(.onAppearFinished("ztestsapling1edm52k336nk70gxqxedd89slrrf5xwnnp5rt6gqnk0tgw4mynv6fcx42ym6x27yac5amvfvwypz")) { state in
state.address = "ztestsapling1edm52k336nk70gxqxedd89slrrf5xwnnp5rt6gqnk0tgw4mynv6fcx42ym6x27yac5amvfvwypz"
_ = await store.send(.onAppear) { state in
state.addressDetailsState.uAddress = uAddress
state.appVersion = "0.0.1"
state.appBuild = "31"
state.sdkVersion = "0.17.0-beta"

View File

@ -0,0 +1,29 @@
//
// AddressDetailsSnapshotTests.swift
// secantTests
//
// Created by Lukáš Korba on 05.01.2023.
//
import XCTest
@testable import secant_testnet
import ComposableArchitecture
import ZcashLightClientKit
import SwiftUI
class AddressDetailsSnapshotTests: XCTestCase {
func testAddressDetailsSnapshot() throws {
// swiftlint:disable line_length
let uAddress = try UnifiedAddress(
encoding: "utest1zkkkjfxkamagznjr6ayemffj2d2gacdwpzcyw669pvg06xevzqslpmm27zjsctlkstl2vsw62xrjktmzqcu4yu9zdhdxqz3kafa4j2q85y6mv74rzjcgjg8c0ytrg7dwyzwtgnuc76h",
network: .testnet
)
let store = Store(
initialState: AddressDetailsReducer.State(uAddress: uAddress),
reducer: AddressDetailsReducer()
)
addAttachments(AddressDetailsView(store: store))
}
}