diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletTransactionsScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletTransactionsScreen.kt index 0f1818809..3dede2f18 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletTransactionsScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletTransactionsScreen.kt @@ -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)) + } + } + } } } } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletViewModel.kt index 0e7934930..48cc4bdd9 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/wallet/WalletViewModel.kt @@ -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(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) {