TP-69166 | Loans tab migration to compose (#11345)

This commit is contained in:
Anupam Kumar
2024-06-18 16:56:30 +05:30
committed by GitHub
parent fa545aba10
commit a416dd6ac7
13 changed files with 304 additions and 424 deletions

View File

@@ -438,6 +438,9 @@ class HomePageActivity :
BottomBarTabType.INSURANCE.name -> {
setStatusBarColor(R.color.white)
}
BottomBarTabType.LOAN.name -> {
setStatusBarColor(R.color.white)
}
}
}
@@ -597,6 +600,7 @@ class HomePageActivity :
observeNpsRatingSubmitResponse()
observeInAppUpdateData()
observeNetworkConnectivity()
observeLoansTabPaymentData()
}
private fun observeGIPaymentFailed() {
@@ -2432,4 +2436,12 @@ class HomePageActivity :
}
}
}
private fun observeLoansTabPaymentData() {
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
sharedVM.initiatePaymentFromLoansTabScreen.collect { initiatePaymentClick(it) }
}
}
}
}

View File

@@ -36,7 +36,6 @@ fun HomePageNavHost(
paymentVM: PaymentVM,
naviHomeAnalytics: NaviAnalytics.Home,
investmentListState: () -> LazyListState,
loanTabScrollState: () -> LazyListState,
inAppUpdateVM: InAppUpdateVM
) {
NavHost(
@@ -58,7 +57,6 @@ fun HomePageNavHost(
sharedVM = sharedVM,
naviHomeAnalytics = naviHomeAnalytics,
investmentListState = investmentListState,
loanTabScrollState = loanTabScrollState,
inAppUpdateVM = inAppUpdateVM
)
}

View File

@@ -23,11 +23,9 @@ import com.naviapp.dashboard.viewmodels.DashboardSharedVM
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.footer.utils.loadFragmentInContainer
import com.naviapp.home.compose.home.ui.screen.HomeScreen
import com.naviapp.home.compose.listener.HomeScreenCallbackListener
import com.naviapp.home.dashboard.ui.DashboardFragment
import com.naviapp.home.dashboard.ui.ProductFragment
import com.naviapp.home.dashboard.ui.compose.loansTab.LoansTabScreen
import com.naviapp.home.viewmodel.HomeVM
import com.naviapp.home.viewmodel.NotificationVM
import com.naviapp.home.viewmodel.SharedVM
@@ -44,7 +42,6 @@ fun NavGraphNavigationItem(
sharedVM: SharedVM,
naviHomeAnalytics: NaviAnalytics.Home,
investmentListState: () -> LazyListState,
loanTabScrollState: () -> LazyListState,
inAppUpdateVM: InAppUpdateVM
) {
val insuranceScrollState = rememberLazyListState()
@@ -82,34 +79,11 @@ fun NavGraphNavigationItem(
sharedVM = sharedVM
)
NavigationItem.Loan.tabId ->
FragmentContainer(
modifier = Modifier.fillMaxSize().padding(bottom = 56.dp).statusBarsPadding(),
fragmentManager = activity.supportFragmentManager
) { containerId ->
val subType = ProductFragment.DashboardTypes.Loans.name
val currentScreenTag = DashboardFragment.TAG
val fragmentTag = currentScreenTag + subType
val fragment =
loadFragmentInContainer(
activity = activity,
currentScreenTag = currentScreenTag,
bundle = null,
subType = subType,
fragmentTag = fragmentTag
) {
loanTabScrollState()
}
homeVM.setCurrentFragmentScreenName(
(fragment as? BaseFragment)?.screenName ?: NaviAnalytics.NEW_HOME_ACTIVITY
)
if (!fragment.isStateSaved && !fragment.isAdded) {
if (fragment.isAdded) {
show(fragment)
} else {
add(containerId, fragment, fragmentTag)
}
}
}
LoansTabScreen(
activity = activity,
sharedVM = sharedVM,
modifier = Modifier.fillMaxSize().padding(bottom = 56.dp).statusBarsPadding()
)
NavigationItem.Insurance.tabId -> {
FragmentContainer(
modifier = Modifier.fillMaxSize().padding(bottom = 56.dp).statusBarsPadding(),

View File

@@ -34,7 +34,6 @@ 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.home.fragment.LoanTabFragment
import com.naviapp.utils.Constants
@Composable
@@ -83,7 +82,6 @@ fun getDashboardFragment(
listState: () -> LazyListState? = { null }
): Fragment {
return when (subType) {
ProductFragment.DashboardTypes.Loans.name -> getLoansTabFragment(listState)
ProductFragment.DashboardTypes.Insurance.name ->
handleInsuranceDashboardTab(
activity = activity,
@@ -100,10 +98,6 @@ fun getDashboardFragment(
}
}
private fun getLoansTabFragment(listState: () -> LazyListState? = { null }): Fragment {
return LoanTabFragment.getInstance(listState)
}
@SuppressLint("CommitTransaction")
private fun handleInsuranceDashboardTab(
activity: HomePageActivity,

View File

@@ -26,7 +26,9 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavHostController
import androidx.navigation.compose.rememberNavController
import com.navi.analytics.utils.NaviTrackEvent
import com.navi.base.deeplink.DeepLinkManager
import com.navi.base.deeplink.util.DeeplinkConstants
import com.navi.base.utils.orFalse
import com.navi.common.ui.compose.DrawerState
import com.navi.common.ui.compose.NaviDrawerValue
import com.navi.common.ui.compose.NaviModalNavigationDrawer
@@ -136,7 +138,6 @@ private fun InitHomeActivityScreen(
navController: NavHostController
) {
val drawerState = rememberNaviDrawerState(NaviDrawerValue.Closed)
val loanTabScrollState = rememberLazyListState()
val investmentListState = rememberLazyListState()
val coroutineScope = rememberCoroutineScope()
@@ -183,7 +184,6 @@ private fun InitHomeActivityScreen(
bottomNavBarVM = bottomNavBarVM,
inAppUpdateVM = inAppUpdateVM,
investmentListState = { investmentListState },
loanTabScrollState = { loanTabScrollState },
homeVM = homeVM,
paymentVM = paymentVM,
dashboardSharedVM = dashboardSharedVM,
@@ -198,16 +198,26 @@ private fun InitHomeActivityScreen(
private fun handleBottomSheetAction(action: UiTronAction?, activity: HomePageActivity) {
when (action) {
is CtaAction -> {
when (action.ctaData?.type) {
HomeCtaTypes.REDIRECTION_CTA.name -> {
when (action.ctaData?.url) {
DeeplinkConstants.LOGOUT -> {
activity.logout()
action.ctaData?.let { ctaData ->
when (ctaData.type) {
HomeCtaTypes.REDIRECTION_CTA.name -> {
when (ctaData.url) {
DeeplinkConstants.LOGOUT -> {
activity.logout()
}
else -> Unit
}
else -> Unit
}
else -> {
DeepLinkManager.getDeepLinkListener()
?.navigateTo(
activity = activity,
ctaData,
finish = ctaData.finish.orFalse(),
clearTask = ctaData.clearTask.orFalse()
)
}
}
else -> Unit
}
}
is AnalyticsAction -> {
@@ -240,7 +250,6 @@ private fun ScreenContent(
bottomNavBarVM: BottomNavBarVM,
inAppUpdateVM: InAppUpdateVM,
investmentListState: () -> LazyListState,
loanTabScrollState: () -> LazyListState,
homeVM: HomeVM,
dashboardSharedVM: DashboardSharedVM,
paymentVM: PaymentVM,
@@ -260,7 +269,6 @@ private fun ScreenContent(
sharedVM = sharedVM,
paymentVM = paymentVM,
naviHomeAnalytics = naviHomeAnalytics,
loanTabScrollState = loanTabScrollState,
investmentListState = investmentListState,
inAppUpdateVM = inAppUpdateVM
)

View File

@@ -5,7 +5,7 @@
*
*/
package com.naviapp.home.dashboard.ui.compose
package com.naviapp.home.dashboard.ui.compose.loansTab
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.LocalOverscrollConfiguration
@@ -14,13 +14,11 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.wrapContentHeight
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.ui.Modifier
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.navi.base.utils.orZero
import com.navi.uitron.model.UiTronResponse
@@ -31,15 +29,15 @@ import com.naviapp.home.dashboard.viewmodels.LoanTabVm
@Composable
fun LoansTab(
loansTabVm: LoanTabVm = hiltViewModel(),
toggleStatusBarColor: (String, String, String) -> Unit,
lazyListState: () -> LazyListState?
loansTabVm: LoanTabVm,
toggleStatusBarColor: (String) -> Unit,
modifier: Modifier = Modifier
) {
val loansTabUiTronConfig = loansTabVm.loansTabUiTronData.collectAsStateWithLifecycle()
loansTabUiTronConfig.value.let { state ->
if (state is LoanTabVm.LoansTabUiTronScreenState.Success) {
RenderWidgets(loansTabVm, state.data, toggleStatusBarColor, lazyListState)
RenderWidgets(loansTabVm, state.data, toggleStatusBarColor, modifier)
}
}
}
@@ -47,10 +45,10 @@ fun LoansTab(
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun RenderWidgets(
loansTabVm: LoanTabVm = hiltViewModel(),
loansTabVm: LoanTabVm,
data: ScreenDefinition,
toggleStatusBarColor: (String, String, String) -> Unit,
lazyListState: () -> LazyListState? = { null }
toggleStatusBarColor: (String) -> Unit,
modifier: Modifier = Modifier
) {
data.screenStructure
?.header
@@ -61,15 +59,13 @@ fun RenderWidgets(
?.backGroundBrushData
?.let {
toggleStatusBarColor(
it.brushType.orEmpty(),
it.colorStops?.get(0)?.second.toString(),
it.colorStops?.get(1)?.second.toString()
)
}
loansTabVm.handleActions(actionData = data.screenStructure?.renderActions?.postRenderAction)
val scrollState = lazyListState() ?: rememberLazyListState()
val scrollState = rememberLazyListState()
Scaffold(
modifier = Modifier.fillMaxSize(),
modifier = modifier.fillMaxSize(),
content = {
CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
LazyColumn(
@@ -107,14 +103,14 @@ fun LoansTabWidgetRenderer(widget: WidgetModelDefinition<UiTronResponse>?, viewM
if (widget == null) return
return when (widget.widgetType) {
LoanTabVm.WidgetTypes.UI_TRON_WIDGET.name -> {
renderUitronWidgets(widget, viewModel)
RenderUitronWidgets(widget, viewModel)
}
else -> Unit
}
}
@Composable
fun renderUitronWidgets(widget: WidgetModelDefinition<UiTronResponse>?, viewModel: LoanTabVm) {
fun RenderUitronWidgets(widget: WidgetModelDefinition<UiTronResponse>?, viewModel: LoanTabVm) {
UiTronRenderer(
dataMap = widget?.widgetData?.data,
uiTronViewModel = viewModel,

View File

@@ -0,0 +1,106 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.naviapp.home.dashboard.ui.compose.loansTab
import android.app.Activity
import com.navi.base.deeplink.DeepLinkManager
import com.navi.base.model.CtaData
import com.navi.base.model.CtaType
import com.navi.base.utils.orFalse
import com.navi.common.utils.Constants.HL_DYNAMIC_MODULE_NAME
import com.navi.common.utils.Constants.PL_DYNAMIC_MODULE_NAME
import com.navi.common.utils.isDynamicModuleInstalled
import com.navi.common.utils.setStatusBarColorInt
import com.navi.design.utils.parseColorSafe
import com.navi.rr.utils.NaviRRAnalytics.Companion.currency
import com.naviapp.home.compose.model.InitiatePaymentFromComposeData
import com.naviapp.home.dashboard.viewmodels.LoanTabVm
import com.naviapp.home.model.HpBottomSheetContent
import com.naviapp.home.model.HpBottomSheetRenderType
import com.naviapp.home.model.HpBottomSheetState
import com.naviapp.home.viewmodel.SharedVM
import com.naviapp.part_prepayment.PartPrePaymentActivity
import com.naviapp.payment.activities.NaviPaymentActivity
import com.naviapp.payment.fragments.PaymentType
import com.naviapp.payment.models.Amount
import com.naviapp.utils.Constants
import com.naviapp.utils.Constants.SEE_MORE_BOTTOMSHEET
import com.naviapp.utils.LOAN_ACCOUNT_NUMBER
class LoansTabHelper {
fun navigateTo(ctaData: CtaData, activity: Activity, sharedVM: SharedVM, viewModel: LoanTabVm) {
viewModel.setLoansTabInitStatus(false)
when (ctaData.url) {
SEE_MORE_BOTTOMSHEET -> {
viewModel.screenDefinition?.let { data ->
sharedVM.updateBottomSheetState(
state = HpBottomSheetState.Visible,
content =
HpBottomSheetContent(
renderType = HpBottomSheetRenderType.UI_TRON,
uiTronContent = listOf(data)
)
)
}
}
CtaType.PAYMENT_PL.name -> {
handlePaymentActionData(ctaData, sharedVM)
}
else -> {
DeepLinkManager.getDeepLinkListener()
?.navigateTo(
activity = activity,
ctaData,
finish = ctaData.finish.orFalse(),
clearTask = ctaData.clearTask.orFalse()
)
}
}
}
fun toggleStatusBarColor(startColor: String, activity: Activity) {
activity.setStatusBarColorInt(startColor.parseColorSafe())
}
private fun handlePaymentActionData(actionData: CtaData, sharedVM: SharedVM) {
sharedVM.updatePaymentDataFromLoansTabScreen(
data =
InitiatePaymentFromComposeData(
amount =
Amount(
value =
actionData.parameters
?.firstOrNull { it.key == NaviPaymentActivity.AMOUNT_DATA }
?.value
?.toDouble(),
currency = currency
),
isPreClosure = false,
paymentType =
PaymentType.get(
actionData.parameters
?.firstOrNull { it.key == PartPrePaymentActivity.PAYMENT_TYPE }
?.value
)
.toString(),
repaymentType = null,
loanType =
actionData.parameters?.firstOrNull { it.key == Constants.LOAN_TYPE }?.value,
loanAccountNumber =
actionData.parameters?.firstOrNull { it.key == LOAN_ACCOUNT_NUMBER }?.value
)
)
}
fun fetchLoansTab(viewModel: LoanTabVm) {
viewModel.refreshLoansTabUiTronConfigs(
cashLoanAppInstalled = isDynamicModuleInstalled(PL_DYNAMIC_MODULE_NAME),
homeLoanAppInstalled = isDynamicModuleInstalled(HL_DYNAMIC_MODULE_NAME)
)
}
}

View File

@@ -0,0 +1,39 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.naviapp.home.dashboard.ui.compose.loansTab
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.naviapp.home.dashboard.viewmodels.LoanTabVm
import com.naviapp.utils.Constants.LOANS_TAB_SCREEN
@Composable
fun LoansTabLifeCycleListener(loansTabVm: LoanTabVm, loansTabHelper: LoansTabHelper) {
val lifecycleOwner = LocalLifecycleOwner.current
val lifecycle = lifecycleOwner.lifecycle
DisposableEffect(lifecycle) {
val observer = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_RESUME -> {
if (loansTabVm.getLoansTabInitStatus().not()) {
sendInitEvent(LOANS_TAB_SCREEN)
loansTabVm.setLoansTabInitStatus(true)
loansTabHelper.fetchLoansTab(loansTabVm)
}
}
else -> {}
}
}
lifecycle.addObserver(observer)
onDispose { lifecycle.removeObserver(observer) }
}
}

View File

@@ -0,0 +1,84 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.naviapp.home.dashboard.ui.compose.loansTab
import android.app.Activity
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.navi.analytics.utils.NaviTrackEvent
import com.navi.common.ui.errorview.FullScreenErrorComposeView
import com.navi.common.utils.Constants.HL_DYNAMIC_MODULE_NAME
import com.navi.common.utils.Constants.PL_DYNAMIC_MODULE_NAME
import com.navi.common.utils.isDynamicModuleInstalled
import com.naviapp.home.compose.activity.HomePageActivity
import com.naviapp.home.compose.home.ui.content.HomePageContentShimmer
import com.naviapp.home.dashboard.viewmodels.LoanTabVm
import com.naviapp.home.viewmodel.SharedVM
@Composable
fun LoansTabScreen(activity: HomePageActivity, sharedVM: SharedVM, modifier: Modifier = Modifier) {
val loansTabVm by lazy { ViewModelProvider(activity)[LoanTabVm::class.java] }
val loansTabHelper by lazy { LoansTabHelper() }
LoansTabLifeCycleListener(loansTabVm, loansTabHelper)
InitObserver(
viewModel = loansTabVm,
activity = activity,
sharedVM = sharedVM,
modifier = modifier,
loansTabHelper = loansTabHelper
)
}
@Composable
private fun InitObserver(
viewModel: LoanTabVm,
activity: Activity,
sharedVM: SharedVM,
modifier: Modifier = Modifier,
loansTabHelper: LoansTabHelper
) {
val lifecycleOwner = LocalLifecycleOwner.current
when (val shimmerState = viewModel.loansTabUiTronData.collectAsStateWithLifecycle().value) {
is LoanTabVm.LoansTabUiTronScreenState.Loading -> {
HomePageContentShimmer()
}
is LoanTabVm.LoansTabUiTronScreenState.Success -> {
LoansTab(
loansTabVm = viewModel,
toggleStatusBarColor = { loansTabHelper.toggleStatusBarColor(it, activity) },
modifier = modifier
)
}
is LoanTabVm.LoansTabUiTronScreenState.Error -> {
FullScreenErrorComposeView(
error = shimmerState.error,
onRetryClick = {
viewModel.fetchLoansTabUiTronConfigs(
cashLoanAppInstalled = isDynamicModuleInstalled(PL_DYNAMIC_MODULE_NAME),
homeLoanAppInstalled = isDynamicModuleInstalled(HL_DYNAMIC_MODULE_NAME)
)
}
)
}
}
viewModel.redirectionCta.observe(lifecycleOwner) { ctaData ->
loansTabHelper.navigateTo(
ctaData,
activity = activity,
viewModel = viewModel,
sharedVM = sharedVM
)
}
}
fun sendInitEvent(screenName: String) {
NaviTrackEvent.trackEvent(screenName)
}

View File

@@ -21,12 +21,13 @@ import com.navi.uitron.model.data.UiTronAction
import com.naviapp.forge.model.ScreenDefinition
import com.naviapp.forge.model.WidgetModelDefinition
import com.naviapp.home.dashboard.repo.LoanTabRepository
import com.naviapp.home.fragment.LoanTabFragment.Companion.LOAN_TAB_FRAGMENT
import com.naviapp.utils.Constants.LOANS_TAB_SCREEN
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
@HiltViewModel
class LoanTabVm @Inject constructor(private val repository: LoanTabRepository) : BaseVM() {
@@ -37,6 +38,7 @@ class LoanTabVm @Inject constructor(private val repository: LoanTabRepository) :
val redirectionCta = SingleLiveEvent<CtaData>()
var screenDefinition: WidgetModelDefinition<UiTronResponse>? = null
private var isLoansTabInitCalled: Boolean = false
init {
handleCallbacks()
@@ -68,7 +70,7 @@ class LoanTabVm @Inject constructor(private val repository: LoanTabRepository) :
fun fetchLoansTabUiTronConfigs(cashLoanAppInstalled: Boolean, homeLoanAppInstalled: Boolean) {
viewModelScope.safeLaunch(Dispatchers.IO) {
_loansTabUiTronData.emit(LoansTabUiTronScreenState.Loading)
_loansTabUiTronData.update { LoansTabUiTronScreenState.Loading }
fetchData(cashLoanAppInstalled, homeLoanAppInstalled)
}
}
@@ -80,6 +82,7 @@ class LoanTabVm @Inject constructor(private val repository: LoanTabRepository) :
}
private suspend fun fetchData(cashLoanAppInstalled: Boolean, homeLoanAppInstalled: Boolean) {
_loansTabUiTronData.emit(LoansTabUiTronScreenState.Loading)
val response =
repository.fetchLoansTabUiTronConfig(cashLoanAppInstalled, homeLoanAppInstalled)
_loansTabUiTronData.emit(
@@ -94,12 +97,20 @@ class LoanTabVm @Inject constructor(private val repository: LoanTabRepository) :
?: run {
val errorUnifiedResponse =
getErrorUnifiedResponse(errors = response.errors, error = response.error)
sendFailureEvent(LOAN_TAB_FRAGMENT, errorUnifiedResponse)
sendFailureEvent(LOANS_TAB_SCREEN, errorUnifiedResponse)
LoansTabUiTronScreenState.Error(errorUnifiedResponse.errorResponse)
}
)
}
fun getLoansTabInitStatus(): Boolean {
return isLoansTabInitCalled
}
fun setLoansTabInitStatus(status: Boolean) {
isLoansTabInitCalled = status
}
sealed interface LoansTabUiTronScreenState {
data object Loading : LoansTabUiTronScreenState

View File

@@ -1,354 +0,0 @@
/*
*
* * Copyright © 2024 by Navi Technologies Limited
* * All rights reserved. Strictly confidential
*
*/
package com.naviapp.home.fragment
import android.app.Activity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.ModalBottomSheetLayout
import androidx.compose.material.ModalBottomSheetState
import androidx.compose.material.ModalBottomSheetValue
import androidx.compose.material.rememberModalBottomSheetState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
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.core.content.ContextCompat
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.facebook.shimmer.ShimmerFrameLayout
import com.navi.analytics.utils.NaviTrackEvent
import com.navi.ap.utils.constants.BOTTOMSHEET
import com.navi.ap.utils.constants.BOTTOM_SHEET_TOP_CORNER_RADIUS
import com.navi.base.deeplink.DeepLinkManager
import com.navi.base.model.CtaData
import com.navi.base.model.CtaType
import com.navi.base.utils.orFalse
import com.navi.common.ui.errorview.FullScreenErrorComposeView
import com.navi.common.utils.Constants.HL_DYNAMIC_MODULE_NAME
import com.navi.common.utils.Constants.PL_DYNAMIC_MODULE_NAME
import com.navi.common.utils.isDynamicModuleInstalled
import com.navi.common.utils.setStatusBarColorInt
import com.navi.design.utils.parseColorSafe
import com.navi.uitron.model.UiTronResponse
import com.naviapp.R
import com.naviapp.analytics.utils.NaviAnalytics
import com.naviapp.common.fragment.InfoBottomSheetListener
import com.naviapp.forge.model.WidgetModelDefinition
import com.naviapp.home.dashboard.ui.compose.LoansTab
import com.naviapp.home.dashboard.ui.compose.renderUitronWidgets
import com.naviapp.home.dashboard.viewmodels.LoanTabVm
import com.naviapp.home.model.BottomBarTabType
import com.naviapp.home.viewmodel.SharedVM
import com.naviapp.part_prepayment.PartPrePaymentActivity
import com.naviapp.payment.activities.NaviPaymentActivity
import com.naviapp.payment.fragments.PaymentBaseFragment
import com.naviapp.payment.fragments.PaymentType
import com.naviapp.payment.models.Amount
import com.naviapp.utils.Constants
import com.naviapp.utils.LOAN_ACCOUNT_NUMBER
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class LoanTabFragment(private val listState: () -> LazyListState? = { null }) :
PaymentBaseFragment(), InfoBottomSheetListener {
private val viewModel by viewModels<LoanTabVm>()
private val sharedVM by lazy { ViewModelProvider(requireActivity())[SharedVM::class.java] }
private var isFirstHiddenCallDone = false
override val screenName: String
get() = LOAN_TAB_FRAGMENT
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val layout =
ComposeView(requireContext()).apply {
setContent {
InitTabChangeObserver()
ScreenComposable(listState)
showBottomSheet()
}
}
sendInitEvent()
initObserver()
initializationPaymentDetail()
return layout
}
private fun sendInitEvent() {
NaviTrackEvent.trackEvent(screenName)
}
@Composable
fun ScreenComposable(listState: () -> LazyListState? = { null }) {
when (val shimmerState = viewModel.loansTabUiTronData.collectAsStateWithLifecycle().value) {
is LoanTabVm.LoansTabUiTronScreenState.Loading -> {
shimmerView()
}
is LoanTabVm.LoansTabUiTronScreenState.Success -> {
LoansTab(
loansTabVm = viewModel,
toggleStatusBarColor = ::toggleStatusBarColor,
lazyListState = listState
)
}
is LoanTabVm.LoansTabUiTronScreenState.Error -> {
FullScreenErrorComposeView(
error = shimmerState.error,
onRetryClick = {
viewModel.fetchLoansTabUiTronConfigs(
cashLoanAppInstalled = isDynamicModuleInstalled(PL_DYNAMIC_MODULE_NAME),
homeLoanAppInstalled = isDynamicModuleInstalled(HL_DYNAMIC_MODULE_NAME)
)
}
)
}
}
}
private fun toggleStatusBarColor(orientation: String, startColor: String, endColor: String) {
if (this@LoanTabFragment.isVisible) {
activity?.setStatusBarColorInt(startColor.parseColorSafe())
}
}
@Composable
fun shimmerView() {
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 onResume() {
super.onResume()
refreshLoansTabUiTronConfigs()
}
private fun refreshLoansTabUiTronConfigs() {
viewModel.refreshLoansTabUiTronConfigs(
cashLoanAppInstalled = isDynamicModuleInstalled(PL_DYNAMIC_MODULE_NAME),
homeLoanAppInstalled = isDynamicModuleInstalled(HL_DYNAMIC_MODULE_NAME)
)
}
private fun initObserver() {
viewModel.redirectionCta.observe(viewLifecycleOwner) { ctaData ->
sharedVM.setBottomSheetState(false)
navigateTo(ctaData)
}
}
private fun navigateTo(ctaData: CtaData) {
when (ctaData.url) {
SEE_MORE_BOTTOMSHEET -> {
when (ctaData.type) {
BOTTOMSHEET -> {
sharedVM.setBottomSheetState(true)
}
}
}
CtaType.PAYMENT_PL.name -> {
handlePaymentActionData(ctaData)
}
else -> {
DeepLinkManager.getDeepLinkListener()
?.navigateTo(
activity = activity,
ctaData,
finish = ctaData.finish.orFalse(),
clearTask = ctaData.clearTask.orFalse()
)
}
}
}
private fun handlePaymentActionData(actionData: CtaData) {
actionData.parameters?.apply {
loanAccountNumber = firstOrNull { it.key == LOAN_ACCOUNT_NUMBER }?.value
val amountData = firstOrNull { it.key == NaviPaymentActivity.AMOUNT_DATA }?.value
val currency = firstOrNull { it.key == PartPrePaymentActivity.CURRENCY }?.value
val symbol = firstOrNull { it.key == PartPrePaymentActivity.SYMBOL }?.value
val paymentType: PaymentType =
PaymentType.get(
firstOrNull { it.key == PartPrePaymentActivity.PAYMENT_TYPE }?.value
)
loanType = firstOrNull { it.key == Constants.LOAN_TYPE }?.value
val isPreClosure = false
updatePaymentType(paymentType)
onPaymentClick(
Amount(amountData?.toDoubleOrNull(), currency = currency, symbol = symbol),
isPreClosure,
loanType = loanType
)
}
}
override fun onHiddenChanged(hidden: Boolean) {
super.onHiddenChanged(hidden)
handleHiddenChange(hidden)
}
private fun handleHiddenChange(hidden: Boolean) {
if (hidden.not()) {
if (viewModel.loansTabUiTronData.value is LoanTabVm.LoansTabUiTronScreenState.Success) {
activity?.setStatusBarColorInt(
(viewModel.loansTabUiTronData.value
as LoanTabVm.LoansTabUiTronScreenState.Success)
.data
.screenStructure
?.header
?.widgetData
?.parentComposeView
?.get(0)
?.property
?.backGroundBrushData
?.colorStops
?.get(0)
?.second
.toString()
.parseColorSafe(Constants.WHITE_COLOR)
)
} else {
activity?.setStatusBarColorInt(
ContextCompat.getColor(requireContext(), R.color.white)
)
}
}
}
@Composable
private fun InitTabChangeObserver() {
val selectedTabId by sharedVM.selectedTabId.collectAsStateWithLifecycle()
when (selectedTabId) {
BottomBarTabType.LOAN.name ->
if (isFirstHiddenCallDone) handleHiddenChange(hidden = false)
else isFirstHiddenCallDone = true
else -> handleHiddenChange(hidden = true)
}
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
fun showBottomSheet() {
val showBottomSheet = sharedVM.showBottomSheet.collectAsState().value
val modalBottomSheetState =
rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden)
LaunchedEffect(showBottomSheet) {
when (showBottomSheet) {
true -> {
modalBottomSheetState.show()
activity?.showAsBottomSheet(
content = { content ->
RenderBottomSheetContent(viewModel.screenDefinition, viewModel)
},
modalBottomSheetState = modalBottomSheetState
)
}
else -> {
modalBottomSheetState.hide()
NaviTrackEvent.trackEvent(NaviAnalytics.PL_POST_MANAGE_LOAN_BOTTOMSHEET_DISMISS)
}
}
}
}
@OptIn(ExperimentalMaterialApi::class)
fun Activity.showAsBottomSheet(
content: @Composable (() -> Unit) -> Unit,
modalBottomSheetState: ModalBottomSheetState
) {
val viewGroup: ViewGroup = this.findViewById(android.R.id.content)
addContentToView(viewGroup, content, modalBottomSheetState)
}
@OptIn(ExperimentalMaterialApi::class)
private fun addContentToView(
viewGroup: ViewGroup,
content: @Composable (() -> Unit) -> Unit,
modalBottomSheetState: ModalBottomSheetState
) {
viewGroup.addView(
ComposeView(viewGroup.context).apply {
setContent { BottomSheetWrapper(viewGroup, this, content, modalBottomSheetState) }
}
)
}
@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun BottomSheetWrapper(
parent: ViewGroup,
composeView: ComposeView,
content: @Composable (() -> Unit) -> Unit,
modalBottomSheetState: ModalBottomSheetState
) {
ModalBottomSheetLayout(
sheetShape =
RoundedCornerShape(
topStart = BOTTOM_SHEET_TOP_CORNER_RADIUS,
topEnd = BOTTOM_SHEET_TOP_CORNER_RADIUS
),
sheetState = modalBottomSheetState,
sheetContent = { content {} }
) {}
LaunchedEffect(modalBottomSheetState.isVisible) {
when (modalBottomSheetState.isVisible) {
false -> {
sharedVM.setBottomSheetState(false)
parent.removeView(composeView)
}
else -> {}
}
}
}
@Composable
fun RenderBottomSheetContent(
data: WidgetModelDefinition<UiTronResponse>?,
viewModel: LoanTabVm
) {
renderUitronWidgets(widget = data, viewModel = viewModel)
}
companion object {
const val SEE_MORE_BOTTOMSHEET = "SEE_MORE_BOTTOMSHEET"
const val LOAN_TAB_FRAGMENT = "LoanTabFragment"
fun getInstance(listState: () -> LazyListState? = { null }): LoanTabFragment {
return LoanTabFragment(listState)
}
}
override fun onPrimaryActionClick(ctaData: CtaData) {}
override fun onSecondaryActionClick(ctaData: CtaData) {
handlePaymentActionData(ctaData)
}
}

View File

@@ -11,6 +11,7 @@ import androidx.lifecycle.viewModelScope
import com.navi.common.viewmodel.BaseVM
import com.naviapp.common.model.UiTronActionHandler
import com.naviapp.home.compose.home.navigation.getNavigationItemsList
import com.naviapp.home.compose.model.InitiatePaymentFromComposeData
import com.naviapp.home.model.BottomBarTabType
import com.naviapp.home.model.BottomNavBarItemData
import com.naviapp.home.model.BottomNavBarStateHolder
@@ -20,7 +21,9 @@ import com.naviapp.home.model.HpBottomSheetState
import com.naviapp.home.model.HpBottomSheetStateHolder
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
@@ -40,6 +43,10 @@ class SharedVM @Inject constructor() : BaseVM() {
private val _showBottomSheet = MutableStateFlow(false)
val showBottomSheet = _showBottomSheet.asStateFlow()
private val _initiatePaymentFromLoansTabScreen =
MutableSharedFlow<InitiatePaymentFromComposeData>()
val initiatePaymentFromLoansTabScreen = _initiatePaymentFromLoansTabScreen.asSharedFlow()
private val _bottomNavBarStateHolder =
MutableStateFlow(
BottomNavBarStateHolder(
@@ -100,4 +107,8 @@ class SharedVM @Inject constructor() : BaseVM() {
) {
_bottomSheetStateHolder.update { HpBottomSheetStateHolder(state, config, content) }
}
fun updatePaymentDataFromLoansTabScreen(data: InitiatePaymentFromComposeData) {
viewModelScope.launch { _initiatePaymentFromLoansTabScreen.emit(data) }
}
}

View File

@@ -456,6 +456,7 @@ object Constants {
const val PL_LOAN_DOCUMENT_SCREEN = "PL_LOAN_DOCUMENT_SCREEN"
const val DOCUMENTS = "DOCUMENTS"
const val DOCUMENT_NAME = "documentName"
const val SEE_MORE_BOTTOMSHEET = "SEE_MORE_BOTTOMSHEET"
const val LENDING_PERMISSION_SCREEN = "LENDING_PERMISSION_SCREEN"
const val ON_SYSTEM_BACK_PRESSED = "on_system_back_pressed"
const val LENDING_PERMISSION_PAGE_LANDS = "lending_permission_page_lands"