dm-19
This commit is contained in:
parent
5ba40c2b3b
commit
9644fce272
5 changed files with 125 additions and 45 deletions
|
|
@ -1,6 +1,7 @@
|
|||
package com.example.login_v3.data.repository.dm
|
||||
|
||||
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.MessageResponse
|
||||
import com.example.login_v3.data.api.api_class.RoomListResponse
|
||||
import javax.inject.Inject
|
||||
|
|
@ -48,4 +49,19 @@ class ChatRoomsRepository @Inject constructor(
|
|||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
|
||||
// 在 ChatRoomsRepository 裡面加入:
|
||||
suspend fun getChatRoom(roomId: String): Result<ChatRoom?> {
|
||||
return try {
|
||||
// 呼叫你原本就有的 fetchRooms() 獲取所有房間
|
||||
val responseResult = fetchRooms()
|
||||
|
||||
responseResult.map { roomListResponse ->
|
||||
// 從列表裡面找出 roomId 相同的那間房間
|
||||
roomListResponse.rooms.find { it.roomId == roomId }
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Result.failure(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,12 +10,18 @@ import androidx.compose.foundation.layout.Row
|
|||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.ArrowBack
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
|
|
@ -30,18 +36,17 @@ import androidx.compose.ui.graphics.Color
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavController
|
||||
import com.example.login_v3.data.api.api_class.Message
|
||||
import com.example.login_v3.home.Message.ViewModel.Detail.ChatViewModel
|
||||
import com.example.login_v3.home.Message.ViewModel.Detail.MessagesUiState
|
||||
import com.example.login_v3.home.Message.ViewModel.UserStatus
|
||||
import com.example.login_v3.navigation.Screen
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun MessageMessaging(
|
||||
roomId: String,
|
||||
roomName: String,
|
||||
navController: NavController,
|
||||
onBackClick: () -> Unit,
|
||||
viewModel: ChatViewModel = hiltViewModel()
|
||||
|
|
@ -51,12 +56,47 @@ fun MessageMessaging(
|
|||
LaunchedEffect(roomId) {
|
||||
viewModel.loadMessages(roomId)
|
||||
}
|
||||
// 根據目前的 uiState 來動態決定 TopBar 要顯示什麼文字
|
||||
val topBarTitle = when (val state = uiState) {
|
||||
is MessagesUiState.Success -> state.roomTitle
|
||||
is MessagesUiState.Error -> "載入失敗"
|
||||
is MessagesUiState.Loading -> "載入中..."
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
//need to be sender id
|
||||
title = { Text(roomName) },
|
||||
title = {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(text = topBarTitle)
|
||||
|
||||
// ✨ 如果是 Success 狀態,且對方是在線狀態(ONLINE),就顯示綠色小圓點
|
||||
if (uiState is MessagesUiState.Success) {
|
||||
val successState = uiState as MessagesUiState.Success
|
||||
if (successState.partnerStatus == UserStatus.ONLINE) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(10.dp)
|
||||
.background(
|
||||
color = UserStatus.ONLINE.color,
|
||||
shape = CircleShape
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = onBackClick) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.ArrowBack,
|
||||
contentDescription = "返回"
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
|
|
@ -66,6 +106,7 @@ fun MessageMessaging(
|
|||
.padding(innerPadding),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
// 根據不同的狀態渲染主畫面內容
|
||||
when (val state = uiState) {
|
||||
is MessagesUiState.Loading -> {
|
||||
CircularProgressIndicator()
|
||||
|
|
@ -74,6 +115,7 @@ fun MessageMessaging(
|
|||
if (state.messages.isEmpty()) {
|
||||
Text("目前沒有新訊息")
|
||||
} else {
|
||||
// ⚠️ 記得把原來的 state.messages 傳進去
|
||||
MessageList(messages = state.messages)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ import com.example.login_v3.home.Message.UI.Detail.Message_add_contact
|
|||
import com.example.login_v3.home.Message.UI.Detail.Message_contact_list
|
||||
import com.example.login_v3.home.Message.UI.Detail.Scan_QRcode
|
||||
import com.example.login_v3.home.Message.ViewModel.ChatRoomsViewModel
|
||||
import com.example.login_v3.home.Message.ViewModel.UserStatus
|
||||
import com.example.login_v3.navigation.BottomBarViewModel
|
||||
|
||||
|
||||
|
|
@ -87,9 +88,9 @@ sealed class Screen(val route: String) {
|
|||
object CreateContact : Screen("create_contact")
|
||||
object FriendsList : Screen("friends_list")
|
||||
object ScanQRcode : Screen("scan_QRcode")
|
||||
object MessageMessaging : Screen("message_messaging?roomId={roomId}&roomName={roomName}") {
|
||||
fun createRoute(roomId: String, roomName: String): String {
|
||||
return "message_messaging?roomId=$roomId&roomName=$roomName"
|
||||
object MessageMessaging : Screen("message_messaging?roomId={roomId}") {
|
||||
fun createRoute(roomId: String): String {
|
||||
return "message_messaging?roomId=$roomId"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -108,9 +109,9 @@ fun Tg_Message(
|
|||
composable(Screen.Messages.route) {
|
||||
Loaded_Tg_Message(
|
||||
navController = navController,
|
||||
// 🚀 完美接起傳出來的兩個參數,不再發生 No value passed 的狀況!
|
||||
onRoomClick = { roomId, roomName ->
|
||||
val route = Screen.MessageMessaging.createRoute(roomId, roomName)
|
||||
// ✨ 2. 這裡的點擊事件如果原本有傳 roomName,現在直接忽略,只傳 roomId 給路由
|
||||
onRoomClick = { roomId, _ ->
|
||||
val route = Screen.MessageMessaging.createRoute(roomId)
|
||||
navController.navigate(route)
|
||||
}
|
||||
)
|
||||
|
|
@ -138,26 +139,19 @@ fun Tg_Message(
|
|||
composable(
|
||||
route = Screen.MessageMessaging.route,
|
||||
arguments = listOf(
|
||||
// ✨ 3. 只保留 roomId 的參數宣告,完全刪除 roomName 的 navArgument
|
||||
navArgument("roomId") {
|
||||
type = NavType.StringType
|
||||
nullable = true
|
||||
defaultValue = ""
|
||||
},
|
||||
// 🚀 修正:新增接收 roomName 的導航參數
|
||||
navArgument("roomName") {
|
||||
type = NavType.StringType
|
||||
nullable = true
|
||||
defaultValue = "聊天室"
|
||||
}
|
||||
)
|
||||
) { backStackEntry ->
|
||||
val roomId = backStackEntry.arguments?.getString("roomId").orEmpty()
|
||||
// 🚀 修正:抓取名字
|
||||
val roomName = backStackEntry.arguments?.getString("roomName") ?: "聊天室"
|
||||
|
||||
// ✨ 4. 呼叫新版的 MessageMessaging,不再傳入 roomName 參數
|
||||
MessageMessaging(
|
||||
roomId = roomId,
|
||||
roomName = roomName, // 🚀 傳入 UI 中
|
||||
navController = navController,
|
||||
onBackClick = {
|
||||
navController.popBackStack()
|
||||
|
|
@ -461,21 +455,3 @@ fun RoomItem(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class UserStatus(val color: Color) {
|
||||
ONLINE(Color.Green),
|
||||
AWAY(Color.Yellow),
|
||||
OFFLINE(Color.Gray),
|
||||
UNKNOWN(Color.DarkGray);
|
||||
|
||||
companion object {
|
||||
fun fromString(status: String?): UserStatus {
|
||||
return when (status?.lowercase()) {
|
||||
"online" -> ONLINE
|
||||
"away" -> AWAY
|
||||
"offline" -> OFFLINE
|
||||
else -> UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
import com.example.login_v3.data.api.api_class.Message
|
||||
import com.example.login_v3.home.Message.ViewModel.UserStatus
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
|
@ -16,7 +17,11 @@ import kotlinx.coroutines.flow.asStateFlow
|
|||
|
||||
sealed interface MessagesUiState {
|
||||
object Loading : MessagesUiState
|
||||
data class Success(val messages: List<Message>) : MessagesUiState
|
||||
data class Success(
|
||||
val roomTitle: String,
|
||||
val partnerStatus: UserStatus,
|
||||
val messages: List<Message>
|
||||
) : MessagesUiState
|
||||
data class Error(val message: String) : MessagesUiState
|
||||
}
|
||||
|
||||
|
|
@ -29,19 +34,40 @@ class ChatViewModel @Inject constructor(
|
|||
val uiState: StateFlow<MessagesUiState> = _uiState.asStateFlow()
|
||||
|
||||
fun loadMessages(roomId: String) {
|
||||
|
||||
viewModelScope.launch {
|
||||
_uiState.value = MessagesUiState.Loading
|
||||
|
||||
repository.getChatMessages(roomId)
|
||||
.onSuccess { response ->
|
||||
// 1. 同時獲取房間資訊與訊息列表
|
||||
val roomResult = repository.getChatRoom(roomId) // ⚠️ 需確保 Repository 有提供此方法
|
||||
val messagesResult = repository.getChatMessages(roomId)
|
||||
|
||||
_uiState.value = MessagesUiState.Success(messages = response.messages)
|
||||
}
|
||||
.onFailure { exception ->
|
||||
if (messagesResult.isSuccess) {
|
||||
val messagesResponse = messagesResult.getOrNull()!!
|
||||
|
||||
_uiState.value = MessagesUiState.Error(message = exception.message ?: "未知錯誤")
|
||||
// 2. 根據房間資訊決定標題邏輯
|
||||
val room = roomResult.getOrNull()
|
||||
val (title, status) = if (room != null) {
|
||||
if (room.roomType == "DM") {
|
||||
val name = room.partner?.displayName ?: room.partner?.username ?: "未知用戶"
|
||||
val userStatus = UserStatus.fromString(room.partner?.status)
|
||||
Pair(name, userStatus)
|
||||
} else {
|
||||
Pair(room.roomName ?: "未命名群組", UserStatus.UNKNOWN)
|
||||
}
|
||||
} else {
|
||||
// 如果真的拿不到房間資訊,給個安全預設值
|
||||
Pair("聊天室", UserStatus.UNKNOWN)
|
||||
}
|
||||
|
||||
_uiState.value = MessagesUiState.Success(
|
||||
roomTitle = title,
|
||||
partnerStatus = status,
|
||||
messages = messagesResponse.messages
|
||||
)
|
||||
} else {
|
||||
val exception = messagesResult.exceptionOrNull()
|
||||
_uiState.value = MessagesUiState.Error(message = exception?.message ?: "未知錯誤")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
package com.example.login_v3.home.Message.ViewModel
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.example.login_v3.R
|
||||
|
|
@ -41,3 +42,22 @@ class ChatRoomsViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
enum class UserStatus(val color: Color) {
|
||||
ONLINE(Color.Green),
|
||||
OFFLINE(Color.Gray),
|
||||
UNKNOWN(Color.Transparent); // 或者是你原本設定的顏色
|
||||
|
||||
companion object {
|
||||
fun fromString(status: String?): UserStatus {
|
||||
if (status == null) return UNKNOWN
|
||||
|
||||
// 使用 try-catch 或者是 safe valueOf 確保絕對不閃退
|
||||
return try {
|
||||
// uppercase() 預防後端一下給大寫一下給小寫 (例如 "online" vs "ONLINE")
|
||||
valueOf(status.uppercase())
|
||||
} catch (e: IllegalArgumentException) {
|
||||
UNKNOWN // 找不到對應的狀態就安全地回傳 UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue