diff --git a/modules/.swiftpm/xcode/xcshareddata/xcschemes/TransactionList.xcscheme b/modules/.swiftpm/xcode/xcshareddata/xcschemes/TransactionList.xcscheme
new file mode 100644
index 00000000..eb4b4eeb
--- /dev/null
+++ b/modules/.swiftpm/xcode/xcshareddata/xcschemes/TransactionList.xcscheme
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/Package.swift b/modules/Package.swift
index 39e0993c..d4293d0d 100644
--- a/modules/Package.swift
+++ b/modules/Package.swift
@@ -52,7 +52,7 @@ let package = Package(
.library(name: "UserPreferencesStorage", targets: ["UserPreferencesStorage"]),
.library(name: "Utils", targets: ["Utils"]),
.library(name: "WalletConfigProvider", targets: ["WalletConfigProvider"]),
- .library(name: "WalletEventsFlow", targets: ["WalletEventsFlow"]),
+ .library(name: "TransactionList", targets: ["TransactionList"]),
.library(name: "WalletStorage", targets: ["WalletStorage"]),
.library(name: "Welcome", targets: ["Welcome"]),
.library(name: "ZcashSDKEnvironment", targets: ["ZcashSDKEnvironment"])
@@ -209,7 +209,7 @@ let package = Package(
"SDKSynchronizer",
"UIComponents",
"Utils",
- "WalletEventsFlow",
+ "TransactionList",
"ZcashSDKEnvironment",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
@@ -364,7 +364,7 @@ let package = Package(
"RecoveryPhraseDisplay",
"Scan",
"SendFlow",
- "WalletEventsFlow",
+ "TransactionList",
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
],
@@ -531,7 +531,7 @@ let package = Package(
path: "Sources/Dependencies/WalletConfigProvider"
),
.target(
- name: "WalletEventsFlow",
+ name: "TransactionList",
dependencies: [
"Generated",
"Models",
@@ -543,7 +543,7 @@ let package = Package(
.product(name: "ComposableArchitecture", package: "swift-composable-architecture"),
.product(name: "ZcashLightClientKit", package: "ZcashLightClientKit")
],
- path: "Sources/Features/WalletEventsFlow"
+ path: "Sources/Features/TransactionList"
),
.target(
name: "WalletStorage",
diff --git a/modules/Sources/Dependencies/SDKSynchronizer/SDKSynchronizerInterface.swift b/modules/Sources/Dependencies/SDKSynchronizer/SDKSynchronizerInterface.swift
index 5829928c..a5f5bf9d 100644
--- a/modules/Sources/Dependencies/SDKSynchronizer/SDKSynchronizerInterface.swift
+++ b/modules/Sources/Dependencies/SDKSynchronizer/SDKSynchronizerInterface.swift
@@ -33,7 +33,7 @@ public struct SDKSynchronizerClient {
public let getShieldedBalance: () -> WalletBalance?
public let getTransparentBalance: () -> WalletBalance?
- public var getAllTransactions: () async throws -> [WalletEvent]
+ public var getAllTransactions: () async throws -> [TransactionState]
public let getUnifiedAddress: (_ account: Int) async throws -> UnifiedAddress?
public let getTransparentAddress: (_ account: Int) async throws -> TransparentAddress?
diff --git a/modules/Sources/Dependencies/SDKSynchronizer/SDKSynchronizerLive.swift b/modules/Sources/Dependencies/SDKSynchronizer/SDKSynchronizerLive.swift
index 67039de7..fcbc4eee 100644
--- a/modules/Sources/Dependencies/SDKSynchronizer/SDKSynchronizerLive.swift
+++ b/modules/Sources/Dependencies/SDKSynchronizer/SDKSynchronizerLive.swift
@@ -52,7 +52,7 @@ extension SDKSynchronizerClient {
getAllTransactions: {
let clearedTransactions = try await synchronizer.allTransactions()
- var clearedTxs: [WalletEvent] = []
+ var clearedTxs: [TransactionState] = []
for clearedTransaction in clearedTransactions {
var transaction = TransactionState.init(
@@ -72,13 +72,7 @@ extension SDKSynchronizerClient {
transaction.zAddress = addresses.first?.stringEncoded
- clearedTxs.append(
- WalletEvent(
- id: transaction.id,
- state: .transaction(transaction),
- timestamp: transaction.timestamp
- )
- )
+ clearedTxs.append(transaction)
}
return clearedTxs
diff --git a/modules/Sources/Dependencies/SDKSynchronizer/SDKSynchronizerTest.swift b/modules/Sources/Dependencies/SDKSynchronizer/SDKSynchronizerTest.swift
index 8dadcd3b..72830133 100644
--- a/modules/Sources/Dependencies/SDKSynchronizer/SDKSynchronizerTest.swift
+++ b/modules/Sources/Dependencies/SDKSynchronizer/SDKSynchronizerTest.swift
@@ -74,11 +74,11 @@ extension SDKSynchronizerClient {
rewind: @escaping (RewindPolicy) -> AnyPublisher = { _ in return Empty().eraseToAnyPublisher() },
getShieldedBalance: @escaping () -> WalletBalance? = { WalletBalance(verified: Zatoshi(12345000), total: Zatoshi(12345000)) },
getTransparentBalance: @escaping () -> WalletBalance? = { WalletBalance(verified: Zatoshi(12345000), total: Zatoshi(12345000)) },
- getAllTransactions: @escaping () -> [WalletEvent] = {
+ getAllTransactions: @escaping () -> [TransactionState] = {
let mockedCleared: [TransactionStateMockHelper] = [
- TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: false), uuid: "aa11"),
+ TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid, uuid: "aa11"),
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, uuid: "cc33"),
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55")
]
@@ -93,18 +93,18 @@ extension SDKSynchronizerClient {
timestamp: $0.date,
uuid: $0.uuid
)
- return WalletEvent(id: transaction.id, state: .transaction(transaction), timestamp: transaction.timestamp ?? 0)
+ return transaction
}
let mockedPending: [TransactionStateMockHelper] = [
TransactionStateMockHelper(
date: 1651039606,
amount: Zatoshi(6),
- status: .paid(success: false),
+ status: .paid,
uuid: "ff66"
),
TransactionStateMockHelper(date: 1651039303, amount: Zatoshi(7), uuid: "gg77"),
- TransactionStateMockHelper(date: 1651039707, amount: Zatoshi(8), status: .paid(success: true), uuid: "hh88"),
+ TransactionStateMockHelper(date: 1651039707, amount: Zatoshi(8), status: .paid, uuid: "hh88"),
TransactionStateMockHelper(date: 1651039808, amount: Zatoshi(9), uuid: "ii99")
]
@@ -118,7 +118,7 @@ extension SDKSynchronizerClient {
timestamp: $0.date,
uuid: $0.uuid
)
- return WalletEvent(id: transaction.id, state: .transaction(transaction), timestamp: transaction.timestamp)
+ return transaction
}
clearedTransactions.append(contentsOf: pendingTransactions)
@@ -156,7 +156,7 @@ extension SDKSynchronizerClient {
zAddress: "tteafadlamnelkqe",
fee: Zatoshi(10),
id: "id",
- status: .paid(success: true),
+ status: .paid,
timestamp: 1234567,
zecAmount: Zatoshi(10)
)
@@ -170,7 +170,7 @@ extension SDKSynchronizerClient {
zAddress: "tteafadlamnelkqe",
fee: Zatoshi(10),
id: "id",
- status: .paid(success: true),
+ status: .paid,
timestamp: 1234567,
zecAmount: Zatoshi(10)
)
diff --git a/modules/Sources/Features/AddressDetails/AddressDetailsView.swift b/modules/Sources/Features/AddressDetails/AddressDetailsView.swift
index 51b07469..8c9ea992 100644
--- a/modules/Sources/Features/AddressDetails/AddressDetailsView.swift
+++ b/modules/Sources/Features/AddressDetails/AddressDetailsView.swift
@@ -56,7 +56,7 @@ public struct AddressDetailsView: View {
Text(address)
.font(.custom(FontFamily.Inter.regular.name, size: 16))
- .foregroundColor(Asset.Colors.suppressed47.color)
+ .foregroundColor(Asset.Colors.shade47.color)
.frame(width: 270)
.padding(.bottom, 20)
diff --git a/modules/Sources/Features/Home/HomeStore.swift b/modules/Sources/Features/Home/HomeStore.swift
index 1cff8904..9161bfb7 100644
--- a/modules/Sources/Features/Home/HomeStore.swift
+++ b/modules/Sources/Features/Home/HomeStore.swift
@@ -9,7 +9,7 @@ import Utils
import Models
import Generated
import ReviewRequest
-import WalletEventsFlow
+import TransactionList
import Scan
public typealias HomeStore = Store
@@ -32,7 +32,7 @@ public struct HomeReducer: ReducerProtocol {
public var shieldedBalance: Balance
public var synchronizerStatusSnapshot: SyncStatusSnapshot
public var walletConfig: WalletConfig
- public var walletEventsState: WalletEventsFlowReducer.State
+ public var transactionListState: TransactionListReducer.State
public var migratingDatabase = true
// TODO: [#311] - Get the ZEC price from the SDK, https://github.com/zcash/secant-ios-wallet/issues/311
public var zecPrice = Decimal(140.0)
@@ -53,7 +53,7 @@ public struct HomeReducer: ReducerProtocol {
shieldedBalance: Balance,
synchronizerStatusSnapshot: SyncStatusSnapshot,
walletConfig: WalletConfig,
- walletEventsState: WalletEventsFlowReducer.State,
+ transactionListState: TransactionListReducer.State,
zecPrice: Decimal = Decimal(140.0)
) {
self.destination = destination
@@ -63,7 +63,7 @@ public struct HomeReducer: ReducerProtocol {
self.shieldedBalance = shieldedBalance
self.synchronizerStatusSnapshot = synchronizerStatusSnapshot
self.walletConfig = walletConfig
- self.walletEventsState = walletEventsState
+ self.transactionListState = transactionListState
self.zecPrice = zecPrice
}
}
@@ -82,8 +82,8 @@ public struct HomeReducer: ReducerProtocol {
case synchronizerStateChanged(SynchronizerState)
case syncFailed(ZcashError)
case updateDestination(HomeReducer.State.Destination?)
- case updateWalletEvents([WalletEvent])
- case walletEvents(WalletEventsFlowReducer.Action)
+ case updateTransactionList([TransactionState])
+ case transactionList(TransactionListReducer.Action)
}
@Dependency(\.audioServices) var audioServices
@@ -98,8 +98,8 @@ public struct HomeReducer: ReducerProtocol {
}
public var body: some ReducerProtocol {
- Scope(state: \.walletEventsState, action: /Action.walletEvents) {
- WalletEventsFlowReducer()
+ Scope(state: \.transactionListState, action: /Action.transactionList) {
+ TransactionListReducer()
}
Reduce { state, action in
@@ -136,7 +136,7 @@ public struct HomeReducer: ReducerProtocol {
state.canRequestReview = false
return .none
- case .updateWalletEvents:
+ case .updateTransactionList:
return .none
case .synchronizerStateChanged(let latestState):
@@ -171,7 +171,7 @@ public struct HomeReducer: ReducerProtocol {
state.destination = destination
return .none
- case .walletEvents:
+ case .transactionList:
return .none
case .retrySync:
@@ -212,10 +212,10 @@ public struct HomeReducer: ReducerProtocol {
// MARK: - Store
extension HomeStore {
- func historyStore() -> WalletEventsFlowStore {
+ func historyStore() -> TransactionListStore {
self.scope(
- state: \.walletEventsState,
- action: HomeReducer.Action.walletEvents
+ state: \.transactionListState,
+ action: HomeReducer.Action.transactionList
)
}
}
@@ -242,7 +242,7 @@ extension HomeReducer.State {
shieldedBalance: Balance.zero,
synchronizerStatusSnapshot: .default,
walletConfig: .default,
- walletEventsState: .emptyPlaceHolder
+ transactionListState: .emptyPlaceHolder
)
}
}
@@ -264,7 +264,7 @@ extension HomeStore {
state: .error(ZcashError.synchronizerNotPrepared)
),
walletConfig: .default,
- walletEventsState: .emptyPlaceHolder
+ transactionListState: .emptyPlaceHolder
),
reducer: HomeReducer(networkType: .testnet)
)
diff --git a/modules/Sources/Features/Home/HomeView.swift b/modules/Sources/Features/Home/HomeView.swift
index 877f4d20..54025603 100644
--- a/modules/Sources/Features/Home/HomeView.swift
+++ b/modules/Sources/Features/Home/HomeView.swift
@@ -2,7 +2,7 @@ import SwiftUI
import ComposableArchitecture
import StoreKit
import Generated
-import WalletEventsFlow
+import TransactionList
import Settings
import UIComponents
@@ -20,9 +20,8 @@ public struct HomeView: View {
VStack {
balance(viewStore)
- WalletEventsFlowView(store: store.historyStore(), tokenName: tokenName)
+ TransactionListView(store: store.historyStore(), tokenName: tokenName)
}
- .padding()
.applyScreenBackground()
.onAppear {
viewStore.send(.onAppear)
@@ -52,26 +51,26 @@ public struct HomeView: View {
extension HomeView {
func balance(_ viewStore: HomeViewStore) -> some View {
- Group {
+ VStack(spacing: 0) {
Button {
viewStore.send(.balanceBreakdown)
} label: {
BalanceTitle(balance: viewStore.shieldedBalance.data.total)
}
+ .padding(.top, 40)
- if viewStore.walletConfig.isEnabled(.showFiatConversion) {
- Text("$\(viewStore.totalCurrencyBalance.decimalZashiFormatted())")
- .font(.custom(FontFamily.Inter.regular.name, size: 20))
- }
-
if viewStore.migratingDatabase {
Text(L10n.Home.migratingDatabases)
+ .padding(.top, 10)
+ .padding(.bottom, 30)
} else {
Text(L10n.Balance.available(viewStore.shieldedBalance.data.verified.decimalZashiFormatted(), tokenName))
.font(.custom(FontFamily.Inter.regular.name, size: 12))
.accessDebugMenuWithHiddenGesture {
viewStore.send(.debugMenuStartup)
}
+ .padding(.top, 10)
+ .padding(.bottom, 30)
}
}
.foregroundColor(Asset.Colors.primary.color)
diff --git a/modules/Sources/Features/ImportWallet/ImportWalletView.swift b/modules/Sources/Features/ImportWallet/ImportWalletView.swift
index 6e8ef3dc..b8d5d307 100644
--- a/modules/Sources/Features/ImportWallet/ImportWalletView.swift
+++ b/modules/Sources/Features/ImportWallet/ImportWalletView.swift
@@ -70,7 +70,7 @@ public struct ImportWalletView: View {
VStack {
Text(L10n.ImportWallet.enterPlaceholder)
.font(.custom(FontFamily.Inter.regular.name, size: 13))
- .foregroundColor(Asset.Colors.suppressed72.color)
+ .foregroundColor(Asset.Colors.shade72.color)
.onTapGesture {
isFocused = true
}
diff --git a/modules/Sources/Features/Sandbox/SandboxStore.swift b/modules/Sources/Features/Sandbox/SandboxStore.swift
index c934508c..68dbd045 100644
--- a/modules/Sources/Features/Sandbox/SandboxStore.swift
+++ b/modules/Sources/Features/Sandbox/SandboxStore.swift
@@ -1,6 +1,6 @@
import ComposableArchitecture
import SwiftUI
-import WalletEventsFlow
+import TransactionList
public typealias SandboxStore = Store
public typealias SandboxViewStore = ViewStore
@@ -13,13 +13,13 @@ public struct SandboxReducer: ReducerProtocol {
case recoveryPhraseDisplay
case scan
}
- public var walletEventsState: WalletEventsFlowReducer.State
+ public var transactionListState: TransactionListReducer.State
public var destination: Destination?
}
public enum Action: Equatable {
case updateDestination(SandboxReducer.State.Destination?)
- case walletEvents(WalletEventsFlowReducer.Action)
+ case transactionList(TransactionListReducer.Action)
case reset
}
@@ -31,10 +31,10 @@ public struct SandboxReducer: ReducerProtocol {
state.destination = destination
return .none
- case let .walletEvents(walletEventsAction):
- return WalletEventsFlowReducer()
- .reduce(into: &state.walletEventsState, action: walletEventsAction)
- .map(SandboxReducer.Action.walletEvents)
+ case let .transactionList(transactionListAction):
+ return TransactionListReducer()
+ .reduce(into: &state.transactionListState, action: transactionListAction)
+ .map(SandboxReducer.Action.transactionList)
case .reset:
return .none
@@ -45,10 +45,10 @@ public struct SandboxReducer: ReducerProtocol {
// MARK: - Store
extension SandboxStore {
- func historyStore() -> WalletEventsFlowStore {
+ func historyStore() -> TransactionListStore {
self.scope(
- state: \.walletEventsState,
- action: SandboxReducer.Action.walletEvents
+ state: \.transactionListState,
+ action: SandboxReducer.Action.transactionList
)
}
}
@@ -56,20 +56,6 @@ extension SandboxStore {
// MARK: - ViewStore
extension SandboxViewStore {
- func toggleSelectedTransaction() {
- let isAlreadySelected = (self.selectedTranactionID != nil)
- let walletEvent = self.walletEventsState.walletEvents[5]
- let newDestination = isAlreadySelected ? nil : WalletEventsFlowReducer.State.Destination.showWalletEvent(walletEvent)
- send(.walletEvents(.updateDestination(newDestination)))
- }
-
- var selectedTranactionID: String? {
- self.walletEventsState
- .destination
- .flatMap(/WalletEventsFlowReducer.State.Destination.showWalletEvent)
- .map(\.id)
- }
-
func bindingForDestination(_ destination: SandboxReducer.State.Destination) -> Binding {
self.binding(
get: { $0.destination == destination },
@@ -85,7 +71,7 @@ extension SandboxViewStore {
extension SandboxReducer.State {
public static var placeholder: Self {
.init(
- walletEventsState: .placeHolder,
+ transactionListState: .placeHolder,
destination: nil
)
}
@@ -95,7 +81,7 @@ extension SandboxStore {
public static var placeholder: SandboxStore {
SandboxStore(
initialState: SandboxReducer.State(
- walletEventsState: .placeHolder,
+ transactionListState: .placeHolder,
destination: nil
),
reducer: SandboxReducer()
diff --git a/modules/Sources/Features/Sandbox/SandboxView.swift b/modules/Sources/Features/Sandbox/SandboxView.swift
index ecba24bb..ffce96db 100644
--- a/modules/Sources/Features/Sandbox/SandboxView.swift
+++ b/modules/Sources/Features/Sandbox/SandboxView.swift
@@ -1,7 +1,7 @@
import SwiftUI
import ComposableArchitecture
import RecoveryPhraseDisplay
-import WalletEventsFlow
+import TransactionList
import Scan
import SendFlow
import ZcashLightClientKit
@@ -29,7 +29,7 @@ public struct SandboxView: View {
@ViewBuilder func view(for destination: SandboxReducer.State.Destination) -> some View {
switch destination {
case .history:
- WalletEventsFlowView(store: store.historyStore(), tokenName: tokenName)
+ TransactionListView(store: store.historyStore(), tokenName: tokenName)
case .send:
SendFlowView(
store: .init(
@@ -77,11 +77,6 @@ public struct SandboxView: View {
}
Section(header: Text("Other Actions")) {
- Button(
- action: { viewStore.toggleSelectedTransaction() },
- label: { Text("Toggle Selected Transaction") }
- )
-
Button(
action: { viewStore.send(.reset) },
label: { Text("Reset (to startup)") }
@@ -93,7 +88,7 @@ public struct SandboxView: View {
isPresented: viewStore.bindingForDestination(.history),
content: {
NavigationView {
- WalletEventsFlowView(store: store.historyStore(), tokenName: tokenName)
+ TransactionListView(store: store.historyStore(), tokenName: tokenName)
.toolbar {
ToolbarItem {
Button("Done") { viewStore.send(.updateDestination(nil)) }
diff --git a/modules/Sources/Features/Settings/SettingsStore.swift b/modules/Sources/Features/Settings/SettingsStore.swift
index c7ee50ee..8d3556fc 100644
--- a/modules/Sources/Features/Settings/SettingsStore.swift
+++ b/modules/Sources/Features/Settings/SettingsStore.swift
@@ -117,7 +117,6 @@ public struct SettingsReducer: ReducerProtocol {
return .none
case .privateDataConsent(.shareFinished):
- state.destination = nil
return .none
case .privateDataConsent:
diff --git a/modules/Sources/Features/Settings/Views/About.swift b/modules/Sources/Features/Settings/Views/About.swift
index b982d92a..83325e18 100644
--- a/modules/Sources/Features/Settings/Views/About.swift
+++ b/modules/Sources/Features/Settings/Views/About.swift
@@ -44,7 +44,7 @@ public struct About: View {
Text(L10n.Settings.About.info)
.font(.custom(FontFamily.Inter.regular.name, size: 14))
- .foregroundColor(Asset.Colors.suppressed30.color)
+ .foregroundColor(Asset.Colors.shade30.color)
Spacer()
}
diff --git a/modules/Sources/Features/Tabs/TabsView.swift b/modules/Sources/Features/Tabs/TabsView.swift
index e6eb4232..5a8215c1 100644
--- a/modules/Sources/Features/Tabs/TabsView.swift
+++ b/modules/Sources/Features/Tabs/TabsView.swift
@@ -82,7 +82,7 @@ public struct TabsView: View {
.foregroundColor(Asset.Colors.primary.color)
Rectangle()
.frame(height: 2)
- .foregroundColor(Asset.Colors.tabsUnderline.color)
+ .foregroundColor(Asset.Colors.primaryTint.color)
.matchedGeometryEffect(id: "Tabs", in: tabsID, properties: .frame)
} else {
Text("\(item.title)")
diff --git a/modules/Sources/Features/TransactionList/TransactionListStore.swift b/modules/Sources/Features/TransactionList/TransactionListStore.swift
new file mode 100644
index 00000000..6fb3de87
--- /dev/null
+++ b/modules/Sources/Features/TransactionList/TransactionListStore.swift
@@ -0,0 +1,202 @@
+import ComposableArchitecture
+import SwiftUI
+import ZcashLightClientKit
+import Utils
+import Models
+import Generated
+import Pasteboard
+import SDKSynchronizer
+import ZcashSDKEnvironment
+
+public typealias TransactionListStore = Store
+public typealias TransactionListViewStore = ViewStore
+
+public struct TransactionListReducer: ReducerProtocol {
+ private enum CancelId { case timer }
+
+ public struct State: Equatable {
+ public var latestMinedHeight: BlockHeight?
+ public var isScrollable = true
+ public var requiredTransactionConfirmations = 0
+ public var transactionList: IdentifiedArrayOf
+ public var latestTranassctionId = ""
+
+ public init(
+ latestMinedHeight: BlockHeight? = nil,
+ isScrollable: Bool = true,
+ requiredTransactionConfirmations: Int = 0,
+ transactionList: IdentifiedArrayOf
+ ) {
+ self.latestMinedHeight = latestMinedHeight
+ self.isScrollable = isScrollable
+ self.requiredTransactionConfirmations = requiredTransactionConfirmations
+ self.transactionList = transactionList
+ }
+ }
+
+ public enum Action: Equatable {
+ case copyToPastboard(RedactableString)
+ case onAppear
+ case onDisappear
+ case synchronizerStateChanged(SyncStatus)
+ case transactionCollapseRequested(String)
+ case transactionAddressExpandRequested(String)
+ case transactionExpandRequested(String)
+ case transactionIdExpandRequested(String)
+ case updateTransactionList([TransactionState])
+ }
+
+ @Dependency(\.mainQueue) var mainQueue
+ @Dependency(\.pasteboard) var pasteboard
+ @Dependency(\.sdkSynchronizer) var sdkSynchronizer
+ @Dependency(\.zcashSDKEnvironment) var zcashSDKEnvironment
+
+ public init() {}
+
+ // swiftlint:disable:next cyclomatic_complexity
+ public func reduce(into state: inout State, action: Action) -> ComposableArchitecture.EffectTask {
+ switch action {
+ case .onAppear:
+ state.requiredTransactionConfirmations = zcashSDKEnvironment.requiredTransactionConfirmations
+ return .merge(
+ sdkSynchronizer.stateStream()
+ .throttle(for: .seconds(0.2), scheduler: mainQueue, latest: true)
+ .map { TransactionListReducer.Action.synchronizerStateChanged($0.syncStatus) }
+ .eraseToEffect()
+ .cancellable(id: CancelId.timer, cancelInFlight: true),
+ .run { send in
+ await send(.updateTransactionList(try await sdkSynchronizer.getAllTransactions()))
+ }
+ )
+
+ case .onDisappear:
+ return .cancel(id: CancelId.timer)
+
+ case .synchronizerStateChanged(.upToDate):
+ state.latestMinedHeight = sdkSynchronizer.latestState().latestBlockHeight
+ return .task {
+ return .updateTransactionList(try await sdkSynchronizer.getAllTransactions())
+ }
+
+ case .synchronizerStateChanged:
+ return .none
+
+ case .updateTransactionList(let transactionList):
+ let sortedTransactionList = transactionList
+ .sorted(by: { lhs, rhs in
+ guard let lhsTimestamp = lhs.timestamp, let rhsTimestamp = rhs.timestamp else {
+ return false
+ }
+ return lhsTimestamp > rhsTimestamp
+ }).map { transaction in
+ if let index = state.transactionList.index(id: transaction.id) {
+ var copiedTransaction = transaction
+
+ copiedTransaction.isAddressExpanded = state.transactionList[index].isAddressExpanded
+ copiedTransaction.isExpanded = state.transactionList[index].isExpanded
+ copiedTransaction.isIdExpanded = state.transactionList[index].isIdExpanded
+
+ return copiedTransaction
+ }
+
+ return transaction
+ }
+ state.transactionList = IdentifiedArrayOf(uniqueElements: sortedTransactionList)
+ state.latestTranassctionId = state.transactionList.first?.id ?? ""
+ return .none
+
+ case .copyToPastboard(let value):
+ pasteboard.setString(value)
+ return .none
+
+ case .transactionCollapseRequested(let id):
+ if let index = state.transactionList.index(id: id) {
+ state.transactionList[index].isAddressExpanded = false
+ state.transactionList[index].isExpanded = false
+ state.transactionList[index].isIdExpanded = false
+ }
+ return .none
+
+ case .transactionAddressExpandRequested(let id):
+ if let index = state.transactionList.index(id: id) {
+ if state.transactionList[index].isExpanded {
+ state.transactionList[index].isAddressExpanded = true
+ } else {
+ state.transactionList[index].isExpanded = true
+ }
+ }
+ return .none
+
+ case .transactionExpandRequested(let id):
+ if let index = state.transactionList.index(id: id) {
+ state.transactionList[index].isExpanded = true
+ }
+ return .none
+
+ case .transactionIdExpandRequested(let id):
+ if let index = state.transactionList.index(id: id) {
+ if state.transactionList[index].isExpanded {
+ state.transactionList[index].isIdExpanded = true
+ } else {
+ state.transactionList[index].isExpanded = true
+ }
+ }
+ return .none
+ }
+ }
+}
+
+// MARK: ViewStore
+
+extension TransactionListViewStore {
+ func isLatestTransaction(id: String) -> Bool {
+ state.latestTranassctionId == id
+ }
+}
+
+// MARK: Placeholders
+
+extension TransactionListReducer.State {
+ public static var placeHolder: Self {
+ .init(transactionList: .mocked)
+ }
+
+ public static var emptyPlaceHolder: Self {
+ .init(transactionList: [])
+ }
+}
+
+extension TransactionListStore {
+ public static var placeholder: Store {
+ return Store(
+ initialState: .placeHolder,
+ reducer: TransactionListReducer()
+ .dependency(\.zcashSDKEnvironment, .testnet)
+ )
+ }
+}
+
+extension IdentifiedArrayOf where Element == TransactionState {
+ public static var placeholder: IdentifiedArrayOf {
+ return .init(
+ uniqueElements: (0..<30).map {
+ TransactionState(
+ fee: Zatoshi(10),
+ id: String($0),
+ status: .paid,
+ timestamp: 1234567,
+ zecAmount: Zatoshi(25)
+ )
+ }
+ )
+ }
+
+ public static var mocked: IdentifiedArrayOf {
+ return .init(
+ uniqueElements: [
+ TransactionState.mockedSent,
+ TransactionState.mockedReceived
+ ]
+ )
+ }
+}
diff --git a/modules/Sources/Features/TransactionList/TransactionListView.swift b/modules/Sources/Features/TransactionList/TransactionListView.swift
new file mode 100644
index 00000000..f111a45e
--- /dev/null
+++ b/modules/Sources/Features/TransactionList/TransactionListView.swift
@@ -0,0 +1,48 @@
+import SwiftUI
+import ComposableArchitecture
+import Generated
+import UIComponents
+
+public struct TransactionListView: View {
+ let store: TransactionListStore
+ let tokenName: String
+
+ public init(store: TransactionListStore, tokenName: String) {
+ self.store = store
+ self.tokenName = tokenName
+ }
+
+ public var body: some View {
+ WithViewStore(store, observe: { $0 }) { viewStore in
+ List {
+ ForEach(viewStore.transactionList) { transaction in
+ TransactionRowView(
+ viewStore: viewStore,
+ transaction: transaction,
+ tokenName: tokenName,
+ isLatestTransaction: viewStore.isLatestTransaction(id: transaction.id)
+ )
+ .listRowInsets(EdgeInsets())
+ }
+ .listRowBackground(Asset.Colors.shade97.color)
+ .listRowSeparator(.hidden)
+ }
+ .refreshable {
+ viewStore.send(.synchronizerStateChanged(.upToDate))
+ }
+ .background(Asset.Colors.shade97.color)
+ .listStyle(.plain)
+ .onAppear { viewStore.send(.onAppear) }
+ .onDisappear(perform: { viewStore.send(.onDisappear) })
+ }
+ }
+}
+
+// MARK: - Previews
+
+#Preview {
+ NavigationView {
+ TransactionListView(store: .placeholder, tokenName: "ZEC")
+ .preferredColorScheme(.light)
+ }
+}
diff --git a/modules/Sources/Features/TransactionList/Views/CollapseTransactionView.swift b/modules/Sources/Features/TransactionList/Views/CollapseTransactionView.swift
new file mode 100644
index 00000000..22cd6227
--- /dev/null
+++ b/modules/Sources/Features/TransactionList/Views/CollapseTransactionView.swift
@@ -0,0 +1,37 @@
+//
+// CollapseTransactionView.swift
+//
+//
+// Created by Lukáš Korba on 04.11.2023.
+//
+
+import SwiftUI
+import Generated
+
+public struct CollapseTransactionView: View {
+ public var body: some View {
+ HStack {
+ Asset.Assets.upArrow.image
+ .resizable()
+ .frame(width: 10, height: 7)
+ .scaleEffect(0.6)
+ .font(.custom(FontFamily.Inter.black.name, size: 10))
+ .foregroundColor(Asset.Colors.primaryTint.color)
+ .overlay {
+ Rectangle()
+ .stroke()
+ .frame(width: 10, height: 10)
+ .foregroundColor(Asset.Colors.shade72.color)
+ }
+
+ Text(L10n.TransactionList.collapse)
+ .font(.custom(FontFamily.Inter.italic.name, size: 13))
+ .foregroundColor(Asset.Colors.shade47.color)
+ .underline()
+ }
+ }
+}
+
+#Preview {
+ CollapseTransactionView()
+}
diff --git a/modules/Sources/Features/TransactionList/Views/MessageView.swift b/modules/Sources/Features/TransactionList/Views/MessageView.swift
new file mode 100644
index 00000000..dd2e1d2f
--- /dev/null
+++ b/modules/Sources/Features/TransactionList/Views/MessageView.swift
@@ -0,0 +1,77 @@
+//
+// MessageView.swift
+//
+//
+// Created by Lukáš Korba on 05.11.2023.
+//
+
+import SwiftUI
+import Generated
+
+struct MessageView: View {
+ let message: String?
+ let isSpending: Bool
+ let isFailed: Bool
+
+ public init(
+ message: String?,
+ isSpending: Bool,
+ isFailed: Bool = false
+ ) {
+ self.message = message
+ self.isSpending = isSpending
+ self.isFailed = isFailed
+ }
+
+ var body: some View {
+ if let memoText = message {
+ VStack(alignment: .leading, spacing: 0) {
+ Text(L10n.TransactionList.messageTitle)
+ .font(.custom(FontFamily.Inter.medium.name, size: 13))
+ .padding(.bottom, 8)
+
+ VStack(alignment: .leading, spacing: 0) {
+ Color.clear.frame(height: 0)
+
+ if isFailed {
+ Text(memoText)
+ .font(.custom(FontFamily.Inter.bold.name, size: 13))
+ .foregroundColor(Asset.Colors.error.color)
+ .strikethrough()
+ .padding()
+ } else {
+ Text(memoText)
+ .font(.custom(FontFamily.Inter.bold.name, size: 13))
+ .padding()
+ }
+ }
+ .messageShape(
+ filled: !isSpending
+ ? Asset.Colors.messageBcgReceived.color
+ : nil,
+ orientation: !isSpending
+ ? .right
+ : .left
+ )
+ }
+ .padding(.bottom, 7)
+ .padding(.vertical, 10)
+ } else {
+ Text(L10n.TransactionList.noMessageIncluded)
+ .font(.custom(FontFamily.Inter.italic.name, size: 13))
+ .foregroundColor(Asset.Colors.shade47.color)
+ }
+ }
+}
+
+#Preview {
+ VStack(alignment: .leading) {
+ MessageView(message: "Test", isSpending: true)
+ .padding(.bottom, 50)
+
+ MessageView(message: "Test", isSpending: true, isFailed: true)
+ .padding(.bottom, 50)
+
+ MessageView(message: "Test", isSpending: false)
+ }
+}
diff --git a/modules/Sources/Features/TransactionList/Views/TransactionFeeView.swift b/modules/Sources/Features/TransactionList/Views/TransactionFeeView.swift
new file mode 100644
index 00000000..0826e86a
--- /dev/null
+++ b/modules/Sources/Features/TransactionList/Views/TransactionFeeView.swift
@@ -0,0 +1,38 @@
+//
+// TransactionFeeView.swift
+//
+//
+// Created by Lukáš Korba on 04.11.2023.
+//
+
+import SwiftUI
+import Generated
+import ZcashLightClientKit
+
+public struct TransactionFeeView: View {
+ let fee: Zatoshi
+
+ public init(fee: Zatoshi) {
+ self.fee = fee
+ }
+
+ public var body: some View {
+ HStack {
+ VStack(alignment: .leading, spacing: 4) {
+ Text(L10n.TransactionList.transactionFee)
+ .font(.custom(FontFamily.Inter.regular.name, size: 13))
+ .foregroundColor(Asset.Colors.shade47.color)
+
+ Text(fee.decimalString(formatter: NumberFormatter.zashiBalanceFormatter))
+ .font(.custom(FontFamily.Inter.bold.name, size: 13))
+ .foregroundColor(Asset.Colors.shade47.color)
+ }
+
+ Color.clear
+ }
+ }
+}
+
+#Preview {
+ TransactionFeeView(fee: Zatoshi(10_000))
+}
diff --git a/modules/Sources/Features/TransactionList/Views/TransactionHeaderView.swift b/modules/Sources/Features/TransactionList/Views/TransactionHeaderView.swift
new file mode 100644
index 00000000..35344e3a
--- /dev/null
+++ b/modules/Sources/Features/TransactionList/Views/TransactionHeaderView.swift
@@ -0,0 +1,193 @@
+//
+// TransactionHeaderView.swift
+//
+//
+// Created by Lukáš Korba on 05.11.2023.
+//
+
+import SwiftUI
+import ComposableArchitecture
+import Generated
+import Models
+import UIComponents
+
+struct TransactionHeaderView: View {
+ let viewStore: TransactionListViewStore
+ let transaction: TransactionState
+ let isLatestTransaction: Bool
+
+ init(
+ viewStore: TransactionListViewStore,
+ transaction: TransactionState,
+ isLatestTransaction: Bool = false
+ ) {
+ self.viewStore = viewStore
+ self.transaction = transaction
+ self.isLatestTransaction = isLatestTransaction
+ }
+
+ var body: some View {
+ VStack(spacing: 0) {
+ Divider()
+ .padding(.horizontal, 30)
+ .padding(.bottom, 30)
+ .opacity(isLatestTransaction ? 0.0 : 1.0)
+
+ HStack {
+ VStack(alignment: .leading, spacing: 5) {
+ HStack(spacing: 0) {
+ iconImage()
+
+ titleText()
+
+ addressArea()
+
+ Spacer(minLength: 60)
+
+ balanceView()
+ }
+ .padding(.trailing, 30)
+
+ if transaction.zAddress != nil && transaction.isAddressExpanded {
+ HStack {
+ Text(transaction.address)
+ .font(.custom(FontFamily.Inter.bold.name, size: 13))
+ .foregroundColor(Asset.Colors.shade47.color)
+
+ Spacer(minLength: 100)
+ }
+ .padding(.horizontal, 60)
+ .padding(.bottom, 5)
+ }
+
+ Text("\(transaction.dateString ?? "")")
+ .font(.custom(FontFamily.Inter.regular.name, size: 13))
+ .foregroundColor(Asset.Colors.shade47.color)
+ .padding(.horizontal, 60)
+ }
+ }
+ }
+ .padding(.bottom, 30)
+ }
+
+ @ViewBuilder private func iconImage() -> some View {
+ HStack {
+ Spacer()
+
+ icon
+ .padding(.trailing, 10)
+ }
+ .frame(width: 60)
+ }
+
+ @ViewBuilder private func titleText() -> some View {
+ Text(transaction.title)
+ .conditionalStrikethrough(transaction.status == .failed)
+ .conditionalFont(
+ condition: transaction.isPending,
+ true: .custom(FontFamily.Inter.boldItalic.name, size: 13),
+ else: .custom(FontFamily.Inter.bold.name, size: 13)
+ )
+ .foregroundColor(transaction.titleColor)
+ .padding(.trailing, 8)
+ }
+
+ @ViewBuilder private func addressArea() -> some View {
+ if transaction.zAddress == nil {
+ Asset.Assets.shield.image
+ .resizable()
+ .frame(width: 17, height: 13)
+ } else if !transaction.isAddressExpanded {
+ Button {
+ viewStore.send(.transactionAddressExpandRequested(transaction.id))
+ } label: {
+ Text(transaction.address)
+ .font(.custom(FontFamily.Inter.regular.name, size: 13))
+ .foregroundColor(Asset.Colors.shade47.color)
+ .lineLimit(1)
+ .truncationMode(.middle)
+ }
+ .disabled(!transaction.isExpanded)
+ }
+ }
+
+ @ViewBuilder private func balanceView() -> some View {
+ if transaction.isExpanded {
+ HStack(spacing: 0) {
+ FullBalanceTitle(
+ primary: transaction.expandedAmountString.primary,
+ secondary: transaction.expandedAmountString.secondary,
+ fontName: FontFamily.Inter.regular.name,
+ primaryFontSize: 12,
+ secondaryFontSize: 8
+ )
+ }
+ .foregroundColor(transaction.balanceColor)
+ } else {
+ Text(transaction.roundedAmountString)
+ .font(.custom(FontFamily.Inter.regular.name, size: 12))
+ .conditionalStrikethrough(transaction.status == .failed)
+ .foregroundColor(transaction.balanceColor)
+ }
+ }
+}
+
+extension TransactionHeaderView {
+ var icon: some View {
+ HStack {
+ switch transaction.status {
+ case .paid, .failed:
+ Asset.Assets.fly.image
+ .resizable()
+ .frame(width: 20, height: 16)
+
+ case .received, .sending, .receiving:
+ Asset.Assets.flyReceived.image
+ .resizable()
+ .frame(width: 17, height: 11)
+ }
+ }
+ }
+}
+
+#Preview {
+ VStack(spacing: 0) {
+ TransactionHeaderView(
+ viewStore: ViewStore(.placeholder, observe: { $0 }),
+ transaction: .mockedFailed
+ )
+ .listRowSeparator(.hidden)
+
+ TransactionHeaderView(
+ viewStore: ViewStore(.placeholder, observe: { $0 }),
+ transaction: .mockedFailedReceive
+ )
+ .listRowSeparator(.hidden)
+
+ TransactionHeaderView(
+ viewStore: ViewStore(.placeholder, observe: { $0 }),
+ transaction: .mockedSent
+ )
+ .listRowSeparator(.hidden)
+
+ TransactionHeaderView(
+ viewStore: ViewStore(.placeholder, observe: { $0 }),
+ transaction: .mockedReceived
+ )
+ .listRowSeparator(.hidden)
+
+ TransactionHeaderView(
+ viewStore: ViewStore(.placeholder, observe: { $0 }),
+ transaction: .mockedSending
+ )
+ .listRowSeparator(.hidden)
+
+ TransactionHeaderView(
+ viewStore: ViewStore(.placeholder, observe: { $0 }),
+ transaction: .mockedReceiving
+ )
+ .listRowSeparator(.hidden)
+ }
+ .listStyle(.plain)
+}
+
diff --git a/modules/Sources/Features/TransactionList/Views/TransactionIDView.swift b/modules/Sources/Features/TransactionList/Views/TransactionIDView.swift
new file mode 100644
index 00000000..9f2a488e
--- /dev/null
+++ b/modules/Sources/Features/TransactionList/Views/TransactionIDView.swift
@@ -0,0 +1,65 @@
+//
+// TransactionIdView.swift
+//
+//
+// Created by Lukáš Korba on 05.11.2023.
+//
+
+import SwiftUI
+import ComposableArchitecture
+import Generated
+import Models
+
+struct TransactionIdView: View {
+ let viewStore: TransactionListViewStore
+ let transaction: TransactionState
+
+ public init(viewStore: TransactionListViewStore, transaction: TransactionState) {
+ self.viewStore = viewStore
+ self.transaction = transaction
+ }
+
+ var body: some View {
+ VStack(alignment: .leading, spacing: 0) {
+ if !transaction.isIdExpanded {
+ HStack {
+ Text(L10n.TransactionList.transactionId)
+
+ Button {
+ viewStore.send(.transactionIdExpandRequested(transaction.id))
+ } label: {
+ Text(transaction.id)
+ .lineLimit(1)
+ .truncationMode(.middle)
+ }
+
+ Spacer(minLength: 50)
+ }
+ .padding(.vertical, 20)
+ }
+
+ if transaction.isIdExpanded {
+ Text(L10n.TransactionList.transactionId)
+ .padding(.top, 20)
+ .padding(.bottom, 4)
+
+ HStack {
+ Text(transaction.id)
+ .font(.custom(FontFamily.Inter.bold.name, size: 13))
+
+ Spacer(minLength: 100)
+ }
+ .padding(.bottom, 20)
+ }
+ }
+ .font(.custom(FontFamily.Inter.regular.name, size: 13))
+ .foregroundColor(Asset.Colors.shade47.color)
+ }
+}
+
+#Preview {
+ TransactionIdView(
+ viewStore: ViewStore(.placeholder, observe: { $0 }),
+ transaction: .placeholder()
+ )
+}
diff --git a/modules/Sources/Features/TransactionList/Views/TransactionRowView.swift b/modules/Sources/Features/TransactionList/Views/TransactionRowView.swift
new file mode 100644
index 00000000..1678fbc3
--- /dev/null
+++ b/modules/Sources/Features/TransactionList/Views/TransactionRowView.swift
@@ -0,0 +1,109 @@
+//
+// TransactionRowView.swift
+// secant-testnet
+//
+// Created by Lukáš Korba on 21.06.2022.
+//
+
+import SwiftUI
+import ComposableArchitecture
+import ZcashLightClientKit
+import Models
+import Generated
+import UIComponents
+
+public struct TransactionRowView: View {
+ let viewStore: TransactionListViewStore
+ let transaction: TransactionState
+ let tokenName: String
+ let isLatestTransaction: Bool
+
+ public init(
+ viewStore: TransactionListViewStore,
+ transaction: TransactionState,
+ tokenName: String,
+ isLatestTransaction: Bool = false
+ ) {
+ self.viewStore = viewStore
+ self.transaction = transaction
+ self.tokenName = tokenName
+ self.isLatestTransaction = isLatestTransaction
+ }
+
+ public var body: some View {
+ Button {
+ viewStore.send(.transactionExpandRequested(transaction.id), animation: .default)
+ } label: {
+ if transaction.isExpanded {
+ TransactionHeaderView(
+ viewStore: viewStore,
+ transaction: transaction,
+ isLatestTransaction: isLatestTransaction
+ )
+ } else {
+ TransactionHeaderView(
+ viewStore: viewStore,
+ transaction: transaction,
+ isLatestTransaction: isLatestTransaction
+ )
+ }
+ }
+
+ if transaction.isExpanded {
+ Group {
+ MessageView(
+ message: transaction.textMemo?.toString(),
+ isSpending: transaction.isSpending,
+ isFailed: transaction.status == .failed
+ )
+
+ TransactionIdView(
+ viewStore: viewStore,
+ transaction: transaction
+ )
+
+ if transaction.isSpending {
+ TransactionFeeView(fee: transaction.fee)
+ .padding(.vertical, 10)
+ }
+
+ Button {
+ viewStore.send(.transactionCollapseRequested(transaction.id), animation: .default)
+ } label: {
+ CollapseTransactionView()
+ .padding(.vertical, 20)
+ }
+ }
+ .padding(.horizontal, 60)
+ }
+ }
+}
+
+#Preview {
+ List {
+ TransactionRowView(
+ viewStore: ViewStore(.placeholder, observe: { $0 }),
+ transaction: .mockedFailed,
+ tokenName: "ZEC"
+ )
+ .listRowSeparator(.hidden)
+ .listRowInsets(EdgeInsets())
+
+ TransactionRowView(
+ viewStore: ViewStore(.placeholder, observe: { $0 }),
+ transaction: .mockedReceived,
+ tokenName: "ZEC"
+ )
+ .listRowSeparator(.hidden)
+ .listRowInsets(EdgeInsets())
+
+ TransactionRowView(
+ viewStore: ViewStore(.placeholder, observe: { $0 }),
+ transaction: .mockedSent,
+ tokenName: "ZEC"
+ )
+ .listRowSeparator(.hidden)
+ .listRowInsets(EdgeInsets())
+ }
+ .listStyle(.plain)
+}
diff --git a/modules/Sources/Features/WalletEventsFlow/Views/TransactionDetailView.swift b/modules/Sources/Features/WalletEventsFlow/Views/TransactionDetailView.swift
deleted file mode 100644
index b53073b0..00000000
--- a/modules/Sources/Features/WalletEventsFlow/Views/TransactionDetailView.swift
+++ /dev/null
@@ -1,301 +0,0 @@
-import SwiftUI
-import ComposableArchitecture
-import ZcashLightClientKit
-import Utils
-import Models
-import Generated
-import UIComponents
-
-public struct TransactionDetailView: View {
- public enum RowMark {
- case neutral
- case success
- case fail
- case inactive
- case highlight
- }
-
- let store: WalletEventsFlowStore
- let transaction: TransactionState
- let tokenName: String
-
- public init(store: WalletEventsFlowStore, transaction: TransactionState, tokenName: String) {
- self.store = store
- self.transaction = transaction
- self.tokenName = tokenName
- }
-
- public var body: some View {
- WithViewStore(store) { viewStore in
- VStack(alignment: .leading) {
- header
-
- HStack {
- VStack(alignment: .leading) {
- switch transaction.status {
- case .paid:
- Text(L10n.Transaction.youSent(transaction.zecAmount.decimalZashiFormatted(), tokenName))
- .padding()
- address(mark: .inactive, viewStore: viewStore)
- memo(transaction, viewStore, mark: .highlight)
-
- case .sending:
- Text(L10n.Transaction.youAreSending(transaction.zecAmount.decimalZashiFormatted(), tokenName))
- .padding()
- address(mark: .inactive, viewStore: viewStore)
- memo(transaction, viewStore, mark: .highlight)
-
- case .receiving:
- Text(L10n.Transaction.youAreReceiving(transaction.zecAmount.decimalZashiFormatted(), tokenName))
- .padding()
- memo(transaction, viewStore, mark: .highlight)
-
- case .received:
- Text(L10n.Transaction.youReceived(transaction.zecAmount.decimalZashiFormatted(), tokenName))
- .padding()
- memo(transaction, viewStore, mark: .highlight)
-
- case .failed:
- Text(L10n.Transaction.youDidNotSent(transaction.zecAmount.decimalZashiFormatted(), tokenName))
- .padding()
-
- address(mark: .inactive, viewStore: viewStore)
- memo(transaction, viewStore, mark: .highlight)
-
- Text(L10n.TransactionDetail.error(transaction.errorMessage ?? L10n.General.unknown))
- .padding()
- }
- }
-
- Spacer()
- }
-
- Spacer()
- }
- .applyScreenBackground()
- .navigationTitle(L10n.TransactionDetail.title)
- }
- .zashiBack()
- }
-}
-
-extension TransactionDetailView {
- var header: some View {
- HStack {
- switch transaction.status {
- case .sending:
- Text(L10n.Transaction.sending)
- Spacer()
- case .receiving:
- Text(L10n.Transaction.receiving)
- Spacer()
- case .failed:
- Text("\(transaction.dateString ?? L10n.General.dateNotAvailable)")
- default:
- Text("\(transaction.dateString ?? L10n.General.dateNotAvailable)")
- }
- }
- .padding()
- }
-
- func address(mark: RowMark = .neutral, viewStore: WalletEventsFlowViewStore) -> some View {
- Text("\(addressPrefixText) \(transaction.address)")
- .lineLimit(1)
- .truncationMode(.middle)
- .padding()
- }
-
- func memo(
- _ transaction: TransactionState,
- _ viewStore: WalletEventsFlowViewStore,
- mark: RowMark = .neutral
- ) -> some View {
- Group {
- if let memoText = transaction.textMemo?.toString() {
- VStack(alignment: .leading) {
- Text(L10n.Transaction.withMemo)
- .padding(.leading)
- Text("\"\(memoText)\"")
- .multilineTextAlignment(.leading)
- .padding(.leading)
- }
- } else {
- EmptyView()
- }
- }
- }
-
- func confirmed(mark: RowMark = .neutral, viewStore: WalletEventsFlowViewStore) -> some View {
- HStack {
- Text(L10n.Transaction.confirmed)
- Spacer()
- Text(L10n.Transaction.confirmedTimes(transaction.confirmationsWith(viewStore.latestMinedHeight)))
- }
- .transactionDetailRow(mark: mark)
- }
-
- func confirming(mark: RowMark = .neutral, viewStore: WalletEventsFlowViewStore) -> some View {
- HStack {
- Text(L10n.Transaction.confirming(viewStore.requiredTransactionConfirmations))
- Spacer()
- Text("\(transaction.confirmationsWith(viewStore.latestMinedHeight))/\(viewStore.requiredTransactionConfirmations)")
- }
- .transactionDetailRow(mark: mark)
- }
-}
-
-extension TransactionDetailView {
- var addressPrefixText: String {
- (transaction.status == .received || transaction.status == .receiving)
- ? "" : L10n.Transaction.to
- }
-
- var heightText: String {
- guard let minedHeight = transaction.minedHeight else { return L10n.Transaction.unconfirmed }
- return minedHeight > 0 ? String(minedHeight) : L10n.Transaction.unconfirmed
- }
-}
-
-// MARK: - Row modifier
-
-struct TransactionDetailRow: ViewModifier {
- let mark: TransactionDetailView.RowMark
- 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(markColor(mark))
- }
-
- private func markColor(_ mark: TransactionDetailView.RowMark) -> Color {
- let markColor: Color
-
- switch mark {
- case .neutral: markColor = Asset.Colors.primary.color
- case .success: markColor = Asset.Colors.primary.color
- case .fail: markColor = Asset.Colors.primary.color
- case .inactive: markColor = Asset.Colors.primary.color
- case .highlight: markColor = Asset.Colors.primary.color
- }
-
- return markColor
- }
-}
-
-extension View {
- func transactionDetailRow(
- mark: TransactionDetailView.RowMark = .neutral
- ) -> some View {
- modifier(
- TransactionDetailRow(
- mark: mark,
- textColor: mark == .inactive ?
- Asset.Colors.primary.color :
- Asset.Colors.primary.color,
- backgroundColor: Asset.Colors.primary.color
- )
- )
- }
-}
-
-// MARK: - Previews
-
-struct TransactionDetail_Previews: PreviewProvider {
- static var previews: some View {
- NavigationView {
- TransactionDetailView(
- store: WalletEventsFlowStore.placeholder,
- transaction:
- TransactionState(
- errorMessage: L10n.Error.rollBack,
- memos: [Memo.placeholder],
- minedHeight: 1_875_256,
- zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
- fee: Zatoshi(1_000_000),
- id: "ff3927e1f83df9b1b0dc75540ddc59ee435eecebae914d2e6dfe8576fbedc9a8",
- status: .paid(success: true),
- timestamp: 1234567,
- zecAmount: Zatoshi(25_000_000)
- ),
- tokenName: "ZEC"
- )
- .preferredColorScheme(.light)
- }
-
- NavigationView {
- TransactionDetailView(
- store: WalletEventsFlowStore.placeholder,
- transaction:
- TransactionState(
- errorMessage: L10n.Error.rollBack,
- memos: [Memo.placeholder],
- minedHeight: 1_875_256,
- zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
- fee: Zatoshi(1_000_000),
- id: "ff3927e1f83df9b1b0dc75540ddc59ee435eecebae914d2e6dfe8576fbedc9a8",
- status: .sending,
- timestamp: 1234567,
- zecAmount: Zatoshi(25_000_000)
- ),
- tokenName: "ZEC"
- )
- .preferredColorScheme(.light)
- }
-
- NavigationView {
- TransactionDetailView(
- store: WalletEventsFlowStore.placeholder,
- transaction:
- TransactionState(
- errorMessage: L10n.Error.rollBack,
- memos: [Memo.placeholder],
- minedHeight: 1_875_256,
- zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
- fee: Zatoshi(1_000_000),
- id: "ff3927e1f83df9b1b0dc75540ddc59ee435eecebae914d2e6dfe8576fbedc9a8",
- status: .failed,
- timestamp: 1234567,
- zecAmount: Zatoshi(25_000_000)
- ),
- tokenName: "ZEC"
- )
- .preferredColorScheme(.light)
- }
-
- NavigationView {
- TransactionDetailView(
- store: WalletEventsFlowStore.placeholder,
- transaction:
- TransactionState(
- errorMessage: L10n.Error.rollBack,
- memos: [Memo.placeholder],
- minedHeight: 1_875_256,
- zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
- fee: Zatoshi(1_000_000),
- id: "ff3927e1f83df9b1b0dc75540ddc59ee435eecebae914d2e6dfe8576fbedc9a8",
- status: .received,
- timestamp: 1234567,
- zecAmount: Zatoshi(25_000_000)
- ),
- tokenName: "ZEC"
- )
- .preferredColorScheme(.light)
- }
- }
-}
-
-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.
- """)
-}
diff --git a/modules/Sources/Features/WalletEventsFlow/Views/TransactionRowView.swift b/modules/Sources/Features/WalletEventsFlow/Views/TransactionRowView.swift
deleted file mode 100644
index 3210ab29..00000000
--- a/modules/Sources/Features/WalletEventsFlow/Views/TransactionRowView.swift
+++ /dev/null
@@ -1,162 +0,0 @@
-//
-// TransactionRowView.swift
-// secant-testnet
-//
-// Created by Lukáš Korba on 21.06.2022.
-//
-
-import SwiftUI
-import ZcashLightClientKit
-import Models
-import Generated
-
-public struct TransactionRowView: View {
- let transaction: TransactionState
- let tokenName: String
-
- public init(transaction: TransactionState, tokenName: String) {
- self.transaction = transaction
- self.tokenName = tokenName
- }
-
- public var body: some View {
- ZStack {
- icon
-
- HStack {
- VStack(alignment: .leading) {
- Text(operationTitle)
- .font(.custom(FontFamily.Inter.bold.name, size: 12))
- .foregroundColor(Asset.Colors.primary.color)
-
- Text("\(transaction.dateString ?? L10n.General.dateNotAvailable)")
- .font(.custom(FontFamily.Inter.regular.name, size: 12))
- .foregroundColor(Asset.Colors.suppressed72.color)
- .opacity(0.5)
- }
-
- Spacer()
-
- Group {
- Text(transaction.unarySymbol)
- + Text(transaction.zecAmount.decimalZashiFormatted())
- }
- .font(.custom(FontFamily.Inter.regular.name, size: 12))
- .foregroundColor(
- transaction.unarySymbol == "-"
- ? Asset.Colors.error.color
- : Asset.Colors.primary.color
- )
- .padding(.trailing, 30)
- }
- .padding(.leading, 80)
- }
- .frame(height: 60)
- }
-}
-
-extension TransactionRowView {
- var operationTitle: String {
- switch transaction.status {
- case .paid:
- return L10n.Transaction.sent
- case .received:
- return L10n.Transaction.received
- case .failed:
- // TODO: [#392] final text to be provided (https://github.com/zcash/secant-ios-wallet/issues/392)
- return L10n.Transaction.failed
- case .sending:
- return L10n.Transaction.sending
- case .receiving:
- return L10n.Transaction.receiving
- }
- }
-
- var icon: some View {
- let inTransaction = transaction.status == .received || transaction.status == .receiving
- return HStack {
- switch transaction.status {
- case .paid, .received, .sending, .receiving:
- Image(systemName: "envelope.fill")
- .resizable()
- .frame(width: 16, height: 12)
- .foregroundColor(Asset.Colors.primary.color)
- .padding(10)
- .padding(.leading, 14)
- case .failed:
- // TODO: [#392] final icon to be provided (https://github.com/zcash/secant-ios-wallet/issues/392)
- Circle()
- .frame(width: 30, height: 30)
- .foregroundColor(Color.red)
- .padding(15)
- }
-
- Spacer()
- }
- .padding(.leading, 15)
- }
-}
-
-struct TransactionRowView_Previews: PreviewProvider {
- static var previews: some View {
- TransactionRowView(
- transaction:
- .init(
- zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
- fee: Zatoshi(10),
- id: "2",
- status: .paid(success: true),
- timestamp: 1234567,
- zecAmount: Zatoshi(123_000_000)
- ),
- tokenName: "ZEC"
- )
- .applyScreenBackground()
- .previewLayout(.fixed(width: 428, height: 60))
-
- TransactionRowView(
- transaction:
- .init(
- zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
- fee: Zatoshi(10),
- id: "2",
- status: .failed,
- timestamp: 1234567,
- zecAmount: Zatoshi(123_000_000)
- ),
- tokenName: "ZEC"
- )
- .applyScreenBackground()
- .previewLayout(.fixed(width: 428, height: 60))
-
- TransactionRowView(
- transaction:
- .init(
- zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
- fee: Zatoshi(10),
- id: "2",
- status: .sending,
- timestamp: 1234567,
- zecAmount: Zatoshi(123_000_000)
- ),
- tokenName: "ZEC"
- )
- .applyScreenBackground()
- .previewLayout(.fixed(width: 428, height: 60))
-
- TransactionRowView(
- transaction:
- .init(
- zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
- fee: Zatoshi(10),
- id: "2",
- status: .received,
- timestamp: 1234567,
- zecAmount: Zatoshi(123_000_000)
- ),
- tokenName: "ZEC"
- )
- .applyScreenBackground()
- .previewLayout(.fixed(width: 428, height: 60))
- }
-}
diff --git a/modules/Sources/Features/WalletEventsFlow/Views/WalletEvent+View.swift b/modules/Sources/Features/WalletEventsFlow/Views/WalletEvent+View.swift
deleted file mode 100644
index 6541a6a3..00000000
--- a/modules/Sources/Features/WalletEventsFlow/Views/WalletEvent+View.swift
+++ /dev/null
@@ -1,89 +0,0 @@
-//
-// WalletEvent+View.swift
-// secant
-//
-// Created by Lukáš Korba on 30.05.2023.
-//
-
-import ComposableArchitecture
-import Models
-import Generated
-import SwiftUI
-import ZcashLightClientKit
-
-// MARK: - Rows
-
-extension WalletEvent {
- @ViewBuilder public func rowView(_ viewStore: WalletEventsFlowViewStore, tokenName: String) -> some View {
- switch state {
- case .transaction(let transaction):
- TransactionRowView(transaction: transaction, tokenName: tokenName)
- case .shielded(let zatoshi):
- // TODO: [#390] implement design once shielding is supported
- // https://github.com/zcash/secant-ios-wallet/issues/390
- Text(L10n.WalletEvent.Row.shielded(zatoshi.decimalZashiFormatted()))
- .padding(.leading, 30)
- case .walletImport:
- // TODO: [#391] implement design once shielding is supported
- // https://github.com/zcash/secant-ios-wallet/issues/391
- Text(L10n.WalletEvent.Row.import)
- .padding(.leading, 30)
- }
- }
-}
-
-// MARK: - Details
-
-extension WalletEvent {
- @ViewBuilder public func detailView(_ store: WalletEventsFlowStore, tokenName: String) -> some View {
- switch state {
- case .transaction(let transaction):
- TransactionDetailView(store: store, transaction: transaction, tokenName: tokenName)
- case .shielded(let zatoshi):
- // TODO: [#390] implement design once shielding is supported
- // https://github.com/zcash/secant-ios-wallet/issues/390
- Text(L10n.WalletEvent.Detail.shielded(zatoshi.decimalZashiFormatted()))
- case .walletImport:
- // TODO: [#391] implement design once shielding is supported
- // https://github.com/zcash/secant-ios-wallet/issues/391
- Text(L10n.WalletEvent.Detail.import)
- }
- }
-}
-
-// MARK: - Placeholders
-
-private extension WalletEvent {
- static func randomWalletEventState() -> WalletEvent.WalletEventState {
- switch Int.random(in: 0..<3) {
- case 1: return .shielded(Zatoshi(234_000_000))
- case 2: return .walletImport(BlockHeight(1_629_724))
- default: return .transaction(.placeholder)
- }
- }
-
- static func mockedWalletEventState(atIndex: Int) -> WalletEvent.WalletEventState {
- switch atIndex % 5 {
- case 0: return .transaction(.statePlaceholder(.received))
- case 1: return .transaction(.statePlaceholder(.failed))
- case 2: return .transaction(.statePlaceholder(.sending))
- case 3: return .transaction(.statePlaceholder(.receiving))
- case 4: return .transaction(.placeholder)
- default: return .transaction(.placeholder)
- }
- }
-}
-
-extension IdentifiedArrayOf where Element == WalletEvent {
- public static var placeholder: IdentifiedArrayOf {
- .init(
- uniqueElements: (0..<30).map {
- WalletEvent(
- id: String($0),
- state: WalletEvent.mockedWalletEventState(atIndex: $0),
- timestamp: 1234567
- )
- }
- )
- }
-}
diff --git a/modules/Sources/Features/WalletEventsFlow/WalletEventsFlowStore.swift b/modules/Sources/Features/WalletEventsFlow/WalletEventsFlowStore.swift
deleted file mode 100644
index b71d239c..00000000
--- a/modules/Sources/Features/WalletEventsFlow/WalletEventsFlowStore.swift
+++ /dev/null
@@ -1,252 +0,0 @@
-import ComposableArchitecture
-import SwiftUI
-import ZcashLightClientKit
-import Utils
-import Models
-import Generated
-import Pasteboard
-import SDKSynchronizer
-import ZcashSDKEnvironment
-
-public typealias WalletEventsFlowStore = Store
-public typealias WalletEventsFlowViewStore = ViewStore
-
-public struct WalletEventsFlowReducer: ReducerProtocol {
- private enum CancelId { case timer }
-
- public struct State: Equatable {
- public enum Destination: Equatable {
- case latest
- case all
- case showWalletEvent(WalletEvent)
- }
-
- @PresentationState public var alert: AlertState?
- public var destination: Destination?
- public var latestMinedHeight: BlockHeight?
- public var isScrollable = true
- public var requiredTransactionConfirmations = 0
- public var walletEvents = IdentifiedArrayOf.placeholder
- public var selectedWalletEvent: WalletEvent?
-
- public init(
- destination: Destination? = nil,
- latestMinedHeight: BlockHeight? = nil,
- isScrollable: Bool = true,
- requiredTransactionConfirmations: Int = 0,
- walletEvents: IdentifiedArrayOf = .placeholder,
- selectedWalletEvent: WalletEvent? = nil
- ) {
- self.destination = destination
- self.latestMinedHeight = latestMinedHeight
- self.isScrollable = isScrollable
- self.requiredTransactionConfirmations = requiredTransactionConfirmations
- self.walletEvents = walletEvents
- self.selectedWalletEvent = selectedWalletEvent
- }
- }
-
- public enum Action: Equatable {
- case alert(PresentationAction)
- case copyToPastboard(RedactableString)
- case onAppear
- case onDisappear
- case openBlockExplorer(URL?)
- case updateDestination(WalletEventsFlowReducer.State.Destination?)
- case synchronizerStateChanged(SyncStatus)
- case updateWalletEvents([WalletEvent])
- case warnBeforeLeavingApp(URL?)
- }
-
- @Dependency(\.mainQueue) var mainQueue
- @Dependency(\.pasteboard) var pasteboard
- @Dependency(\.sdkSynchronizer) var sdkSynchronizer
- @Dependency(\.zcashSDKEnvironment) var zcashSDKEnvironment
-
- public init() {}
-
- // swiftlint:disable:next cyclomatic_complexity
- public func reduce(into state: inout State, action: Action) -> ComposableArchitecture.EffectTask {
- switch action {
- case .onAppear:
- state.requiredTransactionConfirmations = zcashSDKEnvironment.requiredTransactionConfirmations
- return .merge(
- sdkSynchronizer.stateStream()
- .throttle(for: .seconds(0.2), scheduler: mainQueue, latest: true)
- .map { WalletEventsFlowReducer.Action.synchronizerStateChanged($0.syncStatus) }
- .eraseToEffect()
- .cancellable(id: CancelId.timer, cancelInFlight: true),
- .run { send in
- await send(.updateWalletEvents(try await sdkSynchronizer.getAllTransactions()))
- }
- )
-
- case .onDisappear:
- return .cancel(id: CancelId.timer)
-
- case .synchronizerStateChanged(.upToDate):
- state.latestMinedHeight = sdkSynchronizer.latestState().latestBlockHeight
- return .task {
- return .updateWalletEvents(try await sdkSynchronizer.getAllTransactions())
- }
-
- case .synchronizerStateChanged:
- return .none
-
- case .updateWalletEvents(let walletEvents):
- let sortedWalletEvents = walletEvents
- .sorted(by: { lhs, rhs in
- guard let lhsTimestamp = lhs.timestamp, let rhsTimestamp = rhs.timestamp else {
- return false
- }
- return lhsTimestamp > rhsTimestamp
- })
- state.walletEvents = IdentifiedArrayOf(uniqueElements: sortedWalletEvents)
- return .none
-
- case .updateDestination(.showWalletEvent(let walletEvent)):
- state.selectedWalletEvent = walletEvent
- state.destination = .showWalletEvent(walletEvent)
- return .none
-
- case .updateDestination(let destination):
- state.destination = destination
- if destination == nil {
- state.selectedWalletEvent = nil
- }
- return .none
-
- case .copyToPastboard(let value):
- pasteboard.setString(value)
- return .none
-
- case .alert(.presented(let action)):
- return Effect.send(action)
-
- case .alert(.dismiss):
- state.alert = nil
- return .none
-
- case .alert:
- return .none
-
- case .warnBeforeLeavingApp(let blockExplorerURL):
- state.alert = AlertState.warnBeforeLeavingApp(blockExplorerURL)
- return .none
-
- case .openBlockExplorer(let blockExplorerURL):
- if let url = blockExplorerURL {
- UIApplication.shared.open(url, options: [:], completionHandler: nil)
- }
- return .none
- }
- }
-}
-
-// MARK: - ViewStore
-
-extension WalletEventsFlowViewStore {
- private typealias Destination = WalletEventsFlowReducer.State.Destination
-
- func bindingForSelectedWalletEvent(_ walletEvent: WalletEvent?) -> Binding {
- self.binding(
- get: {
- guard let walletEvent else {
- return false
- }
-
- return $0.destination.map(/WalletEventsFlowReducer.State.Destination.showWalletEvent) == walletEvent
- },
- send: { isActive in
- guard let walletEvent else {
- return WalletEventsFlowReducer.Action.updateDestination(nil)
- }
-
- return WalletEventsFlowReducer.Action.updateDestination(
- isActive ? WalletEventsFlowReducer.State.Destination.showWalletEvent(walletEvent) : nil
- )
- }
- )
- }
-}
-
-// MARK: Alerts
-
-extension AlertState where Action == WalletEventsFlowReducer.Action {
- public static func warnBeforeLeavingApp(_ blockExplorerURL: URL?) -> AlertState {
- AlertState {
- TextState(L10n.WalletEvent.Alert.LeavingApp.title)
- } actions: {
- ButtonState(action: .openBlockExplorer(blockExplorerURL)) {
- TextState(L10n.WalletEvent.Alert.LeavingApp.Button.seeOnline)
- }
- ButtonState(role: .cancel, action: .alert(.dismiss)) {
- TextState(L10n.WalletEvent.Alert.LeavingApp.Button.nevermind)
- }
- } message: {
- TextState(L10n.WalletEvent.Alert.LeavingApp.message)
- }
- }
-}
-
-// MARK: Placeholders
-
-extension TransactionState {
- public static var placeholder: Self {
- .init(
- zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
- fee: Zatoshi(10),
- id: "2",
- status: .paid(success: true),
- timestamp: 1234567,
- zecAmount: Zatoshi(123_000_000)
- )
- }
-
- public static func statePlaceholder(_ status: Status) -> Self {
- .init(
- zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
- fee: Zatoshi(10),
- id: "2",
- status: status,
- timestamp: 1234567,
- zecAmount: Zatoshi(123_000_000)
- )
- }
-}
-
-extension WalletEventsFlowReducer.State {
- public static var placeHolder: Self {
- .init(walletEvents: .placeholder)
- }
-
- public static var emptyPlaceHolder: Self {
- .init(walletEvents: [])
- }
-}
-
-extension WalletEventsFlowStore {
- public static var placeholder: Store {
- return Store(
- initialState: .placeHolder,
- reducer: WalletEventsFlowReducer()
- .dependency(\.zcashSDKEnvironment, .testnet)
- )
- }
-}
-
-extension IdentifiedArrayOf where Element == TransactionState {
- public static var placeholder: IdentifiedArrayOf {
- return .init(
- uniqueElements: (0..<30).map {
- TransactionState(
- fee: Zatoshi(10),
- id: String($0),
- status: .paid(success: true),
- timestamp: 1234567,
- zecAmount: Zatoshi(25)
- )
- }
- )
- }
-}
diff --git a/modules/Sources/Features/WalletEventsFlow/WalletEventsFlowView.swift b/modules/Sources/Features/WalletEventsFlow/WalletEventsFlowView.swift
deleted file mode 100644
index 994bca92..00000000
--- a/modules/Sources/Features/WalletEventsFlow/WalletEventsFlowView.swift
+++ /dev/null
@@ -1,58 +0,0 @@
-import SwiftUI
-import ComposableArchitecture
-import Generated
-import UIComponents
-
-public struct WalletEventsFlowView: View {
- let store: WalletEventsFlowStore
- let tokenName: String
-
- public init(store: WalletEventsFlowStore, tokenName: String) {
- self.store = store
- self.tokenName = tokenName
- }
-
- public var body: some View {
- WithViewStore(store) { viewStore in
- List {
- walletEventsList(with: viewStore)
- }
- .navigationTitle(L10n.Transactions.title)
- .listStyle(.plain)
- .onAppear { viewStore.send(.onAppear) }
- .onDisappear(perform: { viewStore.send(.onDisappear) })
- .navigationLinkEmpty(isActive: viewStore.bindingForSelectedWalletEvent(viewStore.selectedWalletEvent)) {
- viewStore.selectedWalletEvent?.detailView(store, tokenName: tokenName)
- }
- }
- .alert(store: store.scope(
- state: \.$alert,
- action: { .alert($0) }
- ))
- .zashiBack()
- }
-}
-
-extension WalletEventsFlowView {
- func walletEventsList(with viewStore: WalletEventsFlowViewStore) -> some View {
- ForEach(viewStore.walletEvents) { walletEvent in
- walletEvent.rowView(viewStore, tokenName: tokenName)
- .onTapGesture {
- viewStore.send(.updateDestination(.showWalletEvent(walletEvent)))
- }
- .listRowInsets(EdgeInsets())
- .frame(height: 60)
- }
- }
-}
-
-// MARK: - Previews
-
-struct TransactionView_Previews: PreviewProvider {
- static var previews: some View {
- NavigationView {
- WalletEventsFlowView(store: .placeholder, tokenName: "ZEC")
- .preferredColorScheme(.light)
- }
- }
-}
diff --git a/modules/Sources/Generated/L10n.swift b/modules/Sources/Generated/L10n.swift
index e53ee124..3a16bbb8 100644
--- a/modules/Sources/Generated/L10n.swift
+++ b/modules/Sources/Generated/L10n.swift
@@ -664,14 +664,16 @@ public enum L10n {
public static func confirming(_ p1: Any) -> String {
return L10n.tr("Localizable", "transaction.confirming", String(describing: p1), fallback: "Confirming ~%@mins")
}
- /// Failed
- public static let failed = L10n.tr("Localizable", "transaction.failed", fallback: "Failed")
+ /// Receive failed
+ public static let failedReceive = L10n.tr("Localizable", "transaction.failedReceive", fallback: "Receive failed")
+ /// Send failed
+ public static let failedSend = L10n.tr("Localizable", "transaction.failedSend", fallback: "Send failed")
/// Received
public static let received = L10n.tr("Localizable", "transaction.received", fallback: "Received")
- /// Receiving
- public static let receiving = L10n.tr("Localizable", "transaction.receiving", fallback: "Receiving")
- /// Sending
- public static let sending = L10n.tr("Localizable", "transaction.sending", fallback: "Sending")
+ /// Receiving...
+ public static let receiving = L10n.tr("Localizable", "transaction.receiving", fallback: "Receiving...")
+ /// Sending...
+ public static let sending = L10n.tr("Localizable", "transaction.sending", fallback: "Sending...")
/// Sent
public static let sent = L10n.tr("Localizable", "transaction.sent", fallback: "Sent")
/// to
@@ -709,6 +711,18 @@ public enum L10n {
/// Transaction detail
public static let title = L10n.tr("Localizable", "transactionDetail.title", fallback: "Transaction detail")
}
+ public enum TransactionList {
+ /// Collapse transaction
+ public static let collapse = L10n.tr("Localizable", "transactionList.collapse", fallback: "Collapse transaction")
+ /// Message
+ public static let messageTitle = L10n.tr("Localizable", "transactionList.messageTitle", fallback: "Message")
+ /// No message included in transaction
+ public static let noMessageIncluded = L10n.tr("Localizable", "transactionList.noMessageIncluded", fallback: "No message included in transaction")
+ /// Transaction Fee
+ public static let transactionFee = L10n.tr("Localizable", "transactionList.transactionFee", fallback: "Transaction Fee")
+ /// Transaction ID
+ public static let transactionId = L10n.tr("Localizable", "transactionList.transactionId", fallback: "Transaction ID")
+ }
public enum Transactions {
/// Transactions
public static let title = L10n.tr("Localizable", "transactions.title", fallback: "Transactions")
@@ -737,38 +751,6 @@ public enum L10n {
public static let phraseAgain = L10n.tr("Localizable", "validationSuccess.button.phraseAgain", fallback: "Show me my phrase again")
}
}
- public enum WalletEvent {
- public enum Alert {
- public enum LeavingApp {
- /// While usually an acceptable risk, you will possibly exposing your behavior and interest in this transaction by going online. OH NOES! What will you do?
- public static let message = L10n.tr("Localizable", "walletEvent.alert.leavingApp.message", fallback: "While usually an acceptable risk, you will possibly exposing your behavior and interest in this transaction by going online. OH NOES! What will you do?")
- /// You are exiting your wallet
- public static let title = L10n.tr("Localizable", "walletEvent.alert.leavingApp.title", fallback: "You are exiting your wallet")
- public enum Button {
- /// NEVERMIND
- public static let nevermind = L10n.tr("Localizable", "walletEvent.alert.leavingApp.button.nevermind", fallback: "NEVERMIND")
- /// SEE TX ONLINE
- public static let seeOnline = L10n.tr("Localizable", "walletEvent.alert.leavingApp.button.seeOnline", fallback: "SEE TX ONLINE")
- }
- }
- }
- public enum Detail {
- /// wallet import wallet event
- public static let `import` = L10n.tr("Localizable", "walletEvent.detail.import", fallback: "wallet import wallet event")
- /// shielded %@ detail
- public static func shielded(_ p1: Any) -> String {
- return L10n.tr("Localizable", "walletEvent.detail.shielded", String(describing: p1), fallback: "shielded %@ detail")
- }
- }
- public enum Row {
- /// wallet import wallet event
- public static let `import` = L10n.tr("Localizable", "walletEvent.row.import", fallback: "wallet import wallet event")
- /// shielded wallet event %@
- public static func shielded(_ p1: Any) -> String {
- return L10n.tr("Localizable", "walletEvent.row.shielded", String(describing: p1), fallback: "shielded wallet event %@")
- }
- }
- }
public enum WelcomeScreen {
/// Just Loading, one sec
public static let subtitle = L10n.tr("Localizable", "welcomeScreen.subtitle", fallback: "Just Loading, one sec")
diff --git a/modules/Sources/Generated/Resources/Assets.xcassets/FlyReceived.imageset/Contents.json b/modules/Sources/Generated/Resources/Assets.xcassets/FlyReceived.imageset/Contents.json
new file mode 100644
index 00000000..8077a99b
--- /dev/null
+++ b/modules/Sources/Generated/Resources/Assets.xcassets/FlyReceived.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "flyReceived.png",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/modules/Sources/Generated/Resources/Assets.xcassets/FlyReceived.imageset/flyReceived.png b/modules/Sources/Generated/Resources/Assets.xcassets/FlyReceived.imageset/flyReceived.png
new file mode 100644
index 00000000..7c6c80b8
Binary files /dev/null and b/modules/Sources/Generated/Resources/Assets.xcassets/FlyReceived.imageset/flyReceived.png differ
diff --git a/modules/Sources/Generated/Resources/Assets.xcassets/flyReceivedFilled.imageset/Contents.json b/modules/Sources/Generated/Resources/Assets.xcassets/flyReceivedFilled.imageset/Contents.json
new file mode 100644
index 00000000..73522d2f
--- /dev/null
+++ b/modules/Sources/Generated/Resources/Assets.xcassets/flyReceivedFilled.imageset/Contents.json
@@ -0,0 +1,21 @@
+{
+ "images" : [
+ {
+ "filename" : "flyReceivedFilled.png",
+ "idiom" : "universal",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/modules/Sources/Generated/Resources/Assets.xcassets/flyReceivedFilled.imageset/flyReceivedFilled.png b/modules/Sources/Generated/Resources/Assets.xcassets/flyReceivedFilled.imageset/flyReceivedFilled.png
new file mode 100644
index 00000000..18cc0915
Binary files /dev/null and b/modules/Sources/Generated/Resources/Assets.xcassets/flyReceivedFilled.imageset/flyReceivedFilled.png differ
diff --git a/modules/Sources/Generated/Resources/Assets.xcassets/shield.imageset/Contents.json b/modules/Sources/Generated/Resources/Assets.xcassets/shield.imageset/Contents.json
new file mode 100644
index 00000000..dd8c90c7
--- /dev/null
+++ b/modules/Sources/Generated/Resources/Assets.xcassets/shield.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "shield.png",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/modules/Sources/Generated/Resources/Assets.xcassets/shield.imageset/shield.png b/modules/Sources/Generated/Resources/Assets.xcassets/shield.imageset/shield.png
new file mode 100644
index 00000000..a943e484
Binary files /dev/null and b/modules/Sources/Generated/Resources/Assets.xcassets/shield.imageset/shield.png differ
diff --git a/modules/Sources/Generated/Resources/Assets.xcassets/upArrow.imageset/Contents.json b/modules/Sources/Generated/Resources/Assets.xcassets/upArrow.imageset/Contents.json
new file mode 100644
index 00000000..076d142a
--- /dev/null
+++ b/modules/Sources/Generated/Resources/Assets.xcassets/upArrow.imageset/Contents.json
@@ -0,0 +1,12 @@
+{
+ "images" : [
+ {
+ "filename" : "upArrow.png",
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/modules/Sources/Generated/Resources/Assets.xcassets/upArrow.imageset/upArrow.png b/modules/Sources/Generated/Resources/Assets.xcassets/upArrow.imageset/upArrow.png
new file mode 100644
index 00000000..e6bc01f7
Binary files /dev/null and b/modules/Sources/Generated/Resources/Assets.xcassets/upArrow.imageset/upArrow.png differ
diff --git a/modules/Sources/Generated/Resources/Colors.xcassets/messageBcgReceived.colorset/Contents.json b/modules/Sources/Generated/Resources/Colors.xcassets/messageBcgReceived.colorset/Contents.json
new file mode 100644
index 00000000..5f48838f
--- /dev/null
+++ b/modules/Sources/Generated/Resources/Colors.xcassets/messageBcgReceived.colorset/Contents.json
@@ -0,0 +1,20 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0xCD",
+ "green" : "0xE9",
+ "red" : "0xF6"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/modules/Sources/Generated/Resources/Colors.xcassets/tabsUnderline.colorset/Contents.json b/modules/Sources/Generated/Resources/Colors.xcassets/primaryTint.colorset/Contents.json
similarity index 100%
rename from modules/Sources/Generated/Resources/Colors.xcassets/tabsUnderline.colorset/Contents.json
rename to modules/Sources/Generated/Resources/Colors.xcassets/primaryTint.colorset/Contents.json
diff --git a/modules/Sources/Generated/Resources/Colors.xcassets/suppressed30.colorset/Contents.json b/modules/Sources/Generated/Resources/Colors.xcassets/shade30.colorset/Contents.json
similarity index 100%
rename from modules/Sources/Generated/Resources/Colors.xcassets/suppressed30.colorset/Contents.json
rename to modules/Sources/Generated/Resources/Colors.xcassets/shade30.colorset/Contents.json
diff --git a/modules/Sources/Generated/Resources/Colors.xcassets/suppressed47.colorset/Contents.json b/modules/Sources/Generated/Resources/Colors.xcassets/shade47.colorset/Contents.json
similarity index 100%
rename from modules/Sources/Generated/Resources/Colors.xcassets/suppressed47.colorset/Contents.json
rename to modules/Sources/Generated/Resources/Colors.xcassets/shade47.colorset/Contents.json
diff --git a/modules/Sources/Generated/Resources/Colors.xcassets/suppressed72.colorset/Contents.json b/modules/Sources/Generated/Resources/Colors.xcassets/shade72.colorset/Contents.json
similarity index 100%
rename from modules/Sources/Generated/Resources/Colors.xcassets/suppressed72.colorset/Contents.json
rename to modules/Sources/Generated/Resources/Colors.xcassets/shade72.colorset/Contents.json
diff --git a/modules/Sources/Generated/Resources/Colors.xcassets/shade97.colorset/Contents.json b/modules/Sources/Generated/Resources/Colors.xcassets/shade97.colorset/Contents.json
new file mode 100644
index 00000000..3e7ba6b8
--- /dev/null
+++ b/modules/Sources/Generated/Resources/Colors.xcassets/shade97.colorset/Contents.json
@@ -0,0 +1,20 @@
+{
+ "colors" : [
+ {
+ "color" : {
+ "color-space" : "srgb",
+ "components" : {
+ "alpha" : "1.000",
+ "blue" : "0.970",
+ "green" : "0.970",
+ "red" : "0.970"
+ }
+ },
+ "idiom" : "universal"
+ }
+ ],
+ "info" : {
+ "author" : "xcode",
+ "version" : 1
+ }
+}
diff --git a/modules/Sources/Generated/Resources/Localizable.strings b/modules/Sources/Generated/Resources/Localizable.strings
index 3d53bf16..8d41cc14 100644
--- a/modules/Sources/Generated/Resources/Localizable.strings
+++ b/modules/Sources/Generated/Resources/Localizable.strings
@@ -81,7 +81,6 @@
"importWallet.optionalBirthday" = "(optional)";
"importWallet.enterPlaceholder" = "Enter private seed here…";
-
// MARK: - Tabs
"tabs.account" = "Account";
"tabs.send" = "Send";
@@ -174,27 +173,6 @@ Sharing this private data is irrevocable — once you have shared this private d
"sync.message.stopped" = "Stopped";
"sync.message.sync" = "%@%% Synced";
-// MARK: - Transactions
-"transactions.title" = "Transactions";
-"transaction.sent" = "Sent";
-"transaction.sending" = "Sending";
-"transaction.receiving" = "Receiving";
-"transaction.received" = "Received";
-"transaction.failed" = "Failed";
-"transaction.youSent" = "You sent %@ %@";
-"transaction.youAreSending" = "You are sending %@ %@";
-"transaction.youAreReceiving" = "You are receiving %@ %@";
-"transaction.youReceived" = "You received %@ %@";
-"transaction.youDidNotSent" = "You DID NOT send %@ %@";
-"transaction.confirmed" = "Confirmed";
-"transaction.confirmedTimes" = "%@ times";
-"transaction.confirming" = "Confirming ~%@mins";
-"transaction.withMemo" = "With memo:";
-"transaction.to" = "to";
-"transaction.unconfirmed" = "unconfirmed";
-"transactionDetail.title" = "Transaction detail";
-"transactionDetail.error" = "Error: %@";
-
// MARK: - Not Enough Free Space
"nefs.message" = "Not enough space on disk to do synchronisation!";
@@ -221,15 +199,34 @@ Sharing this private data is irrevocable — once you have shared this private d
"qrCodeFor" = "QR Code for %@";
"general.dateNotAvailable" = "date not available";
-// MARK: - Wallet event
-"walletEvent.row.shielded" = "shielded wallet event %@";
-"walletEvent.row.import" = "wallet import wallet event";
-"walletEvent.detail.shielded" = "shielded %@ detail";
-"walletEvent.detail.import" = "wallet import wallet event";
-"walletEvent.alert.leavingApp.title" = "You are exiting your wallet";
-"walletEvent.alert.leavingApp.message" = "While usually an acceptable risk, you will possibly exposing your behavior and interest in this transaction by going online. OH NOES! What will you do?";
-"walletEvent.alert.leavingApp.button.nevermind" = "NEVERMIND";
-"walletEvent.alert.leavingApp.button.seeOnline" = "SEE TX ONLINE";
+// MARK: - Transaction List
+"transactionList.collapse" = "Collapse transaction";
+"transactionList.messageTitle" = "Message";
+"transactionList.noMessageIncluded" = "No message included in transaction";
+"transactionList.transactionFee" = "Transaction Fee";
+"transactionList.transactionId" = "Transaction ID";
+
+// MARK: - Transactions
+"transactions.title" = "Transactions";
+"transaction.sent" = "Sent";
+"transaction.sending" = "Sending...";
+"transaction.receiving" = "Receiving...";
+"transaction.received" = "Received";
+"transaction.failedSend" = "Send failed";
+"transaction.failedReceive" = "Receive failed";
+"transaction.youSent" = "You sent %@ %@";
+"transaction.youAreSending" = "You are sending %@ %@";
+"transaction.youAreReceiving" = "You are receiving %@ %@";
+"transaction.youReceived" = "You received %@ %@";
+"transaction.youDidNotSent" = "You DID NOT send %@ %@";
+"transaction.confirmed" = "Confirmed";
+"transaction.confirmedTimes" = "%@ times";
+"transaction.confirming" = "Confirming ~%@mins";
+"transaction.withMemo" = "With memo:";
+"transaction.to" = "to";
+"transaction.unconfirmed" = "unconfirmed";
+"transactionDetail.title" = "Transaction detail";
+"transactionDetail.error" = "Error: %@";
// MARK: - Local authentication
"localAuthentication.reason" = "The Following content requires authentication.";
diff --git a/modules/Sources/Generated/XCAssets+Generated.swift b/modules/Sources/Generated/XCAssets+Generated.swift
index 4aba3911..8135846e 100644
--- a/modules/Sources/Generated/XCAssets+Generated.swift
+++ b/modules/Sources/Generated/XCAssets+Generated.swift
@@ -23,21 +23,27 @@ public typealias AssetImageTypeAlias = ImageAsset.UniversalImage
// swiftlint:disable identifier_name line_length nesting type_body_length type_name
public enum Asset {
public enum Assets {
- public static let fly = ImageAsset(name: "Fly")
- public static let splashHi = ImageAsset(name: "SplashHi")
- public static let welcomeScreenLogo = ImageAsset(name: "WelcomeScreenLogo")
- public static let zashiLogo = ImageAsset(name: "ZashiLogo")
+ public static let fly = ImageAsset(name: "fly")
+ public static let flyReceived = ImageAsset(name: "flyReceived")
+ public static let flyReceivedFilled = ImageAsset(name: "flyReceivedFilled")
+ public static let shield = ImageAsset(name: "shield")
+ public static let splashHi = ImageAsset(name: "splashHi")
+ public static let upArrow = ImageAsset(name: "upArrow")
+ public static let welcomeScreenLogo = ImageAsset(name: "welcomeScreenLogo")
+ public static let zashiLogo = ImageAsset(name: "zashiLogo")
public static let zashiTitle = ImageAsset(name: "zashiTitle")
}
public enum Colors {
public static let error = ColorAsset(name: "error")
+ public static let messageBcgReceived = ColorAsset(name: "messageBcgReceived")
public static let primary = ColorAsset(name: "primary")
+ public static let primaryTint = ColorAsset(name: "primaryTint")
public static let secondary = ColorAsset(name: "secondary")
+ public static let shade30 = ColorAsset(name: "shade30")
+ public static let shade47 = ColorAsset(name: "shade47")
+ public static let shade72 = ColorAsset(name: "shade72")
+ public static let shade97 = ColorAsset(name: "shade97")
public static let splash = ColorAsset(name: "splash")
- public static let suppressed30 = ColorAsset(name: "suppressed30")
- public static let suppressed47 = ColorAsset(name: "suppressed47")
- public static let suppressed72 = ColorAsset(name: "suppressed72")
- public static let tabsUnderline = ColorAsset(name: "tabsUnderline")
}
}
// swiftlint:enable identifier_name line_length nesting type_body_length type_name
diff --git a/modules/Sources/Models/TransactionState.swift b/modules/Sources/Models/TransactionState.swift
index 166afff6..1b44d281 100644
--- a/modules/Sources/Models/TransactionState.swift
+++ b/modules/Sources/Models/TransactionState.swift
@@ -6,17 +6,18 @@
//
import Foundation
+import SwiftUI
import ZcashLightClientKit
import Generated
/// Representation of the transaction on the SDK side, used as a bridge to the TCA wallet side.
public struct TransactionState: Equatable, Identifiable {
public enum Status: Equatable {
- case paid(success: Bool)
- case received
case failed
- case sending
+ case paid
+ case received
case receiving
+ case sending
}
public var errorMessage: String?
@@ -25,6 +26,7 @@ public struct TransactionState: Equatable, Identifiable {
public var minedHeight: BlockHeight?
public var shielded = true
public var zAddress: String?
+ public var isSentTransaction: Bool
public var fee: Zatoshi
public var id: String
@@ -32,41 +34,129 @@ public struct TransactionState: Equatable, Identifiable {
public var timestamp: TimeInterval?
public var zecAmount: Zatoshi
+ public var isAddressExpanded: Bool
+ public var isExpanded: Bool
+ public var isIdExpanded: Bool
+
+ // UI Colors
+ public var balanceColor: Color {
+ status == .failed
+ ? Asset.Colors.error.color
+ : isSpending
+ ? Asset.Colors.error.color
+ : Asset.Colors.primary.color
+ }
+
+ public var titleColor: Color {
+ status == .failed
+ ? Asset.Colors.error.color
+ : isPending
+ ? Asset.Colors.shade47.color
+ : Asset.Colors.primary.color
+ }
+
+ // UI Texts
public var address: String {
zAddress ?? ""
}
- public var unarySymbol: String {
+ public var title: String {
+ switch status {
+ case .failed:
+ return isSentTransaction
+ ? L10n.Transaction.failedSend
+ : L10n.Transaction.failedReceive
+ case .paid:
+ return L10n.Transaction.sent
+ case .received:
+ return L10n.Transaction.received
+ case .receiving:
+ return L10n.Transaction.receiving
+ case .sending:
+ return L10n.Transaction.sending
+ }
+ }
+
+ public var roundedAmountString: String {
+ let formatted = zecAmount.decimalZashiFormatted()
+
switch status {
case .paid, .sending:
- return "-"
+ return "-\(formatted)"
case .received, .receiving:
- return "+"
+ return "+\(formatted)"
case .failed:
- return ""
+ return isSentTransaction ? "-\(formatted)" : "+\(formatted)"
+ }
+ }
+
+ public var expandedAmountString: (primary: String, secondary: String) {
+ let formatted = zecAmount.decimalZashiFullFormatted()
+
+ let smallPart = String(formatted.suffix(5))
+ let normal = formatted.dropLast(5)
+
+ switch status {
+ case .paid, .sending:
+ return (primary: "-\(normal)", secondary: smallPart)
+ case .received, .receiving:
+ return (primary: "+\(normal)", secondary: smallPart)
+ case .failed:
+ return isSentTransaction
+ ? (primary: "-\(normal)", secondary: smallPart)
+ : (primary: "+\(normal)", secondary: smallPart)
}
}
public var dateString: String? {
- guard let minedHeight else { return "" }
+ guard minedHeight != nil else { return "" }
guard let timestamp else { return nil }
return Date(timeIntervalSince1970: timestamp).asHumanReadable()
}
+ // Helper flags
+ public var isPending: Bool {
+ switch status {
+ case .failed:
+ return false
+ case .paid:
+ return false
+ case .received:
+ return false
+ case .receiving:
+ return true
+ case .sending:
+ return true
+ }
+ }
+
+ /// The purpose of this flag is to help understand if the transaction affected the wallet and a user paid a fee
+ public var isSpending: Bool {
+ switch status {
+ case .paid, .sending:
+ return true
+ case .received, .receiving:
+ return false
+ case .failed:
+ return isSentTransaction
+ }
+ }
+
+ // Values
public var totalAmount: Zatoshi {
Zatoshi(zecAmount.amount + fee.amount)
}
- public var viewOnlineURL: URL? {
- URL(string: "https://zcashblockexplorer.com/transactions/\(id)")
- }
-
public var textMemo: Memo? {
guard let memos else { return nil }
for memo in memos {
if case .text = memo {
+ guard let memoText = memo.toString(), !memoText.isEmpty else {
+ return nil
+ }
+
return memo
}
}
@@ -85,7 +175,11 @@ public struct TransactionState: Equatable, Identifiable {
id: String,
status: Status,
timestamp: TimeInterval? = nil,
- zecAmount: Zatoshi
+ zecAmount: Zatoshi,
+ isSentTransaction: Bool = false,
+ isAddressExpanded: Bool = false,
+ isExpanded: Bool = false,
+ isIdExpanded: Bool = false
) {
self.errorMessage = errorMessage
self.expiryHeight = expiryHeight
@@ -98,6 +192,10 @@ public struct TransactionState: Equatable, Identifiable {
self.status = status
self.timestamp = timestamp
self.zecAmount = zecAmount
+ self.isSentTransaction = isSentTransaction
+ self.isAddressExpanded = isAddressExpanded
+ self.isExpanded = isExpanded
+ self.isIdExpanded = isIdExpanded
}
public func confirmationsWith(_ latestMinedHeight: BlockHeight?) -> BlockHeight {
@@ -117,23 +215,30 @@ extension TransactionState {
id = transaction.rawID.toHexStringTxId()
timestamp = transaction.blockTime
zecAmount = transaction.isSentTransaction ? Zatoshi(-transaction.value.amount) : transaction.value
+ isSentTransaction = transaction.isSentTransaction
+ isAddressExpanded = false
+ isExpanded = false
+ isIdExpanded = false
self.memos = memos
- let isSent = transaction.isSentTransaction
-
- // TODO: [#1313] SDK improvements so a client doesn't need to determing if the transaction isPending
- // https://github.com/zcash/ZcashLightClientKit/issues/1313
- // The only reason why `latestBlockHeight` is provided here is to determine pending
- // state of the transaction. SDK knows the latestBlockHeight so ideally ZcashTransaction.Overview
- // already knows and provides isPending as a bool value.
- // Once SDK's #1313 is done, adopt the SDK and remove latestBlockHeight here.
- let isPending = transaction.isPending(currentHeight: latestBlockHeight)
-
- switch (isSent, isPending) {
- case (true, true): status = .sending
- case (true, false): status = .paid(success: minedHeight ?? 0 > 0)
- case (false, true): status = .receiving
- case (false, false): status = .received
+ // failed check
+ if let expiryHeight = transaction.expiryHeight, expiryHeight <= latestBlockHeight && minedHeight == nil {
+ status = .failed
+ } else {
+ // TODO: [#1313] SDK improvements so a client doesn't need to determing if the transaction isPending
+ // https://github.com/zcash/ZcashLightClientKit/issues/1313
+ // The only reason why `latestBlockHeight` is provided here is to determine pending
+ // state of the transaction. SDK knows the latestBlockHeight so ideally ZcashTransaction.Overview
+ // already knows and provides isPending as a bool value.
+ // Once SDK's #1313 is done, adopt the SDK and remove latestBlockHeight here.
+ let isPending = transaction.isPending(currentHeight: latestBlockHeight)
+
+ switch (isSentTransaction, isPending) {
+ case (true, true): status = .sending
+ case (true, false): status = .paid
+ case (false, true): status = .receiving
+ case (false, false): status = .received
+ }
}
}
}
@@ -162,6 +267,91 @@ extension TransactionState {
zecAmount: status == .received ? amount : Zatoshi(-amount.amount)
)
}
+
+ public static let mockedSent = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "utest1vergg5jkp4xy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzjanqtl8uqp5vln3zyy246ejtx86vqftp73j7jg9099jxafyjhfm6u956j3",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .paid,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ public static let mockedReceived = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4xy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .received,
+ timestamp: 1699292621,
+ zecAmount: Zatoshi(25_000_000),
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ public static let mockedFailed = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: nil,
+ zAddress: "utest1vergg5jkp4xy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzjanqtl8uqp5vln3zyy246ejtx86vqftp73j7jg9099jxafyjhfm6u956j3",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .failed,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isSentTransaction: true,
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ public static let mockedFailedReceive = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: nil,
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .failed,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isSentTransaction: false,
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ public static let mockedSending = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: nil,
+ zAddress: "utest1vergg5jkp4xy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzjanqtl8uqp5vln3zyy246ejtx86vqftp73j7jg9099jxafyjhfm6u956j3",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .sending,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isSentTransaction: true,
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ public static let mockedReceiving = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: nil,
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .receiving,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isSentTransaction: false,
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
}
public struct TransactionStateMockHelper {
diff --git a/modules/Sources/Models/WalletEvent.swift b/modules/Sources/Models/WalletEvent.swift
deleted file mode 100644
index f011b63b..00000000
--- a/modules/Sources/Models/WalletEvent.swift
+++ /dev/null
@@ -1,32 +0,0 @@
-//
-// WalletEvent.swift
-// secant-testnet
-//
-// Created by Lukáš Korba on 20.06.2022.
-//
-
-import Foundation
-import ComposableArchitecture
-import SwiftUI
-import ZcashLightClientKit
-import Utils
-
-// MARK: - Model
-
-public struct WalletEvent: Equatable, Identifiable, Redactable {
- public enum WalletEventState: Equatable {
- case transaction(TransactionState)
- case shielded(Zatoshi)
- case walletImport(BlockHeight)
- }
-
- public let id: String
- public let state: WalletEventState
- public var timestamp: TimeInterval?
-
- public init(id: String, state: WalletEventState, timestamp: TimeInterval? = nil) {
- self.id = id
- self.state = state
- self.timestamp = timestamp
- }
-}
diff --git a/modules/Sources/UIComponents/Balance/BalanceTitle.swift b/modules/Sources/UIComponents/Balance/BalanceTitle.swift
index e6aa582c..1298c9df 100644
--- a/modules/Sources/UIComponents/Balance/BalanceTitle.swift
+++ b/modules/Sources/UIComponents/Balance/BalanceTitle.swift
@@ -25,7 +25,7 @@ public struct BalanceTitle: View {
Circle()
.frame(width: 25, height: 25)
- .foregroundColor(Asset.Colors.tabsUnderline.color)
+ .foregroundColor(Asset.Colors.primaryTint.color)
.overlay {
ZcashSymbol()
.frame(width: 15, height: 15)
diff --git a/modules/Sources/UIComponents/Balance/FullBalanceTitle.swift b/modules/Sources/UIComponents/Balance/FullBalanceTitle.swift
new file mode 100644
index 00000000..00c677b0
--- /dev/null
+++ b/modules/Sources/UIComponents/Balance/FullBalanceTitle.swift
@@ -0,0 +1,53 @@
+//
+// FullBalanceTitle.swift
+//
+//
+// Created by Lukáš Korba on 03.11.2023.
+//
+
+import SwiftUI
+import Generated
+import ZcashLightClientKit
+
+public struct FullBalanceTitle: View {
+ let primary: String
+ let secondary: String
+ let fontName: String
+ let primaryFontSize: CGFloat
+ let secondaryFontSize: CGFloat
+
+ public init(
+ primary: String,
+ secondary: String,
+ fontName: String,
+ primaryFontSize: CGFloat,
+ secondaryFontSize: CGFloat
+ ) {
+ self.primary = primary
+ self.secondary = secondary
+ self.fontName = fontName
+ self.primaryFontSize = primaryFontSize
+ self.secondaryFontSize = secondaryFontSize
+ }
+
+ public var body: some View {
+ HStack {
+ Text(primary)
+ .font(.custom(fontName, size: primaryFontSize))
+ + Text(secondary)
+ .font(.custom(fontName, size: secondaryFontSize))
+ }
+ }
+}
+
+#Preview {
+ VStack {
+ FullBalanceTitle(
+ primary: "0.001",
+ secondary: "35466",
+ fontName: FontFamily.Inter.regular.name,
+ primaryFontSize: 13,
+ secondaryFontSize: 9
+ )
+ }
+}
diff --git a/modules/Sources/UIComponents/Buttons/ZashiButton.swift b/modules/Sources/UIComponents/Buttons/ZashiButton.swift
index 81b67e4f..ea34f296 100644
--- a/modules/Sources/UIComponents/Buttons/ZashiButton.swift
+++ b/modules/Sources/UIComponents/Buttons/ZashiButton.swift
@@ -24,16 +24,16 @@ public struct ZcashButtonStyle: ButtonStyle {
.frame(height: 60)
.foregroundColor(
appearance == .primary ? .clear
- : isEnabled ? Asset.Colors.primary.color : Asset.Colors.suppressed72.color
+ : isEnabled ? Asset.Colors.primary.color : Asset.Colors.shade72.color
)
- .border(isEnabled ? Asset.Colors.primary.color : Asset.Colors.suppressed72.color)
+ .border(isEnabled ? Asset.Colors.primary.color : Asset.Colors.shade72.color)
.offset(CGSize(width: 10, height: 10))
Rectangle()
.frame(height: 60)
.foregroundColor(
appearance == .primary ?
- isEnabled ? Asset.Colors.primary.color : Asset.Colors.suppressed72.color
+ isEnabled ? Asset.Colors.primary.color : Asset.Colors.shade72.color
: Asset.Colors.secondary.color
)
.border(Asset.Colors.primary.color)
@@ -42,7 +42,7 @@ public struct ZcashButtonStyle: ButtonStyle {
.font(.custom(FontFamily.Inter.medium.name, size: 14))
.foregroundColor(
appearance == .primary ? Asset.Colors.secondary.color
- : isEnabled ? Asset.Colors.primary.color : Asset.Colors.suppressed72.color
+ : isEnabled ? Asset.Colors.primary.color : Asset.Colors.shade72.color
)
})
.offset(CGSize(width: offset, height: offset))
diff --git a/modules/Sources/UIComponents/Shapes/MessageShape.swift b/modules/Sources/UIComponents/Shapes/MessageShape.swift
index 10248414..43f85867 100644
--- a/modules/Sources/UIComponents/Shapes/MessageShape.swift
+++ b/modules/Sources/UIComponents/Shapes/MessageShape.swift
@@ -8,15 +8,28 @@
import SwiftUI
import Generated
-struct MessageShape: Shape {
- func path(in rect: CGRect) -> Path {
+public struct MessageShape: Shape {
+ public enum Orientation: Sendable {
+ case left
+ case right
+ }
+
+ let orientation: MessageShape.Orientation
+
+ public func path(in rect: CGRect) -> Path {
Path { path in
path.move(to: CGPoint(x: 0, y: 0))
path.addLine(to: CGPoint(x: 0, y: rect.height))
- path.addLine(to: CGPoint(x: 15, y: rect.height))
- path.addLine(to: CGPoint(x: 15, y: rect.height + 7))
- path.addLine(to: CGPoint(x: 35, y: rect.height))
+ if orientation == .left {
+ path.addLine(to: CGPoint(x: 15, y: rect.height))
+ path.addLine(to: CGPoint(x: 15, y: rect.height + 7))
+ path.addLine(to: CGPoint(x: 35, y: rect.height))
+ } else {
+ path.addLine(to: CGPoint(x: rect.width - 35, y: rect.height))
+ path.addLine(to: CGPoint(x: rect.width - 15, y: rect.height + 7))
+ path.addLine(to: CGPoint(x: rect.width - 15, y: rect.height))
+ }
path.addLine(to: CGPoint(x: rect.width, y: rect.height))
path.addLine(to: CGPoint(x: rect.width, y: 0))
@@ -25,33 +38,56 @@ struct MessageShape: Shape {
}
}
-struct MessageShapeModifier: ViewModifier {
- let filled: Bool
+public struct MessageShapeModifier: ViewModifier {
+ let filled: Color?
+ let orientation: MessageShape.Orientation
- func body(content: Content) -> some View {
+ public func body(content: Content) -> some View {
content
- .overlay {
- if filled {
- MessageShape()
- .foregroundColor(Asset.Colors.suppressed72.color)
- MessageShape()
- .stroke()
- } else {
- MessageShape()
- .stroke()
+ .background {
+ if let filled {
+ MessageShape(orientation: orientation)
+ .foregroundColor(filled)
}
}
+ .overlay {
+ MessageShape(orientation: orientation)
+ .stroke()
+ }
}
}
extension View {
- public func messageShape(filled: Bool = false) -> some View {
- modifier(MessageShapeModifier(filled: filled))
+ public func messageShape(
+ filled: Color? = nil,
+ orientation: MessageShape.Orientation = .left
+ ) -> some View {
+ modifier(
+ MessageShapeModifier(
+ filled: filled,
+ orientation: orientation
+ )
+ )
}
}
#Preview {
- Text("some message")
- .frame(width: 320, height: 145)
- .messageShape(filled: true)
+ VStack {
+ Text("some message")
+ .padding(5)
+ .messageShape(
+ filled: Asset.Colors.messageBcgReceived.color,
+ orientation: .right
+ )
+ .padding(.bottom, 20)
+
+ Text("some message")
+ .padding(5)
+ .messageShape()
+ .padding(.bottom, 20)
+
+ Text("some message")
+ .frame(width: 320, height: 145)
+ .messageShape(filled: Asset.Colors.shade72.color)
+ }
}
diff --git a/modules/Sources/UIComponents/Text/ConditionalFont.swift b/modules/Sources/UIComponents/Text/ConditionalFont.swift
new file mode 100644
index 00000000..ebb9c779
--- /dev/null
+++ b/modules/Sources/UIComponents/Text/ConditionalFont.swift
@@ -0,0 +1,20 @@
+//
+// ConditionalFont.swift
+//
+//
+// Created by Lukáš Korba on 08.11.2023.
+//
+
+import SwiftUI
+
+extension Text {
+ public func conditionalFont(condition: Bool, true: Font, else: Font) -> Text {
+ if condition {
+ return self
+ .font(`true`)
+ } else {
+ return self
+ .font(`else`)
+ }
+ }
+}
diff --git a/modules/Sources/UIComponents/Text/ConditionalStrikethrough.swift b/modules/Sources/UIComponents/Text/ConditionalStrikethrough.swift
new file mode 100644
index 00000000..f512fcf9
--- /dev/null
+++ b/modules/Sources/UIComponents/Text/ConditionalStrikethrough.swift
@@ -0,0 +1,19 @@
+//
+// ConditionalStrikethrough.swift
+//
+//
+// Created by Lukáš Korba on 08.11.2023.
+//
+
+import SwiftUI
+
+extension Text {
+ public func conditionalStrikethrough(_ on: Bool) -> Text {
+ if on {
+ return self
+ .strikethrough()
+ } else {
+ return self
+ }
+ }
+}
diff --git a/modules/Sources/UIComponents/TextFields/MessageEditor/MessageEditor.swift b/modules/Sources/UIComponents/TextFields/MessageEditor/MessageEditor.swift
index 24db4e83..83d3442c 100644
--- a/modules/Sources/UIComponents/TextFields/MessageEditor/MessageEditor.swift
+++ b/modules/Sources/UIComponents/TextFields/MessageEditor/MessageEditor.swift
@@ -42,14 +42,18 @@ public struct MessageEditor: View {
.focused($isFocused)
.padding(2)
.font(.custom(FontFamily.Inter.regular.name, size: 14))
- .messageShape(filled: !isEnabled)
+ .messageShape(
+ filled: isEnabled
+ ? nil
+ : Asset.Colors.shade72.color
+ )
.overlay {
if message.isEmpty || !isEnabled {
HStack {
VStack {
Text(L10n.Send.memoPlaceholder)
.font(.custom(FontFamily.Inter.regular.name, size: 13))
- .foregroundColor(Asset.Colors.suppressed72.color)
+ .foregroundColor(Asset.Colors.shade72.color)
.onTapGesture {
isFocused = true
}
@@ -80,7 +84,7 @@ public struct MessageEditor: View {
.font(.custom(FontFamily.Inter.bold.name, size: 13))
.foregroundColor(
viewStore.isValid
- ? Asset.Colors.suppressed72.color
+ ? Asset.Colors.shade72.color
: Asset.Colors.error.color
)
}
diff --git a/modules/Sources/Utils/BalanceFormatter.swift b/modules/Sources/Utils/BalanceFormatter.swift
index a63f89bf..55ce0e88 100644
--- a/modules/Sources/Utils/BalanceFormatter.swift
+++ b/modules/Sources/Utils/BalanceFormatter.swift
@@ -12,4 +12,8 @@ extension Zatoshi {
public func decimalZashiFormatted() -> String {
NumberFormatter.zashiBalanceFormatter.string(from: decimalValue.roundedZec) ?? ""
}
+
+ public func decimalZashiFullFormatted() -> String {
+ NumberFormatter.zcashNumberFormatter8FractionDigits.string(from: decimalValue.roundedZec) ?? ""
+ }
}
diff --git a/modules/Sources/Utils/Date+Readable.swift b/modules/Sources/Utils/Date+Readable.swift
index f068e6a7..289f06b5 100644
--- a/modules/Sources/Utils/Date+Readable.swift
+++ b/modules/Sources/Utils/Date+Readable.swift
@@ -19,6 +19,9 @@ extension Date {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .short
+ formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"//"d MMM h:mm a"
+ formatter.amSymbol = "am"
+ formatter.pmSymbol = "pm"
return formatter
}()
diff --git a/secant.xcodeproj/project.pbxproj b/secant.xcodeproj/project.pbxproj
index c1eeebe0..06ac9999 100644
--- a/secant.xcodeproj/project.pbxproj
+++ b/secant.xcodeproj/project.pbxproj
@@ -21,6 +21,7 @@
34DA414928E439CD00F8CC61 /* sendingTransaction.json in Resources */ = {isa = PBXBuildFile; fileRef = 34DA414828E439CD00F8CC61 /* sendingTransaction.json */; };
9E00319C2A272BB6003DFCEB /* SDKSynchronizer in Frameworks */ = {isa = PBXBuildFile; productRef = 9E00319B2A272BB6003DFCEB /* SDKSynchronizer */; };
9E0031A42A272BC7003DFCEB /* SDKSynchronizer in Frameworks */ = {isa = PBXBuildFile; productRef = 9E0031A32A272BC7003DFCEB /* SDKSynchronizer */; };
+ 9E0C0D702AFB842B00D69A16 /* TransactionStateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E0C0D6F2AFB842B00D69A16 /* TransactionStateTests.swift */; };
9E1FAFB72AF2C6D40084CA3D /* PrivateDataConsentSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1FAFB62AF2C6D40084CA3D /* PrivateDataConsentSnapshotTests.swift */; };
9E1FAFBA2AF2C7F20084CA3D /* PrivateDataConsentTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E1FAFB92AF2C7F20084CA3D /* PrivateDataConsentTests.swift */; };
9E2F1C8C280ED6A7004E65FE /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 9E2F1C8B280ED6A7004E65FE /* LaunchScreen.storyboard */; };
@@ -52,7 +53,7 @@
9E3451B529C8565500177D16 /* LoggerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E0F574A2980260D005304FA /* LoggerTests.swift */; };
9E3451B629C8565500177D16 /* ZatoshiTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E39112D283F91600073DD9A /* ZatoshiTests.swift */; };
9E3451B729C8565500177D16 /* DatabaseFilesTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E02B56B27FED475005B809B /* DatabaseFilesTests.swift */; };
- 9E3451B829C856E700177D16 /* WalletEventsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF63E2819542C00BA3F17 /* WalletEventsTests.swift */; };
+ 9E3451B829C856E700177D16 /* TransactionListTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E5BF63E2819542C00BA3F17 /* TransactionListTests.swift */; };
9E3451B929C857C000177D16 /* AddressDetailsSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E207C352966EC77003E2C9B /* AddressDetailsSnapshotTests.swift */; };
9E3451BA29C857C500177D16 /* BalanceBreakdownSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E94C62228AA7EE0008256E9 /* BalanceBreakdownSnapshotTests.swift */; };
9E3451BB29C857C800177D16 /* HomeSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9ECC8C28589E150099D5A2 /* HomeSnapshotTests.swift */; };
@@ -62,7 +63,7 @@
9E3451C229C857DB00177D16 /* TransactionSendingSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 34429C6D28E703CD00F2B929 /* TransactionSendingSnapshotTests.swift */; };
9E3451C329C857DD00177D16 /* SettingsSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7225F02889539300DF7F17 /* SettingsSnapshotTests.swift */; };
9E3451C429C857DF00177D16 /* View+UIImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E92AF0728530EBF007367AD /* View+UIImage.swift */; };
- 9E3451C529C857E400177D16 /* WalletEventsSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7CB6112869882D00A02233 /* WalletEventsSnapshotTests.swift */; };
+ 9E3451C529C857E400177D16 /* TransactionListSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E7CB6112869882D00A02233 /* TransactionListSnapshotTests.swift */; };
9E3451C629C857E700177D16 /* WelcomeSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9ECC8E28589E150099D5A2 /* WelcomeSnapshotTests.swift */; };
9E46919A2AD5735E0082D7DF /* TabsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4691992AD5735E0082D7DF /* TabsTests.swift */; };
9E4938D82ACE8E8F003C4C1D /* SecurityWarningSnapshotTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E4938D72ACE8E8F003C4C1D /* SecurityWarningSnapshotTests.swift */; };
@@ -120,6 +121,7 @@
34F039B229ABCE8500CF0053 /* WalletConfigProviderTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WalletConfigProviderTests.swift; sourceTree = ""; };
9E01F8272833CDA0000EFC57 /* ScanTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanTests.swift; sourceTree = ""; };
9E02B56B27FED475005B809B /* DatabaseFilesTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseFilesTests.swift; sourceTree = ""; };
+ 9E0C0D6F2AFB842B00D69A16 /* TransactionStateTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionStateTests.swift; sourceTree = ""; };
9E0F574A2980260D005304FA /* LoggerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggerTests.swift; sourceTree = ""; };
9E1FAFB62AF2C6D40084CA3D /* PrivateDataConsentSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivateDataConsentSnapshotTests.swift; sourceTree = ""; };
9E1FAFB92AF2C7F20084CA3D /* PrivateDataConsentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivateDataConsentTests.swift; sourceTree = ""; };
@@ -133,7 +135,7 @@
9E4691992AD5735E0082D7DF /* TabsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabsTests.swift; sourceTree = ""; };
9E4938D72ACE8E8F003C4C1D /* SecurityWarningSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecurityWarningSnapshotTests.swift; sourceTree = ""; };
9E5AAEBF2A67CEC4003F283D /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; };
- 9E5BF63E2819542C00BA3F17 /* WalletEventsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletEventsTests.swift; sourceTree = ""; };
+ 9E5BF63E2819542C00BA3F17 /* TransactionListTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionListTests.swift; sourceTree = ""; };
9E5BF643281FEC9900BA3F17 /* SendTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendTests.swift; sourceTree = ""; };
9E612C7829913F3600D09B09 /* SensitiveDataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SensitiveDataTests.swift; sourceTree = ""; };
9E6612352878345000C75B70 /* endlessCircleProgress.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = endlessCircleProgress.json; sourceTree = ""; };
@@ -141,7 +143,7 @@
9E6713F02897F81B00A6796F /* MultiLineTextFieldTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiLineTextFieldTests.swift; sourceTree = ""; };
9E7225F02889539300DF7F17 /* SettingsSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsSnapshotTests.swift; sourceTree = ""; };
9E74CCCF29DC0628003D6E32 /* ReviewRequestTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReviewRequestTests.swift; sourceTree = ""; };
- 9E7CB6112869882D00A02233 /* WalletEventsSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WalletEventsSnapshotTests.swift; sourceTree = ""; };
+ 9E7CB6112869882D00A02233 /* TransactionListSnapshotTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TransactionListSnapshotTests.swift; sourceTree = ""; };
9E852D6429B0A86300CF4AC1 /* DebugTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DebugTests.swift; sourceTree = ""; };
9E90751D2A269BE300269308 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
9E92AF0728530EBF007367AD /* View+UIImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "View+UIImage.swift"; sourceTree = ""; };
@@ -270,7 +272,7 @@
9E4691982AD573420082D7DF /* TabsTests */,
9EF8135927ECC25E0075AF48 /* UtilTests */,
34F039B129ABCE8500CF0053 /* WalletConfigProviderTests */,
- 9E5BF63D281953F900BA3F17 /* WalletEventsTests */,
+ 9E5BF63D281953F900BA3F17 /* TransactionListTests */,
);
path = secantTests;
sourceTree = "";
@@ -386,7 +388,7 @@
346715A628E20FB30035F7C4 /* SendSnapshotTests */,
9E7225EF2889537E00DF7F17 /* SettingsSnapshotTests */,
9E92AF0728530EBF007367AD /* View+UIImage.swift */,
- 9E7CB6102869881300A02233 /* WalletEventsSnapshotTests */,
+ 9E7CB6102869881300A02233 /* TransactionListSnapshotTests */,
9E9ECC8D28589E150099D5A2 /* WelcomeSnapshotTests */,
);
path = SnapshotTests;
@@ -408,12 +410,13 @@
path = SecurityWarningSnapshotTests;
sourceTree = "";
};
- 9E5BF63D281953F900BA3F17 /* WalletEventsTests */ = {
+ 9E5BF63D281953F900BA3F17 /* TransactionListTests */ = {
isa = PBXGroup;
children = (
- 9E5BF63E2819542C00BA3F17 /* WalletEventsTests.swift */,
+ 9E5BF63E2819542C00BA3F17 /* TransactionListTests.swift */,
+ 9E0C0D6F2AFB842B00D69A16 /* TransactionStateTests.swift */,
);
- path = WalletEventsTests;
+ path = TransactionListTests;
sourceTree = "";
};
9E5BF642281FEC8700BA3F17 /* SendTests */ = {
@@ -476,12 +479,12 @@
path = ReviewRequestTests;
sourceTree = "";
};
- 9E7CB6102869881300A02233 /* WalletEventsSnapshotTests */ = {
+ 9E7CB6102869881300A02233 /* TransactionListSnapshotTests */ = {
isa = PBXGroup;
children = (
- 9E7CB6112869882D00A02233 /* WalletEventsSnapshotTests.swift */,
+ 9E7CB6112869882D00A02233 /* TransactionListSnapshotTests.swift */,
);
- path = WalletEventsSnapshotTests;
+ path = TransactionListSnapshotTests;
sourceTree = "";
};
9E7FE0B6282D1D9800C374E8 /* Resources */ = {
@@ -908,7 +911,7 @@
9E34519E29C849B400177D16 /* WalletConfigProviderTests.swift in Sources */,
9E3451C029C857D400177D16 /* RecoveryPhraseDisplaySnapshotTests.swift in Sources */,
9E3451B629C8565500177D16 /* ZatoshiTests.swift in Sources */,
- 9E3451B829C856E700177D16 /* WalletEventsTests.swift in Sources */,
+ 9E3451B829C856E700177D16 /* TransactionListTests.swift in Sources */,
9E34519529C4A4BF00177D16 /* AddressDetailsTests.swift in Sources */,
9E3451B929C857C000177D16 /* AddressDetailsSnapshotTests.swift in Sources */,
9E34519B29C4A90700177D16 /* DeeplinkTests.swift in Sources */,
@@ -917,12 +920,13 @@
9E4938D82ACE8E8F003C4C1D /* SecurityWarningSnapshotTests.swift in Sources */,
9E3451C429C857DF00177D16 /* View+UIImage.swift in Sources */,
9E34519729C4A51100177D16 /* RecoveryPhraseBackupTests.swift in Sources */,
+ 9E0C0D702AFB842B00D69A16 /* TransactionStateTests.swift in Sources */,
9E3451BC29C857C800177D16 /* NotEnoughFeeSpaceSnapshots.swift in Sources */,
9E3451B229C8565500177D16 /* SecItemClientTests.swift in Sources */,
9E3451C629C857E700177D16 /* WelcomeSnapshotTests.swift in Sources */,
9E3451B329C8565500177D16 /* WalletBalance+testing.swift in Sources */,
9E3451AA29C84ED500177D16 /* CurrencySelectionTests.swift in Sources */,
- 9E3451C529C857E400177D16 /* WalletEventsSnapshotTests.swift in Sources */,
+ 9E3451C529C857E400177D16 /* TransactionListSnapshotTests.swift in Sources */,
9E34519829C4A51100177D16 /* RecoveryPhraseDisplayReducerTests.swift in Sources */,
9E34519C29C4A91A00177D16 /* HomeTests.swift in Sources */,
9E1FAFB72AF2C6D40084CA3D /* PrivateDataConsentSnapshotTests.swift in Sources */,
diff --git a/secant.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/secant.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
index 557f71ea..88d93163 100644
--- a/secant.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/secant.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -50,8 +50,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/google/GoogleUtilities.git",
"state" : {
- "revision" : "1cd556b33550982ec17f80e358253d905e756f0f",
- "version" : "7.11.6"
+ "revision" : "f6532c8d65f8308cfdf2288cbe1971a509822680",
+ "version" : "7.12.0"
}
},
{
@@ -95,8 +95,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/firebase/leveldb.git",
"state" : {
- "revision" : "0706abcc6b0bd9cedfbb015ba840e4a780b5159b",
- "version" : "1.22.2"
+ "revision" : "9d108e9112aa1d65ce508facf804674546116d9c",
+ "version" : "1.22.3"
}
},
{
diff --git a/secantTests/HomeTests/HomeTests.swift b/secantTests/HomeTests/HomeTests.swift
index 608a239b..f1e16da3 100644
--- a/secantTests/HomeTests/HomeTests.swift
+++ b/secantTests/HomeTests/HomeTests.swift
@@ -27,7 +27,7 @@ class HomeTests: XCTestCase {
shieldedBalance: Balance.zero,
synchronizerStatusSnapshot: mockSnapshot,
walletConfig: .default,
- walletEventsState: .emptyPlaceHolder
+ transactionListState: .emptyPlaceHolder
),
reducer: HomeReducer(networkType: .testnet)
)
diff --git a/secantTests/SnapshotTests/HomeSnapshotTests/HomeSnapshotTests.swift b/secantTests/SnapshotTests/HomeSnapshotTests/HomeSnapshotTests.swift
index db6901b6..151f5947 100644
--- a/secantTests/SnapshotTests/HomeSnapshotTests/HomeSnapshotTests.swift
+++ b/secantTests/SnapshotTests/HomeSnapshotTests/HomeSnapshotTests.swift
@@ -15,13 +15,13 @@ import Home
class HomeSnapshotTests: XCTestCase {
func testHomeSnapshot() throws {
let transactionsHelper: [TransactionStateMockHelper] = [
- TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid(success: true), uuid: "1"),
+ TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid, uuid: "1"),
TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), status: .sending, uuid: "2"),
TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .received, uuid: "3"),
TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), status: .failed, uuid: "4")
]
- let walletEvents: [WalletEvent] = transactionsHelper.map {
+ let transactionList: [TransactionState] = transactionsHelper.map {
var transaction = TransactionState.placeholder(
amount: $0.amount,
fee: Zatoshi(10),
@@ -32,7 +32,7 @@ class HomeSnapshotTests: XCTestCase {
)
transaction.zAddress = "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po"
- return WalletEvent(id: transaction.id, state: .transaction(transaction), timestamp: transaction.timestamp)
+ return transaction
}
let balance = WalletBalance(verified: 12_345_000, total: 12_345_000)
@@ -43,7 +43,7 @@ class HomeSnapshotTests: XCTestCase {
shieldedBalance: balance.redacted,
synchronizerStatusSnapshot: .default,
walletConfig: .default,
- walletEventsState: .init(walletEvents: IdentifiedArrayOf(uniqueElements: walletEvents))
+ transactionListState: .init(transactionList: IdentifiedArrayOf(uniqueElements: transactionList))
),
reducer: HomeReducer(networkType: .testnet)
.dependency(\.diskSpaceChecker, .mockEmptyDisk)
diff --git a/secantTests/SnapshotTests/TransactionListSnapshotTests/TransactionListSnapshotTests.swift b/secantTests/SnapshotTests/TransactionListSnapshotTests/TransactionListSnapshotTests.swift
new file mode 100644
index 00000000..919c7178
--- /dev/null
+++ b/secantTests/SnapshotTests/TransactionListSnapshotTests/TransactionListSnapshotTests.swift
@@ -0,0 +1,31 @@
+//
+// TransactionListSnapshotTests.swift
+// secantTests
+//
+// Created by Lukáš Korba on 27.06.2022.
+//
+
+import XCTest
+import ComposableArchitecture
+import ZcashLightClientKit
+import Models
+import TransactionList
+import Home
+@testable import secant_testnet
+
+class TransactionListSnapshotTests: XCTestCase {
+ func testFullTransactionListSnapshot() throws {
+ let store = TransactionListStore(
+ initialState: .placeHolder,
+ reducer: TransactionListReducer()
+ .dependency(\.sdkSynchronizer, .mock)
+ .dependency(\.mainQueue, .immediate)
+ )
+
+ // landing wallet events screen
+ addAttachments(
+ name: "\(#function)_initial",
+ TransactionListView(store: store, tokenName: "ZEC")
+ )
+ }
+}
diff --git a/secantTests/SnapshotTests/WalletEventsSnapshotTests/WalletEventsSnapshotTests.swift b/secantTests/SnapshotTests/WalletEventsSnapshotTests/WalletEventsSnapshotTests.swift
deleted file mode 100644
index 9c8762f0..00000000
--- a/secantTests/SnapshotTests/WalletEventsSnapshotTests/WalletEventsSnapshotTests.swift
+++ /dev/null
@@ -1,233 +0,0 @@
-//
-// WalletEventsSnapshotTests.swift
-// secantTests
-//
-// Created by Lukáš Korba on 27.06.2022.
-//
-
-import XCTest
-import ComposableArchitecture
-import ZcashLightClientKit
-import Models
-import WalletEventsFlow
-import Home
-@testable import secant_testnet
-
-class WalletEventsSnapshotTests: XCTestCase {
- func testFullWalletEventsSnapshot() throws {
- let store = WalletEventsFlowStore(
- initialState: .placeHolder,
- reducer: WalletEventsFlowReducer()
- .dependency(\.sdkSynchronizer, .mock)
- .dependency(\.mainQueue, .immediate)
- )
-
- // landing wallet events screen
- addAttachments(
- name: "\(#function)_initial",
- WalletEventsFlowView(store: store, tokenName: "ZEC")
- )
- }
-
- 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(
- memos: [memo],
- minedHeight: 1_875_256,
- zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
- fee: Zatoshi(1_000_000),
- id: "ff3927e1f83df9b1b0dc75540ddc59ee435eecebae914d2e6dfe8576fbedc9a8",
- status: .paid(success: true),
- timestamp: 1234567,
- zecAmount: Zatoshi(25_000_000)
- )
-
- let walletEvent = WalletEvent(id: transaction.id, state: .transaction(transaction), timestamp: transaction.timestamp)
-
- let balance = WalletBalance(verified: 12_345_000, total: 12_345_000)
- let store = HomeStore(
- initialState: .init(
- scanState: .placeholder,
- shieldedBalance: balance.redacted,
- synchronizerStatusSnapshot: .default,
- walletConfig: .default,
- walletEventsState: .init(walletEvents: IdentifiedArrayOf(uniqueElements: [walletEvent]))
- ),
- reducer: HomeReducer(networkType: .testnet)
- )
-
- ViewStore(store).send(.walletEvents(.updateDestination(.showWalletEvent(walletEvent))))
- let walletEventsStore = WalletEventsFlowStore(
- initialState: .placeHolder,
- reducer: WalletEventsFlowReducer()
- )
-
- addAttachments(
- name: "\(#function)_WalletEventDetail",
- TransactionDetailView(store: walletEventsStore, transaction: transaction, tokenName: "ZEC")
- )
- }
-
- 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(
- memos: [memo],
- minedHeight: 1_875_256,
- zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
- fee: Zatoshi(1_000_000),
- id: "ff3927e1f83df9b1b0dc75540ddc59ee435eecebae914d2e6dfe8576fbedc9a8",
- status: .received,
- timestamp: 1234567,
- zecAmount: Zatoshi(25_000_000)
- )
-
- let walletEvent = WalletEvent(id: transaction.id, state: .transaction(transaction), timestamp: transaction.timestamp)
-
- let balance = WalletBalance(verified: 12_345_000, total: 12_345_000)
- let store = HomeStore(
- initialState: .init(
- scanState: .placeholder,
- shieldedBalance: balance.redacted,
- synchronizerStatusSnapshot: .default,
- walletConfig: .default,
- walletEventsState: .init(walletEvents: IdentifiedArrayOf(uniqueElements: [walletEvent]))
- ),
- reducer: HomeReducer(networkType: .testnet)
- )
-
- ViewStore(store).send(.walletEvents(.updateDestination(.showWalletEvent(walletEvent))))
- let walletEventsStore = WalletEventsFlowStore(
- initialState: .placeHolder,
- reducer: WalletEventsFlowReducer()
- )
-
- addAttachments(
- name: "\(#function)_WalletEventDetail",
- TransactionDetailView(store: walletEventsStore, transaction: transaction, tokenName: "ZEC")
- )
- }
-
- 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(
- memos: [memo],
- minedHeight: 1_875_256,
- zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
- fee: Zatoshi(1_000_000),
- id: "ff3927e1f83df9b1b0dc75540ddc59ee435eecebae914d2e6dfe8576fbedc9a8",
- status: .sending,
- timestamp: 1234567,
- zecAmount: Zatoshi(25_000_000)
- )
-
- let walletEvent = WalletEvent(id: transaction.id, state: .transaction(transaction), timestamp: transaction.timestamp)
-
- let balance = WalletBalance(verified: 12_345_000, total: 12_345_000)
- let store = HomeStore(
- initialState: .init(
- scanState: .placeholder,
- shieldedBalance: balance.redacted,
- synchronizerStatusSnapshot: .default,
- walletConfig: .default,
- walletEventsState: .init(walletEvents: IdentifiedArrayOf(uniqueElements: [walletEvent]))
- ),
- reducer: HomeReducer(networkType: .testnet)
- )
-
- let walletEventsState = WalletEventsFlowReducer.State(
- requiredTransactionConfirmations: 10,
- walletEvents: .placeholder
- )
-
- ViewStore(store).send(.walletEvents(.updateDestination(.showWalletEvent(walletEvent))))
- let walletEventsStore = WalletEventsFlowStore(
- initialState: walletEventsState,
- reducer: WalletEventsFlowReducer()
- )
-
- addAttachments(
- name: "\(#function)_WalletEventDetail",
- TransactionDetailView(store: walletEventsStore, transaction: transaction, tokenName: "ZEC")
- )
- }
-
- 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(
- errorMessage: "possible roll back",
- memos: [memo],
- minedHeight: 1_875_256,
- zAddress: "t1gXqfSSQt6WfpwyuCU3Wi7sSVZ66DYQ3Po",
- fee: Zatoshi(1_000_000),
- id: "ff3927e1f83df9b1b0dc75540ddc59ee435eecebae914d2e6dfe8576fbedc9a8",
- status: .failed,
- timestamp: 1234567,
- zecAmount: Zatoshi(25_000_000)
- )
-
- let walletEvent = WalletEvent(id: transaction.id, state: .transaction(transaction), timestamp: transaction.timestamp)
-
- let balance = WalletBalance(verified: 12_345_000, total: 12_345_000)
- let store = HomeStore(
- initialState: .init(
- scanState: .placeholder,
- shieldedBalance: balance.redacted,
- synchronizerStatusSnapshot: .default,
- walletConfig: .default,
- walletEventsState: .init(walletEvents: IdentifiedArrayOf(uniqueElements: [walletEvent]))
- ),
- reducer: HomeReducer(networkType: .testnet)
- )
-
- ViewStore(store).send(.walletEvents(.updateDestination(.showWalletEvent(walletEvent))))
- let walletEventsStore = WalletEventsFlowStore(
- initialState: .placeHolder,
- reducer: WalletEventsFlowReducer()
- )
-
- addAttachments(
- name: "\(#function)_WalletEventDetail",
- TransactionDetailView(store: walletEventsStore, transaction: transaction, tokenName: "ZEC")
- )
- }
-}
diff --git a/secantTests/TransactionListTests/TransactionListTests.swift b/secantTests/TransactionListTests/TransactionListTests.swift
new file mode 100644
index 00000000..447cda5d
--- /dev/null
+++ b/secantTests/TransactionListTests/TransactionListTests.swift
@@ -0,0 +1,279 @@
+//
+// TransactionListTests.swift
+// secantTests
+//
+// Created by Lukáš Korba on 27.04.2022.
+//
+
+import XCTest
+import ComposableArchitecture
+import ZcashLightClientKit
+import Pasteboard
+import Models
+import TransactionList
+@testable import secant_testnet
+
+class TransactionListTests: XCTestCase {
+ @MainActor func testSynchronizerSubscription() async throws {
+ let store = TestStore(
+ initialState: TransactionListReducer.State(
+ isScrollable: true,
+ transactionList: []
+ ),
+ reducer: TransactionListReducer()
+ )
+
+ store.dependencies.sdkSynchronizer = .mocked()
+ store.dependencies.sdkSynchronizer.getAllTransactions = { [] }
+ store.dependencies.mainQueue = .immediate
+
+ await store.send(.onAppear) { state in
+ state.requiredTransactionConfirmations = 10
+ }
+
+ await store.receive(.synchronizerStateChanged(.unprepared))
+
+ await store.receive(.updateTransactionList([]))
+
+ // ending the subscription
+ await store.send(.onDisappear)
+
+ await store.finish()
+ }
+
+ @MainActor func testSynchronizerStateChanged2Synced() async throws {
+ let mocked: [TransactionStateMockHelper] = [
+ TransactionStateMockHelper(date: 1651039202, amount: Zatoshi(1), status: .paid, uuid: "aa11"),
+ TransactionStateMockHelper(date: 1651039101, amount: Zatoshi(2), uuid: "bb22"),
+ TransactionStateMockHelper(date: 1651039000, amount: Zatoshi(3), status: .paid, uuid: "cc33"),
+ TransactionStateMockHelper(date: 1651039505, amount: Zatoshi(4), uuid: "dd44"),
+ TransactionStateMockHelper(date: 1651039404, amount: Zatoshi(5), uuid: "ee55"),
+ TransactionStateMockHelper(
+ date: 1651039606,
+ amount: Zatoshi(6),
+ status: .paid,
+ uuid: "ff66"
+ ),
+ TransactionStateMockHelper(date: 1651039303, amount: Zatoshi(7), uuid: "gg77"),
+ TransactionStateMockHelper(date: 1651039707, amount: Zatoshi(8), status: .paid, uuid: "hh88"),
+ TransactionStateMockHelper(date: 1651039808, amount: Zatoshi(9), uuid: "ii99")
+ ]
+
+ let transactionList: [TransactionState] = mocked.map {
+ let transaction = TransactionState.placeholder(
+ amount: $0.amount,
+ fee: Zatoshi(10),
+ shielded: $0.shielded,
+ status: $0.amount.amount > 5 ? .sending : $0.status,
+ timestamp: $0.date,
+ uuid: $0.uuid
+ )
+ return transaction
+ }
+
+ let identifiedTransactionList = IdentifiedArrayOf(uniqueElements: transactionList)
+
+ let store = TestStore(
+ initialState: TransactionListReducer.State(
+ isScrollable: true,
+ transactionList: identifiedTransactionList
+ ),
+ reducer: TransactionListReducer()
+ )
+
+ store.dependencies.mainQueue = .immediate
+ store.dependencies.sdkSynchronizer = .mocked()
+
+ await store.send(.synchronizerStateChanged(.upToDate)) { state in
+ state.latestMinedHeight = 0
+ }
+
+ await store.receive(.updateTransactionList(transactionList)) { state in
+ let receivedTransactionList = IdentifiedArrayOf(
+ uniqueElements:
+ transactionList
+ .sorted(by: { lhs, rhs in
+ guard let lhsTimestamp = lhs.timestamp, let rhsTimestamp = rhs.timestamp else {
+ return false
+ }
+ return lhsTimestamp > rhsTimestamp
+ })
+ )
+
+ state.transactionList = receivedTransactionList
+ state.latestTranassctionId = "ii99"
+ }
+
+ await store.finish()
+ }
+
+ func testCopyToPasteboard() throws {
+ let testPasteboard = PasteboardClient.testPasteboard
+
+ let store = TestStore(
+ initialState: TransactionListReducer.State(
+ isScrollable: true,
+ transactionList: []
+ ),
+ reducer: TransactionListReducer()
+ )
+
+ store.dependencies.pasteboard = testPasteboard
+
+ let testText = "test text".redacted
+ store.send(.copyToPastboard(testText))
+
+ XCTAssertEqual(
+ testPasteboard.getString()?.data,
+ testText.data,
+ "WalletEvetns: `testCopyToPasteboard` is expected to match the input `\(testText.data)`"
+ )
+ }
+
+ // MARK: - Expansion
+
+ func testTransactionExpansion() throws {
+ let id = "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja"
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: id,
+ status: .paid,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ let store = TestStore(
+ initialState: TransactionListReducer.State(
+ isScrollable: true,
+ transactionList: [transaction]
+ ),
+ reducer: TransactionListReducer()
+ )
+
+ store.send(.transactionExpandRequested(id)) { state in
+ state.transactionList[0].isExpanded = true
+ }
+ }
+
+ func testAddressExpansionRequestedButTransactionIsNot() throws {
+ let id = "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja"
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: id,
+ status: .paid,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ let store = TestStore(
+ initialState: TransactionListReducer.State(
+ isScrollable: true,
+ transactionList: [transaction]
+ ),
+ reducer: TransactionListReducer()
+ )
+
+ store.send(.transactionAddressExpandRequested(id)) { state in
+ state.transactionList[0].isExpanded = true
+ }
+ }
+
+ func testAddressExpansion() throws {
+ let id = "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja"
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: id,
+ status: .paid,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isAddressExpanded: false,
+ isExpanded: true,
+ isIdExpanded: false
+ )
+
+ let store = TestStore(
+ initialState: TransactionListReducer.State(
+ isScrollable: true,
+ transactionList: [transaction]
+ ),
+ reducer: TransactionListReducer()
+ )
+
+ store.send(.transactionAddressExpandRequested(id)) { state in
+ state.transactionList[0].isAddressExpanded = true
+ }
+ }
+
+ func testIdExpansionRequestedButTransactionIsNot() throws {
+ let id = "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja"
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: id,
+ status: .paid,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ let store = TestStore(
+ initialState: TransactionListReducer.State(
+ isScrollable: true,
+ transactionList: [transaction]
+ ),
+ reducer: TransactionListReducer()
+ )
+
+ store.send(.transactionIdExpandRequested(id)) { state in
+ state.transactionList[0].isExpanded = true
+ }
+ }
+
+ func testIdExpansion() throws {
+ let id = "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja"
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: id,
+ status: .paid,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isAddressExpanded: false,
+ isExpanded: true,
+ isIdExpanded: false
+ )
+
+ let store = TestStore(
+ initialState: TransactionListReducer.State(
+ isScrollable: true,
+ transactionList: [transaction]
+ ),
+ reducer: TransactionListReducer()
+ )
+
+ store.send(.transactionIdExpandRequested(id)) { state in
+ state.transactionList[0].isIdExpanded = true
+ }
+ }
+}
diff --git a/secantTests/TransactionListTests/TransactionStateTests.swift b/secantTests/TransactionListTests/TransactionStateTests.swift
new file mode 100644
index 00000000..6aed640d
--- /dev/null
+++ b/secantTests/TransactionListTests/TransactionStateTests.swift
@@ -0,0 +1,434 @@
+//
+// TransactionStateTests.swift
+// secantTests
+//
+// Created by Lukáš Korba on 08.11.2023.
+//
+
+import XCTest
+import ZcashLightClientKit
+import Models
+import Generated
+
+final class TransactionStateTests: XCTestCase {
+ // MARK: - Title tests (String & Color)
+
+ func testTitleSent() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .paid,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ XCTAssertEqual(transaction.title, L10n.Transaction.sent)
+ }
+
+ func testTitleSentColor() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .paid,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ XCTAssertEqual(transaction.titleColor, Asset.Colors.primary.color)
+ }
+
+ func testTitleReceived() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .received,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ XCTAssertEqual(transaction.title, L10n.Transaction.received)
+ }
+
+ func testTitleReceivedColor() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .received,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ XCTAssertEqual(transaction.titleColor, Asset.Colors.primary.color)
+ }
+
+ func testTitleSending() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .sending,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ XCTAssertEqual(transaction.title, L10n.Transaction.sending)
+ }
+
+ func testTitleSendingColor() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .sending,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ XCTAssertEqual(transaction.titleColor, Asset.Colors.shade47.color)
+ }
+
+ func testTitleReceiving() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .receiving,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ XCTAssertEqual(transaction.title, L10n.Transaction.receiving)
+ }
+
+ func testTitleReceivingColor() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .receiving,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ XCTAssertEqual(transaction.titleColor, Asset.Colors.shade47.color)
+ }
+
+ func testTitleFailedSend() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .failed,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isSentTransaction: true,
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ XCTAssertEqual(transaction.title, L10n.Transaction.failedSend)
+ }
+
+ func testTitleFailedSendColor() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .failed,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isSentTransaction: true,
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ XCTAssertEqual(transaction.titleColor, Asset.Colors.error.color)
+ }
+
+ func testTitleFailedReceived() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .failed,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isSentTransaction: false,
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ XCTAssertEqual(transaction.title, L10n.Transaction.failedReceive)
+ }
+
+ func testTitleFailedReceivedColor() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .failed,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isSentTransaction: false,
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ XCTAssertEqual(transaction.titleColor, Asset.Colors.error.color)
+ }
+
+ // MARK: - Balance color
+
+ func testBalanceSentColor() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .paid,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ XCTAssertEqual(transaction.balanceColor, Asset.Colors.error.color)
+ }
+
+ func testBalanceReceivedColor() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .received,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ XCTAssertEqual(transaction.balanceColor, Asset.Colors.primary.color)
+ }
+
+ func testBalanceFailedSendColor() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .failed,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isSentTransaction: true,
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ XCTAssertEqual(transaction.balanceColor, Asset.Colors.error.color)
+ }
+
+ func testBalanceFailedReceivedColor() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .failed,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isSentTransaction: false,
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ XCTAssertEqual(transaction.balanceColor, Asset.Colors.error.color)
+ }
+
+ func testBalanceSendingColor() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .sending,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isSentTransaction: false,
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ XCTAssertEqual(transaction.balanceColor, Asset.Colors.error.color)
+ }
+
+ func testBalanceReceivingColor() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .receiving,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_000_000),
+ isSentTransaction: false,
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ XCTAssertEqual(transaction.balanceColor, Asset.Colors.primary.color)
+ }
+
+ // MARK: - Balances and String representations for collapsed/expanded states
+
+ func testExpandedPaidAmountTupple() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .paid,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_793_456),
+ isSentTransaction: false,
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ // 0.025793456
+ let expandedString = transaction.expandedAmountString
+
+ XCTAssertEqual(expandedString.primary, "-0.257")
+ XCTAssertEqual(expandedString.secondary, "93456")
+ }
+
+ func testExpandedReceivedAmountTupple() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .received,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_793_456),
+ isSentTransaction: false,
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ let expandedString = transaction.expandedAmountString
+
+ XCTAssertEqual(expandedString.primary, "+0.257")
+ XCTAssertEqual(expandedString.secondary, "93456")
+ }
+
+ func testCollapsedPrimaryAmountRoundingForSend() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .paid,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_793_456),
+ isSentTransaction: false,
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ XCTAssertEqual(transaction.roundedAmountString, "-0.258")
+ }
+
+ func testCollapsedPrimaryAmountRoundingForReceive() throws {
+ let transaction = TransactionState(
+ memos: [try! Memo(string: "Hi, pay me and I'll pay you")],
+ minedHeight: BlockHeight(1),
+ zAddress: "tmP3uLtGx5GPddkq8a6ddmXhqJJ3vy6tpTE",
+ fee: Zatoshi(10_000),
+ id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
+ status: .received,
+ timestamp: 1699290621,
+ zecAmount: Zatoshi(25_793_456),
+ isSentTransaction: false,
+ isAddressExpanded: false,
+ isExpanded: false,
+ isIdExpanded: false
+ )
+
+ XCTAssertEqual(transaction.roundedAmountString, "+0.258")
+ }
+}
diff --git a/secantTests/WalletEventsTests/WalletEventsTests.swift b/secantTests/WalletEventsTests/WalletEventsTests.swift
deleted file mode 100644
index b05c1b88..00000000
--- a/secantTests/WalletEventsTests/WalletEventsTests.swift
+++ /dev/null
@@ -1,138 +0,0 @@
-//
-// WalletEventsTests.swift
-// secantTests
-//
-// Created by Lukáš Korba on 27.04.2022.
-//
-
-import XCTest
-import ComposableArchitecture
-import ZcashLightClientKit
-import Pasteboard
-import Models
-import WalletEventsFlow
-@testable import secant_testnet
-
-class WalletEventsTests: XCTestCase {
- @MainActor func testSynchronizerSubscription() async throws {
- let store = TestStore(
- initialState: WalletEventsFlowReducer.State(
- destination: .latest,
- isScrollable: true,
- walletEvents: []
- ),
- reducer: WalletEventsFlowReducer()
- )
-
- store.dependencies.sdkSynchronizer = .mocked()
- store.dependencies.sdkSynchronizer.getAllTransactions = { [] }
- store.dependencies.mainQueue = .immediate
-
- await store.send(.onAppear) { state in
- state.requiredTransactionConfirmations = 10
- }
-
- await store.receive(.synchronizerStateChanged(.unprepared))
-
- await store.receive(.updateWalletEvents([]))
-
- // ending the subscription
- await store.send(.onDisappear)
-
- await store.finish()
- }
-
- @MainActor func testSynchronizerStateChanged2Synced() async throws {
- 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"),
- 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 transaction = TransactionState.placeholder(
- amount: $0.amount,
- fee: Zatoshi(10),
- shielded: $0.shielded,
- status: $0.amount.amount > 5 ? .sending : $0.status,
- timestamp: $0.date,
- uuid: $0.uuid
- )
- return WalletEvent(
- id: transaction.id,
- state: .transaction(transaction),
- timestamp: transaction.timestamp
- )
- }
-
- let identifiedWalletEvents = IdentifiedArrayOf(uniqueElements: walletEvents)
-
- let store = TestStore(
- initialState: WalletEventsFlowReducer.State(
- destination: .latest,
- isScrollable: true,
- walletEvents: identifiedWalletEvents
- ),
- reducer: WalletEventsFlowReducer()
- )
-
- store.dependencies.mainQueue = .immediate
- store.dependencies.sdkSynchronizer = .mocked()
-
- await store.send(.synchronizerStateChanged(.upToDate)) { state in
- state.latestMinedHeight = 0
- }
-
- await store.receive(.updateWalletEvents(walletEvents)) { state in
- let receivedWalletEvents = IdentifiedArrayOf(
- uniqueElements:
- walletEvents
- .sorted(by: { lhs, rhs in
- guard let lhsTimestamp = lhs.timestamp, let rhsTimestamp = rhs.timestamp else {
- return false
- }
- return lhsTimestamp > rhsTimestamp
- })
- )
-
- state.walletEvents = receivedWalletEvents
- }
-
- await store.finish()
- }
-
- func testCopyToPasteboard() throws {
- let testPasteboard = PasteboardClient.testPasteboard
-
- let store = TestStore(
- initialState: WalletEventsFlowReducer.State(
- destination: .latest,
- isScrollable: true,
- walletEvents: []
- ),
- reducer: WalletEventsFlowReducer()
- )
-
- store.dependencies.pasteboard = testPasteboard
-
- let testText = "test text".redacted
- store.send(.copyToPastboard(testText))
-
- XCTAssertEqual(
- testPasteboard.getString()?.data,
- testText.data,
- "WalletEvetns: `testCopyToPasteboard` is expected to match the input `\(testText.data)`"
- )
- }
-}