@@ -3,6 +3,7 @@ package com.getcode.network
33import androidx.paging.Pager
44import androidx.paging.PagingConfig
55import androidx.paging.PagingData
6+ import com.getcode.api.BuildConfig
67import com.getcode.db.AppDatabase
78import com.getcode.db.Database
89import com.getcode.manager.SessionManager
@@ -15,7 +16,6 @@ import com.getcode.model.ConversationWithLastPointers
1516import com.getcode.model.ID
1617import com.getcode.model.MessageStatus
1718import com.getcode.model.SocialUser
18- import com.getcode.model.chat.Chat
1919import com.getcode.model.chat.ChatType
2020import com.getcode.model.chat.MessageContent
2121import com.getcode.model.chat.OutgoingMessageContent
@@ -27,13 +27,15 @@ import com.getcode.network.client.ChatMessageStreamReference
2727import com.getcode.network.exchange.Exchange
2828import com.getcode.network.repository.base58
2929import com.getcode.network.service.ChatServiceV2
30- import com.getcode.solana.keys.PublicKey
3130import com.getcode.utils.ErrorUtils
3231import com.getcode.utils.bytes
3332import com.getcode.utils.trace
3433import kotlinx.coroutines.CoroutineScope
3534import kotlinx.coroutines.Dispatchers
3635import kotlinx.coroutines.flow.Flow
36+ import kotlinx.coroutines.flow.MutableStateFlow
37+ import kotlinx.coroutines.flow.first
38+ import kotlinx.coroutines.flow.map
3739import kotlinx.coroutines.launch
3840import javax.inject.Inject
3941
@@ -50,6 +52,9 @@ interface ConversationController {
5052 suspend fun advanceReadPointer (conversationId : ID , messageId : ID , status : MessageStatus )
5153 suspend fun sendMessage (conversationId : ID , message : String ): Result <ID >
5254 fun conversationPagingData (conversationId : ID ): Flow <PagingData <ConversationMessageWithContent >>
55+ fun observeTyping (conversationId : ID ): Flow <Boolean >
56+ suspend fun onUserStartedTypingIn (conversationId : ID )
57+ suspend fun onUserStoppedTypingIn (conversationId : ID )
5358}
5459
5560class ConversationStreamController @Inject constructor(
@@ -66,6 +71,8 @@ class ConversationStreamController @Inject constructor(
6671
6772 private var stream: ChatMessageStreamReference ? = null
6873
74+ private val typingChats = MutableStateFlow <List <ID >>(emptyList())
75+
6976 private fun conversationPagingSource (conversationId : ID ) =
7077 db.conversationMessageDao().observeConversationMessages(conversationId.base58)
7178
@@ -136,7 +143,13 @@ class ConversationStreamController @Inject constructor(
136143 ) result@{ result ->
137144 if (result.isSuccess) {
138145 val updates = result.getOrNull() ? : return @result
139- val (messages, pointers) = updates
146+ val (messages, pointers, isTyping) = updates
147+
148+ typingChats.value = if (isTyping) {
149+ typingChats.value + listOf (conversation.id).toSet()
150+ } else {
151+ typingChats.value - listOf (conversation.id).toSet()
152+ }
140153
141154 historyController.updateChatWithMessages(chat, messages)
142155 val messagesWithContent = messages.map {
@@ -165,7 +178,7 @@ class ConversationStreamController @Inject constructor(
165178 }
166179 }
167180
168- println (" chat messages: ${messages.count()} , pointers=${pointers.count()} " )
181+ println (" chat messages: ${messages.count()} , pointers=${pointers.count()} , isTyping= $isTyping " )
169182
170183 scope.launch(Dispatchers .IO ) {
171184 db.conversationMessageDao().upsertMessagesWithContent(messagesWithContent)
@@ -259,4 +272,45 @@ class ConversationStreamController @Inject constructor(
259272 initialKey = null ,
260273 ) { conversationPagingSource(conversationId) }.flow
261274
275+ override fun observeTyping (conversationId : ID ): Flow <Boolean > =
276+ typingChats.map { it.contains(conversationId) }
277+
278+ override suspend fun onUserStartedTypingIn (conversationId : ID ) {
279+ val owner = SessionManager .getOrganizer()?.ownerKeyPair ? : return
280+
281+ val chat = historyController.findChat { it.id == conversationId }
282+ ? : return
283+
284+ val memberId = chat.selfId ? : return
285+
286+ chatService.onStartedTyping(
287+ owner, chat, memberId
288+ ).onSuccess {
289+ println (" on typing started reported" )
290+ }.onFailure {
291+ if (BuildConfig .DEBUG ) {
292+ it.printStackTrace()
293+ }
294+ }
295+ }
296+
297+ override suspend fun onUserStoppedTypingIn (conversationId : ID ) {
298+ val owner = SessionManager .getOrganizer()?.ownerKeyPair ? : return
299+
300+ val chat = historyController.findChat { it.id == conversationId }
301+ ? : return
302+
303+ val memberId = chat.selfId ? : return
304+
305+ chatService.onStoppedTyping(
306+ owner, chat, memberId
307+ ).onSuccess {
308+ println (" on typing stopped reported" )
309+ }.onFailure {
310+ if (BuildConfig .DEBUG ) {
311+ it.printStackTrace()
312+ }
313+ }
314+ }
315+
262316}
0 commit comments