Fix crash

Signed-off-by: denis0001-dev <denis0001.dev@ya.ru>
This commit is contained in:
2026-02-13 21:50:51 +03:00
Unverified
parent a136dd7be3
commit 5ea5e43c23
2 changed files with 20 additions and 14 deletions
@@ -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 {