feat: 优化消息列表和创建记忆屏幕逻辑
- 在MessageList组件中改进自动滚动逻辑,支持流式消息和时间分隔线的处理 - 在CreateMemoryScreen中移除冗余的agentResponse处理,直接使用historyMessages构建消息列表 - 在CreateMemoryViewModel中优化Agent回复处理,确保每条消息作为单独气泡显示并更新历史消息
This commit is contained in:
@@ -11,6 +11,7 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
@@ -36,17 +37,57 @@ fun MessageList(
|
||||
) {
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
// 自动滚动到底部 - 改进:流式输出时实时滚动
|
||||
LaunchedEffect(messages.size, isStreaming, streamingText) {
|
||||
val targetIndex = messages.size + if (isStreaming) 1 else 0
|
||||
if (targetIndex > 0) {
|
||||
// 流式输出时使用平滑滚动,其他情况使用动画滚动
|
||||
if (isStreaming && streamingText.isNotEmpty()) {
|
||||
// 流式输出时,每次文本更新都滚动到底部
|
||||
kotlinx.coroutines.delay(50) // 短暂延迟确保内容已渲染
|
||||
listState.animateScrollToItem(targetIndex)
|
||||
// 计算实际的列表项数量(考虑分割消息和附加项)
|
||||
val estimatedItemCount = remember(messages, isStreaming, streamingText, isTyping) {
|
||||
var count = 0
|
||||
var lastTimestamp: Long? = null
|
||||
|
||||
messages.forEachIndexed { index, message ->
|
||||
// 时间分隔线
|
||||
if (index > 0 && lastTimestamp != null && (message.timestamp - lastTimestamp!!) > 300000) {
|
||||
count++
|
||||
}
|
||||
|
||||
// 消息气泡(考虑分割)
|
||||
if (message.senderType == "assistant") {
|
||||
val parts = message.content.split("[SPLIT]").filter { it.trim().isNotEmpty() }
|
||||
count += if (parts.size > 1) parts.size else 1
|
||||
} else {
|
||||
listState.animateScrollToItem(targetIndex)
|
||||
count++
|
||||
}
|
||||
|
||||
lastTimestamp = message.timestamp
|
||||
}
|
||||
|
||||
// 流式消息
|
||||
if (isStreaming) {
|
||||
val streamingParts = streamingText.split("[SPLIT]").filter { it.trim().isNotEmpty() }
|
||||
count += if (streamingParts.size > 1) streamingParts.size else 1
|
||||
}
|
||||
|
||||
// 输入指示器
|
||||
if (isTyping || (isStreaming && streamingText.isEmpty())) {
|
||||
count++
|
||||
}
|
||||
|
||||
count
|
||||
}
|
||||
|
||||
// 自动滚动到底部 - 当消息变化或流式内容更新时滚动
|
||||
LaunchedEffect(messages.size, messages.lastOrNull()?.id, isStreaming, streamingText, isTyping) {
|
||||
// 短暂延迟确保内容已渲染
|
||||
delay(100)
|
||||
|
||||
// 滚动到最后一项
|
||||
if (estimatedItemCount > 0) {
|
||||
try {
|
||||
listState.animateScrollToItem(estimatedItemCount - 1)
|
||||
} catch (e: Exception) {
|
||||
// 如果索引超出范围,尝试滚动到实际的最后一项
|
||||
val actualCount = listState.layoutInfo.totalItemsCount
|
||||
if (actualCount > 0) {
|
||||
listState.animateScrollToItem(actualCount - 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ fun CreateMemoryScreen(
|
||||
) {
|
||||
val isRecording by viewModel.isRecording.collectAsState()
|
||||
val transcript by viewModel.transcript.collectAsState()
|
||||
val agentResponse by viewModel.agentResponse.collectAsState()
|
||||
val connectionStatus by viewModel.connectionStatus.collectAsState()
|
||||
val userMessages by viewModel.userMessages.collectAsState()
|
||||
val historyMessages by viewModel.historyMessages.collectAsState()
|
||||
@@ -69,7 +68,8 @@ fun CreateMemoryScreen(
|
||||
val keyboardController = LocalSoftwareKeyboardController.current
|
||||
|
||||
// 构建消息列表(包含历史消息和当前消息)
|
||||
val messages = remember(historyMessages, userMessages, agentResponse) {
|
||||
// 注意:AI回复已经直接添加到 historyMessages 中,不需要额外处理 agentResponse
|
||||
val messages = remember(historyMessages, userMessages) {
|
||||
buildList {
|
||||
// 先添加历史消息
|
||||
addAll(historyMessages)
|
||||
@@ -88,18 +88,6 @@ fun CreateMemoryScreen(
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
// 添加AI回复(如果不在历史消息中)
|
||||
if (agentResponse.isNotEmpty() && !historyMessages.any { it.content == agentResponse && it.senderType == "assistant" }) {
|
||||
add(MessageDto(
|
||||
id = "ai_response_${historyMessages.size + userMessages.size}",
|
||||
conversationId = conversationId,
|
||||
content = agentResponse,
|
||||
senderType = "assistant",
|
||||
timestamp = System.currentTimeMillis(),
|
||||
messageType = "text"
|
||||
))
|
||||
}
|
||||
}.sortedBy { it.timestamp } // 按时间排序
|
||||
}
|
||||
|
||||
|
||||
@@ -294,7 +294,7 @@ class CreateMemoryViewModel(
|
||||
transcript.value = message.getString("text") ?: ""
|
||||
}
|
||||
MessageType.agent_response -> {
|
||||
// 处理Agent回复(可能有多条消息)
|
||||
// 处理Agent回复(可能有多条消息,每条作为单独气泡显示)
|
||||
val text = message.getString("text") ?: ""
|
||||
val index = message.getInt("index") ?: 0
|
||||
val total = message.getInt("total") ?: 1
|
||||
@@ -302,25 +302,30 @@ class CreateMemoryViewModel(
|
||||
// 收到第一条回复时,隐藏打字指示器
|
||||
if (index == 0) {
|
||||
isTyping.value = false
|
||||
}
|
||||
|
||||
// 每条消息立即作为单独的气泡添加到历史消息
|
||||
conversationId.value?.let { id ->
|
||||
val aiMessage = MessageDto(
|
||||
id = "ai_${System.currentTimeMillis()}_$index",
|
||||
conversationId = id,
|
||||
content = text,
|
||||
senderType = "assistant",
|
||||
timestamp = System.currentTimeMillis(),
|
||||
messageType = "text"
|
||||
)
|
||||
historyMessages.value = historyMessages.value + aiMessage
|
||||
}
|
||||
|
||||
// 更新 agentResponse(用于显示最新回复)
|
||||
if (index == 0) {
|
||||
agentResponse.value = text
|
||||
} else {
|
||||
// 追加后续消息
|
||||
agentResponse.value += "\n\n$text"
|
||||
}
|
||||
|
||||
// 如果是最后一条消息,结束流式状态并添加到历史消息
|
||||
// 如果是最后一条消息,结束流式状态
|
||||
if (index >= total - 1) {
|
||||
conversationId.value?.let { id ->
|
||||
val aiMessage = MessageDto(
|
||||
id = "ai_${System.currentTimeMillis()}",
|
||||
conversationId = id,
|
||||
content = agentResponse.value,
|
||||
senderType = "assistant",
|
||||
timestamp = System.currentTimeMillis(),
|
||||
messageType = "text"
|
||||
)
|
||||
historyMessages.value = historyMessages.value + aiMessage
|
||||
}
|
||||
isStreaming.value = false
|
||||
streamingText.value = ""
|
||||
isTyping.value = false
|
||||
|
||||
Reference in New Issue
Block a user