This commit is contained in:
Eric85710 2026-05-17 19:37:51 +08:00
parent b7ae279347
commit 003d742a01
2 changed files with 171 additions and 14 deletions

View file

@ -1,23 +1,149 @@
package com.example.login_v3.home.Message.UI.Detail package com.example.login_v3.home.Message.UI.Detail
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import androidx.navigation.NavController 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.navigation.Screen
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun MessageMessaging( fun MessageMessaging(
navController: NavController roomId: String,
navController: NavController,
onBackClick: () -> Unit,
viewModel: ChatViewModel = viewModel()
) { ) {
Column( val uiState by viewModel.uiState
LaunchedEffect(roomId) {
viewModel.loadMessages(roomId)
}
Scaffold(
topBar = {
TopAppBar(
title = { Text("聊天室 ($roomId)") },
navigationIcon = {
IconButton(onClick = onBackClick) {
Text("<")
}
}
)
}
) { innerPadding ->
Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
, .padding(innerPadding),
horizontalAlignment = Alignment.CenterHorizontally contentAlignment = Alignment.Center
) { ) {
Text(text = "MessageMessaging") when (val state = uiState) {
is MessagesUiState.Loading -> {
CircularProgressIndicator()
}
is MessagesUiState.Success -> {
if (state.messages.isEmpty()) {
Text("目前沒有新訊息")
} else {
MessageList(messages = state.messages)
}
}
is MessagesUiState.Error -> {
Text(
text = "載入失敗:${state.message}",
color = MaterialTheme.colorScheme.error
)
}
}
}
}
}
@Composable
fun MessageList(
messages: List<Message>, // 這裡是正確的 Message
modifier: Modifier = Modifier
) {
val currentUserId = "user_123"
LazyColumn(
modifier = modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
// items 內部拿到的 message 會依循 List<Message> 的型態
items(messages) { message ->
val isMe = message.senderId == currentUserId
MessageRow(message = message, isMe = isMe)
}
}
}
@Composable
fun MessageRow(
message: Message, // ✅ 修正這裡:將 Screen.Message 改為 Message
isMe: Boolean
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = if (isMe) Arrangement.End else Arrangement.Start
) {
Box(
modifier = Modifier
.widthIn(max = 280.dp)
.background(
color = if (isMe) Color(0xFFDCF8C6) else Color(0xFFECEFF1),
shape = RoundedCornerShape(
topStart = 12.dp,
topEnd = 12.dp,
bottomStart = if (isMe) 12.dp else 0.dp,
bottomEnd = if (isMe) 0.dp else 12.dp
)
)
.padding(horizontal = 12.dp, vertical = 8.dp)
) {
Column {
if (!isMe) {
Text(
text = message.senderId, // ✅ 這樣型態對了,就能抓到欄位了!
style = MaterialTheme.typography.labelSmall,
color = Color.Gray
)
}
Text(
text = message.content, // ✅ 這樣型態對了,就能抓到欄位了!
style = MaterialTheme.typography.bodyMedium
)
}
}
} }
} }

View file

@ -1,5 +1,6 @@
package com.example.login_v3.home.Message.UI package com.example.login_v3.home.Message.UI
import android.R.attr.type
import androidx.compose.animation.AnimatedContentTransitionScope import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.Canvas import androidx.compose.foundation.Canvas
@ -65,9 +66,11 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
import androidx.navigation.NavController import androidx.navigation.NavController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import coil.compose.AsyncImage import coil.compose.AsyncImage
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.fullContactAvatarUrl import com.example.login_v3.data.api.api_class.fullContactAvatarUrl
@ -84,7 +87,14 @@ sealed class Screen(val route: String) {
object CreateContact : Screen("create_contact") object CreateContact : Screen("create_contact")
object FriendsList : Screen("friends_list") object FriendsList : Screen("friends_list")
object ScanQRcode : Screen("scan_QRcode") object ScanQRcode : Screen("scan_QRcode")
object MessageMessaging : Screen("Message_Messaging") object MessageMessaging : Screen("message_messaging/{roomId}") {
// 這是給上一頁 Loaded_Tg_Message 跳轉時產生的真實路徑
fun createRoute(roomId: String): String {
// 建議加上 URL 編碼,防止 roomId 含有斜線或特殊字元導致二次閃退
val encodedId = java.net.URLEncoder.encode(roomId, "UTF-8")
return "message_messaging/$encodedId"
}
}
} }
@Composable @Composable
@ -94,6 +104,7 @@ fun Tg_Message(
val navController = rememberNavController() val navController = rememberNavController()
NavHost( NavHost(
navController = navController, navController = navController,
startDestination = Screen.Messages.route, startDestination = Screen.Messages.route,
// 進入時的動畫(新頁面出現) // 進入時的動畫(新頁面出現)
@ -149,9 +160,25 @@ fun Tg_Message(
} }
//Message detail //Message detail
composable(Screen.MessageMessaging.route) { // 這樣寫就完全沒問題了!因為 route 內部已經包含了 /{roomId}
MessageMessaging(navController) composable(
route = Screen.MessageMessaging.route,
arguments = listOf(
navArgument("roomId") { type = NavType.StringType }
)
) { backStackEntry ->
// 安全取出參數
val roomId = backStackEntry.arguments?.getString("roomId").orEmpty()
MessageMessaging(
roomId = roomId,
navController = navController,
onBackClick = {
navController.popBackStack()
} }
)
}
} }
} }
@ -279,10 +306,10 @@ fun Loaded_Tg_Message(
key = { it.roomId } // 增加效能,防止列表閃爍 key = { it.roomId } // 增加效能,防止列表閃爍
) { room -> ) { room ->
RoomItem( RoomItem(
navController = navController,
room = room, room = room,
onClick = { onClick = {
onRoomClick(room.roomId) // 執行原本的點擊邏輯 (依你的定義傳入對應的 String) onRoomClick(room.roomId)
navController.navigate(Screen.MessageMessaging.route) // 執行導頁
} }
) )
Spacer(modifier = Modifier.height(6.dp)) Spacer(modifier = Modifier.height(6.dp))
@ -298,6 +325,7 @@ fun Loaded_Tg_Message(
@Composable @Composable
fun RoomItem( fun RoomItem(
navController: NavController,
room: ChatRoom, room: ChatRoom,
onClick: (String) -> Unit onClick: (String) -> Unit
) { ) {
@ -305,7 +333,10 @@ fun RoomItem(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 6.dp) // 增加外邊距,讓 Card 之間有呼吸感 .padding(horizontal = 12.dp, vertical = 6.dp) // 增加外邊距,讓 Card 之間有呼吸感
.clickable { onClick(room.roomId) }, .clickable {
// 💡 修改這裡:動態帶入真實的 roomId
navController.navigate(Screen.MessageMessaging.createRoute(room.roomId))
},
shape = RoundedCornerShape(16.dp), // 較大的圓角看起來更現代 shape = RoundedCornerShape(16.dp), // 較大的圓角看起來更現代
colors = CardDefaults.cardColors( colors = CardDefaults.cardColors(
containerColor = Color.Transparent // ✅ 透明背景 containerColor = Color.Transparent // ✅ 透明背景