TP-57853 | bbps saved bills cache and bills count at categories screen (#10177)
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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?> {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -35,6 +35,11 @@ class NaviBbpsDbModule {
|
||||
NaviBbpsAppDatabase.NAVI_BBPS_DATABASE_NAME
|
||||
)
|
||||
.build()
|
||||
|
||||
@ActivityRetainedScoped
|
||||
@Provides
|
||||
fun providesMyBillsDao(naviBbpsAppDatabase: NaviBbpsAppDatabase) =
|
||||
naviBbpsAppDatabase.myBillsDao()
|
||||
}
|
||||
|
||||
@Module
|
||||
|
||||
@@ -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() = ""
|
||||
|
||||
@@ -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 }
|
||||
}
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
)
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 ?: "",
|
||||
|
||||
@@ -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 & 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>
|
||||
|
||||
Reference in New Issue
Block a user