TP-59832 | Mehul | Biller list caching E2E (#10255)
This commit is contained in:
@@ -16,9 +16,12 @@ internal const val BBPS_CATEGORY_TITLE = "BBPS_CATEGORY_TITLE"
|
||||
internal const val BBPS_CATEGORY_ICON_URL = "BBPS_CATEGORY_ICON_URL"
|
||||
internal const val BBPS_BILLER_SEARCH_BOX_PLACEHOLDER_TEXT =
|
||||
"BBPS_BILLER_SEARCH_BOX_PLACEHOLDER_TEXT"
|
||||
internal const val BBPS_BILLER_LIST_STATE_HEADING = "BBPS_BILLER_LIST_STATE_HEADING"
|
||||
internal const val BBPS_BILLER_LIST_ALL_HEADING = "BBPS_BILLER_LIST_ALL_HEADING"
|
||||
|
||||
// Values
|
||||
const val CONFIG_IN_DB_REFRESH_MIN_TIMESTAMP = 86400000L // 1 day
|
||||
const val BILLER_LIST_REFRESH_TIMESTAMP = 86400000L // 1 day
|
||||
|
||||
// Date time format
|
||||
const val DATE_TIME_FORMAT_DATE_MONTH_NAME_YEAR = "dd MMM yyyy"
|
||||
@@ -61,6 +64,8 @@ const val ICON_FASTAG_BOTTOMSHEET =
|
||||
|
||||
// Preference Keys
|
||||
const val KEY_CONFIG_DB_LAST_REFRESHED_TIMESTAMP = "KEY_CONFIG_DB_LAST_REFRESHED_TIMESTAMP"
|
||||
const val KEY_BBPS_MY_BILLS_DB_LAST_REFRESHED_TIMESTAMP =
|
||||
"KEY_BBPS_MY_BILLS_DB_LAST_REFRESHED_TIMESTAMP"
|
||||
|
||||
// Fixed values
|
||||
const val NOTES_REGEX_CONDITION = "^[a-zA-Z0-9 \\-]*\$"
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2024 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.bbps.common.model
|
||||
|
||||
import com.navi.bbps.common.usecase.BbpsRefreshConfigUseCase
|
||||
import com.navi.bbps.common.usecase.BillerListUseCase
|
||||
import com.navi.bbps.feature.mybills.MyBillsSyncJob
|
||||
import com.navi.common.di.CoroutineDispatcherProvider
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Deferred
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class NaviBbpsManager
|
||||
@Inject
|
||||
constructor(
|
||||
private val dispatcherProvider: CoroutineDispatcherProvider,
|
||||
private val billerListUseCase: BillerListUseCase,
|
||||
private val bbpsRefreshConfigUseCase: BbpsRefreshConfigUseCase,
|
||||
private val myBillsSyncJob: MyBillsSyncJob,
|
||||
) {
|
||||
private var isInitInProgress = false
|
||||
|
||||
fun init() {
|
||||
CoroutineScope(dispatcherProvider.io).launch {
|
||||
if (isInitInProgress) {
|
||||
return@launch
|
||||
}
|
||||
|
||||
isInitInProgress = true
|
||||
|
||||
myBillsSyncJob.refreshBillsAsync()
|
||||
|
||||
val taskList = mutableListOf<Deferred<Unit>>()
|
||||
taskList.add(async { myBillsSyncJob.refreshBills() })
|
||||
taskList.add(async { bbpsRefreshConfigUseCase.execute() })
|
||||
taskList.add(async { billerListUseCase.execute() })
|
||||
|
||||
taskList.awaitAll()
|
||||
|
||||
isInitInProgress = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,6 +7,9 @@
|
||||
|
||||
package com.navi.bbps.common.repository
|
||||
|
||||
import com.navi.bbps.feature.billerlist.dao.BillerListDao
|
||||
import com.navi.bbps.feature.billerlist.model.network.BillerItemListResponse
|
||||
import com.navi.bbps.feature.billerlist.model.view.BillerItemEntity
|
||||
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
|
||||
@@ -20,7 +23,8 @@ class BbpsCommonRepository
|
||||
@Inject
|
||||
constructor(
|
||||
private val naviBbpsRetrofitService: NaviBbpsRetrofitService,
|
||||
private val myBillsSyncJob: MyBillsSyncJob
|
||||
private val myBillsSyncJob: MyBillsSyncJob,
|
||||
private val billerListDao: BillerListDao
|
||||
) : ResponseCallback() {
|
||||
|
||||
suspend fun fetchBillDetails(
|
||||
@@ -41,4 +45,20 @@ constructor(
|
||||
naviBbpsRetrofitService.getConfig(commaSeparatedConfigKeys = commaSeparatedConfigKeys)
|
||||
)
|
||||
}
|
||||
|
||||
suspend fun fetchAllBillers(): RepoResult<BillerItemListResponse> {
|
||||
return apiResponseCallback(naviBbpsRetrofitService.getFullBillerList())
|
||||
}
|
||||
|
||||
suspend fun getBillersFromLocalDb(): List<BillerItemEntity> {
|
||||
return billerListDao.getAllBillers()
|
||||
}
|
||||
|
||||
suspend fun refreshBillers(billers: List<BillerItemEntity>) {
|
||||
billerListDao.refreshBillers(billers)
|
||||
}
|
||||
|
||||
suspend fun getBillersByCategoryFromLocalDb(categoryId: String): List<BillerItemEntity> {
|
||||
return billerListDao.getBillersByCategory(categoryId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2024 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.bbps.common.usecase
|
||||
|
||||
import com.navi.bbps.common.BILLER_LIST_REFRESH_TIMESTAMP
|
||||
import com.navi.bbps.common.BbpsSharedPreferences
|
||||
import com.navi.bbps.common.KEY_BBPS_MY_BILLS_DB_LAST_REFRESHED_TIMESTAMP
|
||||
import com.navi.bbps.common.repository.BbpsCommonRepository
|
||||
import com.navi.bbps.feature.billerlist.BillerItemResponseToEntityMapper
|
||||
import com.navi.bbps.feature.billerlist.model.view.BillerItemEntity
|
||||
import javax.inject.Inject
|
||||
import org.joda.time.DateTime
|
||||
|
||||
class BillerListUseCase
|
||||
@Inject
|
||||
constructor(
|
||||
private val bbpsCommonRepository: BbpsCommonRepository,
|
||||
private val bbpsSharedPreferences: BbpsSharedPreferences,
|
||||
private val billerItemResponseToEntityMapper: BillerItemResponseToEntityMapper
|
||||
) {
|
||||
suspend fun execute() {
|
||||
val existingBillers = bbpsCommonRepository.getBillersFromLocalDb()
|
||||
|
||||
val lastFetchBillerListTimeStamp =
|
||||
bbpsSharedPreferences.getLong(
|
||||
key = KEY_BBPS_MY_BILLS_DB_LAST_REFRESHED_TIMESTAMP,
|
||||
defValue = -1L
|
||||
)
|
||||
|
||||
val shouldFetchBillerList =
|
||||
existingBillers.isEmpty() ||
|
||||
(DateTime.now().millis - lastFetchBillerListTimeStamp >
|
||||
BILLER_LIST_REFRESH_TIMESTAMP)
|
||||
|
||||
if (shouldFetchBillerList) {
|
||||
val billerListResponse = bbpsCommonRepository.fetchAllBillers()
|
||||
|
||||
val billers = billerListResponse.data?.billers.orEmpty()
|
||||
|
||||
if (billers.isNullOrEmpty()) {
|
||||
return
|
||||
}
|
||||
|
||||
refreshBillers(
|
||||
billers =
|
||||
billerItemResponseToEntityMapper.mapBillerItemResponseToEntityList(
|
||||
billers = billers
|
||||
)
|
||||
)
|
||||
|
||||
bbpsSharedPreferences.saveLong(
|
||||
key = KEY_BBPS_MY_BILLS_DB_LAST_REFRESHED_TIMESTAMP,
|
||||
value = System.currentTimeMillis()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun refreshBillers(billers: List<BillerItemEntity>) {
|
||||
bbpsCommonRepository.refreshBillers(billers)
|
||||
}
|
||||
}
|
||||
@@ -10,19 +10,48 @@ package com.navi.bbps.db
|
||||
import androidx.room.Database
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import com.navi.bbps.db.NaviBbpsAppDatabase.Companion.NAVI_BBPS_DATABASE_BILLERS
|
||||
import com.navi.bbps.db.converter.PaymentAmountExactnessConverter
|
||||
import com.navi.bbps.db.converter.StringMapConverter
|
||||
import com.navi.bbps.feature.billerlist.dao.BillerListDao
|
||||
import com.navi.bbps.feature.billerlist.model.view.BillerItemEntity
|
||||
import com.navi.bbps.feature.mybills.MyBillsDao
|
||||
import com.navi.bbps.feature.mybills.model.view.MyBillEntity
|
||||
|
||||
@Database(entities = [MyBillEntity::class], version = 1, exportSchema = false)
|
||||
@Database(
|
||||
entities = [MyBillEntity::class, BillerItemEntity::class],
|
||||
version = 2,
|
||||
exportSchema = false
|
||||
)
|
||||
@TypeConverters(PaymentAmountExactnessConverter::class, StringMapConverter::class)
|
||||
abstract class NaviBbpsAppDatabase : RoomDatabase() {
|
||||
|
||||
abstract fun myBillsDao(): MyBillsDao
|
||||
|
||||
abstract fun billerListDao(): BillerListDao
|
||||
|
||||
companion object {
|
||||
const val NAVI_BBPS_DATABASE_NAME = "navi-bbps.db"
|
||||
const val NAVI_BBPS_TABLE_MY_SAVED_BILLS = "my_saved_bills"
|
||||
const val NAVI_BBPS_DATABASE_BILLERS = "billers"
|
||||
}
|
||||
}
|
||||
|
||||
val NAVI_BBPS_DATABASE_MIGRATION_1_2 =
|
||||
object : Migration(1, 2) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
// Create the new table for BillerItemEntity
|
||||
database.execSQL(
|
||||
"CREATE TABLE IF NOT EXISTS $NAVI_BBPS_DATABASE_BILLERS (" +
|
||||
"`billerId` TEXT NOT NULL PRIMARY KEY, " +
|
||||
"`billerName` TEXT NOT NULL, " +
|
||||
"`billerLogoUrl` TEXT, " +
|
||||
"`status` TEXT NOT NULL, " +
|
||||
"`isAdhoc` INTEGER NOT NULL, " +
|
||||
"`state` TEXT NOT NULL, " +
|
||||
"`categoryId` TEXT NOT NULL)"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import androidx.room.Room
|
||||
import com.navi.bbps.common.BbpsSharedPreferences
|
||||
import com.navi.bbps.common.utils.BbpsSessionCache
|
||||
import com.navi.bbps.common.utils.BbpsSessionCacheImpl
|
||||
import com.navi.bbps.db.NAVI_BBPS_DATABASE_MIGRATION_1_2
|
||||
import com.navi.bbps.db.NaviBbpsAppDatabase
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
@@ -34,12 +35,19 @@ class NaviBbpsDbModule {
|
||||
NaviBbpsAppDatabase::class.java,
|
||||
NaviBbpsAppDatabase.NAVI_BBPS_DATABASE_NAME
|
||||
)
|
||||
.addMigrations(NAVI_BBPS_DATABASE_MIGRATION_1_2)
|
||||
.fallbackToDestructiveMigration()
|
||||
.build()
|
||||
|
||||
@ActivityRetainedScoped
|
||||
@Provides
|
||||
fun providesMyBillsDao(naviBbpsAppDatabase: NaviBbpsAppDatabase) =
|
||||
naviBbpsAppDatabase.myBillsDao()
|
||||
|
||||
@ActivityRetainedScoped
|
||||
@Provides
|
||||
fun providesBillerListDao(naviBbpsAppDatabase: NaviBbpsAppDatabase) =
|
||||
naviBbpsAppDatabase.billerListDao()
|
||||
}
|
||||
|
||||
@Module
|
||||
|
||||
@@ -7,31 +7,19 @@
|
||||
|
||||
package com.navi.bbps.entry
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.navi.bbps.common.NaviBbpsScreen
|
||||
import com.navi.bbps.common.model.NaviBbpsManager
|
||||
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
|
||||
|
||||
@HiltViewModel
|
||||
class NaviBbpsMainViewModel
|
||||
@Inject
|
||||
constructor(
|
||||
private val bbpsRefreshConfigUseCase: BbpsRefreshConfigUseCase,
|
||||
private val myBillsSyncJob: MyBillsSyncJob
|
||||
) : NaviBbpsBaseVM(naviBbpsVmData = NaviBbpsVmData(screen = NaviBbpsScreen.NAVI_BBPS_MAIN)) {
|
||||
class NaviBbpsMainViewModel @Inject constructor(private val naviBbpsManager: NaviBbpsManager) :
|
||||
NaviBbpsBaseVM(naviBbpsVmData = NaviBbpsVmData(screen = NaviBbpsScreen.NAVI_BBPS_MAIN)) {
|
||||
|
||||
init {
|
||||
refreshConfig()
|
||||
}
|
||||
|
||||
private fun refreshConfig() {
|
||||
viewModelScope.safeLaunch(Dispatchers.IO) { bbpsRefreshConfigUseCase.execute() }
|
||||
myBillsSyncJob.refreshBillsAsync()
|
||||
naviBbpsManager.init()
|
||||
}
|
||||
|
||||
fun getDefaultScreenName() = ""
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2024 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.bbps.feature.billerlist
|
||||
|
||||
import com.navi.bbps.feature.billerlist.model.network.BillerItemResponse
|
||||
import com.navi.bbps.feature.billerlist.model.view.BillerItemEntity
|
||||
import javax.inject.Inject
|
||||
|
||||
class BillerItemResponseToEntityMapper @Inject constructor() {
|
||||
fun mapBillerItemResponseToEntityList(
|
||||
billers: List<BillerItemResponse>
|
||||
): List<BillerItemEntity> {
|
||||
return billers.map { response ->
|
||||
BillerItemEntity(
|
||||
billerId = response.billerId,
|
||||
billerName = response.billerName,
|
||||
billerLogoUrl = response.billerLogoUrl,
|
||||
status = response.status ?: "",
|
||||
isAdhoc = response.isAdhoc ?: false,
|
||||
state = response.state ?: "",
|
||||
categoryId = response.categoryId
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import androidx.lifecycle.viewModelScope
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.navi.base.utils.BaseUtils
|
||||
import com.navi.base.utils.NaviNetworkConnectivity
|
||||
import com.navi.bbps.R
|
||||
import com.navi.bbps.common.CATEGORY_ID_FASTAG
|
||||
import com.navi.bbps.common.CATEGORY_ID_MOBILE_POSTPAID
|
||||
import com.navi.bbps.common.DATE_TIME_FORMAT_DATE_MONTH_NAME_YEAR
|
||||
@@ -40,6 +41,7 @@ import com.navi.bbps.feature.customerinput.model.network.DeviceDetails
|
||||
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.mybills.MyBillsRepository
|
||||
import com.navi.bbps.feature.paybill.model.network.PaymentAmountExactness
|
||||
import com.navi.bbps.feature.paybill.model.view.PayBillSource
|
||||
import com.navi.common.di.CoroutineDispatcherProvider
|
||||
@@ -78,6 +80,7 @@ constructor(
|
||||
private val bbpsCommonRepository: BbpsCommonRepository,
|
||||
private val naviNetworkConnectivity: NaviNetworkConnectivity,
|
||||
private val naviBbpsDateUtils: NaviBbpsDateUtils,
|
||||
private val myBillsRepository: MyBillsRepository,
|
||||
private val deviceLocationProvider: DeviceLocationProvider
|
||||
) : NaviBbpsBaseVM(naviBbpsVmData = NaviBbpsVmData(screen = NaviBbpsScreen.NAVI_BBPS_BILLER_LIST)) {
|
||||
|
||||
@@ -95,6 +98,9 @@ constructor(
|
||||
private val _navigateToNextScreen = MutableSharedFlow<Direction>()
|
||||
val navigateToNextScreen = _navigateToNextScreen.asSharedFlow()
|
||||
|
||||
private val _isPayBillShimmerVisible = MutableStateFlow(false)
|
||||
val isPayBillShimmerVisible = _isPayBillShimmerVisible.asStateFlow()
|
||||
|
||||
val phoneNumber = BaseUtils.getPhoneNumber().toString()
|
||||
|
||||
private val _myNumberState =
|
||||
@@ -151,6 +157,14 @@ constructor(
|
||||
initSearchFlow()
|
||||
}
|
||||
|
||||
private fun replaceIfPresent(billerListStateHeading: String?, stateValue: String): String {
|
||||
if (billerListStateHeading.isNullOrEmpty())
|
||||
return resourceProvider
|
||||
.get()
|
||||
.getString(resId = R.string.bbps_state_bills_heading, stateValue)
|
||||
return billerListStateHeading.replace(STATE_PLACEHOLDER, stateValue)
|
||||
}
|
||||
|
||||
fun updateSearchQueryStringState(searchQuery: String) {
|
||||
_searchQuery.update { searchQuery }
|
||||
}
|
||||
@@ -159,6 +173,10 @@ constructor(
|
||||
_billerListState.update { billerListState }
|
||||
}
|
||||
|
||||
private fun updatePayBillShimmerVisibility(isVisible: Boolean) {
|
||||
_isPayBillShimmerVisible.update { isVisible }
|
||||
}
|
||||
|
||||
suspend fun navigateToNextScreen(direction: Direction) {
|
||||
_navigateToNextScreen.emit(direction)
|
||||
}
|
||||
@@ -166,7 +184,7 @@ constructor(
|
||||
val showSpaceBelowSearchBar =
|
||||
combine(_isScreenFastagRecharge, _billerListState) { isScreenFastagRecharge, billerListState
|
||||
->
|
||||
!isScreenFastagRecharge && (billerListState !is BillerListState.FetchBillLoading)
|
||||
!isScreenFastagRecharge && !isPayBillShimmerVisible.value
|
||||
}
|
||||
.stateIn(
|
||||
scope = viewModelScope,
|
||||
@@ -176,7 +194,7 @@ constructor(
|
||||
|
||||
fun onRecentBillItemClicked(billItemEntity: BillItemEntity) {
|
||||
viewModelScope.safeLaunch(dispatcherProvider.io) {
|
||||
updateBillerListState(BillerListState.FetchBillLoading)
|
||||
updatePayBillShimmerVisibility(true)
|
||||
val billDetailsRequest =
|
||||
BillDetailsRequest(
|
||||
billerId = billItemEntity.billerId,
|
||||
@@ -195,10 +213,11 @@ constructor(
|
||||
|
||||
delay(300) // updating state to loaded after navigation
|
||||
|
||||
updatePayBillShimmerVisibility(false)
|
||||
updateBillerListState(createLoadedStateFromBillerResponse(billerListResponse))
|
||||
} else {
|
||||
notifyError(billDetailsResponse)
|
||||
updateBillerListState(createLoadedStateFromBillerResponse(billerListResponse))
|
||||
updatePayBillShimmerVisibility(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -256,7 +275,7 @@ constructor(
|
||||
fun onBillerItemClickedForPostpaid(billerItemEntity: BillerItemEntity) {
|
||||
viewModelScope.safeLaunch(dispatcherProvider.io) {
|
||||
// If all fields are valid, navigate to next screen
|
||||
updateBillerListState(BillerListState.FetchBillLoading)
|
||||
updatePayBillShimmerVisibility(true)
|
||||
val billerDetailsResponse =
|
||||
billerListRepository.fetchBillerDetails(billerItemEntity = billerItemEntity)
|
||||
|
||||
@@ -277,7 +296,7 @@ constructor(
|
||||
}
|
||||
} else {
|
||||
notifyError(billerDetailsResponse)
|
||||
updateBillerListState(createLoadedStateFromBillerResponse(billerListResponse))
|
||||
updatePayBillShimmerVisibility(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,11 +331,10 @@ constructor(
|
||||
)
|
||||
|
||||
delay(300) // updating state to loaded after navigation
|
||||
|
||||
updateBillerListState(createLoadedStateFromBillerResponse(billerListResponse))
|
||||
updatePayBillShimmerVisibility(false)
|
||||
} else {
|
||||
notifyError(billDetailsResponse)
|
||||
updateBillerListState(createLoadedStateFromBillerResponse(billerListResponse))
|
||||
updatePayBillShimmerVisibility(false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -383,7 +401,7 @@ constructor(
|
||||
@OptIn(FlowPreview::class)
|
||||
private fun initSearchFlow() {
|
||||
searchQuery
|
||||
.debounce(600)
|
||||
.debounce { if (it.isEmpty()) 0 else 600 }
|
||||
.map { searchInput -> searchInput.trim() }
|
||||
.distinctUntilChanged()
|
||||
.flowOn(dispatcherProvider.io)
|
||||
@@ -392,7 +410,10 @@ constructor(
|
||||
searchJob?.cancel()
|
||||
searchJob =
|
||||
viewModelScope.launch {
|
||||
fetchBillerList(isFromSearch = true, searchQuery = searchInput)
|
||||
fetchBillerList(
|
||||
isFromSearch = getIsFromSearch(searchQuery.value),
|
||||
searchQuery = searchInput
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
FirebaseCrashlytics.getInstance().recordException(e)
|
||||
@@ -401,6 +422,11 @@ constructor(
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
|
||||
// if search query is not empty, then it is from search
|
||||
private fun getIsFromSearch(searchQuery: String): Boolean {
|
||||
return searchQuery.isNotEmpty()
|
||||
}
|
||||
|
||||
private fun BillerGroupItemResponse.toBillerGroupItemEntity(): BillerGroupItemEntity {
|
||||
return BillerGroupItemEntity(
|
||||
title = this.title,
|
||||
@@ -412,15 +438,20 @@ constructor(
|
||||
return BillerItemEntity(
|
||||
billerId = this.billerId,
|
||||
billerName = this.billerName,
|
||||
billerLogoUrl = this.billerLogoUrl,
|
||||
status = this.status,
|
||||
isAdhoc = this.isAdhoc
|
||||
billerLogoUrl = this.billerLogoUrl ?: "",
|
||||
status = this.status ?: "",
|
||||
isAdhoc = this.isAdhoc ?: false,
|
||||
state = this.state ?: "",
|
||||
categoryId = this.categoryId
|
||||
)
|
||||
}
|
||||
|
||||
private var billerListResponse: RepoResult<BillerListResponse> = RepoResult()
|
||||
|
||||
private suspend fun fetchBillerList(isFromSearch: Boolean = false, searchQuery: String = "") {
|
||||
val categoryIdClicked = billCategoryEntity.categoryId
|
||||
val cachedBillers = bbpsCommonRepository.getBillersByCategoryFromLocalDb(categoryIdClicked)
|
||||
|
||||
if (isFromSearch) {
|
||||
_isSearchBillerRunning.update { true }
|
||||
} else {
|
||||
@@ -428,37 +459,113 @@ constructor(
|
||||
}
|
||||
|
||||
val deviceLocation = deviceLocationProvider.getDeviceLocation()
|
||||
billerListResponse =
|
||||
billerListRepository.fetchBillerList(
|
||||
categoryId = billCategoryEntity.categoryId,
|
||||
billerListRequest =
|
||||
BillerListRequest(
|
||||
latitude = deviceLocation.latitude,
|
||||
longitude = deviceLocation.longitude,
|
||||
state = deviceLocation.state,
|
||||
searchParams = searchQuery
|
||||
)
|
||||
)
|
||||
naviBbpsAnalytics.onBillersFetched(
|
||||
deviceLocation = deviceLocation,
|
||||
billerGroupsCount = billerListResponse.data?.billerGroups?.size ?: 0,
|
||||
recentBillsCount = billerListResponse.data?.recentBills?.bills?.size ?: 0
|
||||
)
|
||||
delay(200) // To avoid jerky transition during plan refresh
|
||||
_isSearchBillerRunning.update { false }
|
||||
if (billerListResponse.isSuccessWithData()) {
|
||||
val fullBillerList =
|
||||
billerListResponse.data!!.billerGroups.orEmpty().flatMap { it.billers.orEmpty() }
|
||||
|
||||
updateBillerListState(createLoadedStateFromBillerResponse(billerListResponse))
|
||||
updateRecentBillsState()
|
||||
_isQueriedListEmpty.update { fullBillerList.isEmpty() }
|
||||
if (cachedBillers.isEmpty() || searchQuery.isNotEmpty()) {
|
||||
billerListResponse =
|
||||
billerListRepository.fetchBillerList(
|
||||
categoryId = billCategoryEntity.categoryId,
|
||||
billerListRequest =
|
||||
BillerListRequest(
|
||||
latitude = deviceLocation.latitude,
|
||||
longitude = deviceLocation.longitude,
|
||||
state = deviceLocation.state,
|
||||
searchParams = searchQuery
|
||||
)
|
||||
)
|
||||
naviBbpsAnalytics.onBillersFetched(
|
||||
deviceLocation = deviceLocation,
|
||||
billerGroupsCount = billerListResponse.data?.billerGroups?.size ?: 0,
|
||||
recentBillsCount = billerListResponse.data?.recentBills?.bills?.size ?: 0
|
||||
)
|
||||
|
||||
_isSearchBillerRunning.update { false }
|
||||
if (billerListResponse.isSuccessWithData()) {
|
||||
val fullBillerList =
|
||||
billerListResponse.data!!.billerGroups.orEmpty().flatMap {
|
||||
it.billers.orEmpty()
|
||||
}
|
||||
|
||||
updateBillerListState(createLoadedStateFromBillerResponse(billerListResponse))
|
||||
updateRecentBillsState()
|
||||
_isQueriedListEmpty.update { fullBillerList.isEmpty() }
|
||||
} else {
|
||||
updateBillerListState(BillerListState.Error)
|
||||
notifyError(billerListResponse)
|
||||
}
|
||||
} else {
|
||||
updateBillerListState(BillerListState.Error)
|
||||
notifyError(billerListResponse)
|
||||
_isQueriedListEmpty.update { cachedBillers.isEmpty() }
|
||||
|
||||
val stateBillers = cachedBillers.filter { it.state == deviceLocation.state }
|
||||
|
||||
val billerGroupItemEntities = mutableListOf<BillerGroupItemEntity>()
|
||||
|
||||
if (stateBillers.isNotEmpty()) {
|
||||
val billerGroupItem =
|
||||
BillerGroupItemEntity(
|
||||
title =
|
||||
replaceIfPresent(
|
||||
billerListStateHeading = billCategoryEntity.billerListStateHeading,
|
||||
stateValue = deviceLocation.state
|
||||
),
|
||||
billers = stateBillers
|
||||
)
|
||||
billerGroupItemEntities.add(billerGroupItem)
|
||||
}
|
||||
|
||||
billerGroupItemEntities.add(
|
||||
BillerGroupItemEntity(
|
||||
title =
|
||||
billCategoryEntity.billerListAllHeading.ifEmpty {
|
||||
resourceProvider
|
||||
.get()
|
||||
.getString(R.string.bbps_all_biller_list_default_heading)
|
||||
},
|
||||
billers = cachedBillers
|
||||
)
|
||||
)
|
||||
|
||||
val recentBillsEntity: RecentBillsEntity =
|
||||
if (categoryIdClicked != CATEGORY_ID_MOBILE_POSTPAID) {
|
||||
getRecentBills()
|
||||
} else {
|
||||
RecentBillsEntity(title = "", bills = listOf())
|
||||
}
|
||||
|
||||
updateBillerListState(
|
||||
BillerListState.Loaded(
|
||||
recentBills = recentBillsEntity,
|
||||
billerGroups = billerGroupItemEntities
|
||||
)
|
||||
)
|
||||
updateRecentBillsState()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun getRecentBills(): RecentBillsEntity {
|
||||
val savedBills =
|
||||
myBillsRepository.fetchSavedBillsByCategory(category = billCategoryEntity.categoryId)
|
||||
|
||||
val billItemEntities =
|
||||
savedBills.map { myBillEntity ->
|
||||
BillItemEntity(
|
||||
billerId = myBillEntity.billerId,
|
||||
billerName = myBillEntity.billerName,
|
||||
billerLogoUrl = myBillEntity.billerLogoUrl,
|
||||
status = myBillEntity.status,
|
||||
isAdhoc = myBillEntity.isAdhoc,
|
||||
paymentAmountExactness = myBillEntity.paymentAmountExactness,
|
||||
formattedLastPaidDate = myBillEntity.formattedLastPaidDate,
|
||||
primaryCustomerParam = myBillEntity.primaryCustomerParamValue,
|
||||
customerParams = myBillEntity.customerParams
|
||||
)
|
||||
}
|
||||
|
||||
return RecentBillsEntity(
|
||||
title = resourceProvider.get().getString(R.string.bbps_recent_bills),
|
||||
bills = billItemEntities
|
||||
)
|
||||
}
|
||||
|
||||
private fun createLoadedStateFromBillerResponse(
|
||||
billerListResponse: RepoResult<BillerListResponse>
|
||||
): BillerListState.Loaded {
|
||||
@@ -506,13 +613,19 @@ constructor(
|
||||
billerName = it.billerName,
|
||||
billerLogoUrl = it.billerLogoUrl,
|
||||
status = it.status,
|
||||
isAdhoc = it.isAdhoc
|
||||
isAdhoc = it.isAdhoc,
|
||||
state = it.state,
|
||||
categoryId = it.categoryId
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val STATE_PLACEHOLDER = "{state}"
|
||||
}
|
||||
}
|
||||
|
||||
class MyNumberState(val isVisible: Boolean, val phoneContactEntity: PhoneContactEntity)
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2024 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.bbps.feature.billerlist.dao
|
||||
|
||||
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_DATABASE_BILLERS
|
||||
import com.navi.bbps.feature.billerlist.model.view.BillerItemEntity
|
||||
|
||||
@Dao
|
||||
interface BillerListDao {
|
||||
@Transaction
|
||||
suspend fun refreshBillers(billers: List<BillerItemEntity>) {
|
||||
deleteAll()
|
||||
insertBillers(billers = billers)
|
||||
}
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertBillers(billers: List<BillerItemEntity>)
|
||||
|
||||
@Query("SELECT * FROM $NAVI_BBPS_DATABASE_BILLERS")
|
||||
suspend fun getAllBillers(): List<BillerItemEntity>
|
||||
|
||||
@Query("SELECT * FROM $NAVI_BBPS_DATABASE_BILLERS WHERE categoryId = :categoryId")
|
||||
suspend fun getBillersByCategory(categoryId: String): List<BillerItemEntity>
|
||||
|
||||
@Query("DELETE FROM $NAVI_BBPS_DATABASE_BILLERS") suspend fun deleteAll()
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2024 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.bbps.feature.billerlist.model.network
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class BillerItemListResponse(
|
||||
@SerializedName("billers") val billers: List<BillerItemResponse>?
|
||||
)
|
||||
@@ -40,6 +40,8 @@ data class BillerItemResponse(
|
||||
@SerializedName("billerId") val billerId: String,
|
||||
@SerializedName("billerName") val billerName: String,
|
||||
@SerializedName("billerLogoUrl") val billerLogoUrl: String?,
|
||||
@SerializedName("status") val status: String,
|
||||
@SerializedName("isAdhoc") val isAdhoc: Boolean
|
||||
@SerializedName("status") val status: String?,
|
||||
@SerializedName("isAdhoc") val isAdhoc: Boolean?,
|
||||
@SerializedName("state") val state: String?,
|
||||
@SerializedName("categoryId") val categoryId: String,
|
||||
)
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
package com.navi.bbps.feature.billerlist.model.view
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.navi.bbps.db.NaviBbpsAppDatabase.Companion.NAVI_BBPS_DATABASE_BILLERS
|
||||
import com.navi.bbps.feature.paybill.model.network.PaymentAmountExactness
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@@ -33,10 +36,13 @@ data class BillItemEntity(
|
||||
data class BillerGroupItemEntity(val title: String, val billers: List<BillerItemEntity>)
|
||||
|
||||
@Parcelize
|
||||
@Entity(tableName = NAVI_BBPS_DATABASE_BILLERS)
|
||||
data class BillerItemEntity(
|
||||
val billerId: String,
|
||||
@PrimaryKey val billerId: String,
|
||||
val billerName: String,
|
||||
val billerLogoUrl: String?,
|
||||
val status: String,
|
||||
val isAdhoc: Boolean
|
||||
val isAdhoc: Boolean,
|
||||
val state: String,
|
||||
val categoryId: String
|
||||
) : Parcelable
|
||||
|
||||
@@ -15,7 +15,5 @@ sealed class BillerListState {
|
||||
val billerGroups: List<BillerGroupItemEntity>,
|
||||
) : BillerListState()
|
||||
|
||||
data object FetchBillLoading : BillerListState()
|
||||
|
||||
data object Error : BillerListState()
|
||||
}
|
||||
|
||||
@@ -92,6 +92,8 @@ fun BillerListScreen(
|
||||
val phoneNumberDetailState by billerListViewModel.myNumberState.collectAsStateWithLifecycle()
|
||||
val showSpaceBelowSearchBar by
|
||||
billerListViewModel.showSpaceBelowSearchBar.collectAsStateWithLifecycle()
|
||||
val isPayBillShimmerVisible by
|
||||
billerListViewModel.isPayBillShimmerVisible.collectAsStateWithLifecycle()
|
||||
|
||||
val bottomSheetState =
|
||||
rememberModalBottomSheetState(
|
||||
@@ -185,7 +187,7 @@ fun BillerListScreen(
|
||||
title = billCategoryEntity.title,
|
||||
onNavigationIconClick = { onBackClick() },
|
||||
)
|
||||
if (billerListState !is BillerListState.FetchBillLoading) {
|
||||
if (!isPayBillShimmerVisible) {
|
||||
RenderSearchBarSection(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
|
||||
phoneNumberDetailState = phoneNumberDetailState,
|
||||
@@ -219,12 +221,10 @@ fun BillerListScreen(
|
||||
)
|
||||
onRecentBillItemClicked(it)
|
||||
},
|
||||
isSearchBillerRunning = isSearchBillerRunning
|
||||
isSearchBillerRunning = isSearchBillerRunning,
|
||||
isPayBillShimmerVisible = isPayBillShimmerVisible
|
||||
)
|
||||
}
|
||||
is BillerListState.FetchBillLoading -> {
|
||||
RenderPayBillScreenLoading()
|
||||
}
|
||||
is BillerListState.Error -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,7 +74,8 @@ fun RenderBillerListScreen(
|
||||
openSheet: () -> Unit,
|
||||
onBillerItemClicked: (BillerItemEntity) -> Unit,
|
||||
onRecentBillItemClicked: (BillItemEntity) -> Unit,
|
||||
isSearchBillerRunning: Boolean
|
||||
isSearchBillerRunning: Boolean,
|
||||
isPayBillShimmerVisible: Boolean
|
||||
) {
|
||||
val isScreenFastagRecharge by
|
||||
billerListViewModel.isScreenFastagRecharge.collectAsStateWithLifecycle()
|
||||
@@ -94,126 +95,137 @@ fun RenderBillerListScreen(
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.fillMaxSize()
|
||||
.padding(
|
||||
start = NaviBbpsDimens.horizontalMargin,
|
||||
end = NaviBbpsDimens.horizontalMargin,
|
||||
bottom = 16.dp
|
||||
)
|
||||
.nestedScroll(nestedScrollConnection)
|
||||
) {
|
||||
if (isScreenFastagRecharge) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.Bottom
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.bbps_how_to_find_your_fastag_bank),
|
||||
fontFamily = ttComposeFontFamily,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_REGULAR),
|
||||
fontSize = 12.sp,
|
||||
color = NaviBbpsColor.textTertiary,
|
||||
modifier = Modifier.align(Alignment.CenterVertically)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.bbps_view),
|
||||
fontFamily = ttComposeFontFamily,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_HEADLINE_REGULAR),
|
||||
fontSize = 14.sp,
|
||||
color = NaviBbpsColor.textPrimary,
|
||||
textDecoration = TextDecoration.Underline,
|
||||
modifier =
|
||||
Modifier.align(Alignment.CenterVertically).noRippleClickableWithDebounce {
|
||||
keyboardController?.customHide(context = context, view = view)
|
||||
openSheet()
|
||||
}
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
modifier = Modifier.padding(vertical = 16.dp),
|
||||
visible = isSearchBillerRunning
|
||||
) {
|
||||
Column {
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
NaviBbpsLottieAnimation(
|
||||
modifier = Modifier.size(16.dp),
|
||||
lottieFileName = GENERIC_LOADER_LOTTIE_FILE_NAME,
|
||||
showLottieInfiniteTimes = true
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.fillMaxSize()
|
||||
.padding(
|
||||
start = NaviBbpsDimens.horizontalMargin,
|
||||
end = NaviBbpsDimens.horizontalMargin,
|
||||
bottom = 16.dp
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
.nestedScroll(nestedScrollConnection)
|
||||
) {
|
||||
if (isScreenFastagRecharge) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.Bottom
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.bbps_searching),
|
||||
text = stringResource(id = R.string.bbps_how_to_find_your_fastag_bank),
|
||||
fontFamily = ttComposeFontFamily,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_REGULAR),
|
||||
fontSize = 12.sp,
|
||||
color = NaviBbpsColor.textTertiary,
|
||||
modifier = Modifier.align(Alignment.CenterVertically)
|
||||
)
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.bbps_view),
|
||||
fontFamily = ttComposeFontFamily,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_HEADLINE_REGULAR),
|
||||
fontSize = 12.sp,
|
||||
color = NaviBbpsColor.textTertiary
|
||||
fontSize = 14.sp,
|
||||
color = NaviBbpsColor.textPrimary,
|
||||
textDecoration = TextDecoration.Underline,
|
||||
modifier =
|
||||
Modifier.align(Alignment.CenterVertically)
|
||||
.noRippleClickableWithDebounce {
|
||||
keyboardController?.customHide(context = context, view = view)
|
||||
openSheet()
|
||||
}
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
}
|
||||
}
|
||||
|
||||
if (isQueriedListEmpty) {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.fillMaxSize().align(Alignment.CenterHorizontally).padding(top = 32.dp)
|
||||
AnimatedVisibility(
|
||||
modifier = Modifier.padding(vertical = 16.dp),
|
||||
visible = isSearchBillerRunning
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_bbps_no_billers_icon),
|
||||
contentDescription = stringResource(id = R.string.bbps_no_billers_found),
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.bbps_no_billers_found),
|
||||
fontFamily = ttComposeFontFamily,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_HEADLINE_REGULAR),
|
||||
fontSize = 14.sp,
|
||||
color = NaviBbpsColor.textTertiary,
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||
)
|
||||
Column {
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
NaviBbpsLottieAnimation(
|
||||
modifier = Modifier.size(16.dp),
|
||||
lottieFileName = GENERIC_LOADER_LOTTIE_FILE_NAME,
|
||||
showLottieInfiniteTimes = true
|
||||
)
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.bbps_searching),
|
||||
fontFamily = ttComposeFontFamily,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_HEADLINE_REGULAR),
|
||||
fontSize = 12.sp,
|
||||
color = NaviBbpsColor.textTertiary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LazyColumn(modifier = Modifier.fillMaxSize(), flingBehavior = maxScrollFlingBehavior()) {
|
||||
if (isSearchQueryEmpty and isRecentBillsMoreThanZero) {
|
||||
item { RecentBillTitle(billerListState = billerListState) }
|
||||
items(count = billerListState.recentBills.bills.size) { item ->
|
||||
val billItem = billerListState.recentBills.bills[item]
|
||||
RecentBillerItem(
|
||||
billItemEntity = billItem,
|
||||
primaryCustomerParamValue = billItem.primaryCustomerParam,
|
||||
isLastPaidDateVisible =
|
||||
billItem.formattedLastPaidDate.isNotNullAndNotEmpty(),
|
||||
onRecentBillItemClicked = { onRecentBillItemClicked(billItem) }
|
||||
if (isQueriedListEmpty) {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.fillMaxSize()
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.padding(top = 32.dp)
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_bbps_no_billers_icon),
|
||||
contentDescription = stringResource(id = R.string.bbps_no_billers_found),
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
Text(
|
||||
text = stringResource(id = R.string.bbps_no_billers_found),
|
||||
fontFamily = ttComposeFontFamily,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_HEADLINE_REGULAR),
|
||||
fontSize = 14.sp,
|
||||
color = NaviBbpsColor.textTertiary,
|
||||
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
billerListState.billerGroups.forEach { group ->
|
||||
if (isSearchQueryEmpty) {
|
||||
item { BillerTitle(billerGroupItemEntity = group) }
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
flingBehavior = maxScrollFlingBehavior()
|
||||
) {
|
||||
if (isSearchQueryEmpty and isRecentBillsMoreThanZero) {
|
||||
item { RecentBillTitle(billerListState = billerListState) }
|
||||
items(count = billerListState.recentBills.bills.size) { item ->
|
||||
val billItem = billerListState.recentBills.bills[item]
|
||||
RecentBillerItem(
|
||||
billItemEntity = billItem,
|
||||
primaryCustomerParamValue = billItem.primaryCustomerParam,
|
||||
isLastPaidDateVisible =
|
||||
billItem.formattedLastPaidDate.isNotNullAndNotEmpty(),
|
||||
onRecentBillItemClicked = { onRecentBillItemClicked(billItem) }
|
||||
)
|
||||
}
|
||||
}
|
||||
items(
|
||||
count = group.billers.size,
|
||||
key = { billerItem -> "${group.billers[billerItem].billerId}${group.title}" }
|
||||
) { billerItem ->
|
||||
val item = group.billers[billerItem]
|
||||
BillerItem(
|
||||
billerItemEntity = item,
|
||||
onItemClicked = { onBillerItemClicked(item) }
|
||||
)
|
||||
|
||||
billerListState.billerGroups.forEach { group ->
|
||||
if (isSearchQueryEmpty) {
|
||||
item { BillerTitle(billerGroupItemEntity = group) }
|
||||
}
|
||||
items(
|
||||
count = group.billers.size,
|
||||
key = { billerItem ->
|
||||
"${group.billers[billerItem].billerId}${group.title}"
|
||||
}
|
||||
) { billerItem ->
|
||||
val item = group.billers[billerItem]
|
||||
BillerItem(
|
||||
billerItemEntity = item,
|
||||
onItemClicked = { onBillerItemClicked(item) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isPayBillShimmerVisible) RenderPayBillScreenLoading()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -263,7 +263,9 @@ constructor(
|
||||
categoryId = myBillEntity.categoryId,
|
||||
title = myBillEntity.categoryName,
|
||||
iconUrl = "",
|
||||
searchBoxPlaceholderText = ""
|
||||
searchBoxPlaceholderText = "",
|
||||
billerListStateHeading = "",
|
||||
billerListAllHeading = ""
|
||||
),
|
||||
payBillScreenSource =
|
||||
PayBillSource.Others(
|
||||
@@ -323,7 +325,9 @@ constructor(
|
||||
categoryId = myBillEntity.categoryId,
|
||||
title = myBillEntity.categoryName,
|
||||
iconUrl = "",
|
||||
searchBoxPlaceholderText = ""
|
||||
searchBoxPlaceholderText = "",
|
||||
billerListStateHeading = "",
|
||||
billerListAllHeading = ""
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -164,7 +164,9 @@ constructor(
|
||||
.getString(
|
||||
R.string.bbps_default_searchbox_placeholder
|
||||
)
|
||||
}
|
||||
},
|
||||
billerListStateHeading = category.billerListStateHeading ?: "",
|
||||
billerListAllHeading = category.billerListAllHeading ?: ""
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -25,5 +25,7 @@ data class CategoryItemResponse(
|
||||
@SerializedName("title") val title: String,
|
||||
@SerializedName("iconUrl") val iconUrl: String?,
|
||||
@SerializedName("order") val order: Int? = 0,
|
||||
@SerializedName("searchBoxPlaceholderText") val searchBoxPlaceholderText: String?
|
||||
@SerializedName("searchBoxPlaceholderText") val searchBoxPlaceholderText: String?,
|
||||
@SerializedName("billerListStateHeading") val billerListStateHeading: String?,
|
||||
@SerializedName("billerListAllHeading") val billerListAllHeading: String?,
|
||||
)
|
||||
|
||||
@@ -26,7 +26,9 @@ data class BillCategoryEntity(
|
||||
@PrimaryKey val categoryId: String,
|
||||
val title: String,
|
||||
val iconUrl: String,
|
||||
val searchBoxPlaceholderText: String
|
||||
val searchBoxPlaceholderText: String,
|
||||
val billerListStateHeading: String,
|
||||
val billerListAllHeading: String
|
||||
) : Parcelable {
|
||||
val fallbackImageResId
|
||||
get() = R.drawable.ic_bbps_biller_placeholder
|
||||
|
||||
@@ -56,7 +56,9 @@ fun BbpsRoutingLauncherScreen(
|
||||
title = bundle.getString(BBPS_CATEGORY_TITLE) ?: "",
|
||||
iconUrl = bundle.getString(BBPS_CATEGORY_ICON_URL) ?: "",
|
||||
searchBoxPlaceholderText =
|
||||
bundle.getString(BBPS_BILLER_SEARCH_BOX_PLACEHOLDER_TEXT) ?: ""
|
||||
bundle.getString(BBPS_BILLER_SEARCH_BOX_PLACEHOLDER_TEXT) ?: "",
|
||||
billerListStateHeading = "",
|
||||
billerListAllHeading = "",
|
||||
),
|
||||
isRootScreen = true
|
||||
)
|
||||
@@ -69,7 +71,9 @@ fun BbpsRoutingLauncherScreen(
|
||||
title = bundle.getString(BBPS_CATEGORY_TITLE) ?: "",
|
||||
iconUrl = bundle.getString(BBPS_CATEGORY_ICON_URL) ?: "",
|
||||
searchBoxPlaceholderText =
|
||||
bundle.getString(BBPS_BILLER_SEARCH_BOX_PLACEHOLDER_TEXT) ?: ""
|
||||
bundle.getString(BBPS_BILLER_SEARCH_BOX_PLACEHOLDER_TEXT) ?: "",
|
||||
billerListStateHeading = "",
|
||||
billerListAllHeading = ""
|
||||
),
|
||||
isRootScreen = true
|
||||
)
|
||||
|
||||
@@ -31,7 +31,6 @@ 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
|
||||
@@ -215,7 +214,6 @@ constructor(
|
||||
listOf()
|
||||
}
|
||||
}
|
||||
delay(300) // a bit lesser than transition animation duration
|
||||
updateContactListUIState(
|
||||
contactListState = ContactListState.Loaded(allContactList = _allContactList.value)
|
||||
)
|
||||
|
||||
@@ -22,7 +22,7 @@ interface MyBillsDao {
|
||||
fun getAllBills(): Flow<List<MyBillEntity>>
|
||||
|
||||
@Query("SELECT * FROM $NAVI_BBPS_TABLE_MY_SAVED_BILLS WHERE categoryId == :categoryId")
|
||||
fun getBillsByCategory(categoryId: String): Flow<List<MyBillEntity>>
|
||||
suspend fun getBillsByCategory(categoryId: String): List<MyBillEntity>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun insertAll(myBills: List<MyBillEntity>)
|
||||
|
||||
@@ -24,4 +24,7 @@ constructor(
|
||||
}
|
||||
|
||||
fun fetchMySavedBills() = myBillsDao.getAllBills()
|
||||
|
||||
suspend fun fetchSavedBillsByCategory(category: String) =
|
||||
myBillsDao.getBillsByCategory(category)
|
||||
}
|
||||
|
||||
@@ -584,7 +584,9 @@ constructor(
|
||||
categoryId = billCategoryEntity.categoryId,
|
||||
title = billCategoryEntity.title,
|
||||
iconUrl = billCategoryEntity.iconUrl,
|
||||
searchBoxPlaceholderText = billCategoryEntity.searchBoxPlaceholderText
|
||||
searchBoxPlaceholderText = billCategoryEntity.searchBoxPlaceholderText,
|
||||
billerListStateHeading = billCategoryEntity.billerListStateHeading,
|
||||
billerListAllHeading = billCategoryEntity.billerListAllHeading
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
package com.navi.bbps.network.service
|
||||
|
||||
import com.navi.bbps.common.model.network.BbpsGenericResponse
|
||||
import com.navi.bbps.feature.billerlist.model.network.BillerItemListResponse
|
||||
import com.navi.bbps.feature.billerlist.model.network.BillerListRequest
|
||||
import com.navi.bbps.feature.billerlist.model.network.BillerListResponse
|
||||
import com.navi.bbps.feature.billhistorydetail.model.network.BillTransactionHistoryResponse
|
||||
@@ -102,4 +103,7 @@ interface NaviBbpsRetrofitService {
|
||||
suspend fun getConfig(
|
||||
@Query("configKeyList") commaSeparatedConfigKeys: String
|
||||
): Response<GenericResponse<Any?>>
|
||||
|
||||
@GET("/billpay-gateway/$NAVI_BBPS_API_VERSION/billpay/categories/billers")
|
||||
suspend fun getFullBillerList(): Response<GenericResponse<BillerItemListResponse>>
|
||||
}
|
||||
|
||||
@@ -79,6 +79,8 @@
|
||||
<string name="bbps_max_accepted_amt">Maximum accepted amount is ₹%s</string>
|
||||
<string name="bbps_exact_accepted_amt">Only amount accepted is ₹%s</string>
|
||||
<string name="bbps_default_searchbox_placeholder">Search…</string>
|
||||
<string name="bbps_state_biller_list_default_heading">Billers of your state</string>
|
||||
<string name="bbps_all_biller_list_default_heading">All billers</string>
|
||||
<string name="bbps_my_number">My number</string>
|
||||
<string name="bbps_delete_bill_confirmation_title">Are you sure you want to delete?</string>
|
||||
<string name="bbps_delete_bill_confirmation_message">All your bill payment history will get removed. Are you sure you want to delete this account?</string>
|
||||
@@ -118,4 +120,6 @@
|
||||
<string name="bbps_recharge_plan_disclaimer_text">Disclaimer- We support most of the recharges, but please check with your operator before you proceed.</string>
|
||||
<string name="bbps_disclaimer_with_dash">Disclaimer-</string>
|
||||
<string name="bbps_view_contacts">View contacts</string>
|
||||
<string name="bbps_recent_bills">Recent bills</string>
|
||||
<string name="bbps_state_bills_heading">Billers in %s</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user