Step 8: Received transactions

This commit is contained in:
Francisco Gindre 2021-02-25 00:52:09 -03:00
parent dbdeb8bcc7
commit 15ff8e6b37
11 changed files with 319 additions and 71 deletions

View File

@ -1,6 +1,6 @@
use_frameworks!
target 'accept-zcash-poc' do
pod 'ZcashLightClientKit'
pod 'ZcashLightClientKit', :git => 'https://github.com/zcash/ZcashLightClientKit.git', :branch => 'key-validation'
end
post_install do |installer|

View File

@ -1,14 +1,13 @@
PODS:
- CGRPCZlib (1.0.0-alpha.19)
- CNIOAtomics (2.25.1)
- CNIOBoringSSL (2.10.2)
- CNIOBoringSSLShims (2.10.2):
- CNIOBoringSSL (= 2.10.2)
- CNIODarwin (2.25.1)
- CNIOHTTPParser (2.25.1)
- CNIOLinux (2.25.1)
- CNIOSHA1 (2.25.1)
- CNIOWindows (2.25.1)
- CNIOAtomics (2.26.0)
- CNIOBoringSSL (2.10.3)
- CNIOBoringSSLShims (2.10.3):
- CNIOBoringSSL (= 2.10.3)
- CNIODarwin (2.26.0)
- CNIOHTTPParser (2.26.0)
- CNIOLinux (2.26.0)
- CNIOWindows (2.26.0)
- gRPC-Swift (1.0.0-alpha.19):
- CGRPCZlib (= 1.0.0-alpha.19)
- Logging (< 2, >= 1.2.0)
@ -21,51 +20,49 @@ PODS:
- SQLite.swift (0.12.2):
- SQLite.swift/standard (= 0.12.2)
- SQLite.swift/standard (0.12.2)
- SwiftNIO (2.25.1):
- CNIOAtomics (= 2.25.1)
- CNIODarwin (= 2.25.1)
- CNIOLinux (= 2.25.1)
- CNIOSHA1 (= 2.25.1)
- CNIOWindows (= 2.25.1)
- SwiftNIOConcurrencyHelpers (= 2.25.1)
- SwiftNIOConcurrencyHelpers (2.25.1):
- CNIOAtomics (= 2.25.1)
- SwiftNIOFoundationCompat (2.25.1):
- SwiftNIO (= 2.25.1)
- SwiftNIOHPACK (1.16.2):
- SwiftNIO (2.26.0):
- CNIODarwin (= 2.26.0)
- CNIOLinux (= 2.26.0)
- CNIOWindows (= 2.26.0)
- SwiftNIOConcurrencyHelpers (= 2.26.0)
- SwiftNIOConcurrencyHelpers (2.26.0):
- CNIOAtomics (= 2.26.0)
- SwiftNIOFoundationCompat (2.26.0):
- SwiftNIO (= 2.26.0)
- SwiftNIOHPACK (1.16.3):
- SwiftNIO (< 3, >= 2.18.0)
- SwiftNIOConcurrencyHelpers (< 3, >= 2.18.0)
- SwiftNIOHTTP1 (< 3, >= 2.18.0)
- SwiftNIOHTTP1 (2.25.1):
- CNIOHTTPParser (= 2.25.1)
- SwiftNIO (= 2.25.1)
- SwiftNIOConcurrencyHelpers (= 2.25.1)
- SwiftNIOHTTP2 (1.16.2):
- SwiftNIOHTTP1 (2.26.0):
- CNIOHTTPParser (= 2.26.0)
- SwiftNIO (= 2.26.0)
- SwiftNIOConcurrencyHelpers (= 2.26.0)
- SwiftNIOHTTP2 (1.16.3):
- SwiftNIO (< 3, >= 2.18.0)
- SwiftNIOConcurrencyHelpers (< 3, >= 2.18.0)
- SwiftNIOHPACK (= 1.16.2)
- SwiftNIOHPACK (= 1.16.3)
- SwiftNIOHTTP1 (< 3, >= 2.18.0)
- SwiftNIOTLS (< 3, >= 2.18.0)
- SwiftNIOSSL (2.10.2):
- CNIOBoringSSL (= 2.10.2)
- CNIOBoringSSLShims (= 2.10.2)
- SwiftNIOSSL (2.10.3):
- CNIOBoringSSL (= 2.10.3)
- CNIOBoringSSLShims (= 2.10.3)
- SwiftNIO (< 3, >= 2.15.0)
- SwiftNIOConcurrencyHelpers (< 3, >= 2.15.0)
- SwiftNIOTLS (< 3, >= 2.15.0)
- SwiftNIOTLS (2.25.1):
- SwiftNIO (= 2.25.1)
- SwiftNIOTransportServices (1.9.1):
- SwiftNIOTLS (2.26.0):
- SwiftNIO (= 2.26.0)
- SwiftNIOTransportServices (1.9.2):
- SwiftNIO (< 3, >= 2.19.0)
- SwiftNIOConcurrencyHelpers (< 3, >= 2.19.0)
- SwiftNIOFoundationCompat (< 3, >= 2.19.0)
- SwiftNIOTLS (< 3, >= 2.19.0)
- SwiftProtobuf (1.15.0)
- ZcashLightClientKit (0.9.2):
- ZcashLightClientKit (0.9.3):
- gRPC-Swift (= 1.0.0-alpha.19)
- SQLite.swift (~> 0.12.2)
DEPENDENCIES:
- ZcashLightClientKit
- ZcashLightClientKit (from `https://github.com/zcash/ZcashLightClientKit.git`, branch `key-validation`)
SPEC REPOS:
trunk:
@ -76,7 +73,6 @@ SPEC REPOS:
- CNIODarwin
- CNIOHTTPParser
- CNIOLinux
- CNIOSHA1
- CNIOWindows
- gRPC-Swift
- Logging
@ -91,33 +87,41 @@ SPEC REPOS:
- SwiftNIOTLS
- SwiftNIOTransportServices
- SwiftProtobuf
- ZcashLightClientKit
EXTERNAL SOURCES:
ZcashLightClientKit:
:branch: key-validation
:git: https://github.com/zcash/ZcashLightClientKit.git
CHECKOUT OPTIONS:
ZcashLightClientKit:
:commit: 01a55a5e1e64b3875553ace409d6dfbd8f252bf6
:git: https://github.com/zcash/ZcashLightClientKit.git
SPEC CHECKSUMS:
CGRPCZlib: befd9b92013c647f18aa9b7d9e9231d52e1ced1b
CNIOAtomics: 2ae668942b9e7ac71e0fe286d11ac9b4fe329024
CNIOBoringSSL: 3d59b923c8ec524308685664ae6388179c678ac7
CNIOBoringSSLShims: 18b267690ba0d57b22e4e69dff2904a6b1d030b8
CNIODarwin: 3874630a0e3506c3e86f272ad436103d27055831
CNIOHTTPParser: 976fcf22759946c80ff74c8a2926728d88954a83
CNIOLinux: e6394d7fd56d36ff43ada5796e9d96c14fcb3546
CNIOSHA1: 43c4a0cfdea5e8be20927d9d2237ebe4233071f8
CNIOWindows: 89048520e4a1484bcd5e20c252edf4089913aa7d
CNIOAtomics: 7599e2500f637f1e043047da52efd710d6020dd3
CNIOBoringSSL: 8e20acf863216a6d7b75a56fe0567a36147d8ee0
CNIOBoringSSLShims: 72cc9d43e6f5d8a137a2699eb08a0c67865c9797
CNIODarwin: 68f6ff440e989ef43ca6eb3335f3e5484066fab7
CNIOHTTPParser: 26678afdbc27f24d1a3498bf87413bfc3391fbc5
CNIOLinux: 5fd92122d58b04508074371d95a1f874310fe8e3
CNIOWindows: 0af55cf84c3811318aee91888737e0b124552029
gRPC-Swift: 75a7a8d7cd08177165ef971159afb84e5130cfc9
Logging: beeb016c9c80cf77042d62e83495816847ef108b
SQLite.swift: d2b4642190917051ce6bd1d49aab565fe794eea3
SwiftNIO: 3f9a197581f6dfebf65a5c85099f25197d892c8c
SwiftNIOConcurrencyHelpers: 3670690a0fb8d8a0a4759804d07aaf89c9599839
SwiftNIOFoundationCompat: 707c223452d1606e1619c2309f089aeadbf2943e
SwiftNIOHPACK: a86298d347455478dcc0d0178b80bbbd38bdf299
SwiftNIOHTTP1: 566744e4a9b4425009b59dce9ed962591052b0d0
SwiftNIOHTTP2: 474925ac2ebabc5899892f7cafc0323eceb67f8c
SwiftNIOSSL: 39756339a7425d68b48f00c0f9a9e068b8f536d0
SwiftNIOTLS: 434418842a6bebc21864ec84229e5e3d2762ca0b
SwiftNIOTransportServices: a8a17a43695a09bfdd5a56426f6e515120d5dbf3
SwiftNIO: 1f309539cfe7ed42648f505a48c546e83a2f66c3
SwiftNIOConcurrencyHelpers: ebbceb814afcd8b4fab86935855f6c8c77ae4285
SwiftNIOFoundationCompat: 6869943bce8c6b777114d840f7d795178a7391ae
SwiftNIOHPACK: 38e855a72ae0c5176485ddd039b3933b99daa2b7
SwiftNIOHTTP1: 7f7aa111d51d09a97ef1fddb50855074e5e42778
SwiftNIOHTTP2: de7eff9d32fd347338f85b86c6fd0e13c3fbd1a0
SwiftNIOSSL: ac6e05adff65df9a3b518bbd27ff1f152fa82999
SwiftNIOTLS: 0faded6aff802c423ddee27828850a8ea4a818d0
SwiftNIOTransportServices: 896c9a4ac98698d32aa2feea7657ade219ae80bb
SwiftProtobuf: 3320217e9d8fb75f36b40282e78c482640fd75dd
ZcashLightClientKit: 56d5c41ecf19d290a2aa25437775f11285edb4fe
ZcashLightClientKit: 2bea7cdf2ddf098795688a198916e9d4ae6c1239
PODFILE CHECKSUM: 39abcdbfeeed27079a3b490f92dba01a275172a7
PODFILE CHECKSUM: 475acd7bc72abd32c68bdf0f8ee3ac3311a9cec2
COCOAPODS: 1.10.1

