dm-13
This commit is contained in:
parent
b7ae279347
commit
003d742a01
2 changed files with 171 additions and 14 deletions
|
|
@ -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,
|
||||||
Column(
|
onBackClick: () -> Unit,
|
||||||
modifier = Modifier
|
viewModel: ChatViewModel = viewModel()
|
||||||
.fillMaxSize()
|
) {
|
||||||
,
|
val uiState by viewModel.uiState
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
|
||||||
) {
|
LaunchedEffect(roomId) {
|
||||||
Text(text = "MessageMessaging")
|
viewModel.loadMessages(roomId)
|
||||||
|
}
|
||||||
|
|
||||||
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
|
TopAppBar(
|
||||||
|
title = { Text("聊天室 ($roomId)") },
|
||||||
|
navigationIcon = {
|
||||||
|
IconButton(onClick = onBackClick) {
|
||||||
|
Text("<")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
) { innerPadding ->
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(innerPadding),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
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
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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 // ✅ 透明背景
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue