TP-74012 | Naman Khurmi | Top Nav Fixes (#11817)

This commit is contained in:
Naman Khurmi
2024-07-18 16:08:32 +05:30
committed by GitHub
parent 87f1b55927
commit ea694a39c9
6 changed files with 140 additions and 47 deletions

View File

@@ -14,6 +14,7 @@ import androidx.compose.animation.fadeOut
import androidx.compose.animation.shrinkVertically
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.LocalOverscrollConfiguration
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -39,9 +40,8 @@ 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.common.uitron.render.CommonCustomUiTronRenderer
import com.navi.naviwidgets.R
import com.navi.uitron.render.UiTronRenderer
import com.naviapp.home.compose.widgetfactory.HomeWidgetRenderer
import com.naviapp.home.model.WidgetUiState
import com.naviapp.home.utils.getHomeWidgetAnimationSpec
import com.naviapp.home.viewmodel.HomeVM
@@ -60,7 +60,12 @@ typealias WidgetId = String
*/
@Composable
@OptIn(ExperimentalFoundationApi::class, ExperimentalComposeUiApi::class)
fun FrontLayerContent(modifier: Modifier, isShimmerVisible: Boolean, homeVM: HomeVM) {
fun FrontLayerContent(
modifier: Modifier,
isShimmerVisible: Boolean,
homeVM: HomeVM,
homeScrollState: () -> ScrollState
) {
CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
Column(
modifier.semantics { testTagsAsResourceId = true }.testTag("homeScreenWidgetsList")
@@ -69,26 +74,33 @@ fun FrontLayerContent(modifier: Modifier, isShimmerVisible: Boolean, homeVM: Hom
val widgetOrderList = homeVM.getHomeContentList()
Column {
UpperContent(homeVM) {
RenderUiTronContent(widgetOrderList.take(homeVM.upperContentSize), homeVM)
RenderUiTronContent(
widgetOrderList.take(homeVM.upperContentSize),
homeVM,
homeScrollState
)
}
val middleAndLowerContent = widgetOrderList.drop(homeVM.upperContentSize)
UpperMiddleContent(homeVM) {
RenderUiTronContent(
middleAndLowerContent.take(Constants.SECTION_CONTENT_SIZE),
homeVM
homeVM,
homeScrollState
)
}
val lowerContent = middleAndLowerContent.drop(Constants.SECTION_CONTENT_SIZE)
LowerMiddleContent(homeVM) {
RenderUiTronContent(
lowerContent.take(Constants.SECTION_CONTENT_SIZE),
homeVM
homeVM,
homeScrollState
)
}
LowerContent(homeVM) {
RenderUiTronContent(
lowerContent.drop(Constants.SECTION_CONTENT_SIZE),
homeVM
homeVM,
homeScrollState
)
}
ContentLoaderLottie(homeVM = homeVM)
@@ -201,7 +213,11 @@ fun ContentLoaderLottie(homeVM: HomeVM) {
}
@Composable
private fun RenderUiTronContent(elementList: List<WidgetId>, homeVM: HomeVM) {
private fun RenderUiTronContent(
elementList: List<WidgetId>,
homeVM: HomeVM,
homeScrollState: () -> ScrollState
) {
elementList.forEach { element ->
key(element) {
val widgetUiMap = homeVM.getWidgetUiStateMap()
@@ -217,12 +233,12 @@ private fun RenderUiTronContent(elementList: List<WidgetId>, homeVM: HomeVM) {
}
AnimatedContainerForWidgets(isVisible = visible) {
val response = homeVM.getWidgetUiStateMap()[element]?.widgetData
UiTronRenderer(
dataMap = response?.data,
uiTronViewModel = homeVM,
customUiTronRenderer = CommonCustomUiTronRenderer()
)
.Render(composeViews = response?.parentComposeView.orEmpty())
HomeWidgetRenderer(
widgetData = response?.data,
viewModel = homeVM,
composeView = response?.parentComposeView.orEmpty(),
homeScrollState = homeScrollState
)
}
}
}

View File

@@ -162,7 +162,8 @@ private fun HomeScreenScaffoldRoot(
.background(Color.White, frontLayerShape)
.verticalScroll(homeScrollState),
isShimmerVisible = widgetResponse.contentWidget.isNullOrEmpty(),
homeVM = homeVM
homeVM = homeVM,
homeScrollState = { homeScrollState }
)
}
)

View File

@@ -0,0 +1,84 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.naviapp.home.compose.uiTron.helper
import androidx.compose.foundation.ScrollState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableIntState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.withContext
class RotatingViewHelper @Inject constructor() {
private val mapOfRotatingViewIndexes = mutableMapOf<String, Int>()
private fun getIndex(layoutId: String): Int = mapOfRotatingViewIndexes.getOrElse(layoutId) { 0 }
private fun updateIndex(layoutId: String, index: Int) {
mapOfRotatingViewIndexes[layoutId] = index
}
@Composable
fun currentIndexToDisplay(
layoutId: String,
numberOfChildren: Int,
delayDuration: Long,
homeScrollState: () -> ScrollState
): MutableIntState {
return if (layoutId.isEmpty().not()) {
var shouldPlayOnLifecycle by remember { mutableStateOf(false) }
val currentIndex = remember { mutableIntStateOf(getIndex(layoutId)) }
val lifecycleOwner = LocalLifecycleOwner.current
if (homeScrollState().isScrollInProgress.not() && shouldPlayOnLifecycle) {
LaunchedEffect(Unit) {
while (true) {
withContext(Dispatchers.IO) {
delay(delayDuration.div(2))
currentIndex.intValue = (currentIndex.intValue + 1) % numberOfChildren
updateIndex(layoutId, currentIndex.intValue)
delay(delayDuration.div(2))
}
}
}
}
DisposableEffect(lifecycleOwner) {
val lifecycle = lifecycleOwner.lifecycle
val observer = LifecycleEventObserver { _, event ->
shouldPlayOnLifecycle =
when (event) {
Lifecycle.Event.ON_RESUME -> {
true
}
Lifecycle.Event.ON_STOP -> {
false
}
else -> {
false
}
}
}
lifecycle.addObserver(observer)
onDispose { lifecycle.removeObserver(observer) }
}
currentIndex
} else remember { mutableIntStateOf(0) }
}
}

View File

@@ -13,10 +13,6 @@ import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.togetherWith
import androidx.compose.foundation.ScrollState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.MutableIntState
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.layoutId
@@ -39,7 +35,8 @@ import com.navi.uitron.utils.setWidth
import com.navi.uitron.utils.setWidthRange
import com.navi.uitron.viewmodel.UiTronViewModel
import com.naviapp.home.compose.uiTron.model.viewProperties.RotatingViewProperty
import kotlinx.coroutines.delay
import com.naviapp.home.viewmodel.HomeVM
import com.naviapp.utils.Constants.HomeCustomUitronWidgetConstants.ROTATING_VIEW_ANIMATION
class RotatingViewRenderer(
private val childrenComposeViews: List<UiTronView>,
@@ -87,10 +84,16 @@ class RotatingViewRenderer(
RotatingView(
modifier = viewModifier,
indexValue = {
currentIndexToDisplay(
duration = { property.delay ?: 2000L },
homeScrollState = homeScrollState
)
(uiTronViewModel as HomeVM)
.rotatingViewHelper
.get()
.currentIndexToDisplay(
layoutId = property.layoutId.orEmpty(),
numberOfChildren = childrenComposeViews.size,
delayDuration = property.delay ?: 2000L,
homeScrollState = homeScrollState
)
.intValue
},
viewToRender = { uiTronRenderer.Render(composeViews = listOf(it)) }
)
@@ -100,34 +103,16 @@ class RotatingViewRenderer(
@Composable
private fun RotatingView(
modifier: Modifier = Modifier,
indexValue: @Composable () -> MutableIntState,
indexValue: @Composable () -> Int,
viewToRender: @Composable (UiTronView) -> Unit
) {
AnimatedContent(
modifier = modifier,
targetState = childrenComposeViews[indexValue().intValue],
targetState = indexValue(),
transitionSpec = { slideInVertically { it } togetherWith slideOutVertically { -it } },
label = ""
) { view ->
viewToRender(view)
label = ROTATING_VIEW_ANIMATION
) { index ->
viewToRender(childrenComposeViews[index])
}
}
@Composable
private fun currentIndexToDisplay(
duration: () -> Long,
homeScrollState: () -> ScrollState
): MutableIntState {
val currentIndex = remember { mutableIntStateOf(0) }
if (homeScrollState().isScrollInProgress.not()) {
LaunchedEffect(Unit) {
while (true) {
delay(duration().div(2))
currentIndex.intValue = (currentIndex.intValue + 1) % childrenComposeViews.size
delay(duration().div(2))
}
}
}
return remember { currentIndex }
}
}

View File

@@ -88,6 +88,7 @@ import com.naviapp.home.compose.listener.HomeScreenCallbackListener
import com.naviapp.home.compose.model.BottomStickyNudgeState
import com.naviapp.home.compose.model.HomePageExtraData
import com.naviapp.home.compose.model.InitiatePaymentFromComposeData
import com.naviapp.home.compose.uiTron.helper.RotatingViewHelper
import com.naviapp.home.model.BottomBarTabType
import com.naviapp.home.model.HomeCtaTypes
import com.naviapp.home.respository.HomeRepository
@@ -109,6 +110,7 @@ import com.naviapp.utils.Constants.OFFER_VIEWED
import com.naviapp.utils.LOAN_ACCOUNT_NUMBER
import com.naviapp.utils.NOTIFICATION_PERMISSION_SHOWN
import com.naviapp.utils.naviAppSerializerGsonBuilder
import dagger.Lazy
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
@@ -140,6 +142,7 @@ constructor(
private val naviPayManager: NaviPayManager,
private val selectiveRefreshHandler: SelectiveRefreshHandler,
private val homePageDataUpdateHandler: HomePageDataUpdateHandler,
val rotatingViewHelper: Lazy<RotatingViewHelper>,
val nuxHandler: NewUserExperienceHandler
) : BaseVM() {

View File

@@ -513,6 +513,10 @@ object Constants {
const val SCROLL_FADE = "scroll_fade"
}
object HomeCustomUitronWidgetConstants {
const val ROTATING_VIEW_ANIMATION = "rotating view animation"
}
enum class JourneyType(val type: String) {
POST_PURCHASE("post_purchase")
}