fix: keep filter chips visible on empty onchain transactions list

When a filter (e.g. Non-Zaps) excluded every loaded transaction, the
empty-state composable replaced the whole screen body — including the
filter chip row — so the user couldn't switch back to All or Zaps
without leaving the screen. Expose `hasAnyTransactions` from the
ViewModel so the screen distinguishes "no chain rows at all" from "no
rows for this filter": the chips stay rendered as long as any
transaction has loaded, and the LazyColumn shows a per-filter empty
message inline beneath them. Also drop the bc1 address header from the
list since the screen title already identifies the wallet.
This commit is contained in:
Claude
2026-05-19 16:18:33 +00:00
parent 1afa86cb26
commit cb6e1fe696
3 changed files with 42 additions and 27 deletions
@@ -97,6 +97,7 @@ fun OnchainTransactionsScreen(
)
val transactions by viewModel.filteredTransactions.collectAsState()
val hasAnyTransactions by viewModel.hasAnyTransactions.collectAsState()
val isLoading by viewModel.isLoading.collectAsState()
val isLoadingMore by viewModel.isLoadingMore.collectAsState()
val hasMore by viewModel.hasMoreTransactions.collectAsState()
@@ -150,7 +151,7 @@ fun OnchainTransactionsScreen(
address == null -> {
EmptyMessage(padding, stringRes(R.string.wallet_onchain_no_address))
}
isLoading && transactions.isEmpty() -> {
isLoading && !hasAnyTransactions -> {
Column(
modifier =
Modifier
@@ -167,13 +168,13 @@ fun OnchainTransactionsScreen(
)
}
}
error != null && transactions.isEmpty() -> {
error != null && !hasAnyTransactions -> {
EmptyMessage(
padding,
error ?: stringRes(R.string.wallet_onchain_no_backend),
)
}
transactions.isEmpty() -> {
!hasAnyTransactions -> {
EmptyMessage(padding, stringRes(R.string.wallet_no_transactions))
}
else -> {
@@ -182,18 +183,35 @@ fun OnchainTransactionsScreen(
modifier = Modifier.padding(padding),
state = listState,
) {
item { AddressHeader(address) }
item {
TransactionFilterRow(currentFilter) { viewModel.setTransactionFilter(it) }
}
items(transactions, key = { it.tx.txid }) { txView ->
OnchainTransactionItem(
view = txView,
accountViewModel = accountViewModel,
nav = nav,
onClick = { handleTxClick(txView, nav, uriHandler) },
)
HorizontalDivider()
if (transactions.isEmpty()) {
item {
Column(
modifier =
Modifier
.fillMaxWidth()
.padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Text(
stringRes(R.string.wallet_no_transactions_for_filter),
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
}
} else {
items(transactions, key = { it.tx.txid }) { txView ->
OnchainTransactionItem(
view = txView,
accountViewModel = accountViewModel,
nav = nav,
onClick = { handleTxClick(txView, nav, uriHandler) },
)
HorizontalDivider()
}
}
if (isLoadingMore) {
item {
@@ -236,21 +254,6 @@ private fun EmptyMessage(
}
}
@Composable
private fun AddressHeader(address: String?) {
if (address.isNullOrBlank()) return
Column(modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)) {
Text(
text = address,
style = MaterialTheme.typography.bodySmall,
fontFamily = FontFamily.Monospace,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
}
@Composable
private fun TransactionFilterRow(
currentFilter: TransactionFilter,
@@ -103,6 +103,17 @@ class OnchainTransactionsViewModel : ViewModel() {
}
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
/**
* Whether the unfiltered chain-side list has any rows. Drives whether the
* filter chips stay on screen — once we've loaded at least one transaction
* the chips should remain visible even if the current filter excludes
* everything, so the user can switch filters without the row disappearing.
*/
val hasAnyTransactions: StateFlow<Boolean> =
chainTxs
.map { it.isNotEmpty() }
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), false)
private val _isLoading = MutableStateFlow(false)
val isLoading = _isLoading.asStateFlow()
+1
View File
@@ -1823,6 +1823,7 @@
<string name="wallet_creating_invoice">Creating invoice…</string>
<string name="wallet_copy_invoice">Copy Invoice</string>
<string name="wallet_no_transactions">No transactions yet</string>
<string name="wallet_no_transactions_for_filter">No transactions match this filter</string>
<string name="wallet_loading">Loading…</string>
<string name="wallet_incoming">Received</string>
<string name="wallet_outgoing">Sent</string>