TP-55716 | Manual vs Autopay sip (#10090)
Co-authored-by: Prakhar Saxena <prakhar.saxena@navi.com>
This commit is contained in:
@@ -29,6 +29,7 @@ import com.navi.amc.common.taskProcessor.AmcTaskManager
|
||||
import com.navi.amc.common.viewmodel.CheckerVM
|
||||
import com.navi.amc.common.viewmodel.PaymentSharedVM
|
||||
import com.navi.amc.databinding.CheckerActivityAmcBinding
|
||||
import com.navi.amc.fundbuy.models.SipDetailsData
|
||||
import com.navi.amc.navigator.NaviAmcDeeplinkNavigator
|
||||
import com.navi.amc.utils.*
|
||||
import com.navi.amc.utils.AmcAnalytics.AMC_LATENCY_ESIGN_CALLBACK_TIME
|
||||
@@ -41,12 +42,17 @@ import com.navi.amc.utils.AmcAnalytics.AMC_LATENCY_PAYMENT_INITIATION_TIME
|
||||
import com.navi.amc.utils.AmcAnalytics.AMC_LATENCY_PENNY_DROP_TIME
|
||||
import com.navi.amc.utils.AmcAnalytics.POLLING_TIMEOUT
|
||||
import com.navi.amc.utils.Constant.AMC_PERSONAL
|
||||
import com.navi.amc.utils.Constant.AUTOPAY_TYPE
|
||||
import com.navi.amc.utils.Constant.AUTO_PAY_CALLBACK_ERROR
|
||||
import com.navi.amc.utils.Constant.CHECKER_DATA
|
||||
import com.navi.amc.utils.Constant.CHECKER_TYPE
|
||||
import com.navi.amc.utils.Constant.FLOW_TYPE
|
||||
import com.navi.amc.utils.Constant.ORDER_ID
|
||||
import com.navi.amc.utils.Constant.OTP_FLOW_TYPE_SIP_AUTOPAY
|
||||
import com.navi.amc.utils.Constant.OTP_FLOW_TYPE_SIP_MANUAL
|
||||
import com.navi.amc.utils.Constant.PAYMENT_CALLBACK_ERROR
|
||||
import com.navi.amc.utils.Constant.REQUEST_CONFIG
|
||||
import com.navi.amc.utils.Constant.SIP_REFERENCE_ID
|
||||
import com.navi.amc.utils.Constant.TRANSACTION_ID
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.base.model.CtaData
|
||||
@@ -156,6 +162,7 @@ class CheckerActivity : BasePaymentActivity() {
|
||||
if(type == PAYMENT_CALLBACK_SYNC || type == AUTO_PAY_PAYMENT_CALLBACK_SYNC) {
|
||||
val requestId = intent.getStringExtra(TRANSACTION_ID)
|
||||
val requestConfig = intent.getParcelableExtra<RequestConfig>(REQUEST_CONFIG)
|
||||
viewModel.autopayType = intent.getStringExtra(AUTOPAY_TYPE)
|
||||
requestId?.let {
|
||||
apiPollInit(requestConfig, requestId)
|
||||
}
|
||||
@@ -351,6 +358,7 @@ class CheckerActivity : BasePaymentActivity() {
|
||||
response.data?.data?.statusData?.let {
|
||||
bundle.putParcelable(DATA, it)
|
||||
bundle.putString(TYPE, type)
|
||||
bundle.putString(SIP_REFERENCE_ID,viewModel.sipReferenceId)
|
||||
}
|
||||
addUpcomingPaymentParams(bundle, response.data?.status)
|
||||
response.data
|
||||
@@ -358,6 +366,21 @@ class CheckerActivity : BasePaymentActivity() {
|
||||
?.nextCTA
|
||||
?.toNavigateAmcModule(activity = this, finish = true, bundle = bundle)
|
||||
}
|
||||
} else if (intent.getStringExtra(FLOW_TYPE) in listOf(
|
||||
OTP_FLOW_TYPE_SIP_MANUAL,
|
||||
OTP_FLOW_TYPE_SIP_AUTOPAY
|
||||
) && response.data?.status == FirebaseStatusType.SUCCESS
|
||||
) {
|
||||
val sipDetailsData = SipDetailsData(
|
||||
scheme = intent?.getStringExtra(AmcAnalytics.ISIN),
|
||||
amount = intent?.getStringExtra(Constant.AMOUNT),
|
||||
frequency = intent?.getStringExtra(Constant.FREQUENCY),
|
||||
sipDate = intent?.getStringExtra(Constant.SIP_DATE),
|
||||
paymentMode = intent?.getStringExtra(Constant.PAYMENT_MODE),
|
||||
orderId = intent?.getStringExtra(ORDER_ID),
|
||||
autoPayChecked = intent.getStringExtra(FLOW_TYPE) == OTP_FLOW_TYPE_SIP_AUTOPAY
|
||||
)
|
||||
viewModel.postSipDetails(sipDetailsData)
|
||||
} else {
|
||||
onFailureResponse(response.errors?.firstOrNull())
|
||||
}
|
||||
|
||||
@@ -22,12 +22,14 @@ import com.navi.amc.databinding.AmcPaymentBottomsheetBinding
|
||||
import com.navi.amc.fundbuy.models.SipDetailsResponse
|
||||
import com.navi.amc.utils.AmcAnalytics
|
||||
import com.navi.amc.utils.Constant
|
||||
import com.navi.amc.utils.Constant.PAYMENT
|
||||
import com.navi.amc.utils.Constant.PAYMENT_MODE
|
||||
import com.navi.amc.utils.SubPageStatusType
|
||||
import com.navi.amc.utils.TempStorageHelper
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.base.model.LineItem
|
||||
import com.navi.base.utils.isNotNull
|
||||
import com.navi.base.utils.orFalse
|
||||
import com.navi.common.ui.activity.BaseActivity
|
||||
import com.navi.common.ui.fragment.BaseBottomSheet
|
||||
import com.navi.design.utils.parseColorSafe
|
||||
@@ -83,13 +85,29 @@ class AmcPaymentBottomSheet : BaseBottomSheet() {
|
||||
setTextColor(it.parseColorSafe())
|
||||
}
|
||||
setOnClickListener {
|
||||
AmcAnalytics.sendEvent(
|
||||
eventsData = response?.metaData?.clickedData,
|
||||
extraAttributes = hashMapOf(PAYMENT_MODE to paymentMode.orEmpty()),
|
||||
screenName = screenName
|
||||
)
|
||||
isPaymentAttempted = true
|
||||
initiateSipPayment()
|
||||
if (response?.footer?.nextCta?.url?.contains(PAYMENT).orFalse()) {
|
||||
AmcAnalytics.sendEvent(
|
||||
eventsData = response?.metaData?.clickedData,
|
||||
extraAttributes = hashMapOf(PAYMENT_MODE to paymentMode.orEmpty()),
|
||||
screenName = screenName
|
||||
)
|
||||
isPaymentAttempted = true
|
||||
initiateSipPayment()
|
||||
} else {
|
||||
val cta = response?.footer?.nextCta?.copy(
|
||||
parameters = response?.footer?.nextCta?.parameters?.toMutableList()
|
||||
?.apply {
|
||||
add(
|
||||
LineItem(
|
||||
PAYMENT_MODE,
|
||||
paymentMode
|
||||
)
|
||||
)
|
||||
})
|
||||
cta?.let { cta -> action?.invoke(cta) }
|
||||
safelyDismissDialog()
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
AmcAnalytics.sendEvent(
|
||||
|
||||
@@ -6,8 +6,6 @@
|
||||
|
||||
package com.navi.amc.common.fragment
|
||||
|
||||
import com.navi.naviwidgets.R as WidgetsR
|
||||
import com.navi.design.R as DesignR
|
||||
import android.content.Context
|
||||
import android.content.IntentFilter
|
||||
import android.os.Bundle
|
||||
@@ -35,10 +33,13 @@ import com.navi.amc.fundbuy.models.PaymentOrder
|
||||
import com.navi.amc.fundbuy.models.PaymentPostData
|
||||
import com.navi.amc.fundbuy.models.SipDetailsData
|
||||
import com.navi.amc.fundbuy.models.SipDetailsResponse
|
||||
import com.navi.amc.utils.*
|
||||
import com.navi.amc.utils.AmcAnalytics
|
||||
import com.navi.amc.utils.Constant
|
||||
import com.navi.amc.utils.Constant.AMOUNT
|
||||
import com.navi.amc.utils.Constant.API_CALL_MULTI_CLICK_THRESOLD_DUR
|
||||
import com.navi.amc.utils.Constant.AUTOPAY_CHECKED
|
||||
import com.navi.amc.utils.Constant.AUTO_PAY_PRESENT
|
||||
import com.navi.amc.utils.Constant.CAPS_DATA
|
||||
import com.navi.amc.utils.Constant.CREATE_REDEEM_ORDER
|
||||
import com.navi.amc.utils.Constant.DATA_SOURCE
|
||||
import com.navi.amc.utils.Constant.DISMISS
|
||||
@@ -48,6 +49,8 @@ import com.navi.amc.utils.Constant.OTP_COUNTDOWN_IN_SECOND
|
||||
import com.navi.amc.utils.Constant.OTP_FLOW_TYPE_LUMPSUM_PURCHASE
|
||||
import com.navi.amc.utils.Constant.OTP_FLOW_TYPE_REDEEMPTION
|
||||
import com.navi.amc.utils.Constant.OTP_FLOW_TYPE_RETRY_PAYMENT
|
||||
import com.navi.amc.utils.Constant.OTP_FLOW_TYPE_SIP_AUTOPAY
|
||||
import com.navi.amc.utils.Constant.OTP_FLOW_TYPE_SIP_MANUAL
|
||||
import com.navi.amc.utils.Constant.OTP_FLOW_TYPE_SIP_PURCHASE
|
||||
import com.navi.amc.utils.Constant.REDEMPTION_ORDER_ID
|
||||
import com.navi.amc.utils.Constant.REQUEST_CONFIG
|
||||
@@ -55,8 +58,15 @@ import com.navi.amc.utils.Constant.SECONDS_PER_MINUTE
|
||||
import com.navi.amc.utils.Constant.SIP_DATE
|
||||
import com.navi.amc.utils.Constant.SIP_REFERENCE_ID
|
||||
import com.navi.amc.utils.Constant.TRANSACTION_ID
|
||||
import com.navi.amc.utils.TempStorageHelper
|
||||
import com.navi.amc.utils.getPaymentSyncFlowStatusCta
|
||||
import com.navi.amc.utils.toNavigateAmcModule
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.base.utils.*
|
||||
import com.navi.base.utils.BaseUtils
|
||||
import com.navi.base.utils.isNotNullAndNotEmpty
|
||||
import com.navi.base.utils.orElse
|
||||
import com.navi.base.utils.orFalse
|
||||
import com.navi.base.utils.orTrue
|
||||
import com.navi.common.customview.BoxInputGroup.Companion.OTP_LENGTH_4
|
||||
import com.navi.common.listeners.FragmentInterchangeListener
|
||||
import com.navi.common.listeners.HeaderInteractionListener
|
||||
@@ -69,10 +79,12 @@ import com.navi.common.utils.RETRY
|
||||
import com.navi.common.utils.getErrorData
|
||||
import com.navi.design.utils.getNaviDrawable
|
||||
import com.navi.design.utils.setSpannableString
|
||||
import com.navi.paymentclients.viewmodel.base.PaymentManager
|
||||
import com.navi.payment.listener.PaymentListener
|
||||
import com.navi.payment.utils.PaymentAnalytics
|
||||
import com.navi.paymentclients.viewmodel.base.PaymentManager
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import com.navi.design.R as DesignR
|
||||
import com.navi.naviwidgets.R as WidgetsR
|
||||
|
||||
@AndroidEntryPoint
|
||||
class OtpFragment : AmcBaseFragment(), View.OnClickListener {
|
||||
@@ -106,14 +118,16 @@ class OtpFragment : AmcBaseFragment(), View.OnClickListener {
|
||||
viewStub.layoutResource = R.layout.otp_fragment_amc
|
||||
binding = DataBindingUtil.getBinding(viewStub.inflate())!!
|
||||
initError(viewModel, buttonListener = {
|
||||
when(it){
|
||||
when (it) {
|
||||
DISMISS -> {
|
||||
popThisFromBackStack()
|
||||
}
|
||||
|
||||
RETRY -> {
|
||||
generateOtp(true)
|
||||
}
|
||||
else ->{
|
||||
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -138,7 +152,7 @@ class OtpFragment : AmcBaseFragment(), View.OnClickListener {
|
||||
// inNewTranscation will be false only for new sip purchase flow
|
||||
private fun fetchData() {
|
||||
var isNewTransaction = true
|
||||
if(flowType == OTP_FLOW_TYPE_SIP_PURCHASE) {
|
||||
if (flowType == OTP_FLOW_TYPE_SIP_PURCHASE) {
|
||||
isNewTransaction = arguments?.getString(SIP_REFERENCE_ID).isNotNullAndNotEmpty()
|
||||
}
|
||||
viewModel.fetchScreenData(flowType, isin, isNewTransaction)
|
||||
@@ -188,7 +202,7 @@ class OtpFragment : AmcBaseFragment(), View.OnClickListener {
|
||||
viewModel.generateOtpResponse.observe(viewLifecycleOwner) {
|
||||
hideLoader()
|
||||
binding.otpLayout.clear()
|
||||
if(it?.isResendOtp.orFalse()) hideResendOtpOnCallUiState()
|
||||
if (it?.isResendOtp.orFalse()) hideResendOtpOnCallUiState()
|
||||
}
|
||||
viewModel.verifyOtpResponse.observe(viewLifecycleOwner) {
|
||||
hideLoader()
|
||||
@@ -279,6 +293,7 @@ class OtpFragment : AmcBaseFragment(), View.OnClickListener {
|
||||
paymentVM.postPaymentStatus(it)
|
||||
val url = getPaymentSyncFlowStatusCta(CheckerActivity.PAYMENT_CALLBACK_SYNC)
|
||||
val bundle = Bundle().apply {
|
||||
putAll(arguments)
|
||||
putString(
|
||||
TRANSACTION_ID,
|
||||
viewModel.paymentInitiateData.value?.tokenDetails?.transactionId
|
||||
@@ -298,6 +313,17 @@ class OtpFragment : AmcBaseFragment(), View.OnClickListener {
|
||||
fragmentInterchangeListener?.navigateToNextScreen(ActionData(url = url), bundle)
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.sipCreateResponse.observe(viewLifecycleOwner) { response ->
|
||||
val bundle = Bundle()
|
||||
response?.data?.statusData?.let {
|
||||
bundle.putParcelable(CAPS_DATA, it)
|
||||
bundle.putString(SIP_REFERENCE_ID, viewModel.sipReferenceId)
|
||||
}
|
||||
response?.data
|
||||
?.nextCTA
|
||||
?.toNavigateAmcModule(activity = activity, bundle = bundle)
|
||||
}
|
||||
}
|
||||
|
||||
private fun fireOtpVerificationResultEvent(isValid: Boolean) {
|
||||
@@ -338,6 +364,36 @@ class OtpFragment : AmcBaseFragment(), View.OnClickListener {
|
||||
"", OTP_FLOW_TYPE_REDEEMPTION -> {
|
||||
redeemUnits(otpResponseData)
|
||||
}
|
||||
|
||||
OTP_FLOW_TYPE_SIP_AUTOPAY -> {
|
||||
val ctaAction = viewModel.dataResponse.value?.footer?.nextCta
|
||||
if (ctaAction?.type == AUTO_PAY_PRESENT) {
|
||||
viewModel.createSip(
|
||||
SipDetailsData(
|
||||
scheme = arguments?.getString(AmcAnalytics.ISIN),
|
||||
amount = arguments?.getString(AMOUNT),
|
||||
frequency = arguments?.getString(FREQUENCY),
|
||||
sipDate = arguments?.getString(SIP_DATE),
|
||||
autoPayChecked = true
|
||||
)
|
||||
)
|
||||
} else {
|
||||
fragmentInterchangeListener?.navigateToNextScreen(
|
||||
viewModel.dataResponse.value?.footer?.nextCta, arguments ?: Bundle()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
OTP_FLOW_TYPE_SIP_MANUAL -> {
|
||||
val sipDetailsData = SipDetailsData(
|
||||
scheme = arguments?.getString(AmcAnalytics.ISIN),
|
||||
amount = arguments?.getString(AMOUNT),
|
||||
frequency = arguments?.getString(FREQUENCY),
|
||||
sipDate = arguments?.getString(SIP_DATE),
|
||||
paymentMode = arguments?.getString(Constant.PAYMENT_MODE)
|
||||
)
|
||||
viewModel.initiateSipPayment(sipDetailsData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,7 +487,7 @@ class OtpFragment : AmcBaseFragment(), View.OnClickListener {
|
||||
|
||||
if (isinParam.isNotNullAndNotEmpty()) {
|
||||
this.isin = isinParam
|
||||
}else{
|
||||
} else {
|
||||
this.isin = fundIdParam
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ class StatusFragment : AmcBaseFragment() , RewardDialogCancelListener {
|
||||
activity?.onBackPressed()
|
||||
} else {
|
||||
fragmentInterchangeListener?.navigateToNextScreen(
|
||||
redirectCta ?: viewModel.dataItems.value?.footer?.nextCta
|
||||
redirectCta ?: viewModel.dataItems.value?.footer?.nextCta, arguments ?: Bundle()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ package com.navi.amc.common.repo
|
||||
|
||||
import com.navi.amc.common.activity.CheckerActivity
|
||||
import com.navi.amc.common.model.CheckerResponse
|
||||
import com.navi.amc.fundbuy.models.SipDetailsData
|
||||
import com.navi.amc.network.retrofit.RetrofitService
|
||||
import com.navi.common.network.models.RepoResult
|
||||
import com.navi.common.network.retrofit.ResponseCallback
|
||||
@@ -48,8 +49,8 @@ class CheckerRepository @Inject constructor(private val retrofitService: Retrofi
|
||||
suspend fun fetchAsyncRequestDataPayment(requestId: String) =
|
||||
apiResponseCallback(retrofitService.fetchAsyncRequestDataPayment(requestId))
|
||||
|
||||
suspend fun fetchAsyncRequestAutoPayDataPayment(requestId: String) =
|
||||
apiResponseCallback(retrofitService.fetchAsyncRequestAutoPayDataPayment(requestId))
|
||||
suspend fun fetchAsyncRequestAutoPayDataPayment(requestId: String , autopayType: String? = null) =
|
||||
apiResponseCallback(retrofitService.fetchAsyncRequestAutoPayDataPayment(requestId, autopayType))
|
||||
|
||||
suspend fun fetchEsignAsyncRequestDataWithNextCta(requestId: String) =
|
||||
apiResponseCallback(retrofitService.fetchEsignAsyncRequestDataWithNextCta(requestId))
|
||||
@@ -75,4 +76,10 @@ class CheckerRepository @Inject constructor(private val retrofitService: Retrofi
|
||||
suspend fun postNameDataV2() =
|
||||
apiResponseCallback(retrofitService.postKycNameDataV2())
|
||||
|
||||
suspend fun postSipDetails(details: SipDetailsData) =
|
||||
apiResponseCallback(retrofitService.postSipDetails(details))
|
||||
|
||||
suspend fun fetchSipSuccessPage() =
|
||||
apiResponseCallback(retrofitService.fetchSipSuccessPage())
|
||||
|
||||
}
|
||||
@@ -42,4 +42,10 @@ class OTPRepository @Inject constructor(private val retrofitService: RetrofitSer
|
||||
|
||||
suspend fun postSameOrderPayment(details: PaymentOrder) =
|
||||
apiResponseCallback(retrofitService.postSameOrderPayment(details))
|
||||
|
||||
suspend fun initiateSipPayment(sipDetails: SipDetailsData) =
|
||||
apiResponseCallback(retrofitService.initiateSipPayment(sipDetails))
|
||||
|
||||
suspend fun fetchSipSuccessPage() =
|
||||
apiResponseCallback(retrofitService.fetchSipSuccessPage())
|
||||
}
|
||||
|
||||
@@ -16,15 +16,18 @@ import com.navi.amc.common.model.KycCheckerContent
|
||||
import com.navi.amc.common.model.NextCtaResponse
|
||||
import com.navi.amc.common.repo.CheckerRepository
|
||||
import com.navi.amc.common.taskProcessor.AmcTaskManager
|
||||
import com.navi.amc.fundbuy.models.SipDetailsData
|
||||
import com.navi.amc.kyc.model.KycPersonalDetailsResponse
|
||||
import com.navi.amc.utils.AmcAnalytics
|
||||
import com.navi.amc.utils.TempStorageHelper
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.base.utils.EMPTY
|
||||
import com.navi.base.utils.isNotNull
|
||||
import com.navi.common.firebasedb.FirebaseStatusType
|
||||
import com.navi.common.network.models.GenericErrorResponse
|
||||
import com.navi.common.network.models.RepoResult
|
||||
import com.navi.common.network.models.SuccessResponse
|
||||
import com.navi.common.network.models.isSuccessWithData
|
||||
import com.navi.design.textview.model.NaviSpan
|
||||
import com.navi.design.textview.model.TextWithStyle
|
||||
import com.navi.naviwidgets.models.response.DataSafeWidget
|
||||
@@ -32,6 +35,7 @@ import com.navi.naviwidgets.models.response.NoteWidget
|
||||
import com.navi.payment.model.clientmodels.PostPaymentData
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -79,6 +83,9 @@ class CheckerVM @Inject constructor(private val repository: CheckerRepository) :
|
||||
val panErrorData: LiveData<GenericErrorResponse?>
|
||||
get() = _panErrorData
|
||||
|
||||
var sipReferenceId = EMPTY
|
||||
var autopayType: String? = null
|
||||
|
||||
var isNavigationRequiredOnFinish = true
|
||||
|
||||
val placeHolderPanCheckerData = CheckerResponse(
|
||||
@@ -222,7 +229,7 @@ class CheckerVM @Inject constructor(private val repository: CheckerRepository) :
|
||||
}
|
||||
CheckerActivity.AUTO_PAY_PAYMENT_CALLBACK,
|
||||
CheckerActivity.AUTO_PAY_PAYMENT_CALLBACK_SYNC -> {
|
||||
val response = repository.fetchAsyncRequestAutoPayDataPayment(requestId)
|
||||
val response = repository.fetchAsyncRequestAutoPayDataPayment(requestId, autopayType)
|
||||
_asyncResponse.value = response
|
||||
}
|
||||
CheckerActivity.PL_DISBURSED_JOURNEY -> {
|
||||
@@ -259,6 +266,23 @@ class CheckerVM @Inject constructor(private val repository: CheckerRepository) :
|
||||
}
|
||||
}
|
||||
|
||||
fun postSipDetails(details: SipDetailsData) {
|
||||
viewModelScope.launch {
|
||||
val responseAsync = async {
|
||||
repository.postSipDetails(details)
|
||||
}
|
||||
val successDataAsync = async {
|
||||
repository.fetchSipSuccessPage()
|
||||
}
|
||||
val response = responseAsync.await()
|
||||
val successData = successDataAsync.await()
|
||||
if (response.isSuccessWithData() && successData.isSuccessWithData()) {
|
||||
sipReferenceId = response.data?.sipReferenceId.orEmpty()
|
||||
_asyncResponse.value = successData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun isBackAllowed(type: String): Boolean {
|
||||
return type !in listOf(
|
||||
CheckerActivity.KYC_CALLBACK,
|
||||
|
||||
@@ -25,13 +25,16 @@ import com.navi.amc.fundbuy.models.SipDetailsResponse
|
||||
import com.navi.amc.utils.AmcAnalytics
|
||||
import com.navi.amc.utils.Constant
|
||||
import com.navi.amc.utils.updateCheckerResponse
|
||||
import com.navi.base.utils.EMPTY
|
||||
import com.navi.common.network.ApiConstants.API_WRONG_OTP
|
||||
import com.navi.common.network.models.RepoResult
|
||||
import com.navi.common.network.models.isSuccessWithData
|
||||
import com.navi.common.utils.Constants
|
||||
import com.navi.common.utils.SingleLiveEvent
|
||||
import com.navi.common.viewmodel.BaseVM
|
||||
import com.navi.payment.utils.PaymentAnalytics
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -63,6 +66,12 @@ class OTPVM @Inject constructor(private val repository: OTPRepository) : BaseAmc
|
||||
val sipDetailsResponse: LiveData<SipDetailsResponse?>
|
||||
get() = _sipDetailsResponse
|
||||
|
||||
private val _sipCreateResponse = SingleLiveEvent<AdditionalDataAsyncResponse<NextCtaResponse>?>()
|
||||
val sipCreateResponse: LiveData<AdditionalDataAsyncResponse<NextCtaResponse>?>
|
||||
get() = _sipCreateResponse
|
||||
|
||||
var sipReferenceId = EMPTY
|
||||
|
||||
fun fetchScreenData(flowType: String, isin: String? = null, isNewTransaction: Boolean) {
|
||||
viewModelScope.launch {
|
||||
val response = repository.getOtpDetails(flowType, isin, isNewTransaction)
|
||||
@@ -151,6 +160,30 @@ class OTPVM @Inject constructor(private val repository: OTPRepository) : BaseAmc
|
||||
}
|
||||
}
|
||||
|
||||
fun initiateSipPayment(sipDetailsData: SipDetailsData) {
|
||||
viewModelScope.launch {
|
||||
val response = repository.initiateSipPayment(sipDetailsData)
|
||||
consumePaymentInitiateResponse(response)
|
||||
}
|
||||
}
|
||||
|
||||
fun createSip(details: SipDetailsData) {
|
||||
viewModelScope.launch {
|
||||
val responseAsync = async {
|
||||
repository.postSipDetails(details)
|
||||
}
|
||||
val successDataAsync = async {
|
||||
repository.fetchSipSuccessPage()
|
||||
}
|
||||
val response = responseAsync.await()
|
||||
val successData = successDataAsync.await()
|
||||
if (response.isSuccessWithData() && successData.isSuccessWithData()) {
|
||||
sipReferenceId = response.data?.sipReferenceId.orEmpty()
|
||||
_sipCreateResponse.value = successData.data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun clearData() {
|
||||
errorResponse.value = null
|
||||
_sipDetailsResponse.value = null
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package com.navi.amc.fundbuy.adapters
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.navi.amc.R
|
||||
import com.navi.amc.databinding.SipTypeOptionLayoutBinding
|
||||
import com.navi.amc.fundbuy.models.SipOption
|
||||
import com.navi.amc.utils.ColorUtils
|
||||
import com.navi.base.utils.isValidIndex
|
||||
import com.navi.base.utils.orFalse
|
||||
import com.navi.design.utils.CornerRadius
|
||||
import com.navi.design.utils.dpToPx
|
||||
import com.navi.design.utils.dpToPxInInt
|
||||
import com.navi.design.utils.getNaviDrawable
|
||||
import com.navi.design.utils.parseColorSafe
|
||||
import com.navi.design.utils.setSpannableString
|
||||
|
||||
class SipTypeListAdapter(
|
||||
private val items: List<SipOption>,
|
||||
val listener: ((SipOption) -> Unit)? = null
|
||||
) : RecyclerView.Adapter<SipTypeViewHolder>() {
|
||||
var lastCheckedPosition = -1
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SipTypeViewHolder {
|
||||
return SipTypeViewHolder(
|
||||
DataBindingUtil.inflate(
|
||||
LayoutInflater.from(parent.context),
|
||||
R.layout.sip_type_option_layout,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return items.size
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SipTypeViewHolder, position: Int) {
|
||||
if (!isValidIndex(position, itemCount)) return
|
||||
val itemData = items[position]
|
||||
holder.binding.apply {
|
||||
title.setSpannableString(itemData.title)
|
||||
root.background =
|
||||
ColorUtils.getBackGroundColor4RoundedDrawable(holder.binding.root.context)
|
||||
label.isVisible =
|
||||
itemData.label?.let {
|
||||
label.setSpannableString(it.title)
|
||||
label.background =
|
||||
getNaviDrawable(
|
||||
cornerRadius = dpToPxInInt(4),
|
||||
backgroundColor = it.bgColor.parseColorSafe()
|
||||
)
|
||||
true
|
||||
} ?: run { false }
|
||||
if (lastCheckedPosition != -1)
|
||||
items[position].isSelected = (lastCheckedPosition == position)
|
||||
radio.isClickable = false
|
||||
if (lastCheckedPosition == -1 && itemData.isSelected.orFalse()) {
|
||||
lastCheckedPosition = position
|
||||
listener?.invoke(itemData)
|
||||
}
|
||||
radio.isChecked = (lastCheckedPosition == position)
|
||||
if (radio.isChecked || itemData.toShowNote.orFalse()) {
|
||||
note.setSpannableString(itemData.note?.title)
|
||||
note.background =
|
||||
getNaviDrawable(
|
||||
radii = CornerRadius(leftBottom = dpToPx(4), rightBottom = dpToPx(4)),
|
||||
backgroundColor = itemData.note?.bgColor.parseColorSafe()
|
||||
)
|
||||
root.setBackgroundResource(R.drawable.bg_purple_rounded_4_amc)
|
||||
} else {
|
||||
note.isVisible = false
|
||||
}
|
||||
root.setOnClickListener {
|
||||
radio.isChecked = true
|
||||
val tempLastCheckedPosition = lastCheckedPosition
|
||||
lastCheckedPosition = position
|
||||
notifyItemChanged(tempLastCheckedPosition)
|
||||
notifyItemChanged(lastCheckedPosition)
|
||||
listener?.invoke(itemData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SipTypeViewHolder(val binding: SipTypeOptionLayoutBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
@@ -57,8 +57,8 @@ class AutoPaySetupFragment() : AmcBaseFragment(), FooterInteractionListener {
|
||||
override val screenName: String
|
||||
get() = AUTO_PAY_SETUP
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
fetchData()
|
||||
}
|
||||
|
||||
@@ -219,6 +219,7 @@ class AutoPaySetupFragment() : AmcBaseFragment(), FooterInteractionListener {
|
||||
)
|
||||
putString(PaymentAnalytics.PROVIDER, it.provider)
|
||||
putParcelable(Constant.PAYMENT_DATA, it)
|
||||
putAll(arguments)
|
||||
}
|
||||
fragmentInterchangeListener?.navigateToNextScreen(ActionData(url = url), bundle)
|
||||
}
|
||||
|
||||
@@ -784,18 +784,26 @@ class FundBuyingFragmentV2 : AmcBaseFragment(), WidgetCallback {
|
||||
)
|
||||
), isNeededForFirebase = true
|
||||
)
|
||||
|
||||
val bottomSheetData = viewModel.getSipBottomSheetData(
|
||||
binding.sipAmount.widgetBinding.plainTextInput.text.toString(),
|
||||
frequency,
|
||||
binding.autopayCheckbox.checkbox.isChecked
|
||||
)
|
||||
val bundle = Bundle().apply { putString(Constant.DATA, bottomSheetData) }
|
||||
getBottomSheet(
|
||||
SubPageStatusType.AMC_COMMON_BOTTOMSHEET,
|
||||
bundle = bundle,
|
||||
genericListener = ::sipWeeklyFortnightlyAction
|
||||
)?.let { safelyShowBottomSheet(it, SubPageStatusType.HORIZONTAL_BUTTON_BOTTOMSHEET) }
|
||||
if (viewModel.isManualVsAutopayExperiment()) {
|
||||
sipWeeklyFortnightlyAction(null)
|
||||
} else {
|
||||
val bottomSheetData = viewModel.getSipBottomSheetData(
|
||||
binding.sipAmount.widgetBinding.plainTextInput.text.toString(),
|
||||
frequency,
|
||||
binding.autopayCheckbox.checkbox.isChecked
|
||||
)
|
||||
val bundle = Bundle().apply { putString(Constant.DATA, bottomSheetData) }
|
||||
getBottomSheet(
|
||||
SubPageStatusType.AMC_COMMON_BOTTOMSHEET,
|
||||
bundle = bundle,
|
||||
genericListener = ::sipWeeklyFortnightlyAction
|
||||
)?.let {
|
||||
safelyShowBottomSheet(
|
||||
it,
|
||||
SubPageStatusType.HORIZONTAL_BUTTON_BOTTOMSHEET
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sendEvent(
|
||||
getCreateSipEventName(), hashMapOf(
|
||||
@@ -843,18 +851,26 @@ class FundBuyingFragmentV2 : AmcBaseFragment(), WidgetCallback {
|
||||
)
|
||||
), isNeededForFirebase = true
|
||||
)
|
||||
|
||||
val bottomSheetData = viewModel.getSipBottomSheetData(
|
||||
binding.sipAmount.widgetBinding.plainTextInput.text.toString(),
|
||||
frequency,
|
||||
binding.autopayCheckbox.checkbox.isChecked
|
||||
)
|
||||
val bundle = Bundle().apply { putString(Constant.DATA, bottomSheetData) }
|
||||
getBottomSheet(
|
||||
SubPageStatusType.AMC_COMMON_BOTTOMSHEET,
|
||||
bundle = bundle,
|
||||
genericListener = ::sipMonthlyAction
|
||||
)?.let { safelyShowBottomSheet(it, SubPageStatusType.HORIZONTAL_BUTTON_BOTTOMSHEET) }
|
||||
if (viewModel.isManualVsAutopayExperiment()) {
|
||||
sipMonthlyAction(null)
|
||||
} else {
|
||||
val bottomSheetData = viewModel.getSipBottomSheetData(
|
||||
binding.sipAmount.widgetBinding.plainTextInput.text.toString(),
|
||||
frequency,
|
||||
binding.autopayCheckbox.checkbox.isChecked
|
||||
)
|
||||
val bundle = Bundle().apply { putString(Constant.DATA, bottomSheetData) }
|
||||
getBottomSheet(
|
||||
SubPageStatusType.AMC_COMMON_BOTTOMSHEET,
|
||||
bundle = bundle,
|
||||
genericListener = ::sipMonthlyAction
|
||||
)?.let {
|
||||
safelyShowBottomSheet(
|
||||
it,
|
||||
SubPageStatusType.HORIZONTAL_BUTTON_BOTTOMSHEET
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sendEvent(
|
||||
getCreateSipEventName(), hashMapOf(
|
||||
@@ -981,8 +997,8 @@ class FundBuyingFragmentV2 : AmcBaseFragment(), WidgetCallback {
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSipRecurringAction(action: ActionData, includeSipDate: Boolean) {
|
||||
if (action.url == DISMISS) {
|
||||
private fun createSipRecurringAction(action: ActionData?, includeSipDate: Boolean) {
|
||||
if (action?.url == DISMISS) {
|
||||
// do nothing
|
||||
return
|
||||
}
|
||||
@@ -995,8 +1011,11 @@ class FundBuyingFragmentV2 : AmcBaseFragment(), WidgetCallback {
|
||||
val autoPayChecked =
|
||||
if (viewModel.isAutoPayThere()) binding.autopayCheckbox.checkbox.isChecked.toString() else null
|
||||
|
||||
if (ctaUrl.endsWith(SubPageStatusType.OTP, true)) {
|
||||
|
||||
if (ctaUrl.endsWith(
|
||||
SubPageStatusType.OTP,
|
||||
true
|
||||
) || ctaUrl.endsWith(SubPageStatusType.SIP_TYPE, true)
|
||||
) {
|
||||
val bundle = Bundle().apply {
|
||||
|
||||
putString(AmcAnalytics.ISIN, isin)
|
||||
@@ -1034,8 +1053,8 @@ class FundBuyingFragmentV2 : AmcBaseFragment(), WidgetCallback {
|
||||
}
|
||||
}
|
||||
|
||||
private fun sipWeeklyFortnightlyAction(action: ActionData) = createSipRecurringAction(action, false)
|
||||
private fun sipMonthlyAction(action: ActionData) = createSipRecurringAction(action, true)
|
||||
private fun sipWeeklyFortnightlyAction(action: ActionData?) = createSipRecurringAction(action, false)
|
||||
private fun sipMonthlyAction(action: ActionData?) = createSipRecurringAction(action, true)
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
|
||||
@@ -0,0 +1,235 @@
|
||||
package com.navi.amc.fundbuy.fragments
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.ViewStub
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import com.google.gson.Gson
|
||||
import com.navi.amc.common.fragment.AmcBaseFragment
|
||||
import com.navi.amc.databinding.SipTypeLayoutBinding
|
||||
import com.navi.amc.fundbuy.adapters.SipTypeListAdapter
|
||||
import com.navi.amc.fundbuy.models.AmountPageFooter
|
||||
import com.navi.amc.fundbuy.models.GenericFooter
|
||||
import com.navi.amc.fundbuy.models.SipOption
|
||||
import com.navi.amc.fundbuy.models.SipTypeScreenContent
|
||||
import com.navi.amc.fundbuy.models.SipTypeScreenData
|
||||
import com.navi.amc.fundbuy.models.SipTypeScreenState
|
||||
import com.navi.amc.fundbuy.viewmodel.SipTypeViewModel
|
||||
import com.navi.amc.utils.AmcAnalytics.ISIN
|
||||
import com.navi.amc.utils.AmcAnalytics.SIP_TYPE
|
||||
import com.navi.amc.utils.Constant
|
||||
import com.navi.amc.utils.Constant.AMOUNT
|
||||
import com.navi.amc.utils.Constant.CONTINUE
|
||||
import com.navi.amc.utils.Constant.DISMISS
|
||||
import com.navi.amc.utils.Constant.FREQUENCY
|
||||
import com.navi.amc.utils.Constant.PAYMENT_MODE
|
||||
import com.navi.amc.utils.Constant.SHOW_BOTTOMSHEET
|
||||
import com.navi.amc.utils.Constant.SIP_DATE
|
||||
import com.navi.amc.utils.Constant.SIP_START_DATE
|
||||
import com.navi.amc.utils.SubPageStatusType
|
||||
import com.navi.amc.utils.getBottomSheet
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.base.utils.CurrencyUtils
|
||||
import com.navi.common.listeners.FragmentInterchangeListener
|
||||
import com.navi.common.model.Header
|
||||
import com.navi.design.utils.setSpannableString
|
||||
import com.navi.naviwidgets.R
|
||||
import com.navi.naviwidgets.extensions.showWhenDataIsAvailable
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import java.math.BigDecimal
|
||||
import com.navi.amc.R as amcR
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SipTypeFragment() : AmcBaseFragment() {
|
||||
private val viewModel by viewModels<SipTypeViewModel>()
|
||||
private lateinit var binding: SipTypeLayoutBinding
|
||||
override val screenName: String
|
||||
get() = SIP_TYPE
|
||||
|
||||
override fun onAttach(context: Context) {
|
||||
super.onAttach(context)
|
||||
fragmentInterchangeListener = context as? FragmentInterchangeListener
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
fetchData()
|
||||
}
|
||||
|
||||
override fun setContainerView(viewStub: ViewStub) {
|
||||
viewStub.layoutResource = amcR.layout.sip_type_layout
|
||||
binding = DataBindingUtil.getBinding(viewStub.inflate())!!
|
||||
initObservers()
|
||||
}
|
||||
|
||||
private fun initObservers() {
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.sipTypeViewState.collectLatest {
|
||||
when (it) {
|
||||
is SipTypeScreenState.Loading -> {
|
||||
showLoader()
|
||||
}
|
||||
|
||||
is SipTypeScreenState.Success -> {
|
||||
hideLoader()
|
||||
initUI(it.data)
|
||||
}
|
||||
|
||||
is SipTypeScreenState.Error -> {
|
||||
hideLoader()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchData() {
|
||||
val map = mutableMapOf<String, String>()
|
||||
arguments?.getString(ISIN)?.let {
|
||||
map.put(ISIN, it)
|
||||
}
|
||||
arguments?.getString(AMOUNT)?.let {
|
||||
map.put(AMOUNT, it)
|
||||
}
|
||||
arguments?.getString(FREQUENCY)?.let {
|
||||
map.put(FREQUENCY, it)
|
||||
}
|
||||
arguments?.getString(SIP_DATE)?.let {
|
||||
map.put(SIP_START_DATE, it)
|
||||
}
|
||||
viewModel.fetchSipTypeData(map)
|
||||
}
|
||||
|
||||
private fun initUI(data: SipTypeScreenData) {
|
||||
data.header?.let { initHeader(it) }
|
||||
data.content?.let { initContent(it) }
|
||||
}
|
||||
|
||||
private fun initHeader(header: Header) {
|
||||
setHeaderProperties(header)
|
||||
hideDivider()
|
||||
}
|
||||
|
||||
private fun initContent(content: SipTypeScreenContent) {
|
||||
binding.fundHeader.setProperties(content.fundHeaderData, ::navigate)
|
||||
binding.title.setSpannableString(content.title)
|
||||
initRecyclerView(content.items)
|
||||
}
|
||||
|
||||
private fun initRecyclerView(items: List<SipOption>? = null) {
|
||||
items?.let {
|
||||
binding.items.adapter = SipTypeListAdapter(it, ::clickAction)
|
||||
val itemDecorator = DividerItemDecoration(context, DividerItemDecoration.VERTICAL)
|
||||
context?.let {
|
||||
ContextCompat.getDrawable(it, R.drawable.empty_space_divider)?.let { drawable ->
|
||||
itemDecorator.setDrawable(drawable)
|
||||
}
|
||||
}
|
||||
binding.items.addItemDecoration(itemDecorator)
|
||||
}
|
||||
}
|
||||
|
||||
private fun clickAction(item: SipOption) {
|
||||
viewModel.selectedType = item.id.orEmpty()
|
||||
viewModel.nextPageCta = item.actionData
|
||||
setFooterData()
|
||||
}
|
||||
|
||||
private fun setFooterData() {
|
||||
viewModel.getPaymentFooter()?.let { footer ->
|
||||
when (footer) {
|
||||
is AmountPageFooter -> {
|
||||
binding.footer.root.isVisible = false
|
||||
binding.paymentFooter.apply {
|
||||
root.isVisible = true
|
||||
account.setSpannableString(footer.paymentCta?.account)
|
||||
footer.paymentCta?.let {
|
||||
val amountData = arguments?.getString(Constant.AMOUNT)
|
||||
amount.text =
|
||||
CurrencyUtils.getNormalizedAmount(
|
||||
if (!amountData.isNullOrEmpty()) amountData.toBigDecimal()
|
||||
else BigDecimal(0)
|
||||
)
|
||||
leftIcon.showWhenDataIsAvailable(it.leftIcon)
|
||||
rightIcon.showWhenDataIsAvailable(it.rightIcon)
|
||||
actionText.showWhenDataIsAvailable(it.actionText)
|
||||
llAction.setOnClickListener { view ->
|
||||
performAction(it.actionData)
|
||||
}
|
||||
}
|
||||
btn.text = footer.nextCta?.title
|
||||
btn.setOnClickListener { showSipBottomSheet() }
|
||||
}
|
||||
}
|
||||
|
||||
is GenericFooter -> {
|
||||
binding.paymentFooter.root.isVisible = false
|
||||
binding.footer.root.isVisible = true
|
||||
binding.footer.title.setSpannableString(footer.title)
|
||||
binding.footer.btn.apply {
|
||||
text = footer.nextCta?.title
|
||||
setOnClickListener { showSipBottomSheet() }
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
binding.paymentFooter.root.isVisible = false
|
||||
binding.footer.root.isVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun navigate(action: ActionData?) {
|
||||
fragmentInterchangeListener?.navigateToNextScreen(action, arguments ?: Bundle())
|
||||
}
|
||||
|
||||
private fun showSipBottomSheet() {
|
||||
val data = viewModel.getBottomSheetData()
|
||||
val bottomSheetData = Gson().toJson(data)
|
||||
val bundle = Bundle().apply { putString(Constant.DATA, bottomSheetData) }
|
||||
getBottomSheet(
|
||||
SubPageStatusType.AMC_COMMON_BOTTOMSHEET,
|
||||
bundle = bundle,
|
||||
genericListener = ::performAction
|
||||
)
|
||||
?.let { safelyShowBottomSheet(it, SubPageStatusType.HORIZONTAL_BUTTON_BOTTOMSHEET) }
|
||||
}
|
||||
|
||||
private fun performAction(actionData: ActionData?) {
|
||||
when (actionData?.url) {
|
||||
DISMISS -> return
|
||||
CONTINUE -> {
|
||||
val action = viewModel.nextPageCta
|
||||
navigate(action)
|
||||
}
|
||||
SHOW_BOTTOMSHEET -> {
|
||||
val data = actionData.parameters?.getOrNull(0)?.value
|
||||
val key = actionData.parameters?.getOrNull(0)?.key.orEmpty()
|
||||
val bundle = Bundle().apply { putString(Constant.DATA, data) }
|
||||
getBottomSheet(key,bundle, genericListener = {
|
||||
viewModel.nextPageCta = it
|
||||
showSipBottomSheet()
|
||||
})?.let{
|
||||
safelyShowBottomSheet(it,key)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
navigate(actionData)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun newInstance(bundle: Bundle): SipTypeFragment {
|
||||
return SipTypeFragment().apply { arguments = bundle }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,10 +40,12 @@ data class FundBuyData(
|
||||
@SerializedName("footerVariations") val footerVariations: Map<String, Footer>? = null,
|
||||
@SerializedName("newFooterVariations") val newFooterVariations: Map<String, CardType>? = null,
|
||||
@SerializedName("amountPageFooter") val amountPageFooter: CardType? = null,
|
||||
@SerializedName("isManualVsAutopay") val isManualVsAutopay:Boolean? = null
|
||||
)
|
||||
|
||||
open class GenericFooter(
|
||||
@SerializedName("nextCta") val nextCta: ActionData? = null
|
||||
@SerializedName("nextCta") val nextCta: ActionData? = null,
|
||||
@SerializedName("title") val title: TextWithStyle? = null
|
||||
) : CardType()
|
||||
|
||||
data class AmountPageFooter(
|
||||
|
||||
@@ -14,7 +14,11 @@ data class SipDetailsData(
|
||||
@SerializedName("customer_id")
|
||||
val customer_id: String? = null,
|
||||
@SerializedName("autoPayChecked")
|
||||
val autoPayChecked: Boolean? = null
|
||||
val autoPayChecked: Boolean? = null,
|
||||
@SerializedName("paymentMode")
|
||||
val paymentMode: String? = null,
|
||||
@SerializedName("orderId")
|
||||
val orderId: String? = null,
|
||||
)
|
||||
|
||||
data class SipDetailsResponse(
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.navi.amc.fundbuy.models
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.navi.amc.common.model.AmcCommonBottomSheetData
|
||||
import com.navi.amc.common.model.Footer
|
||||
import com.navi.amc.kyc.model.Note
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.common.model.Header
|
||||
import com.navi.common.model.LabelData
|
||||
import com.navi.common.network.models.ErrorMessage
|
||||
import com.navi.common.network.models.GenericErrorResponse
|
||||
import com.navi.design.textview.model.TextWithStyle
|
||||
import com.navi.naviwidgets.models.response.CardType
|
||||
|
||||
sealed class SipTypeScreenState {
|
||||
object Loading : SipTypeScreenState()
|
||||
|
||||
data class Success(val data: SipTypeScreenData) : SipTypeScreenState()
|
||||
|
||||
data class Error(
|
||||
val errors: List<GenericErrorResponse>? = null,
|
||||
val error: ErrorMessage? = null,
|
||||
) : SipTypeScreenState()
|
||||
}
|
||||
|
||||
data class SipTypeScreenData(
|
||||
@SerializedName("header") val header: Header? = null,
|
||||
@SerializedName("content") val content: SipTypeScreenContent? = null,
|
||||
@SerializedName("footer") val footer: Footer? = null
|
||||
)
|
||||
|
||||
data class SipTypeScreenContent(
|
||||
@SerializedName("fundHeader") val fundHeaderData: AmcHeaderData? = null,
|
||||
@SerializedName("sipCommonBottomSheetWithOutAutoPay")
|
||||
val sipCommonBottomSheetWithOutAutoPay: AmcCommonBottomSheetData? = null,
|
||||
@SerializedName("sipCommonBottomSheetWithAutoPay")
|
||||
val sipCommonBottomSheetWithAutoPay: AmcCommonBottomSheetData? = null,
|
||||
@SerializedName("footerVariations") val footerVariations: Map<String, CardType>? = null,
|
||||
@SerializedName("title") val title: TextWithStyle? = null,
|
||||
@SerializedName("items") val items: List<SipOption>? = null
|
||||
)
|
||||
|
||||
data class SipOption(
|
||||
@SerializedName("label") val label: LabelData? = null,
|
||||
@SerializedName("title") val title: TextWithStyle? = null,
|
||||
@SerializedName("isSelected", alternate = ["selected"]) var isSelected: Boolean? = null,
|
||||
@SerializedName("id") val id: String? = null,
|
||||
@SerializedName("subTitle") val subTitle: TextWithStyle? = null,
|
||||
@SerializedName("toShowNote") val toShowNote: Boolean? = null,
|
||||
@SerializedName("note") val note: Note? = null,
|
||||
@SerializedName("actionData") val actionData: ActionData? = null,
|
||||
@SerializedName("icon") val icon: String? = null
|
||||
)
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.navi.amc.fundbuy.repository
|
||||
|
||||
import com.navi.amc.network.retrofit.RetrofitService
|
||||
import com.navi.common.network.retrofit.ResponseCallback
|
||||
import javax.inject.Inject
|
||||
|
||||
class SipTypeRepository @Inject constructor(private val retrofitService: RetrofitService) :
|
||||
ResponseCallback() {
|
||||
|
||||
suspend fun fetchSipTypeData(map: Map<String, String>) =
|
||||
apiResponseCallback(retrofitService.fetchSipTypeData(map))
|
||||
}
|
||||
@@ -305,6 +305,10 @@ class FundBuyV2ViewModel @Inject constructor(private val repository: FundBuyRepo
|
||||
return TextWithStyle(text = formattedText, style = styleList)
|
||||
}
|
||||
|
||||
fun isManualVsAutopayExperiment(): Boolean {
|
||||
return _fundBuyScreenData.value?.content?.isManualVsAutopay.orFalse()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val DAY_IN_MILLIS = 24 * 60 * 60 * 1000L
|
||||
}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
package com.navi.amc.fundbuy.viewmodel
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.navi.amc.common.model.AmcCommonBottomSheetData
|
||||
import com.navi.amc.common.viewmodel.BaseAmcVM
|
||||
import com.navi.amc.fundbuy.models.SipTypeScreenState
|
||||
import com.navi.amc.fundbuy.repository.SipTypeRepository
|
||||
import com.navi.amc.utils.Constant.AUTOPAY
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.common.utils.isValidResponse
|
||||
import com.navi.naviwidgets.models.response.CardType
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class SipTypeViewModel @Inject constructor(private val repository: SipTypeRepository) : BaseAmcVM() {
|
||||
|
||||
private val _sipTypeViewState = MutableStateFlow<SipTypeScreenState>(SipTypeScreenState.Loading)
|
||||
val sipTypeViewState = _sipTypeViewState.asStateFlow()
|
||||
|
||||
var selectedType = ""
|
||||
var nextPageCta: ActionData? = null
|
||||
|
||||
fun fetchSipTypeData(map: Map<String, String>) {
|
||||
viewModelScope.safeLaunch(Dispatchers.IO) {
|
||||
_sipTypeViewState.emit(SipTypeScreenState.Loading)
|
||||
val response = repository.fetchSipTypeData(map)
|
||||
if (response.isValidResponse()) {
|
||||
response.data?.let { _sipTypeViewState.emit(SipTypeScreenState.Success(it)) }
|
||||
} else {
|
||||
_sipTypeViewState.emit(SipTypeScreenState.Error(response.errors, response.error))
|
||||
setErrorData(response.errors, response.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getPaymentFooter(): CardType? {
|
||||
return (sipTypeViewState.value as? SipTypeScreenState.Success)
|
||||
?.data
|
||||
?.content
|
||||
?.footerVariations
|
||||
?.let { variations ->
|
||||
return variations.getOrDefault(selectedType, null)
|
||||
}
|
||||
}
|
||||
|
||||
fun getBottomSheetData(): AmcCommonBottomSheetData? {
|
||||
return if (selectedType == AUTOPAY) {
|
||||
sipCommonBottomSheetWithAutoPay()
|
||||
} else {
|
||||
sipCommonBottomSheetWithOutAutoPay()
|
||||
}
|
||||
}
|
||||
|
||||
private fun sipCommonBottomSheetWithOutAutoPay(): AmcCommonBottomSheetData? {
|
||||
return (sipTypeViewState.value as? SipTypeScreenState.Success)
|
||||
?.data
|
||||
?.content
|
||||
?.sipCommonBottomSheetWithOutAutoPay
|
||||
}
|
||||
|
||||
private fun sipCommonBottomSheetWithAutoPay(): AmcCommonBottomSheetData? {
|
||||
return (sipTypeViewState.value as? SipTypeScreenState.Success)
|
||||
?.data
|
||||
?.content
|
||||
?.sipCommonBottomSheetWithAutoPay
|
||||
}
|
||||
|
||||
}
|
||||
@@ -224,7 +224,8 @@ interface RetrofitService {
|
||||
|
||||
@GET("/autopay/{requestId}/status")
|
||||
suspend fun fetchAsyncRequestAutoPayDataPayment(
|
||||
@Path("requestId") requestId: String
|
||||
@Path("requestId") requestId: String,
|
||||
@Query("autopay_type") autopayType: String? = null
|
||||
): Response<GenericResponse<AdditionalDataAsyncResponse<NextCtaResponse>>>
|
||||
|
||||
@GET("/amc/esign/{requestId}")
|
||||
@@ -479,4 +480,14 @@ interface RetrofitService {
|
||||
suspend fun postQuestionnaireData(
|
||||
@Body response: QuestionnaireResponse?
|
||||
): Response<GenericResponse<NextCtaResponse>>
|
||||
|
||||
@GET("fund/get-sip-type-page")
|
||||
suspend fun fetchSipTypeData(@QueryMap map: Map<String, String>): Response<GenericResponse<SipTypeScreenData>>
|
||||
|
||||
@POST("orders/initiate-sip-transaction")
|
||||
suspend fun initiateSipPayment(@Body sipDetails: SipDetailsData): Response<GenericResponse<AdditionalDataAsyncResponse<NextCtaResponse>>>
|
||||
|
||||
@GET("fund/get-sip-success")
|
||||
suspend fun fetchSipSuccessPage(): Response<GenericResponse<AdditionalDataAsyncResponse<NextCtaResponse>>>
|
||||
|
||||
}
|
||||
|
||||
@@ -179,6 +179,7 @@ object AmcAnalytics {
|
||||
const val AMOUNT_REDEEMED = "amount_redeemed"
|
||||
const val UNITS_REDEEMED = "units_redeemed"
|
||||
const val PERC_TOTAL_FUND_VALUE = "perc_total_fund_value"
|
||||
const val SIP_TYPE = "sip_type"
|
||||
|
||||
const val AMC_INIT_SIMPLIFIED_LUMPSUM = "amc_init_simplified_lumpsum"
|
||||
const val AMC_INIT_SIMPLIFIED_SIP = "amc_init_simplified_sip"
|
||||
|
||||
@@ -61,6 +61,8 @@ object Constant {
|
||||
const val FREQUENCY = "frequency"
|
||||
const val AUTOPAY_CHECKED = "autopayChecked"
|
||||
const val SIP_DATE = "sipDate"
|
||||
const val SIP_START_DATE = "sipStartDate"
|
||||
const val AUTO_PAY_PRESENT = "AUTO_PAY_PRESENT"
|
||||
const val AMOUNT_SOURCE = "amount_source"
|
||||
const val CONFINED_INVESTMENT_TYPE = "CONFINED_INVESTMENT_TYPE"
|
||||
|
||||
@@ -106,6 +108,8 @@ object Constant {
|
||||
const val INIT_REFUND = "INIT_REFUND"
|
||||
const val BACK_PRESS = "backPress"
|
||||
const val DISMISS = "dismiss"
|
||||
const val CONTINUE = "continue"
|
||||
const val AUTO_PAY = "AUTO_PAY"
|
||||
const val PD = "PD"
|
||||
const val IS_INVESTMENT_ON_BOTTOM_NAV = "isInvestmentOnBottomNav"
|
||||
const val THOUSAND = 1000L
|
||||
@@ -135,7 +139,8 @@ object Constant {
|
||||
const val OTP_FLOW_TYPE_SIP_PURCHASE = "SIP_PURCHASE"
|
||||
const val OTP_FLOW_TYPE_REDEEMPTION = "REDEMPTION"
|
||||
const val OTP_FLOW_TYPE_RETRY_PAYMENT = "RETRY_PAYMENT"
|
||||
|
||||
const val OTP_FLOW_TYPE_SIP_AUTOPAY = "SIP_AUTOPAY"
|
||||
const val OTP_FLOW_TYPE_SIP_MANUAL = "SIP_MANUAL"
|
||||
const val CUSTOMER_PRIOR_INVESTMENT_QUESTIONNAIRE_REQUIRED = "customerPriorInvestmentQuestionnaireRequired"
|
||||
|
||||
const val UPI_APP_INTENT_URL = "upi://pay"
|
||||
@@ -166,4 +171,8 @@ object Constant {
|
||||
const val HPC_REDIRECTION_ENTRY_TIME = "HPC_REDIRECTION_ENTRY_TIME"
|
||||
const val BYPASS_HPC = "BYPASS_HPC"
|
||||
const val AUTO_PAY_CALLBACK_RESULT_CODE = 1002
|
||||
const val PAYMENT = "payment"
|
||||
const val CAPS_DATA ="DATA"
|
||||
const val AUTOPAY_TYPE = "autopay_type"
|
||||
const val AUTOPAY = "AUTOPAY"
|
||||
}
|
||||
|
||||
@@ -237,6 +237,7 @@ fun getFragment(screen: String, bundle: Bundle): Fragment? {
|
||||
SubPageStatusType.KYC_VERIFICATION_OPTIONS -> KycVerificationOptionsFragment.newInstance(bundle)
|
||||
SubPageStatusType.AMC_WEB_VIEW_PAGE -> AmcWebViewPageFragment.newInstance(bundle)
|
||||
SubPageStatusType.AMC_QUESTIONNAIRE_PAGE -> QuestionnaireFragment.newInstance(bundle)
|
||||
SubPageStatusType.SIP_TYPE -> SipTypeFragment.newInstance(bundle)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -66,4 +66,5 @@ object SubPageStatusType {
|
||||
const val KYC_VERIFICATION_OPTIONS = "kyc_verification_options"
|
||||
const val AMC_WEB_VIEW_PAGE = "amc_web_view_page"
|
||||
const val AMC_QUESTIONNAIRE_PAGE = "amc_questionnaire_page"
|
||||
const val SIP_TYPE ="sip_type"
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
android:id="@+id/btn"
|
||||
android:layout_width="@dimen/dp_0"
|
||||
android:layout_height="@dimen/dp_48"
|
||||
android:layout_marginTop="@dimen/dp_20"
|
||||
android:layout_marginTop="@dimen/dp_16"
|
||||
android:layout_marginBottom="@dimen/dp_32"
|
||||
android:background="@drawable/bg_cta_primary_purple_amc_rounded_4"
|
||||
android:fontFamily="@font/tt_semi_bold"
|
||||
|
||||
69
navi-amc/src/main/res/layout/sip_type_layout.xml
Normal file
69
navi-amc/src/main/res/layout/sip_type_layout.xml
Normal file
@@ -0,0 +1,69 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<data>
|
||||
|
||||
</data>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/white">
|
||||
|
||||
<com.navi.amc.fundbuy.views.AmcHeaderView
|
||||
android:id="@+id/fund_header"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.navi.design.textview.NaviTextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="@dimen/dp_0"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dp_16"
|
||||
android:layout_marginTop="@dimen/dp_32"
|
||||
android:layout_marginEnd="@dimen/dp_16"
|
||||
android:gravity="start"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/fund_header"
|
||||
tools:text="Choose SIP type" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/items"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dp_16"
|
||||
android:layout_marginTop="@dimen/dp_16"
|
||||
android:layout_marginEnd="@dimen/dp_16"
|
||||
android:overScrollMode="never"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/title" />
|
||||
|
||||
<include
|
||||
android:id="@+id/footer"
|
||||
layout="@layout/new_footer_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<include
|
||||
android:id="@+id/payment_footer"
|
||||
layout="@layout/payment_btn_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
||||
66
navi-amc/src/main/res/layout/sip_type_option_layout.xml
Normal file
66
navi-amc/src/main/res/layout/sip_type_option_layout.xml
Normal file
@@ -0,0 +1,66 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<com.navi.design.textview.NaviTextView
|
||||
android:id="@+id/label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="@dimen/dp_8"
|
||||
android:paddingTop="@dimen/dp_2"
|
||||
android:paddingEnd="@dimen/dp_8"
|
||||
android:paddingBottom="@dimen/dp_2"
|
||||
android:layout_marginStart="@dimen/dp_12"
|
||||
app:layout_constraintStart_toEndOf="@id/title"
|
||||
app:layout_constraintTop_toTopOf="@id/title"
|
||||
app:layout_constraintBottom_toBottomOf="@id/title"
|
||||
tools:text="RECOMMENDED" />
|
||||
|
||||
<com.navi.design.textview.NaviTextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dp_16"
|
||||
android:layout_marginTop="@dimen/dp_16"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toTopOf="@id/note"
|
||||
android:layout_marginBottom="@dimen/dp_16"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:text="Via UPI" />
|
||||
|
||||
|
||||
<RadioButton
|
||||
android:id="@+id/radio"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/dp_16"
|
||||
android:layout_marginEnd="@dimen/dp_16"
|
||||
android:button="@drawable/purple_radio_selector"
|
||||
app:layout_constraintBottom_toBottomOf="@id/title"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<com.navi.design.textview.NaviTextView
|
||||
android:id="@+id/note"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/dp_1"
|
||||
android:layout_marginEnd="@dimen/dp_1"
|
||||
android:layout_marginBottom="@dimen/dp_1"
|
||||
android:gravity="start"
|
||||
android:paddingStart="@dimen/dp_16"
|
||||
android:paddingTop="@dimen/dp_8"
|
||||
android:paddingEnd="@dimen/dp_16"
|
||||
android:paddingBottom="@dimen/dp_8"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@id/note"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:text="Amount will be refunded within 24-48 hours" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</layout>
|
||||
@@ -1341,6 +1341,9 @@ fun LottieAnimationView.showWhenDataIsAvailable(
|
||||
LottieEnums.YELLOW_EXCLAMATION_LOTTIE.name -> {
|
||||
setAnimation(R.raw.yellow_exclamation_lottie)
|
||||
}
|
||||
LottieEnums.AMC_SIP_SUCCESS.name -> {
|
||||
setAnimation(R.raw.amc_sip_success)
|
||||
}
|
||||
else -> {
|
||||
isInAppLottie = false
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
|
||||
@@ -115,5 +115,6 @@ enum class LottieEnums {
|
||||
GOLD_PROCESSING_TRANSACTION_LOTTIE,
|
||||
PURCHASE_SUCCESSFUL_LOTTIE,
|
||||
RED_CROSS_LOTTIE,
|
||||
YELLOW_EXCLAMATION_LOTTIE
|
||||
YELLOW_EXCLAMATION_LOTTIE,
|
||||
AMC_SIP_SUCCESS
|
||||
}
|
||||
|
||||
1
navi-widgets/src/main/res/raw/amc_sip_success.json
Normal file
1
navi-widgets/src/main/res/raw/amc_sip_success.json
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user