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:
+36
-1
@@ -40,6 +40,7 @@ import androidx.compose.material.icons.filled.ArrowUpward
|
|||||||
import androidx.compose.material.icons.filled.Refresh
|
import androidx.compose.material.icons.filled.Refresh
|
||||||
import androidx.compose.material3.CircularProgressIndicator
|
import androidx.compose.material3.CircularProgressIndicator
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
|
import androidx.compose.material3.FilterChip
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
@@ -86,10 +87,11 @@ fun WalletTransactionsScreen(
|
|||||||
walletViewModel.fetchTransactions()
|
walletViewModel.fetchTransactions()
|
||||||
}
|
}
|
||||||
|
|
||||||
val transactions by walletViewModel.transactions.collectAsState()
|
val transactions by walletViewModel.filteredTransactions.collectAsState(emptyList())
|
||||||
val isLoading by walletViewModel.isLoading.collectAsState()
|
val isLoading by walletViewModel.isLoading.collectAsState()
|
||||||
val isLoadingMore by walletViewModel.isLoadingMore.collectAsState()
|
val isLoadingMore by walletViewModel.isLoadingMore.collectAsState()
|
||||||
val hasMore by walletViewModel.hasMoreTransactions.collectAsState()
|
val hasMore by walletViewModel.hasMoreTransactions.collectAsState()
|
||||||
|
val currentFilter by walletViewModel.transactionFilter.collectAsState()
|
||||||
|
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
|
|
||||||
@@ -166,6 +168,9 @@ fun WalletTransactionsScreen(
|
|||||||
modifier = Modifier.padding(padding),
|
modifier = Modifier.padding(padding),
|
||||||
state = listState,
|
state = listState,
|
||||||
) {
|
) {
|
||||||
|
item {
|
||||||
|
TransactionFilterRow(currentFilter) { walletViewModel.setTransactionFilter(it) }
|
||||||
|
}
|
||||||
items(transactions) { tx ->
|
items(transactions) { tx ->
|
||||||
TransactionItem(tx, accountViewModel, nav)
|
TransactionItem(tx, accountViewModel, nav)
|
||||||
HorizontalDivider()
|
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
|
@Composable
|
||||||
private fun TransactionItem(
|
private fun TransactionItem(
|
||||||
tx: NwcTransaction,
|
tx: NwcTransaction,
|
||||||
|
|||||||
+24
-1
@@ -39,6 +39,8 @@ import com.vitorpamplona.quartz.nip47WalletConnect.PayInvoiceSuccessResponse
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
sealed class SendState {
|
sealed class SendState {
|
||||||
@@ -70,6 +72,12 @@ sealed class ReceiveState {
|
|||||||
) : ReceiveState()
|
) : ReceiveState()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class TransactionFilter {
|
||||||
|
ALL,
|
||||||
|
ZAPS,
|
||||||
|
NON_ZAPS,
|
||||||
|
}
|
||||||
|
|
||||||
class WalletViewModel : ViewModel() {
|
class WalletViewModel : ViewModel() {
|
||||||
private var account: Account? = null
|
private var account: Account? = null
|
||||||
|
|
||||||
@@ -83,7 +91,18 @@ class WalletViewModel : ViewModel() {
|
|||||||
val walletAlias = _walletAlias.asStateFlow()
|
val walletAlias = _walletAlias.asStateFlow()
|
||||||
|
|
||||||
private val _transactions = MutableStateFlow<List<NwcTransaction>>(emptyList())
|
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)
|
private val _isLoading = MutableStateFlow(false)
|
||||||
val isLoading = _isLoading.asStateFlow()
|
val isLoading = _isLoading.asStateFlow()
|
||||||
@@ -336,4 +355,8 @@ class WalletViewModel : ViewModel() {
|
|||||||
fun clearError() {
|
fun clearError() {
|
||||||
_error.value = null
|
_error.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setTransactionFilter(filter: TransactionFilter) {
|
||||||
|
_transactionFilter.value = filter
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1260,6 +1260,9 @@
|
|||||||
<string name="wallet_incoming">Received</string>
|
<string name="wallet_incoming">Received</string>
|
||||||
<string name="wallet_outgoing">Sent</string>
|
<string name="wallet_outgoing">Sent</string>
|
||||||
<string name="wallet_refresh">Refresh</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_security_filters">Security Filters</string>
|
||||||
<string name="route_import_follows">Import Follows</string>
|
<string name="route_import_follows">Import Follows</string>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user