[#516] Adopt unreleased changes that will go live with SDK 0.18.0-beta release (#532)

- all issues refactored
- previous transactions replaced by new data type from the SDK
- unit tests fixed
This commit is contained in:
Lukas Korba 2023-02-06 09:38:34 +01:00 committed by GitHub
parent 1aca887800
commit fdd6ff19c0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 321 additions and 127 deletions

View File

@ -2617,7 +2617,7 @@
repositoryURL = "https://github.com/zcash/ZcashLightClientKit/"; repositoryURL = "https://github.com/zcash/ZcashLightClientKit/";
requirement = { requirement = {
kind = upToNextMajorVersion; kind = upToNextMajorVersion;
minimumVersion = "0.17.0-beta"; minimumVersion = "0.18.0-beta";
}; };
}; };
6654C7382715A38000901167 /* XCRemoteSwiftPackageReference "swift-composable-architecture" */ = { 6654C7382715A38000901167 /* XCRemoteSwiftPackageReference "swift-composable-architecture" */ = {

View File

@ -41,8 +41,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/stephencelis/SQLite.swift.git", "location" : "https://github.com/stephencelis/SQLite.swift.git",
"state" : { "state" : {
"revision" : "4d543d811ee644fa4cc4bfa0be996b4dd6ba0f54", "revision" : "7a2e3cd27de56f6d396e84f63beefd0267b55ccb",
"version" : "0.13.3" "version" : "0.14.1"
} }
}, },
{ {
@ -203,8 +203,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi.git", "location" : "https://github.com/zcash-hackworks/zcash-light-client-ffi.git",
"state" : { "state" : {
"revision" : "fad9802b907822d5a1877584c91f3786824521b7", "revision" : "b6013b8b313523b2c72ce62dbdc17f6ffad3a500",
"version" : "0.1.0" "version" : "0.1.1"
} }
}, },
{ {
@ -212,8 +212,8 @@
"kind" : "remoteSourceControl", "kind" : "remoteSourceControl",
"location" : "https://github.com/zcash/ZcashLightClientKit", "location" : "https://github.com/zcash/ZcashLightClientKit",
"state" : { "state" : {
"revision" : "d9b85b40ad36ac5183f44b6db9805e44171ee988", "revision" : "731c7bbf4514a90b879c5c15968cdb41ac728e3a",
"version" : "0.17.0-beta" "version" : "0.18.0-beta"
} }
} }
], ],

View File

