This commit is contained in:
Eric85710 2026-06-16 16:30:34 +08:00
parent 944d91e3d9
commit e98eb1706d
3 changed files with 73 additions and 59 deletions

View file

@ -112,8 +112,18 @@ data class Message(
// 🌟 核心新增:讓 UI 可以辨識訊息發送狀態!
@kotlin.jvm.Transient // 👈 告訴 Moshi 這個欄位不需要解析 JSON,這是我們本地維護的
val status: MessageStatus = MessageStatus.SUCCESS
)
val status: MessageStatus = MessageStatus.SUCCESS,
@kotlin.jvm.Transient
var callLogInfo: CallLogInfo? = null
){
init {
if (type == "system" || type.startsWith("call")) {
callLogInfo = CallLogInfo.parseContent(content)
}
}
}
//send message
@JsonClass(generateAdapter = true)
@ -134,26 +144,24 @@ data class MessageReactionUsersResponse(
//voice call
@JsonClass(generateAdapter = true)
data class CallLogInfo(
@Json(name = "type") val type: String, // 例如: call, call_log, call_missed, call_rejected 等
@Json(name = "initiator_id") val initiator_id: String,
@Json(name = "has_video") val has_video: Boolean
@Json(name = "type") val type: String, // 例如: call_missed, call_rejected
@Json(name = "initiator_id") val initiatorId: String, // 順便改成 Kotlin 的駝峰命名規範
@Json(name = "has_video") val hasVideo: Boolean
) {
companion object {
private val moshi = Moshi.Builder()
.addLast(KotlinJsonAdapterFactory())
.addLast(com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory())
.build()
private val adapter = moshi.adapter(CallLogInfo::class.java)
fun parse(message: Message): CallLogInfo? {
// 🌟 修正:只要是 call 開頭的 type 都允許解析,避免 call_missed 被擋掉
if (message.type == null || !message.type.startsWith("call")) return null
val content = message.content?.trim() ?: return null
if (!content.startsWith("{") || !content.endsWith("}")) return null
// 🌟 改成只單純解析 String
fun parseContent(content: String?): CallLogInfo? {
val json = content?.trim() ?: return null
if (!json.startsWith("{") || !json.endsWith("}")) return null
return try {
adapter.fromJson(content)
adapter.fromJson(json)
} catch (e: Exception) {
android.util.Log.e("CallLogInfo", "解析通話 JSON 失敗: ${e.message}")
null

View file

@ -8,12 +8,14 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Call
import androidx.compose.material.icons.filled.CallMissed
import androidx.compose.material.icons.filled.VideoCall
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
@ -27,13 +29,13 @@ import com.example.login_v3.data.api.api_class.CallLogInfo
@Composable
fun CallLogBubble(
callLog: CallLogInfo,
isMe: Boolean,
isMe: Boolean, // 這一則訊息是不是「我」發送的
currentUserId: String,
partnerDisplayName: String,
modifier: Modifier = Modifier
) {
// 判斷這通電話是不是「我」撥打的
val isIInitiated = callLog.initiator_id == currentUserId
// 🎯 改造 1:配合方案 A 的變數名稱修正 (initiator_id -> initiatorId)
val isIInitiated = callLog.initiatorId == currentUserId
// 根據通話狀態決定圖示、顏色與顯示主標題
val (icon, iconColor, titleText) = when (callLog.type) {
@ -45,26 +47,31 @@ fun CallLogBubble(
)
}
else -> {
val callTypeStr = if (callLog.has_video) "視訊通話" else "語音通話"
// 🎯 改造 2:配合方案 A 的變數名稱修正 (has_video -> hasVideo)
val callTypeStr = if (callLog.hasVideo) "視訊通話" else "語音通話"
Triple(
if (callLog.has_video) Icons.Default.VideoCall else Icons.Default.Call,
if (callLog.hasVideo) Icons.Default.VideoCall else Icons.Default.Call,
if (isMe) Color(0xFF4CAF50) else Color(0xFF1976D2),
if (isIInitiated) "撥出$callTypeStr" else "已接聽$callTypeStr"
)
}
}
// 🌟 新增:來電顯示副標題邏輯
val callTypeLabel = if (callLog.has_video) "視訊" else "語音"
// 來電顯示副標題邏輯
val callTypeLabel = if (callLog.hasVideo) "視訊" else "語音"
val subText = when {
// 我撥出去的 -> 顯示「撥給 某某」
isIInitiated -> "$callTypeLabel • 撥給 $partnerDisplayName"
// 對方撥進來的 -> 顯示「來自 某某」
else -> "$callTypeLabel • 來自 $partnerDisplayName"
}
// 🎯 改造 3:外層加上氣泡背景與剪裁,讓它看起來更像聊天氣泡
Surface(
modifier = modifier.padding(vertical = 4.dp),
shape = RoundedCornerShape(12.dp),
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f) // 給予淡淡的灰色氣泡背景
) {
Row(
modifier = modifier.padding(vertical = 2.dp),
modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
@ -93,7 +100,6 @@ fun CallLogBubble(
)
)
Text(
// 🌟 改用動態組合的來電顯示文字
text = subText,
style = MaterialTheme.typography.bodySmall.copy(
fontSize = 12.sp,
@ -103,3 +109,4 @@ fun CallLogBubble(
}
}
}
}

View file

@ -517,7 +517,7 @@ fun MessageRow(
val isImage = isAttachmentImage || isLegacyImage
val rawContent = message.content ?: ""
val callLog = remember(message.id, message.content) { CallLogInfo.parse(message) }
val callLog = message.callLogInfo
val finalImageModel = remember(message) {
val rawPath = if (isAttachmentImage && !message.attachment?.filename.isNullOrBlank()) {
@ -625,17 +625,16 @@ fun MessageRow(
)
}
} else if (callLog != null) {
// 🌟 通話紀錄渲染
// 🌟 🎯 改造 2:通話紀錄渲染優化
// 移除原本包裹在外層、帶有背景與 Padding 的 Box,改由長按/點擊事件的容器包住 CallLogBubble
Box(
modifier = Modifier
.widthIn(max = 280.dp)
.clip(RoundedCornerShape(12.dp))
// 讓通話紀錄一樣能響應點擊與長按(如刪除、回覆)
.combinedClickable(
onLongClick = { onReplyClick(message) },
onClick = { onRowClick(message) }
)
.background(color = finalBubbleColor)
.padding(horizontal = 14.dp, vertical = 10.dp)
) {
CallLogBubble(
callLog = callLog,