diff --git a/app/src/main/java/com/example/login_v3/data/api/api_class/DM_Basic.kt b/app/src/main/java/com/example/login_v3/data/api/api_class/DM_Basic.kt index 1e520a4..fa239ae 100644 --- a/app/src/main/java/com/example/login_v3/data/api/api_class/DM_Basic.kt +++ b/app/src/main/java/com/example/login_v3/data/api/api_class/DM_Basic.kt @@ -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 diff --git a/app/src/main/java/com/example/login_v3/home/Message/UI/Detail/Message_Component/CallLogBubble.kt b/app/src/main/java/com/example/login_v3/home/Message/UI/Detail/Message_Component/CallLogBubble.kt index 91abcae..bb4b8a8 100644 --- a/app/src/main/java/com/example/login_v3/home/Message/UI/Detail/Message_Component/CallLogBubble.kt +++ b/app/src/main/java/com/example/login_v3/home/Message/UI/Detail/Message_Component/CallLogBubble.kt @@ -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,61 +47,66 @@ 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" } - Row( - modifier = modifier.padding(vertical = 2.dp), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.spacedBy(12.dp) + // 🎯 改造 3:外層加上氣泡背景與剪裁,讓它看起來更像聊天氣泡 + Surface( + modifier = modifier.padding(vertical = 4.dp), + shape = RoundedCornerShape(12.dp), + color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f) // 給予淡淡的灰色氣泡背景 ) { - // 左側:圓形背景與狀態圖示 - Box( - modifier = Modifier - .size(38.dp) - .background(iconColor.copy(alpha = 0.12f), shape = CircleShape), - contentAlignment = Alignment.Center + Row( + modifier = Modifier.padding(horizontal = 12.dp, vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(12.dp) ) { - Icon( - imageVector = icon, - contentDescription = titleText, - tint = iconColor, - modifier = Modifier.size(22.dp) - ) - } + // 左側:圓形背景與狀態圖示 + Box( + modifier = Modifier + .size(38.dp) + .background(iconColor.copy(alpha = 0.12f), shape = CircleShape), + contentAlignment = Alignment.Center + ) { + Icon( + imageVector = icon, + contentDescription = titleText, + tint = iconColor, + modifier = Modifier.size(22.dp) + ) + } - // 右側:通話狀態與來電顯示文字 - Column { - Text( - text = titleText, - style = MaterialTheme.typography.bodyMedium.copy( - fontSize = 15.sp, - fontWeight = FontWeight.SemiBold + // 右側:通話狀態與來電顯示文字 + Column { + Text( + text = titleText, + style = MaterialTheme.typography.bodyMedium.copy( + fontSize = 15.sp, + fontWeight = FontWeight.SemiBold + ) ) - ) - Text( - // 🌟 改用動態組合的來電顯示文字 - text = subText, - style = MaterialTheme.typography.bodySmall.copy( - fontSize = 12.sp, - color = Color.Gray + Text( + text = subText, + style = MaterialTheme.typography.bodySmall.copy( + fontSize = 12.sp, + color = Color.Gray + ) ) - ) + } } } } \ No newline at end of file diff --git a/app/src/main/java/com/example/login_v3/home/Message/UI/Detail/Message_Messaging.kt b/app/src/main/java/com/example/login_v3/home/Message/UI/Detail/Message_Messaging.kt index e5684f4..956d4fe 100644 --- a/app/src/main/java/com/example/login_v3/home/Message/UI/Detail/Message_Messaging.kt +++ b/app/src/main/java/com/example/login_v3/home/Message/UI/Detail/Message_Messaging.kt @@ -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,