TP-62634 | Selective-refresh (#10718)
Co-authored-by: Hitesh <hitesh.kumar@navi.com> Co-authored-by: namankhurmi <naman.khurmi@navi.com>
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
package com.naviapp.home.common.actions
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import com.navi.base.utils.orTrue
|
||||
import com.navi.common.uitron.model.action.V3HomeAction
|
||||
import com.naviapp.analytics.utils.NaviAnalytics
|
||||
import com.naviapp.home.compose.activity.HomePageActivity
|
||||
import com.naviapp.home.viewmodel.HomeVM
|
||||
|
||||
@Composable
|
||||
fun HandleApiAction(
|
||||
viewModel: HomeVM,
|
||||
activity: HomePageActivity,
|
||||
naviAnalyticsEventTracker: NaviAnalytics.Home,
|
||||
isPaymentLoaderShowing: Boolean
|
||||
) {
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.getActionCallback().collect { action ->
|
||||
when (action) {
|
||||
is V3HomeAction -> {
|
||||
viewModel.fetchCards(
|
||||
showLoader = action.showLoader.orTrue(),
|
||||
naviAnalyticsEventTracker = naviAnalyticsEventTracker,
|
||||
activity = activity,
|
||||
isPaymentLoaderShowing = isPaymentLoaderShowing
|
||||
)
|
||||
}
|
||||
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package com.naviapp.home.common.handler
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import com.navi.ap.common.handler.HandlePublishEventAction
|
||||
import com.navi.common.managers.PermissionsManager
|
||||
import com.naviapp.analytics.utils.NaviAnalytics
|
||||
import com.naviapp.home.common.actions.HandleApiAction
|
||||
import com.naviapp.home.compose.activity.HomePageActivity
|
||||
import com.naviapp.home.viewmodel.HomeVM
|
||||
|
||||
@Composable
|
||||
fun InitActionsHandler(
|
||||
viewModel: HomeVM,
|
||||
activity: HomePageActivity,
|
||||
naviAnalyticsEventTracker: NaviAnalytics.Home,
|
||||
isPaymentLoaderShowing: Boolean
|
||||
) {
|
||||
HandlePublishEventAction(viewModel = viewModel)
|
||||
HandleApiAction(
|
||||
viewModel = viewModel,
|
||||
activity = activity,
|
||||
naviAnalyticsEventTracker = naviAnalyticsEventTracker,
|
||||
isPaymentLoaderShowing = isPaymentLoaderShowing
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
package com.naviapp.home.common.handler
|
||||
|
||||
import androidx.compose.runtime.mutableStateMapOf
|
||||
import com.navi.common.model.common.WidgetResponse
|
||||
import com.navi.naviwidgets.models.NaviUiTronWidget
|
||||
import com.navi.naviwidgets.models.NaviWidget
|
||||
import com.naviapp.home.model.WidgetUiState
|
||||
import com.naviapp.home.model.WidgetUiStateData
|
||||
import javax.inject.Inject
|
||||
|
||||
typealias WidgetId = String
|
||||
|
||||
class HomePageDataUpdateHandler @Inject constructor() {
|
||||
|
||||
//List to observe existingHomePageData
|
||||
private var existingHomePageWidgets: MutableList<NaviWidget> = mutableListOf()
|
||||
private val widgetUiStateMap = mutableStateMapOf<WidgetId, WidgetUiStateData>()
|
||||
private var homeContentList = mutableListOf<WidgetId>()
|
||||
|
||||
fun updateHomePageData(
|
||||
widgetResponse: WidgetResponse,
|
||||
updateHomePageSuccess: (WidgetResponse) -> Unit
|
||||
) {
|
||||
val content = widgetResponse.contentWidget
|
||||
when (widgetUiStateMap.isEmpty()) {
|
||||
true -> {
|
||||
if (content != null) {
|
||||
val homeContent = mutableListOf<WidgetId>()
|
||||
content.forEach { naviWidget ->
|
||||
val widgetId = naviWidget.widgetId.orEmpty()
|
||||
homeContent.add(widgetId)
|
||||
widgetUiStateMap[widgetId] = WidgetUiStateData(
|
||||
widgetUiState = WidgetUiState.VISIBLE,
|
||||
widgetData = (naviWidget as NaviUiTronWidget).uiTronWidget
|
||||
)
|
||||
}
|
||||
homeContentList = homeContent
|
||||
}
|
||||
}
|
||||
|
||||
false -> {
|
||||
if (content != null) {
|
||||
homeContentList = constructHomePageList(
|
||||
oldData = existingHomePageWidgets.toMutableList(),
|
||||
newData = content.toMutableList()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Update home data
|
||||
updateHomePageSuccess.invoke(widgetResponse)
|
||||
//Replace the existing widgets to new once
|
||||
existingHomePageWidgets =
|
||||
content?.toMutableList() ?: mutableListOf()
|
||||
}
|
||||
|
||||
private fun constructHomePageList(
|
||||
oldData: MutableList<NaviWidget>,
|
||||
newData: MutableList<NaviWidget>
|
||||
): MutableList<WidgetId> {
|
||||
val updatedHomeList: MutableList<WidgetId> = mutableListOf()
|
||||
val seenElements = mutableSetOf<WidgetId>()
|
||||
//For addition of new widgets compared to previous data
|
||||
newData.forEach { naviWidget ->
|
||||
val widgetId = naviWidget.widgetId.orEmpty()
|
||||
seenElements.add(widgetId)
|
||||
val uiState = if (oldData.any { it.widgetId.orEmpty() == widgetId }) {
|
||||
WidgetUiState.VISIBLE
|
||||
} else {
|
||||
WidgetUiState.NEWLY_ADDED
|
||||
}
|
||||
widgetUiStateMap[widgetId] = WidgetUiStateData(
|
||||
uiState,
|
||||
(naviWidget as NaviUiTronWidget).uiTronWidget
|
||||
)
|
||||
updatedHomeList.add(widgetId)
|
||||
}
|
||||
//For deletion of existing widgets compared to new data
|
||||
oldData.forEachIndexed { index, naviWidget ->
|
||||
val widgetId = naviWidget.widgetId.orEmpty()
|
||||
if (widgetId !in seenElements) {
|
||||
seenElements.add(widgetId)
|
||||
widgetUiStateMap[widgetId]?.apply {
|
||||
widgetUiState = WidgetUiState.NOT_VISIBLE
|
||||
}
|
||||
updatedHomeList.getOrNull(index)?.let {
|
||||
updatedHomeList.add(index, widgetId)
|
||||
} ?: updatedHomeList.add(widgetId)
|
||||
}
|
||||
}
|
||||
return updatedHomeList
|
||||
}
|
||||
|
||||
fun getWidgetUiStateMap() = widgetUiStateMap
|
||||
|
||||
fun getHomeContentList() = homeContentList
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.naviapp.home.common.handler
|
||||
|
||||
import com.navi.uitron.model.action.PublishEventAction
|
||||
import com.navi.uitron.model.action.PublishableEventData
|
||||
import com.navi.uitron.viewmodel.UiTronViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
class SelectiveRefreshHandler @Inject constructor() {
|
||||
|
||||
fun handleSuccessState(homeVM: UiTronViewModel) {
|
||||
homeVM.handleAction(
|
||||
PublishEventAction(
|
||||
events = listOf(
|
||||
PublishableEventData(
|
||||
eventName = SELECTIVE_REFRESH_SUCCESS,
|
||||
stateKey = SUCCESS_STATE
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun handleErrorState(homeVM: UiTronViewModel) {
|
||||
homeVM.handleAction(
|
||||
PublishEventAction(
|
||||
events = listOf(
|
||||
PublishableEventData(
|
||||
eventName = SELECTIVE_REFRESH_ERROR,
|
||||
stateKey = ERROR_STATE
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
fun handleLoadingState(homeVM: UiTronViewModel) {
|
||||
homeVM.handleAction(
|
||||
PublishEventAction(
|
||||
events = listOf(
|
||||
PublishableEventData(
|
||||
eventName = SELECTIVE_REFRESH_LOADING,
|
||||
stateKey = LOADING_STATE
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val SELECTIVE_REFRESH_SUCCESS = "selective_refresh_success"
|
||||
const val SELECTIVE_REFRESH_ERROR = "selective_refresh_error"
|
||||
const val SELECTIVE_REFRESH_LOADING = "selective_refresh_loading"
|
||||
const val SUCCESS_STATE = "success_state"
|
||||
const val ERROR_STATE = "error_state"
|
||||
const val LOADING_STATE = "loading_state"
|
||||
}
|
||||
}
|
||||
@@ -30,6 +30,7 @@ import com.naviapp.analytics.utils.NaviAnalytics
|
||||
import com.naviapp.common.viewmodel.BottomNavBarVM
|
||||
import com.naviapp.common.viewmodel.InAppUpdateVM
|
||||
import com.naviapp.dashboard.viewmodels.DashboardSharedVM
|
||||
import com.naviapp.home.common.handler.InitActionsHandler
|
||||
import com.naviapp.home.compose.components.BottomBarWithFabButton
|
||||
import com.naviapp.home.compose.components.HomePageNavHost
|
||||
import com.naviapp.home.compose.homescreen.onTabClick
|
||||
@@ -62,6 +63,12 @@ fun HomePageActivityMainScreen(
|
||||
) {
|
||||
val navController = rememberNavController()
|
||||
val selectedTabId by sharedVM.selectedTabId.collectAsStateWithLifecycle()
|
||||
InitActionsHandler(
|
||||
viewModel = homeVM,
|
||||
activity = homePageActivity,
|
||||
naviAnalyticsEventTracker = naviHomeAnalytics,
|
||||
isPaymentLoaderShowing = paymentVM.isPaymentLoaderShowing()
|
||||
)
|
||||
|
||||
LaunchedEffect(key1 = selectedTabId) {
|
||||
onTabSelected.invoke(selectedTabId)
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
package com.naviapp.home.compose.homescreen
|
||||
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.LocalOverscrollConfiguration
|
||||
import androidx.compose.foundation.ScrollState
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredHeight
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
@@ -22,6 +25,8 @@ import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -33,6 +38,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.airbnb.lottie.compose.LottieAnimation
|
||||
import com.airbnb.lottie.compose.LottieCompositionSpec
|
||||
import com.airbnb.lottie.compose.LottieConstants
|
||||
import com.airbnb.lottie.compose.rememberLottieComposition
|
||||
import com.navi.base.sharedpref.PreferenceManager
|
||||
import com.navi.common.model.common.CollapsingTopNavConfig
|
||||
@@ -40,134 +46,119 @@ import com.navi.common.model.common.Header
|
||||
import com.navi.common.uitron.model.action.LayoutStateIds
|
||||
import com.navi.common.uitron.model.action.UpdateViewStateActionV2
|
||||
import com.navi.common.utils.getSessionId
|
||||
import com.navi.naviwidgets.models.NaviUiTronWidget
|
||||
import com.navi.naviwidgets.models.NaviWidget
|
||||
import com.navi.pay.utils.conditional
|
||||
import com.navi.uitron.UiTronSdkManager
|
||||
import com.navi.uitron.render.UiTronRenderer
|
||||
import com.navi.uitron.utils.toPx
|
||||
import com.naviapp.R
|
||||
import com.naviapp.analytics.utils.NaviAnalytics
|
||||
import com.naviapp.home.common.handler.WidgetId
|
||||
import com.naviapp.home.compose.extension.bottomShadow
|
||||
import com.naviapp.home.utils.shimmerEffect
|
||||
import com.naviapp.home.model.WidgetUiState
|
||||
import com.naviapp.home.utils.getHomeWidgetAnimationSpec
|
||||
import com.naviapp.home.viewmodel.HomeVM
|
||||
import com.naviapp.utils.Constants
|
||||
import com.naviapp.utils.Constants.SECTION_CONTENT_SIZE
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/*
|
||||
Order of composition: UpperContent and ContentLoader Lottie -> BackLayer and TopAppBar -> UpperMiddleContent -> LowerMiddleContent -> LowerContent -> ProfileScreen
|
||||
Added some delay after rendering each layer to get some time to take clicks
|
||||
/**
|
||||
* Order of composition:
|
||||
*
|
||||
* 1. UpperContent and ContentLoader Lottie
|
||||
* 2. BackLayer and TopAppBar
|
||||
* 3. UpperMiddleContent
|
||||
* 4. LowerMiddleContent
|
||||
* 5. LowerContent
|
||||
* 6. ProfileScreen
|
||||
*/
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
fun frontLayerContent(modifier: Modifier, frontLayerData: List<NaviWidget>?, homeVM: HomeVM) =
|
||||
@Composable {
|
||||
CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
|
||||
Column(modifier) {
|
||||
frontLayerData?.let {
|
||||
UpperContent(homeVM, it.take(homeVM.upperContentSize))
|
||||
fun FrontLayerContent(
|
||||
modifier: Modifier,
|
||||
isShimmerVisible: Boolean,
|
||||
homeVM: HomeVM
|
||||
) {
|
||||
CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
|
||||
Column(modifier) {
|
||||
if (isShimmerVisible.not()) {
|
||||
val widgetOrderList = homeVM.getHomeContentList()
|
||||
Column {
|
||||
UpperContent(homeVM) {
|
||||
RenderUiTronContent(widgetOrderList.take(homeVM.upperContentSize), homeVM)
|
||||
|
||||
val middleAndLowerContent = it.drop(homeVM.upperContentSize)
|
||||
UpperMiddleContent(homeVM, middleAndLowerContent.take(SECTION_CONTENT_SIZE))
|
||||
}
|
||||
val middleAndLowerContent = widgetOrderList.drop(homeVM.upperContentSize)
|
||||
UpperMiddleContent(homeVM) {
|
||||
RenderUiTronContent(
|
||||
middleAndLowerContent.take(Constants.SECTION_CONTENT_SIZE), homeVM
|
||||
)
|
||||
}
|
||||
val lowerContent = middleAndLowerContent.drop(Constants.SECTION_CONTENT_SIZE)
|
||||
LowerMiddleContent(homeVM) {
|
||||
RenderUiTronContent(
|
||||
lowerContent.take(Constants.SECTION_CONTENT_SIZE), homeVM
|
||||
)
|
||||
|
||||
val lowerContent = middleAndLowerContent.drop(SECTION_CONTENT_SIZE)
|
||||
LowerMiddleContent(homeVM, lowerContent.take(SECTION_CONTENT_SIZE))
|
||||
LowerContent(homeVM, lowerContent.drop(SECTION_CONTENT_SIZE))
|
||||
|
||||
ContentLoaderLottie(homeVM)
|
||||
} ?: run {
|
||||
DefaultContentShimmer()
|
||||
}
|
||||
LowerContent(homeVM) {
|
||||
RenderUiTronContent(
|
||||
lowerContent.drop(Constants.SECTION_CONTENT_SIZE), homeVM
|
||||
)
|
||||
}
|
||||
ContentLoaderLottie(homeVM = homeVM)
|
||||
}
|
||||
} else run {
|
||||
HomePageContentShimmer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun DefaultContentShimmer() {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp, end = 16.dp)
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(34.dp)
|
||||
.width(94.dp)
|
||||
.shimmerEffect()
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(34.dp)
|
||||
.width(148.dp)
|
||||
.shimmerEffect()
|
||||
)
|
||||
private fun RenderUiTronContent(
|
||||
elementList: List<WidgetId>,
|
||||
homeVM: HomeVM
|
||||
) {
|
||||
elementList.forEach { element ->
|
||||
key(element) {
|
||||
val widgetUiMap = homeVM.getWidgetUiStateMap()
|
||||
val visible = remember(widgetUiMap[element]?.widgetUiState) {
|
||||
mutableStateOf(widgetUiMap[element]?.widgetUiState == WidgetUiState.VISIBLE)
|
||||
}
|
||||
LaunchedEffect(widgetUiMap[element]?.widgetUiState) {
|
||||
if (widgetUiMap[element]?.widgetUiState == WidgetUiState.NEWLY_ADDED) {
|
||||
visible.value = true
|
||||
widgetUiMap[element].apply {
|
||||
this?.widgetUiState = WidgetUiState.VISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
AnimatedContainerForWidgets(isVisible = visible) {
|
||||
val response = homeVM.getWidgetUiStateMap()[element]?.widgetData
|
||||
UiTronRenderer(
|
||||
dataMap = response?.data, uiTronViewModel = homeVM
|
||||
).Render(composeViews = response?.parentComposeView.orEmpty())
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(108.dp)
|
||||
.fillMaxWidth()
|
||||
.shimmerEffect()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(24.dp)
|
||||
.width(176.dp)
|
||||
.shimmerEffect()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(132.dp)
|
||||
.fillMaxWidth()
|
||||
.shimmerEffect()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(24.dp)
|
||||
.width(120.dp)
|
||||
.shimmerEffect()
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(104.dp)
|
||||
.fillMaxWidth()
|
||||
.shimmerEffect()
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(78.dp)
|
||||
.fillMaxWidth()
|
||||
.shimmerEffect()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(24.dp)
|
||||
.width(176.dp)
|
||||
.shimmerEffect()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(132.dp)
|
||||
.fillMaxWidth()
|
||||
.shimmerEffect()
|
||||
)
|
||||
@Composable
|
||||
private fun AnimatedContainerForWidgets(
|
||||
isVisible: MutableState<Boolean>, homeWidget: @Composable () -> Unit
|
||||
) {
|
||||
AnimatedVisibility(visible = isVisible.value, enter = expandVertically(
|
||||
expandFrom = Alignment.Top,
|
||||
clip = true,
|
||||
animationSpec = getHomeWidgetAnimationSpec()
|
||||
) { 0 } + fadeIn(animationSpec = getHomeWidgetAnimationSpec()),
|
||||
exit = shrinkVertically(
|
||||
shrinkTowards = Alignment.Bottom,
|
||||
clip = true,
|
||||
animationSpec = getHomeWidgetAnimationSpec()
|
||||
) { 0 } + fadeOut(animationSpec = getHomeWidgetAnimationSpec())) {
|
||||
homeWidget()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,7 +181,7 @@ fun ContentLoaderLottie(
|
||||
)
|
||||
LottieAnimation(
|
||||
composition = composition,
|
||||
iterations = 5000
|
||||
iterations = LottieConstants.IterateForever
|
||||
)
|
||||
}
|
||||
} else {
|
||||
@@ -201,27 +192,27 @@ fun ContentLoaderLottie(
|
||||
@Composable
|
||||
fun UpperContent(
|
||||
homeVM: HomeVM,
|
||||
content: List<NaviWidget>,
|
||||
upperContent: @Composable () -> Unit,
|
||||
) {
|
||||
LaunchedEffect(Unit) {
|
||||
homeVM.logAppLaunchTimeEvent(Constants.HOME_SCREEN_IN_CAPS)
|
||||
// Show back layer and top app bar
|
||||
homeVM.setIsReadyToRenderBackLayerAndTopAppBar(true)
|
||||
}
|
||||
RenderUiTronContent(content = content, homeVM = homeVM)
|
||||
upperContent()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun UpperMiddleContent(
|
||||
homeVM: HomeVM,
|
||||
content: List<NaviWidget>,
|
||||
upperMiddleContent: @Composable () -> Unit,
|
||||
) {
|
||||
val showUpperMiddleLayer by homeVM.isReadyToRenderUpperMiddleContent.collectAsStateWithLifecycle()
|
||||
if (showUpperMiddleLayer) {
|
||||
LaunchedEffect(Unit) {
|
||||
homeVM.setIsReadyToRenderLowerMiddleContent(true)
|
||||
}
|
||||
RenderUiTronContent(content = content, homeVM = homeVM)
|
||||
upperMiddleContent()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,21 +220,21 @@ fun UpperMiddleContent(
|
||||
@Composable
|
||||
fun LowerMiddleContent(
|
||||
homeVM: HomeVM,
|
||||
content: List<NaviWidget>,
|
||||
lowerMiddleContent: @Composable () -> Unit,
|
||||
) {
|
||||
val showLowerMiddleLayer by homeVM.isReadyToRenderLowerMiddleContent.collectAsStateWithLifecycle()
|
||||
if (showLowerMiddleLayer) {
|
||||
LaunchedEffect(Unit) {
|
||||
homeVM.setIsReadyToRenderLowerContent(true)
|
||||
}
|
||||
RenderUiTronContent(content = content, homeVM = homeVM)
|
||||
lowerMiddleContent()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LowerContent(
|
||||
homeVM: HomeVM,
|
||||
content: List<NaviWidget>,
|
||||
lowerContent: @Composable () -> Unit,
|
||||
) {
|
||||
val showLowerFrontLayer by homeVM.isReadyToRenderLowerContent.collectAsStateWithLifecycle()
|
||||
if (showLowerFrontLayer) {
|
||||
@@ -252,41 +243,28 @@ fun LowerContent(
|
||||
homeVM.setIsShowContentLoader(false)
|
||||
homeVM.setIsReadyToRenderProfileScreen(true)
|
||||
}
|
||||
RenderUiTronContent(content = content, homeVM = homeVM)
|
||||
lowerContent()
|
||||
homeVM.logDnDataDisplayedEvent()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RenderUiTronContent(
|
||||
content: List<NaviWidget>,
|
||||
homeVM: HomeVM
|
||||
) {
|
||||
content.forEach { item ->
|
||||
if (item is NaviUiTronWidget) {
|
||||
UiTronRenderer(item.uiTronWidget?.data, homeVM)
|
||||
.Render(composeViews = item.uiTronWidget?.parentComposeView.orEmpty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun backLayerComposable(modifier: Modifier, backLayerData: Header?, homeVM: HomeVM) =
|
||||
@Composable {
|
||||
Column(modifier.fillMaxWidth()) {
|
||||
val isShowBackLayer by homeVM.isReadyToRenderBackLayerAndTopAppBar.collectAsStateWithLifecycle()
|
||||
if (isShowBackLayer) {
|
||||
LaunchedEffect(Unit) {
|
||||
homeVM.setIsReadyToRenderUpperMiddleContent(true)
|
||||
}
|
||||
backLayerData?.collapsingTopNav?.uiTronResponse?.let {
|
||||
UiTronRenderer(
|
||||
it.data,
|
||||
homeVM
|
||||
).Render(composeViews = it.parentComposeView.orEmpty())
|
||||
}
|
||||
fun BackLayerComposable(modifier: Modifier, backLayerData: Header?, homeVM: HomeVM) {
|
||||
Column(modifier.fillMaxWidth()) {
|
||||
val isShowBackLayer by homeVM.isReadyToRenderBackLayerAndTopAppBar.collectAsStateWithLifecycle()
|
||||
if (isShowBackLayer) {
|
||||
LaunchedEffect(Unit) {
|
||||
homeVM.setIsReadyToRenderUpperMiddleContent(true)
|
||||
}
|
||||
backLayerData?.collapsingTopNav?.uiTronResponse?.let {
|
||||
UiTronRenderer(
|
||||
it.data,
|
||||
homeVM
|
||||
).Render(composeViews = it.parentComposeView.orEmpty())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun topBarLayout(
|
||||
appBarHeight: Dp,
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.naviapp.home.compose.homescreen
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.naviapp.home.utils.shimmerEffect
|
||||
|
||||
@Composable
|
||||
fun HomePageContentShimmer() {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(start = 16.dp, end = 16.dp)
|
||||
) {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(34.dp)
|
||||
.width(94.dp)
|
||||
.shimmerEffect()
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(34.dp)
|
||||
.width(148.dp)
|
||||
.shimmerEffect()
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(108.dp)
|
||||
.fillMaxWidth()
|
||||
.shimmerEffect()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(24.dp)
|
||||
.width(176.dp)
|
||||
.shimmerEffect()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(132.dp)
|
||||
.fillMaxWidth()
|
||||
.shimmerEffect()
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(24.dp)
|
||||
.width(120.dp)
|
||||
.shimmerEffect()
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(104.dp)
|
||||
.fillMaxWidth()
|
||||
.shimmerEffect()
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(78.dp)
|
||||
.fillMaxWidth()
|
||||
.shimmerEffect()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(24.dp)
|
||||
.width(176.dp)
|
||||
.shimmerEffect()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.height(132.dp)
|
||||
.fillMaxWidth()
|
||||
.shimmerEffect()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -167,28 +167,31 @@ fun HomeScreenScaffold(
|
||||
|
||||
NaviHomePageScaffold(
|
||||
appBar =
|
||||
topBarLayout(
|
||||
appBarHeight = appBarHeight,
|
||||
statusBarHeight = statusBarHeight,
|
||||
topBarData = widgetResponse.header,
|
||||
homeVM = homeVM,
|
||||
listState = listState
|
||||
),
|
||||
backLayer =
|
||||
backLayerComposable(
|
||||
topBarLayout(
|
||||
appBarHeight = appBarHeight,
|
||||
statusBarHeight = statusBarHeight,
|
||||
topBarData = widgetResponse.header,
|
||||
homeVM = homeVM,
|
||||
listState = listState
|
||||
),
|
||||
backLayer = {
|
||||
BackLayerComposable(
|
||||
modifier = Modifier.requiredHeight(backLayerHeight),
|
||||
backLayerData = widgetResponse.header,
|
||||
homeVM = homeVM
|
||||
),
|
||||
frontLayer =
|
||||
frontLayerContent(
|
||||
)
|
||||
},
|
||||
frontLayer = {
|
||||
FrontLayerContent(
|
||||
modifier =
|
||||
Modifier.fillMaxHeight()
|
||||
.background(Color.White, frontLayerShapeRadius)
|
||||
.verticalScroll(listState()),
|
||||
frontLayerData = widgetResponse.contentWidget,
|
||||
Modifier
|
||||
.fillMaxHeight()
|
||||
.background(Color.White, frontLayerShapeRadius)
|
||||
.verticalScroll(listState()),
|
||||
isShimmerVisible = widgetResponse.contentWidget.isNullOrEmpty(),
|
||||
homeVM = homeVM
|
||||
),
|
||||
)
|
||||
},
|
||||
concealedHeight = appBarHeight,
|
||||
revealedHeight = backLayerHeight - curvature,
|
||||
gesturesEnabled = gesturesEnabled.value,
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.naviapp.home.model
|
||||
|
||||
import com.navi.uitron.model.UiTronResponse
|
||||
|
||||
enum class WidgetUiState {
|
||||
VISIBLE,
|
||||
NOT_VISIBLE,
|
||||
NEWLY_ADDED
|
||||
}
|
||||
|
||||
data class WidgetUiStateData(
|
||||
var widgetUiState: WidgetUiState,
|
||||
var widgetData: UiTronResponse? = null
|
||||
)
|
||||
@@ -1,5 +1,7 @@
|
||||
package com.naviapp.home.utils
|
||||
|
||||
import androidx.compose.animation.core.CubicBezierEasing
|
||||
import androidx.compose.animation.core.FiniteAnimationSpec
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.infiniteRepeatable
|
||||
@@ -118,4 +120,8 @@ fun DrawIcon(
|
||||
painter = painter,
|
||||
contentDescription = content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> getHomeWidgetAnimationSpec(): FiniteAnimationSpec<T> {
|
||||
return tween(500, easing = CubicBezierEasing(0.83f, 0.17f, 0.23f, 0.89f))
|
||||
}
|
||||
|
||||
@@ -76,6 +76,8 @@ import com.naviapp.common.transformer.AppLoadTimerMapper
|
||||
import com.naviapp.common.viewmodel.BottomNavBarVM
|
||||
import com.naviapp.common.viewmodel.InAppUpdateVM
|
||||
import com.naviapp.home.activity.InAppNotificationActivity
|
||||
import com.naviapp.home.common.handler.HomePageDataUpdateHandler
|
||||
import com.naviapp.home.common.handler.SelectiveRefreshHandler
|
||||
import com.naviapp.home.compose.activity.HomePageActivity
|
||||
import com.naviapp.home.compose.listener.HomeScreenCallbackListener
|
||||
import com.naviapp.home.compose.model.BottomStickyNudgeState
|
||||
@@ -129,7 +131,9 @@ constructor(
|
||||
private val gson: Gson,
|
||||
private val naviCacheRepository: NaviCacheRepositoryImpl,
|
||||
private val connectivityObserver: ConnectivityObserver,
|
||||
private val naviPayCustomerStatusHandler: NaviPayCustomerStatusHandler
|
||||
private val naviPayCustomerStatusHandler: NaviPayCustomerStatusHandler,
|
||||
private val selectiveRefreshHandler: SelectiveRefreshHandler,
|
||||
private val homePageDataUpdateHandler: HomePageDataUpdateHandler
|
||||
) : BaseVM() {
|
||||
|
||||
private val _isReadyToRenderBackLayerAndTopAppBar = MutableStateFlow(false)
|
||||
@@ -418,7 +422,9 @@ constructor(
|
||||
) {
|
||||
val needToShowLoader =
|
||||
TemporaryStorageHelper.isDataModified(screen = TemporaryStorageHelper.HOME)
|
||||
|
||||
if (showLoader) {
|
||||
selectiveRefreshHandler.handleLoadingState(this)
|
||||
}
|
||||
if (needToShowLoader) {
|
||||
coroutineScope.launch(Dispatchers.IO) {
|
||||
val naviCacheAltSourceEntity =
|
||||
@@ -438,8 +444,12 @@ constructor(
|
||||
)
|
||||
)
|
||||
deserializeWidgetResponseString(response = getHomeTabFromDatabase())?.let { responseData ->
|
||||
_homeScreenState.update { HomeScreenState.Success(responseData) }
|
||||
homePageDataUpdateHandler.updateHomePageData(
|
||||
responseData,
|
||||
::updateHomePageSuccess
|
||||
)
|
||||
_homeScreenExtraData.emit(HomePageExtraData(responseData.extraData))
|
||||
selectiveRefreshHandler.handleSuccessState(this@HomeVM)
|
||||
}
|
||||
updateApiTsForHomePage()
|
||||
|
||||
@@ -463,7 +473,7 @@ constructor(
|
||||
val homeTabFromDatabase = getHomeTabFromDatabase()
|
||||
|
||||
naviCacheRepository
|
||||
.getDataAndFetchFromAltSource(
|
||||
.getDataAndFetchFromAltSourceWithDetails(
|
||||
key = NaviSharedDbKeys.HOME_TAB.name,
|
||||
version = BuildConfig.VERSION_CODE.toLong(),
|
||||
getDataFromAltSource = {
|
||||
@@ -482,28 +492,36 @@ constructor(
|
||||
emitMultipleValues = true,
|
||||
)
|
||||
.collect { response ->
|
||||
deserializeWidgetResponseString(response)?.let { widgetResponse ->
|
||||
deserializeWidgetResponseString(response.data)?.let { widgetResponse ->
|
||||
try {
|
||||
if (
|
||||
showLoader.not() &&
|
||||
getHomeScreenData().isNotNull() &&
|
||||
response.value == homeTabFromDatabase?.value
|
||||
response.data?.value == homeTabFromDatabase?.value
|
||||
) {
|
||||
updateCachedResponse(true)
|
||||
} else {
|
||||
_homeScreenState.update {
|
||||
HomeScreenState.Success(
|
||||
widgetResponse
|
||||
if (response.isCurrentAndAltDataSame.orFalse().not()) {
|
||||
homePageDataUpdateHandler.updateHomePageData(
|
||||
widgetResponse,
|
||||
::updateHomePageSuccess
|
||||
)
|
||||
_homeScreenExtraData.emit(
|
||||
HomePageExtraData(
|
||||
widgetResponse.extraData
|
||||
)
|
||||
)
|
||||
updateApiTsForHomePage()
|
||||
response.data?.updatedAt?.let { timestamp ->
|
||||
homeTabLastUpdateTimestamp = timestamp
|
||||
}
|
||||
preCacheLottieUrls(widgetResponse)
|
||||
}
|
||||
_homeScreenExtraData.emit(HomePageExtraData(widgetResponse.extraData))
|
||||
updateApiTsForHomePage()
|
||||
response.updatedAt.let { timestamp ->
|
||||
homeTabLastUpdateTimestamp = timestamp
|
||||
if (response.isComingFromAltSource.orFalse()) {
|
||||
selectiveRefreshHandler.handleSuccessState(this@HomeVM)
|
||||
}
|
||||
}
|
||||
|
||||
preCacheLottieUrls(widgetResponse)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.log()
|
||||
}
|
||||
@@ -514,6 +532,18 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateHomePageSuccess(widgetResponse: WidgetResponse) {
|
||||
_homeScreenState.update {
|
||||
HomeScreenState.Success(
|
||||
widgetResponse
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun getWidgetUiStateMap() = homePageDataUpdateHandler.getWidgetUiStateMap()
|
||||
|
||||
fun getHomeContentList() = homePageDataUpdateHandler.getHomeContentList()
|
||||
|
||||
private fun deserializeWidgetResponseString(response: NaviCacheEntity?): WidgetResponse? =
|
||||
getGsonBuilderForWidgetizedResponse().fromJson(response?.value, WidgetResponse::class.java)
|
||||
|
||||
@@ -585,27 +615,30 @@ constructor(
|
||||
naviUpiDeviceFingerprint = deviceInfoDetails.deviceFingerPrint,
|
||||
ssid = deviceInfoDetails.provider.ssid
|
||||
)
|
||||
|
||||
if (!response.isValidResponse()) {
|
||||
if (
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.N &&
|
||||
!connectivityObserver.isInternetConnected()
|
||||
) {
|
||||
_connectivityObserverHolder.emit(ConnectivityObserver.Status.Unavailable)
|
||||
} else if (response.error?.statusCode == API_CODE_SOCKET_TIMEOUT) {
|
||||
selectiveRefreshHandler.handleErrorState(this)
|
||||
} else if (
|
||||
response.error?.statusCode != API_CODE_UNKNOWN_HOST &&
|
||||
response.error?.statusCode != API_CODE_CONNECT_EXCEPTION &&
|
||||
response.error?.statusCode != NO_INTERNET &&
|
||||
response.error?.statusCode != API_CODE_SOCKET_TIMEOUT
|
||||
response.error?.statusCode != NO_INTERNET
|
||||
) {
|
||||
val errorUnifiedResponse =
|
||||
getErrorUnifiedResponse(errors = response.errors, error = response.error)
|
||||
sendFailureEvent(NaviAnalytics.NEW_HOME_ACTIVITY, errorUnifiedResponse)
|
||||
_homeScreenState.emit(HomeScreenState.Error(errorUnifiedResponse.errorResponse))
|
||||
if ((homeScreenState.value is HomeScreenState.Success).not()) {
|
||||
_homeScreenState.emit(HomeScreenState.Error(errorUnifiedResponse.errorResponse))
|
||||
} else {
|
||||
selectiveRefreshHandler.handleErrorState(this)
|
||||
}
|
||||
}
|
||||
return NaviCacheAltSourceEntity(isSuccess = false)
|
||||
}
|
||||
|
||||
return NaviCacheAltSourceEntity(
|
||||
value = naviAppSerializerGsonBuilder().toJson(response.data),
|
||||
version = BuildConfig.VERSION_CODE,
|
||||
@@ -893,9 +926,7 @@ constructor(
|
||||
isPaymentLoaderShowing: Boolean,
|
||||
) {
|
||||
if (BaseUtils.isUserLoggedIn()) {
|
||||
if (showLoader && !isPaymentLoaderShowing && isProfileDrawerOpen.not()) {
|
||||
activity.showLoader()
|
||||
} else {
|
||||
if (showLoader.not() || isPaymentLoaderShowing || isProfileDrawerOpen) {
|
||||
naviAnalyticsEventTracker.onHomePageInit(
|
||||
System.currentTimeMillis() - analyticsStartTs
|
||||
)
|
||||
@@ -1067,6 +1098,7 @@ constructor(
|
||||
_topNavOffsetValue.update { value }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getHomeScreenData(): WidgetResponse? {
|
||||
return if (homeScreenState.value is HomeScreenState.Success) {
|
||||
(homeScreenState.value as HomeScreenState.Success).data
|
||||
|
||||
@@ -11,20 +11,20 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.navi.ap.common.viewmodel.ApplicationPlatformVM
|
||||
import com.navi.ap.utils.registerApUiTronDeSerializers
|
||||
import com.navi.base.utils.isNotNullAndNotEmpty
|
||||
import com.navi.uitron.model.action.PublishEventAction
|
||||
import com.navi.uitron.model.data.SubscriberEventData
|
||||
import com.navi.uitron.model.event.UiTronDataProviderFactory
|
||||
import com.navi.uitron.model.ui.BaseProperty
|
||||
import com.navi.uitron.viewmodel.UiTronViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun HandlePublishEventAction(
|
||||
viewModel: ApplicationPlatformVM,
|
||||
viewModel: UiTronViewModel,
|
||||
uiTronDataProviderFactory: UiTronDataProviderFactory = UiTronDataProviderFactory()
|
||||
) {
|
||||
|
||||
@@ -62,18 +62,14 @@ fun HandlePublishEventAction(
|
||||
}
|
||||
}
|
||||
|
||||
private fun handlePropertyUpdate(
|
||||
stateKey: String?,
|
||||
layoutId: String?,
|
||||
viewModel: ApplicationPlatformVM
|
||||
) {
|
||||
private fun handlePropertyUpdate(stateKey: String?, layoutId: String?, viewModel: UiTronViewModel) {
|
||||
stateKey?.let { viewModel.handle[layoutId + BaseProperty.PROPERTY_SUFFIX] = it }
|
||||
}
|
||||
|
||||
private fun handleDataUpdate(
|
||||
uiTronData: Any?,
|
||||
subscriberData: SubscriberEventData,
|
||||
viewModel: ApplicationPlatformVM,
|
||||
viewModel: UiTronViewModel,
|
||||
uiTronDataProviderFactory: UiTronDataProviderFactory
|
||||
) {
|
||||
uiTronData?.let {
|
||||
|
||||
@@ -95,7 +95,7 @@ moengage-rich-notification = "4.3.2"
|
||||
navi-alfred = "1.5.0"
|
||||
navi-guarddog = "1.10.0"
|
||||
navi-pulse = "1.3.0"
|
||||
navi-uitron = "1.7.0"
|
||||
navi-uitron = "1.8.0"
|
||||
navigation = "2.5.3"
|
||||
okhttp-bom = "4.12.0"
|
||||
otaliastudios-cameraview = "2.7.2"
|
||||
|
||||
14
android/navi-base/src/main/java/com/navi/base/cache/model/NaviCacheEntityDetails.kt
vendored
Normal file
14
android/navi-base/src/main/java/com/navi/base/cache/model/NaviCacheEntityDetails.kt
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2024 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.base.cache.model
|
||||
|
||||
data class NaviCacheEntityDetails(
|
||||
val data: NaviCacheEntity? = null,
|
||||
val isComingFromAltSource: Boolean? = null,
|
||||
val isCurrentAndAltDataSame: Boolean? = null
|
||||
)
|
||||
@@ -10,6 +10,7 @@ package com.navi.base.cache.repository
|
||||
import com.navi.base.cache.dao.NaviCacheDao
|
||||
import com.navi.base.cache.model.NaviCacheAltSourceEntity
|
||||
import com.navi.base.cache.model.NaviCacheEntity
|
||||
import com.navi.base.cache.model.NaviCacheEntityDetails
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
@@ -45,6 +46,22 @@ interface NaviCacheRepository {
|
||||
},
|
||||
emitMultipleValues: Boolean = false
|
||||
): Flow<NaviCacheEntity?>
|
||||
|
||||
fun getDataAndFetchFromAltSourceWithDetails(
|
||||
key: String,
|
||||
version: Long? = null,
|
||||
getDataFromAltSource: suspend () -> NaviCacheAltSourceEntity,
|
||||
isCurrentAndAltDataSame:
|
||||
(currentData: NaviCacheEntity?, altData: NaviCacheAltSourceEntity) -> Boolean =
|
||||
fun(currentData, altData): Boolean {
|
||||
val currentDataValue = currentData?.value ?: return false
|
||||
val altDataValue = altData.value ?: return false
|
||||
|
||||
return currentData.version == altData.version &&
|
||||
currentDataValue.hashCode() == altDataValue.hashCode()
|
||||
},
|
||||
emitMultipleValues: Boolean = false
|
||||
): Flow<NaviCacheEntityDetails?>
|
||||
}
|
||||
|
||||
class NaviCacheRepositoryImpl @Inject constructor(private val naviCacheDao: NaviCacheDao) :
|
||||
@@ -165,6 +182,62 @@ class NaviCacheRepositoryImpl @Inject constructor(private val naviCacheDao: Navi
|
||||
}
|
||||
}
|
||||
|
||||
override fun getDataAndFetchFromAltSourceWithDetails(
|
||||
key: String,
|
||||
version: Long?,
|
||||
getDataFromAltSource: suspend () -> NaviCacheAltSourceEntity,
|
||||
isCurrentAndAltDataSame:
|
||||
(currentData: NaviCacheEntity?, altData: NaviCacheAltSourceEntity) -> Boolean,
|
||||
emitMultipleValues: Boolean
|
||||
) = flow {
|
||||
var isValueEmitted = false
|
||||
|
||||
val currentValueInDB = get(key = key)
|
||||
|
||||
if (currentValueInDB != null) { // Its a valid value
|
||||
emit(NaviCacheEntityDetails(data = currentValueInDB, isComingFromAltSource = false))
|
||||
isValueEmitted = true
|
||||
}
|
||||
|
||||
val naviCacheValueEntityFromAltSource = getDataFromAltSource.invoke()
|
||||
|
||||
if (
|
||||
!naviCacheValueEntityFromAltSource.isSuccess ||
|
||||
naviCacheValueEntityFromAltSource.value == null
|
||||
) { // alternate source data invalid
|
||||
return@flow
|
||||
}
|
||||
|
||||
val naviCacheEntity =
|
||||
NaviCacheEntity(
|
||||
key = key,
|
||||
value = naviCacheValueEntityFromAltSource.value,
|
||||
version = naviCacheValueEntityFromAltSource.version ?: 1,
|
||||
ttl = naviCacheValueEntityFromAltSource.ttl,
|
||||
clearOnLogout = naviCacheValueEntityFromAltSource.clearOnLogout,
|
||||
metaData = naviCacheValueEntityFromAltSource.metaData
|
||||
)
|
||||
|
||||
val isDataSame =
|
||||
isCurrentAndAltDataSame(currentValueInDB, naviCacheValueEntityFromAltSource)
|
||||
|
||||
if (isDataSame.not()) {
|
||||
// Data from Alt source is different
|
||||
save(naviCacheEntity = naviCacheEntity)
|
||||
}
|
||||
|
||||
// If multiple values are required or no value is emitted yet then emit latest value
|
||||
if (emitMultipleValues || !isValueEmitted) {
|
||||
emit(
|
||||
NaviCacheEntityDetails(
|
||||
data = naviCacheEntity,
|
||||
isComingFromAltSource = true,
|
||||
isCurrentAndAltDataSame = isDataSame
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun checkIfDBValueIsValidElseRemoveEntry(
|
||||
naviCacheEntity: NaviCacheEntity?,
|
||||
version: Long?,
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.navi.common.uitron.model.action.RedeemCoinAction
|
||||
import com.navi.common.uitron.model.action.RewardsAndReferralApiAction
|
||||
import com.navi.common.uitron.model.action.SdkExitAction
|
||||
import com.navi.common.uitron.model.action.SubmitFeedbackAction
|
||||
import com.navi.common.uitron.model.action.V3HomeAction
|
||||
import com.navi.common.uitron.model.action.ValidateVpaAction
|
||||
import com.navi.uitron.deserializer.BaseUiTronTriggerApiActionDeserializer
|
||||
import com.navi.uitron.model.action.TriggerApiAction
|
||||
@@ -63,6 +64,7 @@ class UiTronTriggerApiActionDeserializer : BaseUiTronTriggerApiActionDeserialize
|
||||
context?.deserialize(jsonObject, ValidateVpaAction::class.java)
|
||||
ApiType.RedeemCoins.name ->
|
||||
context?.deserialize(jsonObject, RedeemCoinAction::class.java)
|
||||
ApiType.V3Home.name -> context?.deserialize(jsonObject, V3HomeAction::class.java)
|
||||
else -> super.deserialize(json, typeOfT, context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,7 +60,8 @@ enum class ApiType {
|
||||
SdkExitAction,
|
||||
RewardsAndReferralAction,
|
||||
ValidateVpa,
|
||||
RedeemCoins
|
||||
RedeemCoins,
|
||||
V3Home
|
||||
}
|
||||
|
||||
enum class SourceType {
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2024 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.common.uitron.model.action
|
||||
|
||||
import com.navi.uitron.model.action.TriggerApiAction
|
||||
|
||||
data class V3HomeAction(val showLoader: Boolean? = null) : TriggerApiAction()
|
||||
Reference in New Issue
Block a user