TP-57853 | bbps saved bills cache and bills count at categories screen (#10177)

This commit is contained in:
Mohit Rajput
2024-03-22 13:51:23 +05:30
committed by GitHub
parent a64aac5423
commit 4a404f035a
21 changed files with 338 additions and 159 deletions

View File

@@ -8,6 +8,7 @@
package com.navi.base.utils
import android.content.Context
import androidx.annotation.PluralsRes
import androidx.annotation.StringRes
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
@@ -15,6 +16,8 @@ import javax.inject.Inject
interface ResourceProvider {
fun getString(@StringRes resId: Int, vararg formatArg: Any): String
fun getQuantityString(@PluralsRes resId: Int, quantity: Int, vararg formatArg: Any): String
}
class ResourceProviderImpl @Inject constructor(@ApplicationContext val context: Context) :
@@ -23,4 +26,12 @@ class ResourceProviderImpl @Inject constructor(@ApplicationContext val context:
override fun getString(@StringRes resId: Int, vararg formatArg: Any): String {
return context.getString(resId, *formatArg)
}
override fun getQuantityString(
@PluralsRes resId: Int,
quantity: Int,
vararg formatArg: Any
): String {
return context.resources.getQuantityString(resId, quantity, *formatArg)
}
}

View File

@@ -9,21 +9,31 @@ package com.navi.bbps.common.repository
import com.navi.bbps.feature.customerinput.model.network.BillDetailsRequest
import com.navi.bbps.feature.customerinput.model.network.BillDetailsResponse
import com.navi.bbps.feature.mybills.MyBillsSyncJob
import com.navi.bbps.network.service.NaviBbpsRetrofitService
import com.navi.common.network.models.RepoResult
import com.navi.common.network.models.isSuccessWithData
import com.navi.common.network.retrofit.ResponseCallback
import javax.inject.Inject
class BbpsCommonRepository
@Inject
constructor(private val naviBbpsRetrofitService: NaviBbpsRetrofitService) : ResponseCallback() {
constructor(
private val naviBbpsRetrofitService: NaviBbpsRetrofitService,
private val myBillsSyncJob: MyBillsSyncJob
) : ResponseCallback() {
suspend fun fetchBillDetails(
billDetailsRequest: BillDetailsRequest
): RepoResult<BillDetailsResponse> {
return apiResponseCallback(
naviBbpsRetrofitService.getBillDetails(billDetailsRequest = billDetailsRequest)
)
val response =
apiResponseCallback(
naviBbpsRetrofitService.getBillDetails(billDetailsRequest = billDetailsRequest)
)
if (response.isSuccessWithData()) {
myBillsSyncJob.refreshBillsAsync()
}
return response
}
suspend fun getConfig(commaSeparatedConfigKeys: String): RepoResult<Any?> {

View File

@@ -10,18 +10,19 @@ package com.navi.bbps.db
import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import com.navi.bbps.db.converter.BillCategoryTypeConverter
import com.navi.bbps.feature.category.model.view.BillCategoryEntity
import com.navi.bbps.feature.category.model.view.BillCategoryGroupEntity
import com.navi.bbps.db.converter.PaymentAmountExactnessConverter
import com.navi.bbps.db.converter.StringMapConverter
import com.navi.bbps.feature.mybills.MyBillsDao
import com.navi.bbps.feature.mybills.model.view.MyBillEntity
@Database(
entities = [BillCategoryGroupEntity::class, BillCategoryEntity::class],
version = 1,
exportSchema = false
)
@TypeConverters(BillCategoryTypeConverter::class)
@Database(entities = [MyBillEntity::class], version = 1, exportSchema = false)
@TypeConverters(PaymentAmountExactnessConverter::class, StringMapConverter::class)
abstract class NaviBbpsAppDatabase : RoomDatabase() {
abstract fun myBillsDao(): MyBillsDao
companion object {
const val NAVI_BBPS_DATABASE_NAME = "navi-bbps.db"
const val NAVI_BBPS_TABLE_MY_SAVED_BILLS = "my_saved_bills"
}
}

View File

@@ -1,25 +0,0 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.bbps.db.converter
import androidx.room.TypeConverter
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import com.navi.bbps.feature.category.model.view.BillCategoryEntity
class BillCategoryTypeConverter {
private val gson = Gson()
@TypeConverter
fun toBillCategoryList(json: String): List<BillCategoryEntity> {
val type = object : TypeToken<List<BillCategoryEntity>>() {}.type
return gson.fromJson(json, type)
}
@TypeConverter fun fromBillCategoryList(list: List<BillCategoryEntity>) = gson.toJson(list)
}

View File

@@ -0,0 +1,23 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.bbps.db.converter
import androidx.room.TypeConverter
import com.navi.bbps.feature.paybill.model.network.PaymentAmountExactness
class PaymentAmountExactnessConverter {
@TypeConverter
fun fromPaymentAmountExactness(paymentAmountExactness: PaymentAmountExactness) =
paymentAmountExactness.name
@TypeConverter
fun toPaymentAmountExactness(paymentAmountExactnessName: String): PaymentAmountExactness {
return PaymentAmountExactness.entries.singleOrNull { it.name == paymentAmountExactnessName }
?: PaymentAmountExactness.EXACT
}
}

View File

@@ -0,0 +1,23 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.bbps.db.converter
import androidx.room.TypeConverter
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import live.hms.video.utils.toJson
class StringMapConverter {
private val gson = Gson()
private val type = object : TypeToken<Map<String?, String?>?>() {}.type
@TypeConverter fun fromMap(map: Map<String, String>) = map.toJson()
@TypeConverter fun toMap(mapJson: String): Map<String, String> = gson.fromJson(mapJson, type)
}

View File

@@ -35,6 +35,11 @@ class NaviBbpsDbModule {
NaviBbpsAppDatabase.NAVI_BBPS_DATABASE_NAME
)
.build()
@ActivityRetainedScoped
@Provides
fun providesMyBillsDao(naviBbpsAppDatabase: NaviBbpsAppDatabase) =
naviBbpsAppDatabase.myBillsDao()
}
@Module

View File

@@ -12,6 +12,7 @@ import com.navi.bbps.common.NaviBbpsScreen
import com.navi.bbps.common.model.NaviBbpsVmData
import com.navi.bbps.common.usecase.BbpsRefreshConfigUseCase
import com.navi.bbps.common.viewmodel.NaviBbpsBaseVM
import com.navi.bbps.feature.mybills.MyBillsSyncJob
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
@@ -19,8 +20,10 @@ import kotlinx.coroutines.Dispatchers
@HiltViewModel
class NaviBbpsMainViewModel
@Inject
constructor(private val bbpsRefreshConfigUseCase: BbpsRefreshConfigUseCase) :
NaviBbpsBaseVM(naviBbpsVmData = NaviBbpsVmData(screen = NaviBbpsScreen.NAVI_BBPS_MAIN)) {
constructor(
private val bbpsRefreshConfigUseCase: BbpsRefreshConfigUseCase,
private val myBillsSyncJob: MyBillsSyncJob
) : NaviBbpsBaseVM(naviBbpsVmData = NaviBbpsVmData(screen = NaviBbpsScreen.NAVI_BBPS_MAIN)) {
init {
refreshConfig()
@@ -28,6 +31,7 @@ constructor(private val bbpsRefreshConfigUseCase: BbpsRefreshConfigUseCase) :
private fun refreshConfig() {
viewModelScope.safeLaunch(Dispatchers.IO) { bbpsRefreshConfigUseCase.execute() }
myBillsSyncJob.refreshBillsAsync()
}
fun getDefaultScreenName() = ""

View File

@@ -18,11 +18,9 @@ import com.navi.bbps.common.DATE_TIME_FORMAT_DATE_MONTH_NAME_YEAR
import com.navi.bbps.common.DATE_TIME_FORMAT_DATE_MONTH_NAME_YEAR_COMMA_TIME
import com.navi.bbps.common.DATE_TIME_FORMAT_DATE_MONTH_NAME_YEAR_INPUT
import com.navi.bbps.common.DISPLAYABLE_MOBILE_NUMBER_KEY
import com.navi.bbps.common.KEY_BBPS_MY_BILLS
import com.navi.bbps.common.NaviBbpsScreen
import com.navi.bbps.common.model.NaviBbpsVmData
import com.navi.bbps.common.repository.BbpsCommonRepository
import com.navi.bbps.common.utils.BbpsSessionCache
import com.navi.bbps.common.utils.NaviBbpsDateUtils
import com.navi.bbps.common.utils.getDisplayableAmount
import com.navi.bbps.common.viewmodel.NaviBbpsBaseVM
@@ -42,7 +40,7 @@ import com.navi.bbps.feature.customerinput.model.view.BillDetailsEntity
import com.navi.bbps.feature.customerinput.model.view.BillerDetailsEntity
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
import com.navi.bbps.feature.mybills.model.view.MyBillEntity
import com.navi.bbps.feature.paybill.model.view.PayBillSource
import com.navi.common.di.CoroutineDispatcherProvider
@@ -70,12 +68,11 @@ constructor(
private val dispatcherProvider: CoroutineDispatcherProvider,
private val billHistoryDetailsRepository: BillHistoryDetailsRepository,
private val bbpsCommonRepository: BbpsCommonRepository,
private val bbpsSessionCache: BbpsSessionCache,
private val naviNetworkConnectivity: NaviNetworkConnectivity,
private val myBillsRepository: MyBillsRepository,
private val naviBbpsDateUtils: NaviBbpsDateUtils,
private val contactManager: PhoneContactManager,
private val stringResouceProvider: ResourceProvider
private val stringResouceProvider: ResourceProvider,
private val myBillsSyncJob: MyBillsSyncJob
) :
NaviBbpsBaseVM(
naviBbpsVmData = NaviBbpsVmData(screen = NaviBbpsScreen.NAVI_BBPS_MY_BILL_HISTORY_DETAILS)
@@ -241,7 +238,7 @@ constructor(
updateDeleteBillInProgressState(isInProgress = true)
val deleteBillResponse = billHistoryDetailsRepository.deleteBill(myBillEntity.billId)
if (deleteBillResponse.isSuccessWithData()) {
refreshMyBills()
myBillsSyncJob.refreshBills()
updateDeleteBillInProgressState(isInProgress = false)
_goBackWithBillsRefresh.emit(true)
} else {
@@ -251,14 +248,6 @@ constructor(
}
}
private suspend fun refreshMyBills() {
val myBillsResponse = myBillsRepository.fetchMySavedBills()
if (myBillsResponse.isSuccessWithData()) {
val bills = myBillsResponse.data?.bills.orEmpty()
bbpsSessionCache.save(key = KEY_BBPS_MY_BILLS, value = bills)
}
}
private fun updateNewBillFetchingState(isInProgress: Boolean) {
_isFetchingNewBill.update { isInProgress }
}

View File

@@ -12,17 +12,15 @@ import com.google.gson.Gson
import com.navi.base.cache.model.NaviCacheAltSourceEntity
import com.navi.base.cache.repository.NaviCacheRepository
import com.navi.bbps.R
import com.navi.bbps.common.KEY_BBPS_MY_BILLS
import com.navi.bbps.common.NaviBbpsScreen
import com.navi.bbps.common.model.NaviBbpsVmData
import com.navi.bbps.common.utils.BbpsSessionCache
import com.navi.bbps.common.viewmodel.NaviBbpsBaseVM
import com.navi.bbps.feature.category.model.network.BillCategoriesResponse
import com.navi.bbps.feature.category.model.view.BillCategoryEntity
import com.navi.bbps.feature.category.model.view.BillCategoryGroupEntity
import com.navi.bbps.feature.category.model.view.BillCategoryState
import com.navi.bbps.feature.mybills.MyBillsRepository
import com.navi.bbps.feature.mybills.model.network.SavedBillItem
import com.navi.bbps.feature.mybills.MyBillsSyncJob
import com.navi.bbps.network.di.NaviBbpsGsonBuilder
import com.navi.common.constants.DBCacheConstants
import com.navi.common.di.CoroutineDispatcherProvider
@@ -33,6 +31,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
@HiltViewModel
@@ -40,10 +39,10 @@ class BillCategoriesViewModel
@Inject
constructor(
private val dispatcherProvider: CoroutineDispatcherProvider,
private val bbpsSessionCache: BbpsSessionCache,
private val billCategoriesRepository: BillCategoriesRepository,
private val myBillsRepository: MyBillsRepository,
private val naviCacheRepository: NaviCacheRepository,
private val myBillsSyncJob: MyBillsSyncJob,
private val myBillsRepository: MyBillsRepository,
@NaviBbpsGsonBuilder private val naviBbpsGson: Gson
) :
NaviBbpsBaseVM(
@@ -53,8 +52,33 @@ constructor(
MutableStateFlow<BillCategoryState>(BillCategoryState.Loading)
val billCategoriesState = _billCategoriesState.asStateFlow()
private val _accountsCountText = MutableStateFlow("")
val accountsCountText = _accountsCountText.asStateFlow()
init {
observeMyBillCounts()
fetchBillCategories()
fetchMyBills()
}
private fun observeMyBillCounts() {
viewModelScope.launch(dispatcherProvider.io) {
myBillsRepository.fetchMySavedBills().collect { myBills ->
_accountsCountText.update {
if (myBills.isEmpty()) {
""
} else {
resourceProvider
.get()
.getQuantityString(
resId = R.plurals.bbps_x_accounts,
quantity = myBills.size,
myBills.size
)
}
}
}
}
}
private fun updateBillCategoriesState(state: BillCategoryState) {
@@ -112,20 +136,8 @@ constructor(
)
}
fun fetchMyBills() {
viewModelScope.launch(dispatcherProvider.io) {
val bills = bbpsSessionCache.get(KEY_BBPS_MY_BILLS) as List<SavedBillItem>?
if (bills != null) {
return@launch
}
val myBillsResponse = myBillsRepository.fetchMySavedBills()
if (myBillsResponse.isSuccessWithData()) {
myBillsResponse.data?.bills?.let {
bbpsSessionCache.save(key = KEY_BBPS_MY_BILLS, value = it)
}
}
}
private fun fetchMyBills() {
myBillsSyncJob.refreshBillsAsync()
}
private fun mapBillCategoriesResponseToEntities(

View File

@@ -52,12 +52,10 @@ fun BillCategoriesScreen(
naviBbpsAnalytics: NaviBbpsAnalytics.BillCategories =
NaviBbpsAnalytics.INSTANCE.BillCategories()
) {
LaunchedEffect(Unit) {
naviBbpsAnalytics.onLanded(source = source)
viewModel.fetchMyBills()
}
LaunchedEffect(Unit) { naviBbpsAnalytics.onLanded(source = source) }
val billCategoryState by viewModel.billCategoriesState.collectAsStateWithLifecycle()
val accountsCountText by viewModel.accountsCountText.collectAsStateWithLifecycle()
val onManageMyBillsClicked = {
navigator.navigate(MyBillsScreenDestination(isRootScreen = false))
@@ -112,6 +110,7 @@ fun BillCategoriesScreen(
RenderBillCategoriesScreen(
billCategoryStateLoaded = billCategoryState as BillCategoryState.Loaded,
accountsCountText = accountsCountText,
onCategorySelected = onCategorySelected,
onManageMyBillsClicked = onManageMyBillsClicked
)

View File

@@ -18,6 +18,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
@@ -47,10 +48,14 @@ typealias CategoryGroupId = String
@Composable
fun RenderBillCategoriesScreen(
billCategoryStateLoaded: BillCategoryState.Loaded,
accountsCountText: String,
onManageMyBillsClicked: () -> Unit,
onCategorySelected: (CategoryGroupId, BillCategoryEntity) -> Unit
) {
ManageMyBillCard(onManageMyBillsClicked = onManageMyBillsClicked)
ManageMyBillCard(
accountsCountText = accountsCountText,
onManageMyBillsClicked = onManageMyBillsClicked
)
billCategoryStateLoaded.billCategories.forEach {
BillCategoryGroupItem(billCategoryGroupEntity = it, onCategorySelected = onCategorySelected)
@@ -59,7 +64,7 @@ fun RenderBillCategoriesScreen(
}
@Composable
private fun ManageMyBillCard(onManageMyBillsClicked: () -> Unit) {
private fun ManageMyBillCard(accountsCountText: String, onManageMyBillsClicked: () -> Unit) {
Spacer(modifier = Modifier.height(16.dp))
NaviBbpsCard(
modifier = Modifier.fillMaxWidth().clickableDebounce { onManageMyBillsClicked() },
@@ -79,6 +84,18 @@ private fun ManageMyBillCard(onManageMyBillsClicked: () -> Unit) {
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_DEMI_BOLD),
overflow = TextOverflow.Ellipsis
)
Spacer(modifier = Modifier.width(8.dp))
if (accountsCountText.isNotBlank()) {
Text(
text = accountsCountText,
color = NaviBbpsColor.textSecondary,
fontFamily = ttComposeFontFamily,
textAlign = TextAlign.Center,
fontSize = 12.sp,
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_REGULAR),
overflow = TextOverflow.Ellipsis
)
}
Spacer(modifier = Modifier.weight(1f))
Image(
painter = painterResource(id = R.drawable.ic_bbps_chevron_circle_bg),

View File

@@ -0,0 +1,37 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.bbps.feature.mybills
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Transaction
import com.navi.bbps.db.NaviBbpsAppDatabase.Companion.NAVI_BBPS_TABLE_MY_SAVED_BILLS
import com.navi.bbps.feature.mybills.model.view.MyBillEntity
import kotlinx.coroutines.flow.Flow
@Dao
interface MyBillsDao {
@Query("SELECT * FROM $NAVI_BBPS_TABLE_MY_SAVED_BILLS")
fun getAllBills(): Flow<List<MyBillEntity>>
@Query("SELECT * FROM $NAVI_BBPS_TABLE_MY_SAVED_BILLS WHERE categoryId == :categoryId")
fun getBillsByCategory(categoryId: String): Flow<List<MyBillEntity>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertAll(myBills: List<MyBillEntity>)
@Query("DELETE FROM $NAVI_BBPS_TABLE_MY_SAVED_BILLS") suspend fun deleteAll()
@Transaction
suspend fun refresh(myBills: List<MyBillEntity>) {
deleteAll()
insertAll(myBills = myBills)
}
}

View File

@@ -15,8 +15,13 @@ import javax.inject.Inject
class MyBillsRepository
@Inject
constructor(private val naviBbpsRetrofitService: NaviBbpsRetrofitService) : ResponseCallback() {
suspend fun fetchMySavedBills(): RepoResult<MyBillsResponse> {
constructor(
private val naviBbpsRetrofitService: NaviBbpsRetrofitService,
private val myBillsDao: MyBillsDao
) : ResponseCallback() {
suspend fun fetchBillsFromNetwork(): RepoResult<MyBillsResponse> {
return apiResponseCallback(naviBbpsRetrofitService.getMySavedBills())
}
fun fetchMySavedBills() = myBillsDao.getAllBills()
}

View File

@@ -0,0 +1,65 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.bbps.feature.mybills
import com.navi.base.utils.ResourceProvider
import com.navi.bbps.R
import com.navi.bbps.common.DATE_TIME_FORMAT_DATE_MONTH_NAME_YEAR
import com.navi.bbps.common.utils.NaviBbpsDateUtils
import com.navi.bbps.common.utils.getDisplayableAmount
import com.navi.bbps.feature.mybills.model.network.SavedBillItem
import com.navi.bbps.feature.mybills.model.view.MyBillEntity
import com.navi.bbps.feature.paybill.model.network.PaymentAmountExactness
import javax.inject.Inject
class MyBillsResponseToEntityMapper
@Inject
constructor(
private val naviBbpsDateUtils: NaviBbpsDateUtils,
private val resourceProvider: ResourceProvider
) {
fun map(billsListResponse: List<SavedBillItem>): List<MyBillEntity> {
return billsListResponse.map {
val formattedLastPaidDate =
if (it.lastPaidOn.isNullOrBlank()) ""
else
naviBbpsDateUtils.getFormattedDateFromEpoch(
epochTime = it.lastPaidOn,
outputFormat = DATE_TIME_FORMAT_DATE_MONTH_NAME_YEAR
)
val lastPaidAmount = it.lastPaidAmount?.getDisplayableAmount() ?: ""
MyBillEntity(
billId = it.billId,
billerName = it.billerName,
billerId = it.billerId,
isAdhoc = it.isAdhoc,
billerLogoUrl = it.billerLogoUrl ?: "",
fetchOption = it.fetchOption ?: "",
paymentAmountExactness =
PaymentAmountExactness.toPaymentAmountExactnessEnum(
paymentAmountExactness = it.paymentAmountExactness
),
status = it.status ?: "",
formattedLastPaidDate = formattedLastPaidDate,
epochLastPaidOn = it.lastPaidOn.orEmpty(),
actualLastPaidAmount = it.lastPaidAmount.orEmpty(),
formattedLastPaidAmount = lastPaidAmount,
primaryCustomerParamValue = it.primaryCustomerParam,
categoryId = it.categoryId,
categoryName = it.categoryName,
customerParams = it.customerParams,
isBillPaid = lastPaidAmount.isNotBlank() && formattedLastPaidDate.isNotBlank(),
nextActionCtaText =
it.nextActionCtaText.orEmpty().ifBlank {
resourceProvider.getString(R.string.bbps_pay_now)
}
)
}
}
}

View File

@@ -0,0 +1,52 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.bbps.feature.mybills
import com.navi.common.di.CoroutineDispatcherProvider
import com.navi.common.network.models.isSuccessWithData
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
class MyBillsSyncJob
@Inject
constructor(
private val dispatcherProvider: CoroutineDispatcherProvider,
private val myBillsRepository: MyBillsRepository,
private val myBillsDao: MyBillsDao,
private val myBillsResponseToEntityMapper: MyBillsResponseToEntityMapper
) {
private var syncJob: Job? = null
suspend fun refreshBills() {
val response = myBillsRepository.fetchBillsFromNetwork()
if (response.isSuccessWithData()) {
val myBills =
myBillsResponseToEntityMapper.map(
billsListResponse = response.data?.bills.orEmpty()
)
if (myBills.isNotEmpty()) {
myBillsDao.refresh(myBills)
}
}
}
fun refreshBillsAsync(): Job? {
if (syncJob?.isActive == true) {
try {
syncJob?.cancel()
} catch (e: Exception) {
syncJob = null
// Unable to cancel the job
}
}
syncJob = CoroutineScope(dispatcherProvider.io).launch { refreshBills() }
return syncJob
}
}

View File

@@ -8,25 +8,14 @@
package com.navi.bbps.feature.mybills
import androidx.lifecycle.viewModelScope
import com.navi.bbps.R
import com.navi.bbps.common.DATE_TIME_FORMAT_DATE_MONTH_NAME_YEAR
import com.navi.bbps.common.KEY_BBPS_MY_BILLS
import com.navi.bbps.common.NaviBbpsAnalytics
import com.navi.bbps.common.NaviBbpsScreen
import com.navi.bbps.common.model.NaviBbpsVmData
import com.navi.bbps.common.utils.BbpsSessionCache
import com.navi.bbps.common.utils.NaviBbpsDateUtils
import com.navi.bbps.common.utils.getDisplayableAmount
import com.navi.bbps.common.viewmodel.NaviBbpsBaseVM
import com.navi.bbps.feature.mybills.model.network.SavedBillItem
import com.navi.bbps.feature.mybills.model.view.MyBillEntity
import com.navi.bbps.feature.mybills.model.view.MyBillsState
import com.navi.bbps.feature.paybill.model.network.PaymentAmountExactness.Companion.toPaymentAmountExactnessEnum
import com.navi.common.di.CoroutineDispatcherProvider
import com.navi.common.network.models.isSuccessWithData
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
@@ -36,9 +25,7 @@ class MyBillsViewModel
@Inject
constructor(
private val dispatcherProvider: CoroutineDispatcherProvider,
private val bbpsSessionCache: BbpsSessionCache,
private val myBillsRepository: MyBillsRepository,
private val naviBbpsDateUtils: NaviBbpsDateUtils
private val myBillsRepository: MyBillsRepository
) :
NaviBbpsBaseVM(
naviBbpsVmData = NaviBbpsVmData(screen = NaviBbpsScreen.NAVI_BBPS_MY_SAVED_BILLS)
@@ -60,65 +47,15 @@ constructor(
fun fetchMySavedBills() {
viewModelScope.launch(dispatcherProvider.io) {
updateMyBillsState(MyBillsState.Loading)
val myBillsResponse = myBillsRepository.fetchMySavedBills()
naviBbpsAnalytics.onMyBillsLoaded(myBillsResponse.data?.bills?.size ?: 0)
delay(300) // to avoid jerky loading
if (myBillsResponse.isSuccessWithData()) {
val billsListResponse = myBillsResponse.data!!.bills.orEmpty()
if (billsListResponse.isEmpty()) {
myBillsRepository.fetchMySavedBills().collect { myBillEntities ->
naviBbpsAnalytics.onMyBillsLoaded(totalBills = myBillEntities.size)
if (myBillEntities.isEmpty()) {
updateMyBillsState(MyBillsState.Empty)
} else {
updateMyBillsState(
MyBillsState.Loaded(
myBills = mapMyBillsResponseToEntities(billsListResponse)
)
)
updateMyBillsState(MyBillsState.Loaded(myBills = myBillEntities))
}
bbpsSessionCache.save(key = KEY_BBPS_MY_BILLS, value = billsListResponse)
} else {
updateMyBillsState(MyBillsState.Error)
notifyError(myBillsResponse)
}
}
}
private fun mapMyBillsResponseToEntities(
billsListResponse: List<SavedBillItem>
): List<MyBillEntity> {
return billsListResponse.map {
val formattedLastPaidDate =
if (it.lastPaidOn.isNullOrBlank()) ""
else
naviBbpsDateUtils.getFormattedDateFromEpoch(
epochTime = it.lastPaidOn,
outputFormat = DATE_TIME_FORMAT_DATE_MONTH_NAME_YEAR
)
val lastPaidAmount = it.lastPaidAmount?.getDisplayableAmount() ?: ""
MyBillEntity(
billId = it.billId,
billerName = it.billerName,
billerId = it.billerId,
isAdhoc = it.isAdhoc,
billerLogoUrl = it.billerLogoUrl ?: "",
fetchOption = it.fetchOption ?: "",
paymentAmountExactness =
toPaymentAmountExactnessEnum(
paymentAmountExactness = it.paymentAmountExactness
),
status = it.status ?: "",
formattedLastPaidDate = formattedLastPaidDate,
lastPaidAmount = lastPaidAmount,
primaryCustomerParamValue = it.primaryCustomerParam,
categoryId = it.categoryId,
categoryName = it.categoryName,
customerParams = it.customerParams,
isBillPaid = lastPaidAmount.isNotBlank() && formattedLastPaidDate.isNotBlank(),
nextActionCtaText =
it.nextActionCtaText.orEmpty().ifBlank {
resourceProvider.get().getString(R.string.bbps_pay_now)
}
)
}
}
}

View File

@@ -8,25 +8,37 @@
package com.navi.bbps.feature.mybills.model.view
import android.os.Parcelable
import androidx.annotation.Keep
import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.TypeConverters
import com.navi.bbps.db.NaviBbpsAppDatabase
import com.navi.bbps.db.converter.PaymentAmountExactnessConverter
import com.navi.bbps.db.converter.StringMapConverter
import com.navi.bbps.feature.paybill.model.network.PaymentAmountExactness
import kotlinx.parcelize.Parcelize
@Keep
@Entity(tableName = NaviBbpsAppDatabase.NAVI_BBPS_TABLE_MY_SAVED_BILLS)
@Parcelize
data class MyBillEntity(
val billId: String,
@PrimaryKey val billId: String,
val billerId: String,
val billerName: String,
val billerLogoUrl: String,
val isAdhoc: Boolean,
val isBillPaid: Boolean,
@TypeConverters(PaymentAmountExactnessConverter::class)
val paymentAmountExactness: PaymentAmountExactness,
val fetchOption: String,
val status: String,
val formattedLastPaidDate: String,
val lastPaidAmount: String,
val epochLastPaidOn: String,
val formattedLastPaidAmount: String,
val actualLastPaidAmount: String,
val primaryCustomerParamValue: String,
val categoryId: String,
val categoryName: String,
val customerParams: Map<String, String>,
@TypeConverters(StringMapConverter::class) val customerParams: Map<String, String>,
val nextActionCtaText: String
) : Parcelable

View File

@@ -120,7 +120,7 @@ private fun MyBillItem(myBillEntity: MyBillEntity, onBillItemClicked: () -> Unit
text =
stringResource(
id = R.string.bbps_last_paid_x_amount,
myBillEntity.lastPaidAmount,
myBillEntity.formattedLastPaidAmount,
),
color = NaviBbpsColor.textSecondary,
fontFamily = ttComposeFontFamily,

View File

@@ -491,7 +491,9 @@ constructor(
fetchOption = "",
status = status,
formattedLastPaidDate = formattedTimestamp,
lastPaidAmount = paymentAmount.value,
epochLastPaidOn = "",
actualLastPaidAmount = paymentAmount.value,
formattedLastPaidAmount = paymentAmount.value.getDisplayableAmount(),
primaryCustomerParamValue = "",
categoryId = billCategoryEntity?.categoryId ?: "",
categoryName = billCategoryEntity?.title ?: "",

View File

@@ -9,9 +9,9 @@
<string name="bbps_generic_error_description">Unfortunately we have encountered a technical issue. Please try again after some time.</string>
<string name="bbps_title_bills_and_recharges">Bills &amp; Recharges</string>
<string name="bbps_manage_my_bills">Manage my bills</string>
<plurals name="bbps_x_bills">
<item quantity="one">%d bill</item>
<item quantity="other">%d bills</item>
<plurals name="bbps_x_accounts">
<item quantity="one">(%d account)</item>
<item quantity="other">(%d accounts)</item>
</plurals>
<string name="bbps_last_paid_on">Last paid on</string>
<string name="bbps_last_paid_x_amount_on_y_date">Last paid ₹%s on %s</string>