This commit is contained in:
Eric85710 2026-06-29 14:59:07 +08:00
parent 1d92471ba1
commit 0e6999f7dd

View file

@ -7,12 +7,10 @@ import android.net.Uri
import android.os.Environment import android.os.Environment
import android.provider.OpenableColumns import android.provider.OpenableColumns
import android.util.Log import android.util.Log
import androidx.datastore.core.use
import com.example.login_v3.data.api.TecnologiaApi import com.example.login_v3.data.api.TecnologiaApi
import com.example.login_v3.data.api.api_class.ChatRoom import com.example.login_v3.data.api.api_class.ChatRoom
import com.example.login_v3.data.api.api_class.Message import com.example.login_v3.data.api.api_class.Message
import com.example.login_v3.data.api.api_class.MessageReactionUsersResponse import com.example.login_v3.data.api.api_class.Reaction
import com.example.login_v3.data.api.api_class.MessageResponse
import com.example.login_v3.data.api.api_class.RoomListResponse import com.example.login_v3.data.api.api_class.RoomListResponse
import com.example.login_v3.data.api.api_class.SendMessageRequest import com.example.login_v3.data.api.api_class.SendMessageRequest
import com.example.login_v3.data.di.ChatWebSocketManager import com.example.login_v3.data.di.ChatWebSocketManager
@ -20,6 +18,8 @@ import com.example.login_v3.data.repository.basic.TokenManager
import com.example.login_v3.home.Message.ViewModel.Detail.MessageDao import com.example.login_v3.home.Message.ViewModel.Detail.MessageDao
import com.example.login_v3.home.Message.ViewModel.Detail.MessageEntity import com.example.login_v3.home.Message.ViewModel.Detail.MessageEntity
import com.example.login_v3.home.Message.ViewModel.Detail.MessageStatus import com.example.login_v3.home.Message.ViewModel.Detail.MessageStatus
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import com.squareup.moshi.Moshi import com.squareup.moshi.Moshi
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -44,7 +44,28 @@ import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import kotlin.io.use import kotlin.io.use
import kotlin.jvm.java import kotlin.jvm.java
import kotlin.use
@JsonClass(generateAdapter = true)
data class WebSocketEventResponse(
@Json(name = "type") val type: String,
@Json(name = "payload") val payload: WebSocketMessagePayload? // 🎯 直接強型別對齊
)
// 這是專門給 Socket payload 用的臨時結構,用來解決 room_id 和 chat_room_id 欄位不一致的問題
@JsonClass(generateAdapter = true)
data class WebSocketMessagePayload(
@Json(name = "id") val id: String,
@Json(name = "room_id") val roomId: String, // 🎯 完美吃下後端的 room_id
@Json(name = "sender_id") val senderId: String,
@Json(name = "content") val content: String,
@Json(name = "type") val type: String,
@Json(name = "created_at") val createdAt: String,
@Json(name = "is_edited") val isEdited: Boolean,
@Json(name = "is_deleted") val isDeleted: Boolean,
@Json(name = "reply_to_id") val replyToId: String? = null,
@Json(name = "reactions") val reactions: List<Reaction>? = emptyList()
)
@Singleton @Singleton
class ChatRoomsRepository @Inject constructor( class ChatRoomsRepository @Inject constructor(
@ -68,27 +89,35 @@ class ChatRoomsRepository @Inject constructor(
repositoryScope.launch { repositoryScope.launch {
webSocketManager.incomingMessages.collect { jsonString -> webSocketManager.incomingMessages.collect { jsonString ->
try { try {
// 1. 🎯 精準對齊:使用你的 Message::class.java 進行解析 // 1. 一口氣全解析完畢(包含外殼與內部的 payload)
val networkMessage = moshi.adapter(Message::class.java).fromJson(jsonString) val event = moshi.adapter(WebSocketEventResponse::class.java).fromJson(jsonString)
if (networkMessage != null) { if (event != null && event.type == "new_message" && event.payload != null) {
// 2. 將 Message 轉換為你的 MessageEntity val payload = event.payload
// 2. 直接拿解析好的強型別物件轉換為 MessageEntity
val entity = MessageEntity( val entity = MessageEntity(
id = networkMessage.id, id = payload.id,
chatRoomId = networkMessage.chatRoomId, // 👈 完美對齊你的欄位名稱 chatRoomId = payload.roomId, // 🎯 這裡百分之百能拿到後端的 room_id
senderId = networkMessage.senderId, senderId = payload.senderId,
content = networkMessage.content, content = payload.content,
type = networkMessage.type, type = payload.type,
createdAt = networkMessage.createdAt, createdAt = payload.createdAt,
isEdited = networkMessage.isEdited, isEdited = payload.isEdited,
isDeleted = networkMessage.isDeleted, isDeleted = payload.isDeleted,
replyToId = networkMessage.replyToId, replyToId = payload.replyToId,
status = MessageStatus.SUCCESS, // WebSocket 推過來的必定是成功發送的 status = MessageStatus.SUCCESS,
reactions = networkMessage.reactions reactions = payload.reactions
) )
// 3. 寫入本地資料庫,觸發 UI Flow 更新 // 3. 寫入本地資料庫
messageDao.insertOrUpdate(entity) messageDao.insertOrUpdate(entity)
Log.d("ChatRoomsRepository", "WebSocket 訊息成功寫入資料庫: ${entity.content}")
} else if (event?.type == "dm:typing") {
// 💡 如果未來想要解析 typing 狀態,你可以另外把 event.payload 轉成 Map 來拿資料,因為 typing 的 payload 結構不同
// 由於我們把外層改成了 WebSocketMessagePayload?,若 type 是 dm:typing,外層依然可以解析,但 payload 會是 null 或解析失敗
// 如果你同時想處理 typing,可以用 `moshi.adapter(Map::class.java)` 單獨處理該事件。
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e("ChatRoomsRepository", "解析或寫入 WebSocket 即時訊息失敗", e) Log.e("ChatRoomsRepository", "解析或寫入 WebSocket 即時訊息失敗", e)