Closes #379 - `TransactionDetailView` is updated and instead of `ViewStore` it now has `Store`. `Store` is required to show alert. It's not possible with `ViewStore`. - There are three more actions added to `WalletEventsFlowAction`. These are used to handle the new alert. - Block explorer URL is changed to https://zcashblockexplorer.com. New URL scheme is derived from how URLs looks now when some transaction is opened.
This commit is contained in:
parent
8d45f3ba6e
commit
cc7b767a5a
|
@ -12,49 +12,51 @@ struct TransactionDetailView: View {
|
|||
}
|
||||
|
||||
var transaction: TransactionState
|
||||
var viewStore: WalletEventsFlowViewStore
|
||||
var store: WalletEventsFlowStore
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
header
|
||||
WithViewStore(store) { viewStore in
|
||||
ScrollView {
|
||||
header
|
||||
|
||||
switch transaction.status {
|
||||
case .paid(success: _):
|
||||
plainText("You sent \(transaction.zecAmount.decimalString()) ZEC")
|
||||
plainText("fee \(transaction.fee.decimalString()) ZEC", mark: .inactive)
|
||||
plainText("total amount \(transaction.totalAmount.decimalString()) ZEC", mark: .inactive)
|
||||
address(mark: .inactive)
|
||||
if let text = transaction.memo { memo(text, viewStore, mark: .highlight) }
|
||||
confirmed(mark: .success)
|
||||
case .pending:
|
||||
plainText("You are sending \(transaction.zecAmount.decimalString()) ZEC")
|
||||
plainText("Includes network fee \(transaction.fee.decimalString()) ZEC", mark: .inactive)
|
||||
plainText("total amount \(transaction.totalAmount.decimalString()) ZEC", mark: .inactive)
|
||||
if let text = transaction.memo { memo(text, viewStore, mark: .inactive) }
|
||||
confirming(mark: .highlight)
|
||||
case .received:
|
||||
plainText("You received \(transaction.zecAmount.decimalString()) ZEC")
|
||||
plainText("fee \(transaction.fee.decimalString()) ZEC")
|
||||
plainText("total amount \(transaction.totalAmount.decimalString()) ZEC")
|
||||
address(mark: .inactive)
|
||||
if let text = transaction.memo { memo(text, viewStore, mark: .highlight) }
|
||||
confirmed(mark: .success)
|
||||
case .failed:
|
||||
plainText("You DID NOT send \(transaction.zecAmount.decimalString()) ZEC", mark: .fail)
|
||||
plainText("Includes network fee \(transaction.fee.decimalString()) ZEC", mark: .inactive)
|
||||
plainText("total amount \(transaction.totalAmount.decimalString()) ZEC", mark: .inactive)
|
||||
if let text = transaction.memo { memo(text, viewStore, mark: .inactive) }
|
||||
if let errorMessage = transaction.errorMessage {
|
||||
plainTwoColumnText(left: "Failed", right: errorMessage, mark: .fail)
|
||||
switch transaction.status {
|
||||
case .paid(success: _):
|
||||
plainText("You sent \(transaction.zecAmount.decimalString()) ZEC")
|
||||
plainText("fee \(transaction.fee.decimalString()) ZEC", mark: .inactive)
|
||||
plainText("total amount \(transaction.totalAmount.decimalString()) ZEC", mark: .inactive)
|
||||
address(mark: .inactive, viewStore: viewStore)
|
||||
if let text = transaction.memo { memo(text, viewStore, mark: .highlight) }
|
||||
confirmed(mark: .success, viewStore: viewStore)
|
||||
case .pending:
|
||||
plainText("You are sending \(transaction.zecAmount.decimalString()) ZEC")
|
||||
plainText("Includes network fee \(transaction.fee.decimalString()) ZEC", mark: .inactive)
|
||||
plainText("total amount \(transaction.totalAmount.decimalString()) ZEC", mark: .inactive)
|
||||
if let text = transaction.memo { memo(text, viewStore, mark: .inactive) }
|
||||
confirming(mark: .highlight, viewStore: viewStore)
|
||||
case .received:
|
||||
plainText("You received \(transaction.zecAmount.decimalString()) ZEC")
|
||||
plainText("fee \(transaction.fee.decimalString()) ZEC")
|
||||
plainText("total amount \(transaction.totalAmount.decimalString()) ZEC")
|
||||
address(mark: .inactive, viewStore: viewStore)
|
||||
if let text = transaction.memo { memo(text, viewStore, mark: .highlight) }
|
||||
confirmed(mark: .success, viewStore: viewStore)
|
||||
case .failed:
|
||||
plainText("You DID NOT send \(transaction.zecAmount.decimalString()) ZEC", mark: .fail)
|
||||
plainText("Includes network fee \(transaction.fee.decimalString()) ZEC", mark: .inactive)
|
||||
plainText("total amount \(transaction.totalAmount.decimalString()) ZEC", mark: .inactive)
|
||||
if let text = transaction.memo { memo(text, viewStore, mark: .inactive) }
|
||||
if let errorMessage = transaction.errorMessage {
|
||||
plainTwoColumnText(left: "Failed", right: errorMessage, mark: .fail)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
footer
|
||||
Spacer()
|
||||
|
||||
footer
|
||||
}
|
||||
.applyScreenBackground()
|
||||
.navigationTitle("Transaction detail")
|
||||
}
|
||||
.applyScreenBackground()
|
||||
.navigationTitle("Transaction detail")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -92,7 +94,7 @@ extension TransactionDetailView {
|
|||
.transactionDetailRow(mark: mark)
|
||||
}
|
||||
|
||||
func address(mark: RowMark = .neutral) -> some View {
|
||||
func address(mark: RowMark = .neutral, viewStore: WalletEventsFlowViewStore) -> some View {
|
||||
Button {
|
||||
viewStore.send(.copyToPastboard(transaction.address))
|
||||
} label: {
|
||||
|
@ -137,7 +139,7 @@ extension TransactionDetailView {
|
|||
}
|
||||
}
|
||||
|
||||
func confirmed(mark: RowMark = .neutral) -> some View {
|
||||
func confirmed(mark: RowMark = .neutral, viewStore: WalletEventsFlowViewStore) -> some View {
|
||||
HStack {
|
||||
Text("Confirmed")
|
||||
Spacer()
|
||||
|
@ -146,7 +148,7 @@ extension TransactionDetailView {
|
|||
.transactionDetailRow(mark: mark)
|
||||
}
|
||||
|
||||
func confirming(mark: RowMark = .neutral) -> some View {
|
||||
func confirming(mark: RowMark = .neutral, viewStore: WalletEventsFlowViewStore) -> some View {
|
||||
HStack {
|
||||
Text("Confirming ~\(viewStore.requiredTransactionConfirmations)mins")
|
||||
Spacer()
|
||||
|
@ -156,31 +158,30 @@ extension TransactionDetailView {
|
|||
}
|
||||
|
||||
var footer: some View {
|
||||
VStack {
|
||||
Button {
|
||||
viewStore.send(.copyToPastboard(transaction.id))
|
||||
} label: {
|
||||
Text("txn: \(transaction.id)")
|
||||
.foregroundColor(Asset.Colors.Text.transactionDetailText.color)
|
||||
.font(.system(size: 14))
|
||||
.fontWeight(.thin)
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
.padding(.horizontal, 60)
|
||||
.padding(.vertical, 10)
|
||||
.background(Asset.Colors.BackgroundColors.numberedChip.color)
|
||||
.padding(.vertical, 30)
|
||||
}
|
||||
|
||||
Button { } label: {
|
||||
// TODO: Warn users that they will leave the App when they follow a Block explorer
|
||||
// https://github.com/zcash/secant-ios-wallet/issues/379
|
||||
if let viewOnlineURL = transaction.viewOnlineURL {
|
||||
Link("View online", destination: viewOnlineURL)
|
||||
WithViewStore(store) { viewStore in
|
||||
VStack {
|
||||
Button {
|
||||
viewStore.send(.copyToPastboard(transaction.id))
|
||||
} label: {
|
||||
Text("txn: \(transaction.id)")
|
||||
.foregroundColor(Asset.Colors.Text.transactionDetailText.color)
|
||||
.font(.system(size: 14))
|
||||
.fontWeight(.thin)
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
.padding(.horizontal, 60)
|
||||
.padding(.vertical, 10)
|
||||
.background(Asset.Colors.BackgroundColors.numberedChip.color)
|
||||
.padding(.vertical, 30)
|
||||
}
|
||||
|
||||
Button("View online") {
|
||||
viewStore.send(.warnBeforeLeavingApp(transaction.viewOnlineURL))
|
||||
}
|
||||
.activeButtonStyle
|
||||
.frame(height: 50)
|
||||
.padding(.horizontal, 30)
|
||||
}
|
||||
.activeButtonStyle
|
||||
.frame(height: 50)
|
||||
.padding(.horizontal, 30)
|
||||
.alert(self.store.scope(state: \.alert), dismiss: .dismissAlert)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -266,19 +267,7 @@ struct TransactionDetail_Previews: PreviewProvider {
|
|||
timestamp: 1234567,
|
||||
zecAmount: Zatoshi(25_000_000)
|
||||
),
|
||||
viewStore: ViewStore(
|
||||
WalletEventsFlowStore(
|
||||
initialState: .placeHolder,
|
||||
reducer: .default,
|
||||
environment:
|
||||
WalletEventsFlowEnvironment(
|
||||
pasteboard: .test,
|
||||
scheduler: DispatchQueue.main.eraseToAnyScheduler(),
|
||||
SDKSynchronizer: MockWrappedSDKSynchronizer(),
|
||||
zcashSDKEnvironment: .testnet
|
||||
)
|
||||
)
|
||||
)
|
||||
store: WalletEventsFlowStore.placeholder
|
||||
)
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,7 @@ struct WalletEventsFlowState: Equatable {
|
|||
|
||||
var route: Route?
|
||||
|
||||
@BindableState var alert: AlertState<WalletEventsFlowAction>?
|
||||
var latestMinedHeight: BlockHeight?
|
||||
var isScrollable = false
|
||||
var requiredTransactionConfirmations = 0
|
||||
|
@ -28,12 +29,15 @@ struct WalletEventsFlowState: Equatable {
|
|||
|
||||
enum WalletEventsFlowAction: Equatable {
|
||||
case copyToPastboard(String)
|
||||
case dismissAlert
|
||||
case onAppear
|
||||
case onDisappear
|
||||
case openBlockExplorer(URL?)
|
||||
case updateRoute(WalletEventsFlowState.Route?)
|
||||
case replyTo(String)
|
||||
case synchronizerStateChanged(WrappedSDKSynchronizerState)
|
||||
case updateWalletEvents([WalletEvent])
|
||||
case warnBeforeLeavingApp(URL?)
|
||||
}
|
||||
|
||||
// MARK: - Environment
|
||||
|
@ -100,6 +104,35 @@ extension WalletEventsFlowReducer {
|
|||
|
||||
case .replyTo(let address):
|
||||
return .none
|
||||
|
||||
case .dismissAlert:
|
||||
state.alert = nil
|
||||
return .none
|
||||
|
||||
case .warnBeforeLeavingApp(let blockExplorerURL):
|
||||
state.alert = AlertState(
|
||||
title: TextState("You are exiting your wallet"),
|
||||
message: TextState("""
|
||||
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?
|
||||
"""),
|
||||
primaryButton: .cancel(
|
||||
TextState("NEVERMIND"),
|
||||
action: .send(.dismissAlert)
|
||||
),
|
||||
secondaryButton: .default(
|
||||
TextState("SEE TX ONLINE"),
|
||||
action: .send(.openBlockExplorer(blockExplorerURL))
|
||||
)
|
||||
)
|
||||
return .none
|
||||
|
||||
case .openBlockExplorer(let blockExplorerURL):
|
||||
state.alert = nil
|
||||
if let url = blockExplorerURL {
|
||||
UIApplication.shared.open(url, options: [:], completionHandler: nil)
|
||||
}
|
||||
return .none
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ struct WalletEventsFlowView: View {
|
|||
)
|
||||
.onDisappear(perform: { viewStore.send(.onDisappear) })
|
||||
.navigationLinkEmpty(isActive: viewStore.bindingForSelectedWalletEvent(viewStore.selectedWalletEvent)) {
|
||||
viewStore.selectedWalletEvent?.detailView(viewStore)
|
||||
viewStore.selectedWalletEvent?.detailView(store)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ struct TransactionState: Equatable, Identifiable {
|
|||
var date: Date { Date(timeIntervalSince1970: timestamp) }
|
||||
var totalAmount: Zatoshi { Zatoshi(zecAmount.amount + fee.amount) }
|
||||
var viewOnlineURL: URL? {
|
||||
URL(string: "https://blockchair.com/zcash/transaction/\(id)")
|
||||
URL(string: "https://zcashblockexplorer.com/transactions/\(id)")
|
||||
}
|
||||
|
||||
func confirmationsWith(_ latestMinedHeight: BlockHeight?) -> BlockHeight {
|
||||
|
|
|
@ -52,13 +52,13 @@ extension WalletEvent {
|
|||
// MARK: - Details
|
||||
|
||||
extension WalletEvent {
|
||||
@ViewBuilder func detailView(_ viewStore: WalletEventsFlowViewStore) -> some View {
|
||||
@ViewBuilder func detailView(_ store: WalletEventsFlowStore) -> some View {
|
||||
switch state {
|
||||
case .send(let transaction),
|
||||
.pending(let transaction),
|
||||
.received(let transaction),
|
||||
.failed(let transaction):
|
||||
TransactionDetailView(transaction: transaction, viewStore: viewStore)
|
||||
TransactionDetailView(transaction: transaction, store: store)
|
||||
case .shielded(let zatoshi):
|
||||
// TODO: implement design once shielding is supported, issue 390
|
||||
// https://github.com/zcash/secant-ios-wallet/issues/390
|
||||
|
|
|
@ -116,7 +116,7 @@ class WalletEventsSnapshotTests: XCTestCase {
|
|||
|
||||
addAttachments(
|
||||
name: "\(#function)_WalletEventDetail",
|
||||
TransactionDetailView(transaction: transaction, viewStore: ViewStore(walletEventsStore))
|
||||
TransactionDetailView(transaction: transaction, store: walletEventsStore)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -173,7 +173,7 @@ class WalletEventsSnapshotTests: XCTestCase {
|
|||
|
||||
addAttachments(
|
||||
name: "\(#function)_WalletEventDetail",
|
||||
TransactionDetailView(transaction: transaction, viewStore: ViewStore(walletEventsStore))
|
||||
TransactionDetailView(transaction: transaction, store: walletEventsStore)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -235,7 +235,7 @@ class WalletEventsSnapshotTests: XCTestCase {
|
|||
|
||||
addAttachments(
|
||||
name: "\(#function)_WalletEventDetail",
|
||||
TransactionDetailView(transaction: transaction, viewStore: ViewStore(walletEventsStore))
|
||||
TransactionDetailView(transaction: transaction, store: walletEventsStore)
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -293,7 +293,7 @@ class WalletEventsSnapshotTests: XCTestCase {
|
|||
|
||||
addAttachments(
|
||||
name: "\(#function)_WalletEventDetail",
|
||||
TransactionDetailView(transaction: transaction, viewStore: ViewStore(walletEventsStore))
|
||||
TransactionDetailView(transaction: transaction, store: walletEventsStore)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue