mirror of
https://github.com/fromchat-messenger/app.git
synced 2026-09-22 19:15:05 +03:00
@@ -5,6 +5,8 @@ import kotlinx.coroutines.Dispatchers
|
|||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.sync.Mutex
|
||||||
|
import kotlinx.coroutines.sync.withLock
|
||||||
import kotlinx.serialization.Serializable
|
import kotlinx.serialization.Serializable
|
||||||
import ru.fromchat.api.Message
|
import ru.fromchat.api.Message
|
||||||
import ru.fromchat.api.WebSocketMessage
|
import ru.fromchat.api.WebSocketMessage
|
||||||
@@ -79,14 +81,17 @@ abstract class ChatPanel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val addMessageMutex = Mutex()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add message to list
|
* Add message to list. Mutex prevents duplicate adds when same update
|
||||||
|
* is processed concurrently from multiple WebSocket connections.
|
||||||
*/
|
*/
|
||||||
protected fun addMessage(message: Message) {
|
protected suspend fun addMessage(message: Message) {
|
||||||
|
addMessageMutex.withLock {
|
||||||
val messageExists = _state.messages.any { it.id == message.id }
|
val messageExists = _state.messages.any { it.id == message.id }
|
||||||
if (!messageExists) {
|
if (!messageExists) {
|
||||||
Logger.d("ChatPanel", "Adding message: id=${message.id}, content=${message.content.take(50)}")
|
Logger.d("ChatPanel", "Adding message: id=${message.id}, content=${message.content.take(50)}")
|
||||||
// Add message and sort by timestamp (ISO 8601 strings sort correctly lexicographically)
|
|
||||||
updateState { currentState ->
|
updateState { currentState ->
|
||||||
val newMessages = (currentState.messages + message).sortedBy { it.timestamp }
|
val newMessages = (currentState.messages + message).sortedBy { it.timestamp }
|
||||||
Logger.d("ChatPanel", "Messages count after add: ${newMessages.size}")
|
Logger.d("ChatPanel", "Messages count after add: ${newMessages.size}")
|
||||||
@@ -96,6 +101,7 @@ abstract class ChatPanel(
|
|||||||
Logger.d("ChatPanel", "Message already exists: id=${message.id}")
|
Logger.d("ChatPanel", "Message already exists: id=${message.id}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update existing message
|
* Update existing message
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ class PublicChatPanel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleSingleUpdate(updateMessage: WebSocketMessage) {
|
private suspend fun handleSingleUpdate(updateMessage: WebSocketMessage) {
|
||||||
val json = ApiClient.json
|
val json = ApiClient.json
|
||||||
when (updateMessage.type) {
|
when (updateMessage.type) {
|
||||||
"newMessage" -> {
|
"newMessage" -> {
|
||||||
@@ -154,7 +154,7 @@ class PublicChatPanel(
|
|||||||
val data = message.data ?: return
|
val data = message.data ?: return
|
||||||
val updatesData = json.decodeFromJsonElement(WebSocketUpdatesData.serializer(), data)
|
val updatesData = json.decodeFromJsonElement(WebSocketUpdatesData.serializer(), data)
|
||||||
Logger.d("PublicChatPanel", "Received ${updatesData.updates.size} batched updates (seq: ${updatesData.seq})")
|
Logger.d("PublicChatPanel", "Received ${updatesData.updates.size} batched updates (seq: ${updatesData.seq})")
|
||||||
updatesData.updates.forEach { update ->
|
for (update in updatesData.updates) {
|
||||||
handleSingleUpdate(update)
|
handleSingleUpdate(update)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Reference in New Issue
Block a user