TP-84808 | Insurance Tab Compose Migration (#12774)

This commit is contained in:
Balrambhai Sharma
2024-09-26 17:57:11 +05:30
committed by GitHub
parent 9bf6bfb5fa
commit 8d4244f26c
12 changed files with 422 additions and 731 deletions

View File

@@ -1,354 +0,0 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.naviapp.common.tab
import android.annotation.SuppressLint
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.tween
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.LocalOverscrollConfiguration
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
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.neverEqualPolicy
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.viewinterop.AndroidView
import androidx.fragment.app.viewModels
import com.facebook.react.ReactApplication
import com.facebook.shimmer.ShimmerFrameLayout
import com.navi.analytics.utils.NaviTrackEvent
import com.navi.base.model.ActionData
import com.navi.base.model.CtaData
import com.navi.base.model.CtaType
import com.navi.base.model.NaviClickAction
import com.navi.base.utils.isNotNull
import com.navi.base.utils.orFalse
import com.navi.base.utils.orZero
import com.navi.common.analytics.NaviAnalytics
import com.navi.common.callback.RequestToCallbackHandler
import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper
import com.navi.common.listeners.NewBottomSheetListener
import com.navi.common.react.ReactPreLoadHeadLessActivity
import com.navi.common.ui.errorview.FullScreenErrorComposeView
import com.navi.common.ui.fragment.BaseFragment
import com.navi.insurance.R
import com.navi.insurance.common.models.InsuranceTabState
import com.navi.insurance.navigator.NaviInsuranceDeeplinkNavigator
import com.navi.insurance.util.Constants
import com.navi.insurance.util.log
import com.navi.naviwidgets.callbacks.WidgetCallback
import com.navi.naviwidgets.composewidget.GenericComposableWidgetFactory
import com.navi.naviwidgets.extensions.FloatingButtonOverlay
import com.navi.naviwidgets.extensions.isScrollingDown
import com.navi.uitron.render.UiTronRenderer
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class InsuranceTabFragment(private val lazyListState: () -> LazyListState? = { null }) :
BaseFragment(), WidgetCallback, NewBottomSheetListener {
private val viewModel by viewModels<InsuranceTabViewModel>()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val layout =
ComposeView(requireContext()).apply {
setContent { RenderUiTronDataSecondary(lazyListState) }
}
observeCtaData()
viewModel.clearCacheOnVersionUpgrade()
if (
FirebaseRemoteConfigHelper.getBoolean(
key = FirebaseRemoteConfigHelper.ENABLE_REACT_PREFETCH_IN_TAB,
defaultValue = false
)
) {
try {
preLoadReactResources()
} catch (e: Exception) {
e.log()
}
}
return layout
}
private fun preLoadReactResources() {
try {
val reactApplication = requireActivity().application as ReactApplication
reactApplication.reactNativeHost.reactInstanceManager.createReactContextInBackground()
val intent = Intent(requireActivity(), ReactPreLoadHeadLessActivity::class.java)
startActivity(intent)
} catch (e: Exception) {
e.log()
}
}
override fun onResume() {
super.onResume()
viewModel.fetchInsuranceTabDetails()
}
private fun observeCtaData() {
viewModel.redirectionCta.observe(viewLifecycleOwner) { ctaData ->
NaviTrackEvent.sendEvent(ctaData, screenName)
ctaData.let {
NaviInsuranceDeeplinkNavigator.navigate(
requireActivity(),
it,
finish = it.finish.orFalse(),
clearTask = it.clearTask.orFalse(),
callbackHandler =
object : RequestToCallbackHandler {
override fun onCallbackRaised() {
viewModel.fetchInsuranceTabDetails()
}
}
)
}
}
}
override fun onClick(naviClickAction: NaviClickAction, widgetId: String?) {
if (naviClickAction is CtaData) {
NaviTrackEvent.sendEvent(naviClickAction, screenName)
val bundle = Bundle()
bundle.putParcelable(Constants.PARAMS_EXTRA, naviClickAction)
when (naviClickAction.type) {
CtaType.DISMISS_TOAST.value -> {
var toastIdentifier: String? = null
naviClickAction.parameters?.forEach { lineItem ->
if (lineItem.key == "toastIdentifier") {
toastIdentifier = lineItem.value.orEmpty()
}
}
viewModel.dismissCta(toastIdentifier.orEmpty())
}
CtaType.CANCEL_QUOTE.value -> {
var quoteId: String? = null
naviClickAction.parameters?.forEach { lineItem ->
if (lineItem.key == "quoteId") {
quoteId = lineItem.value.orEmpty()
}
}
viewModel.closePolicyCard(quoteId.orEmpty())
}
else -> {
NaviInsuranceDeeplinkNavigator.navigate(
requireActivity(),
naviClickAction,
finish = naviClickAction.finish.orFalse(),
clearTask = naviClickAction.clearTask.orFalse(),
callbackHandler =
object : RequestToCallbackHandler {
override fun onCallbackRaised() {
viewModel.fetchInsuranceTabDetails()
}
}
)
}
}
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
NaviTrackEvent.trackEventOnClickStream(NaviAnalytics.INSURANCE_TAB_INIT)
}
@OptIn(ExperimentalFoundationApi::class)
@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
@Composable
fun RenderUiTronDataSecondary(lazyListState: () -> LazyListState? = { null }) {
val state by viewModel.insuranceTabDataFlow.collectAsState()
val listState = lazyListState() ?: rememberLazyListState()
val isScrollingDown = listState.isScrollingDown()
val widgetCallback: WidgetCallback = this
Box(modifier = Modifier.fillMaxSize()) {
when {
state.data.isNotNull() -> {
LaunchedEffect(Unit) {
state.data?.pageMetaData?.analyticsEvents?.forEach {
NaviTrackEvent.trackEvent(
it.name.orEmpty(),
it.properties,
it.isNeededForAppsflyer.orFalse(),
it.isNeededForFirebase.orFalse()
)
}
}
Box {
Scaffold(
modifier =
Modifier.fillMaxSize()
.animateContentSize(
animationSpec = tween(durationMillis = 600, delayMillis = 0)
),
topBar = {
if (state.data?.headerWidget.isNotNull()) {
UiTronRenderer(state.data?.headerWidget?.data, viewModel)
.Render(
composeViews =
state.data
?.headerWidget
?.parentComposeView
.orEmpty()
)
} else {
state.data?.headerNativeWidget?.let { data ->
Box(modifier = Modifier.background(Color.White)) {
GenericComposableWidgetFactory(
data = data,
widgetCallback = widgetCallback
)
}
}
}
},
bottomBar = {
if (state.data?.footerWidget.isNotNull()) {
UiTronRenderer(state.data?.footerWidget?.data, viewModel)
.Render(
composeViews =
state.data
?.footerWidget
?.parentComposeView
.orEmpty()
)
} else {
state.data?.footerNativeWidget?.let { data ->
Box(modifier = Modifier.background(Color.White)) {
GenericComposableWidgetFactory(
data = data,
widgetCallback = widgetCallback
)
}
}
}
}
) {
CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
ContentComposable(
listState = listState,
widgetCallback = widgetCallback,
state = state
)
}
}
if (state.data?.floatingButtonData.isNotNull()) {
FloatingButtonOverlay(
state.data?.floatingButtonData,
!isScrollingDown,
widgetCallback,
screenName
)
}
}
}
state.isLoading -> {
ShowShimmer()
}
state.hasErrorOccurred -> {
FullScreenErrorComposeView(
error = state.genericErrorResponse,
onRetryClick = { viewModel.fetchInsuranceTabDetails() }
)
}
}
}
}
@Composable
fun ContentComposable(
listState: LazyListState,
state: InsuranceTabState? = null,
widgetCallback: WidgetCallback?,
) {
val currentList = state?.data?.listOfNativeWidgets
var dataList by
remember(
key1 = state?.data,
calculation = { mutableStateOf(currentList, neverEqualPolicy()) }
)
LazyColumn(state = listState, modifier = Modifier.fillMaxWidth().wrapContentHeight()) {
if (state?.data?.listOfUiTronWidgets.isNotNull()) {
items(state?.data?.listOfUiTronWidgets?.size.orZero()) { index ->
val uiTronResponse = state?.data?.listOfUiTronWidgets?.getOrNull(index)
Column(modifier = Modifier.wrapContentSize()) {
UiTronRenderer(uiTronResponse?.data, viewModel)
.Render(composeViews = uiTronResponse?.parentComposeView.orEmpty())
}
}
} else {
items(dataList?.size.orZero(), key = { index -> index }) { index ->
val widgetData = dataList?.getOrNull(index)
Column(modifier = Modifier.wrapContentSize()) {
GenericComposableWidgetFactory(
data = widgetData,
widgetCallback = widgetCallback,
onWidgetUpdate = { data ->
val updatedList = dataList?.toMutableList() ?: mutableListOf()
updatedList[index] = data
dataList = updatedList
}
)
}
}
}
}
}
@Composable
fun ShowShimmer() {
val layout = ShimmerFrameLayout(requireContext())
layout.addView(
LayoutInflater.from(requireContext())
.inflate(R.layout.shimmer_tab_layout, layout, false)
)
AndroidView(modifier = Modifier.fillMaxSize().background(Color.White), factory = { layout })
}
override fun buttonClick(actionData: ActionData?) = Unit
companion object {
fun newInstance(
bundle: Bundle?,
lazyListState: () -> LazyListState? = { null }
): InsuranceTabFragment {
return InsuranceTabFragment(lazyListState).apply { arguments = bundle }
}
}
override val screenName: String
get() = NaviAnalytics.INSURANCE_TAB_INIT
}

