diff --git a/app/src/main/java/com/example/login_v3/home/Message/UI/Detail/Message_Messaging.kt b/app/src/main/java/com/example/login_v3/home/Message/UI/Detail/Message_Messaging.kt index 99d05dc..8b69c36 100644 --- a/app/src/main/java/com/example/login_v3/home/Message/UI/Detail/Message_Messaging.kt +++ b/app/src/main/java/com/example/login_v3/home/Message/UI/Detail/Message_Messaging.kt @@ -19,26 +19,35 @@ 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.automirrored.filled.Send import androidx.compose.material.icons.filled.AccountCircle import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.Send 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 +import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.TextField +import androidx.compose.material3.TextFieldDefaults import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.unit.dp import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -63,6 +72,7 @@ fun MessageMessaging( ) { val uiState by viewModel.uiState.collectAsStateWithLifecycle() + LaunchedEffect(roomId) { viewModel.loadMessages(roomId) } @@ -255,4 +265,64 @@ fun UserAvatar( .clip(CircleShape), contentScale = ContentScale.Crop ) +} + + +@Composable +fun MessageInputBar( + isLoading: Boolean, + onSendClick: (String) -> Unit, + modifier: Modifier = Modifier +) { + var textInput by remember { mutableStateOf("") } + + // 當發送成功、外面將 isLoading 從 true 變回 false 時,可以順便清空輸入框 + // 這裡直接在點擊發送時清空,或由外面控制皆可。 + + Surface( + tonalElevation = 2.dp, + modifier = modifier.fillMaxWidth() + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 8.dp, vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + TextField( + value = textInput, + onValueChange = { textInput = it }, + placeholder = { Text("輸入訊息...") }, + modifier = Modifier.weight(1.0f), + colors = TextFieldDefaults.colors( + focusedContainerColor = Color.Transparent, + unfocusedContainerColor = Color.Transparent, + disabledContainerColor = Color.Transparent, + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent + ), + maxLines = 4 + ) + + IconButton( + onClick = { + if (textInput.isNotBlank() && !isLoading) { + onSendClick(textInput) + textInput = "" // 點擊後立即清空輸入框 + } + }, + enabled = textInput.isNotBlank() && !isLoading + ) { + if (isLoading) { + CircularProgressIndicator(modifier = Modifier.size(24.dp), strokeWidth = 2.dp) + } else { + Icon( + imageVector = Icons.Default.Send, + contentDescription = "發送訊息", + tint = if (textInput.isNotBlank()) Color(0xFFDA7029) else Color.Gray + ) + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/example/login_v3/home/Message/ViewModel/Message_ViewModel.kt b/app/src/main/java/com/example/login_v3/home/Message/ViewModel/Message_ViewModel.kt index 4e4bc70..eb2283a 100644 --- a/app/src/main/java/com/example/login_v3/home/Message/ViewModel/Message_ViewModel.kt +++ b/app/src/main/java/com/example/login_v3/home/Message/ViewModel/Message_ViewModel.kt @@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.example.login_v3.R 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.repository.dm.ChatRoomsRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.flow.MutableStateFlow @@ -15,6 +16,16 @@ import kotlinx.coroutines.launch import javax.inject.Inject + +//message sending state +sealed class SendMessageState { + object Idle : SendMessageState() // 閒置狀態(預設) + object Loading : SendMessageState() // 發送中 + data class Success(val message: Message) : SendMessageState() // 發送成功,並帶回新訊息 + data class Error(val message: String) : SendMessageState() // 發送失敗,帶有錯誤訊息 +} + + @HiltViewModel class ChatRoomsViewModel @Inject constructor( private val repository: ChatRoomsRepository @@ -23,13 +34,16 @@ class ChatRoomsViewModel @Inject constructor( private val _roomsState = MutableStateFlow>(emptyList()) val roomsState: StateFlow> = _roomsState + // 1. 新增:發送訊息的 UI 狀態 Flow + private val _sendMessageState = MutableStateFlow(SendMessageState.Idle) + val sendMessageState: StateFlow = _sendMessageState + fun loadRooms() { viewModelScope.launch { val result = repository.fetchRooms() result.onSuccess { response -> _roomsState.value = response.rooms - // 打印偵錯訊息 response.rooms.forEach { room -> val status = room.partner?.status val name = room.partner?.displayName @@ -40,6 +54,38 @@ class ChatRoomsViewModel @Inject constructor( } } } + + // 2. 新增:發送訊息的 function + fun sendMessage(roomId: String, content: String, replyToId: String? = null) { + // 如果內容是空的,就直接攔截不送出 + if (content.isBlank()) return + + viewModelScope.launch { + // 切換成載入中狀態 + _sendMessageState.value = SendMessageState.Loading + + // 呼叫 repository + val result = repository.sendMessage(roomId, content, replyToId) + + result.onSuccess { newMessage -> + // 發送成功 + _sendMessageState.value = SendMessageState.Success(newMessage) + + // 【可選】如果你的聊天訊息列表也是歸這個 ViewModel 管, + // 你可以在這裡順便把新訊息加進當前的訊息列表 Flow 中,這樣畫面就會立刻跳出新訊息。 + + }.onFailure { error -> + // 發送失敗 + Log.e("ChatRoomsViewModel", "Send message failed", error) + _sendMessageState.value = SendMessageState.Error(error.message ?: "未知錯誤") + } + } + } + + // 3. 新增:重設發送狀態(通常在 UI 顯示完錯誤跳出視窗、或成功送出清空輸入框後呼叫) + fun resetSendMessageState() { + _sendMessageState.value = SendMessageState.Idle + } } enum class UserStatus(val color: Color) { @@ -60,4 +106,4 @@ enum class UserStatus(val color: Color) { } } } -} +} \ No newline at end of file