Document feedback snapshots and poll UI submission contracts
This commit is contained in:
1 parent
37515df8c1
commit
a1ce486e89
2 files changed
+14
-1
No files matched your search
@@ -3,6 +3,9 @@ package com.utun.chat.data
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
|
||||
/* Модели снимка chat_feedback_snapshot и подготовка форм опроса для общего командного API core.
|
||||
* Сетевой формат/подписи/подсчёт голосов остаются в C; здесь только JSON-разбор и проверка готового ввода UI. */
|
||||
|
||||
/** Элемент каталога/снимка реакций: id=0..15 общего каталога, count — участники, mine — выбор текущего узла.
|
||||
* Участник хранит одну реакцию; нулевой count/mine=false также используются элементами каталога UI. */
|
||||
data class ReactionState(val id: Int, val emoji: String, val count: Int = 0, val mine: Boolean = false)
|
||||
@@ -83,7 +86,7 @@ fun parseFeedback(json: JSONObject): FeedbackState {
|
||||
(0 until values.length()).map { i -> values.getJSONObject(i).let { row ->
|
||||
val answer = row.getJSONObject("answer")
|
||||
val node = row.getString("node_id").removePrefix("0x").toLong(16)
|
||||
require(node > 0) { "Invalid feedback node ID" }
|
||||
require(node > 0) { "Invalid feedback node ID=$node" }
|
||||
PollResponse(node,
|
||||
answer.optJSONArray("choices").choices(), answer.optString("custom"))
|
||||
} }
|
||||
|
||||
@@ -23,6 +23,9 @@ import androidx.compose.ui.unit.dp
|
||||
import com.utun.chat.R
|
||||
import com.utun.chat.data.*
|
||||
|
||||
/** Форма нового опроса: пункты разделены строками, тексты trim-ятся, лимиты проверяются в UTF-8 байтах.
|
||||
* Подтверждение вызывает onCreate(question, options, multiple, allowCustom), затем закрывает форму.
|
||||
* Публикация выполняется core асинхронно; результат/ошибку показывает владелец callback, оптимистического poll здесь нет. */
|
||||
@Composable
|
||||
fun CreatePollDialog(onDismiss: () -> Unit, onCreate: (String, List<String>, Boolean, Boolean) -> Unit) {
|
||||
var question by rememberSaveable { mutableStateOf("") }
|
||||
@@ -52,6 +55,8 @@ fun CreatePollDialog(onDismiss: () -> Unit, onCreate: (String, List<String>, Boo
|
||||
dismissButton = { TextButton(onClick = onDismiss) { Text(stringResource(R.string.cancel)) } })
|
||||
}
|
||||
|
||||
/** Опрос из message.feedback: результаты и проценты берутся из core, без локального пересчёта.
|
||||
* Пока снимок poll отсутствует, показывается placeholder; нажатие с готовым poll открывает выбор/ответы участников. */
|
||||
@Composable
|
||||
fun PollMessageBubble(message: Message, onVote: (Message, List<Int>, String) -> Unit, authorName: (Long) -> String,
|
||||
footer: @Composable () -> Unit = {}, header: @Composable () -> Unit = {}) {
|
||||
@@ -88,6 +93,9 @@ fun PollMessageBubble(message: Message, onVote: (Message, List<Int>, String) ->
|
||||
if (showVote && poll != null) PollVoteDialog(message, poll, { showVote = false }, onVote, authorName)
|
||||
}
|
||||
|
||||
/** Локальный draft сохраняется по messageId, новые счётчики poll не перезаписывают редактируемый выбор.
|
||||
* Single выбирает пункт либо свой текст, multiple позволяет оба. preparePollVote исключает неактивные поля
|
||||
* и проверяет публикуемые байты; пустой выбор/текст снимает голос. onVote ставит команду, затем диалог закрывается. */
|
||||
@Composable
|
||||
private fun PollVoteDialog(message: Message, poll: PollState, onDismiss: () -> Unit,
|
||||
onVote: (Message, List<Int>, String) -> Unit, authorName: (Long) -> String) {
|
||||
@@ -135,6 +143,8 @@ private fun PollVoteDialog(message: Message, poll: PollState, onDismiss: () -> U
|
||||
} })
|
||||
}
|
||||
|
||||
/** Каталог эмодзи и реакции снимка core; нажатие на текущую реакцию переключает её через onReact.
|
||||
* Один участник выбирает одну реакцию, null очищает выбор; новый mine/count приходит в следующем снимке. */
|
||||
@OptIn(ExperimentalLayoutApi::class, ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MessageReactions(message: Message, catalog: List<ReactionState>, onReact: (Message, Int?) -> Unit) {
|
||||
|
||||
Reference in new issue
Block a user