From 3a8801ccc72dd5e26cfd894ebf35186669c8f4a4 Mon Sep 17 00:00:00 2001 From: Aman S Date: Wed, 18 Sep 2024 14:35:09 +0530 Subject: [PATCH] NTP-1410 || fixes for 3pm tab (#12531) Co-authored-by: Aman --- .../CutOffSellTimerComposable.kt | 2 +- .../CutOffTimerWidgetComposable.kt | 211 +++++++++--------- .../investmentTab/InitLifeCycleListener.kt | 3 + ...nvestmentGenericComposableWidgetFactory.kt | 7 +- .../dashboard/viewmodels/InvestmentsVm.kt | 73 ++++-- 5 files changed, 162 insertions(+), 134 deletions(-) diff --git a/android/app/src/main/java/com/naviapp/home/dashboard/ui/compose/investmentTab/CutOffSellTimerComposable.kt b/android/app/src/main/java/com/naviapp/home/dashboard/ui/compose/investmentTab/CutOffSellTimerComposable.kt index 94b50aca0c..f365978468 100644 --- a/android/app/src/main/java/com/naviapp/home/dashboard/ui/compose/investmentTab/CutOffSellTimerComposable.kt +++ b/android/app/src/main/java/com/naviapp/home/dashboard/ui/compose/investmentTab/CutOffSellTimerComposable.kt @@ -73,7 +73,7 @@ fun CutOffSellTimerComposable( ) } - Spacer(modifier = Modifier.height(4.dp)) + Spacer(modifier = Modifier.height(8.dp)) Row(modifier = Modifier.align(Alignment.CenterHorizontally)) { NaviTextWidgetized(textFieldData = data.subTitle) diff --git a/android/app/src/main/java/com/naviapp/home/dashboard/ui/compose/investmentTab/CutOffTimerWidgetComposable.kt b/android/app/src/main/java/com/naviapp/home/dashboard/ui/compose/investmentTab/CutOffTimerWidgetComposable.kt index 165d58ed3d..8116abd6d5 100644 --- a/android/app/src/main/java/com/naviapp/home/dashboard/ui/compose/investmentTab/CutOffTimerWidgetComposable.kt +++ b/android/app/src/main/java/com/naviapp/home/dashboard/ui/compose/investmentTab/CutOffTimerWidgetComposable.kt @@ -20,11 +20,7 @@ import androidx.compose.foundation.layout.width import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -35,6 +31,8 @@ import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.Observer import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.distinctUntilChanged +import coil.compose.AsyncImage import com.navi.base.model.ActionData import com.navi.common.utils.EMPTY import com.navi.common.utils.toActionData @@ -42,53 +40,61 @@ import com.navi.design.utils.clickableWithNoGesture import com.navi.naviwidgets.extensions.NaviImage import com.navi.naviwidgets.extensions.NaviTextWidgetized import com.navi.naviwidgets.extensions.getBrush -import com.navi.naviwidgets.models.response.TextFieldData import com.naviapp.R import com.naviapp.home.dashboard.models.investmentTabWidgetData.CutOffSlotData -import com.naviapp.home.dashboard.models.investmentTabWidgetData.CutOffTimerWidget import com.naviapp.home.dashboard.viewmodels.InvestmentsVm import com.naviapp.home.utils.Timer +import com.naviapp.home.utils.shimmerEffect @Composable fun CutOffTimerWidgetComposable( - widget: CutOffTimerWidget, onClick: (actionData: ActionData?) -> Unit, investmentsTabVm: InvestmentsVm, lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current ) { - val currentSlotIndex by investmentsTabVm.currentSlotIndex.collectAsStateWithLifecycle() - var currentSlotData by remember { mutableStateOf(null) } - var formattedTitle by remember { mutableStateOf(null) } - val derivedFormattedTitle = remember { derivedStateOf { formattedTitle } } - val timerRefresh by investmentsTabVm.timerRefreshCallback.collectAsStateWithLifecycle(false) - val timerDataChange by investmentsTabVm.timerDataChange.collectAsStateWithLifecycle(null) + val timerRefresh by + investmentsTabVm.cutOffTimerRefreshCallback.collectAsStateWithLifecycle(false) + val timerDataChange by investmentsTabVm.cutOffTimerDataChange.collectAsStateWithLifecycle(null) + val currentSlotData by investmentsTabVm.cutOffCurrentSlotData.collectAsStateWithLifecycle(null) + val formattedTitle by investmentsTabVm.cutOffFormattedTitle.collectAsStateWithLifecycle(null) fun updateSlotData(data: CutOffSlotData) { - currentSlotData = data - formattedTitle = data.title + investmentsTabVm.setCurrentTimerSlotData(data) + investmentsTabVm.setTimerChangeData(null) + investmentsTabVm.setFormattedTitle(data.title) } - LaunchedEffect(key1 = currentSlotIndex) { - widget.widgetData?.let { data -> - if (data.extraData?.isHoliday == true) { - data.content - ?.cutOffSlotData - ?.firstOrNull { it.slotIndex == "3" } - ?.let { slotData -> updateSlotData(slotData) } - } else { - if (currentSlotIndex > 0) { - val slotData: CutOffSlotData? = - data.content?.cutOffSlotData?.getOrNull(currentSlotIndex) - slotData?.let { currentData -> updateSlotData(currentData) } + fun updateTimer() { + investmentsTabVm.cutOffCurrentSlotData.value?.timeLeft?.let { timeLeft -> + val timerRefreshInterval = + investmentsTabVm.cutOffCurrentSlotData.value?.timerInvalidateAfter?.let { + timeLeft - it + } + Timer.onStart( + timeFormat = "hms", + timeInMillis = timeLeft, + countDownInterval = 1000L, + timerRefreshInterval = timerRefreshInterval, + viewmodel = investmentsTabVm + ) + } + ?: run { + investmentsTabVm.cutOffCurrentSlotData.value?.timerInvalidateAfter?.let { + Timer.onStart( + timeFormat = "hms", + timeInMillis = it, + countDownInterval = 1000L, + viewmodel = investmentsTabVm + ) } } - } } LaunchedEffect(timerDataChange) { if (timerDataChange != null) { - val titleText = currentSlotData?.title?.text ?: EMPTY - val substringStyles = currentSlotData?.title?.substringStyles + val titleText = investmentsTabVm.cutOffCurrentSlotData.value?.title?.text ?: EMPTY + val substringStyles = + investmentsTabVm.cutOffCurrentSlotData.value?.title?.substringStyles val updatedSubStringStyle = substringStyles?.mapIndexed { index, style -> if (index == substringStyles.lastIndex) { @@ -97,97 +103,78 @@ fun CutOffTimerWidgetComposable( style } } - - formattedTitle = - formattedTitle?.copy( + investmentsTabVm.setFormattedTitle( + investmentsTabVm.cutOffFormattedTitle.value?.copy( text = "$titleText $timerDataChange", substringStyles = updatedSubStringStyle ) + ) } } LaunchedEffect(timerRefresh) { if (timerRefresh) { - widget.widgetData?.content?.cutOffSlotData?.getOrNull(currentSlotIndex + 1)?.let { - updatedData -> - updateSlotData(updatedData) - investmentsTabVm.updateCurrentSlotIndex(currentSlotIndex + 1) - investmentsTabVm.setTimerChangeData(null) - investmentsTabVm.setTimerRefreshData(false) - investmentsTabVm.setIsRecomposition(false) - investmentsTabVm.setIsRecomposition(true) - } - ?: run { - investmentsTabVm.updateCurrentSlotIndex(0) - investmentsTabVm.fetchInvestmentTabUiTronConfigs() - } + investmentsTabVm.setTimerRefreshData(false) + investmentsTabVm.setFormattedTitle(null) + investmentsTabVm.setCurrentTimerSlotData(null) + investmentsTabVm.setTimerChangeData(null) + investmentsTabVm.refreshInvestmentTabUiTronConfigs() } } DisposableEffect(lifecycleOwner) { val lifecycleObserver = LifecycleEventObserver { _, event -> when (event) { - Lifecycle.Event.ON_STOP -> { - investmentsTabVm.timerCompositionCount = 0 - investmentsTabVm.setIsRecomposition(false) - investmentsTabVm.setTimerRefreshData(false) - investmentsTabVm.updateCurrentSlotIndex(0) - investmentsTabVm.setTimerChangeData(null) - Timer.onStop() - } Lifecycle.Event.ON_RESUME -> { - if (investmentsTabVm.timerCompositionCount == 0) { - investmentsTabVm.timerCompositionCount = -1 - if (currentSlotIndex == 0) { - widget.widgetData - ?.content - ?.cutOffSlotData - ?.sortedBy { it.slotIndex?.toIntOrNull() } - ?.first() - ?.let { updateSlotData(it) } - } + if (investmentsTabVm.cutOffTimerCompositionCount == 0) { + investmentsTabVm.cutOffTimerCompositionCount = -1 investmentsTabVm.setIsRecomposition(true) - } else if (investmentsTabVm.timerCompositionCount == -1) { + } else if (investmentsTabVm.cutOffTimerCompositionCount == -1) { investmentsTabVm.setIsRecomposition(false) } } else -> Unit } } + lifecycleOwner.lifecycle.addObserver(lifecycleObserver) - val observer = + val recompositionObserver = Observer { isRecomposition -> if (isRecomposition) { - currentSlotData?.timeLeft?.let { timeLeft -> - val timerRefresh = - currentSlotData?.timerInvalidateAfter?.let { timeLeft - it } - Timer.onStart( - timeFormat = "hms", - timeInMillis = timeLeft, - countDownInterval = 1000L, - timerRefreshInterval = timerRefresh, - viewmodel = investmentsTabVm - ) - } - ?: run { - currentSlotData?.timerInvalidateAfter?.let { - Timer.onStart( - timeFormat = "hms", - timeInMillis = it, - countDownInterval = 1000L, - viewmodel = investmentsTabVm - ) + investmentsTabVm.isDataRefreshed + .distinctUntilChanged() + .observe( + lifecycleOwner, + Observer { data -> + if (data != null) { + Timer.onStop() + if (data.widgetData?.extraData?.isHoliday == true) { + data.widgetData.content + ?.cutOffSlotData + ?.firstOrNull { it.slotIndex == "3" } + ?.let { slotData -> updateSlotData(slotData) } + } else { + data.widgetData + ?.content + ?.cutOffSlotData + ?.sortedBy { it.slotIndex?.toIntOrNull() } + ?.firstOrNull() + ?.let { updateSlotData(it) } + } + updateTimer() + } } - } + ) } } - investmentsTabVm.isRecomposition.observe(lifecycleOwner, observer) + investmentsTabVm.isCutOffRecomposition.observe(lifecycleOwner, recompositionObserver) onDispose { lifecycleOwner.lifecycle.removeObserver(lifecycleObserver) - investmentsTabVm.isRecomposition.removeObserver(observer) + investmentsTabVm.isCutOffRecomposition.removeObserver(recompositionObserver) + investmentsTabVm.isDataRefreshed.removeObservers(lifecycleOwner) } } @@ -198,8 +185,8 @@ fun CutOffTimerWidgetComposable( .background(color = Color.White) .padding(bottom = 12.dp, start = 16.dp, end = 16.dp) .clickableWithNoGesture { - currentSlotData?.icon?.cta?.let { ctaData -> - currentSlotData?.bottomSheetData?.let { bottomSheetData -> + it.icon.cta?.let { ctaData -> + it.bottomSheetData?.let { bottomSheetData -> investmentsTabVm.setBottomSheetData(ctaData.type, bottomSheetData) } onClick(ctaData.toActionData()) @@ -208,28 +195,27 @@ fun CutOffTimerWidgetComposable( ) { Column( modifier = - Modifier.fillMaxWidth() - .background(brush = getBrush(gradient = currentSlotData?.gradient)) + Modifier.fillMaxWidth().background(brush = getBrush(gradient = it.gradient)) ) { - NaviImage( - imageFieldData = currentSlotData?.topIcon, + AsyncImage( + model = it.topIcon?.url ?: EMPTY, modifier = Modifier.fillMaxWidth() - .offset(y = -(((currentSlotData?.topIcon?.iconHeight ?: 0) / 2).dp)) + .offset(y = -(((it.topIcon?.iconHeight ?: 0) / 2).dp)) + .height((it.topIcon?.iconHeight ?: 0).dp), + contentDescription = EMPTY ) - Spacer( - modifier = Modifier.height(((currentSlotData?.topIconBottomMargin ?: 0) / 2).dp) - ) + Spacer(modifier = Modifier.height((it.topIconBottomMargin ?: 0).dp)) - derivedFormattedTitle.value?.let { data -> + formattedTitle?.let { formattedTitle -> NaviTextWidgetized( - textFieldData = data, + textFieldData = formattedTitle, modifier = Modifier.align(Alignment.CenterHorizontally) ) } ?: run { - currentSlotData?.title?.let { + it.title?.let { NaviTextWidgetized( textFieldData = it, modifier = Modifier.align(Alignment.CenterHorizontally) @@ -237,23 +223,28 @@ fun CutOffTimerWidgetComposable( } } - Spacer(modifier = Modifier.height(4.dp)) + Spacer(modifier = Modifier.height(8.dp)) Row(modifier = Modifier.align(Alignment.CenterHorizontally)) { - NaviTextWidgetized(textFieldData = currentSlotData?.subTitle) + NaviTextWidgetized(textFieldData = it.subTitle) Spacer(modifier = Modifier.width(8.dp)) NaviImage( - imageFieldData = currentSlotData?.icon, + imageFieldData = it.icon, modifier = - Modifier.width( - (currentSlotData?.icon?.iconWidth ?: R.integer.value_16).dp - ) - .height( - (currentSlotData?.icon?.iconHeight ?: R.integer.value_16).dp - ) + Modifier.width((it.icon.iconWidth ?: R.integer.value_16).dp) + .height((it.icon.iconHeight ?: R.integer.value_16).dp) ) } } } } + ?: run { + Box( + modifier = + Modifier.height(72.dp) + .padding(start = 16.dp, end = 16.dp) + .fillMaxWidth() + .shimmerEffect() + ) + } } diff --git a/android/app/src/main/java/com/naviapp/home/dashboard/ui/compose/investmentTab/InitLifeCycleListener.kt b/android/app/src/main/java/com/naviapp/home/dashboard/ui/compose/investmentTab/InitLifeCycleListener.kt index 7884de4b17..176b466a4a 100644 --- a/android/app/src/main/java/com/naviapp/home/dashboard/ui/compose/investmentTab/InitLifeCycleListener.kt +++ b/android/app/src/main/java/com/naviapp/home/dashboard/ui/compose/investmentTab/InitLifeCycleListener.kt @@ -27,6 +27,9 @@ fun InitLifeCycleListener( investmentsScreenHelper.refreshInvestmentTabUiTronConfigs(investmentsTabVM) investmentsScreenHelper.resetHorizontalPagerScrollState(investmentsTabVM) } + Lifecycle.Event.ON_STOP -> { + investmentsTabVM.onCutOffTimerClear() + } else -> {} } } diff --git a/android/app/src/main/java/com/naviapp/home/dashboard/ui/compose/investmentTab/InvestmentGenericComposableWidgetFactory.kt b/android/app/src/main/java/com/naviapp/home/dashboard/ui/compose/investmentTab/InvestmentGenericComposableWidgetFactory.kt index 38e5431319..1da7a96a3e 100644 --- a/android/app/src/main/java/com/naviapp/home/dashboard/ui/compose/investmentTab/InvestmentGenericComposableWidgetFactory.kt +++ b/android/app/src/main/java/com/naviapp/home/dashboard/ui/compose/investmentTab/InvestmentGenericComposableWidgetFactory.kt @@ -159,11 +159,8 @@ fun InvestmentGenericComposableWidgetFactory( } InvestmentTabWidgetType.CUT_OFF_TIME_SLOT_WIDGET.value -> { VisibilityTracker(widgetData = data, onVisible = onVisible) { - CutOffTimerWidgetComposable( - widget = data as CutOffTimerWidget, - onClick = onClick, - investmentsTabVm - ) + investmentsTabVm.updateDataRefreshStatus(data as CutOffTimerWidget) + CutOffTimerWidgetComposable(onClick = onClick, investmentsTabVm) } } InvestmentTabWidgetType.CUT_OFF_SELL_TIMER_WIDGET.value -> { diff --git a/android/app/src/main/java/com/naviapp/home/dashboard/viewmodels/InvestmentsVm.kt b/android/app/src/main/java/com/naviapp/home/dashboard/viewmodels/InvestmentsVm.kt index dd6f29517c..f3307e6fdc 100644 --- a/android/app/src/main/java/com/naviapp/home/dashboard/viewmodels/InvestmentsVm.kt +++ b/android/app/src/main/java/com/naviapp/home/dashboard/viewmodels/InvestmentsVm.kt @@ -31,11 +31,15 @@ import com.navi.common.utils.isValidResponse import com.navi.common.utils.log import com.navi.common.utils.toCtaData import com.navi.common.viewmodel.BaseVM +import com.navi.naviwidgets.models.response.TextFieldData import com.navi.uitron.model.action.AnalyticsAction import com.navi.uitron.model.data.UiTronAction import com.naviapp.R +import com.naviapp.home.dashboard.models.investmentTabWidgetData.CutOffSlotData +import com.naviapp.home.dashboard.models.investmentTabWidgetData.CutOffTimerWidget import com.naviapp.home.dashboard.models.response.GenericComposableWidgetInfo import com.naviapp.home.dashboard.repo.InvestmentsTabV2Repository +import com.naviapp.home.utils.Timer import com.naviapp.network.di.DataDeserializers import com.naviapp.network.di.DataSerializers import com.naviapp.utils.Constants.EMPTY @@ -74,22 +78,29 @@ constructor( private val _ctaData = MutableSharedFlow() val ctaData = _ctaData.asSharedFlow() - private val _onTimerDataChange = MutableStateFlow(null) - val timerDataChange: StateFlow = _onTimerDataChange + private val _cutOffTimerDataChange = MutableStateFlow(null) + val cutOffTimerDataChange: StateFlow = _cutOffTimerDataChange - private val _onTimerRefreshCallback = MutableStateFlow(false) - val timerRefreshCallback: StateFlow = _onTimerRefreshCallback + private val _cutOffTimerRefreshCallback = MutableStateFlow(false) + val cutOffTimerRefreshCallback: StateFlow = _cutOffTimerRefreshCallback - private val _currentSlotIndex = MutableStateFlow(0) - val currentSlotIndex: StateFlow = _currentSlotIndex + private val _isCutOffRecomposition = MutableLiveData() + val isCutOffRecomposition: LiveData + get() = _isCutOffRecomposition - private val _isRecomposition = MutableLiveData() - val isRecomposition: LiveData - get() = _isRecomposition + private val _isDataRefreshed = MutableLiveData(null) + val isDataRefreshed: LiveData + get() = _isDataRefreshed - fun updateCurrentSlotIndex(newIndex: Int) { - _currentSlotIndex.value = newIndex - } + private val _cutOffCurrentSlotData = MutableStateFlow(null) + val cutOffCurrentSlotData: StateFlow + get() = _cutOffCurrentSlotData + + private val _cutOffFormattedTitle = MutableStateFlow(null) + val cutOffFormattedTitle: StateFlow + get() = _cutOffFormattedTitle + + var cutOffTimerCompositionCount = 0 private val _bottomSheetType = MutableSharedFlow(replay = 1) val bottomSheetType: SharedFlow = _bottomSheetType @@ -112,19 +123,35 @@ constructor( } fun setTimerChangeData(data: String?) { - _onTimerDataChange.value = data + _cutOffTimerDataChange.value = data } - var timerCompositionCount = 0 + fun setFormattedTitle(data: TextFieldData?) { + _cutOffFormattedTitle.value = data + } + + fun setCurrentTimerSlotData(data: CutOffSlotData?) { + _cutOffCurrentSlotData.value = data + } + + fun updateDataRefreshStatus(data: CutOffTimerWidget?) { + if (data == null) { + _isDataRefreshed.value = null + } else { + if (data != _isDataRefreshed.value) { + _isDataRefreshed.value = data + } + } + } fun setIsRecomposition(value: Boolean) { - if (_isRecomposition.value != value) { - _isRecomposition.value = value + if (_isCutOffRecomposition.value != value) { + _isCutOffRecomposition.value = value } } fun setTimerRefreshData(isRefreshed: Boolean = false) { - _onTimerRefreshCallback.value = isRefreshed + _cutOffTimerRefreshCallback.value = isRefreshed } override fun onCleared() { @@ -240,6 +267,17 @@ constructor( } } + fun onCutOffTimerClear() { + this.updateDataRefreshStatus(null) + this.cutOffTimerCompositionCount = 0 + this.setTimerRefreshData(false) + this.setTimerChangeData(null) + this.setIsRecomposition(false) + this.setFormattedTitle(null) + this.setCurrentTimerSlotData(null) + Timer.onStop() + } + private suspend fun fetchInvestmentTabFromRemote(screenName: String) { val response = if (screenName == INVESTMENT_TAB_SCREEN_V2) { @@ -259,7 +297,6 @@ constructor( _investmentsTabScreenData.update { InvestmentsTabScreenState.Success(data = investmentsTabResponse) } - saveInvestmentsTabResponseInCache( data.content, NaviSharedDbKeys.INVESTMENT_TAB.keyName