add delta sync for dm-v25
This commit is contained in:
parent
0740aaebc5
commit
7c2c89fc8b
3 changed files with 65 additions and 57 deletions
|
|
@ -1,10 +1,8 @@
|
||||||
package com.example.login_v3.home.Message.UI.Detail.Message_Component
|
package com.example.login_v3.home.Message.UI.Detail.Message_Component
|
||||||
|
|
||||||
import android.R.attr.onClick
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
|
@ -20,6 +18,16 @@ import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.BasicTextField
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
|
import androidx.compose.foundation.text.input.TextFieldState
|
||||||
|
import androidx.compose.foundation.text.input.rememberTextFieldState
|
||||||
|
import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
|
||||||
|
import androidx.compose.foundation.text.input.clearText
|
||||||
|
import androidx.compose.foundation.content.contentReceiver
|
||||||
|
import androidx.compose.foundation.content.MediaType
|
||||||
|
import androidx.compose.foundation.content.hasMediaType
|
||||||
|
import androidx.compose.foundation.content.consume
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.AddCircle
|
import androidx.compose.material.icons.filled.AddCircle
|
||||||
import androidx.compose.material.icons.filled.Check
|
import androidx.compose.material.icons.filled.Check
|
||||||
|
|
@ -33,7 +41,7 @@ import androidx.compose.material3.IconButton
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect // 🎯 新增
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
|
@ -43,41 +51,38 @@ import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.blur
|
import androidx.compose.ui.draw.blur
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.text.input.ImeAction
|
||||||
|
import androidx.compose.ui.text.input.KeyboardType
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.example.login_v3.home.Message.UI.Detail.MessageUiModel
|
import com.example.login_v3.home.Message.UI.Detail.MessageUiModel
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MessageInputBar(
|
fun MessageInputBar(
|
||||||
isLoading: Boolean,
|
isLoading: Boolean,
|
||||||
replyingMessage: MessageUiModel?,
|
replyingMessage: MessageUiModel?,
|
||||||
editingMessage: MessageUiModel? = null, // 🎯 新增
|
editingMessage: MessageUiModel? = null,
|
||||||
onCancelReply: () -> Unit,
|
onCancelReply: () -> Unit,
|
||||||
onCancelEdit: () -> Unit = {}, // 🎯 新增
|
onCancelEdit: () -> Unit = {},
|
||||||
onSendClick: (String) -> Unit,
|
onSendClick: (String) -> Unit,
|
||||||
onEditSaveClick: (String) -> Unit = {}, // 🎯 新增
|
onEditSaveClick: (String) -> Unit = {},
|
||||||
onAttachmentClick: () -> Unit
|
onAttachmentClick: () -> Unit,
|
||||||
|
onMediaReceived: (Uri) -> Unit = {}
|
||||||
) {
|
) {
|
||||||
var textState by remember { mutableStateOf("") }
|
val textState = rememberTextFieldState()
|
||||||
|
|
||||||
// 🎯 當進入編輯模式時,自動填入原本的內容
|
|
||||||
LaunchedEffect(editingMessage) {
|
LaunchedEffect(editingMessage) {
|
||||||
if (editingMessage != null) {
|
if (editingMessage != null) {
|
||||||
textState = editingMessage.content
|
textState.setTextAndPlaceCursorAtEnd(editingMessage.content)
|
||||||
} else {
|
|
||||||
// 如果從編輯模式退出且不是因為發送成功,可能需要清空?
|
|
||||||
// 但通常 clearEditingMessage 會觸發這裡
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 💡 外層包裹一層 Box,並加上 padding 與 navigationBarsPadding,確保懸浮且不被系統列擋住
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.navigationBarsPadding() // 自動適應 Android 系統導覽列高度
|
.navigationBarsPadding()
|
||||||
.padding(horizontal = 16.dp, vertical = 8.dp) // 懸浮的外邊距
|
.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||||
|
|
||||||
//color
|
|
||||||
.background(
|
.background(
|
||||||
color = MaterialTheme.colorScheme.primary,
|
color = MaterialTheme.colorScheme.primary,
|
||||||
RoundedCornerShape(16.dp)
|
RoundedCornerShape(16.dp)
|
||||||
|
|
@ -88,34 +93,27 @@ fun MessageInputBar(
|
||||||
RoundedCornerShape(16.dp)
|
RoundedCornerShape(16.dp)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
Column(modifier = Modifier.fillMaxWidth()) {
|
Column(modifier = Modifier.fillMaxWidth()) {
|
||||||
|
|
||||||
// 1. 回覆預覽列 (現代化樣式 + 圓角與間距)
|
|
||||||
AnimatedVisibility(visible = replyingMessage != null) {
|
AnimatedVisibility(visible = replyingMessage != null) {
|
||||||
if (replyingMessage != null) {
|
if (replyingMessage != null) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(start = 8.dp, end = 8.dp, top = 8.dp) // 🎯 與邊框保持間距
|
.padding(start = 8.dp, end = 8.dp, top = 8.dp)
|
||||||
.background(
|
.background(
|
||||||
color = Color.White.copy(alpha = 0.92f),
|
color = Color.White.copy(alpha = 0.92f),
|
||||||
shape = RoundedCornerShape(12.dp) // 🎯 圓角
|
shape = RoundedCornerShape(12.dp)
|
||||||
)
|
)
|
||||||
.padding(horizontal = 12.dp, vertical = 8.dp),
|
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
// 左側綠色裝飾條
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.width(3.dp)
|
.width(3.dp)
|
||||||
.height(16.dp)
|
.height(16.dp)
|
||||||
.background(Color(0xFF4CAF50), RoundedCornerShape(2.dp))
|
.background(Color(0xFF4CAF50), RoundedCornerShape(2.dp))
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(10.dp))
|
Spacer(modifier = Modifier.width(10.dp))
|
||||||
|
|
||||||
Column(modifier = Modifier.weight(1f)) {
|
Column(modifier = Modifier.weight(1f)) {
|
||||||
Text(
|
Text(
|
||||||
text = replyingMessage.content,
|
text = replyingMessage.content,
|
||||||
|
|
@ -137,39 +135,33 @@ fun MessageInputBar(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 🎯 1.5 編輯預覽列 (現代化樣式 + 圓角與間距)
|
|
||||||
AnimatedVisibility(visible = editingMessage != null) {
|
AnimatedVisibility(visible = editingMessage != null) {
|
||||||
if (editingMessage != null) {
|
if (editingMessage != null) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(start = 8.dp, end = 8.dp, top = 8.dp) // 🎯 與邊框保持間距
|
.padding(start = 8.dp, end = 8.dp, top = 8.dp)
|
||||||
.background(
|
.background(
|
||||||
color = Color.White.copy(alpha = 0.92f),
|
color = Color.White.copy(alpha = 0.92f),
|
||||||
shape = RoundedCornerShape(12.dp) // 🎯 圓角
|
shape = RoundedCornerShape(12.dp)
|
||||||
)
|
)
|
||||||
.padding(horizontal = 12.dp, vertical = 8.dp),
|
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
// 左側橘色裝飾條,提升現代感
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.width(3.dp)
|
.width(3.dp)
|
||||||
.height(16.dp)
|
.height(16.dp)
|
||||||
.background(Color(0xFFDA7029), RoundedCornerShape(2.dp))
|
.background(Color(0xFFDA7029), RoundedCornerShape(2.dp))
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(10.dp))
|
Spacer(modifier = Modifier.width(10.dp))
|
||||||
|
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.Edit,
|
imageVector = Icons.Default.Edit,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = Color(0xFFDA7029),
|
tint = Color(0xFFDA7029),
|
||||||
modifier = Modifier.size(16.dp)
|
modifier = Modifier.size(16.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
text = "正在編輯訊息",
|
text = "正在編輯訊息",
|
||||||
style = MaterialTheme.typography.labelMedium.copy(
|
style = MaterialTheme.typography.labelMedium.copy(
|
||||||
|
|
@ -178,11 +170,10 @@ fun MessageInputBar(
|
||||||
),
|
),
|
||||||
modifier = Modifier.weight(1f)
|
modifier = Modifier.weight(1f)
|
||||||
)
|
)
|
||||||
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
onCancelEdit()
|
onCancelEdit()
|
||||||
textState = ""
|
textState.clearText()
|
||||||
},
|
},
|
||||||
modifier = Modifier.size(24.dp)
|
modifier = Modifier.size(24.dp)
|
||||||
) {
|
) {
|
||||||
|
|
@ -197,14 +188,12 @@ fun MessageInputBar(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. 實際的輸入工具列
|
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = 8.dp, vertical = 6.dp),
|
.padding(horizontal = 8.dp, vertical = 6.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
// 檔案上傳
|
|
||||||
IconButton(onClick = { onAttachmentClick() }) {
|
IconButton(onClick = { onAttachmentClick() }) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Default.AddCircle,
|
imageVector = Icons.Default.AddCircle,
|
||||||
|
|
@ -213,16 +202,34 @@ fun MessageInputBar(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 輸入文字框 (改為內嵌樣式,去掉自帶的邊框,讓整體更有一體感)
|
|
||||||
BasicTextField(
|
BasicTextField(
|
||||||
value = textState,
|
state = textState,
|
||||||
onValueChange = { textState = it },
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
.padding(horizontal = 8.dp),
|
.padding(horizontal = 8.dp)
|
||||||
|
.contentReceiver { transferableContent ->
|
||||||
|
if (transferableContent.hasMediaType(MediaType.Image)) {
|
||||||
|
transferableContent.consume { item ->
|
||||||
|
val uri = item.uri
|
||||||
|
if (uri != null) {
|
||||||
|
onMediaReceived(uri)
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
transferableContent
|
||||||
|
}
|
||||||
|
},
|
||||||
textStyle = MaterialTheme.typography.bodyLarge.copy(color = Color.White),
|
textStyle = MaterialTheme.typography.bodyLarge.copy(color = Color.White),
|
||||||
decorationBox = { innerTextField ->
|
keyboardOptions = KeyboardOptions(
|
||||||
if (textState.isEmpty()) {
|
keyboardType = KeyboardType.Text,
|
||||||
|
autoCorrectEnabled = true,
|
||||||
|
imeAction = ImeAction.Default
|
||||||
|
),
|
||||||
|
decorator = { innerTextField ->
|
||||||
|
if (textState.text.isEmpty()) {
|
||||||
Text(
|
Text(
|
||||||
text = if (editingMessage != null) "編輯訊息..." else "請輸入訊息...",
|
text = if (editingMessage != null) "編輯訊息..." else "請輸入訊息...",
|
||||||
color = Color.White.copy(alpha = 0.6f)
|
color = Color.White.copy(alpha = 0.6f)
|
||||||
|
|
@ -232,25 +239,24 @@ fun MessageInputBar(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// 送出按鈕
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
if (textState.isNotBlank() && !isLoading) {
|
val content = textState.text.toString()
|
||||||
|
if (content.isNotBlank() && !isLoading) {
|
||||||
if (editingMessage != null) {
|
if (editingMessage != null) {
|
||||||
onEditSaveClick(textState)
|
onEditSaveClick(content)
|
||||||
} else {
|
} else {
|
||||||
onSendClick(textState)
|
onSendClick(content)
|
||||||
}
|
}
|
||||||
textState = ""
|
textState.clearText()
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
enabled = textState.isNotBlank() && !isLoading
|
enabled = textState.text.isNotBlank() && !isLoading
|
||||||
) {
|
) {
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
CircularProgressIndicator(modifier = Modifier.size(24.dp), strokeWidth = 2.dp)
|
CircularProgressIndicator(modifier = Modifier.size(24.dp), strokeWidth = 2.dp)
|
||||||
} else {
|
} else {
|
||||||
// 加上小背景讓送出按鈕更有重點
|
val iconColor = if (textState.text.isNotBlank()) Color(0xFFDA7029) else Color.LightGray
|
||||||
val iconColor = if (textState.isNotBlank()) Color(0xFFDA7029) else Color.LightGray
|
|
||||||
val icon = if (editingMessage != null) Icons.Default.Check else Icons.Default.Send
|
val icon = if (editingMessage != null) Icons.Default.Check else Icons.Default.Send
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = icon,
|
imageVector = icon,
|
||||||
|
|
|
||||||
|
|
@ -519,7 +519,8 @@ fun MessageMessaging(
|
||||||
// 2. 沒權限,叫起多重權限請求視窗(傳入 requiredPermissions 陣列)
|
// 2. 沒權限,叫起多重權限請求視窗(傳入 requiredPermissions 陣列)
|
||||||
mainPermissionLauncher.launch(requiredPermissions)
|
mainPermissionLauncher.launch(requiredPermissions)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
onMediaReceived = { uri -> viewModel.uploadAttachment(roomId, uri) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -153,9 +153,10 @@ fun SharedTransitionScope.setting_list_Screen(
|
||||||
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
Text(
|
Text(
|
||||||
text = " ${item.title}",
|
text = "${item.title}",
|
||||||
style = MaterialTheme.typography.titleMedium,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
fontSize = 24.sp,
|
fontSize = 24.sp,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue