changes made #1587 : migrated KycLevel1 to compose

This commit is contained in:
Aditya 2024-03-26 12:13:42 +05:30 committed by Rajan Maurya
parent 51b01f9246
commit d05f2be91f
7 changed files with 395 additions and 115 deletions

View File

@ -65,7 +65,8 @@ internal class TimeZoneBroadcastMonitor @Inject constructor(
}
}
// If there isn't a zoneId in the intent, fallback to the systemDefault, which should also reflect the change
/* If there isn't a zoneId in the intent, fallback to the systemDefault,
which should also reflect the change */
trySend(zoneIdFromIntent ?: TimeZone.currentSystemDefault())
}
}

View File

@ -48,6 +48,7 @@ kotlinxSerializationJson = "1.6.3"
retrofitKotlinxSerializationJson = "1.0.0"
kotlinxCoroutines = "1.8.0"
compose_country_code_picker = "2.2.8"
sheets_compose_dialogs_core = "1.3.0"
protobuf = "3.25.2"
protobufPlugin = "0.9.4"
androidxDataStore = "1.0.0"
@ -125,6 +126,8 @@ androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
compose-country-code-picker = { group = "com.github.jump-sdk", name = "jetpack_compose_country_code_picker_emoji", version.ref = "compose_country_code_picker" }
sheets-compose-dialogs-core = { group = "com.maxkeppeler.sheets-compose-dialogs",name = "core",version.ref = "sheets_compose_dialogs_core"}
sheets-compose-dialogs-calender = { group = "com.maxkeppeler.sheets-compose-dialogs",name = "calendar",version.ref = "sheets_compose_dialogs_core"}
protobuf-kotlin-lite = { group = "com.google.protobuf", name = "protobuf-kotlin-lite", version.ref = "protobuf" }
protobuf-protoc = { group = "com.google.protobuf", name = "protoc", version.ref = "protobuf" }
androidx-dataStore-core = { group = "androidx.datastore", name = "datastore", version.ref = "androidxDataStore" }

View File

