fix(wallet): keep self-send legs distinct + deterministic tx order
A self-payment surfaces as two transaction records sharing one payment hash — one outgoing, one incoming. Port the iOS WalletStore.dedupTransactions hardening so they render correctly: - Add dedupTransactions(): drop exact (paymentHash, type) repeats the backend returns, and sort newest-first with the incoming "received" leg above its outgoing "sent" leg on a timestamp tie. Apply on initial load, re-enrich, and load-more. - Key the transaction LazyColumn by "paymentHash|type" so the two legs of a self-send keep distinct identities and both render (mirrors iOS WalletTransaction.id).
This commit is contained in:
@@ -2319,7 +2319,11 @@ private fun TransactionHistoryContent(
|
||||
}
|
||||
else -> {
|
||||
LazyColumn {
|
||||
items(transactions) { tx ->
|
||||
// Key by paymentHash + direction so a self-send's two legs
|
||||
// (same hash, opposite type) keep distinct identities and
|
||||
// both render. The list is deduped by (paymentHash, type)
|
||||
// upstream, so these keys are unique.
|
||||
items(transactions, key = { "${it.paymentHash}|${it.type}" }) { tx ->
|
||||
TransactionRow(tx, profileLookup, displayMode)
|
||||
HorizontalDivider(
|
||||
modifier = Modifier.padding(horizontal = 16.dp),
|
||||
|
||||
@@ -1249,7 +1249,7 @@ class WalletViewModel(
|
||||
}
|
||||
mapped.fold(
|
||||
onSuccess = { txs ->
|
||||
_transactions.value = txs
|
||||
_transactions.value = dedupTransactions(txs)
|
||||
_hasMoreTransactions.value = txs.size >= 50
|
||||
requestMissingProfiles(txs)
|
||||
},
|
||||
@@ -1266,13 +1266,29 @@ class WalletViewModel(
|
||||
enrichTransactions(current, zapMaps)
|
||||
}
|
||||
if (reEnriched != current) {
|
||||
_transactions.value = reEnriched
|
||||
_transactions.value = dedupTransactions(reEnriched)
|
||||
requestMissingProfiles(reEnriched)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collapse exact backend duplicates and order transactions deterministically.
|
||||
*
|
||||
* A self-send surfaces as two records sharing one payment hash — one
|
||||
* outgoing, one incoming. Both legs must survive (we only drop repeats of
|
||||
* the same (paymentHash, type)), and on a timestamp tie the incoming
|
||||
* "received" leg sorts above its outgoing "sent" leg so the conclusion of
|
||||
* the payment reads on top. Mirrors iOS WalletStore.dedupTransactions.
|
||||
*/
|
||||
private fun dedupTransactions(txs: List<WalletTransaction>): List<WalletTransaction> =
|
||||
txs.distinctBy { it.paymentHash to it.type }
|
||||
.sortedWith(
|
||||
compareByDescending<WalletTransaction> { it.settledAt ?: it.createdAt }
|
||||
.thenBy { if (it.type == "incoming") 0 else 1 }
|
||||
)
|
||||
|
||||
private fun enrichTransactions(
|
||||
txs: List<WalletTransaction>,
|
||||
zapMaps: EventRepository.ZapCounterpartyMaps
|
||||
@@ -1325,7 +1341,7 @@ class WalletViewModel(
|
||||
}
|
||||
mapped.fold(
|
||||
onSuccess = { txs ->
|
||||
_transactions.value = _transactions.value + txs
|
||||
_transactions.value = dedupTransactions(_transactions.value + txs)
|
||||
_hasMoreTransactions.value = txs.size >= 50
|
||||
val missing = txs.mapNotNull { it.counterpartyPubkey }
|
||||
.distinct()
|
||||
|
||||
Reference in New Issue
Block a user