This commit is contained in:
Eric85710 2026-06-09 16:07:42 +08:00
parent aee829876f
commit d22213b9ac

View file

@ -48,6 +48,7 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -79,6 +80,7 @@ import com.example.login_v3.home.Message.UI.Detail.Message_Messaging_detail.Mess
import com.example.login_v3.home.Message.UI.Detail.Message_Messaging_detail.ReactionRow import com.example.login_v3.home.Message.UI.Detail.Message_Messaging_detail.ReactionRow
import com.example.login_v3.home.Message.UI.Detail.Message_Messaging_detail.UserAvatar import com.example.login_v3.home.Message.UI.Detail.Message_Messaging_detail.UserAvatar
import com.example.login_v3.home.Message.ViewModel.Detail.ChatViewModel import com.example.login_v3.home.Message.ViewModel.Detail.ChatViewModel
import com.example.login_v3.home.Message.ViewModel.Detail.DeleteMessageState
import com.example.login_v3.home.Message.ViewModel.Detail.MessageStatus import com.example.login_v3.home.Message.ViewModel.Detail.MessageStatus
import com.example.login_v3.home.Message.ViewModel.Detail.MessagesUiState import com.example.login_v3.home.Message.ViewModel.Detail.MessagesUiState
import com.example.login_v3.home.Message.ViewModel.Detail.SendMessageState import com.example.login_v3.home.Message.ViewModel.Detail.SendMessageState
@ -154,6 +156,16 @@ fun MessageMessaging(
} }
} }
val deleteState by viewModel.deleteMessageState.collectAsState()
LaunchedEffect(deleteState) {
if (deleteState is DeleteMessageState.Error) {
// 當背景 API 刪除失敗、訊息彈回來時,給使用者一個提示
val errorMsg = (deleteState as DeleteMessageState.Error).message
Toast.makeText(context, errorMsg, Toast.LENGTH_SHORT).show()
}
}
// 根據目前的 uiState 來動態決定 TopBar 要顯示什麼文字 // 根據目前的 uiState 來動態決定 TopBar 要顯示什麼文字
val topBarTitle = when (val state = uiState) { val topBarTitle = when (val state = uiState) {
is MessagesUiState.Success -> state.roomTitle is MessagesUiState.Success -> state.roomTitle
@ -220,7 +232,7 @@ fun MessageMessaging(
replyingMessage = replyingMessage, replyingMessage = replyingMessage,
onCancelReply = { viewModel.setReplyingMessage(null) }, onCancelReply = { viewModel.setReplyingMessage(null) },
onSendClick = { content -> viewModel.sendMessage(roomId, content) }, onSendClick = { content -> viewModel.sendMessage(roomId, content) },
onImageSelected = { uri -> viewModel.uploadAttachment(roomId, uri) } onImageSelected = { uri -> viewModel.uploadAttachment(roomId, uri) },
) )
} }
@ -238,7 +250,14 @@ fun MessageMessaging(
viewModel.clearActionMessage() viewModel.clearActionMessage()
}, },
onDeleteClick = { msg -> onDeleteClick = { msg ->
viewModel.deleteMessage(roomId, msg.id) // 🌟 核心防呆:如果訊息還在發送中(SENDING),禁止點擊刪除,避免 tempId 送給後端造成 404
if (msg.status == MessageStatus.SENDING) {
// 可以選擇在這裡彈出一個短暫的 Toast 提示使用者
Toast.makeText(context, "訊息發送中,請稍後再操作", Toast.LENGTH_SHORT).show()
} else {
// 正常訊息,呼叫樂觀更新刪除
viewModel.deleteMessage(roomId, msg.id)
}
} }
) )
} }
@ -334,20 +353,25 @@ fun MessageList(
onRowClick: (Message) -> Unit, onRowClick: (Message) -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
// 🌟 核心修正:過濾掉所有已被刪除的訊息,讓它們直接不參與渲染
val visibleMessages = remember(messages) {
messages.filter { !it.isDeleted }
}
LazyColumn( LazyColumn(
modifier = modifier.fillMaxSize(), modifier = modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp), contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp),
reverseLayout = true // ✨ 保持 true,讓畫面底部作為起點,並預設滾動到最下方 reverseLayout = true // ✨ 保持 true,讓畫面底部作為起點,並預設滾動到最下方
) { ) {
items(messages) { message -> // ✨ 直接帶入原始的 messages // 💡 改用過濾後的全新列表 visibleMessages
items(visibleMessages) { message ->
val isMe = message.senderId == currentUserId val isMe = message.senderId == currentUserId
MessageRow( MessageRow(
message = message, message = message,
partnerAvatarUrl = partnerAvatarUrl, partnerAvatarUrl = partnerAvatarUrl,
partnerDisplayName = partnerDisplayName, partnerDisplayName = partnerDisplayName,
isMe = isMe, isMe = isMe,
// 💡 判斷:不論是正在選 Emoji 還是正在開啟動作選單,只要命中就判定為高亮
isHighlight = message.id == activeEmojiMessageId || message.id == activeActionMessageId, isHighlight = message.id == activeEmojiMessageId || message.id == activeActionMessageId,
currentUserId = currentUserId, currentUserId = currentUserId,
onReplyClick = onReplyClick, onReplyClick = onReplyClick,