From bd36deeb6356e397db222f2d84ea080474167495 Mon Sep 17 00:00:00 2001 From: Divyesh Shinde Date: Fri, 11 Apr 2025 15:40:00 +0530 Subject: [PATCH] NTP-48074 | Divyesh | Bill refresh before payment (#15539) --- .../com/navi/bbps/common/NaviBbpsConstants.kt | 1 + .../model/config/NaviBbpsDefaultConfig.kt | 6 + .../common/repository/BbpsCommonRepository.kt | 6 +- .../bbps/common/usecase/FetchBillHandler.kt | 93 +++++ .../common/utils/BbpsDeeplinkDataConverter.kt | 4 +- ...MyBillEntityToBillDetailsResponseMapper.kt | 1 + .../feature/billerlist/BillerListViewModel.kt | 4 +- .../BillHistoryDetailsViewModel.kt | 4 +- .../category/BillCategoriesViewModel.kt | 30 +- .../category/BillCategoryViewModelV2.kt | 3 + .../contactlist/ContactListViewModel.kt | 6 +- .../CustomerDataInputViewModel.kt | 4 +- .../model/network/BillDetailsRequest.kt | 1 + .../model/network/BillDetailsResponse.kt | 3 + .../bbps/feature/mybills/MyBillsRepository.kt | 5 + .../mybills/MyBillsResponseToEntityMapper.kt | 2 + .../bbps/feature/mybills/MyBillsSyncJob.kt | 12 +- .../bbps/feature/mybills/MyBillsViewModel.kt | 6 +- .../bbps/feature/mybills/db/MyBillsDao.kt | 5 +- .../mybills/model/network/MyBillsResponse.kt | 2 + .../mybills/model/view/MyBillEntity.kt | 1 + .../bbps/feature/paybill/PayBillViewModel.kt | 327 +++++++++++++++-- .../feature/paybill/PayBillViewModelV2.kt | 346 ------------------ .../model/view/PayBillBottomSheetType.kt | 3 + .../feature/paybill/ui/AmountTextFieldV2.kt | 2 +- .../paybill/ui/PayBillBottomSheetContent.kt | 15 + .../{PayBillScreenV2.kt => PayBillScreen.kt} | 169 +++++---- ...V2.kt => RenderCreditCardPayBillScreen.kt} | 2 +- ...kt => RenderNonCreditCardPayBillScreen.kt} | 33 +- .../PrepaidRechargeViewModel.kt | 4 +- .../FirebaseRemoteConfigHelper.kt | 2 +- 31 files changed, 608 insertions(+), 494 deletions(-) delete mode 100644 android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/PayBillViewModelV2.kt rename android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/{PayBillScreenV2.kt => PayBillScreen.kt} (90%) rename android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/{RenderCreditCardPayBillScreenV2.kt => RenderCreditCardPayBillScreen.kt} (99%) rename android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/{RenderNonCreditCardPayBillScreenV2.kt => RenderNonCreditCardPayBillScreen.kt} (96%) diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/NaviBbpsConstants.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/NaviBbpsConstants.kt index 878b3b04f2..44ca8ec225 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/NaviBbpsConstants.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/NaviBbpsConstants.kt @@ -180,6 +180,7 @@ const val BILL_FETCH_CONSENT_DEFAULT_MESSAGE = "By continuing, you allow Navi to fetch current bill & send reminders for future bills." const val TAG_BILL_FETCH_ERROR = "TAG_BILL_FETCH_ERROR" const val ACTION_CIRCLE_OPERATOR_SELECTION = "ACTION_CIRCLE_OPERATOR_SELECTION" +const val TAG_BILL_ALREADY_PAID_ON_REFETCH = "TAG_BILL_ALREADY_PAID" // offer experience event attributes and tags const val RCBP_CATEGORY = "RCBP_CATEGORY" diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/model/config/NaviBbpsDefaultConfig.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/model/config/NaviBbpsDefaultConfig.kt index 7b79799b70..0615f32792 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/model/config/NaviBbpsDefaultConfig.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/model/config/NaviBbpsDefaultConfig.kt @@ -49,6 +49,7 @@ data class NaviBbpsDefaultConfig( val detectedBillsPollingTimeoutMillis: Long = 30000, @SerializedName("detectedBillsPollingWaitTimeMillis") val detectedBillsPollingWaitTimeMillis: Long = 2000, + val billAlreadyPaidErrorCodes: List = listOf("INVAL003"), ) { data class PaymentCheckoutConfigItem( @SerializedName("categoryId") val categoryId: String = EMPTY, @@ -488,6 +489,11 @@ data class ConfigMessage( @SerializedName("invalidContactSelectedMessage") val invalidContactSelectedDescription: String = "It looks like you've selected an invalid phone number. Please choose another contact or enter a new phone number", + @SerializedName("billDetailsRefreshedTitle") + val billDetailsUpdatedTitle: String = "Bill details have been updated", + @SerializedName("billDetailsRefreshedDescription") + val billDetailsUpdatedDescription: String = + "We have fetched the latest bill details from your biller. The amount or due date may have changed. Please review before proceeding.", ) data class CoinUtilisationConfig( diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/repository/BbpsCommonRepository.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/repository/BbpsCommonRepository.kt index fed7183723..2bdf61b913 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/repository/BbpsCommonRepository.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/repository/BbpsCommonRepository.kt @@ -49,6 +49,7 @@ constructor( suspend fun fetchBillDetails( billDetailsRequest: BillDetailsRequest, metricInfo: MetricInfo>, + onSavedBillRefresh: (suspend (BillDetailsResponse) -> Unit)? = null, ): RepoResult { val response = apiResponseCallback( @@ -57,7 +58,10 @@ constructor( metricInfo = metricInfo, ) if (response.isSuccessWithData()) { - myBillsSyncJob.refreshBillsAsync(screenName = metricInfo.screen) + myBillsSyncJob.refreshBillsAsync( + screenName = metricInfo.screen, + onSavedBillRefresh = { onSavedBillRefresh?.invoke(response.data!!) }, + ) } return response } diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/usecase/FetchBillHandler.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/usecase/FetchBillHandler.kt index 43a5c2e10d..86e492d10f 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/usecase/FetchBillHandler.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/usecase/FetchBillHandler.kt @@ -9,6 +9,8 @@ package com.navi.bbps.common.usecase import com.navi.base.utils.NaviNetworkConnectivity import com.navi.base.utils.orFalse +import com.navi.base.utils.orZero +import com.navi.bbps.common.TAG_BILL_ALREADY_PAID_ON_REFETCH import com.navi.bbps.common.TAG_BILL_FETCH_ERROR import com.navi.bbps.common.mapper.BillResponseToBillDetailsEntityMapper import com.navi.bbps.common.repository.BbpsCommonRepository @@ -18,6 +20,9 @@ import com.navi.bbps.feature.customerinput.model.network.BillDetailsRequest import com.navi.bbps.feature.customerinput.model.network.BillDetailsResponse import com.navi.bbps.feature.customerinput.model.network.DeviceDetails import com.navi.bbps.feature.customerinput.model.view.BillDetailsEntity +import com.navi.bbps.feature.mybills.MyBillsRepository +import com.navi.bbps.feature.mybills.MyBillsSyncJob +import com.navi.bbps.feature.mybills.model.view.UnpaidBillDetails import com.navi.bbps.feature.paybill.PayBillViewModel import com.navi.common.network.models.RepoResult import com.navi.common.network.models.isSuccessWithData @@ -25,12 +30,16 @@ import javax.inject.Inject typealias IsConsentRequired = Boolean +typealias IsBillDetailsChanged = Boolean + class FetchBillHandler @Inject constructor( private val bbpsCommonRepository: BbpsCommonRepository, private val naviNetworkConnectivity: NaviNetworkConnectivity, private val naviBbpsDateUtils: NaviBbpsDateUtils, + private val myBillsRepository: MyBillsRepository, + private val myBillsSyncJob: MyBillsSyncJob, ) { suspend fun fetchBill( payBillViewModel: PayBillViewModel, @@ -69,6 +78,90 @@ constructor( } } + suspend fun refetchBill( + payBillViewModel: PayBillViewModel, + screenName: String, + onReFetchSuccess: suspend (IsBillDetailsChanged, BillDetailsEntity) -> Unit, + billAlreadyPaidErrorCodes: List, + ) { + + val billDetailsRequest = + BillDetailsRequest( + billerId = payBillViewModel.payBillScreenSource.billerId, + referenceId = payBillViewModel.payBillScreenSource.billDetailsEntity!!.referenceId, + customerParams = payBillViewModel.payBillScreenSource.customerParams, + isConsentProvided = + payBillViewModel.payBillScreenSource.isConsentRequired.orFalse(), + deviceDetails = DeviceDetails(ip = naviNetworkConnectivity.getIpAddress()), + ) + + val billDetailsResponse = + bbpsCommonRepository.fetchBillDetails( + billDetailsRequest = billDetailsRequest, + metricInfo = getBbpsMetricInfo(screenName = screenName, isNae = { false }), + onSavedBillRefresh = { billDetailsResponse -> + val billDetailsEntity = + BillResponseToBillDetailsEntityMapper(naviBbpsDateUtils) + .map(billDetailsResponse) + val billFetchTimestamp = billDetailsResponse.apiCallTimestamp?.millis.orZero() + + if ( + billFetchTimestamp > + payBillViewModel.myBillEntity.value + ?.unpaidBillDetails + ?.lastGeneratedTimestamp + ?.toLongOrNull() + .orZero() && billDetailsResponse.isBillDetailsChanged + ) { + + payBillViewModel.myBillEntity.value?.let { + val updatedMyBillEntity = + payBillViewModel.myBillEntity.value!!.copy( + unpaidBillDetails = + UnpaidBillDetails( + referenceId = billDetailsEntity.referenceId, + amount = billDetailsEntity.amount, + billDate = billDetailsEntity.billDate, + dueDate = billDetailsEntity.dueDate, + billNumber = billDetailsEntity.billNumber, + accountHolderName = billDetailsEntity.accountHolderName, + billerAdditionalParams = + billDetailsEntity.billerAdditionalParams, + planItemDetails = + payBillViewModel.myBillEntity.value + ?.unpaidBillDetails + ?.planItemDetails ?: emptyMap(), + lastGeneratedTimestamp = + billDetailsResponse.apiCallTimestamp + ?.millis + .toString(), + ) + ) + + myBillsRepository.updateSavedBill(myBillEntity = updatedMyBillEntity) + } + } + onReFetchSuccess( + billDetailsResponse.isBillDetailsChanged.orFalse(), + billDetailsEntity, + ) + }, + ) + + if (!billDetailsResponse.isSuccessWithData() && !payBillViewModel.isPayButtonClicked) { + val error = payBillViewModel.getError(billDetailsResponse) + + if (error.code in billAlreadyPaidErrorCodes) { + payBillViewModel.notifyError( + response = billDetailsResponse, + cancelable = false, + tag = TAG_BILL_ALREADY_PAID_ON_REFETCH, + ) + myBillsSyncJob.refreshBillsAsync(screenName = screenName) + } + } + } + private suspend fun showBillError( payBillViewModel: PayBillViewModel, billResponse: RepoResult, diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/utils/BbpsDeeplinkDataConverter.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/utils/BbpsDeeplinkDataConverter.kt index 90af49d3ce..8ce44afa89 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/utils/BbpsDeeplinkDataConverter.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/utils/BbpsDeeplinkDataConverter.kt @@ -24,7 +24,7 @@ import com.navi.bbps.feature.customerinput.model.view.BillerAdditionalParamsEnti import com.navi.bbps.feature.customerinput.model.view.BillerDetailsEntity import com.navi.bbps.feature.customerinput.model.view.CustomerParamsExtraData import com.navi.bbps.feature.destinations.CustomerDataInputScreenDestination -import com.navi.bbps.feature.destinations.PayBillScreenV2Destination +import com.navi.bbps.feature.destinations.PayBillScreenDestination import com.navi.bbps.feature.destinations.PrepaidRechargeScreenDestination import com.navi.bbps.feature.paybill.model.network.PaymentAmountExactness import com.navi.bbps.feature.paybill.model.view.PayBillSource @@ -112,7 +112,7 @@ constructor( getOthersPayBillSource(bundle) } - return PayBillScreenV2Destination( + return PayBillScreenDestination( billCategoryEntity = billCategoryEntity, phoneNumberDetail = phoneContactEntity, payBillScreenSource = payBillSource, diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/utils/MyBillEntityToBillDetailsResponseMapper.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/utils/MyBillEntityToBillDetailsResponseMapper.kt index 584cf4dac9..9954f92438 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/utils/MyBillEntityToBillDetailsResponseMapper.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/common/utils/MyBillEntityToBillDetailsResponseMapper.kt @@ -36,6 +36,7 @@ constructor( myBillEntity.unpaidBillDetails?.billerAdditionalParams?.map { it.toBillerAdditionalParams() }, + isBillDetailsChanged = false, ) } } diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/billerlist/BillerListViewModel.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/billerlist/BillerListViewModel.kt index 460d949ebc..cd23c09ffc 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/billerlist/BillerListViewModel.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/billerlist/BillerListViewModel.kt @@ -48,7 +48,7 @@ import com.navi.bbps.feature.contactlist.model.view.PhoneContactEntity import com.navi.bbps.feature.customerinput.model.view.BillerDetailsEntity import com.navi.bbps.feature.customerinput.model.view.CustomerParamsExtraData import com.navi.bbps.feature.destinations.CustomerDataInputScreenDestination -import com.navi.bbps.feature.destinations.PayBillScreenV2Destination +import com.navi.bbps.feature.destinations.PayBillScreenDestination import com.navi.bbps.feature.mybills.MyBillsSyncJob import com.navi.bbps.feature.mybills.model.view.MyBillEntity import com.navi.bbps.feature.paybill.model.view.PayBillSource @@ -419,7 +419,7 @@ constructor( ) val destination = - PayBillScreenV2Destination( + PayBillScreenDestination( billCategoryEntity = billCategoryEntity, payBillScreenSource = payBillSource, phoneNumberDetail = phoneNumberDetailEntity, diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/billhistorydetail/BillHistoryDetailsViewModel.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/billhistorydetail/BillHistoryDetailsViewModel.kt index a80fbad409..9dcb3b18bc 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/billhistorydetail/BillHistoryDetailsViewModel.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/billhistorydetail/BillHistoryDetailsViewModel.kt @@ -52,7 +52,7 @@ import com.navi.bbps.feature.customerinput.model.view.BillerDetailsEntity import com.navi.bbps.feature.customerinput.model.view.CustomerParamsExtraData import com.navi.bbps.feature.destinations.BbpsTransactionDetailsScreenDestination import com.navi.bbps.feature.destinations.CustomerDataInputScreenDestination -import com.navi.bbps.feature.destinations.PayBillScreenV2Destination +import com.navi.bbps.feature.destinations.PayBillScreenDestination import com.navi.bbps.feature.destinations.PrepaidRechargeScreenDestination import com.navi.bbps.feature.mybills.model.view.MyBillEntity import com.navi.bbps.feature.paybill.model.view.PayBillSource @@ -381,7 +381,7 @@ constructor( val source = NaviBbpsScreen.NAVI_BBPS_MY_BILL_HISTORY_DETAILS.name val destination = - PayBillScreenV2Destination( + PayBillScreenDestination( billCategoryEntity = billCategoryEntity, payBillScreenSource = payBillSource, phoneNumberDetail = phoneNumberDetail, diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/category/BillCategoriesViewModel.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/category/BillCategoriesViewModel.kt index 3a40cb7ec4..d1d098437f 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/category/BillCategoriesViewModel.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/category/BillCategoriesViewModel.kt @@ -29,6 +29,8 @@ import com.navi.bbps.common.BILLER_UNIQUE_ID import com.navi.bbps.common.BbpsSharedPreferences import com.navi.bbps.common.CATEGORY_ID_MOBILE_PREPAID import com.navi.bbps.common.CoinsSyncManager +import com.navi.bbps.common.DATE_TIME_FORMAT_DATE_MONTH_NAME_YEAR +import com.navi.bbps.common.DATE_TIME_FORMAT_DATE_MONTH_NAME_YEAR_INPUT import com.navi.bbps.common.DEFAULT_RETRY_COUNT import com.navi.bbps.common.LocalJsonDataSource import com.navi.bbps.common.NaviBbpsAnalytics @@ -48,6 +50,7 @@ import com.navi.bbps.common.utils.MyBillEntityToBillCategoryEntityMapper import com.navi.bbps.common.utils.MyBillEntityToBillerDetailsEntityMapper import com.navi.bbps.common.utils.NaviBbpsCommonUtils import com.navi.bbps.common.utils.NaviBbpsCommonUtils.getBbpsMetricInfo +import com.navi.bbps.common.utils.NaviBbpsDateUtils import com.navi.bbps.common.utils.getDefaultConfig import com.navi.bbps.common.utils.getDisplayableAmount import com.navi.bbps.common.utils.refresh @@ -72,7 +75,7 @@ import com.navi.bbps.feature.customerinput.model.view.BillerDetailsEntity import com.navi.bbps.feature.customerinput.model.view.CustomerParamsExtraData import com.navi.bbps.feature.destinations.CustomerDataInputScreenDestination import com.navi.bbps.feature.destinations.MyBillHistoryDetailsScreenDestination -import com.navi.bbps.feature.destinations.PayBillScreenV2Destination +import com.navi.bbps.feature.destinations.PayBillScreenDestination import com.navi.bbps.feature.destinations.PrepaidRechargeScreenDestination import com.navi.bbps.feature.mybills.MyBillsRepository import com.navi.bbps.feature.mybills.MyBillsSyncJob @@ -137,6 +140,7 @@ constructor( val naviNetworkConnectivity: NaviNetworkConnectivity, private val bbpsCommonRepository: BbpsCommonRepository, private val resourceProvider: ResourceProvider, + private val naviBbpsDateUtils: NaviBbpsDateUtils, val uploadUserDataUseCase: UploadUserDataUseCase, @NaviBbpsGsonBuilder private val naviBbpsGson: Gson, open val bbpsSharedPreferences: BbpsSharedPreferences, @@ -308,14 +312,29 @@ constructor( billerId = billerDetails.billerId, referenceId = myBillEntity.unpaidBillDetails?.referenceId.orEmpty(), amount = myBillEntity.unpaidBillDetails?.amount, - billDate = myBillEntity.unpaidBillDetails?.billDate, - dueDate = myBillEntity.unpaidBillDetails?.dueDate, + billDate = + naviBbpsDateUtils + .getFormattedDate( + dateTime = myBillEntity.unpaidBillDetails?.billDate, + inputDateFormat = DATE_TIME_FORMAT_DATE_MONTH_NAME_YEAR, + outputDateFormat = DATE_TIME_FORMAT_DATE_MONTH_NAME_YEAR_INPUT, + ) + .orEmpty(), + dueDate = + naviBbpsDateUtils + .getFormattedDate( + dateTime = myBillEntity.unpaidBillDetails?.dueDate, + inputDateFormat = DATE_TIME_FORMAT_DATE_MONTH_NAME_YEAR, + outputDateFormat = DATE_TIME_FORMAT_DATE_MONTH_NAME_YEAR_INPUT, + ) + .orEmpty(), billNumber = myBillEntity.unpaidBillDetails?.billNumber, accountHolderName = myBillEntity.unpaidBillDetails?.accountHolderName, billerAdditionalParams = myBillEntity.unpaidBillDetails?.billerAdditionalParams?.map { it.toBillerAdditionalParams() }, + isBillDetailsChanged = false, ) } @@ -427,7 +446,7 @@ constructor( val source = NaviBbpsScreen.NAVI_BBPS_BILL_CATEGORIES.name _navigateToNextScreen.emit( - PayBillScreenV2Destination( + PayBillScreenDestination( billCategoryEntity = billCategoryEntity, payBillScreenSource = payBillSource, phoneNumberDetail = phoneNumberDetail, @@ -437,6 +456,7 @@ constructor( ) ) } else { + val billCategoryEntity = myBillEntityToBillCategoryEntityMapper.mapMyBillEntityToBillCategoryEntity( myBillEntity @@ -470,7 +490,7 @@ constructor( val source = NaviBbpsScreen.NAVI_BBPS_BILL_CATEGORIES.name _navigateToNextScreen.emit( - PayBillScreenV2Destination( + PayBillScreenDestination( billCategoryEntity = billCategoryEntity, payBillScreenSource = payBillSource, phoneNumberDetail = phoneNumberDetail, diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/category/BillCategoryViewModelV2.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/category/BillCategoryViewModelV2.kt index b33cc62483..61316b783e 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/category/BillCategoryViewModelV2.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/category/BillCategoryViewModelV2.kt @@ -38,6 +38,7 @@ import com.navi.bbps.common.utils.BillDetailsResponseToEntityMapper import com.navi.bbps.common.utils.MyBillEntityToBillCategoryEntityMapper import com.navi.bbps.common.utils.MyBillEntityToBillerDetailsEntityMapper import com.navi.bbps.common.utils.NaviBbpsCommonUtils.getBbpsMetricInfo +import com.navi.bbps.common.utils.NaviBbpsDateUtils import com.navi.bbps.common.utils.OriginSessionAttributes import com.navi.bbps.common.utils.OriginWidgetStatus import com.navi.bbps.common.viewmodel.OriginBillDetectionHandler @@ -86,6 +87,7 @@ constructor( naviBbpsSessionHelper: NaviBbpsSessionHelper, coinsSyncManager: CoinsSyncManager, naviBbpsConfigUseCase: NaviBbpsConfigUseCase, + naviBbpsDateUtils: NaviBbpsDateUtils, resProvider: ResourceProvider, bbpsCommonRepository: BbpsCommonRepository, naviNetworkConnectivity: NaviNetworkConnectivity, @@ -118,6 +120,7 @@ constructor( resourceProvider = resourceProvider, uploadUserDataUseCase = uploadUserDataUseCase, bbpsSharedPreferences = bbpsSharedPreferences, + naviBbpsDateUtils = naviBbpsDateUtils, ), BottomSheetController by BottomSheetControllerImpl() { diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/contactlist/ContactListViewModel.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/contactlist/ContactListViewModel.kt index c93361621e..b003fe1a45 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/contactlist/ContactListViewModel.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/contactlist/ContactListViewModel.kt @@ -49,7 +49,7 @@ import com.navi.bbps.feature.contactlist.model.view.PhoneContactEntity import com.navi.bbps.feature.customerinput.model.network.BillDetailsRequest import com.navi.bbps.feature.customerinput.model.network.DeviceDetails import com.navi.bbps.feature.customerinput.model.view.BillerDetailsEntity -import com.navi.bbps.feature.destinations.PayBillScreenV2Destination +import com.navi.bbps.feature.destinations.PayBillScreenDestination import com.navi.bbps.feature.destinations.PrepaidRechargeScreenDestination import com.navi.bbps.feature.mybills.MyBillsSyncJob import com.navi.bbps.feature.mybills.model.view.MyBillEntity @@ -423,7 +423,7 @@ constructor( _showPostpaidBottomSheet.emit(false) _navigateToNextScreen.emit( - PayBillScreenV2Destination( + PayBillScreenDestination( billCategoryEntity = billCategory, payBillScreenSource = payBillScreenSource, phoneNumberDetail = phoneNumberDetailEntity, @@ -596,7 +596,7 @@ constructor( val screenSource = NaviBbpsScreen.NAVI_BBPS_CONTACT_LIST_SCREEN.name _navigateToNextScreen.emit( - PayBillScreenV2Destination( + PayBillScreenDestination( billCategoryEntity = billCategory, payBillScreenSource = payBillSource, phoneNumberDetail = phoneNumber, diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/customerinput/CustomerDataInputViewModel.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/customerinput/CustomerDataInputViewModel.kt index e31464fef8..ab2f57002d 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/customerinput/CustomerDataInputViewModel.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/customerinput/CustomerDataInputViewModel.kt @@ -52,7 +52,7 @@ import com.navi.bbps.feature.customerinput.model.view.CustomerInputScreenState import com.navi.bbps.feature.customerinput.model.view.CustomerParam import com.navi.bbps.feature.customerinput.model.view.CustomerParamValueState import com.navi.bbps.feature.customerinput.model.view.CustomerParamsExtraData -import com.navi.bbps.feature.destinations.PayBillScreenV2Destination +import com.navi.bbps.feature.destinations.PayBillScreenDestination import com.navi.bbps.feature.detectedbills.DetectedBillsRepository import com.navi.bbps.feature.detectedbills.model.view.DetectedBillCustomerInputResult import com.navi.bbps.feature.mybills.MyBillsRepository @@ -572,7 +572,7 @@ constructor( PhoneContactEntity(name = "", phoneNumber = "", normalisedPhoneNumber = "") _navigateToNextScreen.emit( - PayBillScreenV2Destination( + PayBillScreenDestination( billCategoryEntity = billCategoryEntity, payBillScreenSource = payBillScreenSource, phoneNumberDetail = phoneNumberDetail, diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/customerinput/model/network/BillDetailsRequest.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/customerinput/model/network/BillDetailsRequest.kt index 1ddca4c2c7..61b97f8d4c 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/customerinput/model/network/BillDetailsRequest.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/customerinput/model/network/BillDetailsRequest.kt @@ -12,6 +12,7 @@ import com.google.gson.annotations.SerializedName data class BillDetailsRequest( @SerializedName("billerId") val billerId: String, @SerializedName("customerParams") val customerParams: Map, + @SerializedName("referenceId") val referenceId: String? = null, @SerializedName("deviceDetails") val deviceDetails: DeviceDetails, @SerializedName("amount") val amount: String? = null, @SerializedName("isConsentProvided") val isConsentProvided: Boolean, diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/customerinput/model/network/BillDetailsResponse.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/customerinput/model/network/BillDetailsResponse.kt index 5502a66115..d905f1047f 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/customerinput/model/network/BillDetailsResponse.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/customerinput/model/network/BillDetailsResponse.kt @@ -8,16 +8,19 @@ package com.navi.bbps.feature.customerinput.model.network import com.google.gson.annotations.SerializedName +import org.joda.time.DateTime data class BillDetailsResponse( @SerializedName("billId") val billId: String?, @SerializedName("billerId") val billerId: String, + @SerializedName("isBillDetailsChanged") val isBillDetailsChanged: Boolean, @SerializedName("referenceId") val referenceId: String, @SerializedName("amount") val amount: String?, @SerializedName("billDate") val billDate: String?, @SerializedName("dueDate") val dueDate: String?, @SerializedName("billNumber") val billNumber: String?, @SerializedName("accountHolderName") val accountHolderName: String?, + @SerializedName("apiCallTimestamp") val apiCallTimestamp: DateTime? = null, @SerializedName("billerAdditionalParams") val billerAdditionalParams: List?, ) diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/MyBillsRepository.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/MyBillsRepository.kt index f347a107b5..36d4f3b69c 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/MyBillsRepository.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/MyBillsRepository.kt @@ -10,6 +10,7 @@ package com.navi.bbps.feature.mybills import com.navi.bbps.feature.mybills.db.MyBillsDao import com.navi.bbps.feature.mybills.model.network.BillMarkAsPaidResponse import com.navi.bbps.feature.mybills.model.network.MyBillsResponse +import com.navi.bbps.feature.mybills.model.view.MyBillEntity import com.navi.bbps.network.service.NaviBbpsRetrofitService import com.navi.common.checkmate.model.MetricInfo import com.navi.common.network.models.RepoResult @@ -33,6 +34,10 @@ constructor( fun fetchMySavedBills() = myBillsDao.getAllBills() + suspend fun updateSavedBill(myBillEntity: MyBillEntity) { + myBillsDao.updateSavedBill(myBillEntity = myBillEntity) + } + suspend fun fetchSavedBillsByCategory(category: String) = myBillsDao.getBillsByCategory(categoryId = category) diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/MyBillsResponseToEntityMapper.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/MyBillsResponseToEntityMapper.kt index 8e19861b65..73efcb158e 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/MyBillsResponseToEntityMapper.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/MyBillsResponseToEntityMapper.kt @@ -81,6 +81,8 @@ constructor( it.unpaidBillDetails?.billerAdditionalParams?.map { it.toBillerAdditionalParamsEntity() } ?: emptyList(), + lastGeneratedTimestamp = + it.unpaidBillDetails?.lastGeneratedTimestamp?.millis.toString(), ), ) } diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/MyBillsSyncJob.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/MyBillsSyncJob.kt index 11c59ecfef..2fa01d2a1f 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/MyBillsSyncJob.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/MyBillsSyncJob.kt @@ -27,7 +27,7 @@ constructor( ) { private var syncJob: Job? = null - suspend fun refreshBills(screenName: String) { + suspend fun refreshBills(screenName: String, onSavedBillRefresh: (suspend () -> Unit)? = null) { val response = myBillsRepository.fetchBillsFromNetwork( metricInfo = getBbpsMetricInfo(screenName = screenName, isNae = { false }) @@ -41,9 +41,13 @@ constructor( myBillsDao.refresh(myBills) } } + onSavedBillRefresh?.invoke() } - fun refreshBillsAsync(screenName: String): Job? { + fun refreshBillsAsync( + screenName: String, + onSavedBillRefresh: (suspend () -> Unit)? = null, + ): Job? { if (syncJob?.isActive == true) { try { syncJob?.cancel() @@ -53,7 +57,9 @@ constructor( } } syncJob = - CoroutineScope(dispatcherProvider.io).launch { refreshBills(screenName = screenName) } + CoroutineScope(dispatcherProvider.io).launch { + refreshBills(screenName = screenName, onSavedBillRefresh = onSavedBillRefresh) + } return syncJob } } diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/MyBillsViewModel.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/MyBillsViewModel.kt index 03c31811de..cad40beee3 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/MyBillsViewModel.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/MyBillsViewModel.kt @@ -61,7 +61,7 @@ import com.navi.bbps.feature.customerinput.model.view.BillerDetailsEntity import com.navi.bbps.feature.customerinput.model.view.CustomerParamsExtraData import com.navi.bbps.feature.destinations.CustomerDataInputScreenDestination import com.navi.bbps.feature.destinations.MyBillHistoryDetailsScreenDestination -import com.navi.bbps.feature.destinations.PayBillScreenV2Destination +import com.navi.bbps.feature.destinations.PayBillScreenDestination import com.navi.bbps.feature.destinations.PrepaidRechargeScreenDestination import com.navi.bbps.feature.detectedbills.DetectedBillsRepository import com.navi.bbps.feature.detectedbills.model.view.NewAddedBills @@ -547,7 +547,7 @@ constructor( val source = NaviBbpsScreen.NAVI_BBPS_MY_SAVED_BILLS.name _navigateToNextScreen.emit( - PayBillScreenV2Destination( + PayBillScreenDestination( billCategoryEntity = billCategoryEntity, payBillScreenSource = payBillScreenSource, phoneNumberDetail = phoneNumberDetail, @@ -625,7 +625,7 @@ constructor( val source = NaviBbpsScreen.NAVI_BBPS_MY_SAVED_BILLS.name _navigateToNextScreen.emit( - PayBillScreenV2Destination( + PayBillScreenDestination( billCategoryEntity = billCategoryEntity, payBillScreenSource = payBillScreenSource, phoneNumberDetail = phoneNumberDetail, diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/db/MyBillsDao.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/db/MyBillsDao.kt index cae8be334e..b6e1a05c9b 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/db/MyBillsDao.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/db/MyBillsDao.kt @@ -1,6 +1,6 @@ /* * - * * Copyright © 2024 by Navi Technologies Limited + * * Copyright © 2024-2025 by Navi Technologies Limited * * All rights reserved. Strictly confidential * */ @@ -13,6 +13,7 @@ import androidx.room.OnConflictStrategy import androidx.room.Query import androidx.room.RawQuery import androidx.room.Transaction +import androidx.room.Update import androidx.sqlite.db.SupportSQLiteQuery import com.navi.bbps.db.NaviBbpsAppDatabase.Companion.NAVI_BBPS_TABLE_MY_SAVED_BILLS import com.navi.bbps.feature.mybills.model.view.MyBillEntity @@ -26,6 +27,8 @@ interface MyBillsDao { @Query("SELECT * FROM $NAVI_BBPS_TABLE_MY_SAVED_BILLS WHERE categoryId == :categoryId") suspend fun getBillsByCategory(categoryId: String): List + @Update suspend fun updateSavedBill(myBillEntity: MyBillEntity) + @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertAll(myBills: List) diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/model/network/MyBillsResponse.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/model/network/MyBillsResponse.kt index c87949ae49..4a8c035535 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/model/network/MyBillsResponse.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/model/network/MyBillsResponse.kt @@ -9,6 +9,7 @@ package com.navi.bbps.feature.mybills.model.network import com.google.gson.annotations.SerializedName import com.navi.bbps.feature.customerinput.model.network.BillerAdditionalParams +import org.joda.time.DateTime data class MyBillsResponse(@SerializedName("bills") val bills: List?) @@ -45,6 +46,7 @@ data class UnpaidBillDetailsResponse( @SerializedName("billNumber") val billNumber: String?, @SerializedName("accountHolderName") val accountHolderName: String?, @SerializedName("planItemDetails") val planItemDetails: Map?, + @SerializedName("lastGeneratedTimestamp") val lastGeneratedTimestamp: DateTime?, @SerializedName("billerAdditionalParams") val billerAdditionalParams: List?, ) diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/model/view/MyBillEntity.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/model/view/MyBillEntity.kt index 9478dd3668..90a2b223dd 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/model/view/MyBillEntity.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/mybills/model/view/MyBillEntity.kt @@ -63,4 +63,5 @@ data class UnpaidBillDetails( val accountHolderName: String, val billerAdditionalParams: List, val planItemDetails: Map, + val lastGeneratedTimestamp: String, ) : Parcelable diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/PayBillViewModel.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/PayBillViewModel.kt index a3a584b9bc..3d21e24e03 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/PayBillViewModel.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/PayBillViewModel.kt @@ -12,9 +12,11 @@ import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.viewModelScope import com.navi.adverse.flux.provider.GsonProvider.gson import com.navi.base.cache.repository.NaviCacheRepository +import com.navi.base.model.CtaData import com.navi.base.utils.EMPTY import com.navi.base.utils.NaviNetworkConnectivity import com.navi.base.utils.ResourceProvider +import com.navi.base.utils.TrustedTimeAccessor import com.navi.base.utils.ZERO_STRING import com.navi.base.utils.isNotNull import com.navi.base.utils.orFalse @@ -40,8 +42,10 @@ import com.navi.bbps.common.model.config.NaviBbpsDefaultConfig import com.navi.bbps.common.model.view.NaviBbpsSessionHelper import com.navi.bbps.common.repository.BbpsCommonRepository import com.navi.bbps.common.usecase.FetchBillHandler +import com.navi.bbps.common.usecase.GetABTestingExperimentUseCase import com.navi.bbps.common.usecase.NaviBbpsConfigUseCase import com.navi.bbps.common.usecase.RewardNudgeUseCase +import com.navi.bbps.common.utils.NaviBbpsCommonUtils import com.navi.bbps.common.utils.NaviBbpsCommonUtils.evaluateMvelExpression import com.navi.bbps.common.utils.NaviBbpsCommonUtils.getBbpsMetricInfo import com.navi.bbps.common.utils.NaviBbpsCommonUtils.getValidatedAmountNumber @@ -74,16 +78,21 @@ import com.navi.bbps.feature.paybill.model.network.PayBillResponse import com.navi.bbps.feature.paybill.model.network.PaymentAmountExactness import com.navi.bbps.feature.paybill.model.view.AmountChipEntity import com.navi.bbps.feature.paybill.model.view.CoinUtilisationProperties +import com.navi.bbps.feature.paybill.model.view.CoinUtilisationPropertiesV2 import com.navi.bbps.feature.paybill.model.view.CreditCardAmountType import com.navi.bbps.feature.paybill.model.view.CreditCardPaymentOption +import com.navi.bbps.feature.paybill.model.view.PayBillBottomSheetType import com.navi.bbps.feature.paybill.model.view.PayBillHeaderState import com.navi.bbps.feature.paybill.model.view.PayBillScreenState import com.navi.bbps.feature.paybill.model.view.PayBillSource import com.navi.bbps.feature.paybill.util.getBillOrderModel import com.navi.bbps.feature.prepaidrecharge.model.view.OperatorItemEntity import com.navi.bbps.feature.prepaidrecharge.model.view.PlanItemEntity +import com.navi.common.R as CommonR import com.navi.common.constants.DBCacheConstants.ARC_NUDGE_RESPONSE_CACHE_KEY import com.navi.common.di.CoroutineDispatcherProvider +import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper +import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper.NAVI_BBPS_BILL_REFRESH_DURATION import com.navi.common.model.common.NudgeDetailEntity import com.navi.common.network.models.RepoResult import com.navi.common.network.models.isSuccessWithData @@ -95,6 +104,7 @@ import com.navi.common.utils.Constants import com.navi.common.utils.Constants.LITMUS_EXPERIMENT_NAVIPAY_NAVI_POWER_PLAY import com.navi.common.utils.TemporaryStorageHelper import com.navi.common.utils.toJsonObject +import com.navi.payment.nativepayment.utils.NaviPaymentRewardsEventBus import com.navi.payment.nativepayment.utils.getDiscountAdjustedAmount import com.navi.payment.paymentscreen.utils.PaymentNavigator import com.navi.payment.tstore.repository.TStoreOrderHandler @@ -104,17 +114,20 @@ import com.ramcosta.composedestinations.spec.Direction import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import org.json.JSONObject @HiltViewModel @@ -126,6 +139,7 @@ constructor( private val payBillRepository: PayBillRepository, private val naviBbpsDateUtils: NaviBbpsDateUtils, private val naviBbpsConfigUseCase: NaviBbpsConfigUseCase, + private val getABTestingExperimentUseCase: GetABTestingExperimentUseCase, private val myBillsRepository: MyBillsRepository, private val tStoreOrderHandler: TStoreOrderHandler, private val naviNetworkConnectivity: NaviNetworkConnectivity, @@ -135,6 +149,7 @@ constructor( private val billCategoriesRepository: BillCategoriesRepository, private val resProvider: ResourceProvider, private val rewardsNudgeEntityFetchUseCase: RewardNudgeUseCase, + private val naviPaymentRewardEventBus: NaviPaymentRewardsEventBus, private val naviCacheRepository: NaviCacheRepository, private val naviBbpsCommonRepository: BbpsCommonRepository, private val resourceProvider: ResourceProvider, @@ -166,7 +181,7 @@ constructor( private val _initialPaymentAmount = MutableStateFlow("") val initialPaymentAmount = _initialPaymentAmount.asStateFlow() - internal val _paymentAmount = MutableStateFlow("") + private val _paymentAmount = MutableStateFlow("") val paymentAmount = _paymentAmount.asStateFlow() private val _showErrorText = MutableStateFlow(false) @@ -191,6 +206,10 @@ constructor( MutableStateFlow(PayBillScreenState.BillDetailsUnavailable) val payBillScreenState = _payBillScreenState.asStateFlow() + open val _payBillBottomSheetType = + MutableStateFlow(PayBillBottomSheetType.None) + val payBillBottomSheetType = _payBillBottomSheetType.asStateFlow() + private val _rewardsNudgeDetailEntity = MutableStateFlow(null) val rewardsNudgeDetailEntity = _rewardsNudgeDetailEntity.asStateFlow() @@ -252,6 +271,11 @@ constructor( private val amountChips = MutableStateFlow(emptyList()) + private val _showBillDetailsUpdatedBottomSheet = MutableSharedFlow() + val showBillDetailsUpdatedBottomSheet = _showBillDetailsUpdatedBottomSheet.asSharedFlow() + + var isPayButtonClicked = false + var arcNudgeResponse: ArcNudgeResponse? = null @Inject lateinit var paymentNavigator: PaymentNavigator @@ -272,6 +296,25 @@ constructor( var isIPLPowerPlayThemeExperimentEnabled = false + var updatedBillDetailsEntity: BillDetailsEntity? = null + + private val _navigateToNextScreenFromHelpCta = MutableSharedFlow() + val navigateToNextScreenFromHelpCta = _navigateToNextScreenFromHelpCta.asSharedFlow() + + val helpCta = NaviBbpsCommonUtils.getHelpCtaData(NaviBbpsScreen.NAVI_BBPS_PAY_BILL_SCREEN.name) + + private val _coinsUtilisationPropertiesV2 = MutableStateFlow(null) + val coinUtilisationPropertiesV2 = _coinsUtilisationPropertiesV2.asStateFlow() + + private val _amountAfterCoinDiscount = MutableStateFlow(ZERO_STRING) + val amountAfterCoinDiscount = _amountAfterCoinDiscount.asStateFlow() + + private val _isCoinDiscountApplied = MutableStateFlow(false) + val isCoinDiscountApplied = _isCoinDiscountApplied.asStateFlow() + + private val _isLottieAnimationShown = MutableStateFlow(false) + val isLottieAnimationShown = _isLottieAnimationShown.asStateFlow() + init { viewModelScope.launch(dispatcherProvider.io) { // Concurrent calls section @@ -281,6 +324,7 @@ constructor( launch { fetchBillAndUpdateScreen() } launch { updateScreenState() } launch { updateBottomRewardsNudgeEntity() } + launch { pmsDiscountListener() } } } @@ -437,48 +481,81 @@ constructor( ) updateIsBillLoading(isLoading = false) } else { + fetchMatchingBillAndUpdateEntity() updateBillDetails() + myBillEntity.value?.unpaidBillDetails.let { unpaidBillDetails -> + if ( + TrustedTimeAccessor.getCurrentTimeMillis() - + unpaidBillDetails?.lastGeneratedTimestamp?.toLongOrNull().orZero() > + FirebaseRemoteConfigHelper.getLong( + key = NAVI_BBPS_BILL_REFRESH_DURATION, + defaultValue = 10000L, + ) + ) { + refetchBillDetails() + } + } } } - private fun fetchMatchingBillAndUpdateEntity() { - viewModelScope.launch(dispatcherProvider.io) { - val savedBill = findMatchingSavedBill() + private suspend fun refetchBillDetails() { + fetchBillHandler.refetchBill( + payBillViewModel = this, + onReFetchSuccess = { isBillDetailsChanged, billDetailsEntity -> + if (!isPayButtonClicked && isBillDetailsChanged) { + updatedBillDetailsEntity = billDetailsEntity + _payBillBottomSheetType.update { + PayBillBottomSheetType.BillDetailsUpdateInfo( + title = + naviBbpsDefaultConfig.value.configMessage.billDetailsUpdatedTitle, + description = + naviBbpsDefaultConfig.value.configMessage + .billDetailsUpdatedDescription, + ) + } + _showBillDetailsUpdatedBottomSheet.emit(true) + } + }, + screenName = naviBbpsVmData.screen.screenName, + billAlreadyPaidErrorCodes = naviBbpsDefaultConfig.value.billAlreadyPaidErrorCodes, + ) + } - updateMyBillEntity(myBillEntity = savedBill) - } + fun updateLatestBillDetails() { + _billDetailsEntity.update { updatedBillDetailsEntity } + updateBillDetails() + } + + private suspend fun fetchMatchingBillAndUpdateEntity() { + val savedBill = findMatchingSavedBill() + updateMyBillEntity(myBillEntity = savedBill) } private fun updateBillDetails() { - if (payBillScreenSource is PayBillSource.Others) { - fetchMatchingBillAndUpdateEntity() - billDetailsEntity.value?.let { updateBillAndAmountValues(billDetailsEntity = it) } + billDetailsEntity.value?.let { updateBillAndAmountValues(billDetailsEntity = it) } + billDetailsEntity.value?.let { + fetchAndUpdateBillerAdditionalParams(billerAdditionalParams = it.billerAdditionalParams) + } + if (billCategoryEntity?.categoryId == CATEGORY_ID_CREDIT_CARD) { billDetailsEntity.value?.let { - fetchAndUpdateBillerAdditionalParams( - billerAdditionalParams = it.billerAdditionalParams - ) - } - if (billCategoryEntity?.categoryId == CATEGORY_ID_CREDIT_CARD) { - billDetailsEntity.value?.let { - updateCreditCardPaymentOptions(billDetailsEntity = it) - viewModelScope.launch(dispatcherProvider.main) { - onCreditCardPaymentOptionSelected( - creditCardPaymentOption = creditCardPaymentOptions.first() - ) - } + updateCreditCardPaymentOptions(billDetailsEntity = it) + viewModelScope.launch(dispatcherProvider.main) { + onCreditCardPaymentOptionSelected( + creditCardPaymentOption = creditCardPaymentOptions.first() + ) } } - if ( - !isCategoryOfTypeAmountChipsRequired( - categoryId = billCategoryEntity?.categoryId.orEmpty() - ) - ) { - updateShouldAutoFocusOnAmount(paymentAmount = paymentAmount.value) - return - } - updateAmountChips(chipsConfigData = naviBbpsDefaultConfig.value.chipsConfigData) } + if ( + !isCategoryOfTypeAmountChipsRequired( + categoryId = billCategoryEntity?.categoryId.orEmpty() + ) + ) { + updateShouldAutoFocusOnAmount(paymentAmount = paymentAmount.value) + return + } + updateAmountChips(chipsConfigData = naviBbpsDefaultConfig.value.chipsConfigData) } private fun updateAmountChips(chipsConfigData: ChipsConfigData) { @@ -763,6 +840,10 @@ constructor( } } } + + if (isCategoryOfTypeAmountChipsRequired(billCategoryEntity?.categoryId.orEmpty())) { + updateAmountChipEntity(amount = newAmountValue) + } } internal val isPayButtonEnabled = @@ -807,11 +888,197 @@ constructor( } } + fun getApplicableDiscount() { + if (coinBurnData.value?.expression.isNullOrEmpty()) return + handleDiscountProperties( + discountExpression = coinBurnData.value?.expression.toString(), + cashEquivalentOfDiscount = coinBurnData.value?.cashEquivalentOfDiscount, + coinValue = coinBurnData.value?.config?.coins.toString(), + ) + } + + private fun handleDiscountProperties( + discountExpression: String, + cashEquivalentOfDiscount: Int?, + coinValue: String, + ) { + if (discountExpression.isNotNullAndNotEmpty()) { + val discountAmount = runBlocking { + calculateDiscountedAmountFromFormula( + discountedFormula = discountExpression, + cashEquivalentOfDiscount = cashEquivalentOfDiscount.toString(), + ) + } + val coinsUsed = (discountAmount.toInt() * (coinValue.toInt())).toString() + val discountInfoText = + resProvider.getString( + R.string.bbps_save_rupee_symbol_amount, + discountAmount.getDisplayableAmount(), + ) + val discountInfoTextAfterDiscount = + resProvider.getString( + R.string.bbps_saved_rupee_symbol_amount, + discountAmount.getDisplayableAmount(), + ) + updateCoinsUtilisationProperties( + CoinUtilisationPropertiesV2( + discountAmount = discountAmount, + coinUtilisationText = discountInfoText, + coinUsedForDiscount = coinsUsed.getDisplayableAmount(), + discountAmountTextAfterDiscount = discountInfoTextAfterDiscount, + ) + ) + } else return + } + + private suspend fun calculateDiscountedAmountFromFormula( + discountedFormula: String, + cashEquivalentOfDiscount: String? = null, + ): String { + return evaluateMvelExpression( + key = discountedFormula, + data = + mapOf( + "cashEquivalentOfDiscount" to + (cashEquivalentOfDiscount + ?: coinsSyncManager.getCashEquivalentOfDiscount().toDoubleOrNull() + ?: 0.0), + "billAmount" to (paymentAmount.value.toDoubleOrNull() ?: 0.0), + ), + ZERO_STRING, + ) + } + + private fun updateCoinsUtilisationProperties( + coinUtilisationPropertiesV2: CoinUtilisationPropertiesV2 + ) { + _coinsUtilisationPropertiesV2.update { coinUtilisationPropertiesV2 } + } + + fun onMoreClicked() { + val description = + when (payBillScreenSource) { + is PayBillSource.Prepaid -> { + payBillScreenSource.planItemEntity.packageDescription + } + else -> EMPTY + } + _payBillBottomSheetType.update { + PayBillBottomSheetType.PlanDetails( + title = resProvider.getString(R.string.bbps_plan_details), + description = description, + icon = CommonR.drawable.ic_purple_exclamation, + ctaText = resProvider.getString(R.string.bbps_okay_got_it), + amount = paymentAmount.value.getDisplayableAmount(), + ) + } + } + + fun enteredAmountInputChanged( + newInput: String, + isAmountChangedByChipSelection: Boolean = false, + ) { + viewModelScope.launch(dispatcherProvider.io) { + var newAmount = "" + if (newInput.isNotEmpty() && newInput != ZERO_STRING) { + newAmount = + getValidatedAmountNumber( + text = newInput, + amountMaxLengthAfterDecimal = AMOUNT_MAX_LENGTH_AFTER_DECIMAL, + amountMaxLengthBeforeDecimal = AMOUNT_MAX_LENGTH_BEFORE_DECIMAL, + ) + + if (isAmountChangedByChipSelection) { + newAmount = newInput + } + } + + updatePaymentAmount(newAmountValue = newAmount) + } + } + + fun onAppliedDiscountClicked() { + val discountAmount = coinUtilisationPropertiesV2.value?.discountAmount.orEmpty() + if (discountAmount.isNotEmpty()) { + val paymentAmountAfterCoinDiscount = + (paymentAmount.value.toDoubleOrNull().orZero() - + discountAmount.toDoubleOrNull().orZero()) + .toString() + _amountAfterCoinDiscount.update { paymentAmountAfterCoinDiscount } + _isCoinDiscountApplied.update { true } + } + } + + fun triggerAnimation() { + viewModelScope.launch(dispatcherProvider.io) { + delay(3000) + _isLottieAnimationShown.update { true } + } + } + + fun onRemoveDiscountClicked() { + _isCoinDiscountApplied.update { false } + } + + fun onOfferRolodexClicked(transactionAmount: Double?) { + offerDataList.value?.let { offerDataList -> + _payBillBottomSheetType.update { + PayBillBottomSheetType.OfferList( + offerData = offerDataList, + transactionAmount = transactionAmount, + offerListHeading = + if (offerDataList.size == 1) { + resourceProvider.getString( + R.string.bbps_offer_category_bottomsheet_count_title_singular, + offerDataList.size, + billCategoryEntity?.title ?: EMPTY, + ) + } else { + resourceProvider.getString( + R.string.bbps_offer_category_bottomsheet_count_title_plural, + offerDataList.size, + billCategoryEntity?.title ?: EMPTY, + ) + }, + coinBurnData = null, + ) + } + } + } + + fun onHelpCtaClicked() { + viewModelScope.launch(Dispatchers.IO) { updateNavigateToNextScreenOnHelpCta(helpCta) } + } + + private suspend fun updateNavigateToNextScreenOnHelpCta(ctaData: CtaData?) { + _navigateToNextScreenFromHelpCta.emit(ctaData) + } + + suspend fun pmsDiscountListener() { + naviPaymentRewardEventBus.events.collectLatest { event -> + if (event.showDiscountedAmount) { + onAppliedDiscountClicked() + } else { + onRemoveDiscountClicked() + } + if (event.isDiscountApplyLottieShown) { + _isLottieAnimationShown.update { true } + } + } + } + + fun handleDiscountRemovalOnAmountFieldClick() { + if (billCategoryEntity?.categoryId != CATEGORY_ID_MOBILE_PREPAID) { + onRemoveDiscountClicked() + } + } + internal fun updateIsTokenLoading(isLoading: Boolean) { _isTokenLoading.update { isLoading } } internal fun generatePaymentToken() { + isPayButtonClicked = true viewModelScope.launch(dispatcherProvider.io) { if (billDetailsEntity.value == null) { return@launch diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/PayBillViewModelV2.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/PayBillViewModelV2.kt deleted file mode 100644 index af1f1a1eca..0000000000 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/PayBillViewModelV2.kt +++ /dev/null @@ -1,346 +0,0 @@ -/* - * - * * Copyright © 2024-2025 by Navi Technologies Limited - * * All rights reserved. Strictly confidential - * - */ - -package com.navi.bbps.feature.paybill - -import androidx.lifecycle.SavedStateHandle -import androidx.lifecycle.viewModelScope -import com.navi.base.cache.repository.NaviCacheRepository -import com.navi.base.model.CtaData -import com.navi.base.utils.EMPTY -import com.navi.base.utils.NaviNetworkConnectivity -import com.navi.base.utils.ResourceProvider -import com.navi.base.utils.ZERO_STRING -import com.navi.base.utils.isNotNullAndNotEmpty -import com.navi.base.utils.orZero -import com.navi.bbps.R -import com.navi.bbps.common.AMOUNT_MAX_LENGTH_AFTER_DECIMAL -import com.navi.bbps.common.AMOUNT_MAX_LENGTH_BEFORE_DECIMAL -import com.navi.bbps.common.CATEGORY_ID_MOBILE_PREPAID -import com.navi.bbps.common.CoinsSyncManager -import com.navi.bbps.common.NaviBbpsScreen -import com.navi.bbps.common.model.view.NaviBbpsSessionHelper -import com.navi.bbps.common.repository.BbpsCommonRepository -import com.navi.bbps.common.usecase.FetchBillHandler -import com.navi.bbps.common.usecase.GetABTestingExperimentUseCase -import com.navi.bbps.common.usecase.NaviBbpsConfigUseCase -import com.navi.bbps.common.usecase.RewardNudgeUseCase -import com.navi.bbps.common.utils.NaviBbpsCommonUtils -import com.navi.bbps.common.utils.NaviBbpsCommonUtils.evaluateMvelExpression -import com.navi.bbps.common.utils.NaviBbpsCommonUtils.getValidatedAmountNumber -import com.navi.bbps.common.utils.NaviBbpsCommonUtils.isCategoryOfTypeAmountChipsRequired -import com.navi.bbps.common.utils.NaviBbpsDateUtils -import com.navi.bbps.common.utils.getDisplayableAmount -import com.navi.bbps.feature.category.BillCategoriesRepository -import com.navi.bbps.feature.mybills.MyBillsRepository -import com.navi.bbps.feature.paybill.model.network.PaymentAmountExactness -import com.navi.bbps.feature.paybill.model.view.CoinUtilisationPropertiesV2 -import com.navi.bbps.feature.paybill.model.view.PayBillBottomSheetType -import com.navi.bbps.feature.paybill.model.view.PayBillSource -import com.navi.common.R as CommonR -import com.navi.common.di.CoroutineDispatcherProvider -import com.navi.common.usecase.LitmusExperimentsUseCase -import com.navi.payment.nativepayment.utils.NaviPaymentRewardsEventBus -import com.navi.payment.tstore.repository.TStoreOrderHandler -import dagger.hilt.android.lifecycle.HiltViewModel -import javax.inject.Inject -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asSharedFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.update -import kotlinx.coroutines.launch -import kotlinx.coroutines.runBlocking - -@HiltViewModel -class PayBillViewModelV2 -@Inject -constructor( - savedStateHandle: SavedStateHandle, - private val dispatcherProvider: CoroutineDispatcherProvider, - payBillRepository: PayBillRepository, - naviBbpsDateUtils: NaviBbpsDateUtils, - naviBbpsConfigUseCase: NaviBbpsConfigUseCase, - getABTestingExperimentUseCase: GetABTestingExperimentUseCase, - myBillsRepository: MyBillsRepository, - tStoreOrderHandler: TStoreOrderHandler, - naviNetworkConnectivity: NaviNetworkConnectivity, - fetchBillHandler: FetchBillHandler, - naviBbpsSessionHelper: NaviBbpsSessionHelper, - private val coinsSyncManager: CoinsSyncManager, - billCategoriesRepository: BillCategoriesRepository, - private val resProvider: ResourceProvider, - rewardsNudgeEntityFetchUseCase: RewardNudgeUseCase, - private val naviPaymentRewardEventBus: NaviPaymentRewardsEventBus, - naviCacheRepository: NaviCacheRepository, - naviBbpsCommonRepository: BbpsCommonRepository, - private val resourceProvider: ResourceProvider, - private val litmusExperimentsUseCase: LitmusExperimentsUseCase, -) : - PayBillViewModel( - savedStateHandle = savedStateHandle, - dispatcherProvider = dispatcherProvider, - naviBbpsDateUtils = naviBbpsDateUtils, - naviBbpsConfigUseCase = naviBbpsConfigUseCase, - myBillsRepository = myBillsRepository, - tStoreOrderHandler = tStoreOrderHandler, - naviNetworkConnectivity = naviNetworkConnectivity, - fetchBillHandler = fetchBillHandler, - naviBbpsSessionHelper = naviBbpsSessionHelper, - coinsSyncManager = coinsSyncManager, - billCategoriesRepository = billCategoriesRepository, - resProvider = resProvider, - rewardsNudgeEntityFetchUseCase = rewardsNudgeEntityFetchUseCase, - payBillRepository = payBillRepository, - naviCacheRepository = naviCacheRepository, - naviBbpsCommonRepository = naviBbpsCommonRepository, - resourceProvider = resourceProvider, - litmusExperimentsUseCase = litmusExperimentsUseCase, - ) { - - private val _payBillBottomSheetType = - MutableStateFlow(PayBillBottomSheetType.None) - val payBillBottomSheetType = _payBillBottomSheetType.asStateFlow() - - private val _navigateToNextScreenFromHelpCta = MutableSharedFlow() - val navigateToNextScreenFromHelpCta = _navigateToNextScreenFromHelpCta.asSharedFlow() - - val helpCta = NaviBbpsCommonUtils.getHelpCtaData(NaviBbpsScreen.NAVI_BBPS_PAY_BILL_SCREEN.name) - - private val _coinsUtilisationPropertiesV2 = MutableStateFlow(null) - val coinUtilisationPropertiesV2 = _coinsUtilisationPropertiesV2.asStateFlow() - - private val _amountAfterCoinDiscount = MutableStateFlow(ZERO_STRING) - val amountAfterCoinDiscount = _amountAfterCoinDiscount.asStateFlow() - - private val _isCoinDiscountApplied = MutableStateFlow(false) - val isCoinDiscountApplied = _isCoinDiscountApplied.asStateFlow() - - private val _isLottieAnimationShown = MutableStateFlow(false) - val isLottieAnimationShown = _isLottieAnimationShown.asStateFlow() - - init { - viewModelScope.launch(dispatcherProvider.io) { launch { pmsDiscountListener() } } - } - - override fun updatePaymentAmount(newAmountValue: String) { - val validatedNewAmount = - if (newAmountValue.toDoubleOrNull().orZero() <= 0) "" else newAmountValue - _paymentAmount.update { validatedNewAmount } - - val paymentAmountValue = paymentAmount.value.toDoubleOrNull() ?: 0.0 - val initialPaymentAmountValue = initialPaymentAmount.value.toDoubleOrNull() ?: 0.0 - - if (validatedNewAmount.isEmpty()) { - updateShowErrorText(value = false) - } else { - when (paymentAmountExactness.value) { - PaymentAmountExactness.EXACT_AND_ABOVE -> { - updateShowErrorText(value = paymentAmountValue < initialPaymentAmountValue) - } - PaymentAmountExactness.EXACT_AND_BELOW -> { - updateShowErrorText(value = paymentAmountValue > initialPaymentAmountValue) - } - PaymentAmountExactness.EXACT -> { - updateShowErrorText(value = paymentAmountValue != initialPaymentAmountValue) - } - } - } - - if (isCategoryOfTypeAmountChipsRequired(billCategoryEntity?.categoryId.orEmpty())) { - updateAmountChipEntity(amount = newAmountValue) - } - } - - fun getApplicableDiscount() { - if (coinBurnData.value?.expression.isNullOrEmpty()) return - handleDiscountProperties( - discountExpression = coinBurnData.value?.expression.toString(), - cashEquivalentOfDiscount = coinBurnData.value?.cashEquivalentOfDiscount, - coinValue = coinBurnData.value?.config?.coins.toString(), - ) - } - - private fun handleDiscountProperties( - discountExpression: String, - cashEquivalentOfDiscount: Int?, - coinValue: String, - ) { - if (discountExpression.isNotNullAndNotEmpty()) { - val discountAmount = runBlocking { - calculateDiscountedAmountFromFormula( - discountedFormula = discountExpression, - cashEquivalentOfDiscount = cashEquivalentOfDiscount.toString(), - ) - } - val coinsUsed = (discountAmount.toInt() * (coinValue.toInt())).toString() - val discountInfoText = - resProvider.getString( - R.string.bbps_save_rupee_symbol_amount, - discountAmount.getDisplayableAmount(), - ) - val discountInfoTextAfterDiscount = - resProvider.getString( - R.string.bbps_saved_rupee_symbol_amount, - discountAmount.getDisplayableAmount(), - ) - updateCoinsUtilisationProperties( - CoinUtilisationPropertiesV2( - discountAmount = discountAmount, - coinUtilisationText = discountInfoText, - coinUsedForDiscount = coinsUsed.getDisplayableAmount(), - discountAmountTextAfterDiscount = discountInfoTextAfterDiscount, - ) - ) - } else return - } - - private suspend fun calculateDiscountedAmountFromFormula( - discountedFormula: String, - cashEquivalentOfDiscount: String? = null, - ): String { - return evaluateMvelExpression( - key = discountedFormula, - data = - mapOf( - "cashEquivalentOfDiscount" to - (cashEquivalentOfDiscount - ?: coinsSyncManager.getCashEquivalentOfDiscount().toDoubleOrNull() - ?: 0.0), - "billAmount" to (paymentAmount.value.toDoubleOrNull() ?: 0.0), - ), - ZERO_STRING, - ) - } - - private fun updateCoinsUtilisationProperties( - coinUtilisationPropertiesV2: CoinUtilisationPropertiesV2 - ) { - _coinsUtilisationPropertiesV2.update { coinUtilisationPropertiesV2 } - } - - fun onMoreClicked() { - val description = - when (payBillScreenSource) { - is PayBillSource.Prepaid -> { - payBillScreenSource.planItemEntity.packageDescription - } - else -> EMPTY - } - _payBillBottomSheetType.update { - PayBillBottomSheetType.PlanDetails( - title = resProvider.getString(R.string.bbps_plan_details), - description = description, - icon = CommonR.drawable.ic_purple_exclamation, - ctaText = resProvider.getString(R.string.bbps_okay_got_it), - amount = paymentAmount.value.getDisplayableAmount(), - ) - } - } - - fun enteredAmountInputChanged( - newInput: String, - isAmountChangedByChipSelection: Boolean = false, - ) { - viewModelScope.launch(dispatcherProvider.io) { - var newAmount = "" - if (newInput.isNotEmpty() && newInput != ZERO_STRING) { - newAmount = - getValidatedAmountNumber( - text = newInput, - amountMaxLengthAfterDecimal = AMOUNT_MAX_LENGTH_AFTER_DECIMAL, - amountMaxLengthBeforeDecimal = AMOUNT_MAX_LENGTH_BEFORE_DECIMAL, - ) - - if (isAmountChangedByChipSelection) { - newAmount = newInput - } - } - - updatePaymentAmount(newAmountValue = newAmount) - } - } - - fun onAppliedDiscountClicked() { - val discountAmount = coinUtilisationPropertiesV2.value?.discountAmount.orEmpty() - if (discountAmount.isNotEmpty()) { - val paymentAmountAfterCoinDiscount = - (paymentAmount.value.toDoubleOrNull().orZero() - - discountAmount.toDoubleOrNull().orZero()) - .toString() - _amountAfterCoinDiscount.update { paymentAmountAfterCoinDiscount } - _isCoinDiscountApplied.update { true } - } - } - - fun triggerAnimation() { - viewModelScope.launch(dispatcherProvider.io) { - delay(3000) - _isLottieAnimationShown.update { true } - } - } - - fun onRemoveDiscountClicked() { - _isCoinDiscountApplied.update { false } - } - - fun onOfferRolodexClicked(transactionAmount: Double?) { - offerDataList.value?.let { offerDataList -> - _payBillBottomSheetType.update { - PayBillBottomSheetType.OfferList( - offerData = offerDataList, - transactionAmount = transactionAmount, - offerListHeading = - if (offerDataList.size == 1) { - resourceProvider.getString( - R.string.bbps_offer_category_bottomsheet_count_title_singular, - offerDataList.size, - billCategoryEntity?.title ?: EMPTY, - ) - } else { - resourceProvider.getString( - R.string.bbps_offer_category_bottomsheet_count_title_plural, - offerDataList.size, - billCategoryEntity?.title ?: EMPTY, - ) - }, - coinBurnData = null, - ) - } - } - } - - fun onHelpCtaClicked() { - viewModelScope.launch(Dispatchers.IO) { updateNavigateToNextScreenOnHelpCta(helpCta) } - } - - private suspend fun updateNavigateToNextScreenOnHelpCta(ctaData: CtaData?) { - _navigateToNextScreenFromHelpCta.emit(ctaData) - } - - suspend fun pmsDiscountListener() { - naviPaymentRewardEventBus.events.collectLatest { event -> - if (event.showDiscountedAmount) { - onAppliedDiscountClicked() - } else { - onRemoveDiscountClicked() - } - if (event.isDiscountApplyLottieShown) { - _isLottieAnimationShown.update { true } - } - } - } - - fun handleDiscountRemovalOnAmountFieldClick() { - if (billCategoryEntity?.categoryId != CATEGORY_ID_MOBILE_PREPAID) { - onRemoveDiscountClicked() - } - } -} diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/model/view/PayBillBottomSheetType.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/model/view/PayBillBottomSheetType.kt index c1ef3c57fc..9526d03269 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/model/view/PayBillBottomSheetType.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/model/view/PayBillBottomSheetType.kt @@ -26,5 +26,8 @@ sealed class PayBillBottomSheetType { val transactionAmount: Double?, ) : PayBillBottomSheetType() + data class BillDetailsUpdateInfo(val title: String, val description: String) : + PayBillBottomSheetType() + data object None : PayBillBottomSheetType() } diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/AmountTextFieldV2.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/AmountTextFieldV2.kt index b7f13811a8..9e951da4f8 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/AmountTextFieldV2.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/AmountTextFieldV2.kt @@ -83,7 +83,7 @@ private val AMOUNT_FIELD_FONT_WEIGHT = FontWeightEnum.NAVI_HEADLINE_REGULAR @OptIn(ExperimentalMaterialApi::class) @Composable -fun AmountTextFieldV2( +fun AmountTextField( modifier: Modifier, isReadOnly: Boolean, focusManager: FocusManager, diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/PayBillBottomSheetContent.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/PayBillBottomSheetContent.kt index 41c9364455..9f2e3a29f8 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/PayBillBottomSheetContent.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/PayBillBottomSheetContent.kt @@ -20,15 +20,19 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.navi.base.utils.EMPTY import com.navi.base.utils.SPACE +import com.navi.bbps.R import com.navi.bbps.common.BULLET import com.navi.bbps.common.SYMBOL_RUPEE import com.navi.bbps.common.theme.NaviBbpsColor import com.navi.bbps.common.ui.BbpsOfferBottomSheet +import com.navi.bbps.common.ui.BottomSheetContentWithIconHeaderPrimarySecondaryButton import com.navi.bbps.feature.paybill.model.view.PayBillBottomSheetType +import com.navi.common.R as CommonR import com.navi.common.customview.LoaderRoundedButton import com.navi.design.font.FontWeightEnum import com.navi.design.font.getFontWeight @@ -61,6 +65,17 @@ fun PayBillBottomSheetContent( closeSheet() } } + is PayBillBottomSheetType.BillDetailsUpdateInfo -> { + BottomSheetContentWithIconHeaderPrimarySecondaryButton( + iconId = CommonR.drawable.ic_purple_exclamation, + onPrimaryButtonClicked = closeSheet, + description = payBillBottomSheetType.description, + header = payBillBottomSheetType.title, + primaryButton = stringResource(id = R.string.bbps_okay_got_it), + secondaryButton = null, + onSecondaryButtonClicked = {}, + ) + } is PayBillBottomSheetType.None -> { // Do nothing } diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/PayBillScreenV2.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/PayBillScreen.kt similarity index 90% rename from android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/PayBillScreenV2.kt rename to android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/PayBillScreen.kt index 1a4d8bedbb..74904db8c6 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/PayBillScreenV2.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/PayBillScreen.kt @@ -82,6 +82,7 @@ import com.navi.bbps.common.NaviBbpsScreen import com.navi.bbps.common.RCBP_CATEGORY import com.navi.bbps.common.SCROLL_OFFSET_FOR_TITLE_IN_HEADER import com.navi.bbps.common.SYMBOL_RUPEE +import com.navi.bbps.common.TAG_BILL_ALREADY_PAID_ON_REFETCH import com.navi.bbps.common.TAG_BILL_FETCH_ERROR import com.navi.bbps.common.model.config.NaviBbpsDefaultConfig import com.navi.bbps.common.model.view.LoadingState @@ -111,12 +112,13 @@ import com.navi.bbps.feature.destinations.BillCategoriesScreenV2Destination import com.navi.bbps.feature.destinations.MyBillHistoryDetailsScreenDestination import com.navi.bbps.feature.mybills.model.view.MyBillEntity import com.navi.bbps.feature.mybills.model.view.UnpaidBillDetails -import com.navi.bbps.feature.paybill.PayBillViewModelV2 +import com.navi.bbps.feature.paybill.PayBillViewModel import com.navi.bbps.feature.paybill.model.network.PayBillResponse import com.navi.bbps.feature.paybill.model.network.PaymentAmountExactness import com.navi.bbps.feature.paybill.model.view.AmountChipEntity import com.navi.bbps.feature.paybill.model.view.CoinUtilisationPropertiesV2 import com.navi.bbps.feature.paybill.model.view.CreditCardPaymentOption +import com.navi.bbps.feature.paybill.model.view.PayBillBottomSheetType import com.navi.bbps.feature.paybill.model.view.PayBillHeaderState import com.navi.bbps.feature.paybill.model.view.PayBillScreenState import com.navi.bbps.feature.paybill.model.view.PayBillSource @@ -159,9 +161,9 @@ import kotlinx.coroutines.launch @Destination @Composable -fun PayBillScreenV2( +fun PayBillScreen( naviBbpsActivity: NaviBbpsActivity, - payBillViewModelV2: PayBillViewModelV2 = hiltViewModel(), + payBillViewModel: PayBillViewModel = hiltViewModel(), naviCheckoutViewModel: NaviCheckoutViewModelV2 = hiltViewModel(), billCategoryEntity: BillCategoryEntity, navigator: DestinationsNavigator, @@ -172,16 +174,16 @@ fun PayBillScreenV2( initialSource: String, naviBbpsAnalytics: NaviBbpsAnalytics.PayBill = NaviBbpsAnalytics.INSTANCE.PayBill(), ) { - val billDetailsEntity by payBillViewModelV2.billDetailsEntity.collectAsStateWithLifecycle() + val billDetailsEntity by payBillViewModel.billDetailsEntity.collectAsStateWithLifecycle() val keyboardController = LocalSoftwareKeyboardController.current val paymentsResultLauncher = rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> - payBillViewModelV2.updateIsTokenLoading(isLoading = false) + payBillViewModel.updateIsTokenLoading(isLoading = false) val status = result.data?.extras?.getString(STATUS.uppercase(Locale.getDefault())) val paymentCancelSource = result.data?.extras?.getString(PAYMENT_CANCEL_SOURCE).orEmpty() - payBillViewModelV2.prevPaymentCancelSource = paymentCancelSource + payBillViewModel.prevPaymentCancelSource = paymentCancelSource val data = result.data?.extras?.getString(KEY_DATA) val shouldReopenKeyboard = result.data?.extras?.getBoolean(SHOULD_PROMPT_AMOUNT_EDIT).orFalse() @@ -190,7 +192,7 @@ fun PayBillScreenV2( } when (status) { PaymentSdkTypes.DISMISS_LOADER.name -> { - payBillViewModelV2.updateIsTokenLoading(isLoading = false) + payBillViewModel.updateIsTokenLoading(isLoading = false) } else -> { val dataJSONObject = data?.stringToJsonObject() @@ -200,20 +202,20 @@ fun PayBillScreenV2( paymentStatus = dataJSONObject?.optString(STATUS).toString(), response = data, categoryId = billCategoryEntity.categoryId, - sessionAttribute = payBillViewModelV2.getNaviBbpsSessionAttributes(), + sessionAttribute = payBillViewModel.getNaviBbpsSessionAttributes(), source = source, initialSource = initialSource, ) if (dataJSONObject != null) { - payBillViewModelV2.onPaymentResultReceived( - bbpsTransactionId = payBillViewModelV2.txnId, + payBillViewModel.onPaymentResultReceived( + bbpsTransactionId = payBillViewModel.txnId, paymentResponseMetadata = dataJSONObject, ) } } } } - val orderReferenceId by payBillViewModelV2.orderReferenceId.collectAsStateWithLifecycle() + val orderReferenceId by payBillViewModel.orderReferenceId.collectAsStateWithLifecycle() LaunchedEffect(Unit) { naviBbpsActivity.errorEventHandler.bbpsErrorCtaClickEvent.collectLatest { event -> // In case of bill fetch error, go back to previous screen @@ -226,7 +228,7 @@ fun PayBillScreenV2( LaunchedEffect(Unit) { val category = billCategoryEntity.categoryId val paymentCheckoutConfig = - payBillViewModelV2.naviBbpsDefaultConfig.value.paymentCheckoutConfig.firstOrNull { + payBillViewModel.naviBbpsDefaultConfig.value.paymentCheckoutConfig.firstOrNull { it.categoryId == category } naviCheckoutViewModel.setUpOneClickCheckout( @@ -238,7 +240,7 @@ fun PayBillScreenV2( } LaunchedEffect(Unit) { - payBillViewModelV2.navigateToOrderDetailsScreen.collectLatest { navigateToOrderDetailsScreen + payBillViewModel.navigateToOrderDetailsScreen.collectLatest { navigateToOrderDetailsScreen -> if (navigateToOrderDetailsScreen) { val orderDetailsCtaData = @@ -270,48 +272,45 @@ fun PayBillScreenV2( val view = LocalView.current val scope = rememberCoroutineScope() - val isBillLoading by payBillViewModelV2.isBillLoading.collectAsStateWithLifecycle() - val paymentAmount by payBillViewModelV2.paymentAmount.collectAsStateWithLifecycle() + val isBillLoading by payBillViewModel.isBillLoading.collectAsStateWithLifecycle() + val paymentAmount by payBillViewModel.paymentAmount.collectAsStateWithLifecycle() val shouldAutoFocusOnAmount by - payBillViewModelV2.shouldAutoFocusOnAmount.collectAsStateWithLifecycle() - val errorMessageId by payBillViewModelV2.errorMessageId.collectAsStateWithLifecycle() - val showErrorText by payBillViewModelV2.showErrorText.collectAsStateWithLifecycle() - val isTokenLoading by payBillViewModelV2.isTokenLoading.collectAsStateWithLifecycle() - val initialPaymentAmount by - payBillViewModelV2.initialPaymentAmount.collectAsStateWithLifecycle() - val isAmountReadOnly by payBillViewModelV2.isAmountReadOnly.collectAsStateWithLifecycle() - val isPayButtonEnabled by payBillViewModelV2.isPayButtonEnabled.collectAsStateWithLifecycle() - val payBillHeaderState by payBillViewModelV2.payBillHeaderState.collectAsStateWithLifecycle() - val payBillScreenState by payBillViewModelV2.payBillScreenState.collectAsStateWithLifecycle() + payBillViewModel.shouldAutoFocusOnAmount.collectAsStateWithLifecycle() + val errorMessageId by payBillViewModel.errorMessageId.collectAsStateWithLifecycle() + val showErrorText by payBillViewModel.showErrorText.collectAsStateWithLifecycle() + val isTokenLoading by payBillViewModel.isTokenLoading.collectAsStateWithLifecycle() + val initialPaymentAmount by payBillViewModel.initialPaymentAmount.collectAsStateWithLifecycle() + val isAmountReadOnly by payBillViewModel.isAmountReadOnly.collectAsStateWithLifecycle() + val isPayButtonEnabled by payBillViewModel.isPayButtonEnabled.collectAsStateWithLifecycle() + val payBillHeaderState by payBillViewModel.payBillHeaderState.collectAsStateWithLifecycle() + val payBillScreenState by payBillViewModel.payBillScreenState.collectAsStateWithLifecycle() val billerAdditionalParams by - payBillViewModelV2.billerAdditionalParams.collectAsStateWithLifecycle() + payBillViewModel.billerAdditionalParams.collectAsStateWithLifecycle() val formattedLastPaidDate by - payBillViewModelV2.formattedLastPaidDate.collectAsStateWithLifecycle() + payBillViewModel.formattedLastPaidDate.collectAsStateWithLifecycle() val formattedLastPaidAmount by - payBillViewModelV2.formattedLastPaidAmount.collectAsStateWithLifecycle() - val myBillEntity by payBillViewModelV2.myBillEntity.collectAsStateWithLifecycle() + payBillViewModel.formattedLastPaidAmount.collectAsStateWithLifecycle() + val myBillEntity by payBillViewModel.myBillEntity.collectAsStateWithLifecycle() val coinUtilisationProperties by - payBillViewModelV2.coinUtilisationPropertiesV2.collectAsStateWithLifecycle() - val creditCardPaymentOptions = payBillViewModelV2.creditCardPaymentOptions + payBillViewModel.coinUtilisationPropertiesV2.collectAsStateWithLifecycle() + val creditCardPaymentOptions = payBillViewModel.creditCardPaymentOptions val payBillBottomSheetType by - payBillViewModelV2.payBillBottomSheetType.collectAsStateWithLifecycle() - val amountChipEntityList by - payBillViewModelV2.amountChipEntityList.collectAsStateWithLifecycle() - val offerDataList by payBillViewModelV2.offerDataList.collectAsStateWithLifecycle() - val coinBurnData by payBillViewModelV2.coinBurnData.collectAsStateWithLifecycle() + payBillViewModel.payBillBottomSheetType.collectAsStateWithLifecycle() + val amountChipEntityList by payBillViewModel.amountChipEntityList.collectAsStateWithLifecycle() + val offerDataList by payBillViewModel.offerDataList.collectAsStateWithLifecycle() + val coinBurnData by payBillViewModel.coinBurnData.collectAsStateWithLifecycle() val amountAfterCoinDiscount by - payBillViewModelV2.amountAfterCoinDiscount.collectAsStateWithLifecycle() + payBillViewModel.amountAfterCoinDiscount.collectAsStateWithLifecycle() val isCoinDiscountApplied by - payBillViewModelV2.isCoinDiscountApplied.collectAsStateWithLifecycle() + payBillViewModel.isCoinDiscountApplied.collectAsStateWithLifecycle() val isLottieAnimationShown by - payBillViewModelV2.isLottieAnimationShown.collectAsStateWithLifecycle() + payBillViewModel.isLottieAnimationShown.collectAsStateWithLifecycle() val naviBbpsDefaultConfig by - payBillViewModelV2.naviBbpsDefaultConfig.collectAsStateWithLifecycle() - val isConsentViewVisible by - payBillViewModelV2.isConsentViewVisible.collectAsStateWithLifecycle() - val isConsentProvided by payBillViewModelV2.isConsentProvided.collectAsStateWithLifecycle() - val isArcProtected by payBillViewModelV2.isArcProtected.collectAsStateWithLifecycle() + payBillViewModel.naviBbpsDefaultConfig.collectAsStateWithLifecycle() + val isConsentViewVisible by payBillViewModel.isConsentViewVisible.collectAsStateWithLifecycle() + val isConsentProvided by payBillViewModel.isConsentProvided.collectAsStateWithLifecycle() + val isArcProtected by payBillViewModel.isArcProtected.collectAsStateWithLifecycle() val sortedOfferList by remember(offerDataList, paymentAmount) { @@ -332,7 +331,7 @@ fun PayBillScreenV2( if (offerDataList.isNotNullAndNotEmpty()) { naviBbpsAnalytics.rewardCampaignList( offerDataList = offerDataList.orEmpty(), - sessionAttribute = payBillViewModelV2.getNaviBbpsSessionAttributes(), + sessionAttribute = payBillViewModel.getNaviBbpsSessionAttributes(), source = source, initialSource = initialSource, ) @@ -344,28 +343,28 @@ fun PayBillScreenV2( billerId = payBillScreenSource.billerId, billCategoryEntity = billCategoryEntity, billDetailsEntity = billDetailsEntity, - sessionAttribute = payBillViewModelV2.getNaviBbpsSessionAttributes(), + sessionAttribute = payBillViewModel.getNaviBbpsSessionAttributes(), source = source, initialSource = initialSource, ) - payBillViewModelV2.fetchOffersAndCoinBurnForProduct( + payBillViewModel.fetchOffersAndCoinBurnForProduct( attributes = mapOf( RCBP_CATEGORY to billCategoryEntity.categoryId, BILLER_UNIQUE_ID to payBillScreenSource.billerId, ) ) - payBillViewModelV2.startPaymentFlow.collectLatest { payBillResponse -> + payBillViewModel.startPaymentFlow.collectLatest { payBillResponse -> if (payBillResponse != null) { startPaymentFlow( payBillResponse = payBillResponse, initiatePayment = naviCheckoutViewModel::onTokenGenerated, naviBbpsAnalytics = naviBbpsAnalytics, billCategoryEntity = billCategoryEntity, - sessionAttribute = payBillViewModelV2.getNaviBbpsSessionAttributes(), + sessionAttribute = payBillViewModel.getNaviBbpsSessionAttributes(), source = source, initialSource = initialSource, - prevPaymentCancelSource = payBillViewModelV2.prevPaymentCancelSource, + prevPaymentCancelSource = payBillViewModel.prevPaymentCancelSource, coinUtilisationPropertiesV2 = coinUtilisationProperties, isAppliedDiscountClicked = isCoinDiscountApplied, isDiscountApplyLottieShown = isLottieAnimationShown, @@ -379,13 +378,13 @@ fun PayBillScreenV2( } LaunchedEffect(Unit) { - payBillViewModelV2.navigateToNextScreenFromHelpCta.collectLatest { + payBillViewModel.navigateToNextScreenFromHelpCta.collectLatest { it?.let { NaviBbpsRouter.onCtaClick(naviBbpsActivity = naviBbpsActivity, ctaData = it) } } } LaunchedEffect(paymentAmount, offerDataList, coinBurnData) { - payBillViewModelV2.getApplicableDiscount() + payBillViewModel.getApplicableDiscount() } val bottomSheetState = @@ -401,10 +400,17 @@ fun PayBillScreenV2( event.errorConfig.tag == TAG_BILL_FETCH_ERROR -> { navigator.navigateUp() } + event.errorConfig.tag == TAG_BILL_ALREADY_PAID_ON_REFETCH -> { + if (source == NAVI_HOME || source == EMPTY) { + naviBbpsActivity.finish() + } else { + navigator.navigateUp() + } + } event.clickedButtonConfig.action is NaviBbpsButtonAction.Redirect -> { val apiUrl = event.clickedButtonConfig.action.url if (isUrlPathValidForSavedBills(urlPath = apiUrl)) { - payBillViewModelV2.onSavedBillCtaClicked(apiUrl = apiUrl) + payBillViewModel.onSavedBillCtaClicked(apiUrl = apiUrl) bottomSheetState.hide() navigator.navigateUp() } @@ -414,7 +420,7 @@ fun PayBillScreenV2( } LaunchedEffect(Unit) { - payBillViewModelV2.goToNextScreen.collectLatest { nextScreen -> + payBillViewModel.goToNextScreen.collectLatest { nextScreen -> val direction = nextScreen.first val clearBackStack = nextScreen.second if (direction != null) { @@ -459,16 +465,16 @@ fun PayBillScreenV2( isChecked = !isConsentProvided, billDetailsEntity = payBillScreenSource.billDetailsEntity, billCategoryEntity = billCategoryEntity, - sessionAttribute = payBillViewModelV2.getNaviBbpsSessionAttributes(), + sessionAttribute = payBillViewModel.getNaviBbpsSessionAttributes(), source = source, initialSource = initialSource, ) - payBillViewModelV2.toggleConsentProvided() + payBillViewModel.toggleConsentProvided() } val onAmountChipClicked = { amount: String -> focusManager.clearFocus() - payBillViewModelV2.enteredAmountInputChanged( + payBillViewModel.enteredAmountInputChanged( newInput = amount, isAmountChangedByChipSelection = true, ) @@ -484,12 +490,12 @@ fun PayBillScreenV2( } val onMoreClicked = { - payBillViewModelV2.onMoreClicked() + payBillViewModel.onMoreClicked() openSheet() } val onCreditCardPaymentOptionSelected = { creditCardPaymentOption: CreditCardPaymentOption -> - payBillViewModelV2.onCreditCardPaymentOptionSelected(creditCardPaymentOption) + payBillViewModel.onCreditCardPaymentOptionSelected(creditCardPaymentOption) } var loadingState by remember { @@ -530,6 +536,24 @@ fun PayBillScreenV2( } } + LaunchedEffect(Unit) { + payBillViewModel.showBillDetailsUpdatedBottomSheet.collectLatest { + showBillDetailsUpdatedBottomSheet -> + if (showBillDetailsUpdatedBottomSheet) { + openSheet() + } + } + } + + LaunchedEffect(bottomSheetState.isVisible) { + if ( + !bottomSheetState.isVisible && + payBillBottomSheetType is PayBillBottomSheetType.BillDetailsUpdateInfo + ) { + payBillViewModel.updateLatestBillDetails() + } + } + NaviBbpsModalBottomSheetLayout( sheetContent = { PayBillBottomSheetContent( @@ -549,8 +573,8 @@ fun PayBillScreenV2( scrollState = scrollState, isCreditCardCategory = isCreditCardCategory, billCategoryEntity = billCategoryEntity, - helpCtaText = payBillViewModelV2.helpCta.title.orEmpty(), - onHelpCtaClicked = payBillViewModelV2::onHelpCtaClicked, + helpCtaText = payBillViewModel.helpCta.title.orEmpty(), + onHelpCtaClicked = payBillViewModel::onHelpCtaClicked, payBillScreenSource = payBillScreenSource, ) }, @@ -614,7 +638,7 @@ fun PayBillScreenV2( isAmountReadOnly = isAmountReadOnly, generateToken = { keyboardController?.customHide(context = context, view = view) - payBillViewModelV2.generatePaymentToken() + payBillViewModel.generatePaymentToken() }, ) } @@ -634,7 +658,7 @@ fun PayBillScreenV2( else { if (isCreditCardCategory) { if (payBillScreenState is PayBillScreenState.BillDetailsAvailable) { - RenderCreditCardPayBillScreenV2( + RenderCreditCardPayBillScreen( payBillScreenState = payBillScreenState as PayBillScreenState.BillDetailsAvailable, @@ -655,17 +679,17 @@ fun PayBillScreenV2( initialPaymentAmount = initialPaymentAmount, paymentAmount = paymentAmount, onAmountValueChanged = - payBillViewModelV2::onAmountValueChanged, + payBillViewModel::onAmountValueChanged, scrollState = scrollState, coinBurnData = coinBurnData, onAppliedDiscountClicked = - payBillViewModelV2::onAppliedDiscountClicked, + payBillViewModel::onAppliedDiscountClicked, onRemoveDiscountClicked = - payBillViewModelV2::onRemoveDiscountClicked, + payBillViewModel::onRemoveDiscountClicked, isAppliedDiscountClicked = isCoinDiscountApplied, sortedOfferList = sortedOfferList, onOfferClicked = { - payBillViewModelV2.onOfferRolodexClicked( + payBillViewModel.onOfferRolodexClicked( if (paymentAmount.isNotEmpty()) paymentAmount.toDouble() else null @@ -676,8 +700,7 @@ fun PayBillScreenV2( initialSource = initialSource, coinBurnData = coinBurnData, sessionAttribute = - payBillViewModelV2 - .getNaviBbpsSessionAttributes(), + payBillViewModel.getNaviBbpsSessionAttributes(), billCategoryEntity = billCategoryEntity, ) openSheet() @@ -690,7 +713,7 @@ fun PayBillScreenV2( ) } } else { - RenderNonCreditCardPayBillScreenV2( + RenderNonCreditCardPayBillScreen( billDetailsEntity = billDetailsEntity, phoneNumberDetail = phoneNumberDetail, payBillHeaderState = payBillHeaderState, @@ -700,7 +723,7 @@ fun PayBillScreenV2( initialSource = initialSource, formattedLastPaidDate = formattedLastPaidDate, billCategoryEntity = billCategoryEntity, - payBillViewModelV2 = payBillViewModelV2, + payBillViewModel = payBillViewModel, keyboardController = keyboardController, context = context, view = view, @@ -771,7 +794,7 @@ fun PayBillScreenV2( .padding(top = 16.dp) .fillMaxWidth(), ) - payBillViewModelV2.triggerAnimation() + payBillViewModel.triggerAnimation() } BottomBarShadow(shadowHeight = 32.dp) } @@ -968,6 +991,7 @@ fun onBillHistoryClicked( accountHolderName = payBillHeaderState.phoneContactEntity.name, billerAdditionalParams = emptyList(), planItemDetails = emptyMap(), + lastGeneratedTimestamp = EMPTY, ), ) is PayBillHeaderState.OtherHeaders -> @@ -989,6 +1013,7 @@ fun onBillHistoryClicked( accountHolderName = billDetailsEntity?.accountHolderName.orEmpty(), billerAdditionalParams = emptyList(), planItemDetails = emptyMap(), + lastGeneratedTimestamp = EMPTY, ), ) else -> defaultMyBillEntity diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/RenderCreditCardPayBillScreenV2.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/RenderCreditCardPayBillScreen.kt similarity index 99% rename from android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/RenderCreditCardPayBillScreenV2.kt rename to android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/RenderCreditCardPayBillScreen.kt index 679ff19c09..3f4c4fd2a4 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/RenderCreditCardPayBillScreenV2.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/RenderCreditCardPayBillScreen.kt @@ -106,7 +106,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.filter @Composable -fun RenderCreditCardPayBillScreenV2( +fun RenderCreditCardPayBillScreen( payBillScreenState: PayBillScreenState.BillDetailsAvailable, formattedLastPaidDate: String, formattedLastPaidAmount: String, diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/RenderNonCreditCardPayBillScreenV2.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/RenderNonCreditCardPayBillScreen.kt similarity index 96% rename from android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/RenderNonCreditCardPayBillScreenV2.kt rename to android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/RenderNonCreditCardPayBillScreen.kt index 1cbdbd6375..6c05a93926 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/RenderNonCreditCardPayBillScreenV2.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/paybill/ui/RenderNonCreditCardPayBillScreen.kt @@ -53,7 +53,7 @@ import com.navi.bbps.feature.customerinput.model.view.BillDetailsEntity import com.navi.bbps.feature.customerinput.model.view.BillerAdditionalParamsEntity import com.navi.bbps.feature.customerinput.model.view.BillerDetailsEntity import com.navi.bbps.feature.mybills.model.view.MyBillEntity -import com.navi.bbps.feature.paybill.PayBillViewModelV2 +import com.navi.bbps.feature.paybill.PayBillViewModel import com.navi.bbps.feature.paybill.model.view.AmountChipEntity import com.navi.bbps.feature.paybill.model.view.CoinUtilisationPropertiesV2 import com.navi.bbps.feature.paybill.model.view.PayBillHeaderState @@ -73,7 +73,7 @@ import com.navi.rr.common.models.OfferData import com.ramcosta.composedestinations.navigation.DestinationsNavigator @Composable -fun RenderNonCreditCardPayBillScreenV2( +fun RenderNonCreditCardPayBillScreen( billDetailsEntity: BillDetailsEntity?, phoneNumberDetail: PhoneContactEntity, payBillHeaderState: PayBillHeaderState?, @@ -83,7 +83,7 @@ fun RenderNonCreditCardPayBillScreenV2( initialSource: String, formattedLastPaidDate: String, billCategoryEntity: BillCategoryEntity, - payBillViewModelV2: PayBillViewModelV2, + payBillViewModel: PayBillViewModel, keyboardController: SoftwareKeyboardController?, context: Context, view: View, @@ -120,12 +120,12 @@ fun RenderNonCreditCardPayBillScreenV2( initialSource = initialSource, formattedLastPaidDate = formattedLastPaidDate, billCategoryEntity = billCategoryEntity, - removeDiscountOnViewHistoryClicked = payBillViewModelV2::onRemoveDiscountClicked, + removeDiscountOnViewHistoryClicked = payBillViewModel::onRemoveDiscountClicked, keyboardController = keyboardController, context = context, view = view, ) - AmountTextFieldV2( + AmountTextField( modifier = Modifier.Companion.fillMaxWidth().padding(top = 32.dp), isReadOnly = isAmountReadOnly, focusManager = focusManager, @@ -133,14 +133,13 @@ fun RenderNonCreditCardPayBillScreenV2( stringResource(id = errorMessageId, initialPaymentAmount.getDisplayableAmount()), paymentAmount = if (isAmountReadOnly) paymentAmount.getDisplayableAmount() else paymentAmount, - onPaymentAmountChanged = payBillViewModelV2::onAmountValueChanged, + onPaymentAmountChanged = payBillViewModel::onAmountValueChanged, amountMaxLength = MAX_AMOUNT_LENGTH, showErrorText = showErrorText, isAppliedClicked = isCoinDiscountApplied, discountedAmount = amountAfterCoinDiscount, shouldAutoFocus = shouldAutoFocusOnAmount, - removeDiscountOnAmountFieldClick = - payBillViewModelV2::handleDiscountRemovalOnAmountFieldClick, + removeDiscountOnAmountFieldClick = payBillViewModel::handleDiscountRemovalOnAmountFieldClick, ) if (amountChipEntityList.isNotEmpty()) { Column { @@ -149,7 +148,7 @@ fun RenderNonCreditCardPayBillScreenV2( onAmountChipClicked = onAmountChipClicked, amountChipEntityList = amountChipEntityList, isCoinDiscountApplied = isCoinDiscountApplied, - onRemoveDiscountClicked = payBillViewModelV2::onRemoveDiscountClicked, + onRemoveDiscountClicked = payBillViewModel::onRemoveDiscountClicked, ) Spacer(modifier = Modifier.Companion.height(24.dp)) @@ -175,17 +174,17 @@ fun RenderNonCreditCardPayBillScreenV2( offerSuffix = offerItem.offer.titleSuffix.orEmpty(), coinUtilisationPropertiesV2 = coinUtilisationProperties, onAppliedDiscountClicked = - payBillViewModelV2::onAppliedDiscountClicked, + payBillViewModel::onAppliedDiscountClicked, isCoinDiscountAppliedClicked = isCoinDiscountApplied, onRemoveDiscountClicked = - payBillViewModelV2::onRemoveDiscountClicked, + payBillViewModel::onRemoveDiscountClicked, offerData = offerDataList, isApplied = paymentAmount.isNotEmpty() && sortedOfferList.any { it.disabledData.isNull() }, isBurnDisabled = showErrorText, onViewMoreClick = { - payBillViewModelV2.onOfferRolodexClicked( + payBillViewModel.onOfferRolodexClicked( if (paymentAmount.isNotEmpty()) paymentAmount.toDouble() else null ) @@ -195,7 +194,7 @@ fun RenderNonCreditCardPayBillScreenV2( initialSource = initialSource, coinBurnData = coinBurnData, sessionAttribute = - payBillViewModelV2.getNaviBbpsSessionAttributes(), + payBillViewModel.getNaviBbpsSessionAttributes(), billCategoryEntity = billCategoryEntity, ) openSheet() @@ -215,7 +214,7 @@ fun RenderNonCreditCardPayBillScreenV2( paymentAmount.isNotEmpty() && sortedOfferList.any { it.disabledData.isNull() }, onViewMoreClick = { - payBillViewModelV2.onOfferRolodexClicked( + payBillViewModel.onOfferRolodexClicked( if (paymentAmount.isNotEmpty()) paymentAmount.toDouble() else null ) @@ -225,7 +224,7 @@ fun RenderNonCreditCardPayBillScreenV2( initialSource = initialSource, coinBurnData = coinBurnData, sessionAttribute = - payBillViewModelV2.getNaviBbpsSessionAttributes(), + payBillViewModel.getNaviBbpsSessionAttributes(), billCategoryEntity = billCategoryEntity, ) openSheet() @@ -263,11 +262,11 @@ fun RenderNonCreditCardPayBillScreenV2( billDetailsEntity = billDetailsEntity, billCategoryEntity = billCategoryEntity, ) - payBillViewModelV2.onViewOtherPlansClicked() + payBillViewModel.onViewOtherPlansClicked() }, onMoreClicked = onMoreClicked, removeDiscountAnimationOnOtherPlansClicked = - payBillViewModelV2::onRemoveDiscountClicked, + payBillViewModel::onRemoveDiscountClicked, ) } } diff --git a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/prepaidrecharge/PrepaidRechargeViewModel.kt b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/prepaidrecharge/PrepaidRechargeViewModel.kt index f65bbc07e3..4b2131f9d8 100644 --- a/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/prepaidrecharge/PrepaidRechargeViewModel.kt +++ b/android/navi-bbps/src/main/kotlin/com/navi/bbps/feature/prepaidrecharge/PrepaidRechargeViewModel.kt @@ -40,7 +40,7 @@ import com.navi.bbps.feature.contactlist.model.view.PhoneContactEntity import com.navi.bbps.feature.customerinput.model.network.BillDetailsRequest import com.navi.bbps.feature.customerinput.model.network.DeviceDetails import com.navi.bbps.feature.customerinput.model.view.BillDetailsEntity -import com.navi.bbps.feature.destinations.PayBillScreenV2Destination +import com.navi.bbps.feature.destinations.PayBillScreenDestination import com.navi.bbps.feature.mybills.MyBillsRepository import com.navi.bbps.feature.paybill.model.view.PayBillSource import com.navi.bbps.feature.prepaidrecharge.model.network.CircleItemResponse @@ -959,7 +959,7 @@ constructor( ) _navigateToNextScreen.emit( - PayBillScreenV2Destination( + PayBillScreenDestination( payBillScreenSource = payBillScreenSource, phoneNumberDetail = phoneNumberDetail, billCategoryEntity = billCategoryEntity, diff --git a/android/navi-common/src/main/java/com/navi/common/firebaseremoteconfig/FirebaseRemoteConfigHelper.kt b/android/navi-common/src/main/java/com/navi/common/firebaseremoteconfig/FirebaseRemoteConfigHelper.kt index 76feb6bf47..0fe4d29cf3 100644 --- a/android/navi-common/src/main/java/com/navi/common/firebaseremoteconfig/FirebaseRemoteConfigHelper.kt +++ b/android/navi-common/src/main/java/com/navi/common/firebaseremoteconfig/FirebaseRemoteConfigHelper.kt @@ -194,11 +194,11 @@ object FirebaseRemoteConfigHelper { "NAVI_CHECK_BALANCE_CROSS_SELL_AD_FALLBACK_TIMEOUT" const val NAVI_BBPS_PPS_CROSS_SELL_AD_RE_ID = "NAVI_BBPS_PPS_CROSS_SELL_AD_RE_ID" const val NAVI_BBPS_DISMISS_BILL_DURATION = "NAVI_BBPS_DISMISS_BILL_DURATION" - const val NAVI_BBPS_PPS_SHARE_RECEIPT_CALLOUT_TEXT = "NAVI_BBPS_PPS_SHARE_RECEIPT_CALLOUT_TEXT" const val NAVI_BBPS_TDS_SHARE_RECEIPT_CALLOUT_TEXT = "NAVI_BBPS_TDS_SHARE_RECEIPT_CALLOUT_TEXT" const val NAVI_BBPS_EXTERNAL_APP_SHARE_CONFIGURABLE_TEXT = "NAVI_BBPS_EXTERNAL_APP_SHARE_CONFIGURABLE_TEXT" + const val NAVI_BBPS_BILL_REFRESH_DURATION = "NAVI_BBPS_BILL_REFRESH_DURATION" const val NAVI_BBPS_OFFER_SHIMMER_TIMEOUT = "NAVI_BBPS_OFFER_SHIMMER_TIMEOUT"