[#937] Shielded transaction UI

- The TransactionState has been extended to handle shielding
- The UI for the transaction row has been extended to handle right states for all types of transactions, newly for shielding, shielded and shielding failed

[#937] Shielded transaction UI

- Rebased and updated to the latest API

[#937] Shielded transaction UI

- WIP

[#937] Shielded transaction UI

- Shileding UI updated to show amount the same way as sent/received
- Shielding UI updated to show ID

[#937] Shielded transaction UI

- Duplicated code is removed, happened probably due to some rebases

[#937] Shielded transaction UI

- Code cleanup
This commit is contained in:
Lukas Korba 2024-04-23 15:34:28 +02:00
parent eabdd22634
commit 16d51ea716
9 changed files with 127 additions and 94 deletions

View File

@ -240,81 +240,6 @@ public struct TabsView: View {
}
}
}
.overlayPreferenceValue(ExchangeRateFeaturePreferenceKey.self) { preferences in
if viewStore.isRateEducationEnabled {
GeometryReader { geometry in
preferences.map {
VStack(alignment: .leading, spacing: 0) {
HStack(alignment: .top, spacing: 0) {
Asset.Assets.coinsSwap.image
.renderingMode(.template)
.resizable()
.frame(width: 20, height: 20)
.foregroundColor(Asset.Colors.CurrencyConversion.optionTint.color)
.padding(10)
.background {
Circle()
.fill(Asset.Colors.CurrencyConversion.optionBcg.color)
}
.padding(.trailing, 16)
VStack(alignment: .leading, spacing: 5) {
Text(L10n.CurrencyConversion.cardTitle)
.font(.custom(FontFamily.Inter.regular.name, size: 14))
.foregroundColor(Asset.Colors.CurrencyConversion.tertiary.color)
Text(L10n.CurrencyConversion.title)
.font(.custom(FontFamily.Inter.semiBold.name, size: 16))
.foregroundColor(Asset.Colors.CurrencyConversion.primary.color)
.lineLimit(nil)
}
.padding(.trailing, 16)
Spacer()
Button {
viewStore.send(.currencyConversionCloseTapped)
} label: {
Asset.Assets.buttonCloseX.image
.renderingMode(.template)
.resizable()
.frame(width: 20, height: 20)
.foregroundColor(Asset.Colors.CurrencyConversion.Card.close.color)
}
.padding(20)
.offset(x: 20, y: -20)
}
Button {
viewStore.send(.updateDestination(.currencyConversionSetup))
} label: {
Text(L10n.CurrencyConversion.cardButton)
.font(.custom(FontFamily.Inter.semiBold.name, size: 16))
.foregroundColor(Asset.Colors.CurrencyConversion.primary.color)
.frame(height: 24)
.frame(maxWidth: .infinity)
.padding(.vertical, 12)
.background {
RoundedRectangle(cornerRadius: 12)
.fill(Asset.Colors.CurrencyConversion.btnPrimaryDisabled.color)
}
}
}
.padding(24)
.background {
RoundedRectangle(cornerRadius: 12)
.fill(Asset.Colors.CurrencyConversion.Card.bcg.color)
.background {
RoundedRectangle(cornerRadius: 12)
.stroke(Asset.Colors.CurrencyConversion.Card.outline.color)
}
}
.frame(width: geometry.size.width - 40)
.offset(x: 20, y: geometry[$0].minY + geometry[$0].height)
}
}
}
}
}
}
}

View File

@ -41,11 +41,17 @@ struct TransactionHeaderView: View {
titleText()
addressArea()
if !transaction.isShieldingTransaction {
addressArea()
Spacer(minLength: 60)
balanceView()
Spacer(minLength: 60)
balanceView()
} else {
Spacer()
balanceView()
}
}
.padding(.trailing, 30)
@ -125,7 +131,7 @@ struct TransactionHeaderView: View {
fontName: FontFamily.Inter.regular.name,
mostSignificantFontSize: 12,
leastSignificantFontSize: 8,
prefixSymbol: transaction.isSpending ? .minus : .plus,
prefixSymbol: (transaction.isSpending || transaction.isShieldingTransaction) ? .minus : .plus,
format: transaction.isExpanded ? .expanded : .abbreviated,
strikethrough: transaction.status == .failed,
couldBeHidden: true
@ -145,6 +151,13 @@ extension TransactionHeaderView {
.frame(width: 20, height: 16)
.foregroundColor(Asset.Colors.primary.color)
case .shielded, .shielding:
Asset.Assets.shieldedFunds.image
.renderingMode(.template)
.resizable()
.frame(width: 20, height: 19)
.foregroundColor(Asset.Colors.primary.color)
case .received, .receiving:
if transaction.isUnread {
Asset.Assets.flyReceivedFilled.image
@ -195,7 +208,13 @@ extension TransactionHeaderView {
transaction: .mockedSending
)
.listRowSeparator(.hidden)
TransactionHeaderView(
store: .placeholder,
transaction: .mockedShielded
)
.listRowSeparator(.hidden)
TransactionHeaderView(
store: .placeholder,
transaction: .mockedReceiving

View File

@ -52,7 +52,7 @@ public struct TransactionRowView: View {
if transaction.isExpanded {
Group {
if !transaction.isTransparentRecipient {
if !transaction.isTransparentRecipient && !transaction.isShieldingTransaction {
MessageView(
store: store,
messages: transaction.textMemos,
@ -65,8 +65,8 @@ public struct TransactionRowView: View {
store: store,
transaction: transaction
)
if transaction.isSpending {
if transaction.isSpending || transaction.isShieldingTransaction {
TransactionFeeView(fee: transaction.fee ?? .zero)
.padding(.vertical, 10)
}
@ -94,6 +94,22 @@ public struct TransactionRowView: View {
.listRowSeparator(.hidden)
.listRowInsets(EdgeInsets())
TransactionRowView(
store: .placeholder,
transaction: .mockedShielded,
tokenName: "ZEC"
)
.listRowSeparator(.hidden)
.listRowInsets(EdgeInsets())
TransactionRowView(
store: .placeholder,
transaction: .mockedShieldedExpanded,
tokenName: "ZEC"
)
.listRowSeparator(.hidden)
.listRowInsets(EdgeInsets())
TransactionRowView(
store: .placeholder,
transaction: .mockedReceived,

View File

@ -737,6 +737,8 @@ public enum L10n {
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")
/// Shielded Funds Failed
public static let failedShieldedFunds = L10n.tr("Localizable", "transaction.failedShieldedFunds", fallback: "Shielded Funds Failed")
/// Received
public static let received = L10n.tr("Localizable", "transaction.received", fallback: "Received")
/// Receiving...
@ -745,6 +747,10 @@ public enum L10n {
public static let sending = L10n.tr("Localizable", "transaction.sending", fallback: "Sending...")
/// Sent
public static let sent = L10n.tr("Localizable", "transaction.sent", fallback: "Sent")
/// Shielded Funds
public static let shieldedFunds = L10n.tr("Localizable", "transaction.shieldedFunds", fallback: "Shielded Funds")
/// Shielding Funds
public static let shieldingFunds = L10n.tr("Localizable", "transaction.shieldingFunds", fallback: "Shielding Funds")
}
public enum TransactionList {
/// Collapse transaction

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "shieldedFunds.png",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

View File

@ -229,6 +229,9 @@ Sharing this private data is irrevocable — once you have shared this private d
"transaction.received" = "Received";
"transaction.failedSend" = "Send failed";
"transaction.failedReceive" = "Receive failed";
"transaction.shieldingFunds" = "Shielding Funds";
"transaction.shieldedFunds" = "Shielded Funds";
"transaction.failedShieldedFunds" = "Shielded Funds Failed";
// MARK: - Local authentication
"localAuthentication.reason" = "The Following content requires authentication.";

View File

@ -51,6 +51,7 @@ public enum Asset {
public static let share = ImageAsset(name: "share")
public static let shield = ImageAsset(name: "shield")
public static let shieldTick = ImageAsset(name: "shieldTick")
public static let shieldedFunds = ImageAsset(name: "shieldedFunds")
public static let surroundedShield = ImageAsset(name: "surroundedShield")
public static let tooltip = ImageAsset(name: "tooltip")
public static let torchOff = ImageAsset(name: "torchOff")

View File

@ -18,6 +18,8 @@ public struct TransactionState: Equatable, Identifiable {
case received
case receiving
case sending
case shielding
case shielded
}
public var errorMessage: String?
@ -28,6 +30,7 @@ public struct TransactionState: Equatable, Identifiable {
public var shielded = true
public var zAddress: String?
public var isSentTransaction: Bool
public var isShieldingTransaction: Bool
public var isTransparentRecipient: Bool
public var fee: Zatoshi?
@ -47,7 +50,7 @@ public struct TransactionState: Equatable, Identifiable {
public var balanceColor: Color {
status == .failed
? Asset.Colors.error.color
: isSpending
: (isSpending || isShieldingTransaction)
? Asset.Colors.error.color
: Asset.Colors.primary.color
}
@ -98,6 +101,7 @@ public struct TransactionState: Equatable, Identifiable {
public var title: String {
switch status {
case .failed:
// TODO: failed shileded is not covered!
return isSentTransaction
? L10n.Transaction.failedSend
: L10n.Transaction.failedReceive
@ -109,6 +113,10 @@ public struct TransactionState: Equatable, Identifiable {
return L10n.Transaction.receiving
case .sending:
return L10n.Transaction.sending
case .shielding:
return L10n.Transaction.shieldingFunds
case .shielded:
return L10n.Transaction.shieldedFunds
}
}
@ -132,6 +140,10 @@ public struct TransactionState: Equatable, Identifiable {
return true
case .sending:
return true
case .shielded:
return false
case .shielding:
return true
}
}
@ -142,6 +154,8 @@ public struct TransactionState: Equatable, Identifiable {
return true
case .received, .receiving:
return false
case .shielded, .shielding:
return false
case .failed:
return isSentTransaction
}
@ -184,6 +198,7 @@ public struct TransactionState: Equatable, Identifiable {
timestamp: TimeInterval? = nil,
zecAmount: Zatoshi,
isSentTransaction: Bool = false,
isShieldingTransaction: Bool = false,
isTransparentRecipient: Bool = false,
isAddressExpanded: Bool = false,
isExpanded: Bool = false,
@ -203,6 +218,7 @@ public struct TransactionState: Equatable, Identifiable {
self.timestamp = timestamp
self.zecAmount = zecAmount
self.isSentTransaction = isSentTransaction
self.isShieldingTransaction = isShieldingTransaction
self.isTransparentRecipient = isTransparentRecipient
self.isAddressExpanded = isAddressExpanded
self.isExpanded = isExpanded
@ -238,8 +254,9 @@ extension TransactionState {
fee = transaction.fee
id = transaction.rawID.toHexStringTxId()
timestamp = transaction.blockTime
zecAmount = transaction.isSentTransaction ? Zatoshi(-transaction.value.amount) : transaction.value
isSentTransaction = transaction.isSentTransaction
isShieldingTransaction = transaction.isShielding
zecAmount = isSentTransaction ? Zatoshi(-transaction.value.amount) : transaction.value
isTransparentRecipient = false
isAddressExpanded = false
isExpanded = false
@ -247,18 +264,24 @@ extension TransactionState {
memoCount = transaction.memoCount
self.memos = memos
if transaction.isShielding {
print("aa")
}
// 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)
// failed check
if let expiryHeight = transaction.expiryHeight, expiryHeight <= latestBlockHeight && minedHeight == nil {
status = .failed
} else if isShieldingTransaction {
status = isPending ? .shielding : .shielded
} 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
@ -378,6 +401,34 @@ extension TransactionState {
isExpanded: false,
isIdExpanded: false
)
public static let mockedShielded = TransactionState(
minedHeight: BlockHeight(1),
zAddress: "utest1vergg5jkp4xy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzjanqtl8uqp5vln3zyy246ejtx86vqftp73j7jg9099jxafyjhfm6u956j3",
fee: Zatoshi(10_000),
id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
status: .shielded,
timestamp: 1699290621,
zecAmount: Zatoshi(25_000_000),
isShieldingTransaction: true,
isAddressExpanded: false,
isExpanded: false,
isIdExpanded: false
)
public static let mockedShieldedExpanded = TransactionState(
minedHeight: BlockHeight(1),
zAddress: "utest1vergg5jkp4xy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzjanqtl8uqp5vln3zyy246ejtx86vqftp73j7jg9099jxafyjhfm6u956j3",
fee: Zatoshi(10_000),
id: "t1vergg5jkp4wy8sqfasw6s5zkdpnxvfxlxh35uuc3me7dp596y2r05t6dv9htwe3pf8ksrfr8ksca2lskzja",
status: .shielded,
timestamp: 1699290621,
zecAmount: Zatoshi(25_000_000),
isShieldingTransaction: true,
isAddressExpanded: false,
isExpanded: true,
isIdExpanded: false
)
}
public struct TransactionStateMockHelper {