diff --git a/mifospay/build.gradle.kts b/mifospay/build.gradle.kts index dc9f18f8b..38f832268 100644 --- a/mifospay/build.gradle.kts +++ b/mifospay/build.gradle.kts @@ -143,6 +143,8 @@ dependencies { implementation("de.hdodenhof:circleimageview:3.1.0") implementation("com.github.yalantis:ucrop:2.2.2") + implementation("com.google.accompanist:accompanist-swiperefresh:0.27.0") + kspTest(libs.hilt.compiler) testImplementation(libs.junit) diff --git a/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/bank/presenter/AccountViewModel.kt b/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/bank/presenter/AccountViewModel.kt index 0c4835211..1ad1fda8d 100644 --- a/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/bank/presenter/AccountViewModel.kt +++ b/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/bank/presenter/AccountViewModel.kt @@ -20,6 +20,7 @@ class AccountViewModel @Inject constructor() : ViewModel() { val accountsUiState: StateFlow = _accountUiState private val mRandom = Random() + val isRefreshing = MutableStateFlow(false) init { fetchLinkedAccount() @@ -82,6 +83,11 @@ class AccountViewModel @Inject constructor() : ViewModel() { ) return bankAccountDetailsList } + fun refreshAccountList(){ + isRefreshing.value = true + fetchLinkedAccount() + isRefreshing.value = false + } } diff --git a/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/bank/ui/AccountsScreen.kt b/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/bank/ui/AccountsScreen.kt index e7f0b1ff7..a1c925601 100644 --- a/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/bank/ui/AccountsScreen.kt +++ b/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/bank/ui/AccountsScreen.kt @@ -15,6 +15,9 @@ import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -26,6 +29,8 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.google.accompanist.swiperefresh.SwipeRefresh +import com.google.accompanist.swiperefresh.rememberSwipeRefreshState import com.mifos.mobilewallet.model.domain.BankAccountDetails import org.mifos.mobilewallet.mifospay.R import org.mifos.mobilewallet.mifospay.bank.presenter.AccountViewModel @@ -42,11 +47,18 @@ fun AccountsScreen( ) { val accountsUiState by viewModel.accountsUiState.collectAsStateWithLifecycle() val sampleList = viewModel.bankAccountDetailsList - AccountScreen( - accountsUiState = accountsUiState, - onAddAccount = onAddAccount, - sampleList = sampleList, - ) + val isRefreshing by viewModel.isRefreshing.collectAsStateWithLifecycle() + val swipeRefreshState = rememberSwipeRefreshState(isRefreshing = isRefreshing) + SwipeRefresh( + state = swipeRefreshState, + onRefresh = viewModel::refreshAccountList + ){ + AccountScreen( + accountsUiState = accountsUiState, + onAddAccount = onAddAccount, + sampleList = sampleList, + ) + } } @Composable diff --git a/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/kyc/presenter/KYCDescriptionViewModel.kt b/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/kyc/presenter/KYCDescriptionViewModel.kt index 967898138..a2ac5a498 100644 --- a/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/kyc/presenter/KYCDescriptionViewModel.kt +++ b/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/kyc/presenter/KYCDescriptionViewModel.kt @@ -8,9 +8,7 @@ import kotlinx.coroutines.flow.StateFlow import org.mifos.mobilewallet.core.base.UseCase import org.mifos.mobilewallet.core.base.UseCaseHandler import org.mifos.mobilewallet.core.domain.usecase.kyc.FetchKYCLevel1Details -import org.mifos.mobilewallet.mifospay.R import org.mifos.mobilewallet.mifospay.data.local.LocalRepository -import org.mifos.mobilewallet.mifospay.kyc.KYCContract import javax.inject.Inject @HiltViewModel @@ -23,12 +21,13 @@ class KYCDescriptionViewModel @Inject constructor( MutableStateFlow(KYCDescriptionUiState.Loading) val kycdescriptionState: StateFlow = _kycdescriptionState + val isRefreshing = MutableStateFlow(false) init { fetchCurrentLevel() } - private fun fetchCurrentLevel() { + fun fetchCurrentLevel() { fetchKYCLevel1DetailsUseCase.walletRequestValues = FetchKYCLevel1Details.RequestValues(mLocalRepository.clientDetails.clientId.toInt()) val requestValues = fetchKYCLevel1DetailsUseCase.walletRequestValues @@ -49,6 +48,11 @@ class KYCDescriptionViewModel @Inject constructor( } }) } + fun refreshKYCLevel(){ + isRefreshing.value = true + fetchCurrentLevel() + isRefreshing.value = false + } } diff --git a/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/kyc/ui/KYCDescriptionScreen.kt b/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/kyc/ui/KYCDescriptionScreen.kt index bbf92d9fd..f357e2843 100644 --- a/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/kyc/ui/KYCDescriptionScreen.kt +++ b/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/kyc/ui/KYCDescriptionScreen.kt @@ -1,7 +1,6 @@ package org.mifos.mobilewallet.mifospay.kyc.ui import androidx.compose.foundation.Image -import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -15,7 +14,9 @@ import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check import androidx.compose.material3.Button @@ -26,6 +27,9 @@ import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.scale @@ -39,6 +43,9 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.hilt.navigation.compose.hiltViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.google.accompanist.swiperefresh.SwipeRefresh +import com.google.accompanist.swiperefresh.rememberSwipeRefreshState import com.mifos.mobilewallet.model.entity.kyc.KYCLevel1Details import org.mifos.mobilewallet.mifospay.R import org.mifos.mobilewallet.mifospay.designsystem.component.MifosOverlayLoadingWheel @@ -53,31 +60,45 @@ fun KYCDescriptionScreen( onLevel3Clicked: () -> Unit ) { val kUiState by viewModel.kycdescriptionState.collectAsState() + val isRefreshing by viewModel.isRefreshing.collectAsStateWithLifecycle() + val swipeRefreshState = rememberSwipeRefreshState(isRefreshing = isRefreshing) - when (val state = kUiState) { - KYCDescriptionUiState.Loading -> { - MifosOverlayLoadingWheel(contentDesc = stringResource(R.string.loading)) - } + SwipeRefresh( + state = swipeRefreshState, + onRefresh = viewModel::refreshKYCLevel + ){ + Box( + modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()), + contentAlignment = Alignment.Center + ) { + when (val state = kUiState) { + KYCDescriptionUiState.Loading -> { + MifosOverlayLoadingWheel(contentDesc = stringResource(R.string.loading)) + } - is KYCDescriptionUiState.Error -> { - PlaceholderScreen() - } + is KYCDescriptionUiState.Error -> { + PlaceholderScreen() + } - is KYCDescriptionUiState.KYCDescription -> { - val kyc = state.kycLevel1Details - if (kyc != null) { - KYCDescriptionScreen( - kUiState = state, - kyc, - onLevel1Clicked, - onLevel2Clicked, - onLevel3Clicked - ) + is KYCDescriptionUiState.KYCDescription -> { + val kyc = state.kycLevel1Details + if (kyc != null) { + KYCDescriptionScreen( + kUiState = state, + kyc, + onLevel1Clicked, + onLevel2Clicked, + onLevel3Clicked + ) + } + } + + else -> {} + } } } - - else -> {} - } } @Composable diff --git a/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/merchants/presenter/MerchantViewModel.kt b/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/merchants/presenter/MerchantViewModel.kt index ffd05e7fd..49f8e6c79 100644 --- a/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/merchants/presenter/MerchantViewModel.kt +++ b/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/merchants/presenter/MerchantViewModel.kt @@ -36,6 +36,8 @@ class MerchantViewModel @Inject constructor( private val _merchantUiState = MutableStateFlow(MerchantUiState.Loading) val merchantUiState: StateFlow = _merchantUiState + val isRefreshing = MutableStateFlow(false) + init { fetchMerchants() } @@ -120,6 +122,12 @@ class MerchantViewModel @Inject constructor( } }) } + + fun refreshMerchantsList(){ + isRefreshing.value = true + fetchMerchants() + isRefreshing.value = false + } } sealed class MerchantUiState { diff --git a/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/merchants/ui/MerchantScreen.kt b/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/merchants/ui/MerchantScreen.kt index a71f35862..b6f6dee54 100644 --- a/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/merchants/ui/MerchantScreen.kt +++ b/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/merchants/ui/MerchantScreen.kt @@ -7,6 +7,8 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Search @@ -17,9 +19,11 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.SearchBar import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -29,6 +33,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.google.accompanist.swiperefresh.SwipeRefresh +import com.google.accompanist.swiperefresh.rememberSwipeRefreshState import com.mifos.mobilewallet.model.entity.accounts.savings.SavingsWithAssociations import org.mifos.mobilewallet.mifospay.R import org.mifos.mobilewallet.mifospay.common.Constants @@ -44,11 +50,19 @@ fun MerchantScreen( ) { val merchantUiState by viewModel.merchantUiState.collectAsStateWithLifecycle() val merchantsListUiState by viewModel.merchantsListUiState.collectAsStateWithLifecycle() - MerchantScreen( - merchantUiState = merchantUiState, - merchantListUiState = merchantsListUiState, - updateQuery = { viewModel.updateSearchQuery(it) } - ) + val isRefreshing by viewModel.isRefreshing.collectAsStateWithLifecycle() + val swipeRefreshState = rememberSwipeRefreshState(isRefreshing = isRefreshing) + + SwipeRefresh( + state = swipeRefreshState, + onRefresh = viewModel::refreshMerchantsList + ){ + MerchantScreen( + merchantUiState = merchantUiState, + merchantListUiState = merchantsListUiState, + updateQuery = { viewModel.updateSearchQuery(it) } + ) + } } @Composable @@ -59,7 +73,8 @@ fun MerchantScreen( ) { Box( modifier = Modifier - .fillMaxSize(), + .fillMaxSize() + .verticalScroll(rememberScrollState()), contentAlignment = Alignment.Center, ) { when (merchantUiState) { diff --git a/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/savedcards/presenter/CardsScreenViewModel.kt b/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/savedcards/presenter/CardsScreenViewModel.kt index d3e293cdc..c0a462e5e 100644 --- a/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/savedcards/presenter/CardsScreenViewModel.kt +++ b/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/savedcards/presenter/CardsScreenViewModel.kt @@ -38,6 +38,8 @@ class CardsScreenViewModel @Inject constructor( private val _cardState = MutableStateFlow(CardsUiState.Loading) val cardState: StateFlow = _cardState.asStateFlow() + val isRefreshing = MutableStateFlow(false) + init { fetchSavedCards() } @@ -163,6 +165,11 @@ class CardsScreenViewModel @Inject constructor( } }) } + fun refreshSavedCards(){ + isRefreshing.value = true + fetchSavedCards() + isRefreshing.value = false + } } diff --git a/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/savedcards/ui/CardsScreen.kt b/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/savedcards/ui/CardsScreen.kt index 4a2aa0e2f..317c8749c 100644 --- a/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/savedcards/ui/CardsScreen.kt +++ b/mifospay/src/main/java/org/mifos/mobilewallet/mifospay/savedcards/ui/CardsScreen.kt @@ -10,16 +10,14 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.rounded.Info -import androidx.compose.material3.AssistChip -import androidx.compose.material3.AssistChipDefaults import androidx.compose.material3.Card import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem @@ -39,11 +37,12 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.google.accompanist.swiperefresh.SwipeRefresh +import com.google.accompanist.swiperefresh.rememberSwipeRefreshState import com.mifos.mobilewallet.model.entity.savedcards.Card import org.mifos.mobilewallet.mifospay.R import org.mifos.mobilewallet.mifospay.designsystem.component.MfLoadingWheel @@ -65,19 +64,27 @@ fun CardsScreen( ) { val cardState by viewModel.cardState.collectAsStateWithLifecycle() val cardListUiState by viewModel.cardListUiState.collectAsStateWithLifecycle() - CardsScreen( - cardState = cardState, - cardListUiState = cardListUiState, - onEditCard = onEditCard, - onDeleteCard = { - // TODO implement Delete card by implementing a delete confirm dialog and call - // TODO viewModel.deleteCard - }, - onAddBtn = onAddBtn, - updateQuery = { - viewModel.updateSearchQuery(it) - } - ) + val isRefreshing by viewModel.isRefreshing.collectAsStateWithLifecycle() + val swipeRefreshState = rememberSwipeRefreshState(isRefreshing = isRefreshing) + + SwipeRefresh( + state = swipeRefreshState, + onRefresh = viewModel::refreshSavedCards + ){ + CardsScreen( + cardState = cardState, + cardListUiState = cardListUiState, + onEditCard = onEditCard, + onDeleteCard = { + // TODO implement Delete card by implementing a delete confirm dialog and call + // TODO viewModel.deleteCard + }, + onAddBtn = onAddBtn, + updateQuery = { + viewModel.updateSearchQuery(it) + } + ) + } } @Composable @@ -87,11 +94,12 @@ fun CardsScreen( onEditCard: (Card) -> Unit, onDeleteCard: (Card) -> Unit, onAddBtn: () -> Unit, - updateQuery: (String) -> Unit + updateQuery: (String) -> Unit, ) { Box( modifier = Modifier - .fillMaxSize(), + .fillMaxSize() + .verticalScroll(rememberScrollState()), contentAlignment = Alignment.Center, ) { when (cardState) { @@ -223,7 +231,9 @@ fun CardsList( onMenuItemClick: (Card, CardMenuAction) -> Unit ) { LazyColumn( - modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp) + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp) ) { items(cards) { card -> CardItem(card = card) { clickedCard, menuItem ->