NTP-55320 || Shrihari | FTUE v0 (#15820)
Co-authored-by: Aman S <aman.s@navi.com>
This commit is contained in:
@@ -13,6 +13,8 @@ import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonElement
|
||||
import com.navi.amc.common.model.GenericComposableWidget
|
||||
import com.navi.amc.common.model.widgets.SpacerWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.FtueWithTrackerWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.RepeatOrderWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.RiskFreeFundWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.SetupMonthlyTargetWidget
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.ActionCardWidget
|
||||
@@ -24,7 +26,6 @@ import com.naviapp.home.dashboard.models.investmentTabWidgetData.HighestReturnFu
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.MonthlyInvestmentGoalWidget
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.OrdersInProgressWidget
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.PortfolioWidget
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.RepeatOrderWidget
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.RewardNudgeWidget
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.SipAutoPayNudgeWidget
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.TopInvestingFundsWidget
|
||||
@@ -76,6 +77,8 @@ class InvestmentTabWidgetJsonDeserializer : JsonDeserializer<GenericComposableWi
|
||||
MonthlyInvestmentGoalWidget::class.java
|
||||
InvestmentTabWidgetType.SETUP_MONTHLY_TARGET_WIDGET.value ->
|
||||
SetupMonthlyTargetWidget::class.java
|
||||
InvestmentTabWidgetType.FTUE_WITH_TRACKER_WIDGET.value ->
|
||||
FtueWithTrackerWidget::class.java
|
||||
else -> null
|
||||
}
|
||||
return if (className != null) {
|
||||
|
||||
@@ -39,6 +39,7 @@ data class ExploreMoreSectionData(
|
||||
|
||||
data class TitleWithIconsCard(
|
||||
@SerializedName("property") val property: InvestmentBaseProperty? = null,
|
||||
@SerializedName("titleProperty") val titleProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("title") val title: TextFieldData? = null,
|
||||
@SerializedName("leftIcon") val leftIcon: ImageFieldData? = null,
|
||||
@SerializedName("rightIcon") val rightIcon: ImageFieldData? = null,
|
||||
|
||||
@@ -27,4 +27,5 @@ enum class InvestmentTabWidgetType(val value: String) {
|
||||
SIP_AUTOPAY_NUDGE_WIDGET("sip_autopay_nudge_widget"),
|
||||
MONTHLY_INVESTMENT_GOAL_WIDGET("monthly_investment_goal_widget"),
|
||||
SETUP_MONTHLY_TARGET_WIDGET("setup_monthly_target_widget"),
|
||||
FTUE_WITH_TRACKER_WIDGET("ftue_with_tracker_widget"),
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ import com.navi.amc.common.composables.widgets.SpacerWidgetComposable
|
||||
import com.navi.amc.common.model.GenericComposableWidget
|
||||
import com.navi.amc.common.model.widgets.SpacerWidget
|
||||
import com.navi.amc.fundbuy.composables.widgets.SetupMonthlyTargetWidgetComposable
|
||||
import com.navi.amc.fundbuy.models.widgets.FtueWithTrackerWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.RepeatOrderWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.RiskFreeFundWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.SetupMonthlyTargetWidget
|
||||
import com.navi.base.model.ActionData
|
||||
@@ -33,7 +35,6 @@ import com.naviapp.home.dashboard.models.investmentTabWidgetData.HighestReturnFu
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.MonthlyInvestmentGoalWidget
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.OrdersInProgressWidget
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.PortfolioWidget
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.RepeatOrderWidget
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.RewardNudgeWidget
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.SipAutoPayNudgeWidget
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.TopInvestingFundsWidget
|
||||
@@ -44,6 +45,7 @@ import com.naviapp.home.dashboard.ui.compose.investmentTab.widgets.BannerWithAct
|
||||
import com.naviapp.home.dashboard.ui.compose.investmentTab.widgets.CutOffSellTimerComposable
|
||||
import com.naviapp.home.dashboard.ui.compose.investmentTab.widgets.CutOffTimerWidgetComposable
|
||||
import com.naviapp.home.dashboard.ui.compose.investmentTab.widgets.ExploreMoreWidgetComposable
|
||||
import com.naviapp.home.dashboard.ui.compose.investmentTab.widgets.FtueWithTrackerWidgetComposable
|
||||
import com.naviapp.home.dashboard.ui.compose.investmentTab.widgets.FundCategoriesWidgetComposable
|
||||
import com.naviapp.home.dashboard.ui.compose.investmentTab.widgets.MonthlyInvestmentGoalWidgetComposable
|
||||
import com.naviapp.home.dashboard.ui.compose.investmentTab.widgets.OrdersInProgressWidgetComposable
|
||||
@@ -219,5 +221,14 @@ fun InvestmentGenericComposableWidgetFactory(
|
||||
)
|
||||
}
|
||||
}
|
||||
InvestmentTabWidgetType.FTUE_WITH_TRACKER_WIDGET.value -> {
|
||||
VisibilityTracker(widgetData = data, onVisible = onVisible) {
|
||||
FtueWithTrackerWidgetComposable(
|
||||
widget = data as FtueWithTrackerWidget,
|
||||
onClick = onClick,
|
||||
onHopperStart = onHopperStart,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,9 +11,11 @@ import BottomSheetData
|
||||
import android.app.Activity
|
||||
import android.os.Bundle
|
||||
import androidx.compose.runtime.MutableState
|
||||
import com.navi.amc.common.activity.CheckerActivity
|
||||
import com.navi.amc.common.model.AdditionalDataAsyncResponse
|
||||
import com.navi.amc.common.model.NextCtaResponse
|
||||
import com.navi.amc.navigator.NaviAmcDeeplinkNavigator
|
||||
import com.navi.amc.navigator.NaviAmcDeeplinkNavigator.FTUE
|
||||
import com.navi.amc.utils.AmcAnalytics
|
||||
import com.navi.amc.utils.Constant
|
||||
import com.navi.amc.utils.Constant.CAPS_DATA
|
||||
@@ -81,6 +83,26 @@ class InvestmentsScreenHelper {
|
||||
|
||||
fun handlePennyDropSuccessCase(ctaData: CtaData) {
|
||||
if (
|
||||
(ctaData.url
|
||||
?.contains(
|
||||
NaviAmcDeeplinkNavigator.AMC.plus("/").plus(NaviAmcDeeplinkNavigator.KYC),
|
||||
true,
|
||||
)
|
||||
.orFalse() ||
|
||||
ctaData.url?.contains(CheckerActivity.HPC_PAN_REDIRECTION_PAGE).orFalse() ||
|
||||
ctaData.url?.contains(CheckerActivity.HPC_NAME_REDIRECTION_PAGE).orFalse()) &&
|
||||
ctaData.parameters
|
||||
?.firstOrNull { it.key == Constant.SOURCE }
|
||||
?.value
|
||||
?.equals(FTUE) == true
|
||||
) {
|
||||
val sourceParam =
|
||||
hashMapOf<String, String>().apply {
|
||||
ctaData.parameters?.forEach { put(it.key.toString(), it.value.orEmpty()) }
|
||||
put(Constant.KYC_SOURCE_SCREEN, FTUE)
|
||||
}
|
||||
TempStorageHelper.kycSourceInfo = sourceParam
|
||||
} else if (
|
||||
ctaData
|
||||
?.url
|
||||
?.contains(
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import com.navi.amc.fundbuy.models.widgets.FundCardData
|
||||
import com.navi.amc.fundbuy.models.widgets.RepeatOrderWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.RiskFreeFundWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.SetupMonthlyTargetWidget
|
||||
import com.navi.base.model.GenericAnalytics
|
||||
@@ -21,7 +22,6 @@ import com.naviapp.home.dashboard.models.investmentTabWidgetData.MonthlyInvestme
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.OrderStatusCardData
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.OrdersInProgressWidget
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.PortfolioWidget
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.RepeatOrderWidget
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.SipAutoPayNudgeWidget
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.TopInvestingFundsWidget
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.WhyInvestWidget
|
||||
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.naviapp.home.dashboard.ui.compose.investmentTab.genericComposables
|
||||
|
||||
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.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.navi.amc.fundbuy.composables.widgets.CustomDashedDivider
|
||||
import com.navi.amc.fundbuy.models.widgets.FtueWithTrackerActionCardData
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.base.model.CtaData
|
||||
import com.navi.base.utils.orElse
|
||||
import com.navi.common.utils.Constants.HOPPER
|
||||
import com.navi.design.utils.clickableWithNoGesture
|
||||
import com.navi.naviwidgets.extensions.NaviImage
|
||||
import com.navi.naviwidgets.extensions.NaviTextWidgetized
|
||||
import com.navi.naviwidgets.extensions.hexToColor
|
||||
import com.navi.naviwidgets.models.FooterButtonState
|
||||
import com.navi.uitron.model.ui.ComposePadding
|
||||
import com.navi.uitron.utils.setBackground
|
||||
import com.navi.uitron.utils.setBorderStroke
|
||||
import com.navi.uitron.utils.setPadding
|
||||
import com.naviapp.home.dashboard.ui.compose.investmentTab.InvestmentsScreenHelper
|
||||
|
||||
@Composable
|
||||
fun FtueTrackerCardComposable(
|
||||
data: FtueWithTrackerActionCardData,
|
||||
onClick: (actionData: ActionData?) -> Unit,
|
||||
onHopperStart: (ctaData: CtaData, buttonState: MutableState<FooterButtonState>) -> Unit =
|
||||
{ _, _ ->
|
||||
},
|
||||
) {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.shadow(
|
||||
elevation = (data.actionCardProperty?.cardProperty?.elevation ?: 16).dp,
|
||||
spotColor = (hexToColor(data.actionCardProperty?.cardProperty?.spotColor)),
|
||||
ambientColor = (hexToColor(data.actionCardProperty?.cardProperty?.ambientColor)),
|
||||
)
|
||||
.setBorderStroke(data.actionCardProperty?.cardProperty?.borderStrokeData)
|
||||
.setBackground(
|
||||
brushData = data.actionCardProperty?.cardProperty?.backGroundBrushData,
|
||||
uiTronShape = data.actionCardProperty?.cardProperty?.shape,
|
||||
backgroundColor = data.actionCardProperty?.cardProperty?.backgroundColor,
|
||||
)
|
||||
.setPadding(data.actionCardProperty?.cardProperty?.padding)
|
||||
.wrapContentHeight()
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
data.trackerItems?.let { items ->
|
||||
Row(modifier = Modifier.fillMaxWidth()) {
|
||||
items.forEachIndexed { index, iconWithTextCard ->
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.fillMaxHeight()
|
||||
.width(
|
||||
(data.actionCardProperty
|
||||
?.trackerItemsProperty
|
||||
?.width
|
||||
?.toInt()
|
||||
?.orElse(65) ?: 65)
|
||||
.dp
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
NaviImage(imageFieldData = iconWithTextCard.icon)
|
||||
Spacer(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.height(
|
||||
(data.actionCardProperty?.trackerItemsProperty?.margin?.top
|
||||
?: 4)
|
||||
.dp
|
||||
)
|
||||
)
|
||||
NaviTextWidgetized(textFieldData = iconWithTextCard.text)
|
||||
}
|
||||
|
||||
if (index != items.size - 1) {
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.fillMaxSize()
|
||||
.weight(1f)
|
||||
.setPadding(
|
||||
data.actionCardProperty?.trackerDividerProperty?.padding
|
||||
?: ComposePadding(
|
||||
start = 8,
|
||||
end = 8,
|
||||
top = 20,
|
||||
bottom = 0,
|
||||
)
|
||||
),
|
||||
contentAlignment = Alignment.CenterStart,
|
||||
) {
|
||||
CustomDashedDivider(
|
||||
modifier = Modifier.padding(top = 0.dp, bottom = 0.dp),
|
||||
color = Color.Black,
|
||||
thickness = 1.dp,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
data.buttonText?.let {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.setPadding(data.actionCardProperty?.buttonProperty?.padding)
|
||||
.setBackground(
|
||||
brushData =
|
||||
data.actionCardProperty?.buttonProperty?.backGroundBrushData,
|
||||
uiTronShape = data.actionCardProperty?.buttonProperty?.shape,
|
||||
backgroundColor =
|
||||
data.actionCardProperty?.buttonProperty?.backgroundColor,
|
||||
)
|
||||
.clickableWithNoGesture {
|
||||
data.actionData?.let { actionData ->
|
||||
if (actionData.url == HOPPER) {
|
||||
InvestmentsScreenHelper()
|
||||
.setActionStatus(
|
||||
actionData = actionData,
|
||||
buttonState =
|
||||
mutableStateOf<FooterButtonState>(
|
||||
FooterButtonState.ENABLED
|
||||
),
|
||||
onClick = onClick,
|
||||
onHopperStart = onHopperStart,
|
||||
)
|
||||
} else {
|
||||
onClick(actionData)
|
||||
}
|
||||
}
|
||||
},
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
NaviTextWidgetized(
|
||||
textFieldData = it,
|
||||
modifier =
|
||||
Modifier.setPadding(data.actionCardProperty?.buttonTextProperty?.padding),
|
||||
)
|
||||
}
|
||||
}
|
||||
data.footerTexts?.let {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.setPadding(data.actionCardProperty?.footerTextsProperty?.padding),
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
) {
|
||||
NaviTextWidgetized(textFieldData = it.leftText)
|
||||
NaviTextWidgetized(textFieldData = it.rightText)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,8 +109,16 @@ fun ExploreMoreCardComposable(
|
||||
)
|
||||
Spacer(
|
||||
modifier =
|
||||
Modifier.weight(
|
||||
cardData.property?.spacingWeight?.start ?: DEFAULT_CARD_WEIGHT
|
||||
Modifier.then(
|
||||
cardData.titleProperty?.padding?.start?.let { startPadding ->
|
||||
Modifier.width(startPadding.dp)
|
||||
}
|
||||
?: run {
|
||||
Modifier.weight(
|
||||
cardData.property?.spacingWeight?.start
|
||||
?: DEFAULT_CARD_WEIGHT
|
||||
)
|
||||
}
|
||||
)
|
||||
)
|
||||
NaviTextWidgetized(textFieldData = cardData.title)
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.naviapp.home.dashboard.ui.compose.investmentTab.widgets
|
||||
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
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.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.navi.amc.fundbuy.models.widgets.FtueWithTrackerWidget
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.base.model.CtaData
|
||||
import com.navi.common.ui.compose.RolodexAnimationComposableV2
|
||||
import com.navi.naviwidgets.extensions.NaviImage
|
||||
import com.navi.naviwidgets.extensions.NaviTextWidgetized
|
||||
import com.navi.naviwidgets.models.FooterButtonState
|
||||
import com.navi.uitron.model.ui.BrushData
|
||||
import com.navi.uitron.model.ui.BrushType
|
||||
import com.navi.uitron.model.ui.ColorStop
|
||||
import com.navi.uitron.model.ui.ComposePadding
|
||||
import com.navi.uitron.utils.setBackground
|
||||
import com.navi.uitron.utils.setPadding
|
||||
import com.naviapp.home.dashboard.ui.compose.investmentTab.genericComposables.FtueTrackerCardComposable
|
||||
|
||||
@Composable
|
||||
fun FtueWithTrackerWidgetComposable(
|
||||
widget: FtueWithTrackerWidget,
|
||||
onClick: (actionData: ActionData?) -> Unit,
|
||||
onHopperStart: (ctaData: CtaData, buttonState: MutableState<FooterButtonState>) -> Unit =
|
||||
{ _, _ ->
|
||||
},
|
||||
) {
|
||||
widget.widgetData?.content?.let { content ->
|
||||
Box {
|
||||
val illustrationHeight =
|
||||
(LocalConfiguration.current.screenWidthDp) *
|
||||
(content.properties?.cardProperty?.heightFactor ?: 0.65f)
|
||||
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.height(illustrationHeight.dp)
|
||||
.width(LocalConfiguration.current.screenWidthDp.dp)
|
||||
.setBackground(
|
||||
brushData =
|
||||
content.properties?.cardProperty?.backGroundBrushData
|
||||
?: BrushData(
|
||||
brushType = BrushType.LINEAR.name,
|
||||
colorStops =
|
||||
listOf(
|
||||
ColorStop(0.0f, "#FFFFFF"),
|
||||
ColorStop(1f, "#FFFBD6"),
|
||||
),
|
||||
),
|
||||
uiTronShape = content.properties?.cardProperty?.shape,
|
||||
backgroundColor = content.properties?.cardProperty?.backgroundColor,
|
||||
)
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
Column(horizontalAlignment = Alignment.Start) {
|
||||
content.topLogo?.let {
|
||||
NaviImage(
|
||||
imageFieldData = it,
|
||||
modifier =
|
||||
Modifier.setPadding(
|
||||
content.properties?.topLogoProperty?.padding
|
||||
?: ComposePadding(0, 0, 14, 0)
|
||||
)
|
||||
.align(Alignment.CenterHorizontally),
|
||||
)
|
||||
}
|
||||
content.topText?.let {
|
||||
NaviTextWidgetized(
|
||||
textFieldData = it,
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
start =
|
||||
(content.properties?.topTextProperty?.padding?.start
|
||||
?: 16)
|
||||
.dp,
|
||||
top =
|
||||
(content.properties?.topTextProperty?.padding?.top
|
||||
?: 30)
|
||||
.dp,
|
||||
),
|
||||
)
|
||||
}
|
||||
content.rolodexItems?.let { items ->
|
||||
if (items.isNotEmpty()) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.setPadding(
|
||||
content.properties?.rolodexItemProperty?.padding
|
||||
?: ComposePadding(0, 0, 6, 0)
|
||||
),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
) {
|
||||
RolodexAnimationComposableV2(
|
||||
composableList =
|
||||
List(items.size ?: 0) {
|
||||
{ NaviTextWidgetized(textFieldData = items[it]) }
|
||||
},
|
||||
delayInMillis =
|
||||
content.properties
|
||||
?.rolodexItemProperty
|
||||
?.animationData
|
||||
?.delayInMillis ?: 2200,
|
||||
enterAnimation = slideInVertically { it },
|
||||
exitAnimation = slideOutVertically { -it },
|
||||
contentAlignment = Alignment.CenterStart,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
content.chipItems?.let { chipItems ->
|
||||
if (chipItems.isNotEmpty()) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.setPadding(
|
||||
content.properties?.chipProperty?.padding
|
||||
?: ComposePadding(16, 0, 12, 0)
|
||||
)
|
||||
) {
|
||||
repeat(chipItems.size) {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.setBackground(
|
||||
backgroundColor =
|
||||
content.properties
|
||||
?.chipItemProperty
|
||||
?.backgroundColor,
|
||||
brushData =
|
||||
content.properties
|
||||
?.chipItemProperty
|
||||
?.backGroundBrushData,
|
||||
uiTronShape =
|
||||
content.properties?.chipItemProperty?.shape,
|
||||
)
|
||||
) {
|
||||
NaviTextWidgetized(
|
||||
modifier =
|
||||
Modifier.setPadding(
|
||||
content.properties
|
||||
?.chipItemProperty
|
||||
?.padding ?: ComposePadding(8, 8, 4, 4)
|
||||
),
|
||||
textFieldData = chipItems[it],
|
||||
)
|
||||
}
|
||||
Spacer(
|
||||
modifier =
|
||||
Modifier.width(
|
||||
(content.properties?.chipProperty?.padding?.end
|
||||
?: 10)
|
||||
.dp
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
content.actionCardData?.let { actionCardData ->
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
top =
|
||||
(illustrationHeight *
|
||||
(actionCardData.actionCardProperty
|
||||
?.cardProperty
|
||||
?.heightFactor ?: 0.65f))
|
||||
.dp,
|
||||
start =
|
||||
(actionCardData.actionCardProperty?.cardProperty?.margin?.start
|
||||
?: 16)
|
||||
.dp,
|
||||
end =
|
||||
(actionCardData.actionCardProperty?.cardProperty?.margin?.end ?: 16)
|
||||
.dp,
|
||||
)
|
||||
) {
|
||||
FtueTrackerCardComposable(
|
||||
data = actionCardData,
|
||||
onClick = onClick,
|
||||
onHopperStart = onHopperStart,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,11 +10,11 @@ package com.naviapp.home.dashboard.ui.compose.investmentTab.widgets
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.navi.amc.fundbuy.models.widgets.RepeatOrderWidget
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.base.model.GenericAnalytics
|
||||
import com.navi.naviwidgets.extensions.NaviTextWidgetized
|
||||
import com.navi.uitron.utils.setPadding
|
||||
import com.naviapp.home.dashboard.models.investmentTabWidgetData.RepeatOrderWidget
|
||||
import com.naviapp.home.dashboard.ui.compose.investmentTab.genericComposables.CardListComposable
|
||||
import com.naviapp.home.dashboard.viewmodels.InvestmentsVm
|
||||
import com.naviapp.utils.Constants.DEFAULT_CARD_WIDTH_FACTOR
|
||||
|
||||
@@ -74,6 +74,7 @@ dependencies {
|
||||
implementation libs.digio.gateway.kyc
|
||||
implementation libs.digitap
|
||||
implementation libs.philjay.mpAndroidChart
|
||||
implementation libs.raamcosta.composeDestinations.animation.core
|
||||
|
||||
androidTestImplementation libs.androidx.test.espresso.core
|
||||
androidTestImplementation libs.androidx.test.junit
|
||||
@@ -82,4 +83,5 @@ dependencies {
|
||||
|
||||
ksp libs.androidx.hilt.compiler
|
||||
ksp libs.dagger.hiltCompiler
|
||||
ksp libs.raamcosta.composeDestinations.ksp
|
||||
}
|
||||
|
||||
@@ -44,6 +44,13 @@
|
||||
android:theme="@style/BaseThemeStyle"
|
||||
android:launchMode="singleTop"
|
||||
android:windowSoftInputMode="adjustNothing" />
|
||||
|
||||
<activity
|
||||
android:name="com.navi.amc.compose.entry.AmcComposeActivity"
|
||||
android:screenOrientation="portrait"
|
||||
android:theme="@style/BaseThemeStyle"
|
||||
android:launchMode="singleTop"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -7,23 +7,28 @@
|
||||
|
||||
package com.navi.amc.common.composables
|
||||
|
||||
import PaymentCard
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.PageSize
|
||||
import androidx.compose.foundation.pager.PagerState
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.navi.amc.fundbuy.composables.cards.MonthlyInvestmentGoalCardComposable
|
||||
import com.navi.amc.fundbuy.composables.cards.PaymentCardComposable
|
||||
import com.navi.amc.fundbuy.models.cards.InvestmentGoalCardData
|
||||
import com.navi.amc.utils.Constant.FREE_SCROLL
|
||||
import com.navi.amc.utils.Constant.FUND_CARD_DEFAULT_COLUMN_WEIGHT
|
||||
import com.navi.amc.utils.Constant.MONTHLY_INVESTMENT_GOAL_CARD
|
||||
import com.navi.amc.utils.Constant.RANK_OF_CARD
|
||||
import com.navi.amc.utils.Constant.REPEAT_ORDER
|
||||
import com.navi.base.model.ActionData
|
||||
|
||||
@Composable
|
||||
@@ -36,6 +41,8 @@ fun AmcCardListComposable(
|
||||
listHorizontalPadding: Dp = 16.dp,
|
||||
spacingBetweenCard: Dp = 16.dp,
|
||||
scrollType: String,
|
||||
listState: LazyListState? = null,
|
||||
pagerState: PagerState? = null,
|
||||
) {
|
||||
val configuration = LocalConfiguration.current
|
||||
val screenWidth = configuration.screenWidthDp.dp
|
||||
@@ -44,6 +51,7 @@ fun AmcCardListComposable(
|
||||
when (scrollType) {
|
||||
FREE_SCROLL -> {
|
||||
LazyRow(
|
||||
state = listState ?: LazyListState(),
|
||||
contentPadding = PaddingValues(horizontal = listHorizontalPadding),
|
||||
horizontalArrangement = Arrangement.spacedBy(spacingBetweenCard),
|
||||
) {
|
||||
@@ -67,9 +75,8 @@ fun AmcCardListComposable(
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
val pagerState = rememberPagerState(pageCount = { cardList.size })
|
||||
HorizontalPager(
|
||||
state = pagerState,
|
||||
state = pagerState ?: rememberPagerState(pageCount = { cardList.size }),
|
||||
pageSize = PageSize.Fixed(cardWidth),
|
||||
contentPadding = PaddingValues(horizontal = listHorizontalPadding),
|
||||
pageSpacing = spacingBetweenCard,
|
||||
@@ -111,5 +118,13 @@ fun RenderCardBasedOnType(
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
REPEAT_ORDER -> {
|
||||
PaymentCardComposable(
|
||||
paymentCard = data as PaymentCard,
|
||||
cardWidth = cardWidth,
|
||||
onClick = onClick,
|
||||
cardType = "NORMAL_CARD",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.navi.amc.common.model.GenericComposableWidget
|
||||
import com.navi.amc.fundbuy.models.AmcHeaderExtraData
|
||||
import com.navi.amc.fundbuy.models.SelectionData
|
||||
import com.navi.amc.utils.Constant.WHITE
|
||||
import com.navi.base.model.ActionData
|
||||
import kotlinx.coroutines.delay
|
||||
@@ -34,6 +35,7 @@ fun AmcCommonWidgetListRenderer(
|
||||
scrollToIndex: Int? = null,
|
||||
extraData: AmcHeaderExtraData? = null,
|
||||
eventHandler: (actionData: ActionData?) -> Unit,
|
||||
selectionExtraData: SelectionData? = null,
|
||||
) {
|
||||
val scrollState = rememberLazyListState()
|
||||
|
||||
@@ -88,6 +90,7 @@ fun AmcCommonWidgetListRenderer(
|
||||
val widget = widgetList?.get(index)
|
||||
AmcComposableWidgetFactory(
|
||||
data = widget,
|
||||
selectionData = selectionExtraData,
|
||||
onClick = { actionData -> actionListener(actionData) },
|
||||
onVisible = { actionData -> eventHandler(actionData) },
|
||||
)
|
||||
|
||||
@@ -13,26 +13,34 @@ import com.navi.amc.common.composables.widgets.SpacerWidgetComposable
|
||||
import com.navi.amc.common.model.AmcWidgetType
|
||||
import com.navi.amc.common.model.GenericComposableWidget
|
||||
import com.navi.amc.common.model.widgets.SpacerWidget
|
||||
import com.navi.amc.fundbuy.composables.BannerWidgetComposable
|
||||
import com.navi.amc.fundbuy.composables.FooterWidgetComposable
|
||||
import com.navi.amc.fundbuy.composables.HelpInfoWidgetComposable
|
||||
import com.navi.amc.fundbuy.composables.OnTimeAssuranceWidgetComposable
|
||||
import com.navi.amc.fundbuy.composables.widgets.BannerWidgetComposable
|
||||
import com.navi.amc.fundbuy.composables.widgets.BannerWithLottieWidgetComposable
|
||||
import com.navi.amc.fundbuy.composables.widgets.FooterWidgetComposable
|
||||
import com.navi.amc.fundbuy.composables.widgets.HelpInfoWidgetComposable
|
||||
import com.navi.amc.fundbuy.composables.widgets.InvestmentDetailsWidgetComposable
|
||||
import com.navi.amc.fundbuy.composables.widgets.InvestmentTrackerWidgetComposable
|
||||
import com.navi.amc.fundbuy.composables.widgets.MonthlyInvestmentGoalWidgetComposable
|
||||
import com.navi.amc.fundbuy.composables.widgets.OnTimeAssuranceWidgetComposable
|
||||
import com.navi.amc.fundbuy.composables.widgets.RepeatOrderWidgetComposable
|
||||
import com.navi.amc.fundbuy.composables.widgets.SetupMonthlyTargetWidgetComposable
|
||||
import com.navi.amc.fundbuy.composables.widgets.TitleContentRadioWidgetComposable
|
||||
import com.navi.amc.fundbuy.models.SelectionData
|
||||
import com.navi.amc.fundbuy.models.widgets.InvestmentDetailsWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.MonthlyInvestmentGoalWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.MonthlyInvestmentTrackerWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.OnTimeAssuranceInfoWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.RepeatOrderWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.RiskFreeFundWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.SetupMonthlyTargetWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.TitleContentRadioWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.TitleSubtitleImageWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.TitleSubtitleLottieWidget
|
||||
import com.navi.base.model.ActionData
|
||||
|
||||
@Composable
|
||||
fun AmcComposableWidgetFactory(
|
||||
data: GenericComposableWidget? = null,
|
||||
selectionData: SelectionData? = null,
|
||||
onClick: (actionData: ActionData?) -> Unit,
|
||||
onVisible: (actionData: ActionData?) -> Unit,
|
||||
) {
|
||||
@@ -74,6 +82,9 @@ fun AmcComposableWidgetFactory(
|
||||
onVisible = onVisible,
|
||||
)
|
||||
}
|
||||
AmcWidgetType.REPEAT_ORDER_WIDGET.value -> {
|
||||
RepeatOrderWidgetComposable(widget = data as RepeatOrderWidget, onClick = onClick)
|
||||
}
|
||||
AmcWidgetType.FOOTER_WIDGET.value -> {
|
||||
FooterWidgetComposable(widget = data as TitleSubtitleImageWidget)
|
||||
}
|
||||
@@ -83,5 +94,15 @@ fun AmcComposableWidgetFactory(
|
||||
AmcWidgetType.BANNER_WIDGET.value -> {
|
||||
BannerWidgetComposable(widget = data as TitleSubtitleImageWidget)
|
||||
}
|
||||
AmcWidgetType.BANNER_WITH_LOTTIE_WIDGET.value -> {
|
||||
BannerWithLottieWidgetComposable(widget = data as TitleSubtitleLottieWidget)
|
||||
}
|
||||
AmcWidgetType.TITLE_CONTENT_RADIO_WIDGET.value -> {
|
||||
TitleContentRadioWidgetComposable(
|
||||
widget = data as TitleContentRadioWidget,
|
||||
onClick = onClick,
|
||||
selectionData = selectionData,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,17 +7,25 @@
|
||||
|
||||
package com.navi.amc.common.composables.widgets
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.navi.amc.common.model.widgets.SpacerWidget
|
||||
import com.navi.amc.utils.Constant.SPACER_DEFAULT
|
||||
import com.navi.naviwidgets.extensions.hexToColor
|
||||
|
||||
@Composable
|
||||
fun SpacerWidgetComposable(spacerData: SpacerWidget) {
|
||||
spacerData.widgetData?.let { data ->
|
||||
Spacer(modifier = Modifier.height((data.height ?: SPACER_DEFAULT).dp))
|
||||
Spacer(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.height((data.height ?: SPACER_DEFAULT).dp)
|
||||
.background(color = hexToColor(data.backgroundColor))
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,9 +15,12 @@ import com.navi.amc.fundbuy.models.widgets.InvestmentDetailsWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.MonthlyInvestmentGoalWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.MonthlyInvestmentTrackerWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.OnTimeAssuranceInfoWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.RepeatOrderWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.RiskFreeFundWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.SetupMonthlyTargetWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.TitleContentRadioWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.TitleSubtitleImageWidget
|
||||
import com.navi.amc.fundbuy.models.widgets.TitleSubtitleLottieWidget
|
||||
import java.lang.reflect.Type
|
||||
|
||||
class AmcWidgetJsonDeserializer : JsonDeserializer<GenericComposableWidget> {
|
||||
@@ -45,6 +48,11 @@ class AmcWidgetJsonDeserializer : JsonDeserializer<GenericComposableWidget> {
|
||||
AmcWidgetType.FOOTER_WIDGET.value,
|
||||
AmcWidgetType.BANNER_WIDGET.value,
|
||||
AmcWidgetType.HELP_INFO_WIDGET.value -> TitleSubtitleImageWidget::class.java
|
||||
AmcWidgetType.REPEAT_ORDER_WIDGET.value -> RepeatOrderWidget::class.java
|
||||
AmcWidgetType.BANNER_WITH_LOTTIE_WIDGET.value ->
|
||||
TitleSubtitleLottieWidget::class.java
|
||||
AmcWidgetType.TITLE_CONTENT_RADIO_WIDGET.value ->
|
||||
TitleContentRadioWidget::class.java
|
||||
else -> null
|
||||
}
|
||||
return if (className != null) {
|
||||
|
||||
@@ -18,4 +18,7 @@ enum class AmcWidgetType(val value: String) {
|
||||
HELP_INFO_WIDGET("help_info_widget"),
|
||||
BANNER_WIDGET("banner_widget"),
|
||||
FOOTER_WIDGET("footer_widget"),
|
||||
REPEAT_ORDER_WIDGET("repeat_order_widget"),
|
||||
BANNER_WITH_LOTTIE_WIDGET("banner_with_lottie_widget"),
|
||||
TITLE_CONTENT_RADIO_WIDGET("fund_investing_type"),
|
||||
}
|
||||
|
||||
@@ -10,6 +10,7 @@ package com.navi.amc.common.model
|
||||
import android.os.Parcelable
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.common.model.InvestmentBaseProperty
|
||||
import com.navi.design.textview.model.TextWithStyle
|
||||
import com.navi.naviwidgets.models.response.DataSafeWidget
|
||||
import kotlinx.parcelize.Parcelize
|
||||
@@ -24,6 +25,8 @@ data class Footer(
|
||||
@SerializedName("note") var note: DataSafeWidget? = null,
|
||||
@SerializedName("footerCallout") var footerCallout: FooterCallout? = null,
|
||||
@SerializedName("footerCalloutList") var footerCalloutList: FooterCalloutList? = null,
|
||||
@SerializedName("footerProperties") var footerProperties: FooterProperties? = null,
|
||||
@SerializedName("showShadow") var showShadow: Boolean? = null,
|
||||
) : Parcelable
|
||||
|
||||
@Parcelize
|
||||
@@ -47,3 +50,9 @@ class TimerTextList(
|
||||
@SerializedName("atTimerInfo") var atTimerInfo: TextWithStyle? = null,
|
||||
@SerializedName("afterTimerInfo") var afterTimerInfo: TextWithStyle? = null,
|
||||
) : Parcelable
|
||||
|
||||
@Parcelize
|
||||
data class FooterProperties(
|
||||
@SerializedName("nextCtaProperty") val nextCtaProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("backCtaProperty") val backCtaProperty: InvestmentBaseProperty? = null,
|
||||
) : Parcelable
|
||||
|
||||
@@ -16,4 +16,7 @@ data class SpacerWidget(
|
||||
@SerializedName("widgetData") val widgetData: SpacerWidgetData? = null,
|
||||
) : GenericComposableWidget
|
||||
|
||||
data class SpacerWidgetData(@SerializedName("height") val height: Int? = null)
|
||||
data class SpacerWidgetData(
|
||||
@SerializedName("height") val height: Int? = null,
|
||||
@SerializedName("backgroundColor", alternate = ["bgColor"]) val backgroundColor: String? = null,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.compose.common.model
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.navi.amc.common.model.Footer
|
||||
import com.navi.amc.common.model.GenericComposableWidget
|
||||
import com.navi.common.model.Header
|
||||
|
||||
data class AmcGenericScreenResponse(
|
||||
@SerializedName("header") val header: Header? = null,
|
||||
@SerializedName("content") val content: List<GenericComposableWidget>? = null,
|
||||
@SerializedName("footer") val footer: Footer? = null,
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.compose.common.model
|
||||
|
||||
import com.navi.common.network.models.GenericErrorResponse
|
||||
|
||||
sealed class ScreenState {
|
||||
data object Loading : ScreenState()
|
||||
|
||||
data class Success(val amcGenericScreenResponse: AmcGenericScreenResponse) : ScreenState()
|
||||
|
||||
data class Error(val error: GenericErrorResponse? = null) : ScreenState()
|
||||
|
||||
data object Empty : ScreenState()
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.compose.common.ui
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.navi.amc.compose.common.utils.NaviAmcScreen
|
||||
import com.navi.amc.compose.common.utils.clearBackStackUpToAndNavigate
|
||||
import com.navi.amc.compose.destinations.FtueEducateScreenDestination
|
||||
import com.navi.amc.compose.destinations.FtueFundSelectScreenDestination
|
||||
import com.navi.amc.compose.entry.AmcComposeActivity
|
||||
import com.navi.amc.navigator.NaviAmcDeeplinkNavigator.FTUE
|
||||
import com.navi.amc.utils.Constant.SUB_REDIRECT
|
||||
import com.navi.common.utils.Constants.SECOND_IDENTIFIER
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Destination
|
||||
@Composable
|
||||
fun AmcRouterScreen(amcComposeActivity: AmcComposeActivity, navigator: DestinationsNavigator) {
|
||||
val scope = rememberCoroutineScope()
|
||||
LaunchedEffect(Unit) {
|
||||
val moduleName = amcComposeActivity.intent.getStringExtra(SECOND_IDENTIFIER).orEmpty()
|
||||
val screenName = amcComposeActivity.intent.getStringExtra(SUB_REDIRECT).orEmpty()
|
||||
|
||||
val destination =
|
||||
when (moduleName) {
|
||||
FTUE -> {
|
||||
when (screenName) {
|
||||
NaviAmcScreen.AMC_FTUE_EDUCATE_SCREEN.screenName ->
|
||||
FtueEducateScreenDestination
|
||||
NaviAmcScreen.AMC_FTUE_FUND_SELECT_SCREEN.screenName ->
|
||||
FtueFundSelectScreenDestination
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
scope.launch {
|
||||
destination?.let {
|
||||
navigator.clearBackStackUpToAndNavigate(
|
||||
destination = destination,
|
||||
inclusive = true,
|
||||
popUpTo = FtueEducateScreenDestination,
|
||||
)
|
||||
} ?: run { amcComposeActivity.finish() }
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize().imePadding(),
|
||||
topBar = {},
|
||||
content = { innerPadding ->
|
||||
Column(modifier = Modifier.fillMaxSize().padding(innerPadding)) { AmcShimmer() }
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.compose.common.ui
|
||||
|
||||
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.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun AmcShimmer() {
|
||||
Column(modifier = Modifier.fillMaxWidth().padding(start = 16.dp, end = 16.dp)) {
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
|
||||
Box(modifier = Modifier.height(34.dp).width(94.dp).shimmerEffect())
|
||||
Box(modifier = Modifier.height(34.dp).width(148.dp).shimmerEffect())
|
||||
}
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Box(modifier = Modifier.height(108.dp).fillMaxWidth().shimmerEffect())
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Box(modifier = Modifier.height(24.dp).width(176.dp).shimmerEffect())
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Box(modifier = Modifier.height(132.dp).fillMaxWidth().shimmerEffect())
|
||||
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Box(modifier = Modifier.height(24.dp).width(120.dp).shimmerEffect())
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Box(modifier = Modifier.height(104.dp).fillMaxWidth().shimmerEffect())
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Box(modifier = Modifier.height(78.dp).fillMaxWidth().shimmerEffect())
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Box(modifier = Modifier.height(24.dp).width(176.dp).shimmerEffect())
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
Box(modifier = Modifier.height(132.dp).fillMaxWidth().shimmerEffect())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.compose.common.ui
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.infiniteRepeatable
|
||||
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||
import androidx.compose.animation.core.tween
|
||||
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.ColumnScope
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ModalBottomSheetDefaults
|
||||
import androidx.compose.material.ModalBottomSheetLayout
|
||||
import androidx.compose.material.ModalBottomSheetState
|
||||
import androidx.compose.material.ripple
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableFloatStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Shape
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleEventObserver
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import com.navi.amc.utils.AmcColor
|
||||
import com.navi.common.utils.ClickDebounce
|
||||
import com.navi.common.utils.get
|
||||
import com.navi.naviwidgets.extensions.hexToInt
|
||||
|
||||
@Composable
|
||||
fun AmcModalBottomSheetLayout(
|
||||
sheetContent: @Composable ColumnScope.() -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
sheetState: ModalBottomSheetState,
|
||||
sheetShape: Shape = RoundedCornerShape(topStart = 8.dp, topEnd = 8.dp),
|
||||
sheetElevation: Dp = ModalBottomSheetDefaults.Elevation,
|
||||
scrimColor: Color = ModalBottomSheetDefaults.scrimColor,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Column(modifier = Modifier.navigationBarsPadding()) {
|
||||
ModalBottomSheetLayout(
|
||||
sheetContent = sheetContent,
|
||||
modifier = modifier,
|
||||
sheetState = sheetState,
|
||||
sheetShape = sheetShape,
|
||||
sheetElevation = sheetElevation,
|
||||
scrimColor = scrimColor,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun Modifier.clickableDebounce(
|
||||
debounceTime: Long = 300L,
|
||||
showRipple: Boolean = true,
|
||||
rippleColor: Color = Color.Black.copy(alpha = 0.3f),
|
||||
onClick: () -> Unit,
|
||||
) = composed {
|
||||
val clickDebounce = remember { ClickDebounce.get() }
|
||||
|
||||
Modifier.clickable(
|
||||
interactionSource = remember { MutableInteractionSource() },
|
||||
indication = if (showRipple) ripple(color = rippleColor) else null,
|
||||
onClick = { clickDebounce.processClick(onClick = onClick, debounceTime = debounceTime) },
|
||||
)
|
||||
}
|
||||
|
||||
fun Modifier.shimmerEffect(shimmerColor: List<Color>? = null): Modifier = composed {
|
||||
var size by remember { mutableFloatStateOf(0f) }
|
||||
val transition = rememberInfiniteTransition(label = "")
|
||||
val startOffsetX by
|
||||
transition.animateFloat(
|
||||
initialValue = -2 * size,
|
||||
targetValue = 2 * size,
|
||||
animationSpec =
|
||||
infiniteRepeatable(animation = tween(durationMillis = 1000, easing = LinearEasing)),
|
||||
label = "",
|
||||
)
|
||||
|
||||
drawBehind {
|
||||
size = this.size.width
|
||||
drawRect(
|
||||
size = this.size,
|
||||
brush =
|
||||
Brush.linearGradient(
|
||||
colors =
|
||||
shimmerColor
|
||||
?: listOf(Color(0xFFF9F9FB), Color(0xFFE9E7F0), Color(0xFFF9F9FB)),
|
||||
start = Offset(startOffsetX, 0f),
|
||||
end = Offset(startOffsetX + size, this.size.height),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ShadowStrip(
|
||||
modifier: Modifier = Modifier,
|
||||
height: Dp = 16.dp,
|
||||
brush: Brush =
|
||||
Brush.verticalGradient(
|
||||
colors = listOf(AmcColor.bgDefaultWhite, Color(0xFFCCCCCC).copy(alpha = 0.3f)),
|
||||
startY = 0f,
|
||||
endY = 100f,
|
||||
),
|
||||
) {
|
||||
Box(modifier = modifier.height(height = height).background(brush = brush))
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SetStatusBarColor(activity: Activity, colorResId: Int = hexToInt("#FFFFFF")) {
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
|
||||
DisposableEffect(key1 = lifecycleOwner) {
|
||||
val observer = LifecycleEventObserver { _, event ->
|
||||
when (event) {
|
||||
Lifecycle.Event.ON_RESUME,
|
||||
Lifecycle.Event.ON_CREATE -> {
|
||||
activity.window.statusBarColor = colorResId
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
lifecycleOwner.lifecycle.addObserver(observer)
|
||||
|
||||
onDispose { lifecycleOwner.lifecycle.removeObserver(observer) }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.compose.common.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.navi.amc.common.model.Footer
|
||||
import com.navi.amc.utils.AmcColor
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.naviwidgets.extensions.NaviTextWidgetized
|
||||
import com.navi.naviwidgets.extensions.setPadding
|
||||
import com.navi.naviwidgets.models.response.TextFieldData
|
||||
import com.navi.uitron.model.ui.ComposePadding
|
||||
import com.navi.uitron.utils.setBackground
|
||||
import com.navi.uitron.utils.setPadding
|
||||
|
||||
@Composable
|
||||
fun NaviAmcFooter(
|
||||
footer: Footer? = null,
|
||||
onNextCtaClicked: ((actionData: ActionData?) -> Unit)? = null,
|
||||
onBackCtaClicked: ((actionData: ActionData?) -> Unit)? = null,
|
||||
) {
|
||||
Column(modifier = Modifier.background(color = AmcColor.bgDefaultWhite)) {
|
||||
if (footer?.showShadow == true) {
|
||||
ShadowStrip(modifier = Modifier.fillMaxWidth())
|
||||
}
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 16.dp),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
footer?.backCta?.let {
|
||||
val backCtaProperty = footer.footerProperties?.backCtaProperty
|
||||
NaviTextWidgetized(
|
||||
modifier =
|
||||
Modifier.clickable { onBackCtaClicked?.invoke(footer.backCta) }
|
||||
.setBackground(
|
||||
backgroundColor = backCtaProperty?.backgroundColor,
|
||||
uiTronShape = backCtaProperty?.shape,
|
||||
brushData = backCtaProperty?.backGroundBrushData,
|
||||
)
|
||||
.setPadding(backCtaProperty?.padding ?: ComposePadding(16, 16, 12, 12))
|
||||
.weight(backCtaProperty?.columnWeight ?: 1f),
|
||||
textFieldData =
|
||||
buildFooterTextFieldData(
|
||||
text = footer.backCta?.title,
|
||||
textColor = "#1F002A",
|
||||
),
|
||||
)
|
||||
Spacer(modifier = Modifier.width(16.dp))
|
||||
}
|
||||
footer?.nextCta?.let {
|
||||
val nextCtaProperty = footer.footerProperties?.nextCtaProperty
|
||||
NaviTextWidgetized(
|
||||
modifier =
|
||||
Modifier.clickable { onNextCtaClicked?.invoke(footer.nextCta) }
|
||||
.setBackground(
|
||||
backgroundColor = nextCtaProperty?.backgroundColor,
|
||||
uiTronShape = nextCtaProperty?.shape,
|
||||
brushData = nextCtaProperty?.backGroundBrushData,
|
||||
)
|
||||
.setPadding(nextCtaProperty?.padding ?: ComposePadding(16, 16, 12, 12))
|
||||
.weight(nextCtaProperty?.columnWeight ?: 1f),
|
||||
textFieldData =
|
||||
buildFooterTextFieldData(
|
||||
text = footer.nextCta?.title,
|
||||
textColor = footer.nextCta?.titleColor,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
}
|
||||
}
|
||||
|
||||
fun buildFooterTextFieldData(text: String? = null, textColor: String? = null): TextFieldData? {
|
||||
text?.let {
|
||||
return TextFieldData(
|
||||
text = text,
|
||||
size = 14,
|
||||
font = "NAVI_BODY_DEMI_BOLD",
|
||||
alignment = "CENTER",
|
||||
textColor = textColor,
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.compose.common.ui
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material3.CenterAlignedTopAppBar
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.TopAppBarDefaults
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
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.sp
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.common.R
|
||||
import com.navi.design.font.FontWeightEnum
|
||||
import com.navi.design.font.getFontWeight
|
||||
import com.navi.design.font.naviFontFamily
|
||||
import com.navi.guarddog.utils.clickableDebounce
|
||||
import com.navi.naviwidgets.extensions.NaviText
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun NaviAmcHeader(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
navigationIcon: Int = R.drawable.ic_arrow_left_black_v2,
|
||||
onNavigationIconClick: () -> Unit,
|
||||
actionIconId: Int = -1,
|
||||
actionIconText: String? = null,
|
||||
showRippleOnActionIcon: Boolean = true,
|
||||
onActionClick: ((actionData: ActionData?) -> Unit)? = null,
|
||||
backgroundColor: Color = Color.White,
|
||||
maxLines: Int = 1,
|
||||
) {
|
||||
CenterAlignedTopAppBar(
|
||||
title = {
|
||||
Box(Modifier.fillMaxHeight(), contentAlignment = Alignment.Center) {
|
||||
NaviText(
|
||||
text = title,
|
||||
fontSize = 14.sp,
|
||||
fontFamily = naviFontFamily,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_REGULAR),
|
||||
color = Color.Black,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 40.dp),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
},
|
||||
navigationIcon = {
|
||||
Box(Modifier.fillMaxHeight(), contentAlignment = Alignment.Center) {
|
||||
IconButton(onClick = onNavigationIconClick) {
|
||||
Image(painter = painterResource(navigationIcon), contentDescription = null)
|
||||
}
|
||||
}
|
||||
},
|
||||
actions = {
|
||||
Box(Modifier.fillMaxHeight(), contentAlignment = Alignment.Center) {
|
||||
if (actionIconId > 0) {
|
||||
Image(
|
||||
painter = painterResource(id = actionIconId),
|
||||
contentDescription = "",
|
||||
modifier =
|
||||
Modifier.padding(end = 16.dp).clickableDebounce(
|
||||
showRipple = showRippleOnActionIcon
|
||||
) {
|
||||
onActionClick?.invoke(ActionData())
|
||||
},
|
||||
)
|
||||
} else if (!actionIconText.isNullOrEmpty()) {
|
||||
NaviText(
|
||||
text = actionIconText,
|
||||
fontSize = 14.sp,
|
||||
fontFamily = naviFontFamily,
|
||||
fontWeight = getFontWeight(FontWeightEnum.NAVI_BODY_DEMI_BOLD),
|
||||
color = Color.Black,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier =
|
||||
Modifier.padding(end = 16.dp).clickableDebounce {
|
||||
onActionClick?.invoke(ActionData())
|
||||
},
|
||||
)
|
||||
} else {
|
||||
Image(
|
||||
painter = painterResource(id = navigationIcon),
|
||||
contentDescription = "",
|
||||
modifier = Modifier.padding(end = 16.dp).alpha(0f),
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
modifier = modifier,
|
||||
colors = TopAppBarDefaults.mediumTopAppBarColors(containerColor = backgroundColor),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.compose.common.utils
|
||||
|
||||
import com.navi.amc.fundbuy.models.Temperature
|
||||
|
||||
enum class NaviAmcScreen(val screenName: String, val screenTemperature: Temperature) {
|
||||
AMC_FTUE_EDUCATE_SCREEN("educate_screen", Temperature.ORANGE),
|
||||
AMC_FTUE_FUND_SELECT_SCREEN("fund_select_screen", Temperature.ORANGE),
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.compose.common.utils
|
||||
|
||||
import com.navi.amc.compose.entry.AmcComposeActivity
|
||||
import com.navi.amc.compose.entry.NaviAmcRouter
|
||||
import com.navi.amc.fundbuy.models.Temperature
|
||||
import com.navi.base.model.ActionData
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
import com.ramcosta.composedestinations.spec.Direction
|
||||
import com.ramcosta.composedestinations.spec.Route
|
||||
|
||||
fun DestinationsNavigator.clearBackStackUpToAndNavigate(
|
||||
destination: Direction,
|
||||
popUpTo: Route,
|
||||
inclusive: Boolean = true,
|
||||
) {
|
||||
navigate(destination) { popUpTo(popUpTo) { this.inclusive = inclusive } }
|
||||
}
|
||||
|
||||
/**
|
||||
* Navigates to next screen on the basis of the current screen temperature
|
||||
*
|
||||
* GREEN SCREEN -> this denotes a source screen, should be added to stack ORANGE SCREEN ->
|
||||
* intermediate screen, should be removed from fragment stack RED SCREEN -> destination screen, on
|
||||
* back press from this screen go back to last green screen find the last green screen and pop till
|
||||
* then
|
||||
*/
|
||||
fun navigateToNextScreen(
|
||||
actionData: ActionData?,
|
||||
navigator: DestinationsNavigator,
|
||||
amcComposeActivity: AmcComposeActivity,
|
||||
screen: NaviAmcScreen,
|
||||
) {
|
||||
val direction = NaviAmcRouter.getDirectionFromCta(actionData, amcComposeActivity)
|
||||
val screenTemperature = screen.screenTemperature
|
||||
direction?.let {
|
||||
when (screenTemperature) {
|
||||
Temperature.GREEN -> {}
|
||||
Temperature.ORANGE -> {
|
||||
navigator.popBackStack()
|
||||
}
|
||||
Temperature.RED -> {}
|
||||
}
|
||||
navigator.navigate(direction)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.compose.entry
|
||||
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import androidx.activity.OnBackPressedCallback
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.LocalOverscrollConfiguration
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.navigation.NavHostController
|
||||
import com.navi.amc.common.activity.AmcBaseActivity
|
||||
import com.navi.base.deeplink.DeepLinkManager
|
||||
import com.navi.base.deeplink.util.DeeplinkConstants
|
||||
import com.navi.base.model.CtaData
|
||||
import com.navi.common.model.HelpBottomSheetData
|
||||
import com.navi.common.model.ModuleNameV2
|
||||
import com.navi.common.utils.screenEnterTransition
|
||||
import com.navi.common.utils.screenExitTransition
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AmcComposeActivity : AmcBaseActivity(), BackButtonHandler {
|
||||
|
||||
override val screenName: String
|
||||
get() = "AmcComposeActivity"
|
||||
|
||||
override val moduleName: ModuleNameV2
|
||||
get() = ModuleNameV2.AMC
|
||||
|
||||
lateinit var navController: NavHostController
|
||||
|
||||
override val isNavControllerInitialized: Boolean
|
||||
get() = ::navController.isInitialized
|
||||
|
||||
private val onBackPressedCallback =
|
||||
object : OnBackPressedCallback(true) {
|
||||
// review this
|
||||
override fun handleOnBackPressed() {
|
||||
handleBackPress()
|
||||
}
|
||||
}
|
||||
|
||||
private val amcComposeViewModel by viewModels<AmcComposeViewModel>()
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
screenEnterTransition()
|
||||
super.onCreate(savedInstanceState)
|
||||
WindowCompat.setDecorFitsSystemWindows(window, true)
|
||||
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
|
||||
setContent {
|
||||
CompositionLocalProvider(LocalOverscrollConfiguration provides null) {
|
||||
AmcMainScreen(amcComposeActivity = this)
|
||||
}
|
||||
}
|
||||
window.decorView.setBackgroundColor(Color.WHITE)
|
||||
}
|
||||
|
||||
override fun finish() {
|
||||
if (isTaskRoot) {
|
||||
DeepLinkManager.getDeepLinkListener()
|
||||
?.navigateTo(
|
||||
activity = this,
|
||||
ctaData = CtaData(url = DeeplinkConstants.INVESTMENT),
|
||||
finish = false,
|
||||
)
|
||||
}
|
||||
super.finish()
|
||||
screenExitTransition()
|
||||
}
|
||||
|
||||
fun onHelpClick(helpBottomSheetData: HelpBottomSheetData?, bundle: Bundle?) {
|
||||
openHelpInfo(helpBottomSheetData, bundle)
|
||||
}
|
||||
|
||||
override fun initialiseNavController(navHostController: NavHostController) {
|
||||
this.navController = navHostController
|
||||
onNavControllerSet(navHostController)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.compose.entry
|
||||
|
||||
import com.navi.amc.common.viewmodel.BaseAmcVM
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class AmcComposeViewModel @Inject constructor() : BaseAmcVM() {
|
||||
|
||||
fun getDefaultScreenName() = ""
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.compose.entry
|
||||
|
||||
import androidx.compose.animation.AnimatedContentTransitionScope
|
||||
import androidx.compose.animation.ExperimentalAnimationApi
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.material.ModalBottomSheetValue
|
||||
import androidx.compose.material.navigation.ModalBottomSheetLayout
|
||||
import androidx.compose.material.navigation.rememberBottomSheetNavigator
|
||||
import androidx.compose.material.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.plusAssign
|
||||
import com.navi.amc.compose.NavGraphs
|
||||
import com.navi.amc.compose.common.ui.AmcModalBottomSheetLayout
|
||||
import com.navi.amc.compose.common.utils.NaviAmcScreen
|
||||
import com.navi.amc.utils.Constant.START_SCREEN_NAME
|
||||
import com.navi.amc.utils.Constant.TRANSITION_DURATION_IN_MILLIS
|
||||
import com.ramcosta.composedestinations.DestinationsNavHost
|
||||
import com.ramcosta.composedestinations.animations.defaults.RootNavGraphDefaultAnimations
|
||||
import com.ramcosta.composedestinations.animations.rememberAnimatedNavHostEngine
|
||||
import com.ramcosta.composedestinations.navigation.dependency
|
||||
import com.ramcosta.composedestinations.spec.NavHostEngine
|
||||
|
||||
@Composable
|
||||
fun AmcMainScreen(amcComposeActivity: AmcComposeActivity) {
|
||||
val bottomSheetNavigator = rememberBottomSheetNavigator()
|
||||
val navController =
|
||||
rememberNavController().apply { this.navigatorProvider += bottomSheetNavigator }
|
||||
|
||||
ModalBottomSheetLayout(
|
||||
bottomSheetNavigator = bottomSheetNavigator,
|
||||
content = {
|
||||
AmcModalBottomSheetLayout(
|
||||
sheetState =
|
||||
rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden),
|
||||
sheetContent = {},
|
||||
) {
|
||||
amcComposeActivity.initialiseNavController(navController)
|
||||
DestinationsNavHost(
|
||||
startRoute =
|
||||
NaviAmcRouter.getStartRoute(
|
||||
amcComposeActivity.intent.getStringExtra(START_SCREEN_NAME)
|
||||
?: NaviAmcScreen.AMC_FTUE_EDUCATE_SCREEN.screenName
|
||||
),
|
||||
navGraph = NavGraphs.root,
|
||||
engine = naviHostEngine(),
|
||||
navController = amcComposeActivity.navController,
|
||||
dependenciesContainerBuilder = { dependency(amcComposeActivity) },
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
// generic error bottomsheet
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalAnimationApi::class)
|
||||
@Composable
|
||||
private fun naviHostEngine(): NavHostEngine {
|
||||
return rememberAnimatedNavHostEngine(
|
||||
rootDefaultAnimations =
|
||||
RootNavGraphDefaultAnimations(
|
||||
enterTransition = {
|
||||
slideIntoContainer(
|
||||
towards = AnimatedContentTransitionScope.SlideDirection.Left,
|
||||
animationSpec = tween(TRANSITION_DURATION_IN_MILLIS),
|
||||
)
|
||||
},
|
||||
exitTransition = {
|
||||
slideOutOfContainer(
|
||||
towards = AnimatedContentTransitionScope.SlideDirection.Left,
|
||||
animationSpec = tween(TRANSITION_DURATION_IN_MILLIS),
|
||||
)
|
||||
},
|
||||
popEnterTransition = {
|
||||
slideIntoContainer(
|
||||
towards = AnimatedContentTransitionScope.SlideDirection.Right,
|
||||
animationSpec = tween(TRANSITION_DURATION_IN_MILLIS),
|
||||
)
|
||||
},
|
||||
popExitTransition = {
|
||||
slideOutOfContainer(
|
||||
towards = AnimatedContentTransitionScope.SlideDirection.Companion.Right,
|
||||
animationSpec = tween(TRANSITION_DURATION_IN_MILLIS),
|
||||
)
|
||||
},
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.compose.entry
|
||||
|
||||
import com.navi.base.deeplink.DeepLinkManager
|
||||
import com.navi.base.deeplink.util.DeeplinkConstants
|
||||
import com.navi.base.model.CtaData
|
||||
|
||||
internal interface BackButtonHandler {
|
||||
val isNavControllerInitialized: Boolean
|
||||
|
||||
fun AmcComposeActivity.handleBackPress() {
|
||||
when {
|
||||
isBackPressedOnAmcRootScreen() -> {
|
||||
if (isTaskRoot) {
|
||||
goToInvestmentTab()
|
||||
} else {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun AmcComposeActivity.isBackPressedOnAmcRootScreen(): Boolean {
|
||||
return isNavControllerInitialized
|
||||
}
|
||||
|
||||
private fun AmcComposeActivity.goToInvestmentTab() {
|
||||
DeepLinkManager.getDeepLinkListener()
|
||||
?.navigateTo(
|
||||
activity = this,
|
||||
ctaData = CtaData(url = DeeplinkConstants.INVESTMENT),
|
||||
finish = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.compose.entry
|
||||
|
||||
import com.navi.amc.compose.common.utils.NaviAmcScreen
|
||||
import com.navi.amc.compose.destinations.AmcRouterScreenDestination
|
||||
import com.navi.amc.compose.destinations.DirectionDestination
|
||||
import com.navi.amc.compose.destinations.FtueEducateScreenDestination
|
||||
import com.navi.amc.compose.destinations.FtueFundSelectScreenDestination
|
||||
import com.navi.amc.navigator.NaviAmcDeeplinkNavigator.FTUE
|
||||
import com.navi.amc.utils.toNavigateAmcModule
|
||||
import com.navi.base.model.ActionData
|
||||
import com.ramcosta.composedestinations.spec.Route
|
||||
|
||||
object NaviAmcRouter {
|
||||
|
||||
fun getStartRoute(startScreenName: String): Route {
|
||||
return when (startScreenName) {
|
||||
NaviAmcScreen.AMC_FTUE_EDUCATE_SCREEN.screenName -> FtueEducateScreenDestination
|
||||
NaviAmcScreen.AMC_FTUE_FUND_SELECT_SCREEN.screenName -> FtueFundSelectScreenDestination
|
||||
else -> AmcRouterScreenDestination
|
||||
}
|
||||
}
|
||||
|
||||
fun onCtaClick(amcComposeActivity: AmcComposeActivity, actionData: ActionData?) {
|
||||
actionData?.toNavigateAmcModule(amcComposeActivity)
|
||||
}
|
||||
|
||||
fun getDirectionFromCta(
|
||||
actionData: ActionData?,
|
||||
amcComposeActivity: AmcComposeActivity,
|
||||
): DirectionDestination? {
|
||||
val deepLink = actionData?.url
|
||||
val splitDeepLink = deepLink?.split("/")
|
||||
val firstIdentifier = splitDeepLink?.getOrNull(1)
|
||||
val secondIdentifier = splitDeepLink?.getOrNull(2)
|
||||
return when (firstIdentifier) {
|
||||
FTUE -> {
|
||||
when (secondIdentifier) {
|
||||
NaviAmcScreen.AMC_FTUE_EDUCATE_SCREEN.screenName -> FtueEducateScreenDestination
|
||||
NaviAmcScreen.AMC_FTUE_FUND_SELECT_SCREEN.screenName ->
|
||||
FtueFundSelectScreenDestination
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
actionData?.toNavigateAmcModule(activity = amcComposeActivity, finish = true)
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.compose.feature.ftue
|
||||
|
||||
import com.navi.amc.compose.common.model.AmcGenericScreenResponse
|
||||
import com.navi.amc.compose.feature.ftue.model.FundSelectionData
|
||||
import com.navi.amc.network.retrofit.RetrofitService
|
||||
import com.navi.amc.utils.getAmcMetricInfo
|
||||
import com.navi.common.network.models.RepoResult
|
||||
import com.navi.common.network.models.SuccessResponse
|
||||
import com.navi.common.network.retrofit.ResponseCallback
|
||||
import javax.inject.Inject
|
||||
|
||||
class FtueRepository @Inject constructor(private val retrofitService: RetrofitService) :
|
||||
ResponseCallback() {
|
||||
suspend fun fetchFtueEducateScreenData(): RepoResult<AmcGenericScreenResponse> =
|
||||
apiResponseCallback(
|
||||
response = retrofitService.fetchFtueEducateScreenData(),
|
||||
metricInfo = getAmcMetricInfo(),
|
||||
)
|
||||
|
||||
suspend fun fetchFtueFundSelectScreenData(): RepoResult<AmcGenericScreenResponse> =
|
||||
apiResponseCallback(
|
||||
response = retrofitService.fetchFtueFundSelectScreenData(),
|
||||
metricInfo = getAmcMetricInfo(),
|
||||
)
|
||||
|
||||
suspend fun postSelectedFund(
|
||||
fundSelectionData: FundSelectionData
|
||||
): RepoResult<SuccessResponse> =
|
||||
apiResponseCallback(
|
||||
response = retrofitService.postSelectedFund(fundSelectionData),
|
||||
metricInfo = getAmcMetricInfo(),
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.compose.feature.ftue.model
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class FundSelectionData(@SerializedName("isin") val isin: String? = null)
|
||||
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.compose.feature.ftue.ui
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.navi.amc.common.composables.AmcCommonWidgetListRenderer
|
||||
import com.navi.amc.compose.common.model.AmcGenericScreenResponse
|
||||
import com.navi.amc.compose.common.model.ScreenState
|
||||
import com.navi.amc.compose.common.ui.AmcShimmer
|
||||
import com.navi.amc.compose.common.ui.NaviAmcFooter
|
||||
import com.navi.amc.compose.common.ui.NaviAmcHeader
|
||||
import com.navi.amc.compose.common.ui.SetStatusBarColor
|
||||
import com.navi.amc.compose.common.utils.NaviAmcScreen
|
||||
import com.navi.amc.compose.common.utils.navigateToNextScreen
|
||||
import com.navi.amc.compose.entry.AmcComposeActivity
|
||||
import com.navi.amc.compose.entry.NaviAmcRouter
|
||||
import com.navi.amc.compose.feature.ftue.viewmodel.FtueViewModel
|
||||
import com.navi.amc.fundbuy.models.AmcHeaderExtraData
|
||||
import com.navi.amc.utils.AmcAnalytics.sendEvent
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.base.utils.EMPTY
|
||||
import com.navi.common.ui.errorview.FullScreenErrorComposeView
|
||||
import com.navi.naviwidgets.extensions.hexToColor
|
||||
import com.navi.naviwidgets.extensions.hexToInt
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.annotation.RootNavGraph
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
|
||||
@RootNavGraph(start = true)
|
||||
@Destination
|
||||
@Composable
|
||||
fun FtueEducateScreen(
|
||||
amcComposeActivity: AmcComposeActivity,
|
||||
navigator: DestinationsNavigator,
|
||||
ftueViewModel: FtueViewModel = hiltViewModel(),
|
||||
) {
|
||||
val ftueEducateScreenState by ftueViewModel.ftueEducateScreenState.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(key1 = ftueEducateScreenState) {
|
||||
if (ftueEducateScreenState is ScreenState.Empty) {
|
||||
ftueViewModel.fetchFtueEducateScreenData()
|
||||
}
|
||||
}
|
||||
|
||||
val onFooterNextCtaClicked = { actionData: ActionData? ->
|
||||
sendEvent(
|
||||
actionData?.metaData?.clickedData,
|
||||
screenName = NaviAmcScreen.AMC_FTUE_EDUCATE_SCREEN.screenName,
|
||||
)
|
||||
navigateToNextScreen(
|
||||
actionData = actionData,
|
||||
navigator = navigator,
|
||||
amcComposeActivity = amcComposeActivity,
|
||||
screen = NaviAmcScreen.AMC_FTUE_EDUCATE_SCREEN,
|
||||
)
|
||||
}
|
||||
|
||||
val onBackPressed = {
|
||||
// analytics
|
||||
amcComposeActivity.finish()
|
||||
}
|
||||
|
||||
BackHandler { onBackPressed() }
|
||||
|
||||
val onHelpClicked = { actionData: ActionData? ->
|
||||
amcComposeActivity.onHelpClick(helpBottomSheetData = null, bundle = null)
|
||||
}
|
||||
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
when (ftueEducateScreenState) {
|
||||
is ScreenState.Loading -> {
|
||||
AmcShimmer()
|
||||
}
|
||||
is ScreenState.Success -> {
|
||||
val data = ftueEducateScreenState as ScreenState.Success
|
||||
FtueEducateScreenRenderer(
|
||||
amcComposeActivity = amcComposeActivity,
|
||||
amcGenericScreenResponse = data.amcGenericScreenResponse,
|
||||
onBackPressed = onBackPressed,
|
||||
onHelpClicked = { onHelpClicked(it) },
|
||||
onFooterNextCtaClicked = onFooterNextCtaClicked,
|
||||
)
|
||||
}
|
||||
is ScreenState.Error -> {
|
||||
val data = ftueEducateScreenState as ScreenState.Error
|
||||
FullScreenErrorComposeView(
|
||||
error = data.error,
|
||||
onRetryClick = { ftueViewModel.fetchFtueEducateScreenData() },
|
||||
)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FtueEducateScreenRenderer(
|
||||
amcComposeActivity: AmcComposeActivity,
|
||||
amcGenericScreenResponse: AmcGenericScreenResponse,
|
||||
onBackPressed: () -> Unit,
|
||||
onHelpClicked: ((actionData: ActionData?) -> Unit)? = null,
|
||||
onFooterNextCtaClicked: ((actionData: ActionData?) -> Unit)? = null,
|
||||
onFooterBackCtaClicked: ((actionData: ActionData?) -> Unit)? = null,
|
||||
) {
|
||||
SetStatusBarColor(amcComposeActivity, hexToInt(amcGenericScreenResponse.header?.bgColor))
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize().imePadding(),
|
||||
topBar = {
|
||||
NaviAmcHeader(
|
||||
title = EMPTY,
|
||||
onNavigationIconClick = onBackPressed,
|
||||
actionIconText = "HELP",
|
||||
onActionClick = onHelpClicked,
|
||||
backgroundColor = hexToColor(amcGenericScreenResponse.header?.bgColor),
|
||||
)
|
||||
},
|
||||
content = { innerPadding ->
|
||||
AmcCommonWidgetListRenderer(
|
||||
widgetList = amcGenericScreenResponse.content,
|
||||
actionListener = { actionData ->
|
||||
NaviAmcRouter.onCtaClick(
|
||||
amcComposeActivity = amcComposeActivity,
|
||||
actionData = actionData,
|
||||
)
|
||||
},
|
||||
extraData =
|
||||
AmcHeaderExtraData(
|
||||
header = amcGenericScreenResponse.header,
|
||||
toggleStatusBarAndHeaderData = { color, showTitle -> },
|
||||
showHeaderTitle = false,
|
||||
toggleShadow = { shadowState -> },
|
||||
),
|
||||
eventHandler = { actionData -> },
|
||||
)
|
||||
},
|
||||
bottomBar = {
|
||||
NaviAmcFooter(
|
||||
footer = amcGenericScreenResponse.footer,
|
||||
onNextCtaClicked = onFooterNextCtaClicked,
|
||||
onBackCtaClicked = onFooterBackCtaClicked,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.compose.feature.ftue.ui
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.imePadding
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.navi.amc.common.activity.CheckerActivity
|
||||
import com.navi.amc.common.composables.AmcCommonWidgetListRenderer
|
||||
import com.navi.amc.compose.common.model.AmcGenericScreenResponse
|
||||
import com.navi.amc.compose.common.model.ScreenState
|
||||
import com.navi.amc.compose.common.ui.AmcShimmer
|
||||
import com.navi.amc.compose.common.ui.NaviAmcFooter
|
||||
import com.navi.amc.compose.common.ui.NaviAmcHeader
|
||||
import com.navi.amc.compose.common.ui.SetStatusBarColor
|
||||
import com.navi.amc.compose.common.utils.NaviAmcScreen
|
||||
import com.navi.amc.compose.common.utils.navigateToNextScreen
|
||||
import com.navi.amc.compose.entry.AmcComposeActivity
|
||||
import com.navi.amc.compose.feature.ftue.viewmodel.FtueViewModel
|
||||
import com.navi.amc.fundbuy.models.AmcHeaderExtraData
|
||||
import com.navi.amc.fundbuy.models.SelectionData
|
||||
import com.navi.amc.navigator.NaviAmcDeeplinkNavigator
|
||||
import com.navi.amc.navigator.NaviAmcDeeplinkNavigator.FTUE
|
||||
import com.navi.amc.utils.AmcAnalytics.ISIN
|
||||
import com.navi.amc.utils.AmcAnalytics.sendEvent
|
||||
import com.navi.amc.utils.Constant
|
||||
import com.navi.amc.utils.TempStorageHelper
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.base.utils.EMPTY
|
||||
import com.navi.base.utils.orFalse
|
||||
import com.navi.common.ui.errorview.FullScreenErrorComposeView
|
||||
import com.navi.naviwidgets.extensions.hexToInt
|
||||
import com.ramcosta.composedestinations.annotation.Destination
|
||||
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
|
||||
|
||||
@Destination
|
||||
@Composable
|
||||
fun FtueFundSelectScreen(
|
||||
amcComposeActivity: AmcComposeActivity,
|
||||
navigator: DestinationsNavigator,
|
||||
ftueViewModel: FtueViewModel = hiltViewModel(),
|
||||
) {
|
||||
val ftueFundSelectScreenState by
|
||||
ftueViewModel.ftueFundSelectScreenState.collectAsStateWithLifecycle()
|
||||
|
||||
LaunchedEffect(key1 = ftueFundSelectScreenState) {
|
||||
if (ftueFundSelectScreenState is ScreenState.Empty) {
|
||||
ftueViewModel.fetchFtueFundSelectScreenState()
|
||||
}
|
||||
}
|
||||
|
||||
val onFooterNextCtaClicked = { actionData: ActionData? ->
|
||||
ftueViewModel.postSelectedFund()
|
||||
sendEvent(
|
||||
actionData?.metaData?.clickedData,
|
||||
screenName = NaviAmcScreen.AMC_FTUE_FUND_SELECT_SCREEN.screenName,
|
||||
)
|
||||
if (
|
||||
actionData
|
||||
?.url
|
||||
?.contains(
|
||||
NaviAmcDeeplinkNavigator.AMC.plus("/").plus(NaviAmcDeeplinkNavigator.KYC),
|
||||
true,
|
||||
)
|
||||
.orFalse() ||
|
||||
actionData?.url?.contains(CheckerActivity.HPC_PAN_REDIRECTION_PAGE).orFalse() ||
|
||||
actionData?.url?.contains(CheckerActivity.HPC_NAME_REDIRECTION_PAGE).orFalse()
|
||||
) {
|
||||
TempStorageHelper.kycSourceInfo =
|
||||
mapOf(
|
||||
Constant.KYC_SOURCE_SCREEN to FTUE,
|
||||
ISIN to ftueViewModel.selectedIsin.orEmpty(),
|
||||
)
|
||||
}
|
||||
navigateToNextScreen(
|
||||
actionData = actionData,
|
||||
navigator = navigator,
|
||||
amcComposeActivity = amcComposeActivity,
|
||||
screen = NaviAmcScreen.AMC_FTUE_FUND_SELECT_SCREEN,
|
||||
)
|
||||
}
|
||||
|
||||
val onHelpClicked = { actionData: ActionData? ->
|
||||
amcComposeActivity.onHelpClick(helpBottomSheetData = null, bundle = null)
|
||||
}
|
||||
|
||||
val onBackPressed = {
|
||||
// analytics
|
||||
amcComposeActivity.finish()
|
||||
Unit
|
||||
}
|
||||
|
||||
BackHandler { onBackPressed() }
|
||||
|
||||
val onFundSelected = { actionData: ActionData? ->
|
||||
val isin = actionData?.parameters?.find { it.key == ISIN }?.value
|
||||
ftueViewModel.updateSelectedIsin(isin)
|
||||
}
|
||||
|
||||
val selectedIsin = ftueViewModel.selectedIsin
|
||||
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
when (ftueFundSelectScreenState) {
|
||||
is ScreenState.Loading -> {
|
||||
AmcShimmer()
|
||||
}
|
||||
is ScreenState.Success -> {
|
||||
val data = ftueFundSelectScreenState as ScreenState.Success
|
||||
FtueFundSelectScreenRenderer(
|
||||
amcComposeActivity = amcComposeActivity,
|
||||
amcGenericScreenResponse = data.amcGenericScreenResponse,
|
||||
onFundSelected = onFundSelected,
|
||||
onBackPressed = onBackPressed,
|
||||
onHelpClicked = onHelpClicked,
|
||||
onFooterNextCtaClicked = onFooterNextCtaClicked,
|
||||
selectedIsin = selectedIsin,
|
||||
)
|
||||
}
|
||||
is ScreenState.Error -> {
|
||||
val data = ftueFundSelectScreenState as ScreenState.Error
|
||||
FullScreenErrorComposeView(
|
||||
error = data.error,
|
||||
onRetryClick = { ftueViewModel.fetchFtueFundSelectScreenState() },
|
||||
)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FtueFundSelectScreenRenderer(
|
||||
amcComposeActivity: AmcComposeActivity,
|
||||
amcGenericScreenResponse: AmcGenericScreenResponse,
|
||||
onFundSelected: ((actionData: ActionData?) -> Unit)? = null,
|
||||
onBackPressed: () -> Unit,
|
||||
onHelpClicked: ((actionData: ActionData?) -> Unit)? = null,
|
||||
onFooterNextCtaClicked: ((actionData: ActionData?) -> Unit)? = null,
|
||||
onFooterBackCtaClicked: ((actionData: ActionData?) -> Unit)? = null,
|
||||
selectedIsin: String? = null,
|
||||
) {
|
||||
SetStatusBarColor(amcComposeActivity, hexToInt(amcGenericScreenResponse.header?.bgColor))
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize().imePadding(),
|
||||
topBar = {
|
||||
// generalise this function to receive header dto
|
||||
NaviAmcHeader(
|
||||
title = EMPTY,
|
||||
onNavigationIconClick = onBackPressed,
|
||||
actionIconText = amcGenericScreenResponse.header?.help?.title?.text ?: EMPTY,
|
||||
onActionClick = onHelpClicked,
|
||||
)
|
||||
},
|
||||
content = { innerPadding ->
|
||||
AmcCommonWidgetListRenderer(
|
||||
widgetList = amcGenericScreenResponse.content,
|
||||
actionListener = { actionData -> onFundSelected?.invoke(actionData) },
|
||||
extraData =
|
||||
AmcHeaderExtraData(
|
||||
header = amcGenericScreenResponse.header,
|
||||
toggleStatusBarAndHeaderData = { color, showTitle -> },
|
||||
showHeaderTitle = false,
|
||||
toggleShadow = { shadowState -> },
|
||||
),
|
||||
eventHandler = { actionData -> },
|
||||
selectionExtraData = SelectionData(selectedId = selectedIsin),
|
||||
)
|
||||
},
|
||||
bottomBar = {
|
||||
NaviAmcFooter(
|
||||
footer = amcGenericScreenResponse.footer,
|
||||
onNextCtaClicked = onFooterNextCtaClicked,
|
||||
onBackCtaClicked = onFooterBackCtaClicked,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.compose.feature.ftue.viewmodel
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.navi.amc.common.viewmodel.BaseAmcVM
|
||||
import com.navi.amc.compose.common.model.ScreenState
|
||||
import com.navi.amc.compose.feature.ftue.FtueRepository
|
||||
import com.navi.amc.compose.feature.ftue.model.FundSelectionData
|
||||
import com.navi.common.network.models.isSuccessWithData
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
@HiltViewModel
|
||||
class FtueViewModel @Inject constructor(private val ftueRepository: FtueRepository) : BaseAmcVM() {
|
||||
|
||||
// implement caching
|
||||
|
||||
private val _ftueEducateScreenState = MutableStateFlow<ScreenState>(ScreenState.Empty)
|
||||
val ftueEducateScreenState = _ftueEducateScreenState.asStateFlow()
|
||||
|
||||
private val _ftueFundSelectScreenState = MutableStateFlow<ScreenState>(ScreenState.Empty)
|
||||
val ftueFundSelectScreenState = _ftueFundSelectScreenState.asStateFlow()
|
||||
|
||||
var selectedIsin by mutableStateOf<String?>(null)
|
||||
private set
|
||||
|
||||
private fun updateFtueEducateScreenState(state: ScreenState) {
|
||||
_ftueEducateScreenState.value = state
|
||||
}
|
||||
|
||||
private fun updateFtueFundSelectScreenState(state: ScreenState) {
|
||||
_ftueFundSelectScreenState.value = state
|
||||
}
|
||||
|
||||
fun updateSelectedIsin(isin: String?) {
|
||||
isin?.let { selectedIsin = isin }
|
||||
}
|
||||
|
||||
fun postSelectedFund() {
|
||||
val isin = selectedIsin
|
||||
isin?.let {
|
||||
viewModelScope.safeLaunch {
|
||||
if (isin.isNotEmpty()) {
|
||||
val fundSelectionData = FundSelectionData(isin = isin)
|
||||
ftueRepository.postSelectedFund(fundSelectionData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchFtueEducateScreenData() {
|
||||
viewModelScope.safeLaunch {
|
||||
updateFtueEducateScreenState(ScreenState.Loading)
|
||||
|
||||
val ftueEducateResponse = ftueRepository.fetchFtueEducateScreenData()
|
||||
|
||||
if (ftueEducateResponse.isSuccessWithData()) {
|
||||
updateFtueEducateScreenState(
|
||||
ScreenState.Success(
|
||||
amcGenericScreenResponse = ftueEducateResponse.data ?: return@safeLaunch
|
||||
)
|
||||
)
|
||||
} else {
|
||||
updateFtueEducateScreenState(
|
||||
ScreenState.Error(
|
||||
error = ftueEducateResponse.errors?.firstOrNull() ?: return@safeLaunch
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun fetchFtueFundSelectScreenState() {
|
||||
viewModelScope.safeLaunch {
|
||||
updateFtueFundSelectScreenState(ScreenState.Loading)
|
||||
|
||||
val ftueFundSelectResponse = ftueRepository.fetchFtueFundSelectScreenData()
|
||||
|
||||
if (ftueFundSelectResponse.isSuccessWithData()) {
|
||||
updateFtueFundSelectScreenState(
|
||||
ScreenState.Success(
|
||||
amcGenericScreenResponse = ftueFundSelectResponse.data ?: return@safeLaunch
|
||||
)
|
||||
)
|
||||
} else {
|
||||
updateFtueFundSelectScreenState(
|
||||
ScreenState.Error(
|
||||
error = ftueFundSelectResponse.errors?.firstOrNull() ?: return@safeLaunch
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.fundbuy.composables.cards
|
||||
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2024-2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
import PaymentCard
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.DefaultShadowColor
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.navi.amc.utils.Constant.UPCOMING_SIP_PAYMENT_CARD
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.base.utils.isNotNull
|
||||
import com.navi.base.utils.orFalse
|
||||
import com.navi.base.utils.orTrue
|
||||
import com.navi.design.utils.clickableWithNoGesture
|
||||
import com.navi.naviwidgets.extensions.NaviImage
|
||||
import com.navi.naviwidgets.extensions.NaviTextWidgetized
|
||||
import com.navi.uitron.utils.ShapeUtil
|
||||
import com.navi.uitron.utils.getBorderStrokeBrushData
|
||||
import com.navi.uitron.utils.hexToComposeColor
|
||||
import com.navi.uitron.utils.setBackground
|
||||
import com.navi.uitron.utils.setHorizontalArrangement
|
||||
import com.navi.uitron.utils.setPadding
|
||||
|
||||
@Composable
|
||||
fun PaymentCardComposable(
|
||||
paymentCard: PaymentCard,
|
||||
cardWidth: Dp = LocalConfiguration.current.screenWidthDp.dp,
|
||||
onClick: (actionData: ActionData?) -> Unit,
|
||||
cardType: String? = UPCOMING_SIP_PAYMENT_CARD,
|
||||
) {
|
||||
|
||||
if (paymentCard.properties?.cardProperty?.visible.orTrue().not()) {
|
||||
return
|
||||
}
|
||||
|
||||
Card(
|
||||
shape = ShapeUtil.run { getShape(shape = paymentCard.properties?.cardProperty?.shape) },
|
||||
elevation = 0.dp,
|
||||
backgroundColor =
|
||||
paymentCard.properties?.cardProperty?.backgroundColor?.hexToComposeColor ?: Color.White,
|
||||
border =
|
||||
BorderStroke(
|
||||
width =
|
||||
((paymentCard.properties?.cardProperty?.borderStrokeData?.width) ?: 0.0f).dp,
|
||||
brush =
|
||||
getBorderStrokeBrushData(paymentCard.properties?.cardProperty?.borderStrokeData),
|
||||
),
|
||||
modifier =
|
||||
Modifier.width(cardWidth)
|
||||
.shadow(
|
||||
elevation = (paymentCard.properties?.cardProperty?.elevation ?: 0).dp,
|
||||
ambientColor =
|
||||
paymentCard.properties?.cardProperty?.ambientColor?.hexToComposeColor
|
||||
?: DefaultShadowColor,
|
||||
spotColor =
|
||||
paymentCard.properties?.cardProperty?.spotColor?.hexToComposeColor
|
||||
?: DefaultShadowColor,
|
||||
shape = ShapeUtil.getShape(shape = paymentCard.properties?.cardProperty?.shape),
|
||||
)
|
||||
.clickableWithNoGesture(onClick = { onClick(paymentCard.actionData) }),
|
||||
) {
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.padding(
|
||||
top = (paymentCard.properties?.cardProperty?.padding?.top ?: 0).dp,
|
||||
bottom = (paymentCard.properties?.cardProperty?.padding?.bottom ?: 0).dp,
|
||||
start = (paymentCard.properties?.cardProperty?.padding?.start ?: 0).dp,
|
||||
end = (paymentCard.properties?.cardProperty?.padding?.end ?: 0).dp,
|
||||
)
|
||||
) {
|
||||
if (
|
||||
paymentCard.paymentCardHeader.isNotNull() &&
|
||||
paymentCard.properties?.paymentCardHeaderProperty?.visible.orTrue()
|
||||
) {
|
||||
PaymentCardHeader(paymentCard = paymentCard)
|
||||
}
|
||||
|
||||
PaymentDetailsComposable(paymentCard = paymentCard, cardType = cardType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PaymentDetailsComposable(paymentCard: PaymentCard, cardType: String?) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (paymentCard.properties?.investmentsIconProperty?.visible.orFalse()) {
|
||||
paymentCard.investmentsIcon?.let {
|
||||
NaviImage(
|
||||
imageFieldData = it,
|
||||
modifier =
|
||||
Modifier.width((it.iconWidth ?: 12).dp).height((it.iconHeight ?: 12).dp),
|
||||
)
|
||||
|
||||
Spacer(
|
||||
modifier =
|
||||
Modifier.wrapContentHeight()
|
||||
.width(
|
||||
(paymentCard.properties?.investmentsIconProperty?.margin?.end ?: 8)
|
||||
.dp
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier.weight(paymentCard.properties?.cardProperty?.cardWeight ?: 1.0f)
|
||||
) {
|
||||
if (cardType == "NORMAL_CARD") {
|
||||
NaviTextWidgetized(textFieldData = paymentCard.fundName)
|
||||
} else if (cardType == UPCOMING_SIP_PAYMENT_CARD) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
NaviTextWidgetized(textFieldData = paymentCard.paymentAmount)
|
||||
NaviTextWidgetized(textFieldData = paymentCard.fundName)
|
||||
}
|
||||
NaviTextWidgetized(
|
||||
textFieldData = paymentCard.paymentCardSubtitle,
|
||||
modifier =
|
||||
Modifier.height((paymentCard.paymentCardSubtitle?.lineSpacing ?: 16).dp),
|
||||
)
|
||||
} else {
|
||||
NaviTextWidgetized(
|
||||
textFieldData = paymentCard.fundName,
|
||||
modifier = Modifier.height((paymentCard.fundName?.lineSpacing ?: 16).dp),
|
||||
)
|
||||
|
||||
paymentCard.paymentAmount?.let {
|
||||
NaviTextWidgetized(
|
||||
textFieldData = paymentCard.paymentAmount,
|
||||
modifier =
|
||||
Modifier.height((paymentCard.paymentAmount?.lineSpacing ?: 16).dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(
|
||||
modifier =
|
||||
Modifier.weight((1 - (paymentCard.properties?.cardProperty?.cardWeight ?: 1.0f)))
|
||||
)
|
||||
|
||||
NaviTextWidgetized(
|
||||
textFieldData = paymentCard.buttonText,
|
||||
modifier =
|
||||
Modifier.setBackground(
|
||||
paymentCard.properties?.buttonProperty?.backgroundColor,
|
||||
paymentCard.properties?.buttonProperty?.shape,
|
||||
paymentCard.properties?.buttonProperty?.backGroundBrushData,
|
||||
)
|
||||
.padding(
|
||||
top = (paymentCard.properties?.buttonProperty?.padding?.top ?: 0).dp,
|
||||
bottom = (paymentCard.properties?.buttonProperty?.padding?.bottom ?: 0).dp,
|
||||
start = (paymentCard.properties?.buttonProperty?.padding?.start ?: 0).dp,
|
||||
end = (paymentCard.properties?.buttonProperty?.padding?.end ?: 0).dp,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PaymentCardHeader(paymentCard: PaymentCard) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.setPadding(paymentCard.properties?.paymentCardHeaderProperty?.padding)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement =
|
||||
Arrangement.setHorizontalArrangement(
|
||||
arrangementData = paymentCard.properties?.paymentCardHeaderProperty?.arrangementData
|
||||
),
|
||||
) {
|
||||
paymentCard.paymentCardHeader?.paymentType?.let {
|
||||
NaviTextWidgetized(
|
||||
modifier =
|
||||
Modifier.setBackground(
|
||||
paymentCard.properties?.paymentTypeProperty?.backgroundColor,
|
||||
paymentCard.properties?.paymentTypeProperty?.shape,
|
||||
paymentCard.properties?.paymentTypeProperty?.backGroundBrushData,
|
||||
)
|
||||
.padding(
|
||||
start =
|
||||
(paymentCard.properties?.paymentTypeProperty?.padding?.start ?: 0)
|
||||
.dp,
|
||||
end =
|
||||
(paymentCard.properties?.paymentTypeProperty?.padding?.end ?: 0).dp,
|
||||
top =
|
||||
(paymentCard.properties?.paymentTypeProperty?.padding?.top ?: 0).dp,
|
||||
bottom =
|
||||
(paymentCard.properties?.paymentTypeProperty?.padding?.bottom ?: 0)
|
||||
.dp,
|
||||
),
|
||||
textFieldData = it,
|
||||
)
|
||||
}
|
||||
|
||||
paymentCard.paymentCardHeader?.paymentDate?.let {
|
||||
NaviTextWidgetized(
|
||||
textFieldData = it,
|
||||
modifier = Modifier.setPadding(paymentCard.properties?.paymentDateProperty?.padding),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,12 +5,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.fundbuy.composables
|
||||
package com.navi.amc.fundbuy.composables.widgets
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -42,10 +41,19 @@ fun BannerWidgetComposable(widget: TitleSubtitleImageWidget?) {
|
||||
NaviImage(
|
||||
imageFieldData = data.image,
|
||||
modifier =
|
||||
Modifier.setPadding(
|
||||
data.properties?.titleProperty?.padding
|
||||
?: ComposePadding(top = 16, bottom = 16)
|
||||
),
|
||||
Modifier.then(
|
||||
if (data.properties?.imageProperty?.cardWeight != null) {
|
||||
Modifier.fillMaxWidth(
|
||||
data.properties.imageProperty.cardWeight ?: 1f
|
||||
)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
)
|
||||
.setPadding(
|
||||
data.properties?.titleProperty?.padding
|
||||
?: ComposePadding(top = 16, bottom = 16)
|
||||
),
|
||||
)
|
||||
NaviTextWidgetized(
|
||||
textFieldData = data.subTitle,
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.fundbuy.composables.widgets
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.navi.amc.fundbuy.models.widgets.TitleSubtitleLottieWidget
|
||||
import com.navi.naviwidgets.extensions.NaviLottie
|
||||
import com.navi.naviwidgets.extensions.NaviTextWidgetized
|
||||
import com.navi.naviwidgets.extensions.hexToColor
|
||||
import com.navi.uitron.model.ui.ComposePadding
|
||||
import com.navi.uitron.utils.ShapeUtil
|
||||
import com.navi.uitron.utils.setPadding
|
||||
|
||||
@Composable
|
||||
fun BannerWithLottieWidgetComposable(widget: TitleSubtitleLottieWidget?) {
|
||||
widget?.widgetData?.let { data ->
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.background(
|
||||
color = hexToColor(data.properties?.cardProperty?.backgroundColor),
|
||||
shape = ShapeUtil.getShape(data.properties?.cardProperty?.shape),
|
||||
)
|
||||
.setPadding(
|
||||
data.properties?.cardProperty?.padding
|
||||
?: ComposePadding(start = 16, end = 16, top = 12)
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
NaviTextWidgetized(textFieldData = data.title)
|
||||
data.lottie?.let {
|
||||
NaviLottie(
|
||||
lottie = it,
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.setPadding(
|
||||
data.properties?.lottieProperty?.padding
|
||||
?: ComposePadding(top = 16, bottom = 16)
|
||||
),
|
||||
)
|
||||
}
|
||||
NaviTextWidgetized(
|
||||
textFieldData = data.subTitle,
|
||||
modifier =
|
||||
Modifier.setPadding(
|
||||
data.properties?.subTitleProperty?.padding ?: ComposePadding(bottom = 24)
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.fundbuy.composables
|
||||
package com.navi.amc.fundbuy.composables.widgets
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.fundbuy.composables
|
||||
package com.navi.amc.fundbuy.composables.widgets
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.fundbuy.composables
|
||||
package com.navi.amc.fundbuy.composables.widgets
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.Canvas
|
||||
@@ -15,7 +15,6 @@ import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.fundbuy.composables.widgets
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
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.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.navi.amc.common.composables.AmcCardListComposable
|
||||
import com.navi.amc.fundbuy.models.widgets.RepeatOrderWidget
|
||||
import com.navi.amc.utils.Constant.DEFAULT_CARD_WIDTH_FACTOR
|
||||
import com.navi.amc.utils.Constant.FREE_SCROLL
|
||||
import com.navi.amc.utils.Constant.REPEAT_ORDER
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.base.model.GenericAnalytics
|
||||
import com.navi.naviwidgets.extensions.NaviTextWidgetized
|
||||
import com.navi.naviwidgets.extensions.hexToColor
|
||||
import com.navi.uitron.utils.setPadding
|
||||
|
||||
@Composable
|
||||
fun RepeatOrderWidgetComposable(
|
||||
widget: RepeatOrderWidget,
|
||||
onClick: (actionData: ActionData?) -> Unit,
|
||||
onVisible: ((genericAnalytics: GenericAnalytics?) -> Unit)? = null,
|
||||
) {
|
||||
widget.widgetData?.let {
|
||||
Column(modifier = Modifier.setPadding(it.extraData?.property?.padding)) {
|
||||
val listState = rememberLazyListState()
|
||||
val pagerState =
|
||||
rememberPagerState(pageCount = { it.content?.repeatOrderCardList?.size ?: 0 })
|
||||
NaviTextWidgetized(
|
||||
textFieldData = it.header?.title,
|
||||
modifier = Modifier.setPadding(it.header?.property?.padding),
|
||||
)
|
||||
|
||||
AmcCardListComposable(
|
||||
cardType = REPEAT_ORDER,
|
||||
cardWidthFactor =
|
||||
(it.extraData?.property?.cardWidthFactor ?: DEFAULT_CARD_WIDTH_FACTOR),
|
||||
cardList = it.content?.repeatOrderCardList,
|
||||
onClick = onClick,
|
||||
scrollType = it.extraData?.scrollType ?: FREE_SCROLL,
|
||||
listState = listState,
|
||||
pagerState = pagerState,
|
||||
)
|
||||
|
||||
it.content?.repeatOrderCardList?.size?.let { listSize ->
|
||||
it.content.carouselData?.let { carouselData ->
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier =
|
||||
Modifier.setPadding(carouselData.carouselProperty?.padding)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
repeat(listSize) { index ->
|
||||
Box(
|
||||
modifier =
|
||||
Modifier.size(
|
||||
width =
|
||||
if (index == pagerState.currentPage) {
|
||||
(carouselData.activeCarouselWidth ?: 24).dp
|
||||
} else {
|
||||
(carouselData.inactiveCarouselWidth ?: 16).dp
|
||||
},
|
||||
height = (carouselData.carouselHeight ?: 3).dp,
|
||||
)
|
||||
.clip(CircleShape)
|
||||
.background(
|
||||
if (index == pagerState.currentPage) {
|
||||
hexToColor(carouselData.activeCarouselColor)
|
||||
} else hexToColor(carouselData.inactiveCarouselColor)
|
||||
)
|
||||
.padding(horizontal = 4.dp)
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.fundbuy.composables.widgets
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.layout.wrapContentWidth
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.navi.amc.fundbuy.models.SelectionData
|
||||
import com.navi.amc.fundbuy.models.widgets.TitleContentRadioWidget
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.base.model.LineItem
|
||||
import com.navi.base.utils.orZero
|
||||
import com.navi.design.utils.clickableWithNoGesture
|
||||
import com.navi.naviwidgets.extensions.NaviImage
|
||||
import com.navi.naviwidgets.extensions.NaviTextWidgetized
|
||||
import com.navi.naviwidgets.extensions.hexToColor
|
||||
import com.navi.uitron.model.ui.ComposePadding
|
||||
import com.navi.uitron.utils.ShapeUtil
|
||||
import com.navi.uitron.utils.setPadding
|
||||
|
||||
@Composable
|
||||
fun TitleContentRadioWidgetComposable(
|
||||
widget: TitleContentRadioWidget?,
|
||||
onClick: (actionData: ActionData?) -> Unit,
|
||||
selectionData: SelectionData? = null,
|
||||
) {
|
||||
LaunchedEffect(Unit) {
|
||||
if (widget?.widgetData?.selectionProperties?.isSelected == true) {
|
||||
onClick(
|
||||
ActionData(
|
||||
parameters =
|
||||
listOf(
|
||||
LineItem(
|
||||
key = "isin",
|
||||
value = widget.widgetData.selectionProperties.isin,
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
widget?.widgetData?.let { data ->
|
||||
Column(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.clickableWithNoGesture {
|
||||
onClick(
|
||||
ActionData(
|
||||
parameters =
|
||||
listOf(
|
||||
LineItem(
|
||||
key = "isin",
|
||||
value = data.selectionProperties?.isin,
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
.background(
|
||||
color = hexToColor(data.properties?.cardProperty?.backgroundColor),
|
||||
shape = ShapeUtil.getShape(data.properties?.cardProperty?.shape),
|
||||
)
|
||||
.setPadding(
|
||||
data.properties?.cardProperty?.padding
|
||||
?: ComposePadding(start = 16, end = 16, top = 16, bottom = 16)
|
||||
)
|
||||
.border(
|
||||
width = data.properties?.cardProperty?.borderStrokeData?.width?.dp ?: 0.dp,
|
||||
color = hexToColor(data.properties?.cardProperty?.borderStrokeData?.color),
|
||||
shape =
|
||||
ShapeUtil.getShape(
|
||||
data.properties?.cardProperty?.borderStrokeData?.shape
|
||||
),
|
||||
),
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.setPadding(
|
||||
data.properties?.titleProperty?.padding
|
||||
?: ComposePadding(start = 16, end = 16, top = 16, bottom = 4)
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
data.title?.let { NaviTextWidgetized(textFieldData = it) }
|
||||
Spacer(modifier = Modifier.width(8.dp))
|
||||
data.tagTitle?.let {
|
||||
NaviTextWidgetized(
|
||||
textFieldData = it,
|
||||
modifier =
|
||||
Modifier.background(
|
||||
color =
|
||||
hexToColor(data.properties?.tagProperty?.backgroundColor),
|
||||
shape = ShapeUtil.getShape(data.properties?.tagProperty?.shape),
|
||||
)
|
||||
.setPadding(
|
||||
data.properties?.tagProperty?.padding
|
||||
?: ComposePadding(start = 10, end = 10, top = 2, bottom = 2)
|
||||
),
|
||||
)
|
||||
}
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
if (data.showTitleRightIcon == true) {
|
||||
data.selectionProperties?.let {
|
||||
NaviImage(
|
||||
imageFieldData =
|
||||
if (selectionData?.selectedId == it.isin) it.selectedTitleRightIcon
|
||||
else it.unselectedTitleRightIcon,
|
||||
modifier =
|
||||
Modifier.setPadding(
|
||||
data.selectionProperties.titleRightIconProperties?.padding
|
||||
?: ComposePadding(start = 0, end = 0, top = 0, bottom = 0)
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
data.subTitle?.let {
|
||||
NaviTextWidgetized(
|
||||
textFieldData = it,
|
||||
modifier =
|
||||
Modifier.setPadding(
|
||||
data.properties?.subTitleProperty?.padding
|
||||
?: ComposePadding(start = 16, end = 16, top = 0, bottom = 16)
|
||||
),
|
||||
)
|
||||
}
|
||||
data.contentTitle?.let {
|
||||
NaviTextWidgetized(
|
||||
textFieldData = it,
|
||||
modifier =
|
||||
Modifier.setPadding(
|
||||
data.properties?.contentTitleProperty?.padding
|
||||
?: ComposePadding(start = 16, end = 16, top = 0, bottom = 4)
|
||||
),
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier =
|
||||
Modifier.fillMaxWidth()
|
||||
.setPadding(
|
||||
data.properties?.contentSubtitleProperty?.padding
|
||||
?: ComposePadding(start = 16, end = 16, top = 0, bottom = 16)
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
data.contentLeftSubtitle?.let {
|
||||
NaviTextWidgetized(
|
||||
textFieldData = it,
|
||||
modifier =
|
||||
Modifier.then(
|
||||
if (data.contentLeftSubtitle.text?.length.orZero() > 40) {
|
||||
Modifier.weight(1f)
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
),
|
||||
)
|
||||
}
|
||||
data.contentMiddleImage?.let { NaviImage(imageFieldData = it) }
|
||||
data.contentRightSubtitle?.let {
|
||||
NaviTextWidgetized(textFieldData = it, modifier = Modifier.wrapContentWidth())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,3 +46,5 @@ data class AmcHeaderExtraData(
|
||||
val scrollOffsetForHeaderStateToggle: Int? = null,
|
||||
val toggleShadow: ((showShadow: Boolean) -> Unit)? = null,
|
||||
)
|
||||
|
||||
data class SelectionData(val selectedId: String? = null)
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
*/
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.navi.amc.common.model.NextCtaResponse
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.common.model.GenericBottomSheetData
|
||||
import com.navi.common.model.InvestmentBaseProperty
|
||||
@@ -40,3 +41,30 @@ data class CardProperties(
|
||||
@SerializedName("investmentsIconProperty")
|
||||
val investmentsIconProperty: InvestmentBaseProperty? = null,
|
||||
)
|
||||
|
||||
data class BottomSheetData(
|
||||
@SerializedName("title") val title: TextFieldData? = null,
|
||||
@SerializedName("subtitle") val subtitle: TextFieldData? = null,
|
||||
@SerializedName("leftIcon") val leftIcon: ImageFieldData? = null,
|
||||
@SerializedName("rightIcon") val rightIcon: ImageFieldData? = null,
|
||||
@SerializedName("buttonText", alternate = ["primaryButtonText"])
|
||||
val buttonText: TextFieldData? = null,
|
||||
@SerializedName("secondaryButtonText") val secondaryButtonText: TextFieldData? = null,
|
||||
@SerializedName("noteData") val noteData: BottomSheetData? = null,
|
||||
@SerializedName("imageUrl") val imageUrl: ImageFieldData? = null,
|
||||
@SerializedName("properties") val properties: BottomSheetProperties? = null,
|
||||
@SerializedName("actionData") val actionData: ActionData? = null,
|
||||
@SerializedName("nextCtaResponse") val nextCtaResponse: NextCtaResponse? = null,
|
||||
)
|
||||
|
||||
data class BottomSheetProperties(
|
||||
@SerializedName("buttonProperty", alternate = ["primaryButtonProperty"])
|
||||
val primaryButtonProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("secondaryButtonProperty")
|
||||
val secondaryButtonProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("subtitleProperty") val subtitleProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("titleProperty") val titleProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("imageProperty") val imageProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("noteProperty") val noteProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("bottomSheetProperty") val bottomSheetProperty: InvestmentBaseProperty? = null,
|
||||
)
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.fundbuy.models.widgets
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.navi.amc.common.model.GenericComposableWidget
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.base.model.GenericAnalytics
|
||||
import com.navi.common.model.InvestmentBaseProperty
|
||||
import com.navi.naviwidgets.models.response.ImageFieldData
|
||||
import com.navi.naviwidgets.models.response.TextFieldData
|
||||
|
||||
data class FtueWithTrackerWidget(
|
||||
@SerializedName("widgetName") override val widgetName: String? = null,
|
||||
@SerializedName("widgetId") override val widgetId: String? = null,
|
||||
@SerializedName("widgetData") val widgetData: FtueWithTrackerWidgetData? = null,
|
||||
) : GenericComposableWidget
|
||||
|
||||
data class FtueWithTrackerWidgetData(
|
||||
@SerializedName("content") val content: FtueWithTrackerWidgetContent? = null,
|
||||
@SerializedName("extraData") val extraData: FtueWithTrackerWidgetExtraData? = null,
|
||||
)
|
||||
|
||||
data class FtueWithTrackerWidgetContent(
|
||||
@SerializedName("topLogo") val topLogo: ImageFieldData? = null,
|
||||
@SerializedName("topText") val topText: TextFieldData? = null,
|
||||
@SerializedName("rolodexItems") val rolodexItems: List<TextFieldData>? = null,
|
||||
@SerializedName("chipItems") val chipItems: List<TextFieldData>? = null,
|
||||
@SerializedName("actionCardData") val actionCardData: FtueWithTrackerActionCardData? = null,
|
||||
@SerializedName("properties") val properties: FtueWithTrackerWidgetProperties? = null,
|
||||
)
|
||||
|
||||
data class FtueWithTrackerActionCardData(
|
||||
@SerializedName("trackerItems") val trackerItems: List<IconWithTextCard>? = null,
|
||||
@SerializedName("buttonText") val buttonText: TextFieldData? = null,
|
||||
@SerializedName("footerTexts") val footerTexts: FooterTexts? = null,
|
||||
@SerializedName("actionData") val actionData: ActionData? = null,
|
||||
@SerializedName("actionCardProperty") val actionCardProperty: FtueActionCardProperty? = null,
|
||||
)
|
||||
|
||||
data class IconWithTextCard(
|
||||
@SerializedName("icon") val icon: ImageFieldData? = null,
|
||||
@SerializedName("text") val text: TextFieldData? = null,
|
||||
@SerializedName("actionData") val actionData: ActionData? = null,
|
||||
@SerializedName("property") val property: InvestmentBaseProperty? = null,
|
||||
)
|
||||
|
||||
data class FooterTexts(
|
||||
@SerializedName("leftText") val leftText: TextFieldData? = null,
|
||||
@SerializedName("rightText") val rightText: TextFieldData? = null,
|
||||
)
|
||||
|
||||
data class FtueActionCardProperty(
|
||||
@SerializedName("cardProperty") val cardProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("trackerItemsProperty")
|
||||
val trackerItemsProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("trackerDividerProperty")
|
||||
val trackerDividerProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("buttonProperty") val buttonProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("buttonTextProperty") val buttonTextProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("footerTextsProperty") val footerTextsProperty: InvestmentBaseProperty? = null,
|
||||
)
|
||||
|
||||
data class FtueWithTrackerWidgetProperties(
|
||||
@SerializedName("cardProperty") val cardProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("topLogoProperty") val topLogoProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("topTextProperty") val topTextProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("rolodexItemProperty") val rolodexItemProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("chipProperty") val chipProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("chipItemProperty") val chipItemProperty: InvestmentBaseProperty? = null,
|
||||
)
|
||||
|
||||
data class FtueWithTrackerWidgetExtraData(
|
||||
@SerializedName("extraProperties") val extraProperties: InvestmentBaseProperty? = null,
|
||||
@SerializedName("metaData") val metaData: GenericAnalytics? = null,
|
||||
)
|
||||
@@ -5,7 +5,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
package com.naviapp.home.dashboard.models.investmentTabWidgetData
|
||||
package com.navi.amc.fundbuy.models.widgets
|
||||
|
||||
import PaymentCard
|
||||
import com.google.gson.annotations.SerializedName
|
||||
@@ -32,10 +32,21 @@ data class RepeatOrderWidgetHeader(
|
||||
)
|
||||
|
||||
data class RepeatOrderWidgetContent(
|
||||
@SerializedName("repeatOrderCardList") val repeatOrderCardList: List<PaymentCard>? = null
|
||||
@SerializedName("repeatOrderCardList") val repeatOrderCardList: List<PaymentCard>? = null,
|
||||
@SerializedName("carouselData") val carouselData: CarouselData? = null,
|
||||
)
|
||||
|
||||
data class RepeatOrderWidgetExtraData(
|
||||
@SerializedName("property") val property: InvestmentBaseProperty? = null,
|
||||
@SerializedName("scrollType") val scrollType: String? = null,
|
||||
@SerializedName("metaData") val metaData: GenericAnalytics? = null,
|
||||
)
|
||||
|
||||
data class CarouselData(
|
||||
@SerializedName("carouselHeight") val carouselHeight: Int? = null,
|
||||
@SerializedName("activeCarouselWidth") val activeCarouselWidth: Int? = null,
|
||||
@SerializedName("inactiveCarouselWidth") val inactiveCarouselWidth: Int? = null,
|
||||
@SerializedName("activeCarouselColor") val activeCarouselColor: String? = null,
|
||||
@SerializedName("inactiveCarouselColor") val inactiveCarouselColor: String? = null,
|
||||
@SerializedName("carouselProperty") val carouselProperty: InvestmentBaseProperty? = null,
|
||||
)
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.fundbuy.models.widgets
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.navi.amc.common.model.GenericComposableWidget
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.common.model.InvestmentBaseProperty
|
||||
import com.navi.naviwidgets.models.response.ImageFieldData
|
||||
import com.navi.naviwidgets.models.response.TextFieldData
|
||||
|
||||
data class TitleContentRadioWidget(
|
||||
@SerializedName("widgetName") override val widgetName: String? = null,
|
||||
@SerializedName("widgetId") override val widgetId: String? = null,
|
||||
@SerializedName("widgetData") val widgetData: TitleContentRadioWidgetData? = null,
|
||||
) : GenericComposableWidget
|
||||
|
||||
data class TitleContentRadioWidgetData(
|
||||
@SerializedName("title") val title: TextFieldData? = null,
|
||||
@SerializedName("subTitle", alternate = ["subtitle"]) val subTitle: TextFieldData? = null,
|
||||
@SerializedName("tagTitle") val tagTitle: TextFieldData? = null,
|
||||
@SerializedName("showTitleRightIcon") val showTitleRightIcon: Boolean? = null,
|
||||
@SerializedName("contentTitle") val contentTitle: TextFieldData? = null,
|
||||
@SerializedName("contentLeftSubtitle") val contentLeftSubtitle: TextFieldData? = null,
|
||||
@SerializedName("contentRightSubtitle") val contentRightSubtitle: TextFieldData? = null,
|
||||
@SerializedName("contentMiddleImage") val contentMiddleImage: ImageFieldData? = null,
|
||||
@SerializedName("properties") val properties: TitleContentRadioWidgetProperties? = null,
|
||||
@SerializedName("actionData") val actionData: ActionData? = null,
|
||||
@SerializedName("selectionProperties") val selectionProperties: SelectionProperties? = null,
|
||||
)
|
||||
|
||||
data class TitleContentRadioWidgetProperties(
|
||||
@SerializedName("cardProperty") val cardProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("titleProperty") val titleProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("subTitleProperty") val subTitleProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("tagProperty") val tagProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("contentTitleProperty")
|
||||
val contentTitleProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("contentSubtitleProperty")
|
||||
val contentSubtitleProperty: InvestmentBaseProperty? = null,
|
||||
)
|
||||
|
||||
data class SelectionProperties(
|
||||
@SerializedName("type") val type: String? = null,
|
||||
@SerializedName("isin") val isin: String? = null,
|
||||
@SerializedName("isSelected") val isSelected: Boolean? = null,
|
||||
@SerializedName("selectedTitleRightIcon") val selectedTitleRightIcon: ImageFieldData? = null,
|
||||
@SerializedName("unselectedTitleRightIcon")
|
||||
val unselectedTitleRightIcon: ImageFieldData? = null,
|
||||
@SerializedName("titleRightIconProperties")
|
||||
val titleRightIconProperties: InvestmentBaseProperty? = null,
|
||||
)
|
||||
@@ -31,7 +31,8 @@ data class TitleSubtitleImageWidgetData(
|
||||
data class TitleSubtitleImageWidgetProperties(
|
||||
@SerializedName("cardProperty") val cardProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("titleProperty") val titleProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("subTitleProperty") val subTitleProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("subTitleProperty", alternate = ["subtitleProperty"])
|
||||
val subTitleProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("imageProperty") val imageProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("footerProperty") val footerProperty: InvestmentBaseProperty? = null,
|
||||
)
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2025 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
|
||||
package com.navi.amc.fundbuy.models.widgets
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.navi.amc.common.model.GenericComposableWidget
|
||||
import com.navi.base.model.ActionData
|
||||
import com.navi.common.model.InvestmentBaseProperty
|
||||
import com.navi.naviwidgets.models.LottieFieldData
|
||||
import com.navi.naviwidgets.models.response.TextFieldData
|
||||
|
||||
data class TitleSubtitleLottieWidget(
|
||||
@SerializedName("widgetName") override val widgetName: String? = null,
|
||||
@SerializedName("widgetId") override val widgetId: String? = null,
|
||||
@SerializedName("widgetData") val widgetData: TitleSubtitleLottieWidgetData? = null,
|
||||
) : GenericComposableWidget
|
||||
|
||||
data class TitleSubtitleLottieWidgetData(
|
||||
@SerializedName("title") val title: TextFieldData? = null,
|
||||
@SerializedName("subTitle", alternate = ["subtitle"]) val subTitle: TextFieldData? = null,
|
||||
@SerializedName("lottie", alternate = ["icon"]) val lottie: LottieFieldData? = null,
|
||||
@SerializedName("properties") val properties: TitleSubtitleLottieWidgetProperties? = null,
|
||||
@SerializedName("actionData") val actionData: ActionData? = null,
|
||||
)
|
||||
|
||||
data class TitleSubtitleLottieWidgetProperties(
|
||||
@SerializedName("cardProperty") val cardProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("titleProperty") val titleProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("subTitleProperty", alternate = ["subtitleProperty"])
|
||||
val subTitleProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("lottieProperty") val lottieProperty: InvestmentBaseProperty? = null,
|
||||
@SerializedName("footerProperty") val footerProperty: InvestmentBaseProperty? = null,
|
||||
)
|
||||
@@ -12,9 +12,11 @@ import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import com.navi.amc.common.activity.CheckerActivity
|
||||
import com.navi.amc.compose.entry.AmcComposeActivity
|
||||
import com.navi.amc.fundbuy.activities.FundBuyActivity
|
||||
import com.navi.amc.kyc.activity.KycActivity
|
||||
import com.navi.amc.kyc.activity.KycOnboardActivity
|
||||
import com.navi.amc.utils.AmcAnalytics.TRUE
|
||||
import com.navi.amc.utils.getCtaToNavigateToInvestmentTab
|
||||
import com.navi.amc.utils.updateSecondIdentifier
|
||||
import com.navi.base.deeplink.DeepLinkManager
|
||||
@@ -22,7 +24,6 @@ import com.navi.base.model.ActionData
|
||||
import com.navi.base.utils.orFalse
|
||||
import com.navi.base.utils.orZero
|
||||
import com.navi.common.utils.Constants
|
||||
import com.navi.common.utils.Constants.TRUE
|
||||
import com.navi.common.utils.toCtaData
|
||||
import timber.log.Timber
|
||||
|
||||
@@ -39,6 +40,7 @@ object NaviAmcDeeplinkNavigator {
|
||||
const val WEB_URL = "webUrl"
|
||||
const val FUND_LANDING = "fund_landing"
|
||||
const val V2_PARAMETER = "v2"
|
||||
const val FTUE = "ftue"
|
||||
|
||||
fun navigate(
|
||||
activity: Activity?,
|
||||
@@ -113,6 +115,9 @@ object NaviAmcDeeplinkNavigator {
|
||||
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
|
||||
}
|
||||
}
|
||||
FTUE -> {
|
||||
Intent(activity, AmcComposeActivity::class.java)
|
||||
}
|
||||
WEB_URL -> {
|
||||
var url: String? = null
|
||||
ctaData.parameters?.forEach { keyValue ->
|
||||
|
||||
@@ -9,6 +9,8 @@ package com.navi.amc.network.retrofit
|
||||
|
||||
import com.navi.amc.common.model.*
|
||||
import com.navi.amc.common.model.cart.CartRequest
|
||||
import com.navi.amc.compose.common.model.AmcGenericScreenResponse
|
||||
import com.navi.amc.compose.feature.ftue.model.FundSelectionData
|
||||
import com.navi.amc.digio.AadhaarVerificationData
|
||||
import com.navi.amc.digio.DigioKycPollingResponse
|
||||
import com.navi.amc.digio.DigioKycResponse
|
||||
@@ -599,4 +601,15 @@ interface RetrofitService {
|
||||
@Path("checkoutId") checkoutId: String?,
|
||||
@Path(value = "nextStepCta", encoded = true) nextStepCta: String?,
|
||||
): Response<GenericResponse<T>>
|
||||
|
||||
@GET("/investor/ftue/educate")
|
||||
suspend fun fetchFtueEducateScreenData(): Response<GenericResponse<AmcGenericScreenResponse>>
|
||||
|
||||
@GET("/investor/ftue/fund-details")
|
||||
suspend fun fetchFtueFundSelectScreenData(): Response<GenericResponse<AmcGenericScreenResponse>>
|
||||
|
||||
@POST("/investor/ftue/update")
|
||||
suspend fun postSelectedFund(
|
||||
@Body fundSelectionData: FundSelectionData
|
||||
): Response<GenericResponse<SuccessResponse>>
|
||||
}
|
||||
|
||||
@@ -240,4 +240,10 @@ object Constant {
|
||||
const val ERROR_RESPONSE = "ERROR_RESPONSE"
|
||||
const val NEW_FLOW_TYPE = "new_flow_type"
|
||||
const val SIP_TYPE = "sip_type"
|
||||
|
||||
const val UPCOMING_SIP_PAYMENT_CARD = "upcomingSipPaymentCard"
|
||||
const val REPEAT_ORDER = "repeatOrder"
|
||||
|
||||
const val START_SCREEN_NAME = "start_screen_name"
|
||||
const val TRANSITION_DURATION_IN_MILLIS = 400
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user