@ -96,6 +96,9 @@ dependencies {
implementation("androidx.compose.material:material:1.6.0")
implementation(libs.compose.country.code.picker) // remove after moving auth code to module
//calender for date picking
implementation(libs.sheets.compose.dialogs.core)
implementation(libs.sheets.compose.dialogs.calender)
// ViewModel
implementation(libs.androidx.lifecycle.ktx)

View File

@ -0,0 +1,67 @@
package org.mifos.mobilewallet.mifospay.kyc.presenter
import androidx.lifecycle.ViewModel
import com.mifos.mobilewallet.model.entity.kyc.KYCLevel1Details
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.flow.MutableStateFlow
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.UploadKYCLevel1Details
import org.mifos.mobilewallet.mifospay.data.local.LocalRepository
import javax.inject.Inject
@HiltViewModel
class KYCLevel1ViewModel @Inject constructor(
private val mUseCaseHandler: UseCaseHandler,
private val mLocalRepository: LocalRepository,
private val uploadKYCLevel1DetailsUseCase: UploadKYCLevel1Details
) : ViewModel() {
private val _kyc1uiState =
MutableStateFlow<KYCLevel1UiState>(KYCLevel1UiState.Loading)
val kyc1uiState: StateFlow<KYCLevel1UiState> = _kyc1uiState
fun submitData(
fname: String,
lname: String,
address1: String,
address2: String,
phoneno: String,
dob: String
) {
val kycLevel1Details =
KYCLevel1Details(
fname,
lname,
address1,
address2,
phoneno,
dob,
"1"
)
uploadKYCLevel1DetailsUseCase.walletRequestValues = UploadKYCLevel1Details.RequestValues(
mLocalRepository.clientDetails.clientId.toInt(),
kycLevel1Details
)
val requestValues = uploadKYCLevel1DetailsUseCase.walletRequestValues
mUseCaseHandler.execute(uploadKYCLevel1DetailsUseCase, requestValues,
object : UseCase.UseCaseCallback<UploadKYCLevel1Details.ResponseValue> {
override fun onSuccess(response: UploadKYCLevel1Details.ResponseValue) {
_kyc1uiState.value = KYCLevel1UiState.Success
}
override fun onError(message: String) {
_kyc1uiState.value = KYCLevel1UiState.Error
}
}
)
}
}
sealed interface KYCLevel1UiState {
data object Loading : KYCLevel1UiState
data object Success : KYCLevel1UiState
data object Error : KYCLevel1UiState
}

View File

@ -1,142 +1,43 @@
package org.mifos.mobilewallet.mifospay.kyc.ui
import android.app.DatePickerDialog
import android.app.DatePickerDialog.OnDateSetListener
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import butterknife.BindView
import butterknife.ButterKnife
import butterknife.OnClick
import com.hbb20.CountryCodePicker
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.ViewCompositionStrategy
import dagger.hilt.android.AndroidEntryPoint
import org.mifos.mobilewallet.mifospay.R
import org.mifos.mobilewallet.mifospay.base.BaseFragment
import org.mifos.mobilewallet.mifospay.kyc.KYCContract
import org.mifos.mobilewallet.mifospay.kyc.KYCContract.KYCLevel1View
import org.mifos.mobilewallet.mifospay.kyc.presenter.KYCLevel1Presenter
import org.mifos.mobilewallet.mifospay.common.Constants
import org.mifos.mobilewallet.mifospay.utils.Toaster
import org.mifos.mobilewallet.mifospay.utils.Utils.hideSoftKeyboard
import java.text.SimpleDateFormat
import java.util.Calendar
import javax.inject.Inject
import org.mifos.mobilewallet.mifospay.theme.MifosTheme
/**
* Created by ankur on 17/May/2018
*/
@AndroidEntryPoint
class KYCLevel1Fragment : BaseFragment(), KYCLevel1View {
@JvmField
@Inject
var mPresenter: KYCLevel1Presenter? = null
var mKYCLevel1Presenter: KYCContract.KYCLevel1Presenter? = null
@JvmField
@BindView(R.id.et_fname)
var etFname: EditText? = null
@JvmField
@BindView(R.id.et_lname)
var etLname: EditText? = null
@JvmField
@BindView(R.id.et_address1)
var etAddress1: EditText? = null
@JvmField
@BindView(R.id.et_address2)
var etAddress2: EditText? = null
@JvmField
@BindView(R.id.ccp_code)
var ccpPhonecode: CountryCodePicker? = null
@JvmField
@BindView(R.id.et_mobile_number)
var etMobileNumber: EditText? = null
@JvmField
@BindView(R.id.et_dob)
var etDOB: EditText? = null
@JvmField
@BindView(R.id.btn_submit)
var btnSubmit: Button? = null
var date: OnDateSetListener? = null
private lateinit var myCalendar: Calendar
class KYCLevel1Fragment : BaseFragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val rootView = inflater.inflate(R.layout.fragment_kyc_lvl1, container, false)
ButterKnife.bind(this, rootView)
mPresenter!!.attachView(this)
//setToolbarTitle(Constants.KYC_REGISTRATION_LEVEL_1);
myCalendar = Calendar.getInstance()
date = OnDateSetListener { view, year, monthOfYear, dayOfMonth ->
myCalendar.set(Calendar.YEAR, year)
myCalendar.set(Calendar.MONTH, monthOfYear)
myCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth)
val myFormat = Constants.DD_MM_YY
val sdf = SimpleDateFormat(myFormat)
etDOB!!.setText(sdf.format(myCalendar.getTime()))
}
ccpPhonecode!!.registerCarrierNumberEditText(etMobileNumber)
ccpPhonecode!!.setCustomMasterCountries(null)
return rootView
}
@OnClick(R.id.et_dob)
fun onClickDOB() {
myCalendar
?.get(Calendar.YEAR)?.let {
DatePickerDialog(
requireContext(), date, it, myCalendar!![Calendar.MONTH],
myCalendar!![Calendar.DAY_OF_MONTH]
).show()
): View {
return ComposeView(requireContext()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
MifosTheme {
KYCLevel1Screen(
navigateToKycLevel2 = { goBack() }
)
}
}
}
}
@OnClick(R.id.btn_submit)
fun onClickSubmit() {
showProgressDialog(Constants.PLEASE_WAIT)
val fname = etFname!!.text.toString().trim { it <= ' ' }
val lname = etLname!!.text.toString().trim { it <= ' ' }
val address1 = etAddress1!!.text.toString().trim { it <= ' ' }
val address2 = etAddress2!!.text.toString().trim { it <= ' ' }
val phoneno = ccpPhonecode!!.fullNumber
val dob = etDOB!!.text.toString().trim { it <= ' ' }
mKYCLevel1Presenter!!.submitData(fname, lname, address1, address2, phoneno, dob)
hideSoftKeyboard(requireActivity())
}
override fun showToast(s: String?) {
Toaster.show(view, s)
}
public override fun showProgressDialog(message: String?) {
super.showProgressDialog(message)
}
override fun hideProgressDialog() {
super.hideProgressDialog()
}
override fun goBack() {
private fun goBack() {
val intent = requireActivity().intent
requireActivity().finish()
startActivity(intent)
}
override fun setPresenter(presenter: KYCContract.KYCLevel1Presenter?) {
mKYCLevel1Presenter = presenter
}
companion object {
@JvmStatic
fun newInstance(): KYCLevel1Fragment {

View File

@ -0,0 +1,300 @@
package org.mifos.mobilewallet.mifospay.kyc.ui
import android.widget.Toast
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.material.TextFieldDefaults
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.MaterialTheme
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.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
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.maxkeppeker.sheets.core.models.base.rememberUseCaseState
import com.maxkeppeler.sheets.calendar.CalendarDialog
import com.maxkeppeler.sheets.calendar.models.CalendarConfig
import com.maxkeppeler.sheets.calendar.models.CalendarSelection
import com.maxkeppeler.sheets.calendar.models.CalendarStyle
import com.togitech.ccp.component.TogiCountryCodePicker
import org.mifos.mobilewallet.mifospay.R
import org.mifos.mobilewallet.mifospay.designsystem.component.MfOverlayLoadingWheel
import org.mifos.mobilewallet.mifospay.designsystem.component.MifosOutlinedTextField
import org.mifos.mobilewallet.mifospay.kyc.presenter.KYCLevel1UiState
import org.mifos.mobilewallet.mifospay.kyc.presenter.KYCLevel1ViewModel
import org.mifos.mobilewallet.mifospay.theme.MifosTheme
import java.time.format.DateTimeFormatter
@Composable
fun KYCLevel1Screen(
viewModel: KYCLevel1ViewModel = hiltViewModel(),
navigateToKycLevel2: () -> Unit
) {
val kyc1uiState by viewModel.kyc1uiState.collectAsStateWithLifecycle()
KYCLevel1Screen(
uiState = kyc1uiState,
submitData = { fname,
lname,
address1,
address2,
phoneno,
dob ->
viewModel.submitData(
fname.trim { it <= ' ' },
lname.trim { it <= ' ' },
address1.trim { it <= ' ' },
address2.trim { it <= ' ' },
phoneno.trim { it <= ' ' },
dob.trim { it <= ' ' }
)
},
navigateToKycLevel2 = navigateToKycLevel2
)
}
@Composable
fun KYCLevel1Screen(
uiState: KYCLevel1UiState,
submitData: (
String,
String,
String,
String,
String,
String
) -> Unit,
navigateToKycLevel2: () -> Unit
) {
val context = LocalContext.current
Kyc1Form(
modifier = Modifier,
submitData = submitData
)
when (uiState) {
KYCLevel1UiState.Loading -> {
MfOverlayLoadingWheel(stringResource(id = R.string.submitting))
}
KYCLevel1UiState.Error -> {
Toast.makeText(
context,
stringResource(R.string.error_adding_KYC_Level_1_details),
Toast.LENGTH_SHORT
).show()
navigateToKycLevel2.invoke()
}
KYCLevel1UiState.Success -> {
Toast.makeText(
context,
stringResource(R.string.successkyc1),
Toast.LENGTH_SHORT
).show()
navigateToKycLevel2.invoke()
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun Kyc1Form(
modifier: Modifier,
submitData: (
String,
String,
String,
String,
String,
String
) -> Unit
) {
var firstName by rememberSaveable { mutableStateOf("") }
var lastName by rememberSaveable { mutableStateOf("") }
var address1 by rememberSaveable { mutableStateOf("") }
var address2 by rememberSaveable { mutableStateOf("") }
var mobileNumber by rememberSaveable { mutableStateOf("") }
var dateOfBirth by rememberSaveable { mutableStateOf("") }
val dateState = rememberUseCaseState()
val dateFormatter = DateTimeFormatter.ofPattern(stringResource(R.string.date_format))
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Spacer(modifier = Modifier.height(20.dp))
MifosOutlinedTextField(
modifier = modifier
.fillMaxWidth()
.padding(vertical = 8.dp),
value = firstName,
onValueChange = {
firstName = it
},
label = R.string.first_name,
)
MifosOutlinedTextField(
modifier = modifier
.fillMaxWidth()
.padding(vertical = 8.dp),
value = lastName,
onValueChange = {
lastName = it
},
label = R.string.last_name,
)
MifosOutlinedTextField(
modifier = modifier
.fillMaxWidth()
.padding(vertical = 8.dp),
value = address1,
onValueChange = {
address1 = it
},
label = R.string.address_line_1,
)
MifosOutlinedTextField(
modifier = modifier
.fillMaxWidth()
.padding(vertical = 8.dp),
value = address2,
onValueChange = {
address2 = it
},
label = R.string.address_line_2,
)
Box(
modifier = Modifier
.padding(vertical = 7.dp)
) {
val keyboardController = LocalSoftwareKeyboardController.current
TogiCountryCodePicker(
modifier = Modifier,
shape = RoundedCornerShape(3.dp),
colors = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = MaterialTheme.colorScheme.primary
),
onValueChange = { (code, phone), isValid ->
if (isValid) {
mobileNumber = code + phone
}
},
label = {
Text(stringResource(id = R.string.phone_number))
},
keyboardActions = KeyboardActions { keyboardController?.hide() }
)
}
CalendarDialog(
state = dateState,
config = CalendarConfig(
monthSelection = true,
yearSelection = true,
style = CalendarStyle.MONTH
),
selection = CalendarSelection.Date { date ->
dateOfBirth = dateFormatter.format(date)
}
)
Box(
modifier = Modifier
.fillMaxWidth()
.height(70.dp)
.padding(vertical = 9.dp)
.clickable { dateState.show() }
.border(
width = 1.dp,
color = Color.Black
)
.padding(12.dp)
.clip(shape = RoundedCornerShape(8.dp))
) {
Text(
text = dateOfBirth.ifEmpty { stringResource(R.string.select_dob) },
style = MaterialTheme.typography.bodyLarge
)
}
Button(
onClick = {
submitData(
firstName,
lastName,
address1,
address2,
mobileNumber,
dateOfBirth
)
},
modifier = Modifier
.align(Alignment.CenterHorizontally)
.padding(vertical = 7.dp),
) {
Text(text = stringResource(R.string.submit))
}
}
}
@Preview(showBackground = true)
@Composable
fun Kyc1FormPreview() {
MifosTheme {
KYCLevel1Screen(
uiState = KYCLevel1UiState.Loading,
submitData = { _, _, _, _, _, _ -> },
navigateToKycLevel2 = {}
)
}
}
@Preview(showBackground = true)
@Composable
fun Kyc1PreviewWithError() {
MifosTheme {
KYCLevel1Screen(
uiState = KYCLevel1UiState.Error,
submitData = { _, _, _, _, _, _ -> },
navigateToKycLevel2 = {}
)
}
}
@Preview(showBackground = true)
@Composable
fun Kyc1FormPreviewWithSuccess() {
MifosTheme {
KYCLevel1Screen(
uiState = KYCLevel1UiState.Success,
submitData = { _, _, _, _, _, _ -> },
navigateToKycLevel2 = {}
)
}
}

View File

@ -303,6 +303,11 @@
<string name="deleted_successfully">Deleted Successfully</string>
<string name="error_kyc_details">Couldn\'t fetch KYC details. Please try again.</string>
<string name="loading">Loading</string>
<string name="date_format">dd-MM-yyyy</string>
<string name="select_dob">Select Date of Birth</string>
<string name="error_adding_KYC_Level_1_details">Error adding KYC Level 1 details.</string>
<string name="successkyc1">KYC Level 1 details added.</string>
<string name="submitting">Submitting</string>