Merge pull request #2294 from medici/feature/configure_chat_touchpoints

Configure chat touchpoints
This commit is contained in:
rahul bhat
2022-02-03 10:57:51 +05:30
committed by GitHub Enterprise
13 changed files with 193 additions and 17 deletions

View File

@@ -2,17 +2,18 @@ package com.naviapp.common.customview
import android.content.Context
import android.graphics.Color
import android.graphics.PorterDuff
import android.util.AttributeSet
import android.util.TypedValue
import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
import android.widget.RelativeLayout
import androidx.core.content.ContextCompat
import androidx.databinding.DataBindingUtil
import com.navi.common.extensions.setVisibilityState
import com.navi.naviwidgets.extensions.showWhenDataIsAvailable
import com.naviapp.R
import com.naviapp.databinding.IconButtonViewBinding
import com.naviapp.models.response.ImageDetail
import com.naviapp.utils.*
class IconTextCustomView(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) {
@@ -91,4 +92,15 @@ class IconTextCustomView(context: Context, attrs: AttributeSet?) : LinearLayout(
binding.iconIv.setImageResource(icon)
binding.titleTv.spannableString(text, spanLength, color)
}
fun setProperties(title: String?, subtitle: String?, imageDetail: ImageDetail?) {
imageDetail?.let {
binding.iconIv.setVisibilityState(VISIBLE)
IconUtils.updateIcon(it, binding.iconIv)
} ?: binding.iconIv.setVisibilityState(GONE)
binding.titleTv.showWhenDataIsAvailable(title)
binding.tvSubTitle.showWhenDataIsAvailable(subtitle)
}
}

View File

@@ -31,4 +31,11 @@ class DashboardRepository : ResponseCallback() {
suspend fun submitLatePaymentReason(latePaymentReason: LatePaymentReasonData) =
apiResponseCallback(retrofitService().submitLatePaymentReason(latePaymentReason))
suspend fun fetchCustomerSupportOptions(screenName: String) =
apiResponseCallback(
retrofitService().fetchCustomerSupportOptions(
screenName = screenName
)
)
}

View File

