Merge pull request #1307 from LukasKorba/1305-Upgrade-TransactionListReducer-to-the-latest-TCA
[#1305] Upgrade TransactionListReducer to the latest TCA
This commit is contained in:
commit
1375c75267
|
@ -25,7 +25,7 @@ public struct Home {
|
|||
public var scanState: Scan.State
|
||||
public var syncProgressState: SyncProgress.State
|
||||
public var walletConfig: WalletConfig
|
||||
public var transactionListState: TransactionListReducer.State
|
||||
public var transactionListState: TransactionList.State
|
||||
public var walletBalancesState: WalletBalances.State
|
||||
|
||||
public init(
|
||||
|
@ -33,7 +33,7 @@ public struct Home {
|
|||
migratingDatabase: Bool = true,
|
||||
scanState: Scan.State,
|
||||
syncProgressState: SyncProgress.State,
|
||||
transactionListState: TransactionListReducer.State,
|
||||
transactionListState: TransactionList.State,
|
||||
walletBalancesState: WalletBalances.State,
|
||||
walletConfig: WalletConfig
|
||||
) {
|
||||
|
@ -60,7 +60,7 @@ public struct Home {
|
|||
case syncFailed(ZcashError)
|
||||
case syncProgress(SyncProgress.Action)
|
||||
case updateTransactionList([TransactionState])
|
||||
case transactionList(TransactionListReducer.Action)
|
||||
case transactionList(TransactionList.Action)
|
||||
case walletBalances(WalletBalances.Action)
|
||||
}
|
||||
|
||||
|
@ -73,7 +73,7 @@ public struct Home {
|
|||
|
||||
public var body: some Reducer<State, Action> {
|
||||
Scope(state: \.transactionListState, action: /Action.transactionList) {
|
||||
TransactionListReducer()
|
||||
TransactionList()
|
||||
}
|
||||
|
||||
Scope(state: \.syncProgressState, action: /Action.syncProgress) {
|
||||
|
|
|
@ -13,13 +13,13 @@ public struct SandboxReducer: Reducer {
|
|||
case recoveryPhraseDisplay
|
||||
case scan
|
||||
}
|
||||
public var transactionListState: TransactionListReducer.State
|
||||
public var transactionListState: TransactionList.State
|
||||
public var destination: Destination?
|
||||
}
|
||||
|
||||
public enum Action: Equatable {
|
||||
case updateDestination(SandboxReducer.State.Destination?)
|
||||
case transactionList(TransactionListReducer.Action)
|
||||
case transactionList(TransactionList.Action)
|
||||
case reset
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ public struct SandboxReducer: Reducer {
|
|||
return .none
|
||||
|
||||
case let .transactionList(transactionListAction):
|
||||
return TransactionListReducer()
|
||||
return TransactionList()
|
||||
.reduce(into: &state.transactionListState, action: transactionListAction)
|
||||
.map(SandboxReducer.Action.transactionList)
|
||||
|
||||
|
@ -45,7 +45,7 @@ public struct SandboxReducer: Reducer {
|
|||
// MARK: - Store
|
||||
|
||||
extension SandboxStore {
|
||||
func historyStore() -> TransactionListStore {
|
||||
func historyStore() -> StoreOf<TransactionList> {
|
||||
self.scope(
|
||||
state: \.transactionListState,
|
||||
action: SandboxReducer.Action.transactionList
|
||||
|
|
|
@ -9,19 +9,18 @@ import SDKSynchronizer
|
|||
import ReadTransactionsStorage
|
||||
import ZcashSDKEnvironment
|
||||
|
||||
public typealias TransactionListStore = Store<TransactionListReducer.State, TransactionListReducer.Action>
|
||||
public typealias TransactionListViewStore = ViewStore<TransactionListReducer.State, TransactionListReducer.Action>
|
||||
|
||||
public struct TransactionListReducer: Reducer {
|
||||
@Reducer
|
||||
public struct TransactionList {
|
||||
private let CancelStateId = UUID()
|
||||
private let CancelEventId = UUID()
|
||||
|
||||
@ObservableState
|
||||
public struct State: Equatable {
|
||||
public var latestMinedHeight: BlockHeight?
|
||||
public var requiredTransactionConfirmations = 0
|
||||
public var latestTransactionList: [TransactionState] = []
|
||||
public var transactionList: IdentifiedArrayOf<TransactionState>
|
||||
public var latestTranassctionId = ""
|
||||
public var latestTransactionId = ""
|
||||
|
||||
public init(
|
||||
latestMinedHeight: BlockHeight? = nil,
|
||||
|
@ -68,7 +67,7 @@ public struct TransactionListReducer: Reducer {
|
|||
.publisher {
|
||||
sdkSynchronizer.stateStream()
|
||||
.throttle(for: .seconds(0.2), scheduler: mainQueue, latest: true)
|
||||
.map { TransactionListReducer.Action.synchronizerStateChanged($0.syncStatus) }
|
||||
.map { TransactionList.Action.synchronizerStateChanged($0.syncStatus) }
|
||||
}
|
||||
.cancellable(id: CancelStateId, cancelInFlight: true),
|
||||
.publisher {
|
||||
|
@ -76,7 +75,7 @@ public struct TransactionListReducer: Reducer {
|
|||
.throttle(for: .seconds(0.2), scheduler: mainQueue, latest: true)
|
||||
.compactMap {
|
||||
if case SynchronizerEvent.foundTransactions = $0 {
|
||||
return TransactionListReducer.Action.foundTransactions
|
||||
return TransactionList.Action.foundTransactions
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -156,7 +155,7 @@ public struct TransactionListReducer: Reducer {
|
|||
}
|
||||
|
||||
state.transactionList = IdentifiedArrayOf(uniqueElements: sortedTransactionList)
|
||||
state.latestTranassctionId = state.transactionList.first?.id ?? ""
|
||||
state.latestTransactionId = state.transactionList.first?.id ?? ""
|
||||
|
||||
return .none
|
||||
|
||||
|
@ -247,59 +246,3 @@ public struct TransactionListReducer: Reducer {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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 initial: Self {
|
||||
.init(transactionList: [])
|
||||
}
|
||||
}
|
||||
|
||||
extension TransactionListStore {
|
||||
public static var placeholder: Store<TransactionListReducer.State, TransactionListReducer.Action> {
|
||||
Store(
|
||||
initialState: .placeholder
|
||||
) {
|
||||
TransactionListReducer()
|
||||
.dependency(\.zcashSDKEnvironment, .testnet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension IdentifiedArrayOf where Element == TransactionState {
|
||||
public static var placeholder: IdentifiedArrayOf<TransactionState> {
|
||||
.init(
|
||||
uniqueElements: (0..<30).map {
|
||||
TransactionState(
|
||||
fee: Zatoshi(10),
|
||||
id: String($0),
|
||||
status: .paid,
|
||||
timestamp: 1234567,
|
||||
zecAmount: Zatoshi(25)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
public static var mocked: IdentifiedArrayOf<TransactionState> {
|
||||
.init(
|
||||
uniqueElements: [
|
||||
TransactionState.mockedSent,
|
||||
TransactionState.mockedReceived
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,20 +2,22 @@ import SwiftUI
|
|||
import ComposableArchitecture
|
||||
import Generated
|
||||
import UIComponents
|
||||
import Models
|
||||
import ZcashLightClientKit
|
||||
|
||||
public struct TransactionListView: View {
|
||||
let store: TransactionListStore
|
||||
let store: StoreOf<TransactionList>
|
||||
let tokenName: String
|
||||
|
||||
public init(store: TransactionListStore, tokenName: String) {
|
||||
public init(store: StoreOf<TransactionList>, tokenName: String) {
|
||||
self.store = store
|
||||
self.tokenName = tokenName
|
||||
}
|
||||
|
||||
public var body: some View {
|
||||
WithViewStore(store, observe: { $0 }) { viewStore in
|
||||
WithPerceptionTracking {
|
||||
List {
|
||||
if viewStore.transactionList.isEmpty {
|
||||
if store.transactionList.isEmpty {
|
||||
Text(L10n.TransactionList.noTransactions)
|
||||
.font(.custom(FontFamily.Inter.bold.name, size: 13))
|
||||
.frame(maxWidth: .infinity)
|
||||
|
@ -24,24 +26,26 @@ public struct TransactionListView: View {
|
|||
.listRowSeparator(.hidden)
|
||||
.padding(.top, 30)
|
||||
} else {
|
||||
ForEach(viewStore.transactionList) { transaction in
|
||||
TransactionRowView(
|
||||
viewStore: viewStore,
|
||||
transaction: transaction,
|
||||
tokenName: tokenName,
|
||||
isLatestTransaction: viewStore.isLatestTransaction(id: transaction.id)
|
||||
)
|
||||
.listRowInsets(EdgeInsets())
|
||||
ForEach(store.transactionList) { transaction in
|
||||
WithPerceptionTracking {
|
||||
TransactionRowView(
|
||||
store: store,
|
||||
transaction: transaction,
|
||||
tokenName: tokenName,
|
||||
isLatestTransaction: store.latestTransactionId == transaction.id
|
||||
)
|
||||
.listRowInsets(EdgeInsets())
|
||||
}
|
||||
}
|
||||
.listRowBackground(Asset.Colors.shade97.color)
|
||||
.listRowSeparator(.hidden)
|
||||
}
|
||||
}
|
||||
.disabled(viewStore.transactionList.isEmpty)
|
||||
.disabled(store.transactionList.isEmpty)
|
||||
.background(Asset.Colors.shade97.color)
|
||||
.listStyle(.plain)
|
||||
.onAppear { viewStore.send(.onAppear) }
|
||||
.onDisappear(perform: { viewStore.send(.onDisappear) })
|
||||
.onAppear { store.send(.onAppear) }
|
||||
.onDisappear(perform: { store.send(.onDisappear) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,3 +58,51 @@ public struct TransactionListView: View {
|
|||
.preferredColorScheme(.light)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Placeholders
|
||||
|
||||
extension TransactionList.State {
|
||||
public static var placeholder: Self {
|
||||
.init(transactionList: .mocked)
|
||||
}
|
||||
|
||||
public static var initial: Self {
|
||||
.init(transactionList: [])
|
||||
}
|
||||
}
|
||||
|
||||
extension StoreOf<TransactionList> {
|
||||
public static var placeholder: Store<TransactionList.State, TransactionList.Action> {
|
||||
Store(
|
||||
initialState: .placeholder
|
||||
) {
|
||||
TransactionList()
|
||||
.dependency(\.zcashSDKEnvironment, .testnet)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension IdentifiedArrayOf where Element == TransactionState {
|
||||
public static var placeholder: IdentifiedArrayOf<TransactionState> {
|
||||
.init(
|
||||
uniqueElements: (0..<30).map {
|
||||
TransactionState(
|
||||
fee: Zatoshi(10),
|
||||
id: String($0),
|
||||
status: .paid,
|
||||
timestamp: 1234567,
|
||||
zecAmount: Zatoshi(25)
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
public static var mocked: IdentifiedArrayOf<TransactionState> {
|
||||
.init(
|
||||
uniqueElements: [
|
||||
TransactionState.mockedSent,
|
||||
TransactionState.mockedReceived
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,70 +11,72 @@ import ComposableArchitecture
|
|||
import Generated
|
||||
|
||||
struct MessageView: View {
|
||||
let viewStore: TransactionListViewStore
|
||||
let store: StoreOf<TransactionList>
|
||||
|
||||
let messages: [String]?
|
||||
let isSpending: Bool
|
||||
let isFailed: Bool
|
||||
|
||||
public init(
|
||||
viewStore: TransactionListViewStore,
|
||||
store: StoreOf<TransactionList>,
|
||||
messages: [String]?,
|
||||
isSpending: Bool,
|
||||
isFailed: Bool = false
|
||||
) {
|
||||
self.viewStore = viewStore
|
||||
self.store = store
|
||||
self.messages = messages
|
||||
self.isSpending = isSpending
|
||||
self.isFailed = isFailed
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
if let memoTexts = messages {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text(memoTexts.count == 1
|
||||
? L10n.TransactionList.messageTitle
|
||||
: L10n.TransactionList.messageTitlePlural
|
||||
)
|
||||
.font(.custom(FontFamily.Inter.medium.name, size: 13))
|
||||
.padding(.bottom, 8)
|
||||
|
||||
ForEach(0..<memoTexts.count, id: \.self) { index in
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Color.clear.frame(height: 0)
|
||||
|
||||
Text(memoTexts[index])
|
||||
.font(.custom(FontFamily.Inter.regular.name, size: 13))
|
||||
.foregroundColor(
|
||||
isFailed ?
|
||||
Asset.Colors.error.color
|
||||
: Asset.Colors.primary.color
|
||||
)
|
||||
.conditionalStrikethrough(isFailed)
|
||||
.padding()
|
||||
}
|
||||
.messageShape(
|
||||
filled: !isSpending
|
||||
? Asset.Colors.messageBcgReceived.color
|
||||
: nil,
|
||||
border: isSpending
|
||||
? Asset.Colors.primary.color
|
||||
: nil,
|
||||
orientation: !isSpending
|
||||
? .right
|
||||
: .left
|
||||
WithPerceptionTracking {
|
||||
if let memoTexts = messages {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text(memoTexts.count == 1
|
||||
? L10n.TransactionList.messageTitle
|
||||
: L10n.TransactionList.messageTitlePlural
|
||||
)
|
||||
.font(.custom(FontFamily.Inter.medium.name, size: 13))
|
||||
.padding(.bottom, 8)
|
||||
|
||||
TapToCopyTransactionDataView(viewStore: viewStore, data: memoTexts[index].redacted)
|
||||
ForEach(0..<memoTexts.count, id: \.self) { index in
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Color.clear.frame(height: 0)
|
||||
|
||||
Text(memoTexts[index])
|
||||
.font(.custom(FontFamily.Inter.regular.name, size: 13))
|
||||
.foregroundColor(
|
||||
isFailed ?
|
||||
Asset.Colors.error.color
|
||||
: Asset.Colors.primary.color
|
||||
)
|
||||
.conditionalStrikethrough(isFailed)
|
||||
.padding()
|
||||
}
|
||||
.messageShape(
|
||||
filled: !isSpending
|
||||
? Asset.Colors.messageBcgReceived.color
|
||||
: nil,
|
||||
border: isSpending
|
||||
? Asset.Colors.primary.color
|
||||
: nil,
|
||||
orientation: !isSpending
|
||||
? .right
|
||||
: .left
|
||||
)
|
||||
|
||||
TapToCopyTransactionDataView(store: store, data: memoTexts[index].redacted)
|
||||
}
|
||||
.padding(.bottom, 20)
|
||||
}
|
||||
.padding(.bottom, 20)
|
||||
.padding(.bottom, 7)
|
||||
.padding(.vertical, 10)
|
||||
} else {
|
||||
Text(L10n.TransactionList.noMessageIncluded)
|
||||
.font(.custom(FontFamily.Inter.regular.name, size: 13))
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
}
|
||||
.padding(.bottom, 7)
|
||||
.padding(.vertical, 10)
|
||||
} else {
|
||||
Text(L10n.TransactionList.noMessageIncluded)
|
||||
.font(.custom(FontFamily.Inter.regular.name, size: 13))
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -82,14 +84,14 @@ struct MessageView: View {
|
|||
#Preview {
|
||||
VStack(alignment: .leading) {
|
||||
MessageView(
|
||||
viewStore: ViewStore(.placeholder, observe: { $0 }),
|
||||
store: .placeholder,
|
||||
messages: ["Test"],
|
||||
isSpending: true
|
||||
)
|
||||
.padding(.bottom, 50)
|
||||
|
||||
MessageView(
|
||||
viewStore: ViewStore(.placeholder, observe: { $0 }),
|
||||
store: .placeholder,
|
||||
messages: ["Test"],
|
||||
isSpending: true,
|
||||
isFailed: true
|
||||
|
@ -97,13 +99,13 @@ struct MessageView: View {
|
|||
.padding(.bottom, 50)
|
||||
|
||||
MessageView(
|
||||
viewStore: ViewStore(.placeholder, observe: { $0 }),
|
||||
store: .placeholder,
|
||||
messages: ["Test"],
|
||||
isSpending: false
|
||||
)
|
||||
|
||||
MessageView(
|
||||
viewStore: ViewStore(.placeholder, observe: { $0 }),
|
||||
store: .placeholder,
|
||||
messages: nil,
|
||||
isSpending: false
|
||||
)
|
||||
|
|
|
@ -11,37 +11,39 @@ import Generated
|
|||
import Utils
|
||||
|
||||
struct TapToCopyTransactionDataView: View {
|
||||
let viewStore: TransactionListViewStore
|
||||
let store: StoreOf<TransactionList>
|
||||
let data: RedactableString
|
||||
|
||||
public init(viewStore: TransactionListViewStore, data: RedactableString) {
|
||||
self.viewStore = viewStore
|
||||
public init(store: StoreOf<TransactionList>, data: RedactableString) {
|
||||
self.store = store
|
||||
self.data = data
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Button {
|
||||
viewStore.send(.copyToPastboard(data))
|
||||
} label: {
|
||||
HStack {
|
||||
Asset.Assets.copy.image
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.frame(width: 11, height: 11)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
|
||||
Text(L10n.General.tapToCopy)
|
||||
.font(.custom(FontFamily.Inter.regular.name, size: 13))
|
||||
.foregroundColor(Asset.Colors.shade47.color)
|
||||
WithPerceptionTracking {
|
||||
Button {
|
||||
store.send(.copyToPastboard(data))
|
||||
} label: {
|
||||
HStack {
|
||||
Asset.Assets.copy.image
|
||||
.renderingMode(.template)
|
||||
.resizable()
|
||||
.frame(width: 11, height: 11)
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
|
||||
Text(L10n.General.tapToCopy)
|
||||
.font(.custom(FontFamily.Inter.regular.name, size: 13))
|
||||
.foregroundColor(Asset.Colors.shade47.color)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
}
|
||||
.buttonStyle(.borderless)
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
TapToCopyTransactionDataView(
|
||||
viewStore: ViewStore(.placeholder, observe: { $0 }),
|
||||
store: .placeholder,
|
||||
data: "something to copy".redacted
|
||||
)
|
||||
}
|
||||
|
|
|
@ -12,66 +12,68 @@ import Models
|
|||
import UIComponents
|
||||
|
||||
struct TransactionHeaderView: View {
|
||||
let viewStore: TransactionListViewStore
|
||||
let store: StoreOf<TransactionList>
|
||||
let transaction: TransactionState
|
||||
let isLatestTransaction: Bool
|
||||
|
||||
init(
|
||||
viewStore: TransactionListViewStore,
|
||||
store: StoreOf<TransactionList>,
|
||||
transaction: TransactionState,
|
||||
isLatestTransaction: Bool = false
|
||||
) {
|
||||
self.viewStore = viewStore
|
||||
self.store = store
|
||||
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.regular.name, size: 13))
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
|
||||
Spacer(minLength: 100)
|
||||
WithPerceptionTracking {
|
||||
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(.horizontal, 60)
|
||||
.padding(.bottom, 5)
|
||||
.padding(.trailing, 30)
|
||||
|
||||
TapToCopyTransactionDataView(viewStore: viewStore, data: transaction.address.redacted)
|
||||
if transaction.zAddress != nil && transaction.isAddressExpanded {
|
||||
HStack {
|
||||
Text(transaction.address)
|
||||
.font(.custom(FontFamily.Inter.regular.name, size: 13))
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
|
||||
Spacer(minLength: 100)
|
||||
}
|
||||
.padding(.horizontal, 60)
|
||||
.padding(.bottom, 5)
|
||||
|
||||
TapToCopyTransactionDataView(store: store, data: transaction.address.redacted)
|
||||
.padding(.horizontal, 60)
|
||||
.padding(.bottom, 20)
|
||||
}
|
||||
|
||||
Text("\(transaction.dateString ?? "")")
|
||||
.font(.custom(FontFamily.Inter.regular.name, size: 13))
|
||||
.foregroundColor(Asset.Colors.shade47.color)
|
||||
.padding(.horizontal, 60)
|
||||
.padding(.bottom, 20)
|
||||
}
|
||||
|
||||
Text("\(transaction.dateString ?? "")")
|
||||
.font(.custom(FontFamily.Inter.regular.name, size: 13))
|
||||
.foregroundColor(Asset.Colors.shade47.color)
|
||||
.padding(.horizontal, 60)
|
||||
}
|
||||
}
|
||||
.padding(.bottom, 30)
|
||||
}
|
||||
.padding(.bottom, 30)
|
||||
}
|
||||
|
||||
@ViewBuilder private func iconImage() -> some View {
|
||||
|
@ -105,7 +107,7 @@ struct TransactionHeaderView: View {
|
|||
.foregroundColor(Asset.Colors.primary.color)
|
||||
} else if !transaction.isAddressExpanded {
|
||||
Button {
|
||||
viewStore.send(.transactionAddressExpandRequested(transaction.id))
|
||||
store.send(.transactionAddressExpandRequested(transaction.id))
|
||||
} label: {
|
||||
Text(transaction.address)
|
||||
.font(.custom(FontFamily.Inter.regular.name, size: 13))
|
||||
|
@ -165,37 +167,37 @@ extension TransactionHeaderView {
|
|||
#Preview {
|
||||
VStack(spacing: 0) {
|
||||
TransactionHeaderView(
|
||||
viewStore: ViewStore(.placeholder, observe: { $0 }),
|
||||
store: .placeholder,
|
||||
transaction: .mockedFailed
|
||||
)
|
||||
.listRowSeparator(.hidden)
|
||||
|
||||
TransactionHeaderView(
|
||||
viewStore: ViewStore(.placeholder, observe: { $0 }),
|
||||
store: .placeholder,
|
||||
transaction: .mockedFailedReceive
|
||||
)
|
||||
.listRowSeparator(.hidden)
|
||||
|
||||
TransactionHeaderView(
|
||||
viewStore: ViewStore(.placeholder, observe: { $0 }),
|
||||
store: .placeholder,
|
||||
transaction: .mockedSent
|
||||
)
|
||||
.listRowSeparator(.hidden)
|
||||
|
||||
TransactionHeaderView(
|
||||
viewStore: ViewStore(.placeholder, observe: { $0 }),
|
||||
store: .placeholder,
|
||||
transaction: .mockedReceived
|
||||
)
|
||||
.listRowSeparator(.hidden)
|
||||
|
||||
TransactionHeaderView(
|
||||
viewStore: ViewStore(.placeholder, observe: { $0 }),
|
||||
store: .placeholder,
|
||||
transaction: .mockedSending
|
||||
)
|
||||
.listRowSeparator(.hidden)
|
||||
|
||||
TransactionHeaderView(
|
||||
viewStore: ViewStore(.placeholder, observe: { $0 }),
|
||||
store: .placeholder,
|
||||
transaction: .mockedReceiving
|
||||
)
|
||||
.listRowSeparator(.hidden)
|
||||
|
|
|
@ -11,53 +11,55 @@ import Generated
|
|||
import Models
|
||||
|
||||
struct TransactionIdView: View {
|
||||
let viewStore: TransactionListViewStore
|
||||
let store: StoreOf<TransactionList>
|
||||
let transaction: TransactionState
|
||||
|
||||
public init(viewStore: TransactionListViewStore, transaction: TransactionState) {
|
||||
self.viewStore = viewStore
|
||||
public init(store: StoreOf<TransactionList>, transaction: TransactionState) {
|
||||
self.store = store
|
||||
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)
|
||||
WithPerceptionTracking {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
if !transaction.isIdExpanded {
|
||||
HStack {
|
||||
Text(L10n.TransactionList.transactionId)
|
||||
|
||||
Button {
|
||||
store.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)
|
||||
|
||||
Spacer(minLength: 50)
|
||||
HStack {
|
||||
Text(transaction.id)
|
||||
.font(.custom(FontFamily.Inter.regular.name, size: 13))
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
|
||||
Spacer(minLength: 100)
|
||||
}
|
||||
.padding(.bottom, 10)
|
||||
|
||||
TapToCopyTransactionDataView(store: store, data: transaction.id.redacted)
|
||||
.padding(.bottom, 20)
|
||||
}
|
||||
.padding(.vertical, 20)
|
||||
}
|
||||
|
||||
if transaction.isIdExpanded {
|
||||
Text(L10n.TransactionList.transactionId)
|
||||
.padding(.top, 20)
|
||||
.padding(.bottom, 4)
|
||||
|
||||
HStack {
|
||||
Text(transaction.id)
|
||||
.font(.custom(FontFamily.Inter.regular.name, size: 13))
|
||||
.foregroundColor(Asset.Colors.primary.color)
|
||||
|
||||
Spacer(minLength: 100)
|
||||
}
|
||||
.padding(.bottom, 10)
|
||||
|
||||
TapToCopyTransactionDataView(viewStore: viewStore, data: transaction.id.redacted)
|
||||
.padding(.bottom, 20)
|
||||
}
|
||||
.font(.custom(FontFamily.Inter.regular.name, size: 13))
|
||||
.foregroundColor(Asset.Colors.shade47.color)
|
||||
}
|
||||
.font(.custom(FontFamily.Inter.regular.name, size: 13))
|
||||
.foregroundColor(Asset.Colors.shade47.color)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,7 +68,7 @@ struct TransactionIdView: View {
|
|||
transaction.isIdExpanded = true
|
||||
|
||||
return TransactionIdView(
|
||||
viewStore: ViewStore(.placeholder, observe: { $0 }),
|
||||
store: .placeholder,
|
||||
transaction: transaction
|
||||
)
|
||||
}
|
||||
|
|
|
@ -13,71 +13,73 @@ import Generated
|
|||
import UIComponents
|
||||
|
||||
public struct TransactionRowView: View {
|
||||
let viewStore: TransactionListViewStore
|
||||
let store: StoreOf<TransactionList>
|
||||
let transaction: TransactionState
|
||||
let tokenName: String
|
||||
let isLatestTransaction: Bool
|
||||
|
||||
public init(
|
||||
viewStore: TransactionListViewStore,
|
||||
store: StoreOf<TransactionList>,
|
||||
transaction: TransactionState,
|
||||
tokenName: String,
|
||||
isLatestTransaction: Bool = false
|
||||
) {
|
||||
self.viewStore = viewStore
|
||||
self.store = store
|
||||
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 {
|
||||
if !transaction.isTransparentRecipient {
|
||||
MessageView(
|
||||
viewStore: viewStore,
|
||||
messages: transaction.textMemos,
|
||||
isSpending: transaction.isSpending,
|
||||
isFailed: transaction.status == .failed
|
||||
WithPerceptionTracking {
|
||||
Button {
|
||||
store.send(.transactionExpandRequested(transaction.id), animation: .default)
|
||||
} label: {
|
||||
if transaction.isExpanded {
|
||||
TransactionHeaderView(
|
||||
store: store,
|
||||
transaction: transaction,
|
||||
isLatestTransaction: isLatestTransaction
|
||||
)
|
||||
} else {
|
||||
TransactionHeaderView(
|
||||
store: store,
|
||||
transaction: transaction,
|
||||
isLatestTransaction: isLatestTransaction
|
||||
)
|
||||
}
|
||||
|
||||
TransactionIdView(
|
||||
viewStore: viewStore,
|
||||
transaction: transaction
|
||||
)
|
||||
|
||||
if transaction.isSpending {
|
||||
TransactionFeeView(fee: transaction.fee ?? .zero)
|
||||
.padding(.vertical, 10)
|
||||
}
|
||||
|
||||
Button {
|
||||
viewStore.send(.transactionCollapseRequested(transaction.id), animation: .default)
|
||||
} label: {
|
||||
CollapseTransactionView()
|
||||
.padding(.vertical, 20)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 60)
|
||||
|
||||
if transaction.isExpanded {
|
||||
Group {
|
||||
if !transaction.isTransparentRecipient {
|
||||
MessageView(
|
||||
store: store,
|
||||
messages: transaction.textMemos,
|
||||
isSpending: transaction.isSpending,
|
||||
isFailed: transaction.status == .failed
|
||||
)
|
||||
}
|
||||
|
||||
TransactionIdView(
|
||||
store: store,
|
||||
transaction: transaction
|
||||
)
|
||||
|
||||
if transaction.isSpending {
|
||||
TransactionFeeView(fee: transaction.fee ?? .zero)
|
||||
.padding(.vertical, 10)
|
||||
}
|
||||
|
||||
Button {
|
||||
store.send(.transactionCollapseRequested(transaction.id), animation: .default)
|
||||
} label: {
|
||||
CollapseTransactionView()
|
||||
.padding(.vertical, 20)
|
||||
}
|
||||
}
|
||||
.padding(.horizontal, 60)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -85,7 +87,7 @@ public struct TransactionRowView: View {
|
|||
#Preview {
|
||||
List {
|
||||
TransactionRowView(
|
||||
viewStore: ViewStore(.placeholder, observe: { $0 }),
|
||||
store: .placeholder,
|
||||
transaction: .mockedFailed,
|
||||
tokenName: "ZEC"
|
||||
)
|
||||
|
@ -93,7 +95,7 @@ public struct TransactionRowView: View {
|
|||
.listRowInsets(EdgeInsets())
|
||||
|
||||
TransactionRowView(
|
||||
viewStore: ViewStore(.placeholder, observe: { $0 }),
|
||||
store: .placeholder,
|
||||
transaction: .mockedReceived,
|
||||
tokenName: "ZEC"
|
||||
)
|
||||
|
@ -101,7 +103,7 @@ public struct TransactionRowView: View {
|
|||
.listRowInsets(EdgeInsets())
|
||||
|
||||
TransactionRowView(
|
||||
viewStore: ViewStore(.placeholder, observe: { $0 }),
|
||||
store: .placeholder,
|
||||
transaction: .mockedSent,
|
||||
tokenName: "ZEC"
|
||||
)
|
||||
|
|
|
@ -15,10 +15,10 @@ import Home
|
|||
|
||||
class TransactionListSnapshotTests: XCTestCase {
|
||||
func testFullTransactionListSnapshot() throws {
|
||||
let store = TransactionListStore(
|
||||
let store = StoreOf<TransactionList>(
|
||||
initialState: .placeholder
|
||||
) {
|
||||
TransactionListReducer()
|
||||
TransactionList()
|
||||
.dependency(\.sdkSynchronizer, .mock)
|
||||
.dependency(\.mainQueue, .immediate)
|
||||
}
|
||||
|
|
|
@ -17,11 +17,11 @@ import TransactionList
|
|||
class TransactionListTests: XCTestCase {
|
||||
func testSynchronizerSubscription() async throws {
|
||||
let store = TestStore(
|
||||
initialState: TransactionListReducer.State(
|
||||
initialState: TransactionList.State(
|
||||
transactionList: []
|
||||
)
|
||||
) {
|
||||
TransactionListReducer()
|
||||
TransactionList()
|
||||
}
|
||||
|
||||
store.dependencies.sdkSynchronizer = .mocked()
|
||||
|
@ -76,11 +76,11 @@ class TransactionListTests: XCTestCase {
|
|||
let identifiedTransactionList = IdentifiedArrayOf(uniqueElements: transactionList)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: TransactionListReducer.State(
|
||||
initialState: TransactionList.State(
|
||||
transactionList: identifiedTransactionList
|
||||
)
|
||||
) {
|
||||
TransactionListReducer()
|
||||
TransactionList()
|
||||
}
|
||||
|
||||
store.dependencies.mainQueue = .immediate
|
||||
|
@ -104,7 +104,7 @@ class TransactionListTests: XCTestCase {
|
|||
|
||||
state.transactionList = receivedTransactionList
|
||||
state.latestTransactionList = transactionList
|
||||
state.latestTranassctionId = "aa11"
|
||||
state.latestTransactionId = "aa11"
|
||||
}
|
||||
|
||||
await store.finish()
|
||||
|
@ -143,12 +143,12 @@ class TransactionListTests: XCTestCase {
|
|||
let identifiedTransactionList = IdentifiedArrayOf(uniqueElements: transactionList)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: TransactionListReducer.State(
|
||||
initialState: TransactionList.State(
|
||||
latestTransactionList: transactionList,
|
||||
transactionList: identifiedTransactionList
|
||||
)
|
||||
) {
|
||||
TransactionListReducer()
|
||||
TransactionList()
|
||||
}
|
||||
|
||||
store.dependencies.mainQueue = .immediate
|
||||
|
@ -168,11 +168,11 @@ class TransactionListTests: XCTestCase {
|
|||
let testPasteboard = PasteboardClient.testPasteboard
|
||||
|
||||
let store = TestStore(
|
||||
initialState: TransactionListReducer.State(
|
||||
initialState: TransactionList.State(
|
||||
transactionList: []
|
||||
)
|
||||
) {
|
||||
TransactionListReducer()
|
||||
TransactionList()
|
||||
}
|
||||
|
||||
store.dependencies.pasteboard = testPasteboard
|
||||
|
@ -210,11 +210,11 @@ class TransactionListTests: XCTestCase {
|
|||
)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: TransactionListReducer.State(
|
||||
initialState: TransactionList.State(
|
||||
transactionList: [transaction]
|
||||
)
|
||||
) {
|
||||
TransactionListReducer()
|
||||
TransactionList()
|
||||
}
|
||||
|
||||
store.dependencies.readTransactionsStorage = .noOp
|
||||
|
@ -243,11 +243,11 @@ class TransactionListTests: XCTestCase {
|
|||
)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: TransactionListReducer.State(
|
||||
initialState: TransactionList.State(
|
||||
transactionList: [transaction]
|
||||
)
|
||||
) {
|
||||
TransactionListReducer()
|
||||
TransactionList()
|
||||
}
|
||||
|
||||
store.dependencies.readTransactionsStorage = .noOp
|
||||
|
@ -277,11 +277,11 @@ class TransactionListTests: XCTestCase {
|
|||
)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: TransactionListReducer.State(
|
||||
initialState: TransactionList.State(
|
||||
transactionList: [transaction]
|
||||
)
|
||||
) {
|
||||
TransactionListReducer()
|
||||
TransactionList()
|
||||
}
|
||||
|
||||
store.dependencies.readTransactionsStorage = .noOp
|
||||
|
@ -312,11 +312,11 @@ class TransactionListTests: XCTestCase {
|
|||
)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: TransactionListReducer.State(
|
||||
initialState: TransactionList.State(
|
||||
transactionList: [transaction]
|
||||
)
|
||||
) {
|
||||
TransactionListReducer()
|
||||
TransactionList()
|
||||
}
|
||||
|
||||
store.dependencies.readTransactionsStorage = .noOp
|
||||
|
@ -346,11 +346,11 @@ class TransactionListTests: XCTestCase {
|
|||
)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: TransactionListReducer.State(
|
||||
initialState: TransactionList.State(
|
||||
transactionList: [transaction]
|
||||
)
|
||||
) {
|
||||
TransactionListReducer()
|
||||
TransactionList()
|
||||
}
|
||||
|
||||
store.dependencies.readTransactionsStorage = .noOp
|
||||
|
@ -380,11 +380,11 @@ class TransactionListTests: XCTestCase {
|
|||
)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: TransactionListReducer.State(
|
||||
initialState: TransactionList.State(
|
||||
transactionList: [transaction]
|
||||
)
|
||||
) {
|
||||
TransactionListReducer()
|
||||
TransactionList()
|
||||
}
|
||||
|
||||
store.dependencies.readTransactionsStorage = .noOp
|
||||
|
@ -414,11 +414,11 @@ class TransactionListTests: XCTestCase {
|
|||
)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: TransactionListReducer.State(
|
||||
initialState: TransactionList.State(
|
||||
transactionList: [transaction]
|
||||
)
|
||||
) {
|
||||
TransactionListReducer()
|
||||
TransactionList()
|
||||
}
|
||||
|
||||
store.dependencies.readTransactionsStorage = .noOp
|
||||
|
@ -448,11 +448,11 @@ class TransactionListTests: XCTestCase {
|
|||
)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: TransactionListReducer.State(
|
||||
initialState: TransactionList.State(
|
||||
transactionList: [transaction]
|
||||
)
|
||||
) {
|
||||
TransactionListReducer()
|
||||
TransactionList()
|
||||
}
|
||||
|
||||
store.dependencies.readTransactionsStorage = .noOp
|
||||
|
@ -488,11 +488,11 @@ class TransactionListTests: XCTestCase {
|
|||
let identifiedTransactionList = IdentifiedArrayOf(uniqueElements: transactionList)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: TransactionListReducer.State(
|
||||
initialState: TransactionList.State(
|
||||
transactionList: identifiedTransactionList
|
||||
)
|
||||
) {
|
||||
TransactionListReducer()
|
||||
TransactionList()
|
||||
}
|
||||
|
||||
store.dependencies.mainQueue = .immediate
|
||||
|
@ -509,7 +509,7 @@ class TransactionListTests: XCTestCase {
|
|||
await store.receive(.updateTransactionList(transactionList)) { state in
|
||||
state.transactionList = IdentifiedArrayOf(uniqueElements: transactionList)
|
||||
state.latestTransactionList = transactionList
|
||||
state.latestTranassctionId = id
|
||||
state.latestTransactionId = id
|
||||
|
||||
XCTAssertTrue(state.transactionList[0].isUnread)
|
||||
}
|
||||
|
@ -541,11 +541,11 @@ class TransactionListTests: XCTestCase {
|
|||
let identifiedTransactionList = IdentifiedArrayOf(uniqueElements: transactionList)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: TransactionListReducer.State(
|
||||
initialState: TransactionList.State(
|
||||
transactionList: identifiedTransactionList
|
||||
)
|
||||
) {
|
||||
TransactionListReducer()
|
||||
TransactionList()
|
||||
}
|
||||
|
||||
store.dependencies.mainQueue = .immediate
|
||||
|
@ -563,7 +563,7 @@ class TransactionListTests: XCTestCase {
|
|||
await store.receive(.updateTransactionList(transactionList)) { state in
|
||||
state.transactionList = IdentifiedArrayOf(uniqueElements: transactionList)
|
||||
state.latestTransactionList = transactionList
|
||||
state.latestTranassctionId = id
|
||||
state.latestTransactionId = id
|
||||
state.transactionList[0].isMarkedAsRead = true
|
||||
|
||||
XCTAssertFalse(state.transactionList[0].isUnread)
|
||||
|
@ -596,11 +596,11 @@ class TransactionListTests: XCTestCase {
|
|||
let identifiedTransactionList = IdentifiedArrayOf(uniqueElements: transactionList)
|
||||
|
||||
let store = TestStore(
|
||||
initialState: TransactionListReducer.State(
|
||||
initialState: TransactionList.State(
|
||||
transactionList: identifiedTransactionList
|
||||
)
|
||||
) {
|
||||
TransactionListReducer()
|
||||
TransactionList()
|
||||
}
|
||||
|
||||
store.dependencies.mainQueue = .immediate
|
||||
|
@ -618,7 +618,7 @@ class TransactionListTests: XCTestCase {
|
|||
await store.receive(.updateTransactionList(transactionList)) { state in
|
||||
state.transactionList = IdentifiedArrayOf(uniqueElements: transactionList)
|
||||
state.latestTransactionList = transactionList
|
||||
state.latestTranassctionId = id
|
||||
state.latestTransactionId = id
|
||||
state.transactionList[0].isMarkedAsRead = true
|
||||
|
||||
XCTAssertFalse(state.transactionList[0].isUnread)
|
||||
|
|
Loading…
Reference in New Issue