feat: add transaction filter for zaps vs non-zaps on wallet screen

Adds filter chips (All / Zaps / Non-Zaps) to the transactions list.
Zap transactions are identified by having nostr metadata. The filter
is applied client-side via a combined Flow in WalletViewModel.

https://claude.ai/code/session_01CdNY7qYDRHJr1jhAFvVgzj
This commit is contained in:
Claude
2026-03-17 02:55:16 +00:00
parent c3f7e491a6
commit 11950dc4f4
3 changed files with 63 additions and 2 deletions
@@ -40,6 +40,7 @@ import androidx.compose.material.icons.filled.ArrowUpward
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilterChip
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
@@ -86,10 +87,11 @@ fun WalletTransactionsScreen(
walletViewModel.fetchTransactions()
}
val transactions by walletViewModel.transactions.collectAsState()
val transactions by walletViewModel.filteredTransactions.collectAsState(emptyList())
val isLoading by walletViewModel.isLoading.collectAsState()
val isLoadingMore by walletViewModel.isLoadingMore.collectAsState()
val hasMore by walletViewModel.hasMoreTransactions.collectAsState()
val currentFilter by walletViewModel.transactionFilter.collectAsState()
val listState = rememberLazyListState()
@@ -166,6 +168,9 @@ fun WalletTransactionsScreen(
modifier = Modifier.padding(padding),
state = listState,
) {
item {
TransactionFilterRow(currentFilter) { walletViewModel.setTransactionFilter(it) }
}
items(transactions) { tx ->
TransactionItem(tx, accountViewModel, nav)
HorizontalDivider()
@@ -188,6 +193,36 @@ fun WalletTransactionsScreen(
}
}
@Composable
private fun TransactionFilterRow(
currentFilter: TransactionFilter,
onFilterSelected: (TransactionFilter) -> Unit,
) {
Row(
modifier =
Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp),
) {
FilterChip(
selected = currentFilter == TransactionFilter.ALL,
onClick = { onFilterSelected(TransactionFilter.ALL) },
label = { Text(stringRes(R.string.wallet_filter_all)) },
)
FilterChip(
selected = currentFilter == TransactionFilter.ZAPS,
onClick = { onFilterSelected(TransactionFilter.ZAPS) },
label = { Text(stringRes(R.string.wallet_filter_zaps)) },
)
FilterChip(
selected = currentFilter == TransactionFilter.NON_ZAPS,
onClick = { onFilterSelected(TransactionFilter.NON_ZAPS) },
label = { Text(stringRes(R.string.wallet_filter_non_zaps)) },
)
}
}
@Composable
private fun TransactionItem(
tx: NwcTransaction,
@@ -39,6 +39,8 @@ import com.vitorpamplona.quartz.nip47WalletConnect.PayInvoiceSuccessResponse
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
sealed class SendState {
@@ -70,6 +72,12 @@ sealed class ReceiveState {
) : ReceiveState()
}
enum class TransactionFilter {
ALL,
ZAPS,
NON_ZAPS,
}
class WalletViewModel : ViewModel() {
private var account: Account? = null
@@ -83,7 +91,18 @@ class WalletViewModel : ViewModel() {
val walletAlias = _walletAlias.asStateFlow()
private val _transactions = MutableStateFlow<List<NwcTransaction>>(emptyList())
val transactions = _transactions.asStateFlow()
private val _transactionFilter = MutableStateFlow(TransactionFilter.ALL)
val transactionFilter = _transactionFilter.asStateFlow()
val filteredTransactions =
combine(_transactions, _transactionFilter) { txs, filter ->
when (filter) {
TransactionFilter.ALL -> txs
TransactionFilter.ZAPS -> txs.filter { it.parsedMetadata()?.nostr != null }
TransactionFilter.NON_ZAPS -> txs.filter { it.parsedMetadata()?.nostr == null }
}
}
private val _isLoading = MutableStateFlow(false)
val isLoading = _isLoading.asStateFlow()
@@ -336,4 +355,8 @@ class WalletViewModel : ViewModel() {
fun clearError() {
_error.value = null
}
fun setTransactionFilter(filter: TransactionFilter) {
_transactionFilter.value = filter
}
}
+3
View File
@@ -1260,6 +1260,9 @@
<string name="wallet_incoming">Received</string>
<string name="wallet_outgoing">Sent</string>
<string name="wallet_refresh">Refresh</string>
<string name="wallet_filter_all">All</string>
<string name="wallet_filter_zaps">Zaps</string>
<string name="wallet_filter_non_zaps">Non-Zaps</string>
<string name="route_security_filters">Security Filters</string>
<string name="route_import_follows">Import Follows</string>