feat: auto-load wallet transactions on scroll
Add infinite scroll pagination to the wallet transactions screen. When the user scrolls within 5 items of the bottom, the next page of transactions is automatically fetched and appended. Uses the existing NWC list_transactions limit/offset support and total_count to know when all transactions have been loaded. https://claude.ai/code/session_01CdNY7qYDRHJr1jhAFvVgzj
This commit is contained in:
+34
@@ -32,6 +32,7 @@ import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.ArrowDownward
|
||||
@@ -49,6 +50,7 @@ import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -86,6 +88,24 @@ fun WalletTransactionsScreen(
|
||||
|
||||
val transactions by walletViewModel.transactions.collectAsState()
|
||||
val isLoading by walletViewModel.isLoading.collectAsState()
|
||||
val isLoadingMore by walletViewModel.isLoadingMore.collectAsState()
|
||||
val hasMore by walletViewModel.hasMoreTransactions.collectAsState()
|
||||
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
val shouldLoadMore by remember {
|
||||
derivedStateOf {
|
||||
val lastVisibleIndex = listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: 0
|
||||
val totalItems = listState.layoutInfo.totalItemsCount
|
||||
lastVisibleIndex >= totalItems - 5 && !isLoadingMore && hasMore && transactions.isNotEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(shouldLoadMore) {
|
||||
if (shouldLoadMore) {
|
||||
walletViewModel.loadMoreTransactions()
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
@@ -144,11 +164,25 @@ fun WalletTransactionsScreen(
|
||||
} else {
|
||||
LazyColumn(
|
||||
modifier = Modifier.padding(padding),
|
||||
state = listState,
|
||||
) {
|
||||
items(transactions) { tx ->
|
||||
TransactionItem(tx, accountViewModel, nav)
|
||||
HorizontalDivider()
|
||||
}
|
||||
if (isLoadingMore) {
|
||||
item {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
CircularProgressIndicator(modifier = Modifier.size(24.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+63
-7
@@ -88,6 +88,14 @@ class WalletViewModel : ViewModel() {
|
||||
private val _isLoading = MutableStateFlow(false)
|
||||
val isLoading = _isLoading.asStateFlow()
|
||||
|
||||
private val _isLoadingMore = MutableStateFlow(false)
|
||||
val isLoadingMore = _isLoadingMore.asStateFlow()
|
||||
|
||||
private val _hasMoreTransactions = MutableStateFlow(true)
|
||||
val hasMoreTransactions = _hasMoreTransactions.asStateFlow()
|
||||
|
||||
private val pageSize = 20
|
||||
|
||||
private val _error = MutableStateFlow<String?>(null)
|
||||
val error = _error.asStateFlow()
|
||||
|
||||
@@ -153,24 +161,30 @@ class WalletViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchTransactions(
|
||||
limit: Int = 20,
|
||||
offset: Int = 0,
|
||||
) {
|
||||
fun fetchTransactions() {
|
||||
val acc = account ?: return
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
_isLoading.value = true
|
||||
_hasMoreTransactions.value = true
|
||||
try {
|
||||
acc.sendNwcRequest(
|
||||
ListTransactionsMethod.create(
|
||||
limit = limit,
|
||||
offset = offset,
|
||||
limit = pageSize,
|
||||
offset = 0,
|
||||
unpaid = false,
|
||||
),
|
||||
) { response ->
|
||||
when (response) {
|
||||
is ListTransactionsSuccessResponse -> {
|
||||
_transactions.value = response.result?.transactions ?: emptyList()
|
||||
val txs = response.result?.transactions ?: emptyList()
|
||||
_transactions.value = txs
|
||||
val totalCount = response.result?.total_count
|
||||
_hasMoreTransactions.value =
|
||||
if (totalCount != null) {
|
||||
txs.size < totalCount
|
||||
} else {
|
||||
txs.size >= pageSize
|
||||
}
|
||||
}
|
||||
|
||||
is NwcErrorResponse -> {
|
||||
@@ -188,6 +202,48 @@ class WalletViewModel : ViewModel() {
|
||||
}
|
||||
}
|
||||
|
||||
fun loadMoreTransactions() {
|
||||
if (_isLoadingMore.value || !_hasMoreTransactions.value) return
|
||||
val acc = account ?: return
|
||||
val currentOffset = _transactions.value.size
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
_isLoadingMore.value = true
|
||||
try {
|
||||
acc.sendNwcRequest(
|
||||
ListTransactionsMethod.create(
|
||||
limit = pageSize,
|
||||
offset = currentOffset,
|
||||
unpaid = false,
|
||||
),
|
||||
) { response ->
|
||||
when (response) {
|
||||
is ListTransactionsSuccessResponse -> {
|
||||
val newTxs = response.result?.transactions ?: emptyList()
|
||||
_transactions.value = _transactions.value + newTxs
|
||||
val totalCount = response.result?.total_count
|
||||
_hasMoreTransactions.value =
|
||||
if (totalCount != null) {
|
||||
_transactions.value.size < totalCount
|
||||
} else {
|
||||
newTxs.size >= pageSize
|
||||
}
|
||||
}
|
||||
|
||||
is NwcErrorResponse -> {
|
||||
_error.value = response.error?.message ?: "Failed to load more transactions"
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
_isLoadingMore.value = false
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
_error.value = e.message
|
||||
_isLoadingMore.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun sendPayment(bolt11: String) {
|
||||
val acc = account ?: return
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
|
||||
Reference in New Issue
Block a user