@ -59,6 +59,8 @@ protocol SDKSynchronizerClient {
func getShieldedBalance() -> WalletBalance? func getShieldedBalance() -> WalletBalance?
func getTransparentBalance() -> WalletBalance? func getTransparentBalance() -> WalletBalance?
func getAllSentTransactions() -> Effect<[WalletEvent], Never>
func getAllReceivedTransactions() -> Effect<[WalletEvent], Never>
func getAllClearedTransactions() -> Effect<[WalletEvent], Never> func getAllClearedTransactions() -> Effect<[WalletEvent], Never>
func getAllPendingTransactions() -> Effect<[WalletEvent], Never> func getAllPendingTransactions() -> Effect<[WalletEvent], Never>
func getAllTransactions() -> Effect<[WalletEvent], Never> func getAllTransactions() -> Effect<[WalletEvent], Never>

View File

@ -113,10 +113,35 @@ class LiveSDKSynchronizerClient: SDKSynchronizerClient {
latestScannedSynchronizerState?.transparentBalance latestScannedSynchronizerState?.transparentBalance
} }
func getAllSentTransactions() -> Effect<[WalletEvent], Never> {
if let transactions = try? synchronizer?.allSentTransactions() {
return Effect(value: transactions.map {
let memos = try? synchronizer?.getMemos(for: $0)
let transaction = TransactionState.init(transaction: $0, memos: memos)
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
})
}
return .none
}
func getAllReceivedTransactions() -> Effect<[WalletEvent], Never> {
if let transactions = try? synchronizer?.allReceivedTransactions() {
return Effect(value: transactions.map {
let memos = try? synchronizer?.getMemos(for: $0)
let transaction = TransactionState.init(transaction: $0, memos: memos)
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
})
}
return .none
}
func getAllClearedTransactions() -> Effect<[WalletEvent], Never> { func getAllClearedTransactions() -> Effect<[WalletEvent], Never> {
if let clearedTransactions = try? synchronizer?.allClearedTransactions() { if let transactions = try? synchronizer?.allClearedTransactions() {
return Effect(value: clearedTransactions.map { return Effect(value: transactions.map {
let transaction = TransactionState.init(confirmedTransaction: $0, sent: ($0.toAddress != nil)) let memos = try? synchronizer?.getMemos(for: $0)
let transaction = TransactionState.init(transaction: $0, memos: memos)
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp) return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
}) })
} }
@ -125,9 +150,9 @@ class LiveSDKSynchronizerClient: SDKSynchronizerClient {
} }
func getAllPendingTransactions() -> Effect<[WalletEvent], Never> { func getAllPendingTransactions() -> Effect<[WalletEvent], Never> {
if let pendingTransactions = try? synchronizer?.allPendingTransactions(), if let transactions = try? synchronizer?.allPendingTransactions(),
let syncedBlockHeight = synchronizer?.latestScannedHeight { let syncedBlockHeight = synchronizer?.latestScannedHeight {
return Effect(value: pendingTransactions.map { return Effect(value: transactions.map {
let transaction = TransactionState.init(pendingTransaction: $0, latestBlockHeight: syncedBlockHeight) let transaction = TransactionState.init(pendingTransaction: $0, latestBlockHeight: syncedBlockHeight)
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp) return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
}) })
@ -141,21 +166,21 @@ class LiveSDKSynchronizerClient: SDKSynchronizerClient {
let clearedTransactions = try? synchronizer?.allClearedTransactions(), let clearedTransactions = try? synchronizer?.allClearedTransactions(),
let syncedBlockHeight = synchronizer?.latestScannedHeight { let syncedBlockHeight = synchronizer?.latestScannedHeight {
let clearedTxs: [WalletEvent] = clearedTransactions.map { let clearedTxs: [WalletEvent] = clearedTransactions.map {
let transaction = TransactionState.init(confirmedTransaction: $0, sent: ($0.toAddress != nil)) let transaction = TransactionState.init(transaction: $0)
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp) return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
} }
let pendingTxs: [WalletEvent] = pendingTransactions.map { let pendingTxs: [WalletEvent] = pendingTransactions.map {
let transaction = TransactionState.init(pendingTransaction: $0, latestBlockHeight: syncedBlockHeight) let transaction = TransactionState.init(pendingTransaction: $0, latestBlockHeight: syncedBlockHeight)
return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp) return WalletEvent(id: transaction.id, state: .pending(transaction), timestamp: transaction.timestamp)
} }
let cTxs = clearedTxs.filter { transaction in
let txs = clearedTxs.filter { cleared in
pendingTxs.first { pending in pendingTxs.first { pending in
pending.id == cleared.id pending.id == transaction.id
} == nil } == nil
} }
return .merge( return .merge(
Effect(value: txs), Effect(value: cTxs),
Effect(value: pendingTxs) Effect(value: pendingTxs)
) )
.flatMap(Publishers.Sequence.init(sequence:)) .flatMap(Publishers.Sequence.init(sequence:))

View File

@ -47,6 +47,56 @@ class MockSDKSynchronizerClient: SDKSynchronizerClient {
WalletBalance(verified: Zatoshi(12345000), total: Zatoshi(12345000)) WalletBalance(verified: Zatoshi(12345000), total: Zatoshi(12345000))
} }
func getAllSentTransactions() -> Effect<[WalletEvent], Never> {
let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"),
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55")
]
return Effect(
value:
mocked.map {
let transaction = TransactionState.placeholder(
amount: $0.amount,
fee: Zatoshi(10),
shielded: $0.shielded,
status: $0.status,
timestamp: $0.date,
uuid: $0.uuid
)
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp ?? 0)
}
)
}
func getAllReceivedTransactions() -> Effect<[WalletEvent], Never> {
let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"),
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55")
]
return Effect(
value:
mocked.map {
let transaction = TransactionState.placeholder(
amount: $0.amount,
fee: Zatoshi(10),
shielded: $0.shielded,
status: $0.status,
timestamp: $0.date,
uuid: $0.uuid
)
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp ?? 0)
}
)
}
func getAllClearedTransactions() -> Effect<[WalletEvent], Never> { func getAllClearedTransactions() -> Effect<[WalletEvent], Never> {
let mocked: [TransactionStateMockHelper] = [ let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"), TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),
@ -67,7 +117,7 @@ class MockSDKSynchronizerClient: SDKSynchronizerClient {
timestamp: $0.date, timestamp: $0.date,
uuid: $0.uuid uuid: $0.uuid
) )
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp) return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp ?? 0)
} }
) )
} }
@ -132,9 +182,12 @@ class MockSDKSynchronizerClient: SDKSynchronizerClient {
to recipientAddress: Recipient, to recipientAddress: Recipient,
memo: Memo? memo: Memo?
) -> Effect<Result<TransactionState, NSError>, Never> { ) -> Effect<Result<TransactionState, NSError>, Never> {
var memos: [Memo]? = []
if let memo { memos?.append(memo) }
let transactionState = TransactionState( let transactionState = TransactionState(
expirationHeight: 40, expiryHeight: 40,
memo: memo, memos: memos,
minedHeight: 50, minedHeight: 50,
shielded: true, shielded: true,
zAddress: "tteafadlamnelkqe", zAddress: "tteafadlamnelkqe",

View File

@ -42,6 +42,10 @@ class NoopSDKSynchronizer: SDKSynchronizerClient {
func getTransparentBalance() -> WalletBalance? { nil } func getTransparentBalance() -> WalletBalance? { nil }
func getAllSentTransactions() -> Effect<[WalletEvent], Never> { Effect(value: []) }
func getAllReceivedTransactions() -> Effect<[WalletEvent], Never> { Effect(value: []) }
func getAllClearedTransactions() -> Effect<[WalletEvent], Never> { Effect(value: []) } func getAllClearedTransactions() -> Effect<[WalletEvent], Never> { Effect(value: []) }
func getAllPendingTransactions() -> Effect<[WalletEvent], Never> { Effect(value: []) } func getAllPendingTransactions() -> Effect<[WalletEvent], Never> { Effect(value: []) }
@ -69,7 +73,6 @@ class NoopSDKSynchronizer: SDKSynchronizerClient {
} }
class TestSDKSynchronizerClient: SDKSynchronizerClient { class TestSDKSynchronizerClient: SDKSynchronizerClient {
private(set) var blockProcessor: CompactBlockProcessor?
private(set) var notificationCenter: NotificationCenterClient private(set) var notificationCenter: NotificationCenterClient
private(set) var synchronizer: SDKSynchronizer? private(set) var synchronizer: SDKSynchronizer?
private(set) var stateChanged: CurrentValueSubject<SDKSynchronizerState, Never> private(set) var stateChanged: CurrentValueSubject<SDKSynchronizerState, Never>
@ -97,6 +100,56 @@ class TestSDKSynchronizerClient: SDKSynchronizerClient {
func getTransparentBalance() -> WalletBalance? { nil } func getTransparentBalance() -> WalletBalance? { nil }
func getAllSentTransactions() -> Effect<[WalletEvent], Never> {
let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"),
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55")
]
return Effect(
value:
mocked.map {
let transaction = TransactionState.placeholder(
amount: $0.amount,
fee: Zatoshi(10),
shielded: $0.shielded,
status: $0.status,
timestamp: $0.date,
uuid: $0.uuid
)
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
}
)
}
func getAllReceivedTransactions() -> Effect<[WalletEvent], Never> {
let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"),
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55")
]
return Effect(
value:
mocked.map {
let transaction = TransactionState.placeholder(
amount: $0.amount,
fee: Zatoshi(10),
shielded: $0.shielded,
status: $0.status,
timestamp: $0.date,
uuid: $0.uuid
)
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
}
)
}
func getAllClearedTransactions() -> Effect<[WalletEvent], Never> { func getAllClearedTransactions() -> Effect<[WalletEvent], Never> {
let mocked: [TransactionStateMockHelper] = [ let mocked: [TransactionStateMockHelper] = [
TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"), TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),

View File

@ -40,8 +40,8 @@ struct HomeReducer: ReducerProtocol {
Zatoshi.from(decimal: shieldedBalance.total.decimalValue.decimalValue * zecPrice) Zatoshi.from(decimal: shieldedBalance.total.decimalValue.decimalValue * zecPrice)
} }
var isDownloading: Bool { var isSyncing: Bool {
if case .downloading = synchronizerStatusSnapshot.syncStatus { if case .syncing = synchronizerStatusSnapshot.syncStatus {
return true return true
} }
return false return false
@ -120,7 +120,7 @@ struct HomeReducer: ReducerProtocol {
case .synchronizerStateChanged(.synced): case .synchronizerStateChanged(.synced):
return .merge( return .merge(
sdkSynchronizer.getAllClearedTransactions() sdkSynchronizer.getAllTransactions()
.receive(on: mainQueue) .receive(on: mainQueue)
.map(HomeReducer.Action.updateWalletEvents) .map(HomeReducer.Action.updateWalletEvents)
.eraseToEffect(), .eraseToEffect(),

View File

@ -118,8 +118,8 @@ extension HomeView {
VStack { VStack {
ZStack { ZStack {
CircularProgress( CircularProgress(
outerCircleProgress: viewStore.isDownloading ? 0 : viewStore.synchronizerStatusSnapshot.progress, outerCircleProgress: viewStore.isSyncing ? 0 : viewStore.synchronizerStatusSnapshot.progress,
innerCircleProgress: viewStore.isDownloading ? viewStore.synchronizerStatusSnapshot.progress : 1, innerCircleProgress: 1,
maxSegments: viewStore.requiredTransactionConfirmations, maxSegments: viewStore.requiredTransactionConfirmations,
innerCircleHidden: viewStore.isUpToDate innerCircleHidden: viewStore.isUpToDate
) )

View File

@ -25,26 +25,26 @@ struct TransactionDetailView: View {
plainText("fee \(transaction.fee.decimalString()) ZEC", mark: .inactive) plainText("fee \(transaction.fee.decimalString()) ZEC", mark: .inactive)
plainText("total amount \(transaction.totalAmount.decimalString()) ZEC", mark: .inactive) plainText("total amount \(transaction.totalAmount.decimalString()) ZEC", mark: .inactive)
address(mark: .inactive, viewStore: viewStore) address(mark: .inactive, viewStore: viewStore)
if let text = transaction.memo?.toString() { memo(text, viewStore, mark: .highlight) } if let text = transaction.memos?.first?.toString() { memo(text, viewStore, mark: .highlight) }
confirmed(mark: .success, viewStore: viewStore) confirmed(mark: .success, viewStore: viewStore)
case .pending: case .pending:
plainText("You are sending \(transaction.zecAmount.decimalString()) ZEC") plainText("You are sending \(transaction.zecAmount.decimalString()) ZEC")
plainText("Includes network fee \(transaction.fee.decimalString()) ZEC", mark: .inactive) plainText("Includes network fee \(transaction.fee.decimalString()) ZEC", mark: .inactive)
plainText("total amount \(transaction.totalAmount.decimalString()) ZEC", mark: .inactive) plainText("total amount \(transaction.totalAmount.decimalString()) ZEC", mark: .inactive)
if let text = transaction.memo?.toString() { memo(text, viewStore, mark: .inactive) } if let text = transaction.memos?.first?.toString() { memo(text, viewStore, mark: .inactive) }
confirming(mark: .highlight, viewStore: viewStore) confirming(mark: .highlight, viewStore: viewStore)
case .received: case .received:
plainText("You received \(transaction.zecAmount.decimalString()) ZEC") plainText("You received \(transaction.zecAmount.decimalString()) ZEC")
plainText("fee \(transaction.fee.decimalString()) ZEC") plainText("fee \(transaction.fee.decimalString()) ZEC")
plainText("total amount \(transaction.totalAmount.decimalString()) ZEC") plainText("total amount \(transaction.totalAmount.decimalString()) ZEC")
address(mark: .inactive, viewStore: viewStore) address(mark: .inactive, viewStore: viewStore)
if let text = transaction.memo?.toString() { memo(text, viewStore, mark: .highlight) } if let text = transaction.memos?.first?.toString() { memo(text, viewStore, mark: .highlight) }
confirmed(mark: .success, viewStore: viewStore) confirmed(mark: .success, viewStore: viewStore)
case .failed: case .failed:
plainText("You DID NOT send \(transaction.zecAmount.decimalString()) ZEC", mark: .fail) plainText("You DID NOT send \(transaction.zecAmount.decimalString()) ZEC", mark: .fail)
plainText("Includes network fee \(transaction.fee.decimalString()) ZEC", mark: .inactive) plainText("Includes network fee \(transaction.fee.decimalString()) ZEC", mark: .inactive)
plainText("total amount \(transaction.totalAmount.decimalString()) ZEC", mark: .inactive) plainText("total amount \(transaction.totalAmount.decimalString()) ZEC", mark: .inactive)
if let text = transaction.memo?.toString() { memo(text, viewStore, mark: .inactive) } if let text = transaction.memos?.first?.toString() { memo(text, viewStore, mark: .inactive) }
if let errorMessage = transaction.errorMessage { if let errorMessage = transaction.errorMessage {
plainTwoColumnText(left: "Failed", right: errorMessage, mark: .fail) plainTwoColumnText(left: "Failed", right: errorMessage, mark: .fail)
} }
@ -68,11 +68,11 @@ extension TransactionDetailView {
Text("PENDING") Text("PENDING")
Spacer() Spacer()
case .failed: case .failed:
Text("\(transaction.date.asHumanReadable())") Text("\(transaction.date?.asHumanReadable() ?? "date not available")")
Spacer() Spacer()
Text("FAILED") Text("FAILED")
default: default:
Text("\(transaction.date.asHumanReadable())") Text("\(transaction.date?.asHumanReadable() ?? "date not available")")
Spacer() Spacer()
Text("HEIGHT \(heightText)") Text("HEIGHT \(heightText)")
} }
@ -192,7 +192,8 @@ extension TransactionDetailView {
} }
var heightText: String { var heightText: String {
transaction.minedHeight > 0 ? String(transaction.minedHeight) : "unconfirmed" guard let minedHeight = transaction.minedHeight else { return "unconfirmed" }
return minedHeight > 0 ? String(minedHeight) : "unconfirmed"
} }
} }
@ -253,12 +254,7 @@ struct TransactionDetail_Previews: PreviewProvider {
transaction: transaction:
TransactionState( TransactionState(
errorMessage: "possible roll back", errorMessage: "possible roll back",
memo: try? Memo(string: memos: [Memo.placeholder],
"""
Testing some long memo so I can see many lines of text \
instead of just one. This can take some time and I'm \
bored to write all this stuff.
"""),
minedHeight: 1_875_256, minedHeight: 1_875_256,
zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po", zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
fee: Zatoshi(1_000_000), fee: Zatoshi(1_000_000),
@ -273,3 +269,13 @@ struct TransactionDetail_Previews: PreviewProvider {
} }
} }
} }
private extension Memo {
// swiftlint:disable:next force_try
static let placeholder = try! Memo(string:
"""
Testing some long memo so I can see many lines of text \
instead of just one. This can take some time and I'm \
bored to write all this stuff.
""")
}

View File

@ -62,14 +62,14 @@ extension TransactionRowView {
var operationTitle: String { var operationTitle: String {
switch transaction.status { switch transaction.status {
case .paid(success: _): case .paid(success: _):
return "You sent to" return "You sent"
case .received: case .received:
return "Unknown paid you" return "Unknown paid you"
case .failed: case .failed:
// TODO: [#392] final text to be provided (https://github.com/zcash/secant-ios-wallet/issues/392) // TODO: [#392] final text to be provided (https://github.com/zcash/secant-ios-wallet/issues/392)
return "Transaction failed" return "Transaction failed"
case .pending: case .pending:
return "You are sending to" return "You are sending"
} }
} }

View File

@ -60,7 +60,7 @@ struct WalletEventsFlowReducer: ReducerProtocol {
if let latestMinedHeight = sdkSynchronizer.synchronizer?.latestScannedHeight { if let latestMinedHeight = sdkSynchronizer.synchronizer?.latestScannedHeight {
state.latestMinedHeight = latestMinedHeight state.latestMinedHeight = latestMinedHeight
} }
return sdkSynchronizer.getAllTransactions() return sdkSynchronizer.getAllClearedTransactions()
.receive(on: mainQueue) .receive(on: mainQueue)
.map(WalletEventsFlowReducer.Action.updateWalletEvents) .map(WalletEventsFlowReducer.Action.updateWalletEvents)
.eraseToEffect() .eraseToEffect()
@ -71,7 +71,10 @@ struct WalletEventsFlowReducer: ReducerProtocol {
case .updateWalletEvents(let walletEvents): case .updateWalletEvents(let walletEvents):
let sortedWalletEvents = walletEvents let sortedWalletEvents = walletEvents
.sorted(by: { lhs, rhs in .sorted(by: { lhs, rhs in
lhs.timestamp > rhs.timestamp guard let lhsTimestamp = lhs.timestamp, let rhsTimestamp = rhs.timestamp else {
return false
}
return lhsTimestamp > rhsTimestamp
}) })
state.walletEvents = IdentifiedArrayOf(uniqueElements: sortedWalletEvents) state.walletEvents = IdentifiedArrayOf(uniqueElements: sortedWalletEvents)
return .none return .none

View File

@ -21,18 +21,12 @@ struct SyncStatusSnapshot: Equatable {
static func snapshotFor(state: SyncStatus) -> SyncStatusSnapshot { static func snapshotFor(state: SyncStatus) -> SyncStatusSnapshot {
switch state { switch state {
case .downloading(let progress):
return SyncStatusSnapshot(state, "downloading - \(String(format: "%d%%", Int(progress.progress * 100.0)))", progress.progress)
case .enhancing(let enhanceProgress): case .enhancing(let enhanceProgress):
return SyncStatusSnapshot(state, "Enhancing tx \(enhanceProgress.enhancedTransactions) of \(enhanceProgress.totalTransactions)") return SyncStatusSnapshot(state, "Enhancing tx \(enhanceProgress.enhancedTransactions) of \(enhanceProgress.totalTransactions)")
case .fetching: case .fetching:
return SyncStatusSnapshot(state, "fetching UTXOs") return SyncStatusSnapshot(state, "fetching UTXOs")
case .scanning(let progress):
return SyncStatusSnapshot(state, "scanning - \(String(format: "%d%%", Int(progress.progress * 100.0)))", progress.progress)
case .disconnected: case .disconnected:
return SyncStatusSnapshot(state, "disconnected 💔") return SyncStatusSnapshot(state, "disconnected 💔")
@ -45,11 +39,11 @@ struct SyncStatusSnapshot: Equatable {
case .unprepared: case .unprepared:
return SyncStatusSnapshot(state, "Unprepared 😅") return SyncStatusSnapshot(state, "Unprepared 😅")
case .validating:
return SyncStatusSnapshot(state, "Validating")
case .error(let err): case .error(let err):
return SyncStatusSnapshot(state, "Error: \(err.localizedDescription)") return SyncStatusSnapshot(state, "Error: \(err.localizedDescription)")
case .syncing(let progress):
return SyncStatusSnapshot(state, "Syncing \(progress)")
} }
} }
} }

View File

@ -18,27 +18,38 @@ struct TransactionState: Equatable, Identifiable {
} }
var errorMessage: String? var errorMessage: String?
var expirationHeight = -1 var expiryHeight: BlockHeight?
var memo: Memo? var memos: [Memo]?
var minedHeight = -1 var minedHeight: BlockHeight?
var shielded = true var shielded = true
var zAddress: String? var zAddress: String?
var fee: Zatoshi var fee: Zatoshi
var id: String var id: String
var status: Status var status: Status
var timestamp: TimeInterval var timestamp: TimeInterval?
var zecAmount: Zatoshi var zecAmount: Zatoshi
var address: String { zAddress ?? "" } var address: String {
var date: Date { Date(timeIntervalSince1970: timestamp) } zAddress ?? ""
var totalAmount: Zatoshi { Zatoshi(zecAmount.amount + fee.amount) } }
var date: Date? {
guard let timestamp else { return nil }
return Date(timeIntervalSince1970: timestamp)
}
var totalAmount: Zatoshi {
Zatoshi(zecAmount.amount + fee.amount)
}
var viewOnlineURL: URL? { var viewOnlineURL: URL? {
URL(string: "https://zcashblockexplorer.com/transactions/\(id)") URL(string: "https://zcashblockexplorer.com/transactions/\(id)")
} }
func confirmationsWith(_ latestMinedHeight: BlockHeight?) -> BlockHeight { func confirmationsWith(_ latestMinedHeight: BlockHeight?) -> BlockHeight {
guard let latestMinedHeight = latestMinedHeight, minedHeight > 0, latestMinedHeight > 0 else { guard let minedHeight, let latestMinedHeight, minedHeight > 0, latestMinedHeight > 0 else {
return 0 return 0
} }
@ -47,20 +58,39 @@ struct TransactionState: Equatable, Identifiable {
} }
extension TransactionState { extension TransactionState {
init(confirmedTransaction: ConfirmedTransactionEntity, sent: Bool = false) { init(transaction: ZcashTransaction.Overview, memos: [Memo]? = nil) {
timestamp = confirmedTransaction.blockTimeInSeconds expiryHeight = transaction.expiryHeight
id = confirmedTransaction.transactionEntity.transactionId.toHexStringTxId() minedHeight = transaction.minedHeight
shielded = true fee = transaction.fee ?? Zatoshi(0)
status = sent ? .paid(success: confirmedTransaction.minedHeight > 0) : .received id = transaction.rawID.toHexStringTxId()
zAddress = confirmedTransaction.toAddress status = transaction.isSentTransaction ? .paid(success: minedHeight ?? 0 > 0) : .received
zecAmount = sent ? Zatoshi(-confirmedTransaction.value.amount) : confirmedTransaction.value timestamp = transaction.blockTime
fee = Zatoshi(10) zecAmount = transaction.isSentTransaction ? Zatoshi(-transaction.value.amount) : transaction.value
if let memoData = confirmedTransaction.memo { self.memos = memos
self.memo = try? Memo(bytes: Array(memoData))
}
minedHeight = confirmedTransaction.minedHeight
} }
init(transaction: ZcashTransaction.Sent, memos: [Memo]? = nil) {
expiryHeight = transaction.expiryHeight
minedHeight = transaction.minedHeight
fee = .zero
id = transaction.rawID?.toHexStringTxId() ?? ""
status = .paid(success: minedHeight ?? 0 > 0)
timestamp = transaction.blockTime
zecAmount = transaction.value
self.memos = memos
}
init(transaction: ZcashTransaction.Received, memos: [Memo]? = nil) {
expiryHeight = transaction.expiryHeight
minedHeight = transaction.minedHeight
fee = .zero
id = transaction.rawID?.toHexStringTxId() ?? ""
status = .received
timestamp = transaction.blockTime
zecAmount = transaction.value
self.memos = memos
}
init(pendingTransaction: PendingTransactionEntity, latestBlockHeight: BlockHeight? = nil) { init(pendingTransaction: PendingTransactionEntity, latestBlockHeight: BlockHeight? = nil) {
timestamp = pendingTransaction.createTime timestamp = pendingTransaction.createTime
id = pendingTransaction.rawTransactionId?.toHexStringTxId() ?? String(pendingTransaction.createTime) id = pendingTransaction.rawTransactionId?.toHexStringTxId() ?? String(pendingTransaction.createTime)
@ -69,12 +99,9 @@ extension TransactionState {
pendingTransaction.minedHeight > 0 ? pendingTransaction.minedHeight > 0 ?
.paid(success: pendingTransaction.isSubmitSuccess) : .paid(success: pendingTransaction.isSubmitSuccess) :
.pending .pending
expirationHeight = pendingTransaction.expiryHeight expiryHeight = pendingTransaction.expiryHeight
zecAmount = pendingTransaction.value zecAmount = pendingTransaction.value
fee = Zatoshi(10) fee = Zatoshi(10)
if let memoData = pendingTransaction.memo {
self.memo = try? Memo(bytes: Array(memoData))
}
minedHeight = pendingTransaction.minedHeight minedHeight = pendingTransaction.minedHeight
errorMessage = pendingTransaction.errorMessage errorMessage = pendingTransaction.errorMessage
@ -99,8 +126,8 @@ extension TransactionState {
uuid: String = UUID().debugDescription uuid: String = UUID().debugDescription
) -> TransactionState { ) -> TransactionState {
.init( .init(
expirationHeight: -1, expiryHeight: -1,
memo: nil, memos: nil,
minedHeight: -1, minedHeight: -1,
shielded: shielded, shielded: shielded,
zAddress: nil, zAddress: nil,

View File

@ -24,7 +24,7 @@ struct WalletEvent: Equatable, Identifiable {
let id: String let id: String
let state: WalletEventState let state: WalletEventState
var timestamp: TimeInterval var timestamp: TimeInterval?
} }
// MARK: - Rows // MARK: - Rows

View File

@ -50,18 +50,27 @@ class HomeTests: XCTestCase {
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"), TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"), TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"),
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"), TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55") TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55"),
TransactionStateMockHelper(
date: 1651039606,
amount: Zatoshi(6),
status: .paid(success: false),
uuid: "ff66"
),
TransactionStateMockHelper(date: 1651039303, amount: Zatoshi(7), uuid: "gg77"),
TransactionStateMockHelper(date: 1651039707, amount: Zatoshi(8), status: .paid(success: true), uuid: "hh88"),
TransactionStateMockHelper(date: 1651039808, amount: Zatoshi(9), uuid: "ii99")
] ]
let walletEvents: [WalletEvent] = transactionsHelper.map { let walletEvents: [WalletEvent] = transactionsHelper.map {
let transaction = TransactionState.placeholder( let transaction = TransactionState.placeholder(
amount: $0.amount, amount: $0.amount,
fee: Zatoshi(10), fee: Zatoshi(10),
shielded: $0.shielded, shielded: $0.shielded,
status: $0.status, status: $0.amount.amount > 5 ? .pending : $0.status,
timestamp: $0.date, timestamp: $0.date,
uuid: $0.uuid uuid: $0.uuid
) )
return WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp) return WalletEvent(id: transaction.id, state: $0.amount.amount > 5 ? .pending(transaction) : .send(transaction), timestamp: transaction.timestamp)
} }
store.receive(.updateWalletEvents(walletEvents)) store.receive(.updateWalletEvents(walletEvents))

View File

@ -62,10 +62,14 @@ class SendTests: XCTestCase {
} }
await testScheduler.advance(by: 0.01) await testScheduler.advance(by: 0.01)
guard let memo = try? Memo(string: "test") else {
XCTFail("testSendSucceeded: memo is expected to be successfuly initialized.")
return
}
let transactionState = TransactionState( let transactionState = TransactionState(
expirationHeight: 40, expiryHeight: 40,
memo: try? Memo(string: "test"), memos: [memo],
minedHeight: 50, minedHeight: 50,
shielded: true, shielded: true,
zAddress: "tteafadlamnelkqe", zAddress: "tteafadlamnelkqe",
@ -132,8 +136,8 @@ class SendTests: XCTestCase {
await testScheduler.advance(by: 0.01) await testScheduler.advance(by: 0.01)
let transactionState = TransactionState( let transactionState = TransactionState(
expirationHeight: 40, expiryHeight: 40,
memo: nil, memos: [],
minedHeight: 50, minedHeight: 50,
shielded: true, shielded: true,
zAddress: "tteafadlamnelkqe", zAddress: "tteafadlamnelkqe",

View File

@ -21,7 +21,7 @@ class HomeCircularProgressSnapshotTests: XCTestCase {
progressHeight: BlockHeight(55) progressHeight: BlockHeight(55)
) )
return SyncStatusSnapshot.snapshotFor(state: .downloading(blockProgress)) return SyncStatusSnapshot.snapshotFor(state: .syncing(blockProgress))
} }
} }
@ -57,7 +57,7 @@ class HomeCircularProgressSnapshotTests: XCTestCase {
progressHeight: BlockHeight(72) progressHeight: BlockHeight(72)
) )
return SyncStatusSnapshot.snapshotFor(state: .scanning(blockProgress)) return SyncStatusSnapshot.snapshotFor(state: .syncing(blockProgress))
} }
} }

View File

@ -63,13 +63,19 @@ class WalletEventsSnapshotTests: XCTestCase {
} }
func testWalletEventDetailSnapshot_sent() throws { func testWalletEventDetailSnapshot_sent() throws {
let memo = try? Memo(string:
"""
Testing some long memo so I can see many lines of text \
instead of just one. This can take some time and I'm \
bored to write all this stuff.
""")
guard let memo else {
XCTFail("testWalletEventDetailSnapshot_sent: memo is expected to be successfuly initialized")
return
}
let transaction = TransactionState( let transaction = TransactionState(
memo: try? Memo(string: memos: [memo],
"""
Testing some long memo so I can see many lines of text \
instead of just one. This can take some time and I'm \
bored to write all this stuff.
"""),
minedHeight: 1_875_256, minedHeight: 1_875_256,
zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po", zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
fee: Zatoshi(1_000_000), fee: Zatoshi(1_000_000),
@ -110,13 +116,19 @@ class WalletEventsSnapshotTests: XCTestCase {
} }
func testWalletEventDetailSnapshot_received() throws { func testWalletEventDetailSnapshot_received() throws {
let memo = try? Memo(string:
"""
Testing some long memo so I can see many lines of text \
instead of just one. This can take some time and I'm \
bored to write all this stuff.
""")
guard let memo else {
XCTFail("testWalletEventDetailSnapshot_received: memo is expected to be successfuly initialized")
return
}
let transaction = TransactionState( let transaction = TransactionState(
memo: try? Memo(string: memos: [memo],
"""
Testing some long memo so I can see many lines of text \
instead of just one. This can take some time and I'm \
bored to write all this stuff.
"""),
minedHeight: 1_875_256, minedHeight: 1_875_256,
zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po", zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
fee: Zatoshi(1_000_000), fee: Zatoshi(1_000_000),
@ -157,13 +169,19 @@ class WalletEventsSnapshotTests: XCTestCase {
} }
func testWalletEventDetailSnapshot_pending() throws { func testWalletEventDetailSnapshot_pending() throws {
let memo = try? Memo(string:
"""
Testing some long memo so I can see many lines of text \
instead of just one. This can take some time and I'm \
bored to write all this stuff.
""")
guard let memo else {
XCTFail("testWalletEventDetailSnapshot_pending: memo is expected to be successfuly initialized")
return
}
let transaction = TransactionState( let transaction = TransactionState(
memo: try? Memo(string: memos: [memo],
"""
Testing some long memo so I can see many lines of text \
instead of just one. This can take some time and I'm \
bored to write all this stuff.
"""),
minedHeight: 1_875_256, minedHeight: 1_875_256,
zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po", zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
fee: Zatoshi(1_000_000), fee: Zatoshi(1_000_000),
@ -209,14 +227,20 @@ class WalletEventsSnapshotTests: XCTestCase {
} }
func testWalletEventDetailSnapshot_failed() throws { func testWalletEventDetailSnapshot_failed() throws {
let memo = try? Memo(string:
"""
Testing some long memo so I can see many lines of text \
instead of just one. This can take some time and I'm \
bored to write all this stuff.
""")
guard let memo else {
XCTFail("testWalletEventDetailSnapshot_failed: memo is expected to be successfuly initialized")
return
}
let transaction = TransactionState( let transaction = TransactionState(
errorMessage: "possible roll back", errorMessage: "possible roll back",
memo: try? Memo(string: memos: [memo],
"""
Testing some long memo so I can see many lines of text \
instead of just one. This can take some time and I'm \
bored to write all this stuff.
"""),
minedHeight: 1_875_256, minedHeight: 1_875_256,
zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po", zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
fee: Zatoshi(1_000_000), fee: Zatoshi(1_000_000),

View File

@ -39,16 +39,7 @@ class WalletEventsTests: XCTestCase {
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"), TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"), TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid(success: true), uuid: "cc33"),
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"), TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55"), TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55")
TransactionStateMockHelper(
date: 1651039606,
amount: Zatoshi(6),
status: .paid(success: false),
uuid: "ff66"
),
TransactionStateMockHelper(date: 1651039303, amount: Zatoshi(7), uuid: "gg77"),
TransactionStateMockHelper(date: 1651039707, amount: Zatoshi(8), status: .paid(success: true), uuid: "hh88"),
TransactionStateMockHelper(date: 1651039808, amount: Zatoshi(9), uuid: "ii99")
] ]
let walletEvents: [WalletEvent] = mocked.map { let walletEvents: [WalletEvent] = mocked.map {
@ -56,7 +47,7 @@ class WalletEventsTests: XCTestCase {
amount: $0.amount, amount: $0.amount,
fee: Zatoshi(10), fee: Zatoshi(10),
shielded: $0.shielded, shielded: $0.shielded,
status: $0.amount.amount > 5 ? .pending : $0.status, status: $0.status,
timestamp: $0.date, timestamp: $0.date,
uuid: $0.uuid uuid: $0.uuid
) )
@ -90,7 +81,10 @@ class WalletEventsTests: XCTestCase {
uniqueElements: uniqueElements:
walletEvents walletEvents
.sorted(by: { lhs, rhs in .sorted(by: { lhs, rhs in
lhs.timestamp > rhs.timestamp guard let lhsTimestamp = lhs.timestamp, let rhsTimestamp = rhs.timestamp else {
return false
}
return lhsTimestamp > rhsTimestamp
}) })
) )