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