dm-29
This commit is contained in:
parent
68c025da5c
commit
b8a5ace88a
2 changed files with 118 additions and 2 deletions
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -256,3 +266,63 @@ fun UserAvatar(
|
|||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<List<ChatRoom>>(emptyList())
|
||||
val roomsState: StateFlow<List<ChatRoom>> = _roomsState
|
||||
|
||||
// 1. 新增:發送訊息的 UI 狀態 Flow
|
||||
private val _sendMessageState = MutableStateFlow<SendMessageState>(SendMessageState.Idle)
|
||||
val sendMessageState: StateFlow<SendMessageState> = _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) {
|
||||
|
|
|
|||
Loading…
Reference in a new issue