From aee829876fc0611bfc52352c1884a038c7be279b Mon Sep 17 00:00:00 2001 From: Eric85710 Date: Tue, 9 Jun 2026 15:56:32 +0800 Subject: [PATCH] dm-105 --- .../data/repository/dm/DM_Basic_Repository.kt | 31 ++++++++++++++++--- .../Detail/Message_Messaging_ViewModel.kt | 17 +++++++--- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/com/example/login_v3/data/repository/dm/DM_Basic_Repository.kt b/app/src/main/java/com/example/login_v3/data/repository/dm/DM_Basic_Repository.kt index 28b65ef..250d58e 100644 --- a/app/src/main/java/com/example/login_v3/data/repository/dm/DM_Basic_Repository.kt +++ b/app/src/main/java/com/example/login_v3/data/repository/dm/DM_Basic_Repository.kt @@ -374,18 +374,41 @@ class ChatRoomsRepository @Inject constructor( } } - // 刪除訊息 + + // 刪除訊息(樂觀更新版本) suspend fun deleteMessage(roomId: String, messageId: String): Result { + // 1. 先從本地資料庫撈出原本的訊息作為備份 + val backupEntity = try { + messageDao.getMessageById(messageId) + } catch (e: Exception) { + null // 如果本地查詢就失敗,雖然機率極低,但我們還是設為 null 防呆 + } + + // 2. 樂觀更新:立刻從本地砍掉,UI 的 Flow 會在「一瞬間」同步消失,體驗極佳 + try { + messageDao.deleteById(messageId) + } catch (e: Exception) { + // 如果本地 Room 刪除指令本身就失敗,直接回傳錯誤,不繼續往下戳 API + return Result.failure(e) + } + + // 3. 呼叫後端 API 進行實際刪除 return try { val response = api.deleteMessage(roomId, messageId) + if (response.isSuccessful) { - // 💡 既然刪除了,本地也一併砍掉,UI 就會立刻同步消失 - messageDao.deleteById(messageId) + // 真正成功了!因為本地已經提早刪除了,所以什麼都不用做,直接回傳 Unit Result.success(Unit) } else { - Result.failure(Exception("Delete failed")) + // 後端回報失敗(例如:沒權限、訊息已被他人刪除):把剛才備份的資料塞回去 + backupEntity?.let { messageDao.insertOrUpdate(it) } + + val errorMsg = response.errorBody()?.string() ?: "未知錯誤" + Result.failure(Exception("刪除失敗,錯誤碼: ${response.code()}, 訊息: $errorMsg")) } } catch (e: Exception) { + // 網路斷線、Timeout 或伺服器崩潰:一樣把備份資料塞回去,讓 UI 訊息彈回來 + backupEntity?.let { messageDao.insertOrUpdate(it) } Result.failure(e) } } diff --git a/app/src/main/java/com/example/login_v3/home/Message/ViewModel/Detail/Message_Messaging_ViewModel.kt b/app/src/main/java/com/example/login_v3/home/Message/ViewModel/Detail/Message_Messaging_ViewModel.kt index 4897745..682542a 100644 --- a/app/src/main/java/com/example/login_v3/home/Message/ViewModel/Detail/Message_Messaging_ViewModel.kt +++ b/app/src/main/java/com/example/login_v3/home/Message/ViewModel/Detail/Message_Messaging_ViewModel.kt @@ -182,21 +182,30 @@ class ChatViewModel @Inject constructor( // ===================================================================== // 💡 核心改造三:刪除訊息 // ===================================================================== + // ===================================================================== + // 💡 核心改造三:刪除訊息(配合樂觀更新優化) + // ===================================================================== fun deleteMessage(roomId: String, messageId: String) { + // 1. 🌟 樂觀更新的核心精神:立刻關閉長按選單(Action Menu) + // 因為訊息在下一行就會從資料庫消失,選單如果還開著會點不到東西或出錯 + clearActionMessage() + viewModelScope.launch { + // 2. 💡 注意:這裡「不要」在 UI 顯示阻擋式的 Loading 轉圈圈。 + // 這裡的 Loading 狀態只留在背景,或者用於特定的微小 UI 提示(例如標題列小藍點)。 _deleteMessageState.value = DeleteMessageState.Loading val result = repository.deleteMessage(roomId, messageId) result.onSuccess { _deleteMessageState.value = DeleteMessageState.Success - clearActionMessage() - - // 🌟 刪除成功!Repository 已經把本地 Room 的資料刪除 - // Flow 會自動發送全新的列表給 UI,這裡「完全不需要」再手動 map 改 content 了! + // 🌟 刪除成功!本地早就不見了,Flow 也早已更新,這裡乾淨俐落。 }.onFailure { error -> Log.e("ChatViewModel", "Delete message failed", error) _deleteMessageState.value = DeleteMessageState.Error(error.message ?: "刪除訊息失敗") + + // 3. 🌟 失敗處理:因為 Repository 把訊息彈回來了,我們可以讓 UI 知道 + // 這裡可以透過監聽 deleteMessageState 的 Error,在 Activity/Fragment/Compose 彈出 Toast 提示:「刪除失敗,請檢查網路」 } } }