NTP-45169 loan tab offer nudge super app (#15305)
This commit is contained in:
@@ -7,12 +7,17 @@
|
||||
|
||||
package com.naviapp.home.compose.home.ui.footer
|
||||
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.LocalOverscrollConfiguration
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.navigation.NavHostController
|
||||
import com.navi.uitron.model.UiTronResponse
|
||||
@@ -72,6 +77,7 @@ fun HomeFooterRoot(
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
fun HomeFooter(
|
||||
modifier: Modifier,
|
||||
@@ -114,14 +120,24 @@ fun HomeFooter(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
HomeBottomBar(
|
||||
selectedTabId = selectedTabId,
|
||||
bottomNavBarState = { state.bottomNavBarState },
|
||||
onTabSelected = { tabId ->
|
||||
onFooterEvent(HomeFooterEvents.BottomBarOnTabClick(tabId))
|
||||
onNudgeEvent(NudgeEvent.UpdateNudgeExpandedState(expandState = false))
|
||||
},
|
||||
)
|
||||
Column {
|
||||
CompositionLocalProvider(
|
||||
LocalDensity provides
|
||||
Density(
|
||||
LocalDensity.current.density,
|
||||
LocalDensity.current.fontScale.coerceIn(0.85f, 1.5f),
|
||||
),
|
||||
LocalOverscrollConfiguration provides null,
|
||||
) {
|
||||
HomeBottomBar(
|
||||
selectedTabId = selectedTabId,
|
||||
bottomNavBarState = { state.bottomNavBarState },
|
||||
onTabSelected = { tabId ->
|
||||
onFooterEvent(HomeFooterEvents.BottomBarOnTabClick(tabId))
|
||||
onNudgeEvent(NudgeEvent.UpdateNudgeExpandedState(expandState = false))
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,12 +7,14 @@
|
||||
|
||||
package com.naviapp.home.compose.home.ui.footer
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
@@ -21,16 +23,31 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.wrapContentSize
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.BottomAppBar
|
||||
import androidx.compose.material.ripple
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.onSizeChanged
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.navi.base.utils.EMPTY
|
||||
import com.navi.design.font.FontWeightEnum
|
||||
import com.navi.design.font.getFontWeight
|
||||
import com.navi.design.font.naviFontFamily
|
||||
import com.naviapp.home.compose.home.navigation.NavigationItem
|
||||
import com.naviapp.home.compose.home.navigation.getDefaultBottomTabsList
|
||||
import com.naviapp.home.compose.home.ui.footer.utils.isRedDotBadgeEnabled
|
||||
@@ -39,6 +56,7 @@ import com.naviapp.home.utils.DrawIcon
|
||||
import com.naviapp.home.utils.TabText
|
||||
import com.naviapp.utils.BottomBarUtils
|
||||
|
||||
@SuppressLint("UseOfNonLambdaOffsetOverload")
|
||||
@Composable
|
||||
fun HomeBottomBar(
|
||||
selectedTabId: String,
|
||||
@@ -46,7 +64,7 @@ fun HomeBottomBar(
|
||||
onTabSelected: (String) -> Unit,
|
||||
) {
|
||||
BottomAppBar(
|
||||
modifier = Modifier.height(68.dp).fillMaxWidth(),
|
||||
modifier = Modifier.height(70.dp).fillMaxWidth(),
|
||||
backgroundColor = Color.White,
|
||||
elevation = 8.dp,
|
||||
contentPadding = PaddingValues(0.dp),
|
||||
@@ -57,6 +75,7 @@ fun HomeBottomBar(
|
||||
selectedTabId = selectedTabId,
|
||||
onTabSelected = onTabSelected,
|
||||
showRedDotBadge = { isRedDotBadgeEnabled(bottomTabItem.tabId, bottomNavBarState) },
|
||||
nudgeText = bottomNavBarState().items[bottomTabItem.tabId]?.nudgeText ?: EMPTY,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -68,8 +87,12 @@ private fun RowScope.BottomBarItem(
|
||||
selectedTabId: String,
|
||||
showRedDotBadge: () -> Boolean,
|
||||
onTabSelected: (String) -> Unit,
|
||||
nudgeText: String? = null,
|
||||
) {
|
||||
item.tabId.let {
|
||||
val density = LocalDensity.current
|
||||
val widthCheck = remember { mutableIntStateOf(0) }
|
||||
val widthInDp = with(density) { widthCheck.intValue.toDp() }
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier =
|
||||
@@ -81,9 +104,10 @@ private fun RowScope.BottomBarItem(
|
||||
) {
|
||||
onTabSelected(it)
|
||||
}
|
||||
.padding(top = 6.dp, bottom = 10.dp),
|
||||
.padding(top = 12.dp, bottom = 6.dp)
|
||||
.onSizeChanged { widthCheck.intValue = it.width },
|
||||
) {
|
||||
Box {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
DrawIcon(
|
||||
drawableIconId =
|
||||
BottomBarUtils.fetchDefaultTabIconResourceId(
|
||||
@@ -102,6 +126,7 @@ private fun RowScope.BottomBarItem(
|
||||
.background(Color.Red, shape = CircleShape)
|
||||
)
|
||||
}
|
||||
nudgeText?.takeIf { it.isNotEmpty() }?.let { NudgePill(it, widthInDp) }
|
||||
}
|
||||
Spacer(modifier = Modifier.height(6.dp))
|
||||
TabText(
|
||||
@@ -112,3 +137,33 @@ private fun RowScope.BottomBarItem(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NudgePill(text: String, widthInDp: Dp) {
|
||||
Row {
|
||||
Box(modifier = Modifier.weight(1f).offset(x = widthInDp / 2, y = (-10).dp)) {
|
||||
Text(
|
||||
text = text,
|
||||
modifier =
|
||||
Modifier.background(
|
||||
Color(0xFF22A940),
|
||||
shape =
|
||||
RoundedCornerShape(bottomEnd = 4.dp, topStart = 4.dp, topEnd = 4.dp),
|
||||
)
|
||||
.padding(horizontal = 4.dp, vertical = 2.dp)
|
||||
.wrapContentSize(),
|
||||
color = Color.White,
|
||||
style =
|
||||
TextStyle(
|
||||
fontSize = 8.sp,
|
||||
fontFamily = naviFontFamily,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_DEMI_BOLD),
|
||||
lineHeight = 12.sp,
|
||||
),
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
maxLines = 1,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,6 +109,19 @@ fun handleBottomBarData(footer: AlchemistWidgetGroup, sharedVM: SharedVM) {
|
||||
?.data
|
||||
?.get(HOME_BOTTOM_NAV_BAR_DATA) as? HomeBottomNavBarData
|
||||
bottomNavBarData?.bottomNavBarDetails?.entries?.forEach { (key, value) ->
|
||||
sharedVM.updateBottomNavBarState(tabId = key, showRedDotBadge = value.showRedDot)
|
||||
if (value.resetNudgeVisibleCount == true && key == BottomBarTabType.LOAN.name) {
|
||||
sharedVM.bottomNavTabsNudgeHandler.resetLoanTabOfferNudgeAppearedCount()
|
||||
}
|
||||
val nudgeText =
|
||||
sharedVM.bottomNavTabsNudgeHandler.getNudgeText(
|
||||
value.nudgeText,
|
||||
value.nudgeTextSuffix,
|
||||
value.nudgeVisibleMaxCount,
|
||||
)
|
||||
sharedVM.updateBottomNavBarState(
|
||||
tabId = key,
|
||||
showRedDotBadge = value.showRedDot,
|
||||
nudgeText = nudgeText,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2024 by Navi Technologies Limited
|
||||
* * Copyright © 2024-2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
@@ -12,4 +12,5 @@ data class BottomNavBarStateHolder(val items: Map<String, BottomNavBarItemData>
|
||||
data class BottomNavBarItemData(
|
||||
val isTabClicked: Boolean = false,
|
||||
val showRedDotBadge: Boolean = false,
|
||||
val nudgeText: String? = null,
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2024 by Navi Technologies Limited
|
||||
* * Copyright © 2024-2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
@@ -13,4 +13,10 @@ data class HomeBottomNavBarData(
|
||||
val bottomNavBarDetails: Map<String, BottomNavBarItemInfo>? = null
|
||||
) : UiTronData()
|
||||
|
||||
data class BottomNavBarItemInfo(val showRedDot: Boolean? = null)
|
||||
data class BottomNavBarItemInfo(
|
||||
val showRedDot: Boolean? = null,
|
||||
val nudgeText: String? = null,
|
||||
val nudgeTextSuffix: String? = null,
|
||||
val nudgeVisibleMaxCount: Int? = null,
|
||||
val resetNudgeVisibleCount: Boolean? = null,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.naviapp.home.usecase
|
||||
|
||||
import com.navi.base.sharedpref.PreferenceManager
|
||||
import com.navi.base.utils.EMPTY
|
||||
import com.naviapp.utils.Constants.LOAN_TAB_OFFER_NUDGE_APPEARED_COUNT
|
||||
import javax.inject.Inject
|
||||
|
||||
class BottomNavTabsNudgeHandler @Inject constructor() {
|
||||
|
||||
companion object {
|
||||
private const val DEFAULT_NUDGE_VISIBLE_MAX_COUNT = 3
|
||||
}
|
||||
|
||||
private var loanTabOfferNudgeAppearedCount: Int =
|
||||
PreferenceManager.getIntPreference(LOAN_TAB_OFFER_NUDGE_APPEARED_COUNT)
|
||||
|
||||
fun incrementLoanTabOfferNudgeAppearedCount() {
|
||||
PreferenceManager.setIntPreference(
|
||||
LOAN_TAB_OFFER_NUDGE_APPEARED_COUNT,
|
||||
loanTabOfferNudgeAppearedCount + 1,
|
||||
)
|
||||
}
|
||||
|
||||
fun resetLoanTabOfferNudgeAppearedCount() {
|
||||
PreferenceManager.setIntPreference(LOAN_TAB_OFFER_NUDGE_APPEARED_COUNT, 0)
|
||||
}
|
||||
|
||||
fun getNudgeText(
|
||||
nudgeText: String?,
|
||||
nudgeTextSuffix: String?,
|
||||
nudgeVisibleMaxCount: Int?,
|
||||
): String {
|
||||
val finalNudgeText = (nudgeText ?: EMPTY) + (nudgeTextSuffix ?: EMPTY)
|
||||
val shouldDisplayNudge =
|
||||
loanTabOfferNudgeAppearedCount <
|
||||
(nudgeVisibleMaxCount ?: DEFAULT_NUDGE_VISIBLE_MAX_COUNT)
|
||||
|
||||
return if (shouldDisplayNudge) finalNudgeText else EMPTY
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
package com.naviapp.home.viewmodel
|
||||
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.navi.base.utils.isNotNullAndNotEmpty
|
||||
import com.navi.common.viewmodel.BaseVM
|
||||
import com.naviapp.common.model.AppUpdateState
|
||||
import com.naviapp.common.model.UiTronActionHandler
|
||||
@@ -20,6 +21,7 @@ import com.naviapp.home.model.HpBottomSheetConfig
|
||||
import com.naviapp.home.model.HpBottomSheetContent
|
||||
import com.naviapp.home.model.HpBottomSheetState
|
||||
import com.naviapp.home.model.HpBottomSheetStateHolder
|
||||
import com.naviapp.home.usecase.BottomNavTabsNudgeHandler
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
@@ -30,7 +32,8 @@ import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@HiltViewModel
|
||||
class SharedVM @Inject constructor() : BaseVM() {
|
||||
class SharedVM @Inject constructor(val bottomNavTabsNudgeHandler: BottomNavTabsNudgeHandler) :
|
||||
BaseVM() {
|
||||
|
||||
private val _uiTronActionHandler = MutableStateFlow<UiTronActionHandler?>(null)
|
||||
val uiTronActionHandler = _uiTronActionHandler.asStateFlow()
|
||||
@@ -70,6 +73,12 @@ class SharedVM @Inject constructor() : BaseVM() {
|
||||
|
||||
fun updateSelectedTabId(tabId: String) {
|
||||
_selectedTabId.update { tabId }
|
||||
if (
|
||||
tabId == BottomBarTabType.LOAN.name &&
|
||||
_bottomNavBarStateHolder.value.items[tabId]?.nudgeText.isNotNullAndNotEmpty()
|
||||
) {
|
||||
bottomNavTabsNudgeHandler.incrementLoanTabOfferNudgeAppearedCount()
|
||||
}
|
||||
}
|
||||
|
||||
fun getSelectedTabId(): String {
|
||||
@@ -84,6 +93,7 @@ class SharedVM @Inject constructor() : BaseVM() {
|
||||
tabId: String,
|
||||
isTabClicked: Boolean? = null,
|
||||
showRedDotBadge: Boolean? = null,
|
||||
nudgeText: String? = null,
|
||||
) {
|
||||
val currentState = _bottomNavBarStateHolder.value.items.toMutableMap()
|
||||
currentState[tabId]?.let {
|
||||
@@ -91,6 +101,7 @@ class SharedVM @Inject constructor() : BaseVM() {
|
||||
BottomNavBarItemData(
|
||||
isTabClicked = isTabClicked ?: it.isTabClicked,
|
||||
showRedDotBadge = showRedDotBadge ?: it.showRedDotBadge,
|
||||
nudgeText = nudgeText ?: it.nudgeText,
|
||||
)
|
||||
}
|
||||
_bottomNavBarStateHolder.update { BottomNavBarStateHolder(currentState) }
|
||||
|
||||
@@ -241,6 +241,7 @@ object Constants {
|
||||
const val STATUS = "status"
|
||||
const val HTTP_REGEX = "^https?://"
|
||||
const val NAVIHQ = "NAVIHQ"
|
||||
const val LOAN_TAB_OFFER_NUDGE_APPEARED_COUNT = "LOAN_TAB_OFFER_NUDGE_APPEARED_COUNT"
|
||||
|
||||
object Notification {
|
||||
const val HIDE_NOTIFICATION_COUNT = "hideNotificationCount"
|
||||
|
||||
Reference in New Issue
Block a user