@@ -16,6 +16,7 @@ import com.naviapp.models.GiPaymentError
import com.naviapp.models.PgRepaymentData
import com.naviapp.models.UserProfile
import com.naviapp.models.request.LatePaymentReasonData
import com.naviapp.models.response.CustomerSupportOptionsResponse
import com.naviapp.models.response.GroupFaq
import com.naviapp.models.response.SuccessResponse
import com.naviapp.models.response.WidgetGenericResponse
@@ -79,6 +80,10 @@ class DashboardSharedVM(private val repository: DashboardRepository = DashboardR
val latePaymentReasonSubmitResponse: LiveData<SuccessResponse>
get() = _latePaymentReasonSubmitResponse
private val _customerSupportOptionsResponse = MutableLiveData<CustomerSupportOptionsResponse>()
val customerSupportOptionsResponse: LiveData<CustomerSupportOptionsResponse>
get() = _customerSupportOptionsResponse
fun fetchFaqs() {
coroutineScope.launch {
val response = repository.fetchFaqs()
@@ -152,4 +157,15 @@ class DashboardSharedVM(private val repository: DashboardRepository = DashboardR
_latePaymentReasonSubmitResponse.value = response.data
}
}
fun fetchCustomerSupportOptions(screenName: String) {
coroutineScope.launch {
val response = repository.fetchCustomerSupportOptions(screenName)
if (response.error == null && response.errors.isNullOrEmpty()) {
_customerSupportOptionsResponse.value = response.data
} else {
setErrorData(response.errors, response.error)
}
}
}
}

View File

@@ -29,12 +29,10 @@ import com.naviapp.models.RedirectPageStatus
import com.naviapp.models.response.RejectionDetailsResponse
import com.naviapp.network.models.GenericErrorResponse
import com.naviapp.personalloan.getloan.activities.GetLoanActivity.Companion.IS_FOR_SECOND_LOAN_JOURNEY
import com.naviapp.personalloan.getloan.activities.GetLoanActivity.Companion.LOAN_OFFER_TYPE
import com.naviapp.utils.*
import com.naviapp.utils.Constants.E_ELIGIBILITY_SOFT_REJECTION_001
import com.naviapp.utils.Constants.E_OFFER_SOFT_REJECTION_001
import com.naviapp.utils.Constants.REJECTED
import com.naviapp.utils.IntentConstants.OFFER_TYPE_ON_DEMAND
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
@@ -127,10 +125,14 @@ class ErrorActivity : BaseActivity(), View.OnClickListener {
val bundle = fragment.arguments ?: Bundle()
fragment.arguments =
bundle.apply {
if (isHomeLoan) putBoolean(IS_SOFT_REJECT, true)
else putBoolean(IS_SOFT_REJECT, isSoftReject)
if (isHomeLoan) {
putBoolean(IS_SOFT_REJECT, true)
} else {
putBoolean(IS_SOFT_REJECT, isSoftReject)
}
putParcelable(SECOND_LOAN_ERROR_DATA, data?.humanReadableContent)
putParcelable(CREDIT_ANALYSIS_DATA, data?.creditScoreData)
putParcelable(CHAT_NUDGE_DATA, data?.chatNudge)
}
supportFragmentManager.beginTransaction().run {
replace(R.id.error_container_fl, fragment, tag)
@@ -292,6 +294,7 @@ class ErrorActivity : BaseActivity(), View.OnClickListener {
const val LOAN_OFFER_EXPIRED = "LOAN_OFFER_EXPIRED"
const val SECOND_LOAN_ERROR_DATA = "SECOND_LOAN_ERROR_DATA"
const val CREDIT_ANALYSIS_DATA = "CREDIT_ANALYSIS_DATA"
const val CHAT_NUDGE_DATA = "CHAT_NUDGE_DATA"
const val REJECTION_DATA = "REJECTION_DATA"
const val IS_SOFT_REJECT = "softReject"
const val SYSTEM_UNDER_MAINTENANCE = "SYSTEM_UNDER_MAINTENANCE"

View File

@@ -4,7 +4,9 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.navi.common.model.CtaData
import com.naviapp.R
import com.naviapp.chat.NaviChatUtil
import com.naviapp.common.fragment.BaseFragment
import com.naviapp.common.navigator.NaviDeepLinkNavigator
import com.naviapp.dashboard.listeners.FragmentListener
@@ -16,8 +18,7 @@ import com.naviapp.errors.fragments.ActionErrorFragment.Companion.DOCUMENT_ERROR
import com.naviapp.errors.fragments.ActionErrorFragment.Companion.PROFILE_ERROR_ICON
import com.naviapp.errors.fragments.ActionErrorFragment.Companion.USER_ERROR_ICON
import com.naviapp.errors.utils.getRejectionScreenData
import com.navi.common.model.CtaData
import com.naviapp.chat.NaviChatUtil
import com.naviapp.models.response.ChatNudge
import com.naviapp.models.response.CreditAnalysisData
import com.naviapp.models.response.ErrorContent
import com.naviapp.network.models.GenericErrorResponse
@@ -64,9 +65,10 @@ class OfferRejectedFragment : BaseFragment(), FragmentListener {
it, this, R.color.white
)
}
if (NaviChatUtil.shouldShowFreshChat()) {
val chatNudge = arguments?.getParcelable<ChatNudge>(ErrorActivity.CHAT_NUDGE_DATA)
chatNudge?.let {
binding.chatView.setProperties(
resources.getString(R.string.chat_help),
it.text ?: resources.getString(R.string.chat_help),
iconResId = R.drawable.ic_chat_dots,
styleResId = R.style.InstructionsFontStyle
)

View File

@@ -11,6 +11,7 @@ import com.naviapp.common.fragment.BaseFragment
import com.naviapp.common.navigator.NaviDeepLinkNavigator
import com.naviapp.dashboard.listeners.FragmentListener
import com.naviapp.databinding.ProfileRejectedFragmentBinding
import com.naviapp.errors.activities.ErrorActivity.Companion.CHAT_NUDGE_DATA
import com.naviapp.errors.activities.ErrorActivity.Companion.CREDIT_ANALYSIS_DATA
import com.naviapp.errors.activities.ErrorActivity.Companion.ERROR_DATA
import com.naviapp.errors.activities.ErrorActivity.Companion.IS_SOFT_REJECT
@@ -20,6 +21,7 @@ import com.naviapp.errors.fragments.ActionErrorFragment.Companion.DOCUMENT_ERROR
import com.naviapp.errors.fragments.ActionErrorFragment.Companion.PROFILE_ERROR_ICON
import com.naviapp.errors.fragments.ActionErrorFragment.Companion.USER_ERROR_ICON
import com.naviapp.errors.utils.getProfileRejectionScreenData
import com.naviapp.models.response.ChatNudge
import com.naviapp.models.response.CreditAnalysisData
import com.naviapp.models.response.ErrorContent
import com.naviapp.network.models.GenericErrorResponse
@@ -66,9 +68,10 @@ class ProfileRejectedFragment : BaseFragment(), FragmentListener {
it, this, R.color.white
)
}
if (NaviChatUtil.shouldShowFreshChat()) {
val chatNudge = arguments?.getParcelable<ChatNudge>(CHAT_NUDGE_DATA)
chatNudge?.let {
binding.chatView.setProperties(
resources.getString(R.string.chat_help),
it.text ?: resources.getString(R.string.chat_help),
iconResId = R.drawable.ic_chat_dots,
styleResId = R.style.InstructionsFontStyle
)

View File

@@ -0,0 +1,8 @@
package com.naviapp.models
enum class CustomerSupportOptionEnum {
FAQ,
EMAIL,
CALL,
CHAT
}

View File

@@ -0,0 +1,28 @@
package com.naviapp.models.response
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import com.navi.common.model.CtaData
import kotlinx.android.parcel.Parcelize
@Parcelize
data class CustomerSupportOptionsResponse(
@SerializedName("title")
val title: String? = null,
@SerializedName("items")
val listOfOptions: List<CustomerSupportOption>? = null
) : Parcelable
@Parcelize
data class CustomerSupportOption(
@SerializedName("type")
val type: String? = null,
@SerializedName("title")
val title: String? = null,
@SerializedName("subTitle")
val subTitle: String? = null,
@SerializedName("imageDetail")
val imageDetail: ImageDetail? = null,
@SerializedName("cta")
val cta: CtaData? = null
) : Parcelable

View File

@@ -11,8 +11,8 @@ data class RejectionDetailsResponse(
@SerializedName("rejectionCode") val rejectionCode: String?,
@SerializedName("humanReadableContent") val humanReadableContent: ErrorContent? = null,
@SerializedName("creditScoreData") val creditScoreData: CreditAnalysisData? = null,
@SerializedName("ratingEnabled") val ratingEnabled: Boolean? = null
@SerializedName("ratingEnabled") val ratingEnabled: Boolean? = null,
@SerializedName("chatNudge") val chatNudge: ChatNudge? = null
) : Parcelable
@Parcelize
@@ -26,4 +26,9 @@ data class ErrorContent(
@SerializedName("iconCode") val iconCode: String?,
@SerializedName("optionIconCode") val optionIconCode: String?,
@SerializedName("numberOfDays") val numberOfDays: String?
) : Parcelable
@Parcelize
data class ChatNudge(
@SerializedName("text") val text: String? = null
) : Parcelable

View File

@@ -758,4 +758,9 @@ interface RetrofitService {
@Query("page") page: String
): Response<GenericResponse<UxCamMetaConfig>>
@GET("/fetch-customer-support-options")
suspend fun fetchCustomerSupportOptions(
@Header("screenName") screenName: String
): Response<GenericResponse<CustomerSupportOptionsResponse>>
}

View File

@@ -2,20 +2,24 @@ package com.naviapp.personalloan.getloan.common.fragment
import android.os.Bundle
import android.view.View
import android.view.View.VISIBLE
import android.view.ViewStub
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider
import com.navi.common.utils.CommonUtils
import com.naviapp.R
import com.naviapp.analytics.utils.NaviAnalytics
import com.naviapp.chat.NaviChatUtil
import com.naviapp.common.customview.BaseBottomSheet
import com.naviapp.common.navigator.ScreenNavigator
import com.naviapp.common.navigator.ScreenNavigator.Companion.FAQS_SCREEN
import com.naviapp.dashboard.viewmodels.DashboardSharedVM
import com.naviapp.databinding.CustomerSupportFragmentBinding
import com.naviapp.models.CustomerSupportOptionEnum
import com.naviapp.personalloan.getloan.activities.FaqsActivity.Companion.FAQS_DATA
import com.naviapp.personalloan.getloan.utils.*
import com.naviapp.utils.observeNonNull
import com.naviapp.utils.setVisibilityState
class CustomerSupportFragment : BaseBottomSheet(), View.OnClickListener {
private lateinit var binding: CustomerSupportFragmentBinding
@@ -25,13 +29,19 @@ class CustomerSupportFragment : BaseBottomSheet(), View.OnClickListener {
override fun setContainerView(viewStub: ViewStub) {
viewStub.layoutResource = R.layout.customer_support_fragment
binding = DataBindingUtil.getBinding(viewStub.inflate())!!
initUI()
val screenName = arguments?.getString(CUST_SCREEN_NAME)
if (shouldFetchCustomerSupportOptions(screenName)) {
screenName?.let { fetchCustomerSupportOptions(it) }
} else {
initUI()
}
initListeners()
}
private fun initListeners() {
binding.sendEmailIbv.setOnClickListener(this)
binding.faqsIbv.setOnClickListener(this)
binding.itcvChat.setOnClickListener(this)
viewModel.userProfile.observeNonNull(this) {
hideLoader()
ScreenNavigator.instance.startActivity(
@@ -40,13 +50,50 @@ class CustomerSupportFragment : BaseBottomSheet(), View.OnClickListener {
isRootActivity = false
)
}
viewModel.customerSupportOptionsResponse.observeNonNull(this) { customerSupportOptionResponse ->
hideLoader()
customerSupportOptionResponse.listOfOptions?.let { listOfOptions ->
for (customerSupportOption in listOfOptions) {
when (customerSupportOption.type) {
CustomerSupportOptionEnum.FAQ.toString() -> {
binding.faqsIbv.setVisibilityState(VISIBLE)
binding.faqsIbv.setProperties(
title = customerSupportOption.title,
subtitle = customerSupportOption.subTitle,
imageDetail = customerSupportOption.imageDetail
)
}
CustomerSupportOptionEnum.CHAT.toString() -> {
binding.itcvChat.setVisibilityState(VISIBLE)
binding.itcvChat.setProperties(
title = customerSupportOption.title,
subtitle = customerSupportOption.subTitle,
imageDetail = customerSupportOption.imageDetail
)
}
CustomerSupportOptionEnum.EMAIL.toString() -> {
binding.sendEmailIbv.setVisibilityState(VISIBLE)
binding.sendEmailIbv.setProperties(
title = customerSupportOption.title,
subtitle = customerSupportOption.subTitle,
imageDetail = customerSupportOption.imageDetail
)
}
}
}
}
}
}
private fun initUI() {
binding.faqsIbv.setVisibilityState(VISIBLE)
binding.faqsIbv.setProperties(
R.drawable.ic_help_svg,
getString(R.string.faqs_text)
)
binding.sendEmailIbv.setVisibilityState(VISIBLE)
binding.sendEmailIbv.setProperties(
R.drawable.ic_send_email_icon_svg,
getString(R.string.send_email)
@@ -63,6 +110,9 @@ class CustomerSupportFragment : BaseBottomSheet(), View.OnClickListener {
R.id.faqs_ibv -> {
navigateToFaqs()
}
R.id.itcvChat -> {
NaviChatUtil.showConversations()
}
}
}
@@ -88,6 +138,11 @@ class CustomerSupportFragment : BaseBottomSheet(), View.OnClickListener {
}
}
private fun fetchCustomerSupportOptions(screenName: String) {
showLoader()
viewModel.fetchCustomerSupportOptions(screenName)
}
override val screenName: String
get() = "help_${arguments?.getString(CUST_SCREEN_NAME).orEmpty()}_screen"
@@ -104,4 +159,15 @@ class CustomerSupportFragment : BaseBottomSheet(), View.OnClickListener {
}
}
}
private fun shouldFetchCustomerSupportOptions(screenName: String?): Boolean {
return when (screenName) {
NaviAnalytics.LOAN_DETAILS,
NaviAnalytics.KYC,
NaviAnalytics.BANK_DETAILS,
NaviAnalytics.BANK_DETAIL_AUTO_DEBIT,
NaviAnalytics.LOAN_AGREEMENT -> true
else -> false
}
}
}

View File

@@ -22,14 +22,24 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_dp_16"
android:layout_marginBottom="@dimen/layout_dp_16" />
android:layout_marginBottom="@dimen/layout_dp_16"
android:visibility="gone" />
<com.naviapp.common.customview.IconTextCustomView
android:id="@+id/itcvChat"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_dp_16"
android:layout_marginBottom="@dimen/layout_dp_16"
android:visibility="gone" />
<com.naviapp.common.customview.IconTextCustomView
android:id="@+id/send_email_ibv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/layout_dp_16"
android:layout_marginBottom="@dimen/layout_dp_16" />
android:layout_marginBottom="@dimen/layout_dp_16"
android:visibility="gone" />
</LinearLayout>
</layout>

View File

@@ -21,7 +21,18 @@
style="@style/AgreementTwoFontStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/_4dp"
android:layout_toEndOf="@id/icon_iv"
tools:text="@string/send_email" />
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/tvSubTitle"
android:layout_marginTop="@dimen/_1dp"
style="@style/TextDescExtraSmallRegularStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/title_tv"
android:visibility="gone"
tools:text="(Mon-Fri)" />
</RelativeLayout>
</layout>