TP-66121 | [GI] - Implementing an Efficient Cache Mechanism for Seamless User Experience (#11108)

This commit is contained in:
Balrambhai Sharma
2024-09-24 17:55:32 +05:30
committed by GitHub
parent b20faa4526
commit 9b0d5b80f5
13 changed files with 124 additions and 28 deletions

View File

@@ -86,6 +86,7 @@ class InsuranceTabFragment(private val lazyListState: () -> LazyListState? = { n
setContent { RenderUiTronDataSecondary(lazyListState) } setContent { RenderUiTronDataSecondary(lazyListState) }
} }
observeCtaData() observeCtaData()
viewModel.clearCacheOnVersionUpgrade()
if ( if (
FirebaseRemoteConfigHelper.getBoolean( FirebaseRemoteConfigHelper.getBoolean(
key = FirebaseRemoteConfigHelper.ENABLE_REACT_PREFETCH_IN_TAB, key = FirebaseRemoteConfigHelper.ENABLE_REACT_PREFETCH_IN_TAB,

View File

@@ -13,6 +13,7 @@ import com.navi.common.network.models.RepoResult
import com.navi.common.network.retrofit.ResponseCallback import com.navi.common.network.retrofit.ResponseCallback
import com.navi.common.utils.Constants.GZIP import com.navi.common.utils.Constants.GZIP
import com.navi.common.utils.TemporaryStorageHelper import com.navi.common.utils.TemporaryStorageHelper
import com.navi.insurance.common.models.ClearCacheResponse
import com.navi.insurance.common.models.InsuranceTabResponse import com.navi.insurance.common.models.InsuranceTabResponse
import com.navi.insurance.common.models.ToastRequestBody import com.navi.insurance.common.models.ToastRequestBody
import com.navi.insurance.models.response.ClosePolicyCardResponse import com.navi.insurance.models.response.ClosePolicyCardResponse
@@ -50,4 +51,12 @@ constructor(@SuperAppRetroFit private val apiService: RetrofitService) : Respons
apiService.closePolicyCard(applicationId = quoteId, target = ModuleName.GI.name) apiService.closePolicyCard(applicationId = quoteId, target = ModuleName.GI.name)
) )
} }
suspend fun clearCache(): RepoResult<ClearCacheResponse> {
return apiResponseCallback(
apiService.clearCache(
target = ModuleName.GI.name,
)
)
}
} }

View File

