From 8f8c8c267f372c25799beb2220bea4f55ba6b0ed Mon Sep 17 00:00:00 2001 From: evgeny Date: Tue, 6 Oct 2026 21:45:59 +0200 Subject: [PATCH] Add typed poll and reaction states to Android message models --- .../java/com/utun/chat/data/ChatRepository.kt | 4 +- .../main/java/com/utun/chat/data/Feedback.kt | 43 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 tools/chatgui-android/app/src/main/java/com/utun/chat/data/Feedback.kt diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/ChatRepository.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/ChatRepository.kt index 889a5178..0b7e8b87 100644 --- a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/ChatRepository.kt +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/ChatRepository.kt @@ -67,7 +67,8 @@ data class Message(val id: Long, val author: String, val text: String, val ts: L val imageWidth: Int = 0, val imageHeight: Int = 0, val videoWidth: Int = 0, val videoHeight: Int = 0, val videoDurationMs: Int = 0, val downloadState: String = "", /* "", "downloading", "downloaded", "error" */ - val deliveryStatusRes: Int = 0, val inviteGroupName: String? = null, val authorNodeId: Long = 0) + val deliveryStatusRes: Int = 0, val inviteGroupName: String? = null, val authorNodeId: Long = 0, + val messageId: String = "", val feedback: FeedbackState? = null) data class ChatMember(val nodeId: Long, val name: String, val online: Boolean = false, val isAdmin: Boolean = false, val isSuper: Boolean = false, @@ -382,6 +383,7 @@ class ChatRepository { list.add(Message( id = obj.getLong("id"), + messageId = obj.optString("messageId"), author = obj.getString("author"), authorNodeId = obj.optLong("authorNodeId"), text = displayText, diff --git a/tools/chatgui-android/app/src/main/java/com/utun/chat/data/Feedback.kt b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/Feedback.kt new file mode 100644 index 00000000..c903917d --- /dev/null +++ b/tools/chatgui-android/app/src/main/java/com/utun/chat/data/Feedback.kt @@ -0,0 +1,43 @@ +package com.utun.chat.data + +import org.json.JSONArray +import org.json.JSONObject + +data class ReactionState(val id: Int, val emoji: String, val count: Int = 0, val mine: Boolean = false) +data class PollOption(val id: Int, val text: String, val count: Int, val percent: Double) +data class PollResponse(val nodeId: Long, val choices: List, val custom: String) +data class PollState(val question: String, val multiple: Boolean, val allowCustom: Boolean, + val options: List, val voters: Int, val ver: Long, + val myChoices: List, val myCustom: String, val answers: List) +data class FeedbackState(val messageId: String, val pending: Boolean, val reactions: List, val poll: PollState?) + +private fun JSONArray?.choices(): List = if (this == null) emptyList() else (0 until length()).map { getInt(it) } + +/** Формат и правила задаёт ядро; интерфейс разбирает готовый снимок. */ +fun parseFeedback(json: JSONObject): FeedbackState { + val id = json.getString("message_id") + require(id.length == 64) { "Invalid message ID" } + val reactions = json.getJSONArray("reactions").let { values -> + (0 until values.length()).map { i -> values.getJSONObject(i).let { + ReactionState(it.getInt("id"), it.getString("emoji"), it.getInt("count"), it.getBoolean("mine")) + } } + } + val poll = json.optJSONObject("poll")?.let { value -> + val options = value.getJSONArray("options").let { values -> + (0 until values.length()).map { i -> values.getJSONObject(i).let { + PollOption(it.getInt("id"), it.getString("text"), it.getInt("count"), it.getDouble("percent")) + } } + } + val mine = value.getJSONObject("mine") + val answers = value.getJSONArray("answers").let { values -> + (0 until values.length()).map { i -> values.getJSONObject(i).let { row -> + val answer = row.getJSONObject("answer") + PollResponse(row.getString("node_id").removePrefix("0x").toLong(16), + answer.optJSONArray("choices").choices(), answer.optString("custom")) + } } + } + PollState(value.getString("question"), value.getString("mode") == "multiple", value.getBoolean("allow_custom_answer"), + options, value.getInt("voters"), value.getLong("ver"), mine.optJSONArray("choices").choices(), mine.optString("custom"), answers) + } + return FeedbackState(id, json.optBoolean("pending"), reactions, poll) +}