NTP-1410 || fixes for 3pm tab (#12531)

Co-authored-by: Aman <amankasyapp@gmail.com>
This commit is contained in:
Aman S
2024-09-18 14:35:09 +05:30
committed by GitHub
parent 78d5b437d8
commit 3a8801ccc7
5 changed files with 162 additions and 134 deletions

View File

@@ -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)

View File

@@ -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<CutOffSlotData?>(null) }
var formattedTitle by remember { mutableStateOf<TextFieldData?>(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<Boolean> { 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()
)
}
}

View File

@@ -27,6 +27,9 @@ fun InitLifeCycleListener(
investmentsScreenHelper.refreshInvestmentTabUiTronConfigs(investmentsTabVM)
investmentsScreenHelper.resetHorizontalPagerScrollState(investmentsTabVM)
}
Lifecycle.Event.ON_STOP -> {
investmentsTabVM.onCutOffTimerClear()
}
else -> {}
}
}

View File

@@ -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 -> {

View File

@@ -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<CtaData>()
val ctaData = _ctaData.asSharedFlow()
private val _onTimerDataChange = MutableStateFlow<String?>(null)
val timerDataChange: StateFlow<String?> = _onTimerDataChange
private val _cutOffTimerDataChange = MutableStateFlow<String?>(null)
val cutOffTimerDataChange: StateFlow<String?> = _cutOffTimerDataChange
private val _onTimerRefreshCallback = MutableStateFlow(false)
val timerRefreshCallback: StateFlow<Boolean> = _onTimerRefreshCallback
private val _cutOffTimerRefreshCallback = MutableStateFlow(false)
val cutOffTimerRefreshCallback: StateFlow<Boolean> = _cutOffTimerRefreshCallback
private val _currentSlotIndex = MutableStateFlow(0)
val currentSlotIndex: StateFlow<Int> = _currentSlotIndex
private val _isCutOffRecomposition = MutableLiveData<Boolean>()
val isCutOffRecomposition: LiveData<Boolean>
get() = _isCutOffRecomposition
private val _isRecomposition = MutableLiveData<Boolean>()
val isRecomposition: LiveData<Boolean>
get() = _isRecomposition
private val _isDataRefreshed = MutableLiveData<CutOffTimerWidget?>(null)
val isDataRefreshed: LiveData<CutOffTimerWidget?>
get() = _isDataRefreshed
fun updateCurrentSlotIndex(newIndex: Int) {
_currentSlotIndex.value = newIndex
}
private val _cutOffCurrentSlotData = MutableStateFlow<CutOffSlotData?>(null)
val cutOffCurrentSlotData: StateFlow<CutOffSlotData?>
get() = _cutOffCurrentSlotData
private val _cutOffFormattedTitle = MutableStateFlow<TextFieldData?>(null)
val cutOffFormattedTitle: StateFlow<TextFieldData?>
get() = _cutOffFormattedTitle
var cutOffTimerCompositionCount = 0
private val _bottomSheetType = MutableSharedFlow<String?>(replay = 1)
val bottomSheetType: SharedFlow<String?> = _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