View File

@@ -7,27 +7,41 @@
package com.naviapp.common.tab
import android.content.Intent
import android.os.Bundle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.viewModelScope
import com.facebook.react.ReactApplication
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.navi.analytics.utils.NaviTrackEvent
import com.navi.base.cache.repository.NaviCacheRepository
import com.navi.base.model.CtaData
import com.navi.base.model.CtaType
import com.navi.base.model.NaviClickAction
import com.navi.base.utils.isNotNull
import com.navi.base.utils.isNull
import com.navi.base.utils.orFalse
import com.navi.base.utils.orZero
import com.navi.common.analytics.NaviAnalytics
import com.navi.common.analytics.NaviAnalytics.Companion.CACHE_API_LOAD_ERROR
import com.navi.common.analytics.NaviAnalytics.Companion.INSURANCE_TAB_INIT
import com.navi.common.callback.RequestToCallbackHandler
import com.navi.common.model.ModuleNameV2
import com.navi.common.network.models.ErrorMetaData
import com.navi.common.react.ReactPreLoadHeadLessActivity
import com.navi.common.uitron.model.action.CtaAction
import com.navi.common.utils.SingleLiveEvent
import com.navi.common.utils.isValidResponse
import com.navi.common.viewmodel.BaseVM
import com.navi.insurance.common.models.GiErrorMetaData
import com.navi.insurance.common.models.InsuranceTabState
import com.navi.insurance.navigator.NaviInsuranceDeeplinkNavigator
import com.navi.insurance.network.ApiErrorTagType
import com.navi.insurance.util.Constants
import com.navi.insurance.util.log
import com.navi.uitron.model.action.AnalyticsAction
import com.navi.uitron.model.data.UiTronAction
import com.naviapp.home.compose.activity.HomePageActivity
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
@@ -54,7 +68,7 @@ constructor(
val insuranceTabDataFlow: StateFlow<InsuranceTabState>
get() = _insuranceTabDataFlow.asStateFlow()
val redirectionCta = SingleLiveEvent<CtaData>()
private val redirectionCta = SingleLiveEvent<CtaData>()
fun fetchInsuranceTabDetails() {
if (_insuranceTabDataFlow.value.data.isNull()) {
@@ -84,7 +98,7 @@ constructor(
)
)
sendFailureEvent(
NaviAnalytics.INSURANCE_TAB_INIT,
INSURANCE_TAB_INIT,
errorUnifiedResponse,
ModuleNameV2.Insurance.name
)
@@ -100,7 +114,7 @@ constructor(
}
}
fun dismissCta(toastIdentifier: String) {
private fun dismissCta(toastIdentifier: String) {
viewModelScope.safeLaunch(coroutineContext = Dispatchers.IO) {
val response = repository.dismissCta(toastIdentifier)
if (response.statusCode.orZero().equals(200)) {} else {
@@ -131,7 +145,7 @@ constructor(
}
}
fun closePolicyCard(quoteId: String) {
private fun closePolicyCard(quoteId: String) {
_insuranceTabDataFlow.update { it.copy(isLoading = true, data = null) }
viewModelScope.safeLaunch(coroutineContext = Dispatchers.IO) {
val response = repository.closePolicyCard(quoteId)
@@ -192,7 +206,7 @@ constructor(
}
private fun handleCtaAction(ctaAction: CtaAction) {
ctaAction.ctaData?.let { ctaData -> redirectionCta.postValue(ctaAction.ctaData) }
ctaAction.ctaData?.let { ctaData -> redirectionCta.postValue(ctaData) }
}
fun clearCacheOnVersionUpgrade() {
@@ -214,16 +228,97 @@ constructor(
error = response.error,
errorMetaData =
ErrorMetaData(
methodName = ApiErrorTagType.INSURANCE_TAB_SCREEN_PAGE_ERROR.value,
methodName = CACHE_API_LOAD_ERROR,
flowName = GiErrorMetaData.INSURANCE_TAB_FLOW
)
)
sendFailureEvent(
NaviAnalytics.INSURANCE_TAB_INIT,
CACHE_API_LOAD_ERROR,
errorUnifiedResponse,
ModuleNameV2.Insurance.name
)
}
}
}
fun preLoadReactResources(activity: HomePageActivity) {
try {
val reactApplication = activity.application as ReactApplication
reactApplication.reactNativeHost.reactInstanceManager.createReactContextInBackground()
val intent = Intent(activity, ReactPreLoadHeadLessActivity::class.java)
activity.startActivity(intent)
} catch (e: Exception) {
e.log()
}
}
fun observeCtaData(
viewmodel: InsuranceTabViewModel,
activity: HomePageActivity,
lifeCycleOwner: LifecycleOwner
) {
viewmodel.redirectionCta.observe(lifeCycleOwner) { ctaData ->
NaviTrackEvent.sendEvent(ctaData, INSURANCE_TAB_INIT)
ctaData.let {
NaviInsuranceDeeplinkNavigator.navigate(
activity,
it,
finish = it.finish.orFalse(),
clearTask = it.clearTask.orFalse(),
callbackHandler =
object : RequestToCallbackHandler {
override fun onCallbackRaised() {
viewmodel.fetchInsuranceTabDetails()
}
}
)
}
}
}
fun onCtaClick(
naviClickAction: NaviClickAction,
viewModel: InsuranceTabViewModel,
activity: HomePageActivity
) {
if (naviClickAction is CtaData) {
NaviTrackEvent.sendEvent(naviClickAction, INSURANCE_TAB_INIT)
val bundle = Bundle()
bundle.putParcelable(Constants.PARAMS_EXTRA, naviClickAction)
when (naviClickAction.type) {
CtaType.DISMISS_TOAST.value -> {
var toastIdentifier: String? = null
naviClickAction.parameters?.forEach { lineItem ->
if (lineItem.key == "toastIdentifier") {
toastIdentifier = lineItem.value.orEmpty()
}
}
viewModel.dismissCta(toastIdentifier.orEmpty())
}
CtaType.CANCEL_QUOTE.value -> {
var quoteId: String? = null
naviClickAction.parameters?.forEach { lineItem ->
if (lineItem.key == "quoteId") {
quoteId = lineItem.value.orEmpty()
}
}
viewModel.closePolicyCard(quoteId.orEmpty())
}
else -> {
NaviInsuranceDeeplinkNavigator.navigate(
activity,
naviClickAction,
finish = naviClickAction.finish.orFalse(),
clearTask = naviClickAction.clearTask.orFalse(),
callbackHandler =
object : RequestToCallbackHandler {
override fun onCallbackRaised() {
viewModel.fetchInsuranceTabDetails()
}
}
)
}
}
}
}
}

View File

@@ -7,7 +7,6 @@
package com.naviapp.common.viewmodel
import android.os.Bundle
import androidx.lifecycle.viewModelScope
import com.navi.common.lottie.LottieRemoteHelper
import com.navi.common.viewmodel.BaseVM
@@ -38,8 +37,6 @@ class BottomNavBarVM @Inject constructor() : BaseVM() {
private val naviAnalyticsEventTracker = NaviAnalytics.naviAnalytics.Dashboard()
var isInsuranceTabOnNewInstanceCalled = false
private val _shouldUpdateSelectedBottomTab =
MutableSharedFlow<UpdateTabFragmentData>(replay = 1)
val shouldUpdateSelectedBottomTab = _shouldUpdateSelectedBottomTab.asSharedFlow()
@@ -70,15 +67,6 @@ class BottomNavBarVM @Inject constructor() : BaseVM() {
_bottomStickyNudgeData.update { BottomStickyNudgeData(visible, bottomStickyNudgeState) }
}
fun updateInsuranceTabFragmentInCompose(screen: String, bundle: Bundle?, subType: String) {
viewModelScope.launch {
isInsuranceTabOnNewInstanceCalled = true
_shouldUpdateSelectedBottomTab.emit(
UpdateTabFragmentData(screen = screen, bundle = bundle, subType = subType)
)
}
}
fun triggerTabClickedEvent(tabId: String, screenName: String) {
when (tabId) {
BottomBarTabType.HOME.name -> {

View File

@@ -72,7 +72,6 @@ import com.navi.common.resourcemanager.manager.ResourceManager
import com.navi.common.utils.ApiPollScheduler
import com.navi.common.utils.BiometricPromptUtils.Companion.getScreenLockEventName
import com.navi.common.utils.CommonNaviAnalytics
import com.navi.common.utils.Constants.EMPTY
import com.navi.common.utils.Constants.INSURANCE
import com.navi.common.utils.Constants.IN_APP_UPDATE_REQUEST_CODE
import com.navi.common.utils.Constants.SUB_REDIRECT
@@ -977,15 +976,6 @@ class HomePageActivity :
private fun redirectToTab() {
intent?.getStringExtra(Constants.REDIRECT_STATUS)?.let {
val bundle = intent.extras
val subType = bundle?.getString(SUB_REDIRECT) ?: EMPTY
if (subType.lowercase().contains(INSURANCE)) {
bottomNavBarVM.updateInsuranceTabFragmentInCompose(
screen = it,
bundle = bundle,
subType = subType
)
}
updateTab(screen = it, bundle = intent.extras)
}
}

View File

@@ -1,165 +0,0 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.naviapp.home.compose.fragment
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.foundation.lazy.LazyListState
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.navi.base.utils.isNotNull
import com.navi.common.ui.fragment.BaseFragment
import com.navi.insurance.common.fragment.LandingPageInfoFragment
import com.navi.insurance.common.util.NavigationHandler.Companion.URL_INSURANCE_TAB_FRAGMENT
import com.naviapp.R
import com.naviapp.common.tab.InsuranceTabFragment
import com.naviapp.common.tab.insurance.ui.TrialInsuranceDashboardFragment
import com.naviapp.common.viewmodel.BottomNavBarVM
import com.naviapp.databinding.FragmentInsuranceContainerBinding
import com.naviapp.home.dashboard.models.response.DashboardTab
import com.naviapp.home.dashboard.ui.DashboardFragment
import com.naviapp.home.dashboard.ui.ProductFragment
import com.naviapp.utils.Constants
import kotlinx.coroutines.launch
class InsuranceContainerFragment(private val lazyListState: LazyListState? = null) :
BaseFragment() {
override val screenName: String
get() = "InsuranceContainerFragment"
private lateinit var binding: FragmentInsuranceContainerBinding
private val bottomNavBarVM by lazy {
ViewModelProvider(requireActivity())[BottomNavBarVM::class.java]
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentInsuranceContainerBinding.inflate(inflater)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (!bottomNavBarVM.isInsuranceTabOnNewInstanceCalled) {
navigateToNextScreen(
currentScreenTag = DashboardFragment.TAG,
bundle = null,
subType = URL_INSURANCE_TAB_FRAGMENT
)
}
initFragmentChangeListener()
}
private fun initFragmentChangeListener() {
viewLifecycleOwner.lifecycleScope.launch {
bottomNavBarVM.shouldUpdateSelectedBottomTab.collect { fragmentData ->
bottomNavBarVM.clearShouldUpdateSelectedBottomTab()
navigateToNextScreen(fragmentData.screen, fragmentData.bundle, fragmentData.subType)
}
}
}
private fun hideFragments(fragmentTransaction: FragmentTransaction) {
val fragments = childFragmentManager.fragments
fragments.forEach { currentFragment ->
currentFragment?.let { fragmentTransaction.hide(it) }
}
}
fun navigateToNextScreen(currentScreenTag: String, bundle: Bundle?, subType: String? = null) {
val fragmentTag =
if (subType == null) {
currentScreenTag
} else {
currentScreenTag + subType
}
val fragment: Fragment
if (
bundle?.get(Constants.OPEN_NEW_FRAGMENT).isNotNull() &&
bundle?.get(Constants.OPEN_NEW_FRAGMENT) == "true"
) {
fragment = getFragment(screen = currentScreenTag, bundle = bundle, subType = subType)
} else {
fragment =
childFragmentManager.findFragmentByTag(fragmentTag)
?: getFragment(screen = currentScreenTag, bundle = bundle, subType = subType)
}
val fragmentTransaction = childFragmentManager.beginTransaction()
if (!childFragmentManager.isStateSaved && !childFragmentManager.isDestroyed) {
hideFragments(fragmentTransaction)
if (fragment.isAdded) {
fragmentTransaction.show(fragment)
} else {
fragmentTransaction.add(R.id.container, fragment, fragmentTag)
}
fragmentTransaction.commitNow()
}
}
private fun getFragment(screen: String, bundle: Bundle?, subType: String? = null): Fragment {
return getInsuranceFragment(
screen,
subType ?: bundle?.getString(com.navi.common.utils.Constants.SUB_REDIRECT),
bundle
)
}
private fun getInsuranceFragment(screen: String, subType: String?, bundle: Bundle?): Fragment {
return when (subType) {
ProductFragment.DashboardTypes.Insurance.name ->
handleInsuranceDashboardTab(screen = screen, subType = subType, bundle = bundle)
ProductFragment.DashboardTypes.Insurance_tab_page.name ->
InsuranceTabFragment.newInstance(bundle) { lazyListState }
ProductFragment.DashboardTypes.Trial_Insurance_Dashboard.name ->
TrialInsuranceDashboardFragment.newInstance(bundle) { lazyListState }
ProductFragment.DashboardTypes.Insurance_landing_page.name ->
LandingPageInfoFragment().apply { arguments = bundle }
else -> DashboardFragment.getInstance(bundle)
}
}
private fun handleInsuranceDashboardTab(
screen: String,
subType: String,
bundle: Bundle?
): Fragment {
if (
bundle?.get(Constants.OPEN_NEW_FRAGMENT).isNotNull() &&
bundle?.get(Constants.OPEN_NEW_FRAGMENT) == "true"
) {
for (fragment in childFragmentManager.fragments) {
childFragmentManager.beginTransaction().remove(fragment).commit()
}
} else {
val fragment =
childFragmentManager.findFragmentByTag(
screen + ProductFragment.DashboardTypes.Insurance_landing_page.name
)
if (fragment != null) {
val fragmentTransaction = childFragmentManager.beginTransaction()
fragmentTransaction.remove(fragment)
fragmentTransaction.commit()
}
}
return ProductFragment.getInstance(DashboardTab(subType), bundle)
}
companion object {
fun getInstance(lazyListState: LazyListState? = null): InsuranceContainerFragment {
return InsuranceContainerFragment(lazyListState)
}
}
}

View File

@@ -7,6 +7,7 @@
package com.naviapp.home.compose.home.navigation
import InsuranceTabScreen
import InvestmentsScreen
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
@@ -18,17 +19,13 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.navi.common.ui.fragment.BaseFragment
import com.naviapp.analytics.utils.NaviAnalytics
import com.naviapp.common.viewmodel.InAppUpdateVM
import com.naviapp.dashboard.viewmodels.DashboardSharedVM
import com.naviapp.home.common.hopperProcessor.processHandlerImpl.Hopper
import com.naviapp.home.compose.activity.HomePageActivity
import com.naviapp.home.compose.fragment.InsuranceContainerFragment
import com.naviapp.home.compose.home.ui.footer.utils.FragmentContainer
import com.naviapp.home.compose.home.ui.screen.HomeScreen
import com.naviapp.home.dashboard.ui.compose.loansTab.LoansTabScreen
import com.naviapp.home.reducer.HpEvents
import com.naviapp.home.reducer.HpStates
import com.naviapp.home.viewmodel.HomeViewModel
import com.naviapp.home.viewmodel.NotificationVM
@@ -87,22 +84,14 @@ fun NavGraphNavigationItem(
.background(Color.White)
)
NavigationItem.Insurance.tabId -> {
FragmentContainer(
modifier = Modifier.fillMaxSize().padding(bottom = 56.dp).statusBarsPadding(),
fragmentManager = activity.supportFragmentManager
) { containerId ->
val fragment = InsuranceContainerFragment.getInstance(insuranceScrollState)
val screenName =
(fragment as? BaseFragment)?.screenName ?: NaviAnalytics.NEW_HOME_ACTIVITY
homeVM().sendEvent(HpEvents.UpdateCurrentLoadedFragmentName(screenName))
if (!fragment.isStateSaved && !fragment.isAdded) {
if (fragment.isAdded) {
show(fragment)
} else {
add(containerId, fragment, "insurance")
}
}
}
InsuranceTabScreen(
activity = activity,
modifier =
Modifier.fillMaxSize()
.padding(bottom = 56.dp)
.statusBarsPadding()
.background(Color.White)
)
}
}
}

View File

@@ -1,163 +0,0 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.naviapp.home.compose.home.ui.footer.utils
import android.annotation.SuppressLint
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.viewinterop.AndroidView
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentContainerView
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentTransaction
import androidx.fragment.app.commit
import com.navi.base.utils.isNotNull
import com.naviapp.common.tab.InsuranceTabFragment
import com.naviapp.common.tab.insurance.ui.TrialInsuranceDashboardFragment
import com.naviapp.home.compose.activity.HomePageActivity
import com.naviapp.home.dashboard.models.response.DashboardTab
import com.naviapp.home.dashboard.ui.DashboardFragment
import com.naviapp.home.dashboard.ui.ProductFragment
import com.naviapp.utils.Constants
@Composable
fun FragmentContainer(
modifier: Modifier = Modifier,
fragmentManager: FragmentManager,
commit: FragmentTransaction.(containerId: Int) -> Unit
) {
Column(modifier = modifier) {
val containerId by rememberSaveable { mutableIntStateOf(View.generateViewId()) }
AndroidView(
modifier = Modifier.background(color = Color.White).fillMaxSize(),
factory = { context ->
fragmentManager.findFragmentById(containerId)?.view?.also {
(it.parent as? ViewGroup)?.removeView(it)
}
?: FragmentContainerView(context)
.apply { id = containerId }
.also { fragmentManager.commit(allowStateLoss = true) { commit(it.id) } }
},
update = {}
)
}
}
private fun getFragment(
activity: HomePageActivity,
screen: String,
bundle: Bundle?,
subType: String? = null,
listState: () -> LazyListState? = { null }
): Fragment =
getDashboardFragment(
activity = activity,
screen = screen,
subType = subType ?: bundle?.getString(com.navi.common.utils.Constants.SUB_REDIRECT),
bundle = bundle,
listState = listState
)
fun getDashboardFragment(
activity: HomePageActivity,
screen: String,
subType: String?,
bundle: Bundle?,
listState: () -> LazyListState? = { null }
): Fragment {
return when (subType) {
ProductFragment.DashboardTypes.Insurance.name ->
handleInsuranceDashboardTab(
activity = activity,
screen = screen,
subType = subType,
bundle = bundle
)
ProductFragment.DashboardTypes.Insurance_tab_page.name ->
InsuranceTabFragment.newInstance(bundle)
ProductFragment.DashboardTypes.Trial_Insurance_Dashboard.name -> {
TrialInsuranceDashboardFragment.newInstance(bundle)
}
else -> DashboardFragment.getInstance(bundle)
}
}
@SuppressLint("CommitTransaction")
private fun handleInsuranceDashboardTab(
activity: HomePageActivity,
screen: String,
subType: String,
bundle: Bundle?
): Fragment {
if (
bundle?.get(Constants.OPEN_NEW_FRAGMENT).isNotNull() &&
bundle?.get(Constants.OPEN_NEW_FRAGMENT) == "true"
) {
for (fragment in activity.supportFragmentManager.fragments) {
activity.supportFragmentManager.beginTransaction().remove(fragment).commit()
}
} else {
val fragment =
activity.supportFragmentManager.findFragmentByTag(
screen + ProductFragment.DashboardTypes.Insurance_landing_page.name
)
if (fragment != null) {
val fragmentTransaction = activity.supportFragmentManager.beginTransaction()
fragmentTransaction.remove(fragment)
fragmentTransaction.commit()
}
}
return ProductFragment.getInstance(DashboardTab(subType), bundle)
}
fun loadFragmentInContainer(
activity: HomePageActivity,
currentScreenTag: String,
bundle: Bundle?,
subType: String?,
fragmentTag: String,
listState: () -> LazyListState? = { null }
): Fragment {
val fragment: Fragment
if (
bundle?.get(Constants.OPEN_NEW_FRAGMENT).isNotNull() &&
bundle?.get(Constants.OPEN_NEW_FRAGMENT) == "true"
) {
fragment =
getFragment(
activity = activity,
screen = currentScreenTag,
bundle = bundle,
subType = subType,
listState = listState
)
} else {
fragment =
activity.supportFragmentManager.findFragmentByTag(fragmentTag)
?: getFragment(
activity = activity,
screen = currentScreenTag,
bundle = bundle,
subType = subType,
listState = listState
)
}
return fragment
}

View File

@@ -0,0 +1,53 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.naviapp.home.dashboard.ui.compose.insuranceTab
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import com.navi.common.firebaseremoteconfig.FirebaseRemoteConfigHelper
import com.navi.insurance.util.log
import com.naviapp.common.tab.InsuranceTabViewModel
import com.naviapp.home.compose.activity.HomePageActivity
@Composable
fun InitScreenLifeCycleListener(activity: HomePageActivity, viewmodel: InsuranceTabViewModel) {
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(key1 = lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_RESUME -> {
viewmodel.fetchInsuranceTabDetails()
viewmodel.clearCacheOnVersionUpgrade()
viewmodel.observeCtaData(
viewmodel = viewmodel,
activity = activity,
lifeCycleOwner = lifecycleOwner
)
if (
FirebaseRemoteConfigHelper.getBoolean(
key = FirebaseRemoteConfigHelper.ENABLE_REACT_PREFETCH_IN_TAB,
defaultValue = false
)
) {
try {
viewmodel.preLoadReactResources(activity)
} catch (e: Exception) {
e.log()
}
}
}
else -> {}
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
}
}

View File

@@ -0,0 +1,33 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel
import com.navi.base.model.NaviClickAction
import com.navi.naviwidgets.callbacks.WidgetCallback
import com.naviapp.common.tab.InsuranceTabViewModel
import com.naviapp.home.compose.activity.HomePageActivity
import com.naviapp.home.dashboard.ui.compose.insuranceTab.InsuranceTabInit
import com.naviapp.home.dashboard.ui.compose.insuranceTab.RenderUiTronDataSecondary
@Composable
fun InsuranceTabScreen(activity: HomePageActivity, modifier: Modifier = Modifier) {
val viewModel: InsuranceTabViewModel = hiltViewModel()
val widgetCallback =
object : WidgetCallback {
override fun onClick(naviClickAction: NaviClickAction, widgetId: String?) {
viewModel.onCtaClick(naviClickAction, viewModel, activity)
}
}
InsuranceTabInit(viewModel = viewModel, activity = activity)
RenderUiTronDataSecondary(
modifier = modifier,
viewModel = viewModel,
widgetCallback = widgetCallback
)
}

View File

@@ -0,0 +1,203 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.naviapp.home.dashboard.ui.compose.insuranceTab
import android.annotation.SuppressLint
import android.view.LayoutInflater
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.tween
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.LocalOverscrollConfiguration
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
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.neverEqualPolicy
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.facebook.shimmer.ShimmerFrameLayout
import com.navi.analytics.utils.NaviTrackEvent
import com.navi.base.utils.isNotNull
import com.navi.base.utils.orFalse
import com.navi.base.utils.orZero
import com.navi.common.analytics.NaviAnalytics.Companion.INSURANCE_TAB_INIT
import com.navi.common.ui.errorview.FullScreenErrorComposeView
import com.navi.insurance.R
import com.navi.insurance.common.models.InsuranceTabResponse
import com.navi.naviwidgets.callbacks.WidgetCallback
import com.navi.naviwidgets.composewidget.GenericComposableWidgetFactory
import com.navi.naviwidgets.extensions.FloatingButtonOverlay
import com.navi.naviwidgets.extensions.isScrollingDown
import com.navi.uitron.render.UiTronRenderer
import com.naviapp.common.tab.InsuranceTabViewModel
@OptIn(ExperimentalFoundationApi::class)
@SuppressLint("UnusedMaterialScaffoldPaddingParameter")
@Composable
fun RenderUiTronDataSecondary(
modifier: Modifier,
viewModel: InsuranceTabViewModel,
widgetCallback: WidgetCallback? = null
) {
val state by viewModel.insuranceTabDataFlow.collectAsStateWithLifecycle()
val listState = rememberLazyListState()
val isScrollingDown = listState.isScrollingDown()
Box(modifier = modifier.fillMaxSize()) {
when {
state.data.isNotNull() -> {
LaunchedEffect(Unit) {
state.data?.pageMetaData?.analyticsEvents?.forEach {
NaviTrackEvent.trackEvent(
it.name.orEmpty(),
it.properties,
it.isNeededForAppsflyer.orFalse(),
it.isNeededForFirebase.orFalse()
)
}
}
Box {
Scaffold(
modifier =
Modifier.fillMaxSize()
.animateContentSize(
animationSpec = tween(durationMillis = 600, delayMillis = 0)
),
topBar = {
if (state.data?.headerWidget.isNotNull()) {
UiTronRenderer(state.data?.headerWidget?.data, viewModel)
.Render(
composeViews =
state.data?.headerWidget?.parentComposeView.orEmpty()
)
} else {
state.data?.headerNativeWidget?.let { data ->
Box(modifier = Modifier.background(Color.White)) {
GenericComposableWidgetFactory(
data = data,
widgetCallback = widgetCallback
)
}
}
}
},
bottomBar = {
if (state.data?.footerWidget.isNotNull()) {
UiTronRenderer(state.data?.footerWidget?.data, viewModel)
.Render(
composeViews =
state.data?.footerWidget?.parentComposeView.orEmpty()
)
} else {
state.data?.footerNativeWidget?.let { data ->
Box(modifier = Modifier.background(Color.White)) {
GenericComposableWidgetFactory(
data = data,
widgetCallback = widgetCallback
)
}
}
}
}
) {
CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
ContentComposable(
listState = listState,
widgetCallback = widgetCallback,
data = state?.data,
viewModel = viewModel
)
}
}
if (state.data?.floatingButtonData.isNotNull()) {
FloatingButtonOverlay(
state.data?.floatingButtonData,
!isScrollingDown,
widgetCallback,
INSURANCE_TAB_INIT
)
}
}
}
state.isLoading -> {
ShowShimmer()
}
state.hasErrorOccurred -> {
FullScreenErrorComposeView(
error = state.genericErrorResponse,
onRetryClick = { viewModel.fetchInsuranceTabDetails() }
)
}
}
}
}
@Composable
fun ContentComposable(
listState: LazyListState,
data: InsuranceTabResponse? = null,
widgetCallback: WidgetCallback?,
viewModel: InsuranceTabViewModel
) {
var dataList by
remember(
key1 = data,
calculation = { mutableStateOf(data?.listOfNativeWidgets, neverEqualPolicy()) }
)
LazyColumn(state = listState, modifier = Modifier.fillMaxWidth().wrapContentHeight()) {
if (data?.listOfUiTronWidgets.isNotNull()) {
items(data?.listOfUiTronWidgets?.size.orZero()) { index ->
val uiTronResponse = data?.listOfUiTronWidgets?.getOrNull(index)
Column(modifier = Modifier.wrapContentSize()) {
UiTronRenderer(uiTronResponse?.data, viewModel)
.Render(composeViews = uiTronResponse?.parentComposeView.orEmpty())
}
}
} else {
items(dataList?.size.orZero(), key = { index -> index }) { index ->
val widgetData = dataList?.getOrNull(index)
Column(modifier = Modifier.wrapContentSize()) {
GenericComposableWidgetFactory(
data = widgetData,
widgetCallback = widgetCallback,
onWidgetUpdate = { data ->
val updatedList = dataList?.toMutableList() ?: mutableListOf()
updatedList[index] = data
dataList = updatedList
}
)
}
}
}
}
}
@Composable
fun ShowShimmer() {
val context = LocalContext.current
val layout = ShimmerFrameLayout(context)
layout.addView(LayoutInflater.from(context).inflate(R.layout.shimmer_tab_layout, layout, false))
AndroidView(modifier = Modifier.fillMaxSize().background(Color.White), factory = { layout })
}

View File

@@ -0,0 +1,21 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.naviapp.home.dashboard.ui.compose.insuranceTab
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import com.navi.analytics.utils.NaviTrackEvent
import com.navi.common.analytics.NaviAnalytics.Companion.INSURANCE_TAB_INIT
import com.naviapp.common.tab.InsuranceTabViewModel
import com.naviapp.home.compose.activity.HomePageActivity
@Composable
fun InsuranceTabInit(viewModel: InsuranceTabViewModel, activity: HomePageActivity) {
InitScreenLifeCycleListener(viewmodel = viewModel, activity = activity)
LaunchedEffect(Unit) { NaviTrackEvent.trackEventOnClickStream(INSURANCE_TAB_INIT) }
}

View File

@@ -90,6 +90,7 @@ class NaviAnalytics private constructor() {
const val PL_FINARKEIN_EVENT = "finarkein_event_"
const val HL_FINARKEIN_EVENT = "hl_finarkein_event_"
const val INSURANCE_TAB_INIT = "hi_insurance_tab_screen_init"
const val CACHE_API_LOAD_ERROR = "hi_cache_api_load_error"
const val DISMISS_TOAST = "hi_dismiss_toast"
const val CLOSE_POLICY_CARD = "hi_close_policy_card"
const val TRIAL_INSURANCE_DASHBOARD_INIT = "hi_trial_insurance_dashboard_init"