feat(chat): show agent message reactions (#1422)
This commit is contained in:
14 files changed
+289
No files matched your search
@@ -0,0 +1,33 @@
|
||||
package com.m57.hermescontrol.data.model
|
||||
|
||||
import kotlinx.serialization.json.JsonArray
|
||||
import kotlinx.serialization.json.JsonElement
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
|
||||
/** One emoji reaction on a message. Tapback semantics: at most one per [author] per message. */
|
||||
data class MessageReaction(
|
||||
val emoji: String,
|
||||
val author: String,
|
||||
)
|
||||
|
||||
/** Reactions carried by a live `message.reaction` event payload (`reactions` list of maps). */
|
||||
internal fun parseMessageReactions(raw: Any?): List<MessageReaction> =
|
||||
(raw as? List<*>).orEmpty().mapNotNull { item ->
|
||||
val map = item as? Map<*, *> ?: return@mapNotNull null
|
||||
val emoji = (map["emoji"] as? String)?.takeIf { it.isNotBlank() } ?: return@mapNotNull null
|
||||
MessageReaction(emoji = emoji, author = map["author"] as? String ?: "")
|
||||
}
|
||||
|
||||
/** Reactions persisted under `display_metadata.reactions` on a REST transcript row. */
|
||||
internal fun parseMessageReactions(displayMetadata: JsonElement?): List<MessageReaction> {
|
||||
val list = (displayMetadata as? JsonObject)?.get("reactions") as? JsonArray ?: return emptyList()
|
||||
return list.mapNotNull { item ->
|
||||
val obj = item as? JsonObject ?: return@mapNotNull null
|
||||
val emoji =
|
||||
(obj["emoji"] as? JsonPrimitive)?.takeIf { it.isString }?.content?.takeIf { it.isNotBlank() }
|
||||
?: return@mapNotNull null
|
||||
val author = (obj["author"] as? JsonPrimitive)?.takeIf { it.isString }?.content.orEmpty()
|
||||
MessageReaction(emoji = emoji, author = author)
|
||||
}
|
||||
}
|
||||
@@ -75,6 +75,10 @@ data class SessionMessage(
|
||||
val timestampText: String?
|
||||
get() = (timestamp as? JsonPrimitive)?.content
|
||||
|
||||
/** Emoji reactions persisted on this row (`display_metadata.reactions`). */
|
||||
val reactions: List<MessageReaction>
|
||||
get() = parseMessageReactions(display_metadata)
|
||||
|
||||
val tokenCount: Int?
|
||||
get() {
|
||||
val prim = token_count as? JsonPrimitive ?: return null
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.m57.hermescontrol.data.ws
|
||||
|
||||
import android.util.Log
|
||||
import com.m57.hermescontrol.data.model.parseMessageReactions
|
||||
import com.m57.hermescontrol.ui.chat.extractTodosFromMap
|
||||
import kotlinx.serialization.json.JsonObject
|
||||
|
||||
@@ -333,6 +334,20 @@ object EventParser {
|
||||
WsEvent.ReactionEvent(kind)
|
||||
}
|
||||
|
||||
"message.reaction" -> {
|
||||
val rowId = (payload?.get("row_id") as? Number)?.toLong()
|
||||
if (rowId == null) {
|
||||
WsEvent.Unknown(rawJson)
|
||||
} else {
|
||||
WsEvent.MessageReactionUpdated(
|
||||
rowId = rowId,
|
||||
reactions = parseMessageReactions(payload["reactions"]),
|
||||
role = payload["role"] as? String ?: "",
|
||||
sessionId = sessionId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
"approval.request" -> {
|
||||
val command = payload?.get("command") as? String
|
||||
val description = payload?.get("description") as? String
|
||||
|
||||
@@ -445,6 +445,18 @@ sealed class WsEvent {
|
||||
val kind: String = "",
|
||||
) : WsEvent()
|
||||
|
||||
/**
|
||||
* `message.reaction`: the agent (or another client) set/cleared a tapback on a stored
|
||||
* message. [reactions] is the row's full list after the write; [rowId] is the gateway
|
||||
* SQLite row id, matched against `ChatMessage.serverRowId`.
|
||||
*/
|
||||
data class MessageReactionUpdated(
|
||||
val rowId: Long,
|
||||
val reactions: List<com.m57.hermescontrol.data.model.MessageReaction>,
|
||||
val role: String,
|
||||
val sessionId: String?,
|
||||
) : WsEvent()
|
||||
|
||||
// ── Replay resync (internal) ──────────────────────────────────────────
|
||||
|
||||
/** Internal: replay could not cover the reconnect gap (truncated or epoch change) — UI must refetch history. */
|
||||
|
||||
Reference in new issue
Block a user