Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix: Client Sync issue fixed #2266

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package com.mifos.core.domain.useCases

import com.mifos.core.common.utils.Resource
import com.mifos.core.data.repository.ClientListRepository
import com.mifos.core.objects.client.Client
import com.mifos.core.objects.client.Page
import com.mifos.core.objects.group.Center
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.flow
import rx.Subscriber
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
import javax.inject.Inject
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine

class GetClientListDbUseCase @Inject constructor(private val repository: ClientListRepository) {
suspend operator fun invoke(): Flow<Resource<List<Client>>> = flow {
try {
emit(Resource.Loading())
emit(Resource.Success(getClientList()))
} catch (exception: Exception) {
emit(Resource.Error(exception.toString()))


}
}
private suspend fun getClientList(): List<Client> = suspendCoroutine { continuation ->
try {
val getClientListFormDb = repository.allDatabaseClients()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(object : Subscriber<Page<Client>>() {
override fun onCompleted() {
}
override fun onError(exception: Throwable) {
continuation.resumeWithException(exception)
}
override fun onNext(centerPage: Page<Client>) {
continuation.resume(centerPage.pageItems)
}
})
} catch (exception: Exception) {
continuation.resumeWithException(exception)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,7 @@ private fun CenterListDbContent(
}
}

class CenterListUiStateProvider :
private class CenterListUiStateProvider :
PreviewParameterProvider<CenterListUiState> {

override val values: Sequence<CenterListUiState>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,16 @@ import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.rounded.Close
import androidx.compose.material.icons.rounded.Sync
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
Expand All @@ -53,7 +48,6 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.runtime.toMutableStateList
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
Expand All @@ -74,12 +68,14 @@ import com.google.accompanist.swiperefresh.rememberSwipeRefreshState
import com.mifos.core.designsystem.component.MifosCircularProgress
import com.mifos.core.designsystem.component.MifosPagingAppendProgress
import com.mifos.core.designsystem.component.MifosSweetError
import com.mifos.core.designsystem.icon.MifosIcons
import com.mifos.core.designsystem.theme.Black
import com.mifos.core.designsystem.theme.BlueSecondary
import com.mifos.core.designsystem.theme.DarkGray
import com.mifos.core.designsystem.theme.LightGray
import com.mifos.core.designsystem.theme.White
import com.mifos.core.objects.client.Client
import com.mifos.core.ui.components.SelectionModeTopAppBar
import com.mifos.feature.client.R
import com.mifos.feature.client.syncClientDialog.SyncClientsDialogScreen

Expand All @@ -94,15 +90,32 @@ internal fun ClientListScreen(
onClientSelect: (Int) -> Unit,
viewModel: ClientListViewModel = hiltViewModel(),
) {
LaunchedEffect(key1 = true) {
viewModel.getClientList()
}
val isRefreshing by viewModel.isRefreshing.collectAsState()
val swipeRefreshState = rememberSwipeRefreshState(isRefreshing = isRefreshing)
val state = viewModel.clientListUiState.collectAsState().value

val snackbarHostState = remember { SnackbarHostState() }
ClientListScreen(
paddingValues = paddingValues,
state = state,
createNewClient = createNewClient,
onRefresh = {
viewModel.refreshClientList()
},
refreshState = isRefreshing,
onClientSelect = onClientSelect,
)
}

val state = viewModel.clientListUiState.collectAsState().value
@Composable
internal fun ClientListScreen(
paddingValues: PaddingValues,
state: ClientListUiState,
createNewClient: () -> Unit,
onRefresh: () -> Unit,
refreshState: Boolean,
onClientSelect: (Int) -> Unit,
) {
val snackbarHostState = remember { SnackbarHostState() }
val swipeRefreshState = rememberSwipeRefreshState(isRefreshing = refreshState)

var isInSelectionMode by remember { mutableStateOf(false) }
val selectedItems = remember { ClientSelectionState() }
Expand All @@ -114,7 +127,6 @@ internal fun ClientListScreen(
val sync = rememberSaveable {
mutableStateOf(false)
}

BackHandler(enabled = isInSelectionMode) {
resetSelectionMode()
}
Expand All @@ -127,15 +139,25 @@ internal fun ClientListScreen(
isInSelectionMode = false
}
}

Scaffold(
modifier = Modifier
.padding(paddingValues),
topBar = {
if (isInSelectionMode) {
SelectionModeTopAppBar(
currentSelectedItems = selectedItems.selectedItems.value,
syncClicked = { sync.value = true },
itemCount = selectedItems.size(),
actions = {
FilledTonalButton(
onClick = {
sync.value = true
},
) {
Icon(
imageVector = MifosIcons.sync,
contentDescription = "Sync Items",
)
}
},
resetSelectionMode = resetSelectionMode,
)
}
Expand All @@ -158,7 +180,7 @@ internal fun ClientListScreen(
SwipeRefresh(
state = swipeRefreshState,
onRefresh = {
viewModel.refreshClientList()
onRefresh()
},
) {
Column(
Expand All @@ -175,7 +197,7 @@ internal fun ClientListScreen(
onClientSelect = {
onClientSelect(it)
},
failedRefresh = { viewModel.refreshClientList() },
failedRefresh = { onRefresh() },
selectedMode = {
isInSelectionMode = true
},
Expand All @@ -187,21 +209,22 @@ internal fun ClientListScreen(
}

ClientListUiState.Empty -> {
MifosCircularProgress()
}

is ClientListUiState.Error -> {
MifosSweetError(message = state.message) {
viewModel.refreshClientList()
onRefresh()
}
}
}
}
if (sync.value) {
SyncClientsDialogScreen(
dismiss = {
resetSelectionMode.invoke()
selectedItems.clear()
sync.value = false
resetSelectionMode()
selectedItems.clear()
},
hide = { sync.value = false },
list = selectedItems.selectedItems.value,
Expand All @@ -211,54 +234,6 @@ internal fun ClientListScreen(
}
}

@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun SelectionModeTopAppBar(
currentSelectedItems: List<Client>,
syncClicked: () -> Unit,
resetSelectionMode: () -> Unit,
) {
val selectedItems = currentSelectedItems.toMutableStateList()
TopAppBar(
colors = TopAppBarDefaults.topAppBarColors(
containerColor = BlueSecondary,
),
title = {
Text(
text = "${selectedItems.size} selected",
style = MaterialTheme.typography.titleLarge.copy(
color = MaterialTheme.colorScheme.onBackground,
),
)
},
navigationIcon = {
IconButton(
onClick = resetSelectionMode,
) {
Icon(
imageVector = Icons.Rounded.Close,
contentDescription = null,
tint = Black,
)
}
},
actions = {
IconButton(
onClick = {
syncClicked()
resetSelectionMode()
},
) {
Icon(
imageVector = Icons.Rounded.Sync,
contentDescription = null,
tint = Black,
)
}
},
)
}

class ClientSelectionState(initialSelectedItems: List<Client> = emptyList()) {
private val _selectedItems = mutableStateListOf<Client>().also { it.addAll(initialSelectedItems) }
var selectedItems: State<List<Client>> = derivedStateOf { _selectedItems }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,15 @@ package com.mifos.feature.client.clientList.presentation

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.mifos.core.common.utils.Resource
import com.mifos.core.data.repository.ClientListRepository
import com.mifos.core.datastore.PrefManager
import com.mifos.core.objects.client.Client
import com.mifos.core.objects.client.Page
import com.mifos.core.domain.useCases.GetClientListDbUseCase
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import rx.Subscriber
import rx.android.schedulers.AndroidSchedulers
import rx.schedulers.Schedulers
import javax.inject.Inject

/**
Expand All @@ -33,6 +30,7 @@ import javax.inject.Inject
class ClientListViewModel @Inject constructor(
private val repository: ClientListRepository,
private val prefManager: PrefManager,
private val getClientListDbUseCase: GetClientListDbUseCase,
) : ViewModel() {

private val _clientListUiState = MutableStateFlow<ClientListUiState>(ClientListUiState.Empty)
Expand Down Expand Up @@ -66,20 +64,15 @@ class ClientListViewModel @Inject constructor(
}

private fun loadClientsFromDb() = viewModelScope.launch(Dispatchers.IO) {
repository.allDatabaseClients()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(object : Subscriber<Page<Client>>() {
override fun onCompleted() {
}

override fun onError(error: Throwable) {
getClientListDbUseCase.invoke().collect { result ->
when (result) {
is Resource.Error<*> ->
_clientListUiState.value = ClientListUiState.Error("Failed to Fetch Clients")
}
is Resource.Loading -> _clientListUiState.value = ClientListUiState.Empty

override fun onNext(clients: Page<Client>) {
_clientListUiState.value = ClientListUiState.ClientListDb(clients.pageItems)
}
})
is Resource.Success ->
_clientListUiState.value = ClientListUiState.ClientListDb(result.data ?: emptyList())
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
Expand Down Expand Up @@ -75,7 +76,10 @@ internal fun SyncClientsDialogScreen(
) {
val snackBarHostState = remember { SnackbarHostState() }

Box {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center,
) {
SyncClientsDialogContent(
uiData = uiData,
okClicked = dismiss,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ class SyncClientsDialogViewModel @Inject constructor(
updateTotalSyncProgressBarAndCount()
if (mClientSyncIndex != mClientList.size) {
updateClientName()
syncClientAccounts(mClientList[mClientSyncIndex].id)
mClientList[mClientSyncIndex].id?.let { syncClientAccounts(it) }
} else {
_syncClientData.update { it.copy(isSyncSuccess = true) }
}
Expand Down
Loading