[96] [Scaffold] Received Transaction Details (#378)
- the view holding all requested (mocked) data is prepared - copy to pasteboard implemented - deeplink 'reply-to' memo implemented - view online implemented and button visible only when the URL is valid - asset colors for the texts - unit tests - snapshot tests
This commit is contained in:
parent
e321114214
commit
df2c721d32
|
@ -120,6 +120,7 @@
|
|||
9E5BF64F2823E94900BA3F17 /* TransactionAddressTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF64D2823E94900BA3F17 /* TransactionAddressTextField.swift */; };
|
||||
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 */; };
|
||||
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 */; };
|
||||
|
@ -329,6 +330,7 @@
|
|||
9E5BF64D2823E94900BA3F17 /* TransactionAddressTextField.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransactionAddressTextField.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
|
@ -810,6 +812,7 @@
|
|||
9E391162284E3ECF0073DD9A /* SnapshotTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9E7CB6102869881300A02233 /* WalletEventsSnapshotTests */,
|
||||
9E9ECC8B28589E150099D5A2 /* HomeSnapshotTests */,
|
||||
9E9ECC9328589E150099D5A2 /* ImportWalletSnapshotTests */,
|
||||
9E9ECC9528589E150099D5A2 /* OnboardingSnapshotTests */,
|
||||
|
@ -858,6 +861,14 @@
|
|||
path = TransactionAddress;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9E7CB6102869881300A02233 /* WalletEventsSnapshotTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
9E7CB6112869882D00A02233 /* WalletEventsSnapshotTests.swift */,
|
||||
);
|
||||
path = WalletEventsSnapshotTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
9E7FE0B6282D1D9800C374E8 /* Resources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
|
@ -1543,6 +1554,7 @@
|
|||
9EDDEAA32829610D00B4100C /* TransactionAmountInputTests.swift in Sources */,
|
||||
9EAFEB862805A23100199FC9 /* WrappedSecItemTests.swift in Sources */,
|
||||
9E9ECC9828589E150099D5A2 /* WelcomeSnapshotTests.swift in Sources */,
|
||||
9E7CB6122869882D00A02233 /* WalletEventsSnapshotTests.swift in Sources */,
|
||||
9E5BF644281FEC9900BA3F17 /* SendTests.swift in Sources */,
|
||||
0D1C1AA327611EFD0004AF6A /* RecoveryPhraseDisplayReducerTests.swift in Sources */,
|
||||
9E9ECC9A28589E150099D5A2 /* RecoveryPhraseValidationFlowSnapshotTests.swift in Sources */,
|
||||
|
|
|
@ -342,6 +342,12 @@ extension AppReducer {
|
|||
state.homeState.sendState.memo = memo
|
||||
return .none
|
||||
|
||||
case .home(.walletEvents(.replyTo(let address))):
|
||||
guard let url = URL(string: "zcash:\(address)") else {
|
||||
return .none
|
||||
}
|
||||
return Effect(value: .deeplink(url))
|
||||
|
||||
/// Default is meaningful here because there's `appReducer` handling actions and this reducer is handling only routes. We don't here plenty of unused cases.
|
||||
default:
|
||||
break
|
||||
|
|
|
@ -181,7 +181,8 @@ extension HomeReducer {
|
|||
environment: { environment in
|
||||
WalletEventsFlowEnvironment(
|
||||
scheduler: environment.scheduler,
|
||||
SDKSynchronizer: environment.SDKSynchronizer
|
||||
SDKSynchronizer: environment.SDKSynchronizer,
|
||||
pasteboard: .live
|
||||
)
|
||||
}
|
||||
)
|
||||
|
|
|
@ -50,7 +50,8 @@ extension SandboxReducer {
|
|||
walletEventsAction,
|
||||
WalletEventsFlowEnvironment(
|
||||
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
SDKSynchronizer: LiveWrappedSDKSynchronizer()
|
||||
SDKSynchronizer: LiveWrappedSDKSynchronizer(),
|
||||
pasteboard: .live
|
||||
)
|
||||
)
|
||||
.map(SandboxAction.walletEvents)
|
||||
|
|
|
@ -1,11 +1,146 @@
|
|||
import SwiftUI
|
||||
import ComposableArchitecture
|
||||
|
||||
struct TransactionDetailView: View {
|
||||
var transaction: TransactionState
|
||||
var viewStore: WalletEventsFlowViewStore
|
||||
|
||||
var body: some View {
|
||||
Text(String(dumping: transaction))
|
||||
ScrollView {
|
||||
HStack {
|
||||
Text("\(transaction.date.asHumanReadable())")
|
||||
Spacer()
|
||||
Text("HEIGHT \(heightText)")
|
||||
}
|
||||
.padding()
|
||||
.navigationTitle("Transaction: \(transaction.id)")
|
||||
|
||||
Text("\(amountPrefixText) \(transaction.zecAmount.decimalString()) ZEC")
|
||||
.transactionDetailRow()
|
||||
|
||||
Text("fee \(transaction.fee.decimalString()) ZEC")
|
||||
.transactionDetailRow()
|
||||
|
||||
Text("total amount \(transaction.totalAmount.decimalString()) ZEC")
|
||||
.transactionDetailRow()
|
||||
|
||||
Button {
|
||||
viewStore.send(.copyToPastboard(transaction.address))
|
||||
} label: {
|
||||
Text("\(addressPrefixText) \(transaction.address)")
|
||||
.lineLimit(1)
|
||||
.truncationMode(.middle)
|
||||
.transactionDetailRow()
|
||||
}
|
||||
|
||||
if let memo = transaction.memo {
|
||||
Button {
|
||||
viewStore.send(.copyToPastboard(memo))
|
||||
} label: {
|
||||
VStack {
|
||||
Text("\(memo)")
|
||||
.multilineTextAlignment(.leading)
|
||||
|
||||
HStack {
|
||||
Text("reply-to address included")
|
||||
Spacer()
|
||||
Button {
|
||||
viewStore.send(.replyTo(transaction.address))
|
||||
} label: {
|
||||
Text("reply now")
|
||||
.padding(5)
|
||||
.overlay(
|
||||
RoundedRectangle(cornerRadius: 6)
|
||||
.stroke(Asset.Colors.Text.transactionDetailText.color, lineWidth: 1)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
.transactionDetailRow()
|
||||
}
|
||||
}
|
||||
|
||||
HStack {
|
||||
Text("Confirmed")
|
||||
Spacer()
|
||||
Text("\(transaction.confirmations) times")
|
||||
}
|
||||
.transactionDetailRow()
|
||||
|
||||
Spacer()
|
||||
|
||||
Button {
|
||||
viewStore.send(.copyToPastboard(transaction.id))
|
||||
} label: {
|
||||
Text("txn: \(transaction.id)")
|
||||
.foregroundColor(Asset.Colors.Text.transactionDetailText.color)
|
||||
.font(.system(size: 14))
|
||||
.fontWeight(.thin)
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
.padding(.horizontal, 60)
|
||||
.padding(.vertical, 10)
|
||||
.background(Asset.Colors.BackgroundColors.numberedChip.color)
|
||||
.padding(.vertical, 30)
|
||||
}
|
||||
|
||||
Button { } label: {
|
||||
// TODO: Warn users that they will leave the App when they follow a Block explorer
|
||||
// https://github.com/zcash/secant-ios-wallet/issues/379
|
||||
if let viewOnlineURL = transaction.viewOnlineURL {
|
||||
Link("View online", destination: viewOnlineURL)
|
||||
}
|
||||
}
|
||||
.activeButtonStyle
|
||||
.frame(height: 50)
|
||||
.padding(.horizontal, 30)
|
||||
}
|
||||
.applyScreenBackground()
|
||||
.navigationTitle("Transaction detail")
|
||||
}
|
||||
}
|
||||
|
||||
extension TransactionDetailView {
|
||||
var amountPrefixText: String {
|
||||
transaction.status == .received ? "You received" : "You sent"
|
||||
}
|
||||
|
||||
var addressPrefixText: String {
|
||||
transaction.status == .received ? "from" : "to"
|
||||
}
|
||||
|
||||
var heightText: String {
|
||||
transaction.minedHeight > 0 ? String(transaction.minedHeight) : "unconfirmed"
|
||||
}
|
||||
}
|
||||
|
||||
struct TransactionDetailRow: ViewModifier {
|
||||
let tint: Color
|
||||
let textColor: Color
|
||||
let backgroundColor: Color
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
content
|
||||
.foregroundColor(textColor)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.padding()
|
||||
.background(backgroundColor)
|
||||
.padding(.leading, 20)
|
||||
.background(tint)
|
||||
}
|
||||
}
|
||||
|
||||
extension View {
|
||||
func transactionDetailRow(
|
||||
_ tint: Color = Asset.Colors.BackgroundColors.red.color,
|
||||
_ textColor: Color = Asset.Colors.Text.transactionDetailText.color,
|
||||
_ backgroundColor: Color = Asset.Colors.BackgroundColors.numberedChip.color
|
||||
) -> some View {
|
||||
modifier(
|
||||
TransactionDetailRow(
|
||||
tint: tint,
|
||||
textColor: textColor,
|
||||
backgroundColor: backgroundColor
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,7 +149,38 @@ struct TransactionDetailView: View {
|
|||
struct TransactionDetail_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NavigationView {
|
||||
TransactionDetailView(transaction: .placeholder)
|
||||
TransactionDetailView(
|
||||
transaction:
|
||||
TransactionState(
|
||||
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,
|
||||
zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
|
||||
fee: Zatoshi(amount: 1_000_000),
|
||||
id: "ff3927e1f83df9b1b0dc75540ddc59ee435eecebae914d2e6dfe8576fbedc9a8",
|
||||
status: .paid(success: true),
|
||||
subtitle: "",
|
||||
timestamp: 1234567,
|
||||
zecAmount: Zatoshi(amount: 25_000_000)
|
||||
),
|
||||
viewStore: ViewStore(
|
||||
WalletEventsFlowStore(
|
||||
initialState: .placeHolder,
|
||||
reducer: .default,
|
||||
environment:
|
||||
WalletEventsFlowEnvironment(
|
||||
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
SDKSynchronizer: MockWrappedSDKSynchronizer(),
|
||||
pasteboard: .test
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,9 +23,11 @@ struct WalletEventsFlowState: Equatable {
|
|||
// MARK: - Action
|
||||
|
||||
enum WalletEventsFlowAction: Equatable {
|
||||
case copyToPastboard(String)
|
||||
case onAppear
|
||||
case onDisappear
|
||||
case updateRoute(WalletEventsFlowState.Route?)
|
||||
case replyTo(String)
|
||||
case synchronizerStateChanged(WrappedSDKSynchronizerState)
|
||||
case updateWalletEvents([WalletEvent])
|
||||
}
|
||||
|
@ -35,6 +37,7 @@ enum WalletEventsFlowAction: Equatable {
|
|||
struct WalletEventsFlowEnvironment {
|
||||
let scheduler: AnySchedulerOf<DispatchQueue>
|
||||
let SDKSynchronizer: WrappedSDKSynchronizer
|
||||
let pasteboard: WrappedPasteboard
|
||||
}
|
||||
|
||||
// MARK: - Reducer
|
||||
|
@ -70,9 +73,16 @@ extension WalletEventsFlowReducer {
|
|||
state.walletEvents = IdentifiedArrayOf(uniqueElements: sortedWalletEvents)
|
||||
return .none
|
||||
|
||||
case let .updateRoute(route):
|
||||
case .updateRoute(let route):
|
||||
state.route = route
|
||||
return .none
|
||||
|
||||
case .copyToPastboard(let value):
|
||||
environment.pasteboard.setString(value)
|
||||
return .none
|
||||
|
||||
case .replyTo(let address):
|
||||
return .none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -97,6 +107,7 @@ extension WalletEventsFlowViewStore {
|
|||
extension TransactionState {
|
||||
static var placeholder: Self {
|
||||
.init(
|
||||
fee: Zatoshi(amount: 10),
|
||||
id: "2",
|
||||
status: .paid(success: true),
|
||||
subtitle: "",
|
||||
|
@ -123,7 +134,8 @@ extension WalletEventsFlowStore {
|
|||
reducer: .default,
|
||||
environment: WalletEventsFlowEnvironment(
|
||||
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
SDKSynchronizer: LiveWrappedSDKSynchronizer()
|
||||
SDKSynchronizer: LiveWrappedSDKSynchronizer(),
|
||||
pasteboard: .live
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -134,6 +146,7 @@ extension IdentifiedArrayOf where Element == TransactionState {
|
|||
return .init(
|
||||
uniqueElements: (0..<30).map {
|
||||
TransactionState(
|
||||
fee: Zatoshi(amount: 10),
|
||||
id: String($0),
|
||||
status: .paid(success: true),
|
||||
subtitle: "",
|
||||
|
|
|
@ -33,11 +33,11 @@ extension WalletEventsFlowView {
|
|||
ForEach(viewStore.walletEvents) { walletEvent in
|
||||
WithStateBinding(binding: viewStore.bindingForSelectingWalletEvent(walletEvent)) { active in
|
||||
VStack {
|
||||
walletEvent.rowView()
|
||||
walletEvent.rowView(viewStore)
|
||||
}
|
||||
.navigationLink(
|
||||
isActive: active,
|
||||
destination: { walletEvent.detailView() }
|
||||
destination: { walletEvent.detailView(viewStore) }
|
||||
)
|
||||
.foregroundColor(Asset.Colors.Text.body.color)
|
||||
.listRowBackground(Color.clear)
|
||||
|
|
|
@ -16,19 +16,26 @@ struct TransactionState: Equatable, Identifiable {
|
|||
case failed
|
||||
}
|
||||
|
||||
var confirmations = 0
|
||||
var expirationHeight = -1
|
||||
var memo: String?
|
||||
var minedHeight = -1
|
||||
var shielded = true
|
||||
var zAddress: String?
|
||||
|
||||
var fee: Zatoshi
|
||||
var id: String
|
||||
var status: Status
|
||||
var subtitle: String
|
||||
var timestamp: TimeInterval
|
||||
var zecAmount: Zatoshi
|
||||
|
||||
|
||||
var address: String { zAddress ?? "" }
|
||||
var date: Date { Date(timeIntervalSince1970: timestamp) }
|
||||
var totalAmount: Zatoshi { Zatoshi(amount: zecAmount.amount + fee.amount) }
|
||||
var viewOnlineURL: URL? {
|
||||
URL(string: "https://blockchair.com/zcash/transaction/\(id)")
|
||||
}
|
||||
}
|
||||
|
||||
extension TransactionState {
|
||||
|
@ -40,6 +47,7 @@ extension TransactionState {
|
|||
subtitle = "sent"
|
||||
zAddress = confirmedTransaction.toAddress
|
||||
zecAmount = sent ? Zatoshi(amount: -Int64(confirmedTransaction.value)) : Zatoshi(amount: Int64(confirmedTransaction.value))
|
||||
fee = Zatoshi(amount: 10)
|
||||
if let memo = confirmedTransaction.memo {
|
||||
self.memo = memo.asZcashTransactionMemo()
|
||||
}
|
||||
|
@ -55,6 +63,7 @@ extension TransactionState {
|
|||
subtitle = "pending"
|
||||
zAddress = pendingTransaction.toAddress
|
||||
zecAmount = Zatoshi(amount: -Int64(pendingTransaction.value))
|
||||
fee = Zatoshi(amount: 10)
|
||||
if let memo = pendingTransaction.memo {
|
||||
self.memo = memo.asZcashTransactionMemo()
|
||||
}
|
||||
|
@ -67,6 +76,7 @@ extension TransactionState {
|
|||
extension TransactionState {
|
||||
static func placeholder(
|
||||
amount: Zatoshi,
|
||||
fee: Zatoshi,
|
||||
shielded: Bool = true,
|
||||
status: Status = .received,
|
||||
subtitle: String = "",
|
||||
|
@ -79,6 +89,7 @@ extension TransactionState {
|
|||
minedHeight: -1,
|
||||
shielded: shielded,
|
||||
zAddress: nil,
|
||||
fee: fee,
|
||||
id: uuid,
|
||||
status: status,
|
||||
subtitle: subtitle,
|
||||
|
|
|
@ -30,7 +30,7 @@ struct WalletEvent: Equatable, Identifiable {
|
|||
// MARK: - Rows
|
||||
|
||||
extension WalletEvent {
|
||||
@ViewBuilder func rowView() -> some View {
|
||||
@ViewBuilder func rowView(_ viewStore: WalletEventsFlowViewStore) -> some View {
|
||||
switch state {
|
||||
case .send(let transaction):
|
||||
TransactionRowView(transaction: transaction)
|
||||
|
@ -51,10 +51,10 @@ extension WalletEvent {
|
|||
// MARK: - Details
|
||||
|
||||
extension WalletEvent {
|
||||
@ViewBuilder func detailView() -> some View {
|
||||
@ViewBuilder func detailView(_ viewStore: WalletEventsFlowViewStore) -> some View {
|
||||
switch state {
|
||||
case .send(let transaction):
|
||||
TransactionDetailView(transaction: transaction)
|
||||
TransactionDetailView(transaction: transaction, viewStore: viewStore)
|
||||
case .pending:
|
||||
Text("pending transaction detail")
|
||||
case .received:
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "0x52",
|
||||
"green" : "0x31",
|
||||
"red" : "0x26"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
},
|
||||
{
|
||||
"appearances" : [
|
||||
{
|
||||
"appearance" : "luminosity",
|
||||
"value" : "dark"
|
||||
}
|
||||
],
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "1.000",
|
||||
"red" : "1.000"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
|
@ -111,6 +111,7 @@ internal enum Asset {
|
|||
internal static let regular = ColorAsset(name: "Regular")
|
||||
internal static let secondaryButtonText = ColorAsset(name: "SecondaryButtonText")
|
||||
internal static let titleText = ColorAsset(name: "TitleText")
|
||||
internal static let transactionDetailText = ColorAsset(name: "TransactionDetailText")
|
||||
internal static let validMnemonic = ColorAsset(name: "ValidMnemonic")
|
||||
internal static let captionText = ColorAsset(name: "captionText")
|
||||
internal static let captionTextShadow = ColorAsset(name: "captionTextShadow")
|
||||
|
|
|
@ -307,6 +307,7 @@ class MockWrappedSDKSynchronizer: WrappedSDKSynchronizer {
|
|||
mocked.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(amount: 10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.status,
|
||||
subtitle: $0.subtitle,
|
||||
|
@ -331,6 +332,7 @@ class MockWrappedSDKSynchronizer: WrappedSDKSynchronizer {
|
|||
mocked.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(amount: 10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.status,
|
||||
subtitle: $0.subtitle,
|
||||
|
@ -368,6 +370,7 @@ class MockWrappedSDKSynchronizer: WrappedSDKSynchronizer {
|
|||
minedHeight: 50,
|
||||
shielded: true,
|
||||
zAddress: "tteafadlamnelkqe",
|
||||
fee: Zatoshi(amount: 10),
|
||||
id: "id",
|
||||
status: .paid(success: true),
|
||||
subtitle: "sub",
|
||||
|
@ -417,6 +420,7 @@ class TestWrappedSDKSynchronizer: WrappedSDKSynchronizer {
|
|||
mocked.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(amount: 10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.status,
|
||||
subtitle: $0.subtitle,
|
||||
|
@ -447,6 +451,7 @@ class TestWrappedSDKSynchronizer: WrappedSDKSynchronizer {
|
|||
mocked.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(amount: 10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.status,
|
||||
subtitle: $0.subtitle,
|
||||
|
|
|
@ -166,4 +166,19 @@ class AppTests: XCTestCase {
|
|||
|
||||
store.receive(.checkBackupPhraseValidation)
|
||||
}
|
||||
|
||||
func testWalletEventReplyTo_validAddress() throws {
|
||||
let store = TestStore(
|
||||
initialState: .placeholder,
|
||||
reducer: AppReducer.default,
|
||||
environment: testEnvironment
|
||||
)
|
||||
|
||||
let address = "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po"
|
||||
store.send(.home(.walletEvents(.replyTo(address))))
|
||||
|
||||
if let url = URL(string: "zcash:\(address)") {
|
||||
store.receive(.deeplink(url))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -77,6 +77,7 @@ class HomeTests: XCTestCase {
|
|||
let walletEvents: [WalletEvent] = transactionsHelper.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(amount: 10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.status,
|
||||
subtitle: $0.subtitle,
|
||||
|
|
|
@ -64,6 +64,7 @@ class SendTests: XCTestCase {
|
|||
minedHeight: 50,
|
||||
shielded: true,
|
||||
zAddress: "tteafadlamnelkqe",
|
||||
fee: Zatoshi(amount: 10),
|
||||
id: "id",
|
||||
status: .paid(success: true),
|
||||
subtitle: "sub",
|
||||
|
|
|
@ -18,9 +18,11 @@ class HomeSnapshotTests: XCTestCase {
|
|||
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(amount: 4), uuid: "4"),
|
||||
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(amount: 5), uuid: "5")
|
||||
]
|
||||
let transactions: [WalletEvent] = transactionsHelper.map {
|
||||
|
||||
let walletEvents: [WalletEvent] = transactionsHelper.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(amount: 10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.status,
|
||||
subtitle: $0.subtitle,
|
||||
|
@ -41,7 +43,7 @@ class HomeSnapshotTests: XCTestCase {
|
|||
scanState: .placeholder,
|
||||
synchronizerStatus: "",
|
||||
totalBalance: Zatoshi(amount: balance.total),
|
||||
walletEventsState: .init(walletEvents: IdentifiedArrayOf(uniqueElements: transactions)),
|
||||
walletEventsState: .init(walletEvents: IdentifiedArrayOf(uniqueElements: walletEvents)),
|
||||
verifiedBalance: Zatoshi(amount: balance.verified)
|
||||
),
|
||||
reducer: .default,
|
||||
|
@ -61,4 +63,61 @@ class HomeSnapshotTests: XCTestCase {
|
|||
HomeView(store: store)
|
||||
)
|
||||
}
|
||||
|
||||
func testWalletEventDetailSnapshot() throws {
|
||||
let transaction = TransactionState(
|
||||
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,
|
||||
zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
|
||||
fee: Zatoshi(amount: 1_000_000),
|
||||
id: "ff3927e1f83df9b1b0dc75540ddc59ee435eecebae914d2e6dfe8576fbedc9a8",
|
||||
status: .paid(success: true),
|
||||
subtitle: "",
|
||||
timestamp: 1234567,
|
||||
zecAmount: Zatoshi(amount: 25_000_000)
|
||||
)
|
||||
|
||||
let walletEvent = WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
|
||||
|
||||
let balance = Balance(verified: 12_345_000, total: 12_345_000)
|
||||
let store = HomeStore(
|
||||
initialState: .init(
|
||||
drawerOverlay: .partial,
|
||||
profileState: .placeholder,
|
||||
requestState: .placeholder,
|
||||
sendState: .placeholder,
|
||||
scanState: .placeholder,
|
||||
synchronizerStatus: "",
|
||||
totalBalance: Zatoshi(amount: balance.total),
|
||||
walletEventsState: .init(walletEvents: IdentifiedArrayOf(uniqueElements: [walletEvent])),
|
||||
verifiedBalance: Zatoshi(amount: balance.verified)
|
||||
),
|
||||
reducer: .default,
|
||||
environment: .demo
|
||||
)
|
||||
|
||||
// wallet event detail
|
||||
let testEnvironment = WalletEventsFlowEnvironment(
|
||||
scheduler: DispatchQueue.test.eraseToAnyScheduler(),
|
||||
SDKSynchronizer: TestWrappedSDKSynchronizer(),
|
||||
pasteboard: .test
|
||||
)
|
||||
|
||||
ViewStore(store).send(.walletEvents(.updateRoute(.showWalletEvent(walletEvent))))
|
||||
let walletEventsStore = WalletEventsFlowStore(
|
||||
initialState: .placeHolder,
|
||||
reducer: .default,
|
||||
environment: testEnvironment
|
||||
)
|
||||
|
||||
addAttachments(
|
||||
name: "\(#function)_WalletEventDetail",
|
||||
TransactionDetailView(transaction: transaction, viewStore: ViewStore(walletEventsStore))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
//
|
||||
// WalletEventsSnapshotTests.swift
|
||||
// secantTests
|
||||
//
|
||||
// Created by Lukáš Korba on 27.06.2022.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import secant_testnet
|
||||
import ComposableArchitecture
|
||||
|
||||
class WalletEventsSnapshotTests: XCTestCase {
|
||||
func testWalletEventDetailSnapshot() throws {
|
||||
let transaction = TransactionState(
|
||||
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,
|
||||
zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
|
||||
fee: Zatoshi(amount: 1_000_000),
|
||||
id: "ff3927e1f83df9b1b0dc75540ddc59ee435eecebae914d2e6dfe8576fbedc9a8",
|
||||
status: .paid(success: true),
|
||||
subtitle: "",
|
||||
timestamp: 1234567,
|
||||
zecAmount: Zatoshi(amount: 25_000_000)
|
||||
)
|
||||
|
||||
let walletEvent = WalletEvent(id: transaction.id, state: .send(transaction), timestamp: transaction.timestamp)
|
||||
|
||||
let balance = Balance(verified: 12_345_000, total: 12_345_000)
|
||||
let store = HomeStore(
|
||||
initialState: .init(
|
||||
drawerOverlay: .partial,
|
||||
profileState: .placeholder,
|
||||
requestState: .placeholder,
|
||||
sendState: .placeholder,
|
||||
scanState: .placeholder,
|
||||
synchronizerStatus: "",
|
||||
totalBalance: Zatoshi(amount: balance.total),
|
||||
walletEventsState: .init(walletEvents: IdentifiedArrayOf(uniqueElements: [walletEvent])),
|
||||
verifiedBalance: Zatoshi(amount: balance.verified)
|
||||
),
|
||||
reducer: .default,
|
||||
environment: .demo
|
||||
)
|
||||
|
||||
// wallet event detail
|
||||
let testEnvironment = WalletEventsFlowEnvironment(
|
||||
scheduler: DispatchQueue.test.eraseToAnyScheduler(),
|
||||
SDKSynchronizer: TestWrappedSDKSynchronizer(),
|
||||
pasteboard: .test
|
||||
)
|
||||
|
||||
ViewStore(store).send(.walletEvents(.updateRoute(.showWalletEvent(walletEvent))))
|
||||
let walletEventsStore = WalletEventsFlowStore(
|
||||
initialState: .placeHolder,
|
||||
reducer: .default,
|
||||
environment: testEnvironment
|
||||
)
|
||||
|
||||
addAttachments(
|
||||
name: "\(#function)_WalletEventDetail",
|
||||
TransactionDetailView(transaction: transaction, viewStore: ViewStore(walletEventsStore))
|
||||
)
|
||||
}
|
||||
}
|
|
@ -14,7 +14,8 @@ class WalletEventsTests: XCTestCase {
|
|||
|
||||
let testEnvironment = WalletEventsFlowEnvironment(
|
||||
scheduler: testScheduler.eraseToAnyScheduler(),
|
||||
SDKSynchronizer: TestWrappedSDKSynchronizer()
|
||||
SDKSynchronizer: TestWrappedSDKSynchronizer(),
|
||||
pasteboard: .test
|
||||
)
|
||||
|
||||
func testSynchronizerSubscription() throws {
|
||||
|
@ -58,6 +59,7 @@ class WalletEventsTests: XCTestCase {
|
|||
let walletEvents: [WalletEvent] = mocked.map {
|
||||
let transaction = TransactionState.placeholder(
|
||||
amount: $0.amount,
|
||||
fee: Zatoshi(amount: 10),
|
||||
shielded: $0.shielded,
|
||||
status: $0.status,
|
||||
subtitle: $0.subtitle,
|
||||
|
@ -71,13 +73,13 @@ class WalletEventsTests: XCTestCase {
|
|||
)
|
||||
}
|
||||
|
||||
let identifiedTransactions = IdentifiedArrayOf(uniqueElements: walletEvents)
|
||||
let identifiedWalletEvents = IdentifiedArrayOf(uniqueElements: walletEvents)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: WalletEventsFlowState(
|
||||
route: .latest,
|
||||
isScrollable: true,
|
||||
walletEvents: identifiedTransactions
|
||||
walletEvents: identifiedWalletEvents
|
||||
),
|
||||
reducer: WalletEventsFlowReducer.default,
|
||||
environment: testEnvironment
|
||||
|
@ -88,7 +90,7 @@ class WalletEventsTests: XCTestCase {
|
|||
Self.testScheduler.advance(by: 0.01)
|
||||
|
||||
store.receive(.updateWalletEvents(walletEvents)) { state in
|
||||
let receivedTransactions = IdentifiedArrayOf(
|
||||
let receivedWalletEvents = IdentifiedArrayOf(
|
||||
uniqueElements:
|
||||
walletEvents
|
||||
.sorted(by: { lhs, rhs in
|
||||
|
@ -96,7 +98,32 @@ class WalletEventsTests: XCTestCase {
|
|||
})
|
||||
)
|
||||
|
||||
state.walletEvents = receivedTransactions
|
||||
state.walletEvents = receivedWalletEvents
|
||||
}
|
||||
}
|
||||
|
||||
func testCopyToPasteboard() throws {
|
||||
let pasteboard = WrappedPasteboard.test
|
||||
|
||||
let testEnvironment = WalletEventsFlowEnvironment(
|
||||
scheduler: DispatchQueue.test.eraseToAnyScheduler(),
|
||||
SDKSynchronizer: TestWrappedSDKSynchronizer(),
|
||||
pasteboard: pasteboard
|
||||
)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: WalletEventsFlowState(
|
||||
route: .latest,
|
||||
isScrollable: true,
|
||||
walletEvents: []
|
||||
),
|
||||
reducer: WalletEventsFlowReducer.default,
|
||||
environment: testEnvironment
|
||||
)
|
||||
|
||||
let testText = "test text"
|
||||
store.send(.copyToPastboard(testText))
|
||||
|
||||
XCTAssertEqual(pasteboard.getString(), testText, "WalletEvetns: `testCopyToPasteboard` is expected to match the input `\(testText)`")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue