[PS] Feature invest before kyc (#5447)

* [PS] kyc after payment

* [PS] fix bottom sheet issue on order status page

* [PS] fix design changes

* [PS] added kyc after payments events

* [PS] fix design issues

* [PS] resolved comments

* [PS] resolved comments
This commit is contained in:
Prakhar Saxena
2023-02-22 14:28:40 +05:30
committed by GitHub Enterprise
parent f6e5331370
commit 7ff3f5e60f
27 changed files with 719 additions and 40 deletions

View File

@@ -138,6 +138,22 @@ class CheckerActivity : BasePaymentActivity() {
}
}
}
viewModel.kycRedirectCta.observe(this) {
val bundle = intent?.extras ?: Bundle()
if(it?.url.isNotNullAndNotEmpty()) {
it?.toNavigateAmcModule(activity = this, finish = true, bundle = bundle)
} else {
val response = viewModel.asyncResponse.value
response?.data?.data?.statusData?.let {
bundle.putParcelable(DATA, it)
bundle.putString(TYPE, type)
}
response?.data
?.data
?.nextCTA
?.toNavigateAmcModule(activity = this, finish = true, bundle = bundle)
}
}
}
private fun onSpanClick(actionData: ActionData?) {
@@ -207,6 +223,8 @@ class CheckerActivity : BasePaymentActivity() {
recurring = "1",
)
)
} else if(needRedirection(response.data?.data?.nextCTA)) {
viewModel.fetchKycRedirectCta()
} else if (response.data?.data?.nextCTA?.url.isNullOrEmpty().not()) {
val bundle = intent?.extras ?: Bundle()
response.data?.data?.statusData?.let {
@@ -364,6 +382,10 @@ class CheckerActivity : BasePaymentActivity() {
)
}
private fun needRedirection(actionData: ActionData?): Boolean {
return type == PENNY_DROP && actionData?.url.equals(REDIRECT_CTA)
}
companion object {
const val DEFAULT_RETRY_COUNT = 10
const val INITIATE_KYC = "INITIATE_KYC"
@@ -378,6 +400,7 @@ class CheckerActivity : BasePaymentActivity() {
const val AUTO_PAY_PAYMENT_CALLBACK = "autopay_payment_callback"
const val PENNY_DROP = "PENNY_DROP"
const val ERROR_DATA = "ERROR_DATA"
const val REDIRECT_CTA = "REDIRECT_CTA"
const val DATA = "DATA"
const val TYPE = "TYPE"
}

View File

@@ -8,7 +8,6 @@
package com.navi.amc.common.fragment
import android.os.Bundle
import android.view.View
import android.view.ViewStub
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
@@ -16,6 +15,7 @@ import com.google.gson.Gson
import com.navi.amc.R
import com.navi.amc.common.model.AmcCommonBottomSheetData
import com.navi.amc.databinding.AmcCommonBottomsheetBinding
import com.navi.amc.utils.AmcAnalytics
import com.navi.amc.utils.Constant.DATA
import com.navi.amc.utils.SubPageStatusType
import com.navi.base.model.ActionData
@@ -59,6 +59,7 @@ class AmcCommonBottomSheet : BaseBottomSheet() {
safelyDismissDialog()
}
}
AmcAnalytics.sendEvent(eventsData = response.metaData?.viewedData, screenName = screenName)
}
}

View File

@@ -8,33 +8,39 @@ import android.view.ViewGroup
import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.viewModels
import com.google.gson.Gson
import com.navi.amc.R
import com.navi.amc.common.activity.CheckerActivity
import com.navi.amc.common.listener.BackListener
import com.navi.amc.common.listener.FooterInteractionListener
import com.navi.amc.common.model.AmcCommonBottomSheetData
import com.navi.amc.common.viewmodel.OrderStatusViewModel
import com.navi.amc.databinding.OrderStatusLayoutBinding
import com.navi.amc.utils.*
import com.navi.amc.fundbuy.models.PaymentOrder
import com.navi.amc.utils.AmcAnalytics.ORDER_STATUS_SCREEN
import com.navi.amc.utils.Constant
import com.navi.amc.utils.Constant.DATA
import com.navi.amc.utils.Constant.INIT_REFUND
import com.navi.amc.utils.Constant.ORDER_ID
import com.navi.amc.utils.TempStorageHelper
import com.navi.amc.utils.orFalse
import com.navi.amc.utils.orValue
import com.navi.base.model.ActionData
import com.navi.common.firebasedb.FirebaseStatusType
import com.navi.common.listeners.FragmentInterchangeListener
import com.navi.common.listeners.HeaderInteractionListener
import com.navi.common.model.UploadDataAsyncResponse
import com.navi.common.utils.ApiPollScheduler
import com.navi.common.utils.isNull
import com.navi.common.utils.orFalse
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class OrderStatusFragment : AmcBaseFragment(), FooterInteractionListener {
class OrderStatusFragment : AmcBaseFragment(), FooterInteractionListener, BackListener {
private lateinit var binding: OrderStatusLayoutBinding
private val viewModel by viewModels<OrderStatusViewModel>()
private var apiPollScheduler: ApiPollScheduler? = null
override val screenName: String
get() = ORDER_STATUS_SCREEN
private var isBackAllowed: Boolean = true
private var learnMoreBottomSheetData: AmcCommonBottomSheetData? = null
override fun onCreateView(
inflater: LayoutInflater,
@@ -100,7 +106,17 @@ class OrderStatusFragment : AmcBaseFragment(), FooterInteractionListener {
onFooterBackPress(it.backCta)
}
} != null
it.content?.learnMoreNote?.let { note ->
binding.learnMoreNoteData = note
learnMoreNote.action.setOnClickListener { view ->
handleOnClick(note.actionText?.action)
}
learnMoreNote.icon.setOnClickListener { view ->
handleOnClick(note.actionText?.action)
}
}
}
isBackAllowed = it.content?.exitBottomSheetData.isNull()
}
}
viewModel.createOrderResponse.observe(viewLifecycleOwner) {
@@ -120,6 +136,14 @@ class OrderStatusFragment : AmcBaseFragment(), FooterInteractionListener {
)
}
}
viewModel.refundStatus.observe(viewLifecycleOwner) {
if(it?.success.orFalse()) {
fetchData()
} else {
hideLoader()
}
}
}
private fun onPollingResponse(response: UploadDataAsyncResponse?) {
@@ -209,6 +233,67 @@ class OrderStatusFragment : AmcBaseFragment(), FooterInteractionListener {
super.onDestroyView()
}
private fun handleOnClick(action: ActionData?) {
sendEvent(
action?.metaData?.clickedData,
)
val url = action?.url
if(url == Constant.SHOW_BOTTOMSHEET) {
val data = action.parameters?.getOrNull(0)?.value
val key = action.parameters?.getOrNull(0)?.key
val bundle = Bundle().apply {
putString(Constant.DATA, data)
}
learnMoreBottomSheetData = Gson().fromJson(data, AmcCommonBottomSheetData::class.java)
key?.let {
getBottomSheet(it, bundle, genericListener = ::actionListener)?.let {
safelyShowBottomSheet(it, key)
}
}
} else if (url == INIT_REFUND) {
initiateRefund(action)
} else {
fragmentInterchangeListener?.navigateToNextScreen(actionData = action)
}
}
override fun onBackPressed(actionData: ActionData?): Boolean {
return showExitBottomSheetIfAvailable()
}
override fun onTopPressed(actionData: ActionData?): Boolean {
return showExitBottomSheetIfAvailable()
}
private fun showExitBottomSheetIfAvailable(): Boolean {
if(!isBackAllowed) {
viewModel.orderStatusScreenData.value?.content?.exitBottomSheetData?.let {
isBackAllowed = true
val bundle = Bundle()
bundle.putString(DATA, Gson().toJson(it))
getBottomSheet(SubPageStatusType.AMC_COMMON_BOTTOMSHEET, bundle, genericListener = ::actionListener)?.let {
safelyShowBottomSheet(it, SubPageStatusType.AMC_COMMON_BOTTOMSHEET)
}
return true
}
}
isBackAllowed = true
return false
}
private fun initiateRefund(action: ActionData?) {
val key = action?.parameters?.firstOrNull()?.key
val value = action?.parameters?.firstOrNull()?.value
if(key == ORDER_ID) {
showLoader()
viewModel.refundOrder(value.orEmpty())
}
}
private fun actionListener(action : ActionData) {
handleOnClick(action)
}
companion object {
fun newInstance(bundle: Bundle): OrderStatusFragment {
return OrderStatusFragment().apply {

View File

@@ -7,10 +7,14 @@
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.base.model.GenericAnalytics
import com.navi.design.textview.model.TextWithStyle
import kotlinx.parcelize.Parcelize
@Parcelize
data class AmcCommonBottomSheetData(
@SerializedName("title")
val title: TextWithStyle? = null,
@@ -19,5 +23,7 @@ data class AmcCommonBottomSheetData(
@SerializedName("iconCode")
val iconCode: String? = null,
@SerializedName("action", alternate = ["actionData"])
val actionData: ActionData? = null
)
val actionData: ActionData? = null,
@SerializedName("metaData")
val metaData: GenericAnalytics? = null,
): Parcelable

View File

@@ -6,6 +6,8 @@ import com.navi.amc.fundbuy.models.SubItemData
import com.navi.base.model.ActionData
import com.navi.common.model.Header
import com.navi.common.model.LabelData
import com.navi.design.textview.model.NaviSpan
import com.navi.design.textview.model.TextWithStyle
import com.navi.naviwidgets.models.response.NoteWidget
data class OrderStatusScreenData(
@@ -26,6 +28,10 @@ data class OrderStatusContent(
val orderStatus : StatusTimeLineData ?= null ,
@SerializedName("statusNote")
val statusNote : InformationCardData ?= null,
@SerializedName("learnMoreNote")
val learnMoreNote : InformationCardData ?= null,
@SerializedName("exitBottomSheetData")
val exitBottomSheetData : AmcCommonBottomSheetData ?= null,
@SerializedName("note")
val note : NoteWidget ?= null,
@SerializedName("actionData")
@@ -44,3 +50,11 @@ data class OrderDetailsData(
@SerializedName("label")
val label : LabelData?= null
)
data class TimerInfo(
@SerializedName("bgColor") val bgColor: String? = null,
@SerializedName("title") val title: TextWithStyle? = null,
@SerializedName("timeLeft") val timeLeft: Long? = null,
@SerializedName("timerSpan") val timerSpan: List<NaviSpan>? = null,
@SerializedName("enableShimmer") val enableShimmer: Boolean? = null,
)

View File

@@ -9,7 +9,9 @@ class StatusTimeLineData(
@SerializedName("rightTitle")
val rightTitle: TextWithStyle? = null,
@SerializedName("items")
val items: List<StatusData>? = null
val items: List<StatusData>? = null,
@SerializedName("footerTimerInfo")
val footerTimerInfo : TimerInfo? = null,
)
data class StatusData(

View File

@@ -53,6 +53,9 @@ class CheckerRepository @Inject constructor(private val retrofitService: Retrofi
suspend fun checkPennyDropPollStatus(requestId: String) =
apiResponseCallback(retrofitService.fetchPennyDropAsyncRequestData(requestId))
suspend fun fetchKycRedirectCta(params: Map<String, String>) =
apiResponseCallback(retrofitService.fetchKycRedirectCta(params))
suspend fun checkPLDisbursedJourneyStatus(requestId: String) =
apiResponseCallback(retrofitService.fetchPLJourneyStatusAsyncRequestData(requestId))

View File

@@ -21,4 +21,7 @@ class OrderStatusRepository @Inject constructor(private val retrofitService: Ret
suspend fun postSameOrderPayment(details: PaymentOrder) =
apiResponseCallback(retrofitService.postSameOrderPayment(details))
suspend fun refundOrder(orderId: String) =
apiResponseCallback(retrofitService.refundOrder(orderId))
}

View File

@@ -1,9 +1,9 @@
package com.navi.amc.common.view
import android.content.Context
import android.os.CountDownTimer
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import androidx.constraintlayout.widget.ConstraintLayout
@@ -11,10 +11,19 @@ import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import com.navi.amc.R
import com.navi.amc.common.model.StatusTimeLineData
import com.navi.amc.common.model.TimerInfo
import com.navi.amc.databinding.StatusRowBinding
import com.navi.amc.databinding.StatusTimelineLayoutBinding
import com.navi.amc.utils.Constant.THOUSAND
import com.navi.amc.utils.DateUtils
import com.navi.common.utils.SPACE
import com.navi.design.textview.model.SpanInterface
import com.navi.design.utils.moveViewWithDistance
import com.navi.design.utils.parseColorSafe
import com.navi.design.utils.setSpannableString
import com.navi.design.utils.spannedText
import com.navi.naviwidgets.extensions.showWhenDataIsAvailable
import com.navi.naviwidgets.utils.REWARDS_TOOLTIP_ANIMATION_DELAY
class StatusTimeLineView(context: Context, attributeSet: AttributeSet? = null) :
ConstraintLayout(context, attributeSet) {
@@ -47,7 +56,49 @@ class StatusTimeLineView(context: Context, attributeSet: AttributeSet? = null) :
childBinding.root.layoutParams = LayoutParams(MATCH_PARENT, WRAP_CONTENT)
binding.container.addView(childBinding.root)
}
setFooter(data.footerTimerInfo)
}
}
}
private fun setFooter(data: TimerInfo?) {
data?.let {
binding.footerTv.isVisible = true
val difference = it.timeLeft ?: 0
val timer = object : CountDownTimer(difference, THOUSAND) {
override fun onTick(millisUntilFinished: Long) {
val timerText = DateUtils.instance.formatSecondsInDaysTimer(millisUntilFinished / 1000)
val spanList: MutableList<SpanInterface> = mutableListOf()
it.title?.style?.forEach {
spanList.add(it)
}
it.timerSpan?.forEach {
spanList.add(it)
}
val heading = it.title?.text.plus(SPACE).plus(timerText)
binding.footerTv.text = heading.spannedText(context, spanList)
}
override fun onFinish() {
}
}
timer.start()
binding.footerTv.setBackgroundColor(it.bgColor.parseColorSafe())
if (it.enableShimmer == true) {
binding.pgvShimmer.isVisible = true
binding.root.post {
moveViewWithDistance(
binding.pgvShimmer,
binding.footerTv.width,
REWARDS_TOOLTIP_ANIMATION_DELAY
)
}
} else {
binding.pgvShimmer.isVisible = false
}
} ?: kotlin.run {
binding.pgvShimmer.isVisible = false
binding.footerTv.isVisible = false
}
}
}

View File

@@ -14,6 +14,8 @@ import com.navi.amc.common.model.CheckerResponse
import com.navi.amc.common.model.NextCtaResponse
import com.navi.amc.common.model.PostPaymentData
import com.navi.amc.common.repo.CheckerRepository
import com.navi.amc.utils.TempStorageHelper
import com.navi.base.model.ActionData
import com.navi.common.network.models.RepoResult
import com.navi.common.network.models.SuccessResponse
import com.navi.common.viewmodel.BaseVM
@@ -37,6 +39,10 @@ class CheckerVM @Inject constructor(private val repository: CheckerRepository) :
val postPaymentResponse: LiveData<RepoResult<SuccessResponse>>
get() = _postPaymentResponse
private val _kycRedirectCta = MutableLiveData<ActionData?>()
val kycRedirectCta: LiveData<ActionData?>
get() = _kycRedirectCta
fun setInitData(data: CheckerResponse?) {
_dataResponse.value = data
}
@@ -104,4 +110,14 @@ class CheckerVM @Inject constructor(private val repository: CheckerRepository) :
}
}
}
fun fetchKycRedirectCta() {
coroutineScope.launch {
val response = repository.fetchKycRedirectCta(TempStorageHelper.kycSourceInfo.orEmpty())
if (response.error == null && response.errors.isNullOrEmpty()) {
_kycRedirectCta.value = response.data
TempStorageHelper.kycSourceInfo = null
}
}
}
}

View File

@@ -11,6 +11,8 @@ import com.navi.amc.common.repo.OrderStatusRepository
import com.navi.amc.fundbuy.models.PaymentOrder
import com.navi.amc.utils.AmcAnalytics
import com.navi.common.model.UploadDataAsyncResponse
import com.navi.common.network.models.GenericResponse
import com.navi.common.network.models.SuccessResponse
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.launch
import javax.inject.Inject
@@ -34,6 +36,10 @@ class OrderStatusViewModel @Inject constructor(private val repository: OrderStat
val paymentInitiateData: LiveData<CheckerResponse?>
get() = _paymentInitiateData
private val _refundStatus = MutableLiveData<SuccessResponse?>()
val refundStatus: LiveData<SuccessResponse?>
get() = _refundStatus
private var isInitEventTriggered = false
fun fetchScreenData(orderId: String, dataSource: String) {
@@ -91,6 +97,17 @@ class OrderStatusViewModel @Inject constructor(private val repository: OrderStat
}
}
fun refundOrder(orderId: String) {
viewModelScope.launch {
val response = repository.refundOrder(orderId)
if(response.error == null && response.errors.isNullOrEmpty()){
_refundStatus.value = response.data
} else {
setErrorData(response.errors, response.error)
}
}
}
private fun updateData(data: AdditionalDataAsyncResponse<NextCtaResponse>?): CheckerResponse? {
data?.data?.checkerData?.content?.asyncData = data
return data?.data?.checkerData

View File

@@ -44,7 +44,14 @@ class FundBuyActivity : AmcBaseActivity(), FragmentInterchangeListener, HeaderIn
override fun onBackPressed() {
val cta = binding.headerView.getHeaderData()?.backCta
navigateToNextScreen(cta, Bundle())
val currentFragment = supportFragmentManager.findFragmentById(R.id.container)
if (currentFragment is BackListener) {
if ((currentFragment as BackListener).onTopPressed(cta).not()) {
navigateToNextScreen(cta)
}
} else {
navigateToNextScreen(cta)
}
}
override fun onCreate(savedInstanceState: Bundle?) {

View File

@@ -369,6 +369,11 @@ interface RetrofitService {
@Body data: PaymentOrder
): Response<GenericResponse<AdditionalDataAsyncResponse<NextCtaResponse>>>
@POST("/orders/refund-order/{orderId}")
suspend fun refundOrder(
@Path("orderId") orderId: String
): Response<GenericResponse<SuccessResponse>>
@GET("/fund/fund-details-nfo")
suspend fun fetchNfoDetails(@Query("isin") isin: String): Response<GenericResponse<NfoDetailsScreenData>>

View File

@@ -8,7 +8,9 @@
package com.navi.amc.portfolio.fragments
import android.content.Context
import android.content.res.ColorStateList
import android.os.Bundle
import android.os.CountDownTimer
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -17,8 +19,11 @@ import androidx.core.view.isVisible
import androidx.databinding.DataBindingUtil
import androidx.fragment.app.viewModels
import androidx.recyclerview.widget.DividerItemDecoration
import com.google.gson.Gson
import com.navi.amc.R
import com.navi.amc.common.fragment.AmcBaseFragment
import com.navi.amc.common.model.AmcCommonBottomSheetData
import com.navi.amc.common.model.TimerInfo
import com.navi.amc.databinding.OrdersLayoutBinding
import com.navi.amc.fundbuy.fragments.SelectOptionBottomSheet
import com.navi.amc.fundbuy.models.ChipData
@@ -26,22 +31,25 @@ import com.navi.amc.fundbuy.models.Identifier
import com.navi.amc.fundbuy.models.SelectOptionsBottomSheetData
import com.navi.amc.portfolio.adapters.OrdersAdapter
import com.navi.amc.portfolio.models.IndividualOrderDetail
import com.navi.amc.portfolio.models.OrderSummaryCard
import com.navi.amc.portfolio.viewmodels.OrdersViewModel
import com.navi.amc.utils.AmcAnalytics
import com.navi.amc.utils.*
import com.navi.amc.utils.AmcAnalytics.AMC_BTN_INVESTMENTS_INVEST_NOW
import com.navi.amc.utils.ColorUtils
import com.navi.amc.utils.Constant.DATA_SOURCE
import com.navi.amc.utils.Constant.ORDER_ID
import com.navi.amc.utils.Constant.RED
import com.navi.amc.utils.Constant.THOUSAND
import com.navi.base.model.ActionData
import com.navi.common.listeners.FragmentInterchangeListener
import com.navi.common.listeners.HeaderInteractionListener
import com.navi.common.utils.orFalse
import com.navi.design.font.FontWeightEnum
import com.navi.design.textview.model.NaviSpan
import com.navi.design.textview.model.SpanInterface
import com.navi.design.textview.model.TextWithStyle
import com.navi.design.utils.GradientOrientation
import com.navi.design.utils.parseColorSafe
import com.navi.design.utils.setSpannableString
import com.navi.design.utils.*
import com.navi.naviwidgets.utils.NaviWidgetIconUtils
import com.navi.naviwidgets.utils.REWARDS_TOOLTIP_ANIMATION_DELAY
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
@@ -51,6 +59,7 @@ class OrdersFragment : AmcBaseFragment() {
private val viewModel by viewModels<OrdersViewModel>()
override val screenName: String
get() = AmcAnalytics.ORDER_INIT_MY_INVESTMENT
private var learnMoreBottomSheetData: AmcCommonBottomSheetData? = null
override fun onCreateView(
inflater: LayoutInflater,
@@ -79,12 +88,13 @@ class OrdersFragment : AmcBaseFragment() {
viewModel.orderDetails.observe(viewLifecycleOwner) {
hideLoader()
it?.let {
binding.orderValue = it.orderSummary
val orderSummary = it.orderSummary
binding.orderValue = orderSummary
binding.orderTitle.setSpannableString(it.orderTitle)
val gradientColor =
intArrayOf(
it.orderSummary?.gradient?.startGradientColor.parseColorSafe(),
it.orderSummary?.gradient?.endGradientColor.parseColorSafe()
orderSummary?.gradient?.startGradientColor.parseColorSafe(),
orderSummary?.gradient?.endGradientColor.parseColorSafe()
)
binding.orderView.root.background =
ColorUtils.getGradientBackgroundGreyBorderDrawable(
@@ -92,6 +102,7 @@ class OrdersFragment : AmcBaseFragment() {
gradientColor,
GradientOrientation.TOP_BOTTOM
)
binding.orderView.root.isVisible = (orderSummary?.totalOrders != null)
binding.title.setSpannableString(it?.orderSummary?.title)
it.orders?.let { initAdapter(it) }
?: run {
@@ -106,8 +117,102 @@ class OrdersFragment : AmcBaseFragment() {
}
}
}
setOrderSummaryCardProperties(orderSummary?.orderSummaryCard)
binding.learnMoreNote.action.setOnClickListener { view ->
handleOnClick(orderSummary?.learnMoreNote?.actionText?.action)
}
binding.learnMoreNote.icon.setOnClickListener { view ->
handleOnClick(orderSummary?.learnMoreNote?.actionText?.action)
}
}
}
viewModel.refundStatus.observe(viewLifecycleOwner) {
if(it?.success.orFalse()) {
fetchData()
} else {
hideLoader()
}
}
}
private fun setOrderSummaryCardProperties(orderSummaryCard: OrderSummaryCard?) {
orderSummaryCard?.let {
binding.orderSummaryCard.root.isVisible = true
binding.orderSummaryCard.cardTitle.setSpannableString(it.title)
binding.orderSummaryCard.cardSubtitle.setSpannableString(it.subTitle)
it.imageMargin?.let { margin ->
val lp = binding.orderSummaryCard.cardIcon.layoutParams as? ViewGroup.MarginLayoutParams
lp?.bottomMargin = dpToPxInInt(margin.bottomDp.toInt())
lp?.marginEnd = dpToPxInInt(margin.endDp.toInt())
binding.orderSummaryCard.cardIcon.layoutParams = lp
}
it.imageDetails?.let { imageDetails ->
imageDetails.height?.let { height ->
binding.orderSummaryCard.cardIcon.layoutParams.height = dpToPxInInt(height)
}
imageDetails.width?.let { width ->
binding.orderSummaryCard.cardIcon.layoutParams.width = dpToPxInInt(width)
}
binding.orderSummaryCard.cardIcon.requestLayout()
it.iconCode?.let { iconCode ->
NaviWidgetIconUtils.updateIcon(iconCode, binding.orderSummaryCard.cardIcon)
}
} ?: kotlin.run {
it.iconCode?.let { iconCode ->
NaviWidgetIconUtils.updateIcon(iconCode, binding.orderSummaryCard.cardIcon)
}
}
binding.orderSummaryCard.actionBtn.setProperties(it.actionData, null, it.actionTitle)
binding.orderSummaryCard.root.setOnClickListener { view ->
sendEvent(it.actionData?.metaData?.clickedData)
fragmentInterchangeListener?.navigateToNextScreen(actionData = it.actionData)
}
binding.orderSummaryCard.summaryCard.setBackgroundColor(it.bgColor.parseColorSafe())
setCardTimerProperties(it.timerInfo)
} ?: kotlin.run {
binding.orderSummaryCard.root.isVisible = false
}
}
private fun setCardTimerProperties(timerInfo: TimerInfo?) {
timerInfo?.let {
val difference = it.timeLeft ?: 0
val timer = object : CountDownTimer(difference, THOUSAND) {
override fun onTick(millisUntilFinished: Long) {
val timerText = DateUtils.instance.formatSecondsInDaysTimer(millisUntilFinished / 1000)
val spanList: MutableList<SpanInterface> = mutableListOf()
it.title?.style?.forEach {
spanList.add(it)
}
it.timerSpan?.forEach {
spanList.add(it)
}
val heading = it.title?.text.plus(" ").plus(timerText)
binding.orderSummaryCard.footerTv.text = heading.spannedText(requireContext(), spanList)
}
override fun onFinish() {
}
}
timer.start()
binding.orderSummaryCard.footerContainer.setBackgroundColor(it.bgColor.parseColorSafe())
if (it.enableShimmer == true) {
binding.orderSummaryCard.pgvShimmer.isVisible = true
binding.root.post {
moveViewWithDistance(
binding.orderSummaryCard.pgvShimmer,
binding.orderSummaryCard.footerContainer.width,
REWARDS_TOOLTIP_ANIMATION_DELAY
)
}
} else {
binding.orderSummaryCard.pgvShimmer.isVisible = false
}
} ?: kotlin.run {
binding.orderSummaryCard.pgvShimmer.isVisible = false
binding.orderSummaryCard.footerContainer.isVisible = false
}
}
private fun openBottomSheet(data: SelectOptionsBottomSheetData?) {
@@ -126,6 +231,30 @@ class OrdersFragment : AmcBaseFragment() {
)
}
private fun handleOnClick(action: ActionData?) {
sendEvent(
action?.metaData?.clickedData,
)
val url = action?.url
if(url == Constant.SHOW_BOTTOMSHEET) {
val data = action.parameters?.getOrNull(0)?.value
val key = action.parameters?.getOrNull(0)?.key
val bundle = Bundle().apply {
putString(Constant.DATA, data)
}
learnMoreBottomSheetData = Gson().fromJson(data, AmcCommonBottomSheetData::class.java)
key?.let {
getBottomSheet(it, bundle, genericListener = ::actionListener)?.let {
safelyShowBottomSheet(it, key)
}
}
} else if (url == Constant.INIT_REFUND) {
initiateRefund(action)
} else {
fragmentInterchangeListener?.navigateToNextScreen(actionData = action)
}
}
private fun processData(list: List<Identifier>) {
setFilterIcon(list)
viewModel.bottomSheetData?.data?.forEach { data ->
@@ -227,6 +356,19 @@ class OrdersFragment : AmcBaseFragment() {
super.onDestroyView()
}
private fun initiateRefund(action: ActionData?) {
val key = action?.parameters?.firstOrNull()?.key
val value = action?.parameters?.firstOrNull()?.value
if(key == ORDER_ID) {
showLoader()
viewModel.refundOrder(value.orEmpty())
}
}
private fun actionListener(action : ActionData) {
handleOnClick(action)
}
companion object {
fun newInstance(bundle: Bundle? = null): OrdersFragment {
return OrdersFragment().apply { arguments = bundle }

View File

@@ -2,11 +2,17 @@ package com.navi.amc.portfolio.models
import com.google.gson.annotations.SerializedName
import com.navi.amc.common.model.EmptyProductData
import com.navi.amc.common.model.InformationCardData
import com.navi.amc.common.model.TimerInfo
import com.navi.amc.fundbuy.models.ChipData
import com.navi.amc.fundbuy.models.IconData
import com.navi.amc.fundbuy.models.SubItemData
import com.navi.base.model.ActionData
import com.navi.base.model.ImageDetails
import com.navi.design.textview.model.TextWithStyle
import com.navi.naviwidgets.models.response.Gradient
import com.navi.naviwidgets.models.response.amc.LabelData
import com.navi.naviwidgets.widgets.textdisplay.Margin
data class OrdersScreenData(
@SerializedName("orderSummary")
@@ -33,7 +39,11 @@ data class OrderSummary(
@SerializedName("sellOrders")
val sellOrders: SubItemData? = null,
@SerializedName("gradient")
val gradient: Gradient? = null
val gradient: Gradient? = null,
@SerializedName("orderSummaryCard")
val orderSummaryCard: OrderSummaryCard? = null,
@SerializedName("learnMoreNote")
val learnMoreNote : InformationCardData?= null
)
data class HeaderWithStyle(
@@ -41,4 +51,16 @@ data class HeaderWithStyle(
val item: TextWithStyle? = null,
@SerializedName("bgColor")
val bgColor: String? = null
)
data class OrderSummaryCard(
@SerializedName("iconCode") val iconCode: String? = null,
@SerializedName("bgColor") val bgColor: String? = null,
@SerializedName("imageDetails") val imageDetails: ImageDetails?,
@SerializedName("imageMargin") val imageMargin: Margin?,
@SerializedName("title") val title: TextWithStyle? = null,
@SerializedName("subTitle") val subTitle: TextWithStyle? = null,
@SerializedName("actionTitle") val actionTitle: TextWithStyle? = null,
@SerializedName("actionData") val actionData: ActionData? = null,
@SerializedName("footerTimerInfo") val timerInfo : TimerInfo? = null
)

View File

@@ -15,8 +15,7 @@ class OrdersRepository @Inject constructor(private val retrofitService: Retrofit
ResponseCallback() {
suspend fun fetchOrdersData() = apiResponseCallback(retrofitService.fetchOrders())
/*suspend fun fetchOrdersData(): RepoResult<OrdersScreenData> {
val type = object : TypeToken<OrdersScreenData>() {}.type
return mockApiResponse(type, "orders_list")
}*/
suspend fun refundOrder(orderId: String) =
apiResponseCallback(retrofitService.refundOrder(orderId))
}

View File

@@ -10,6 +10,7 @@ import com.navi.amc.portfolio.models.OrdersScreenData
import com.navi.amc.portfolio.repositories.OrdersRepository
import com.navi.amc.utils.Constant.DATE_FORMAT_YYYY_MM_DD
import com.navi.common.extensions.isNotNull
import com.navi.common.network.models.SuccessResponse
import com.navi.common.utils.log
import com.navi.common.viewmodel.BaseVM
import dagger.hilt.android.lifecycle.HiltViewModel
@@ -31,6 +32,10 @@ class OrdersViewModel @Inject constructor(private val repository: OrdersReposito
var filterList = mutableListOf<Identifier>()
private val _refundStatus = MutableLiveData<SuccessResponse?>()
val refundStatus: LiveData<SuccessResponse?>
get() = _refundStatus
fun fetchScreenData() {
viewModelScope.launch {
val response = repository.fetchOrdersData()
@@ -65,6 +70,17 @@ class OrdersViewModel @Inject constructor(private val repository: OrdersReposito
}?.sortedByDescending { sdf.parse(it.metaData?.timeStamp.orEmpty()) }
}
fun refundOrder(orderId: String) {
viewModelScope.launch {
val response = repository.refundOrder(orderId)
if(response.error == null && response.errors.isNullOrEmpty()){
_refundStatus.value = response.data
} else {
setErrorData(response.errors, response.error)
}
}
}
private fun compareTimeStamp(time1: String, time2: String): Boolean {
try {
val timeStamp1 = sdf.parse(time1)

View File

@@ -90,8 +90,10 @@ object Constant {
const val CONFIRMED = "CONFIRMED"
const val BUSINESS_VERTICAL_AMC = "AMC"
const val UPI = "UPI"
const val INIT_REFUND = "INIT_REFUND"
const val BACK_PRESS = "backPress"
const val DISMISS = "dismiss"
const val PD = "PD"
const val IS_INVESTMENT_ON_BOTTOM_NAV = "isInvestmentOnBottomNav"
const val THOUSAND = 1000L
}

View File

@@ -62,6 +62,20 @@ class DateUtils {
return dateFormat.format(convertedDate)
}
fun formatSecondsInDaysTimer(timeInSeconds: Long): String {
val secondsLeft = timeInSeconds % 3600 % 60
val minutes = timeInSeconds % 3600 / 60
val hours = (timeInSeconds / 3600) % 24
val days = timeInSeconds / 24 / 3600
val DD = "${if(days < 10) "0" else ""}$days"
val HH = "${if(hours < 10) "0" else ""}$hours"
val MM = "${if(minutes < 10) "0" else ""}$minutes"
val SS = "${if(secondsLeft < 10) "0" else ""}$secondsLeft"
return "$DD : $HH : $MM : $SS days"
}
private object Holder {
val INSTANCE = DateUtils()
}

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="note"
type="com.navi.amc.common.model.InformationCardData" />
</data>
<androidx.appcompat.widget.LinearLayoutCompat
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="center">
<com.navi.design.textview.NaviTextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
setTextWithStyle="@{note.title}"
tools:text="Unable to set up account"/>
<com.navi.design.textview.NaviTextView
android:id="@+id/action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_8"
setTextWithStyle="@{note.actionText.title}"
tools:text="Learn More"/>
<ImageView
android:id="@+id/icon"
android:layout_width="@dimen/dp_16"
android:layout_height="@dimen/dp_16"
tools:src="@drawable/ic_right_arrow"
setImageFromIconCode="@{note.rightIcon}"
android:layout_gravity="center_vertical"/>
</androidx.appcompat.widget.LinearLayoutCompat>
</layout>

View File

@@ -8,9 +8,15 @@
name="response"
type="com.navi.amc.common.model.OrderDetailsData" />
<variable
name="learnMoreNoteData"
type="com.navi.amc.common.model.InformationCardData" />
<variable
name="footerResponse"
type="com.navi.amc.common.model.Footer" />
<import type="android.view.View" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
@@ -56,23 +62,35 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_40"
android:paddingBottom="@dimen/dp_8"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/order_details" />
<include
android:id="@+id/learn_more_note"
layout="@layout/learn_more_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dp_16"
android:visibility="@{learnMoreNoteData != null ? View.VISIBLE : View.GONE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:note="@{learnMoreNoteData}"
app:layout_constraintTop_toBottomOf="@id/timeline"/>
<com.navi.amc.common.view.InformationView
android:id="@+id/status_note"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginEnd="@dimen/dp_16"
android:paddingTop="@dimen/dp_32"
android:paddingBottom="@dimen/dp_24"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/timeline" />
app:layout_constraintTop_toBottomOf="@id/learn_more_note" />
<com.navi.naviwidgets.views.NoteViewWidgetLayout
android:id="@+id/note"
@@ -80,6 +98,7 @@
android:layout_height="match_parent"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginEnd="@dimen/dp_16"
android:paddingTop="@dimen/dp_32"
android:paddingBottom="@dimen/dp_24"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"

View File

@@ -0,0 +1,109 @@
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.cardview.widget.CardView
android:id="@+id/widget_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginEnd="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_16"
android:layout_marginBottom="@dimen/dp_16"
android:outlineSpotShadowColor="@color/outlineSpotShadowColor"
app:cardCornerRadius="@dimen/dp_8"
app:cardElevation="@dimen/dp_10"
app:cardUseCompatPadding="false">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/summary_card"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.navi.design.textview.NaviTextView
android:id="@+id/card_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_24"
android:gravity="center_vertical"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="Fill your basic details" />
<com.navi.design.textview.NaviTextView
android:id="@+id/card_subtitle"
android:layout_width="@dimen/dp_0"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_2"
android:layout_marginEnd="@dimen/dp_16"
android:gravity="center_vertical"
app:layout_constraintStart_toStartOf="@id/card_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/card_title"
tools:text="Start investing with" />
<com.navi.naviwidgets.views.ButtonViewLayout
android:id="@+id/action_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_18"
android:layout_marginBottom="@dimen/dp_16"
app:layout_constraintBottom_toTopOf="@id/footer_container"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/card_subtitle"
tools:text="Start investing with"/>
<ImageView
android:id="@+id/cardIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/dp_16"
android:layout_marginEnd="@dimen/dp_16"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/footer_container"
tools:src="@drawable/person_tick" />
<LinearLayout
android:id="@+id/footer_container"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_32"
android:orientation="horizontal"
android:gravity="center"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent">
<com.navi.design.textview.NaviTextView
android:id="@+id/footer_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginStart="@dimen/dp_8"
android:baselineAligned="false"
android:gravity="center"
tools:text="Hurry up! your offer expires in 06 : 00 : 23 :43" />
</LinearLayout>
<com.navi.design.customview.ParallelogramView
android:id="@+id/pgvShimmer"
android:layout_width="@dimen/dp_32"
android:layout_height="@dimen/dp_0"
android:layout_gravity="start"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@id/footer_container"
app:tilt_direction="forward" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</FrameLayout>
</layout>

View File

@@ -32,6 +32,27 @@
app:layout_constraintTop_toTopOf="parent"
tools:text="Order Overview" />
<include
android:id="@+id/order_summary_card"
layout="@layout/order_summary_card_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/title"/>
<include
android:id="@+id/learn_more_note"
layout="@layout/learn_more_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="@{orderValue.learnMoreNote != null ? View.VISIBLE : View.GONE}"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:note="@{orderValue.learnMoreNote}"
app:layout_constraintTop_toBottomOf="@id/order_summary_card"/>
<include
android:id="@+id/order_view"
layout="@layout/order_view"
@@ -40,10 +61,10 @@
android:layout_marginStart="@dimen/dp_16"
android:layout_marginTop="@dimen/dp_16"
android:layout_marginEnd="@dimen/dp_16"
android:visibility="@{orderValue!=null ?View.VISIBLE : View.GONE }"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/title"
app:layout_constraintTop_toBottomOf="@id/learn_more_note"
app:orderValue="@{orderValue}" />
<com.navi.amc.common.view.EmptyProductView

View File

@@ -38,16 +38,45 @@
app:cardCornerRadius="@dimen/dp_8"
app:cardElevation="@dimen/dp_8"
app:cardUseCompatPadding="true"
app:contentPadding="@dimen/dp_24"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/left_title">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/container"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" />
android:layout_height="wrap_content">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dp_24"
android:layout_marginVertical="@dimen/dp_24"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@id/footer_tv"
android:orientation="vertical" />
<com.navi.design.textview.NaviTextView
android:id="@+id/footer_tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:paddingVertical="@dimen/dp_6"
android:paddingHorizontal="@dimen/dp_16"
app:layout_constraintBottom_toBottomOf="parent"
android:gravity="center"
tools:text="Hurry up! your offer expires in 06 : 00 : 23 :43" />
<com.navi.design.customview.ParallelogramView
android:id="@+id/pgvShimmer"
android:layout_width="@dimen/dp_32"
android:layout_height="@dimen/dp_0"
android:layout_gravity="start"
app:layout_constraintBottom_toBottomOf="@id/footer_tv"
app:layout_constraintStart_toStartOf="@id/footer_tv"
app:layout_constraintTop_toTopOf="@id/footer_tv"
app:tilt_direction="forward" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.card.MaterialCardView>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -28,6 +28,7 @@ class ParallelogramView(context: Context?, attrs: AttributeSet?) : View(context,
private var path: Path? = null
private var paint: Paint? = null
private var isReset = false
var opacity = defaultAlpha
get() = field
@@ -61,13 +62,7 @@ class ParallelogramView(context: Context?, attrs: AttributeSet?) : View(context,
recycle()
}
}
paint =
Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = parallelogramColor
style = Paint.Style.FILL_AND_STROKE
alpha = opacity
}
path = Path()
initView()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
@@ -102,6 +97,30 @@ class ParallelogramView(context: Context?, attrs: AttributeSet?) : View(context,
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
resetView()
}
override fun onAttachedToWindow() {
super.onAttachedToWindow()
if(isReset) {
initView()
requestLayout()
}
}
private fun initView() {
paint =
Paint(Paint.ANTI_ALIAS_FLAG).apply {
color = parallelogramColor
style = Paint.Style.FILL_AND_STROKE
alpha = opacity
}
path = Path()
isReset = false
}
private fun resetView() {
isReset = true
path?.reset()
paint?.reset()
}

View File

@@ -236,6 +236,7 @@ object NaviWidgetIconUtils {
private const val CHEVRON_V2 = "CHEVRON_V2"
private const val CHEVRON_V3 = "CHEVRON_V3"
private const val CHEVRON_ICON_WITH_WHITE_BG = "CHEVRON_ICON_WITH_WHITE_BG"
private const val CHEVRON_BLACK_RIGHT = "CHEVRON_BLACK_RIGHT"
private const val TRENDING_ICON = "TRENDING_ICON"
private const val ICON_BACK_ARROW = "ICON_BACK_ARROW"
private const val ICON_CROSS_ARROW = "ICON_CROSS_ARROW"
@@ -767,6 +768,7 @@ object NaviWidgetIconUtils {
CHEVRON_V2-> R.drawable.ic_chevrons_v2
CHEVRON_V3-> R.drawable.ic_chevron_v3
CHEVRON_ICON_WITH_WHITE_BG -> R.drawable.ic_chevrons_with_white_bg_svg
CHEVRON_BLACK_RIGHT -> R.drawable.ic_chevron_black_right
CHAT_NEW_ICON -> R.drawable.ic_new_chat_svg
CALL_NEW_ICON -> R.drawable.ic_new_phone_call_svg
DOWNLOAD_ICON -> R.drawable.download

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportWidth="16"
android:viewportHeight="16">
<path
android:pathData="M5.531,3.529C5.792,3.268 6.214,3.268 6.474,3.529L10.474,7.529C10.734,7.789 10.734,8.211 10.474,8.471L6.474,12.471C6.214,12.732 5.792,12.732 5.531,12.471C5.271,12.211 5.271,11.789 5.531,11.529L9.06,8L5.531,4.471C5.271,4.211 5.271,3.789 5.531,3.529Z"
android:fillColor="#2A0038"
android:fillType="evenOdd"/>
</vector>