From 99cc223dbdfb7fe9ff05a8ea406648ed161ef55c Mon Sep 17 00:00:00 2001 From: ashutosh-kumar-kushwaha Date: Tue, 2 Apr 2024 21:44:19 +0530 Subject: [PATCH] Refactor: Migrate CreateNewCenterFragment to Jetpack Compose --- .../component/MifosEditTextField.kt | 16 +- mifosng-android/build.gradle.kts | 3 + .../CreateNewCenterFragment.kt | 214 +----------- .../createnewcenter/CreateNewCenterScreen.kt | 312 ++++++++++++++++++ .../createnewcenter/CreateNewCenterUiState.kt | 21 +- .../CreateNewCenterViewModel.kt | 80 +++-- 6 files changed, 398 insertions(+), 248 deletions(-) create mode 100644 mifosng-android/src/main/java/com/mifos/mifosxdroid/online/createnewcenter/CreateNewCenterScreen.kt diff --git a/core/designsystem/src/main/java/com/mifos/core/designsystem/component/MifosEditTextField.kt b/core/designsystem/src/main/java/com/mifos/core/designsystem/component/MifosEditTextField.kt index 1b3994cdb06..6f337321d01 100644 --- a/core/designsystem/src/main/java/com/mifos/core/designsystem/component/MifosEditTextField.kt +++ b/core/designsystem/src/main/java/com/mifos/core/designsystem/component/MifosEditTextField.kt @@ -4,7 +4,7 @@ import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.text.KeyboardOptions -import androidx.compose.material.Icon +import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextFieldDefaults @@ -18,6 +18,7 @@ import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.mifos.core.designsystem.theme.BluePrimary @@ -33,22 +34,24 @@ import com.mifos.core.designsystem.theme.White fun MifosOutlinedTextField( value: TextFieldValue, onValueChange: (TextFieldValue) -> Unit, + modifier: Modifier = Modifier, maxLines: Int = 1, singleLine: Boolean = true, icon: ImageVector? = null, label: Int, visualTransformation: VisualTransformation = VisualTransformation.None, trailingIcon: @Composable (() -> Unit)? = null, - error: Int? + error: Int?, + readOnly: Boolean = false, + padding: Dp = 16.dp ) { - OutlinedTextField( value = value, onValueChange = onValueChange, label = { Text(stringResource(id = label)) }, - modifier = Modifier + modifier = modifier .fillMaxWidth() - .padding(start = 16.dp, end = 16.dp), + .padding(horizontal = padding), leadingIcon = if (icon != null) { { Icon( @@ -78,6 +81,7 @@ fun MifosOutlinedTextField( color = MaterialTheme.colorScheme.error ) } - } + }, + readOnly = readOnly ) } \ No newline at end of file diff --git a/mifosng-android/build.gradle.kts b/mifosng-android/build.gradle.kts index 87a2ffef27d..a1802c60c5e 100644 --- a/mifosng-android/build.gradle.kts +++ b/mifosng-android/build.gradle.kts @@ -126,6 +126,7 @@ dependencies { implementation(project(":core:datastore")) implementation(project(":core:network")) implementation(project(":core:common")) + implementation(project(":core:designsystem")) // Multidex dependency implementation("androidx.multidex:multidex:2.0.1") @@ -255,4 +256,6 @@ dependencies { // ViewModel utilities for Compose implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0") implementation("androidx.hilt:hilt-navigation-compose:1.1.0") + implementation("androidx.lifecycle:lifecycle-runtime-compose:2.7.0") + } \ No newline at end of file diff --git a/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/createnewcenter/CreateNewCenterFragment.kt b/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/createnewcenter/CreateNewCenterFragment.kt index dceb7d9a9fd..4cbe83cb4b3 100755 --- a/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/createnewcenter/CreateNewCenterFragment.kt +++ b/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/createnewcenter/CreateNewCenterFragment.kt @@ -5,229 +5,29 @@ package com.mifos.mifosxdroid.online.createnewcenter import android.os.Bundle -import android.text.TextUtils import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.Toast -import androidx.lifecycle.ViewModelProvider -import com.mifos.core.data.CenterPayload -import com.mifos.core.objects.organisation.Office -import com.mifos.core.objects.response.SaveResponse -import com.mifos.exceptions.InvalidTextInputException -import com.mifos.exceptions.RequiredFieldException -import com.mifos.exceptions.ShortOfLengthException -import com.mifos.mifosxdroid.R +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy import com.mifos.mifosxdroid.core.MifosBaseFragment -import com.mifos.mifosxdroid.databinding.FragmentCreateNewCenterBinding -import com.mifos.utils.DatePickerConstrainType -import com.mifos.utils.FragmentConstants -import com.mifos.utils.MifosResponseHandler -import com.mifos.utils.ValidationUtil -import com.mifos.utils.getDatePickerDialog -import com.mifos.utils.getTodayFormatted import dagger.hilt.android.AndroidEntryPoint -import java.text.SimpleDateFormat -import java.time.Instant -import java.util.Locale - /** * Created by nellyk on 1/22/2016. */ + @AndroidEntryPoint class CreateNewCenterFragment : MifosBaseFragment() { - - private lateinit var binding: FragmentCreateNewCenterBinding - - var officeId: Int? = 0 - var result = true - - private lateinit var viewModel: CreateNewCenterViewModel - - private var activationDateString: String? = null - private val officeNameIdHashMap = HashMap() - - private var activationDate: Instant = Instant.now() - private val submissionDatePickerDialog by lazy { - getDatePickerDialog(activationDate, DatePickerConstrainType.ONLY_FUTURE_DAYS) { - val formattedDate = SimpleDateFormat("dd MM yyyy", Locale.getDefault()).format(it) - activationDate = Instant.ofEpochMilli(it) - binding.activateDateFieldContainer.editText?.setText(formattedDate) - } - } - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { - binding = FragmentCreateNewCenterBinding.inflate(inflater, container, false) - viewModel = ViewModelProvider(this)[CreateNewCenterViewModel::class.java] - inflateOfficeSpinner() - inflateActivationDate() - //client active checkbox onCheckedListener - - viewModel.createNewCenterUiState.observe(viewLifecycleOwner) { - when (it) { - is CreateNewCenterUiState.CenterCreatedSuccessfully -> { - showProgressbar(false) - centerCreatedSuccessfully(it.saveResponse) - } - - is CreateNewCenterUiState.ShowFetchingError -> { - showProgressbar(false) - showFetchingError(it.message) - } - - is CreateNewCenterUiState.ShowFetchingErrorString -> { - showProgressbar(false) - showFetchingError(it.message) - } - - is CreateNewCenterUiState.ShowOffices -> { - showProgressbar(false) - showOffices(it.offices) - } - - is CreateNewCenterUiState.ShowProgressbar -> showProgressbar(true) + return ComposeView(requireContext()).apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + CreateNewCenterScreen() } } - - return binding.root - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - binding.cbCenterActiveStatus.setOnCheckedChangeListener { compoundButton, isChecked -> - if (isChecked) { - binding.activateDateFieldContainer.visibility = View.VISIBLE - activationDateString = binding.activateDateFieldContainer.editText?.text.toString() - } else { - binding.activateDateFieldContainer.visibility = View.GONE - } - } - binding.btnSubmit.setOnClickListener { - val centerPayload = CenterPayload() - centerPayload.name = binding.etCenterName.editableText.toString() - centerPayload.active = binding.cbCenterActiveStatus.isChecked - centerPayload.activationDate = activationDateString - centerPayload.officeId = officeId - centerPayload.dateFormat = "dd MMMM yyyy" - centerPayload.locale = "en" - initiateCenterCreation(centerPayload) - } - - binding.activateDateFieldContainer.setEndIconOnClickListener { - submissionDatePickerDialog.show( - requireActivity().supportFragmentManager, - FragmentConstants.DFRAG_DATE_PICKER - ) - } - } - - //inflating office list spinner - private fun inflateOfficeSpinner() { - viewModel.loadOffices() - } - - private fun initiateCenterCreation(centerPayload: CenterPayload) { - if (isCenterNameValid) { - viewModel.createCenter(centerPayload) - } - } - - private fun inflateActivationDate() { - binding.activateDateFieldContainer.editText?.setText(getTodayFormatted()) - } - - private val isCenterNameValid: Boolean - get() { - result = true - try { - if (TextUtils.isEmpty(binding.etCenterName.editableText.toString())) { - throw RequiredFieldException( - resources.getString(R.string.center_name), - resources.getString(R.string.error_cannot_be_empty) - ) - } - if (binding.etCenterName.editableText.toString() - .trim { it <= ' ' }.length < 4 && binding.etCenterName - .editableText.toString().trim { it <= ' ' }.isNotEmpty() - ) { - throw ShortOfLengthException(resources.getString(R.string.center_name), 4) - } - if (!ValidationUtil.isNameValid(binding.etCenterName.editableText.toString())) { - throw InvalidTextInputException( - resources.getString(R.string.center_name), - resources.getString(R.string.error_should_contain_only), - InvalidTextInputException.TYPE_ALPHABETS - ) - } - } catch (e: InvalidTextInputException) { - e.notifyUserWithToast(activity) - result = false - } catch (e: ShortOfLengthException) { - e.notifyUserWithToast(activity) - result = false - } catch (e: RequiredFieldException) { - e.notifyUserWithToast(activity) - result = false - } - return result - } - - private fun showOffices(offices: List) { - val officeList: MutableList = ArrayList() - if (offices != null) { - for (office in offices) { - if (office != null) { - office.name?.let { officeList.add(it) } - } - if (office != null) { - officeNameIdHashMap[office.name!!] = office.id!! - } - } - } - officeList.sort() - - binding.officeListField.setSimpleItems(officeList.toTypedArray()) - - binding.officeListField.setOnItemClickListener { adapterView, view, relativePosition, l -> - val index = officeList.indexOf(adapterView.getItemAtPosition(relativePosition)) - officeId = officeNameIdHashMap[officeList[index]] - if (officeId != -1) { - } else { - Toast.makeText( - activity, getString(R.string.error_select_office), Toast.LENGTH_SHORT - ).show() - } - } - } - - private fun centerCreatedSuccessfully(saveResponse: SaveResponse?) { - Toast.makeText( - activity, "Center " + MifosResponseHandler.response, - Toast.LENGTH_LONG - ).show() - requireActivity().supportFragmentManager.popBackStack() - } - - private fun showFetchingError(errorMessage: Int) { - Toast.makeText(activity, errorMessage, Toast.LENGTH_SHORT).show() - } - - private fun showFetchingError(s: String?) { - Toast.makeText(activity, s, Toast.LENGTH_SHORT).show() - } - - private fun showProgressbar(show: Boolean) { - if (show) { - binding.llCenter.visibility = View.GONE - showMifosProgressBar() - } else { - binding.llCenter.visibility = View.VISIBLE - hideMifosProgressBar() - } } } \ No newline at end of file diff --git a/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/createnewcenter/CreateNewCenterScreen.kt b/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/createnewcenter/CreateNewCenterScreen.kt new file mode 100644 index 00000000000..9efddd16d72 --- /dev/null +++ b/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/createnewcenter/CreateNewCenterScreen.kt @@ -0,0 +1,312 @@ +package com.mifos.mifosxdroid.online.createnewcenter + +import android.icu.util.Calendar +import android.text.format.DateFormat +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.Arrangement +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.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.CalendarToday +import androidx.compose.material3.Button +import androidx.compose.material3.ButtonDefaults +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.DatePicker +import androidx.compose.material3.DatePickerDialog +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.ExposedDropdownMenuBox +import androidx.compose.material3.ExposedDropdownMenuDefaults +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.RadioButton +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.rememberDatePickerState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableIntStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +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.res.stringResource +import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.compose.ui.window.Dialog +import androidx.compose.ui.window.DialogProperties +import androidx.hilt.navigation.compose.hiltViewModel +import com.mifos.core.data.CenterPayload +import com.mifos.core.designsystem.component.MifosOutlinedTextField +import com.mifos.core.designsystem.theme.BluePrimary +import com.mifos.core.designsystem.theme.BluePrimaryDark +import com.mifos.core.designsystem.theme.White +import com.mifos.mifosxdroid.R + +@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class) +@Composable +fun CreateNewCenterScreen( + viewModel: CreateNewCenterViewModel = hiltViewModel() +) { + val uiState = viewModel.uiState.collectAsState().value + val snackbarHostState = remember { SnackbarHostState() } + var centerName by rememberSaveable(stateSaver = TextFieldValue.Saver) { + mutableStateOf( + TextFieldValue("") + ) + } + var isOfficeListDropDownExpanded by rememberSaveable { + mutableStateOf(false) + } + var office by rememberSaveable(stateSaver = TextFieldValue.Saver) { + mutableStateOf( + TextFieldValue("") + ) + } + var officeId by rememberSaveable { + mutableIntStateOf(0) + } + var isActiveSelected by rememberSaveable { + mutableStateOf(false) + } + val activationDatePickerState = + rememberDatePickerState(initialSelectedDateMillis = Calendar.getInstance().timeInMillis) + var openActivationDatePickerDialog by rememberSaveable { + mutableStateOf(false) + } + var activationDate by rememberSaveable(stateSaver = TextFieldValue.Saver) { + mutableStateOf( + TextFieldValue( + DateFormat.format( + "dd MMM yyyy", + activationDatePickerState.selectedDateMillis!! + ).toString() + ) + ) + } + + Scaffold( + topBar = { + TopAppBar( + title = { + Text( + text = stringResource(id = R.string.create_center), + fontSize = 24.sp + ) + } + ) + }, + snackbarHost = { + SnackbarHost(hostState = snackbarHostState) + } + ) { + Column( + modifier = Modifier + .fillMaxSize() + .padding(it) + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + MifosOutlinedTextField( + value = centerName, + onValueChange = { + centerName = it + }, + label = R.string.name, + error = null, + padding = 0.dp + ) + ExposedDropdownMenuBox( + expanded = isOfficeListDropDownExpanded, + onExpandedChange = { + isOfficeListDropDownExpanded = !isOfficeListDropDownExpanded + } + ) { + MifosOutlinedTextField( + value = office, + onValueChange = {}, + label = R.string.office, + error = null, + readOnly = true, + modifier = Modifier.menuAnchor(), + trailingIcon = { + ExposedDropdownMenuDefaults.TrailingIcon(expanded = isOfficeListDropDownExpanded) + }, + padding = 0.dp + ) + DropdownMenu( + expanded = isOfficeListDropDownExpanded, + onDismissRequest = { + isOfficeListDropDownExpanded = false + }, + modifier = Modifier.exposedDropdownSize() + ) { + uiState.offices.forEach { + DropdownMenuItem( + text = { + Text( + text = it.name!! + ) + }, + onClick = { + office = TextFieldValue(it.name!!) + officeId = it.id!! + isOfficeListDropDownExpanded = false + } + ) + } + } + } + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .combinedClickable { + isActiveSelected = !isActiveSelected + } + ) { + RadioButton( + selected = isActiveSelected, + onClick = { + isActiveSelected = !isActiveSelected + }, + modifier = Modifier.size(20.dp) + ) + Text( + text = stringResource(id = R.string.active) + ) + } + Spacer(modifier = Modifier.height(4.dp)) + if (isActiveSelected) { + MifosOutlinedTextField( + value = activationDate, + onValueChange = { + activationDate = it + }, + label = R.string.activation_date, + error = null, + readOnly = true, + trailingIcon = { + IconButton( + onClick = { + openActivationDatePickerDialog = true + } + ) { + Icon( + imageVector = Icons.Filled.CalendarToday, + contentDescription = null + ) + } + }, + padding = 0.dp + ) + if (openActivationDatePickerDialog) { + DatePickerDialog( + onDismissRequest = { + openActivationDatePickerDialog = false + }, + confirmButton = { + TextButton( + onClick = { + openActivationDatePickerDialog = false + activationDate = TextFieldValue( + DateFormat.format( + "dd MMM yyyy", + activationDatePickerState.selectedDateMillis!! + ).toString() + ) + } + ) { + Text(text = "OK") + } + }, + dismissButton = { + TextButton( + onClick = { + openActivationDatePickerDialog = false + } + ) { + Text("Cancel") + } + } + ) { + DatePicker(state = activationDatePickerState) + } + } + } + Button( + onClick = { + viewModel.initiateCenterCreation( + CenterPayload( + name = centerName.text, + active = isActiveSelected, + activationDate = activationDate.text, + officeId = officeId, + dateFormat = "dd MMMM yyyy", + locale = "en" + ) + ) + }, + modifier = Modifier + .fillMaxWidth() + .heightIn(44.dp), + colors = ButtonDefaults.buttonColors( + containerColor = if (isSystemInDarkTheme()) BluePrimaryDark else BluePrimary + ) + ) { + Text( + text = stringResource(id = R.string.submit), + fontSize = 16.sp + ) + } + + if (uiState.isLoading) { + ProgressDialog( + onDismissRequest = { + viewModel.dismissDialog() + } + ) + } + if(!uiState.message.isNullOrEmpty()){ + LaunchedEffect(uiState.message) { + snackbarHostState.showSnackbar(uiState.message) + viewModel.resetErrorMessage() + } + } + } + } +} + +@Composable +fun ProgressDialog( + onDismissRequest: () -> Unit +) { + Dialog( + onDismissRequest = onDismissRequest, + properties = DialogProperties( + dismissOnBackPress = false, + dismissOnClickOutside = false + ) + ) { + CircularProgressIndicator(color = White) + } +} \ No newline at end of file diff --git a/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/createnewcenter/CreateNewCenterUiState.kt b/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/createnewcenter/CreateNewCenterUiState.kt index 58296f81e93..2b2d6e62bc3 100644 --- a/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/createnewcenter/CreateNewCenterUiState.kt +++ b/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/createnewcenter/CreateNewCenterUiState.kt @@ -1,20 +1,9 @@ package com.mifos.mifosxdroid.online.createnewcenter import com.mifos.core.objects.organisation.Office -import com.mifos.core.objects.response.SaveResponse -/** - * Created by Aditya Gupta on 10/08/23. - */ -sealed class CreateNewCenterUiState { - - data object ShowProgressbar : CreateNewCenterUiState() - - data class ShowFetchingError(val message: Int) : CreateNewCenterUiState() - - data class ShowOffices(val offices: List) : CreateNewCenterUiState() - - data class ShowFetchingErrorString(val message: String) : CreateNewCenterUiState() - - data class CenterCreatedSuccessfully(val saveResponse: SaveResponse) : CreateNewCenterUiState() -} +data class CreateNewCenterUiState( + val isLoading: Boolean = false, + val offices: List = emptyList(), + val message: String? = null +) diff --git a/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/createnewcenter/CreateNewCenterViewModel.kt b/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/createnewcenter/CreateNewCenterViewModel.kt index 6ecbb91de3b..af7e5a51ae3 100644 --- a/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/createnewcenter/CreateNewCenterViewModel.kt +++ b/mifosng-android/src/main/java/com/mifos/mifosxdroid/online/createnewcenter/CreateNewCenterViewModel.kt @@ -1,13 +1,14 @@ package com.mifos.mifosxdroid.online.createnewcenter -import androidx.lifecycle.LiveData -import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import com.mifos.core.data.CenterPayload import com.mifos.core.objects.organisation.Office import com.mifos.core.objects.response.SaveResponse -import com.mifos.mifosxdroid.R +import com.mifos.utils.ValidationUtil import dagger.hilt.android.lifecycle.HiltViewModel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow import rx.Subscriber import rx.android.schedulers.AndroidSchedulers import rx.schedulers.Schedulers @@ -17,34 +18,44 @@ import javax.inject.Inject * Created by Aditya Gupta on 10/08/23. */ @HiltViewModel -class CreateNewCenterViewModel @Inject constructor(private val repository: CreateNewCenterRepository) : - ViewModel() { +class CreateNewCenterViewModel @Inject constructor( + private val repository: CreateNewCenterRepository +) : ViewModel() { - private val _createNewCenterUiState = MutableLiveData() + private val _uiState = MutableStateFlow(CreateNewCenterUiState()) - val createNewCenterUiState: LiveData - get() = _createNewCenterUiState + val uiState: StateFlow + get() = _uiState.asStateFlow() - fun loadOffices() { - _createNewCenterUiState.value = CreateNewCenterUiState.ShowProgressbar + init { + loadOffices() + } + + private fun loadOffices() { + _uiState.value = CreateNewCenterUiState(isLoading = true) repository.offices() .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .subscribe(object : Subscriber>() { override fun onCompleted() {} override fun onError(e: Throwable) { - _createNewCenterUiState.value = - CreateNewCenterUiState.ShowFetchingError(R.string.failed_to_fetch_offices) + _uiState.value = + CreateNewCenterUiState(message = "Failed to fetch office") } override fun onNext(offices: List) { - _createNewCenterUiState.value = CreateNewCenterUiState.ShowOffices(offices) + if (offices.isEmpty()) { + _uiState.value = + CreateNewCenterUiState(message = "Failed to fetch office") + } else { + _uiState.value = CreateNewCenterUiState(offices = offices) + } } }) } - fun createCenter(centerPayload: CenterPayload) { - _createNewCenterUiState.value = CreateNewCenterUiState.ShowProgressbar + private fun createCenter(centerPayload: CenterPayload) { + _uiState.value = _uiState.value.copy(isLoading = true) repository.createCenter(centerPayload) .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) @@ -53,14 +64,45 @@ class CreateNewCenterViewModel @Inject constructor(private val repository: Creat } override fun onError(e: Throwable) { - _createNewCenterUiState.value = - CreateNewCenterUiState.ShowFetchingErrorString(e.message.toString()) + _uiState.value = _uiState.value.copy(message = e.message.toString(), isLoading = false) } override fun onNext(saveResponse: SaveResponse) { - _createNewCenterUiState.value = - CreateNewCenterUiState.CenterCreatedSuccessfully(saveResponse) + _uiState.value = _uiState.value.copy(message = "Center created successfully", isLoading = false) } }) } + + fun initiateCenterCreation(centerPayload: CenterPayload) { + if (isCenterNameValid(centerPayload.name)) { + createCenter(centerPayload) + } + } + + fun dismissDialog() { + _uiState.value = _uiState.value.copy(isLoading = false) + } + + fun resetErrorMessage() { + _uiState.value = _uiState.value.copy(message = null) + } + + private fun isCenterNameValid(name: String?): Boolean { + if (name.isNullOrEmpty()) { + _uiState.value = _uiState.value.copy(message = "Center name cannot be empty") + return false + } + if (name.trim().length < 4) { + _uiState.value = + _uiState.value.copy(message = "Center name cannot be less than 4 characters") + return false + } + if (!ValidationUtil.isNameValid(name)) { + _uiState.value = + _uiState.value.copy(message = "Center name should contain only alphabets") + return false + } + return true + } + } \ No newline at end of file