NTP-8836 New App Update for homepage UAT changes (#14023)
This commit is contained in:
@@ -9,19 +9,14 @@ package com.naviapp.home.compose.model
|
||||
|
||||
import com.navi.uitron.model.data.UiTronData
|
||||
import com.navi.uitron.model.ui.BaseProperty
|
||||
import com.naviapp.home.model.ToastData
|
||||
|
||||
data class ProfileAppUpdateSettingData(
|
||||
val leftIconUrl: String? = null,
|
||||
val titleText: String? = null,
|
||||
val updateCtaText: String? = null,
|
||||
val installCtaText: String? = null,
|
||||
val toastData: AppUpdateToastData? = null
|
||||
val toastData: ToastData? = null
|
||||
) : UiTronData()
|
||||
|
||||
data class AppUpdateToastData(
|
||||
val message: String? = null,
|
||||
val leftIconUrl: String? = null,
|
||||
val dismissIconUrl: String? = null
|
||||
)
|
||||
|
||||
class ProfileAppUpdateSettingProperty : BaseProperty()
|
||||
|
||||
@@ -7,8 +7,6 @@
|
||||
|
||||
package com.naviapp.home.compose.profile
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@@ -24,18 +22,10 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalSavedStateRegistryOwner
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import androidx.lifecycle.setViewTreeLifecycleOwner
|
||||
import androidx.savedstate.SavedStateRegistryOwner
|
||||
import androidx.savedstate.setViewTreeSavedStateRegistryOwner
|
||||
import com.navi.common.utils.Constants.PROFILE_SCREEN
|
||||
import com.navi.elex.atoms.ElexAsyncImage
|
||||
import com.navi.elex.atoms.ElexText
|
||||
@@ -44,33 +34,41 @@ import com.naviapp.R
|
||||
import com.naviapp.analytics.utils.NaviAnalytics
|
||||
import com.naviapp.common.listeners.InAppUpdateBridge
|
||||
import com.naviapp.common.model.AppUpdateState
|
||||
import com.naviapp.home.compose.model.AppUpdateToastData
|
||||
import com.naviapp.home.compose.model.ProfileAppUpdateSettingData
|
||||
import com.naviapp.home.model.CustomDismissibleToastState
|
||||
|
||||
@Composable
|
||||
fun ProfileAppUpdateWidget(
|
||||
state: CustomDismissibleToastState,
|
||||
data: ProfileAppUpdateSettingData,
|
||||
appUpdateState: AppUpdateState,
|
||||
inAppUpdateBridge: InAppUpdateBridge,
|
||||
appUpdateAnalytics: NaviAnalytics.InAppUpdate =
|
||||
NaviAnalytics.naviAnalytics.InAppUpdate(PROFILE_SCREEN)
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
val savedStateRegistryOwner = LocalSavedStateRegistryOwner.current
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.clickable(enabled = (appUpdateState == AppUpdateState.UPDATE_NOT_AVAILABLE)) {
|
||||
appUpdateAnalytics.appUpdateUnavailableClickEvent()
|
||||
showToast(
|
||||
content = { toast: Toast? -> AppUpdateToastUI(data.toastData, toast) },
|
||||
context = context,
|
||||
lifecycleOwner = lifecycleOwner,
|
||||
savedStateRegistryOwner = savedStateRegistryOwner
|
||||
)
|
||||
.clickable(enabled = (appUpdateState != AppUpdateState.UPDATE_DOWNLOADING)) {
|
||||
when (appUpdateState) {
|
||||
AppUpdateState.UPDATE_AVAILABLE,
|
||||
AppUpdateState.UPDATE_FAILED,
|
||||
AppUpdateState.UPDATE_PENDING -> {
|
||||
appUpdateAnalytics.appUpdateNudgeClickEvent()
|
||||
inAppUpdateBridge.downloadUpdate()
|
||||
}
|
||||
AppUpdateState.UPDATE_DOWNLOADED -> {
|
||||
appUpdateAnalytics.appInstallNudgeClickEvent()
|
||||
inAppUpdateBridge.installUpdate()
|
||||
}
|
||||
AppUpdateState.UPDATE_NOT_AVAILABLE -> {
|
||||
appUpdateAnalytics.appUpdateUnavailableClickEvent()
|
||||
data.toastData?.let { state.showToast(it) }
|
||||
}
|
||||
AppUpdateState.UPDATE_DOWNLOADING -> {}
|
||||
}
|
||||
}
|
||||
.padding(start = 16.dp, top = 12.dp, bottom = 12.dp, end = 16.dp)
|
||||
) {
|
||||
@@ -98,10 +96,7 @@ fun ProfileAppUpdateWidget(
|
||||
AppUpdateState.UPDATE_AVAILABLE,
|
||||
AppUpdateState.UPDATE_FAILED,
|
||||
AppUpdateState.UPDATE_PENDING -> {
|
||||
UpdateButton(text = data.updateCtaText.orEmpty()) {
|
||||
appUpdateAnalytics.appUpdateNudgeClickEvent()
|
||||
inAppUpdateBridge.downloadUpdate()
|
||||
}
|
||||
UpdateButton(text = data.updateCtaText.orEmpty())
|
||||
}
|
||||
AppUpdateState.UPDATE_DOWNLOADING -> {
|
||||
ElexText(
|
||||
@@ -114,10 +109,7 @@ fun ProfileAppUpdateWidget(
|
||||
)
|
||||
}
|
||||
AppUpdateState.UPDATE_DOWNLOADED -> {
|
||||
UpdateButton(text = data.installCtaText.orEmpty()) {
|
||||
appUpdateAnalytics.appInstallNudgeClickEvent()
|
||||
inAppUpdateBridge.installUpdate()
|
||||
}
|
||||
UpdateButton(text = data.installCtaText.orEmpty())
|
||||
}
|
||||
AppUpdateState.UPDATE_NOT_AVAILABLE -> {}
|
||||
}
|
||||
@@ -125,14 +117,13 @@ fun ProfileAppUpdateWidget(
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun UpdateButton(text: String, onClick: () -> Unit) {
|
||||
private fun UpdateButton(text: String) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier =
|
||||
Modifier.clip(RoundedCornerShape(4.dp))
|
||||
.background(color = Color(0xFFF5F5F5), shape = RoundedCornerShape(4.dp))
|
||||
.clickable { onClick() }
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||
) {
|
||||
ElexText(
|
||||
@@ -145,52 +136,3 @@ private fun UpdateButton(text: String, onClick: () -> Unit) {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showToast(
|
||||
content: @Composable (toast: Toast?) -> Unit,
|
||||
context: Context,
|
||||
lifecycleOwner: LifecycleOwner,
|
||||
savedStateRegistryOwner: SavedStateRegistryOwner
|
||||
) {
|
||||
var toast: Toast? = null
|
||||
toast =
|
||||
Toast(context).apply {
|
||||
duration = Toast.LENGTH_SHORT
|
||||
val views = ComposeView(context).apply { setContent { content(toast) } }
|
||||
views.setViewTreeLifecycleOwner(lifecycleOwner)
|
||||
views.setViewTreeSavedStateRegistryOwner(savedStateRegistryOwner)
|
||||
view = views
|
||||
}
|
||||
toast.show()
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AppUpdateToastUI(data: AppUpdateToastData?, toast: Toast?) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.background(color = Color(0xFFEDECFF), shape = RoundedCornerShape(4.dp))
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
ElexAsyncImage(
|
||||
icon = data?.leftIconUrl,
|
||||
contentDescription = "",
|
||||
modifier = Modifier.height(16.dp).width(16.dp)
|
||||
)
|
||||
ElexText(
|
||||
text = data?.message.orEmpty(),
|
||||
color = Color(0xFF191919),
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeightEnum.NAVI_HEADLINE_REGULAR,
|
||||
lineHeight = 22.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
ElexAsyncImage(
|
||||
icon = data?.dismissIconUrl,
|
||||
contentDescription = "",
|
||||
modifier = Modifier.height(16.dp).width(16.dp).clickable { toast?.cancel() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,9 +17,11 @@ import com.navi.uitron.viewmodel.UiTronViewModel
|
||||
import com.naviapp.common.listeners.InAppUpdateBridge
|
||||
import com.naviapp.common.model.AppUpdateState
|
||||
import com.naviapp.home.compose.model.ProfileAppUpdateSettingData
|
||||
import com.naviapp.home.model.CustomDismissibleToastState
|
||||
import com.naviapp.home.model.HomeCustomWidgetType
|
||||
|
||||
class ProfileCustomUiTronRenderer(
|
||||
private val state: CustomDismissibleToastState,
|
||||
private val appUpdateState: AppUpdateState,
|
||||
private val inAppUpdateBridge: InAppUpdateBridge,
|
||||
) : CommonCustomUiTronRenderer() {
|
||||
@@ -38,6 +40,7 @@ class ProfileCustomUiTronRenderer(
|
||||
dataMap?.getOrElse(APP_UPDATE_DATA) { null } as? ProfileAppUpdateSettingData
|
||||
if (data != null) {
|
||||
ProfileAppUpdateWidget(
|
||||
state = state,
|
||||
data = data,
|
||||
appUpdateState = appUpdateState,
|
||||
inAppUpdateBridge = inAppUpdateBridge
|
||||
|
||||
@@ -16,6 +16,7 @@ import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.LocalOverscrollConfiguration
|
||||
import androidx.compose.foundation.ScrollState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@@ -50,6 +51,8 @@ import com.naviapp.analytics.utils.NaviAnalytics
|
||||
import com.naviapp.forge.model.WidgetModelDefinition
|
||||
import com.naviapp.home.compose.activity.HomePageActivity
|
||||
import com.naviapp.home.compose.model.ProfileHeaderWidgetData
|
||||
import com.naviapp.home.model.CustomDismissibleToastState
|
||||
import com.naviapp.home.ui.CustomDismissibleToast
|
||||
import com.naviapp.home.ui.state.ProfileScreenState
|
||||
import com.naviapp.home.viewmodel.ProfileVM
|
||||
import com.naviapp.home.viewmodel.SharedVM
|
||||
@@ -63,6 +66,7 @@ fun ProfileScreen(
|
||||
activity: HomePageActivity,
|
||||
sharedVM: SharedVM
|
||||
) {
|
||||
val state = remember { CustomDismissibleToastState() }
|
||||
val profileItems = profileVM.profileScreenDataState.collectAsStateWithLifecycle()
|
||||
val appUpdateState by sharedVM.appUpdateState.collectAsStateWithLifecycle()
|
||||
val scrollState = rememberScrollState()
|
||||
@@ -87,42 +91,47 @@ fun ProfileScreen(
|
||||
}
|
||||
is ProfileScreenState.Success -> {
|
||||
CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.fillMaxSize()
|
||||
.background(
|
||||
profileData.data.screenStructure
|
||||
?.content
|
||||
?.backgroundColor
|
||||
?.hexToComposeColor ?: Color.White
|
||||
)
|
||||
.navigationBarsPadding()
|
||||
) {
|
||||
// header widget
|
||||
profileData.data.screenStructure?.header?.let {
|
||||
ProfileScreenWidgetRenderer(
|
||||
widget = it,
|
||||
viewModel = profileVM,
|
||||
drawerState = drawerState,
|
||||
appUpdateState = appUpdateState,
|
||||
inAppUpdateBridge = activity
|
||||
)
|
||||
}
|
||||
|
||||
// content widgets
|
||||
Content(
|
||||
scrollState = { scrollState },
|
||||
Box(Modifier.fillMaxSize().navigationBarsPadding()) {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.fillMaxSize()
|
||||
.background(
|
||||
profileData.data.screenStructure
|
||||
?.content
|
||||
?.backgroundColor
|
||||
?.hexToComposeColor ?: Color.White
|
||||
)
|
||||
) {
|
||||
profileData.data.screenStructure?.content?.widgets?.forEach { widget ->
|
||||
// header widget
|
||||
profileData.data.screenStructure?.header?.let {
|
||||
ProfileScreenWidgetRenderer(
|
||||
widget = widget,
|
||||
state = state,
|
||||
widget = it,
|
||||
viewModel = profileVM,
|
||||
drawerState = drawerState,
|
||||
appUpdateState = appUpdateState,
|
||||
inAppUpdateBridge = activity
|
||||
)
|
||||
}
|
||||
|
||||
// content widgets
|
||||
Content(
|
||||
scrollState = { scrollState },
|
||||
) {
|
||||
profileData.data.screenStructure?.content?.widgets?.forEach { widget
|
||||
->
|
||||
ProfileScreenWidgetRenderer(
|
||||
state = state,
|
||||
widget = widget,
|
||||
viewModel = profileVM,
|
||||
drawerState = drawerState,
|
||||
appUpdateState = appUpdateState,
|
||||
inAppUpdateBridge = activity
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
CustomDismissibleToast(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,12 +17,14 @@ import com.naviapp.common.listeners.InAppUpdateBridge
|
||||
import com.naviapp.common.model.AppUpdateState
|
||||
import com.naviapp.forge.model.WidgetModelDefinition
|
||||
import com.naviapp.forge.model.WidgetTypes
|
||||
import com.naviapp.home.model.CustomDismissibleToastState
|
||||
import com.naviapp.home.model.HomeCustomWidgetType
|
||||
import com.naviapp.home.viewmodel.ProfileVM
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun ProfileScreenWidgetRenderer(
|
||||
state: CustomDismissibleToastState,
|
||||
widget: WidgetModelDefinition<UiTronResponse>?,
|
||||
viewModel: ProfileVM,
|
||||
drawerState: () -> DrawerState,
|
||||
@@ -36,7 +38,7 @@ fun ProfileScreenWidgetRenderer(
|
||||
dataMap = widget.widgetData?.data,
|
||||
uiTronViewModel = viewModel,
|
||||
customUiTronRenderer =
|
||||
ProfileCustomUiTronRenderer(appUpdateState, inAppUpdateBridge)
|
||||
ProfileCustomUiTronRenderer(state, appUpdateState, inAppUpdateBridge)
|
||||
)
|
||||
.Render(composeViews = widget.widgetData?.parentComposeView.orEmpty())
|
||||
}
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2024 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.naviapp.home.model
|
||||
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
|
||||
class CustomDismissibleToastState {
|
||||
|
||||
var updateState by mutableStateOf(false)
|
||||
private set
|
||||
|
||||
private val _toastData = mutableStateOf<ToastData?>(null)
|
||||
val toastData: MutableState<ToastData?> = _toastData
|
||||
|
||||
fun showToast(toastData: ToastData) {
|
||||
_toastData.value = toastData
|
||||
updateState = !updateState
|
||||
}
|
||||
|
||||
fun isNotEmpty(): Boolean {
|
||||
return _toastData.value != null
|
||||
}
|
||||
}
|
||||
|
||||
data class ToastData(
|
||||
val message: String? = null,
|
||||
val leftIconUrl: String? = null,
|
||||
val dismissIconUrl: String? = null,
|
||||
val backgroundColor: String? = null
|
||||
)
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2024 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.naviapp.home.ui
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
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.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.navi.elex.atoms.ElexAsyncImage
|
||||
import com.navi.elex.atoms.ElexText
|
||||
import com.navi.elex.font.FontWeightEnum
|
||||
import com.navi.uitron.utils.hexToComposeColor
|
||||
import com.naviapp.home.model.CustomDismissibleToastState
|
||||
import java.util.Timer
|
||||
import kotlin.concurrent.schedule
|
||||
|
||||
@Composable
|
||||
fun CustomDismissibleToast(state: CustomDismissibleToastState) {
|
||||
val showToast = remember { mutableStateOf(false) }
|
||||
val data by rememberUpdatedState(newValue = state.toastData)
|
||||
val timer = Timer("Animation Timer", true)
|
||||
DisposableEffect(key1 = state.updateState) {
|
||||
showToast.value = true
|
||||
timer.schedule(3000L) { showToast.value = false }
|
||||
onDispose {
|
||||
timer.cancel()
|
||||
timer.purge()
|
||||
}
|
||||
}
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.Bottom,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = state.isNotEmpty() && showToast.value,
|
||||
enter = fadeIn(),
|
||||
exit = fadeOut()
|
||||
) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.padding(start = 16.dp, end = 16.dp, bottom = 32.dp)
|
||||
.background(
|
||||
color =
|
||||
data.value?.backgroundColor?.hexToComposeColor ?: Color.Black,
|
||||
shape = RoundedCornerShape(4.dp)
|
||||
)
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
ElexAsyncImage(
|
||||
icon = data.value?.leftIconUrl,
|
||||
contentDescription = "",
|
||||
modifier = Modifier.size(16.dp)
|
||||
)
|
||||
ElexText(
|
||||
text = data.value?.message.orEmpty(),
|
||||
color = Color(0xFF191919),
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeightEnum.NAVI_HEADLINE_REGULAR,
|
||||
lineHeight = 22.sp,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
ElexAsyncImage(
|
||||
icon = data.value?.dismissIconUrl,
|
||||
contentDescription = "",
|
||||
modifier =
|
||||
Modifier.size(16.dp).clickable {
|
||||
showToast.value = false
|
||||
timer.cancel()
|
||||
timer.purge()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user