NTP-61 | Update user typing status in firebase (#11531)
This commit is contained in:
@@ -21,6 +21,7 @@ import com.navi.chat.utils.NaviChatAnalytics.Companion.DOCUMENT
|
||||
import com.navi.chat.utils.NaviChatAnalytics.Companion.DOCUMENT_ADDED_FROM_FIRESTORE
|
||||
import com.navi.chat.utils.NaviChatAnalytics.Companion.DOCUMENT_TYPE
|
||||
import com.navi.chat.utils.NaviChatAnalytics.Companion.ERROR_LISTENING_TO_FIRESTORE_CHANGES
|
||||
import com.navi.chat.utils.NaviChatAnalytics.Companion.ERROR_UPDATING_USER_TYPING_STATUS
|
||||
import com.navi.chat.utils.NaviChatAnalytics.Companion.LISTENER_ATTACHED_TO_FIRESTORE_SUCCESSFULLY
|
||||
import com.navi.chat.utils.NaviChatAnalytics.Companion.LISTEN_TO_CONTROLLER_WIDGET
|
||||
import com.navi.chat.utils.NaviChatAnalytics.Companion.LISTEN_TO_FIRESTORE_CLIENT_CHANGES
|
||||
@@ -35,12 +36,15 @@ import com.navi.chat.utils.NaviChatAnalytics.Companion.NOT_ABLE_TO_LISTEN_TO_FIR
|
||||
import com.navi.chat.utils.NaviChatAnalytics.Companion.PATH
|
||||
import com.navi.chat.utils.NaviChatAnalytics.Companion.PROCESSING_CONTROLLER_WIDGET
|
||||
import com.navi.chat.utils.NaviChatAnalytics.Companion.SUCCESS_LISTENING_TO_FIRESTORE_CHANGES
|
||||
import com.navi.chat.utils.NaviChatAnalytics.Companion.SUCCESS_UPDATING_USER_TYPING_STATUS
|
||||
import com.navi.chat.utils.NaviChatAnalytics.Companion.UPDATE_FIRESTORE_USER_TYPING_STATUS
|
||||
import com.navi.chat.utils.NaviChatAnalytics.Companion.UPDATING_CLIENT_MESSAGE_RECEIPTS
|
||||
import com.navi.chat.utils.NaviChatAnalytics.Companion.UPDATING_TYPING_STATUS
|
||||
import com.navi.chat.utils.NaviChatAnalytics.Companion.WRITE_TO_FIRESTORE
|
||||
import com.navi.chat.utils.SOURCE
|
||||
import com.navi.chat.utils.TYPING_CUES_PATH_AGENT
|
||||
import com.navi.common.utils.ERROR
|
||||
import com.navi.common.utils.log
|
||||
import com.navi.naviwidgets.models.NaviChatWidget
|
||||
import com.navi.naviwidgets.models.response.*
|
||||
import com.navi.naviwidgets.utils.NAVI_CHAT_RT_CREATED_AT
|
||||
@@ -584,6 +588,38 @@ class ChatFireStoreDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
fun updateFireStoreForUserTypingStatus(
|
||||
path: String,
|
||||
documentId: String,
|
||||
typingStatusDocument: Map<String, Any>
|
||||
) {
|
||||
ChatFireStore.getFireStoreReference(path)
|
||||
.document(documentId)
|
||||
.set(typingStatusDocument)
|
||||
.addOnSuccessListener {
|
||||
crmEventTracker.sendEvent(
|
||||
SUCCESS_UPDATING_USER_TYPING_STATUS,
|
||||
hashMapOf(
|
||||
DOCUMENT to typingStatusDocument.toString(),
|
||||
PATH to path,
|
||||
SOURCE to UPDATE_FIRESTORE_USER_TYPING_STATUS
|
||||
)
|
||||
)
|
||||
}
|
||||
.addOnFailureListener { e ->
|
||||
e.log()
|
||||
crmEventTracker.sendEvent(
|
||||
ERROR_UPDATING_USER_TYPING_STATUS,
|
||||
hashMapOf(
|
||||
DOCUMENT to typingStatusDocument.toString(),
|
||||
PATH to path,
|
||||
ERROR to e.toString(),
|
||||
SOURCE to UPDATE_FIRESTORE_USER_TYPING_STATUS
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeFireStoreListener() {
|
||||
if (listenerToFirestoreChangesList.size > 0) {
|
||||
for (listener in listenerToFirestoreChangesList) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
*
|
||||
* * Copyright © 2022 by Navi Technologies Limited
|
||||
* * Copyright © 2022-2024 by Navi Technologies Limited
|
||||
* * All rights reserved. Strictly confidential
|
||||
*
|
||||
*/
|
||||
@@ -20,4 +20,6 @@ interface ChatDataProvider {
|
||||
fun writeMessage(naviChatWidget: NaviChatWidget)
|
||||
|
||||
fun listenToControllerWidget()
|
||||
|
||||
fun updateUserTypingStatus(typingStatusDocument: Map<String, Any>)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.navi.chat.data.firestore.ChatFireStoreDatabase
|
||||
import com.navi.chat.models.FireStoreDataProviderModel
|
||||
import com.navi.chat.provider.ChatDataProvider
|
||||
import com.navi.chat.provider.MessageOperation
|
||||
import com.navi.chat.utils.TYPING_CUES_PATH_USER
|
||||
import com.navi.naviwidgets.models.NaviChatWidget
|
||||
import dagger.hilt.android.scopes.ActivityRetainedScoped
|
||||
|
||||
@@ -71,6 +72,16 @@ class FireStoreDataProvider : ChatDataProvider, LifecycleObserver {
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateUserTypingStatus(typingStatusDocument: Map<String, Any>) {
|
||||
if (::chatFireStoreDatabase.isInitialized) {
|
||||
chatFireStoreDatabase.updateFireStoreForUserTypingStatus(
|
||||
path = fireStoreDataProviderModel.eventsPath,
|
||||
documentId = TYPING_CUES_PATH_USER,
|
||||
typingStatusDocument = typingStatusDocument
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun listenToTypingStatus() {
|
||||
if (::chatFireStoreDatabase.isInitialized) {
|
||||
if (fireStoreDataProviderModel.eventsPath.isNotNullAndNotEmpty()) {
|
||||
|
||||
@@ -148,6 +148,7 @@ class NaviChatFragment : BaseFragment(), WidgetCallback, MessageOperation, Toolb
|
||||
@Inject lateinit var fireStoreDataProvider: FireStoreDataProvider
|
||||
@Inject lateinit var fileUploadManager: FileUploadManager
|
||||
@Inject lateinit var fileHelper: ChatFileHelper
|
||||
@Inject lateinit var firebaseAuthHelper: FirebaseAuthHelper
|
||||
private lateinit var binding: FragmentCommonNaviChatBinding
|
||||
private lateinit var linearLayoutManager: LinearLayoutManager
|
||||
private lateinit var chatRVAdapter: ChatRVAdapter<NaviBaseAdapterModel>
|
||||
@@ -170,9 +171,11 @@ class NaviChatFragment : BaseFragment(), WidgetCallback, MessageOperation, Toolb
|
||||
private var showCsatHandler = Handler(Looper.getMainLooper())
|
||||
private var timerHandler = Handler(Looper.getMainLooper())
|
||||
private var callStatusHandler = Handler(Looper.getMainLooper())
|
||||
private val userTypingStatusHandler = Handler(Looper.getMainLooper())
|
||||
private var textWatcherForTimer: TextWatcher? = null
|
||||
private var textWatcherForCharLimit: TextWatcher? = null
|
||||
private var textWatcherForSendAnimation: TextWatcher? = null
|
||||
private var textWatcherForUserTyping: TextWatcher? = null
|
||||
private var naviChatSystemLocalData: NaviChatSystemLocalData? = null
|
||||
private var chatResolutionStatusWidget: NaviChatResolutionStatusWidget? = null
|
||||
private var csatWidgetData: NaviChatCsatRatingWidget? = null
|
||||
@@ -193,8 +196,10 @@ class NaviChatFragment : BaseFragment(), WidgetCallback, MessageOperation, Toolb
|
||||
private var topBackButtonClickListener: ChatTopBackButtonClickListener? = null
|
||||
private var latestMessageLocation: Int? = null
|
||||
private var productConfigId: String? = null
|
||||
|
||||
@Inject lateinit var firebaseAuthHelper: FirebaseAuthHelper
|
||||
private val userTypingTimeoutInterval: Long = 3000L
|
||||
private val userTypingStatusUpdateInterval: Long = 3000L
|
||||
private var isUserTyping: Boolean = false
|
||||
private var currTimeForUserTyping: Long? = null
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
@@ -290,10 +295,16 @@ class NaviChatFragment : BaseFragment(), WidgetCallback, MessageOperation, Toolb
|
||||
setTextWatcher()
|
||||
setTextWatcherForCharLimit()
|
||||
setTextWatcherForSendAnimation()
|
||||
setTextWatcherForUserTyping()
|
||||
|
||||
ivSendFreeText.setOnClickListener {
|
||||
val freeText = binding.etFreeText.text.toString()
|
||||
if (freeText != EMPTY) {
|
||||
if (isUserTyping) {
|
||||
userTypingStatusHandler.removeCallbacks(userTypingTimeoutRunnable)
|
||||
isUserTyping = false
|
||||
onUserTypingStatusChanged(false)
|
||||
}
|
||||
writeUserMessageToFirestore(message = freeText, metaData = NaviChatMetaData())
|
||||
binding.etFreeText.text = null
|
||||
naviChatSharedViewModel.incrementNumberOfMessagesSent()
|
||||
@@ -1681,6 +1692,7 @@ class NaviChatFragment : BaseFragment(), WidgetCallback, MessageOperation, Toolb
|
||||
override fun processControllerWidget(naviChatControllerWidget: NaviChatControllerWidget) {
|
||||
when (naviChatControllerWidget.widgetData?.message) {
|
||||
NaviChatControllerEnum.ENABLE_FREE_TEXT.name -> {
|
||||
attachTextWatcherForUserTyping()
|
||||
if (binding.clFreeText.visibility == GONE) {
|
||||
binding.clFreeText.isVisible = true
|
||||
binding.etFreeText.requestFocus()
|
||||
@@ -1696,6 +1708,7 @@ class NaviChatFragment : BaseFragment(), WidgetCallback, MessageOperation, Toolb
|
||||
}
|
||||
if (naviChatControllerWidget.widgetData?.controllerConfig == null) {
|
||||
hideWaitTimeNudges()
|
||||
naviChatSharedViewModel.setChatAttachmentOptionList(emptyList())
|
||||
}
|
||||
naviChatControllerWidget.widgetData?.controllerConfig?.let { config ->
|
||||
config.showAttachment?.let { showAttachment ->
|
||||
@@ -1821,6 +1834,16 @@ class NaviChatFragment : BaseFragment(), WidgetCallback, MessageOperation, Toolb
|
||||
naviChatSharedViewModel.setChatExitOptionList(options)
|
||||
}
|
||||
}
|
||||
if (
|
||||
naviChatControllerWidget.widgetData?.controllerConfig?.attachmentConfig == null ||
|
||||
naviChatControllerWidget.widgetData
|
||||
?.controllerConfig
|
||||
?.attachmentConfig
|
||||
?.chatAttachmentOptions
|
||||
.isNullOrEmpty()
|
||||
) {
|
||||
naviChatSharedViewModel.setChatAttachmentOptionList(emptyList())
|
||||
}
|
||||
naviChatControllerWidget.widgetData?.controllerConfig?.attachmentConfig?.let {
|
||||
attachmentConfig ->
|
||||
attachmentConfig.chatAttachmentOptions?.let { attachmentOptions ->
|
||||
@@ -1860,6 +1883,66 @@ class NaviChatFragment : BaseFragment(), WidgetCallback, MessageOperation, Toolb
|
||||
}
|
||||
}
|
||||
|
||||
private val userTypingTimeoutRunnable = Runnable {
|
||||
isUserTyping = false
|
||||
onUserTypingStatusChanged(false)
|
||||
}
|
||||
|
||||
private fun setTextWatcherForUserTyping() {
|
||||
textWatcherForUserTyping =
|
||||
object : TextWatcher {
|
||||
override fun afterTextChanged(s: Editable?) {
|
||||
userTypingStatusHandler.removeCallbacks(userTypingTimeoutRunnable)
|
||||
if (s?.isNotEmpty() == true) {
|
||||
if (
|
||||
System.currentTimeMillis().minus(currTimeForUserTyping ?: 0) >=
|
||||
userTypingStatusUpdateInterval || isUserTyping.not()
|
||||
) {
|
||||
currTimeForUserTyping = System.currentTimeMillis()
|
||||
isUserTyping = true
|
||||
onUserTypingStatusChanged(true)
|
||||
}
|
||||
userTypingStatusHandler.postDelayed(
|
||||
userTypingTimeoutRunnable,
|
||||
userTypingTimeoutInterval
|
||||
)
|
||||
} else {
|
||||
if (isUserTyping) {
|
||||
isUserTyping = false
|
||||
userTypingStatusHandler.postDelayed(
|
||||
userTypingTimeoutRunnable,
|
||||
userTypingTimeoutInterval
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(
|
||||
s: CharSequence?,
|
||||
start: Int,
|
||||
count: Int,
|
||||
after: Int
|
||||
) {}
|
||||
|
||||
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
|
||||
}
|
||||
}
|
||||
|
||||
private fun onUserTypingStatusChanged(isUserTyping: Boolean) {
|
||||
val typingStatusDocument = mapOf(TYPING to isUserTyping, UPDATED_AT to Timestamp.now())
|
||||
if (::chatDataProvider.isInitialized)
|
||||
chatDataProvider.updateUserTypingStatus(typingStatusDocument)
|
||||
}
|
||||
|
||||
private fun attachTextWatcherForUserTyping() {
|
||||
removeTextWatcherForUserTyping()
|
||||
binding.etFreeText.addTextChangedListener(textWatcherForUserTyping)
|
||||
}
|
||||
|
||||
private fun removeTextWatcherForUserTyping() {
|
||||
binding.etFreeText.removeTextChangedListener(textWatcherForUserTyping)
|
||||
}
|
||||
|
||||
private fun attachTextWatcher() {
|
||||
removeTextWatcher()
|
||||
binding.etFreeText.addTextChangedListener(textWatcherForTimer)
|
||||
@@ -2115,12 +2198,14 @@ class NaviChatFragment : BaseFragment(), WidgetCallback, MessageOperation, Toolb
|
||||
super.onDestroyView()
|
||||
removeTextWatcher()
|
||||
removeTextWatcherForCharLimit()
|
||||
removeTextWatcherForUserTyping()
|
||||
removeInactivityTimer()
|
||||
fireStoreListenerHandler.removeCallbacksAndMessages(null)
|
||||
botTypingStatusHandler.removeCallbacksAndMessages(null)
|
||||
typingStatusHandler.removeCallbacksAndMessages(null)
|
||||
showCsatHandler.removeCallbacksAndMessages(null)
|
||||
callStatusHandler.removeCallbacksAndMessages(null)
|
||||
userTypingStatusHandler.removeCallbacksAndMessages(null)
|
||||
fileUploadManager.cancelUploads()
|
||||
naviChatSharedViewModel.resetNumberOfMessagesSent()
|
||||
naviChatSharedViewModel.resetNumberOfOptionsSelected()
|
||||
|
||||
@@ -84,6 +84,7 @@ const val FILE_TYPE_PARAM = "fileType"
|
||||
const val CONVERSATION_ID_PARAM = "conversationId"
|
||||
const val NAVI_CHAT_SYSTEM_REMOTE_DATA = "naviChatSystemRemoteData"
|
||||
const val TYPING_CUES_PATH_AGENT = "agent"
|
||||
const val TYPING_CUES_PATH_USER = "user"
|
||||
const val BOT = "BOT"
|
||||
const val DIGITAL_GOLD = "DIGITAL_GOLD"
|
||||
const val FILENAME = "filename"
|
||||
@@ -125,3 +126,5 @@ const val CONNECTED_NUDGE_DELAY = 2000L
|
||||
const val FREE_TEXT_DISABLED_ALPHA_VALUE = 0.2f
|
||||
const val FREE_TEXT_ENABLED_ALPHA_VALUE = 1.0f
|
||||
const val HELP_CENTER_CACHE_KEY = "help_center"
|
||||
const val TYPING = "typing"
|
||||
const val UPDATED_AT = "updated_at"
|
||||
|
||||
@@ -161,6 +161,9 @@ class NaviChatAnalytics private constructor() {
|
||||
const val LISTEN_TO_FIRESTORE_SERVER_UPDATES = "listen_to_firestore_server_updates"
|
||||
const val FIREBASE_AUTHENTICATION_SUCCESS = "firebase_authentication_success"
|
||||
const val FIREBASE_AUTHENTICATION_FAILURE = "firebase_authentication_failure"
|
||||
const val SUCCESS_UPDATING_USER_TYPING_STATUS = "success_updating_user_typing_status"
|
||||
const val ERROR_UPDATING_USER_TYPING_STATUS = "error_updating_user_typing_status"
|
||||
const val UPDATE_FIRESTORE_USER_TYPING_STATUS = "update_firestore_user_typing_status"
|
||||
|
||||
val naviChatAnalytics: NaviChatAnalytics by lazy { Holder.INSTANCE }
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user