@@ -10,6 +10,7 @@ package com.naviapp.common.tab
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.navi.analytics.utils.NaviTrackEvent import com.navi.analytics.utils.NaviTrackEvent
import com.navi.base.cache.repository.NaviCacheRepository
import com.navi.base.model.CtaData import com.navi.base.model.CtaData
import com.navi.base.utils.isNotNull import com.navi.base.utils.isNotNull
import com.navi.base.utils.isNull import com.navi.base.utils.isNull
@@ -20,6 +21,7 @@ import com.navi.common.model.ModuleNameV2
import com.navi.common.network.models.ErrorMetaData import com.navi.common.network.models.ErrorMetaData
import com.navi.common.uitron.model.action.CtaAction import com.navi.common.uitron.model.action.CtaAction
import com.navi.common.utils.SingleLiveEvent import com.navi.common.utils.SingleLiveEvent
import com.navi.common.utils.isValidResponse
import com.navi.common.viewmodel.BaseVM import com.navi.common.viewmodel.BaseVM
import com.navi.insurance.common.models.GiErrorMetaData import com.navi.insurance.common.models.GiErrorMetaData
import com.navi.insurance.common.models.InsuranceTabState import com.navi.insurance.common.models.InsuranceTabState
@@ -41,6 +43,7 @@ class InsuranceTabViewModel
@Inject @Inject
constructor( constructor(
private val repository: InsuranceTabRepository, private val repository: InsuranceTabRepository,
private val naviCacheRepository: NaviCacheRepository
) : BaseVM() { ) : BaseVM() {
init { init {
@@ -191,4 +194,36 @@ constructor(
private fun handleCtaAction(ctaAction: CtaAction) { private fun handleCtaAction(ctaAction: CtaAction) {
ctaAction.ctaData?.let { ctaData -> redirectionCta.postValue(ctaAction.ctaData) } ctaAction.ctaData?.let { ctaData -> redirectionCta.postValue(ctaAction.ctaData) }
} }
fun clearCacheOnVersionUpgrade() {
viewModelScope.safeLaunch(coroutineContext = Dispatchers.IO) {
val response = repository.clearCache()
if (response.isValidResponse()) {
response.data?.pageVersionList?.forEach { item ->
val entity = naviCacheRepository.get(key = item.pageName.orEmpty())
entity?.let {
if (it.version != item.version.orZero()) {
naviCacheRepository.clear(key = item.pageName.orEmpty())
}
}
}
} else {
val errorUnifiedResponse =
getErrorUnifiedResponse(
errors = response.errors,
error = response.error,
errorMetaData =
ErrorMetaData(
methodName = ApiErrorTagType.INSURANCE_TAB_SCREEN_PAGE_ERROR.value,
flowName = GiErrorMetaData.INSURANCE_TAB_FLOW
)
)
sendFailureEvent(
NaviAnalytics.INSURANCE_TAB_INIT,
errorUnifiedResponse,
ModuleNameV2.Insurance.name
)
}
}
}
} }

View File

@@ -57,6 +57,7 @@ import com.navi.common.utils.Constants.HEADER_VERTICAL_TYPE
import com.navi.common.utils.Constants.HEADER_X_PLATFORM import com.navi.common.utils.Constants.HEADER_X_PLATFORM
import com.navi.common.utils.Constants.HEADER_X_TENANT_ID import com.navi.common.utils.Constants.HEADER_X_TENANT_ID
import com.navi.common.utils.Constants.ScreenLockConstants.X_IS_MOBILE_SCREEN_LOCK_SET import com.navi.common.utils.Constants.ScreenLockConstants.X_IS_MOBILE_SCREEN_LOCK_SET
import com.navi.insurance.common.models.ClearCacheResponse
import com.navi.insurance.common.models.InsuranceTabResponse import com.navi.insurance.common.models.InsuranceTabResponse
import com.navi.insurance.common.models.ToastRequestBody import com.navi.insurance.common.models.ToastRequestBody
import com.navi.insurance.models.request.FlagUpdateRequest import com.navi.insurance.models.request.FlagUpdateRequest
@@ -1836,6 +1837,11 @@ interface RetrofitService {
@Header("X-Target") target: String = ModuleName.SAPHYRA.name, @Header("X-Target") target: String = ModuleName.SAPHYRA.name,
): Response<GenericResponse<Any>> ): Response<GenericResponse<Any>>
@GET("/gi/static-pages/versions")
suspend fun clearCache(
@Header("X-Target") target: String
): Response<GenericResponse<ClearCacheResponse>>
@GET("/forge/screen/{screenId}") @GET("/forge/screen/{screenId}")
suspend fun fetchInvestmentTabForgeScreen( suspend fun fetchInvestmentTabForgeScreen(
@Header(X_IS_MOBILE_SCREEN_LOCK_SET) isMobileScreenLockSet: Boolean? = null, @Header(X_IS_MOBILE_SCREEN_LOCK_SET) isMobileScreenLockSet: Boolean? = null,

View File

@@ -12,7 +12,9 @@ object DBCacheConstants {
const val NAVI_HL_INTRO_SCREEN_CACHE_KEY = "NAVI_HL_INTRO_SCREEN_CACHE_KEY" const val NAVI_HL_INTRO_SCREEN_CACHE_KEY = "NAVI_HL_INTRO_SCREEN_CACHE_KEY"
const val NAVI_GI_LANDING_PAGE_CACHE_KEY = "NAVI_GI_LANDING_PAGE_CACHE_KEY" const val NAVI_GI_LANDING_PAGE_CACHE_KEY = "NAVI_GI_LANDING_PAGE_CACHE_KEY"
const val NAVI_GI_BENEFIT_PAGE_CACHE_KEY = "NAVI_GI_BENEFIT_PAGE_CACHE_KEY" const val NAVI_GI_BENEFIT_PAGE_CACHE_KEY = "NAVI_GI_BENEFIT_PAGE_CACHE_KEY"
const val GI_PRE_DIAB_RESULT_CACHE_KEY = "GI_PRE_DIAB_RESULT_CACHE_KEY" const val DIGITAL_CLAIMS_PRE_PURCHASE_DATA = "DIGITAL_CLAIMS_PRE_PURCHASE_DATA"
const val NAVI_GI_AUTOPAY_SUMMARY_SCREEN_CACHE_KEY = "NAVI_GI_AUTOPAY_SUMMARY_SCREEN_CACHE_KEY"
const val PD_QUESTIONNAIRE_PAGE = "PD_QUESTIONNAIRE_PAGE"
const val GI_PRE_DIAB_QUESTION_CACHE_KEY = "GI_PRE_DIAB_QUESTION_CACHE_KEY" const val GI_PRE_DIAB_QUESTION_CACHE_KEY = "GI_PRE_DIAB_QUESTION_CACHE_KEY"
const val COIN_HOME_SCREEN_CACHE_KEY = "COIN_HOME_SCREEN_CACHE_KEY" const val COIN_HOME_SCREEN_CACHE_KEY = "COIN_HOME_SCREEN_CACHE_KEY"
const val LEADERBOARD_SCREEN_CACHE_KEY = "LEADERBOARD_SCREEN_CACHE_KEY" const val LEADERBOARD_SCREEN_CACHE_KEY = "LEADERBOARD_SCREEN_CACHE_KEY"

View File

@@ -0,0 +1,19 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.navi.insurance.common.models
import com.google.gson.annotations.SerializedName
data class ClearCacheResponse(
@SerializedName("pageVersions") val pageVersionList: List<PageVersion>? = null,
)
data class PageVersion(
@SerializedName("pageName") val pageName: String? = null,
@SerializedName("version") val version: Int? = null,
)

View File

@@ -14,6 +14,7 @@ import com.google.gson.Gson
import com.navi.base.cache.model.NaviCacheAltSourceEntity import com.navi.base.cache.model.NaviCacheAltSourceEntity
import com.navi.base.cache.repository.NaviCacheRepository import com.navi.base.cache.repository.NaviCacheRepository
import com.navi.base.model.CtaData import com.navi.base.model.CtaData
import com.navi.common.constants.DBCacheConstants
import com.navi.common.constants.VENDOR_NAVI_API import com.navi.common.constants.VENDOR_NAVI_API
import com.navi.common.di.CoroutineDispatcherProvider import com.navi.common.di.CoroutineDispatcherProvider
import com.navi.common.model.ModuleName import com.navi.common.model.ModuleName
@@ -114,8 +115,14 @@ constructor(
if (type.equals(SUMMARY_INSURANCE_TAB)) { if (type.equals(SUMMARY_INSURANCE_TAB)) {
val cachedResponse = val cachedResponse =
naviCacheRepository.getDataOrFetchFromAltSource( naviCacheRepository.getDataOrFetchFromAltSource(
SUMMARY_INSURANCE_TAB, key = DBCacheConstants.NAVI_GI_AUTOPAY_SUMMARY_SCREEN_CACHE_KEY,
getDataFromAltSource = { getPageResponseFromAPI(type, policyId) } getDataFromAltSource = {
getPageResponseFromAPI(
type,
policyId,
key = DBCacheConstants.NAVI_GI_AUTOPAY_SUMMARY_SCREEN_CACHE_KEY
)
}
) )
RepoResult( RepoResult(
statusCode = 200, statusCode = 200,
@@ -151,8 +158,10 @@ constructor(
private suspend fun getPageResponseFromAPI( private suspend fun getPageResponseFromAPI(
pageType: String?, pageType: String?,
policyId: String? policyId: String?,
key: String
): NaviCacheAltSourceEntity { ): NaviCacheAltSourceEntity {
val version = 1
val response = val response =
incentiviseAutopayRepository.fetchIncentiviseAutopayResponse(pageType, policyId) incentiviseAutopayRepository.fetchIncentiviseAutopayResponse(pageType, policyId)
@@ -173,7 +182,7 @@ constructor(
return NaviCacheAltSourceEntity( return NaviCacheAltSourceEntity(
value = Gson().toJson(response.data!!), value = Gson().toJson(response.data!!),
isSuccess = true, isSuccess = true,
version = 1 version = version
) )
} }

View File

@@ -63,7 +63,7 @@ constructor(
val landingPageResponse = val landingPageResponse =
if (requestPageType == LandingPageInfoFragment.STATIC_BENEFITS_SCREEN) { if (requestPageType == LandingPageInfoFragment.STATIC_BENEFITS_SCREEN) {
naviCacheRepository.getDataOrFetchFromAltSource( naviCacheRepository.getDataOrFetchFromAltSource(
key = DBCacheConstants.NAVI_GI_BENEFIT_PAGE_CACHE_KEY + tabId.orEmpty(), key = DBCacheConstants.NAVI_GI_BENEFIT_PAGE_CACHE_KEY,
version = cacheVersion.toLong(), version = cacheVersion.toLong(),
getDataFromAltSource = { getDataFromAltSource = {
fetchStaticBenefitsResponseFromApi( fetchStaticBenefitsResponseFromApi(
@@ -71,7 +71,8 @@ constructor(
applicationType, applicationType,
tabId, tabId,
preQuoteExists, preQuoteExists,
cacheVersion cacheVersion,
DBCacheConstants.NAVI_GI_BENEFIT_PAGE_CACHE_KEY
) )
} }
) )
@@ -150,11 +151,13 @@ constructor(
applicationType: String? = null, applicationType: String? = null,
tabId: String? = null, tabId: String? = null,
preQuoteExists: Boolean? = null, preQuoteExists: Boolean? = null,
cacheVersion: Int cacheVersion: Int,
key: String
): NaviCacheAltSourceEntity { ): NaviCacheAltSourceEntity {
val version = cacheVersion
if (requestPageType.isNullOrEmpty()) { if (requestPageType.isNullOrEmpty()) {
_landingInfoPageResponseStateFlow.emit(LandingPageInfoState.Error) _landingInfoPageResponseStateFlow.emit(LandingPageInfoState.Error)
return NaviCacheAltSourceEntity(value = null, version = cacheVersion, isSuccess = true) return NaviCacheAltSourceEntity(value = null, version = version, isSuccess = true)
} }
val response = val response =
landingPageInfoRepository.fetchBenefitPageInfo( landingPageInfoRepository.fetchBenefitPageInfo(
@@ -166,7 +169,7 @@ constructor(
if (response.error == null && response.errors.isNullOrEmpty() && response.data != null) { if (response.error == null && response.errors.isNullOrEmpty() && response.data != null) {
return NaviCacheAltSourceEntity( return NaviCacheAltSourceEntity(
value = Gson().toJson(response.data), value = Gson().toJson(response.data),
version = response.data?.screenVersion ?: 1, version = version,
isSuccess = true isSuccess = true
) )
// TODO remove // TODO remove
@@ -182,7 +185,7 @@ constructor(
) )
) )
} }
return NaviCacheAltSourceEntity(value = null, version = cacheVersion, isSuccess = true) return NaviCacheAltSourceEntity(value = null, version = version, isSuccess = true)
} }
private fun saveFooterCta(landingPageInfoResponse: LandingPageInfoResponse?) { private fun saveFooterCta(landingPageInfoResponse: LandingPageInfoResponse?) {

View File

@@ -15,5 +15,6 @@ data class PdResultsData(
@SerializedName("screenVersion") val screenVersion: Int? = null, @SerializedName("screenVersion") val screenVersion: Int? = null,
@SerializedName("widgetData") val widgetData: List<GenericWidgetDataInfo>? = null, @SerializedName("widgetData") val widgetData: List<GenericWidgetDataInfo>? = null,
@SerializedName("footerWidget") val footerWidget: GenericWidgetDataInfo? = null, @SerializedName("footerWidget") val footerWidget: GenericWidgetDataInfo? = null,
@SerializedName("resultBackButtonCta") val resultBackButtonCta: CtaData? = null @SerializedName("resultBackButtonCta") val resultBackButtonCta: CtaData? = null,
@SerializedName("pageVersion") val pageVersion: Int? = null,
) )

View File

@@ -120,7 +120,12 @@ constructor(
naviCacheRepository.getDataOrFetchFromAltSource( naviCacheRepository.getDataOrFetchFromAltSource(
key = DBCacheConstants.GI_PRE_DIAB_QUESTION_CACHE_KEY, key = DBCacheConstants.GI_PRE_DIAB_QUESTION_CACHE_KEY,
version = cacheVersion.toLong(), version = cacheVersion.toLong(),
getDataFromAltSource = { getQuestionnaireDataFromApi(cacheVersion) } getDataFromAltSource = {
getQuestionnaireDataFromApi(
cacheVersion,
key = DBCacheConstants.GI_PRE_DIAB_QUESTION_CACHE_KEY
)
}
) )
response?.let { naviCacheEntity -> response?.let { naviCacheEntity ->
@@ -136,7 +141,11 @@ constructor(
} }
} }
private suspend fun getQuestionnaireDataFromApi(cacheVersion: Int): NaviCacheAltSourceEntity { private suspend fun getQuestionnaireDataFromApi(
cacheVersion: Int,
key: String
): NaviCacheAltSourceEntity {
val version = cacheVersion
val response = repository.getQuestionnaireData() val response = repository.getQuestionnaireData()
if (response.error == null && response.errors.isNullOrEmpty() && response.data != null) { if (response.error == null && response.errors.isNullOrEmpty() && response.data != null) {
@@ -158,7 +167,7 @@ constructor(
) )
) )
} }
return NaviCacheAltSourceEntity(value = null, version = cacheVersion, isSuccess = true) return NaviCacheAltSourceEntity(value = null, version = version, isSuccess = true)
} }
fun getResultScreenData(cacheVersion: Int = 0, overrideScoreType: String? = null) { fun getResultScreenData(cacheVersion: Int = 0, overrideScoreType: String? = null) {
@@ -187,12 +196,10 @@ constructor(
_scoreRangeFlow.emit(overrideScoreType ?: scoreType) _scoreRangeFlow.emit(overrideScoreType ?: scoreType)
val response = val response =
naviCacheRepository.getDataOrFetchFromAltSource( naviCacheRepository.getDataOrFetchFromAltSource(
key = key = DBCacheConstants.PD_QUESTIONNAIRE_PAGE + (overrideScoreType ?: scoreType),
DBCacheConstants.GI_PRE_DIAB_RESULT_CACHE_KEY +
(overrideScoreType ?: scoreType),
version = cacheVersion.toLong(), version = cacheVersion.toLong(),
getDataFromAltSource = { getDataFromAltSource = {
getResultScreenDataFromApi((overrideScoreType ?: scoreType), cacheVersion) getResultScreenDataFromApi((overrideScoreType ?: scoreType))
} }
) )
@@ -210,14 +217,13 @@ constructor(
private suspend fun getResultScreenDataFromApi( private suspend fun getResultScreenDataFromApi(
scoreType: String, scoreType: String,
cacheVersion: Int
): NaviCacheAltSourceEntity { ): NaviCacheAltSourceEntity {
val response = repository.getResultScreenData(scoreType) val response = repository.getResultScreenData(scoreType)
if (response.error == null && response.errors.isNullOrEmpty() && response.data != null) { if (response.error == null && response.errors.isNullOrEmpty() && response.data != null) {
return NaviCacheAltSourceEntity( return NaviCacheAltSourceEntity(
value = Gson().toJson(response.data), value = Gson().toJson(response.data),
version = 1, version = response.data?.pageVersion ?: 1,
isSuccess = true isSuccess = true
) )
} else { } else {
@@ -233,7 +239,11 @@ constructor(
) )
) )
} }
return NaviCacheAltSourceEntity(value = null, version = cacheVersion, isSuccess = true) return NaviCacheAltSourceEntity(
value = null,
version = response.data?.pageVersion ?: 1,
isSuccess = true
)
} }
private fun saveScoreToBackend(score: Float?) { private fun saveScoreToBackend(score: Float?) {

View File

@@ -24,6 +24,7 @@ data class DigitalClaimScreenResponse(
@SerializedName("bulletIcon") val bulletIcon: ImageFieldData? = null, @SerializedName("bulletIcon") val bulletIcon: ImageFieldData? = null,
@SerializedName("firstTabContent") val firstTabContent: TabContent? = null, @SerializedName("firstTabContent") val firstTabContent: TabContent? = null,
@SerializedName("secondTabContent") val secondTabContent: TabContent? = null, @SerializedName("secondTabContent") val secondTabContent: TabContent? = null,
@SerializedName("pageVersion") val pageVersion: Int? = null,
) )
@Parcelize @Parcelize

View File

@@ -12,6 +12,7 @@ import com.google.gson.Gson
import com.navi.base.cache.model.NaviCacheAltSourceEntity import com.navi.base.cache.model.NaviCacheAltSourceEntity
import com.navi.base.cache.repository.NaviCacheRepository import com.navi.base.cache.repository.NaviCacheRepository
import com.navi.common.ResponseState import com.navi.common.ResponseState
import com.navi.common.constants.DBCacheConstants
import com.navi.common.di.CoroutineDispatcherProvider import com.navi.common.di.CoroutineDispatcherProvider
import com.navi.common.model.ModuleName import com.navi.common.model.ModuleName
import com.navi.common.network.models.RepoResult import com.navi.common.network.models.RepoResult
@@ -20,7 +21,6 @@ import com.navi.common.utils.CommonNaviAnalytics
import com.navi.insurance.common.GiBaseVM import com.navi.insurance.common.GiBaseVM
import com.navi.insurance.common.models.GiErrorMetaData import com.navi.insurance.common.models.GiErrorMetaData
import com.navi.insurance.common.util.ActionHandler import com.navi.insurance.common.util.ActionHandler
import com.navi.insurance.common.util.NavigationHandler
import com.navi.insurance.network.ApiErrorTagType import com.navi.insurance.network.ApiErrorTagType
import com.navi.insurance.static_digital_claim.model.DigitalClaimScreenResponse import com.navi.insurance.static_digital_claim.model.DigitalClaimScreenResponse
import com.navi.insurance.static_digital_claim.repository.DigitalClaimRepository import com.navi.insurance.static_digital_claim.repository.DigitalClaimRepository
@@ -83,7 +83,7 @@ constructor(
_responseDataFlow.emit(ResponseState.Loading) _responseDataFlow.emit(ResponseState.Loading)
val cachedResponse = val cachedResponse =
naviCacheRepository.getDataOrFetchFromAltSource( naviCacheRepository.getDataOrFetchFromAltSource(
NavigationHandler.URL_STATIC_DIGITAL_CLAIM, DBCacheConstants.DIGITAL_CLAIMS_PRE_PURCHASE_DATA,
getDataFromAltSource = { getPageResponseFromAPI() } getDataFromAltSource = { getPageResponseFromAPI() }
) )
val response = val response =
@@ -135,7 +135,7 @@ constructor(
return NaviCacheAltSourceEntity( return NaviCacheAltSourceEntity(
value = Gson().toJson(response.data!!), value = Gson().toJson(response.data!!),
isSuccess = true, isSuccess = true,
version = 1 version = response.data?.pageVersion ?: 1
) )
} }

View File

@@ -25,7 +25,7 @@ export const get = async <T>(
baseURL: baseUrl ? baseUrl : BASE_URL, baseURL: baseUrl ? baseUrl : BASE_URL,
timeout: 10000, timeout: 10000,
headers: requestConfig.headers, headers: requestConfig.headers,
signal: newAbortSignal(11000) signal: newAbortSignal(11000),
}); });
addBundleVersionToHeader(axiosInstance); addBundleVersionToHeader(axiosInstance);
@@ -52,7 +52,7 @@ export const post = async <T>(
baseURL: baseUrl ? baseUrl : BASE_URL, baseURL: baseUrl ? baseUrl : BASE_URL,
timeout: 10000, timeout: 10000,
headers: requestConfig.headers, headers: requestConfig.headers,
signal: newAbortSignal(11000) signal: newAbortSignal(11000),
}); });
addBundleVersionToHeader(axiosInstance); addBundleVersionToHeader(axiosInstance);
@@ -83,7 +83,7 @@ export const patch = async <T>(
baseURL: baseUrl ? baseUrl : BASE_URL, baseURL: baseUrl ? baseUrl : BASE_URL,
timeout: 10000, timeout: 10000,
headers: requestConfig.headers, headers: requestConfig.headers,
signal: newAbortSignal(11000) signal: newAbortSignal(11000),
}); });
addBundleVersionToHeader(axiosInstance); addBundleVersionToHeader(axiosInstance);