View File

@ -376,3 +376,88 @@ And that's it! Customers will be able to scan our Zcash Sapling Address!
Our next step will be receiving the transactions.
## Tag: `step-8-received-transactions`
We synced our Viewing Key and created a Request Zec Screen. Now we need to know whether we received the payment or not.
We follow our experts' recommendation of waiting 10 confirmations for those funds to be spendable, but we will be able to see the incoming payment as soon as it is mined.
So let's give our `ReceivedTransactions` screen some love.
first let's add the ZcashEnvironment and the App's model, and also two @State variables, one for the selection (yes, we are going to see the Tx details too!) and the other ones for the transactions that were decrypted with our viewing key.
````
struct ReceivedTransactions: View {
@EnvironmentObject var model: ZcashPoSModel
@Environment(\.zcashEnvironment) var zcash: ZcashEnvironment
@State var selection: DetailModel? = nil
@State var transactions: [DetailModel] = []
var body: some View {
NavigationView {
buildBody(transactions: transactions)
.navigationBarTitle("Received Transactions", displayMode: .inline)
.navigationBarHidden(false)
.onReceive(zcash.synchronizer.receivedTransactionBuffer) { (r) in
self.transactions = r
}
}.onAppear() {
let clearView = UIView()
clearView.backgroundColor = UIColor.clear
UITableViewCell.appearance().selectedBackgroundView = clearView
UITableView.appearance().backgroundColor = UIColor.clear
}
}
````
We are going to show an empty state when we don't see any transactions and an (ugly) list when we do with this view builder
````
@ViewBuilder func buildBody(transactions: [DetailModel]) -> some View {
if transactions.isEmpty {
ZStack {
ZcashBackground()
Text("No transactions yet")
.foregroundColor(.white)
}
} else {
ZStack {
ZcashBackground()
List(transactions) { (row) in
ZStack {
ZcashBackground()
NavigationLink(destination: TransactionDetails(model: row,isCopyAlertShown: false), tag: row, selection: $selection) {
EmptyView()
}
HStack {
VStack {
Text(row.memo ?? "No Memo")
.foregroundColor(.white)
Text(row.subtitle)
.foregroundColor(.white)
}
Text("$\(row.zecAmount.toZecAmount())")
.foregroundColor(.zPositiveZecAmount)
}
.frame(height: 64)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.padding(0)
}
}
.listStyle(PlainListStyle())
.listRowBackground(ZcashBackground())
}
}
````
I once nuked my app by mistake so we are also going to fix that. On this commit you will see several bug fixes! If you find more, or have improvement ideas, please send a pull request!
Alright Folks! This is it for the time being!
Android devs, I dare you to create the same app for your beloved green robot using the zcash-android-wallet-sdk!

View File

@ -17,6 +17,7 @@
0D08DEB025E732C900E08533 /* Zboto.otf in Resources */ = {isa = PBXBuildFile; fileRef = 0D08DEAF25E732C900E08533 /* Zboto.otf */; };
0D08DEB325E7397A00E08533 /* QRCodeGenerator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D08DEB225E7397A00E08533 /* QRCodeGenerator.swift */; };
0D08DEB625E73B7000E08533 /* QRCodeContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D08DEB525E73B7000E08533 /* QRCodeContainer.swift */; };
0D26A58625E741D30034E54D /* TransactionDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D26A58525E741D30034E54D /* TransactionDetail.swift */; };
0D475B2125E3ED600067978E /* CombineSynchronizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D475B2025E3ED600067978E /* CombineSynchronizer.swift */; };
0D475B2725E3EEE10067978E /* DetailModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D475B2625E3EEE10067978E /* DetailModel.swift */; };
0D475B2A25E3F04C0067978E /* String+Zcash.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0D475B2925E3F04C0067978E /* String+Zcash.swift */; };
@ -55,6 +56,7 @@
0D08DEAF25E732C900E08533 /* Zboto.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = Zboto.otf; sourceTree = "<group>"; };
0D08DEB225E7397A00E08533 /* QRCodeGenerator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QRCodeGenerator.swift; sourceTree = "<group>"; };
0D08DEB525E73B7000E08533 /* QRCodeContainer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QRCodeContainer.swift; sourceTree = "<group>"; };
0D26A58525E741D30034E54D /* TransactionDetail.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionDetail.swift; sourceTree = "<group>"; };
0D475B2025E3ED600067978E /* CombineSynchronizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CombineSynchronizer.swift; sourceTree = "<group>"; };
0D475B2625E3EEE10067978E /* DetailModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailModel.swift; sourceTree = "<group>"; };
0D475B2925E3F04C0067978E /* String+Zcash.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Zcash.swift"; sourceTree = "<group>"; };
@ -119,6 +121,7 @@
0D475B8A25E478750067978E /* Views */ = {
isa = PBXGroup;
children = (
0D26A58525E741D30034E54D /* TransactionDetail.swift */,
0D5050A925DFEFAC00E7A697 /* ImportViewingKey.swift */,
0DC0819625E53E690028532F /* HomeScreen.swift */,
0D08DE7225E6FE4B00E08533 /* SellScreen.swift */,
@ -368,6 +371,7 @@
0D475B2A25E3F04C0067978E /* String+Zcash.swift in Sources */,
0D475B2125E3ED600067978E /* CombineSynchronizer.swift in Sources */,
0D475B3025E3F34E0067978E /* SimpleLogger.swift in Sources */,
0D26A58625E741D30034E54D /* TransactionDetail.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -202,7 +202,9 @@ class CombineSynchronizer {
self.balance.send(initializer.getBalance().asHumanReadableZecBalance())
self.verifiedBalance.send(initializer.getVerifiedBalance().asHumanReadableZecBalance())
self.status.send(.synced)
self.receivedTransactions.sink(receiveCompletion: { _ in
self.receivedTransactions
.receive(on: DispatchQueue.main)
.sink(receiveCompletion: { _ in
}) { [weak self] (details) in
guard !details.isEmpty else { return }
self?.receivedTransactionBuffer.send(details.map({ DetailModel(confirmedTransaction: $0) }))

View File

@ -42,6 +42,8 @@ extension DetailModel: Equatable {
lhs.id == rhs.id
}
}
extension DetailModel: Hashable {}
extension DetailModel.Status: Hashable {}
import ZcashLightClientKit
extension Date {

View File

@ -67,6 +67,7 @@ struct ImportViewingKey: View {
Text("Import Viewing Key")
.foregroundColor(.black)
.zcashButtonBackground(shape: .roundedCorners(fillStyle: .gradient(gradient: .zButtonGradient)))
.frame(height: 48)
}
.disabled(!isFormValid)
.opacity(isFormValid ? 1.0 : 0.4)

View File

@ -8,11 +8,66 @@
import SwiftUI
struct ReceivedTransactions: View {
@EnvironmentObject var model: ZcashPoSModel
@Environment(\.zcashEnvironment) var zcash: ZcashEnvironment
@State var selection: DetailModel? = nil
@State var transactions: [DetailModel] = []
var body: some View {
ZStack {
ZcashBackground()
Text("This is where our transactions will be")
.foregroundColor(.white)
NavigationView {
buildBody(transactions: transactions)
.navigationBarTitle("Received Transactions", displayMode: .inline)
.navigationBarHidden(false)
.onReceive(zcash.synchronizer.receivedTransactionBuffer) { (r) in
self.transactions = r
}
}.onAppear() {
let clearView = UIView()
clearView.backgroundColor = UIColor.clear
UITableViewCell.appearance().selectedBackgroundView = clearView
UITableView.appearance().backgroundColor = UIColor.clear
}
}
@ViewBuilder func buildBody(transactions: [DetailModel]) -> some View {
if transactions.isEmpty {
ZStack {
ZcashBackground()
Text("No transactions yet")
.foregroundColor(.white)
}
} else {
ZStack {
ZcashBackground()
List(transactions) { (row) in
ZStack {
ZcashBackground()
NavigationLink(destination: TransactionDetails(model: row,isCopyAlertShown: false), tag: row, selection: $selection) {
EmptyView()
}
HStack {
VStack {
Text(row.memo ?? "No Memo")
.foregroundColor(.white)
Text(row.subtitle)
.foregroundColor(.white)
}
Text("$\(row.zecAmount.toZecAmount())")
.foregroundColor(.zPositiveZecAmount)
}
.frame(height: 64)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.padding(0)
}
}
.listStyle(PlainListStyle())
.listRowBackground(ZcashBackground())
}
}
}
}

View File

@ -23,10 +23,10 @@ struct RequestZec: View {
var body: some View {
ZStack {
ZcashBackground()
VStack(alignment: .center, spacing: 40){
VStack(alignment: .center, spacing: 20){
QRCodeContainer(qrImage: qrImage,
badge: Image("QR-zcashlogo"))
.frame(width: 150, height: 150, alignment: .center)
.frame(width: 200, height: 200, alignment: .center)
.layoutPriority(1)
Text("$\(model.request.amount.toZecAmount())")

View File

@ -24,6 +24,7 @@ struct SettingsScreen: View {
ZcashBackground()
VStack {
ZcashLogo()
Spacer()
switch status {
case .offline:
Button(action: {
@ -36,16 +37,18 @@ struct SettingsScreen: View {
case .syncing:
Text("Syncing \(progress)% Block: \(height)").foregroundColor(.white)
}
Button(action: {
zcash.synchronizer.stop()
model.nuke()
try! zcash.nuke()
model.navigation = .importViewingKey
}) {
Spacer()
Text("Stop And Nuke")
.foregroundColor(.red)
.font(.title3)
}
.onLongPressGesture {
zcash.synchronizer.stop()
model.nuke()
try! zcash.nuke()
model.navigation = .importViewingKey
}
}
}
.onReceive(zcash.synchronizer.progress) { (p) in

View File

@ -0,0 +1,92 @@
//
// TransactionDetail.swift
// wallet
//
// Created by Francisco Gindre on 4/14/20.
// Copyright © 2020 Francisco Gindre. All rights reserved.
//
import Foundation
import SwiftUI
struct TransactionDetails: View {
var model: DetailModel
@State var isCopyAlertShown: Bool = false
var status: String {
switch model.status {
case .paid(let success):
return success ? "Outbound" : "Unsent"
case .received:
return "Inbound"
}
}
func copyToClipBoard(_ content: String) {
UIPasteboard.general.string = content
logger.debug("content copied to clipboard")
self.isCopyAlertShown = true
}
var body: some View {
ZStack {
ZcashBackground()
VStack {
ScrollView([.vertical], showsIndicators: false) {
Text("$\(model.zecAmount.toZecAmount())")
.lineLimit(1)
.minimumScaleFactor(0.5)
.foregroundColor(.white)
.font(
.custom("Zboto", size: 72)
)
Text(status).foregroundColor(.white)
.font(.largeTitle)
Spacer()
VStack(alignment: .center, spacing: 10) {
if model.minedHeight > 0 {
DetailCell(title: "Mined Height", description: "\(model.minedHeight)", action: nil)
}
DetailCell(title: "Tx Id:" , description: model.id, action: self.copyToClipBoard)
DetailCell(title: "Date:", description: model.date.description)
DetailCell(title: "Shielded:", description: model.shielded ? "🛡" : "")
DetailCell(title: "Memo:", description: model.memo ?? "No memo" , action: self.copyToClipBoard)
DetailCell(title: "Address:", description: model.zAddress ?? "", action: self.copyToClipBoard).opacity( model.zAddress != nil ? 1.0 : 0)
}
Spacer()
}.padding(.horizontal, 40)
}
}.alert(isPresented: self.$isCopyAlertShown) {
Alert(title: Text(""),
message: Text("feedback_addresscopied"),
dismissButton: .default(Text("button_close"))
)
}
.navigationBarTitle(Text("Transaction Detail"), displayMode: .inline)
.navigationBarBackButtonHidden(false)
}
}
struct DetailCell: View {
var title: String
var description: String
var action: ((String) -> Void)?
var body: some View {
VStack {
Text(title).foregroundColor(.zYellow)
.font(.title)
Text(description)
.foregroundColor(.white)
}
.onTapGesture {
self.action?(self.description)
}
}
}