android: micro-polish chat bubbles and composer visuals
This commit is contained in:
@@ -901,3 +901,13 @@
|
||||
- Added top mini audio strip under pinned area:
|
||||
- shows latest audio/voice context from loaded chat,
|
||||
- includes play affordance, speed badge, and dismiss action.
|
||||
|
||||
### Step 126 - Message bubble/composer micro-polish
|
||||
- Updated message bubble sizing and density:
|
||||
- reduced bubble width for cleaner conversation rhythm,
|
||||
- tighter vertical spacing,
|
||||
- text style adjusted for better readability.
|
||||
- Refined bottom composer visuals:
|
||||
- switched to Telegram-like rounded input container look,
|
||||
- emoji/attach/send buttons now use circular tinted surfaces,
|
||||
- text input moved to filled style with hidden indicator lines.
|
||||
|
||||
@@ -58,6 +58,8 @@ import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.material3.TextFieldDefaults
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
@@ -1012,9 +1014,9 @@ fun ChatScreen(
|
||||
.fillMaxWidth()
|
||||
.navigationBarsPadding()
|
||||
.imePadding()
|
||||
.padding(horizontal = 10.dp, vertical = 8.dp),
|
||||
color = MaterialTheme.colorScheme.surface.copy(alpha = 0.92f),
|
||||
shape = RoundedCornerShape(24.dp),
|
||||
.padding(horizontal = 10.dp, vertical = 6.dp),
|
||||
color = MaterialTheme.colorScheme.surface.copy(alpha = 0.95f),
|
||||
shape = RoundedCornerShape(22.dp),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
@@ -1023,30 +1025,49 @@ fun ChatScreen(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||
) {
|
||||
IconButton(
|
||||
onClick = { /* emoji picker step */ },
|
||||
enabled = state.canSendMessages,
|
||||
Surface(
|
||||
shape = CircleShape,
|
||||
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.14f),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.EmojiEmotions,
|
||||
contentDescription = "Emoji",
|
||||
)
|
||||
IconButton(
|
||||
onClick = { /* emoji picker step */ },
|
||||
enabled = state.canSendMessages,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.EmojiEmotions,
|
||||
contentDescription = "Emoji",
|
||||
)
|
||||
}
|
||||
}
|
||||
OutlinedTextField(
|
||||
TextField(
|
||||
value = state.inputText,
|
||||
onValueChange = onInputChanged,
|
||||
modifier = Modifier.weight(1f),
|
||||
placeholder = { Text("Message") },
|
||||
shape = RoundedCornerShape(14.dp),
|
||||
maxLines = 4,
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedContainerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.45f),
|
||||
unfocusedContainerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.35f),
|
||||
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.25f),
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
disabledIndicatorColor = Color.Transparent,
|
||||
),
|
||||
)
|
||||
IconButton(
|
||||
onClick = onPickMedia,
|
||||
enabled = state.canSendMessages && !state.isUploadingMedia && !state.isRecordingVoice,
|
||||
Surface(
|
||||
shape = CircleShape,
|
||||
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.14f),
|
||||
) {
|
||||
if (state.isUploadingMedia) {
|
||||
CircularProgressIndicator(modifier = Modifier.size(18.dp), strokeWidth = 2.dp)
|
||||
} else {
|
||||
Icon(imageVector = Icons.Filled.AttachFile, contentDescription = "Attach")
|
||||
IconButton(
|
||||
onClick = onPickMedia,
|
||||
enabled = state.canSendMessages && !state.isUploadingMedia && !state.isRecordingVoice,
|
||||
) {
|
||||
if (state.isUploadingMedia) {
|
||||
CircularProgressIndicator(modifier = Modifier.size(18.dp), strokeWidth = 2.dp)
|
||||
} else {
|
||||
Icon(imageVector = Icons.Filled.AttachFile, contentDescription = "Attach")
|
||||
}
|
||||
}
|
||||
}
|
||||
val canSend = state.canSendMessages &&
|
||||
@@ -1054,11 +1075,16 @@ fun ChatScreen(
|
||||
!state.isUploadingMedia &&
|
||||
state.inputText.isNotBlank()
|
||||
if (canSend) {
|
||||
IconButton(
|
||||
onClick = onSendClick,
|
||||
enabled = state.canSendMessages && !state.isUploadingMedia,
|
||||
Surface(
|
||||
shape = CircleShape,
|
||||
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.18f),
|
||||
) {
|
||||
Icon(imageVector = Icons.AutoMirrored.Filled.Send, contentDescription = "Send")
|
||||
IconButton(
|
||||
onClick = onSendClick,
|
||||
enabled = state.canSendMessages && !state.isUploadingMedia,
|
||||
) {
|
||||
Icon(imageVector = Icons.AutoMirrored.Filled.Send, contentDescription = "Send")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
VoiceHoldToRecordButton(
|
||||
@@ -1291,7 +1317,7 @@ private fun MessageBubble(
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(0.84f)
|
||||
.fillMaxWidth(0.8f)
|
||||
.widthIn(min = 82.dp)
|
||||
.background(
|
||||
color = when {
|
||||
@@ -1305,8 +1331,8 @@ private fun MessageBubble(
|
||||
onClick = onClick,
|
||||
onLongClick = onLongPress,
|
||||
)
|
||||
.padding(horizontal = 10.dp, vertical = 8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
.padding(horizontal = 10.dp, vertical = 7.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(3.dp),
|
||||
) {
|
||||
if (!isOutgoing && !message.senderDisplayName.isNullOrBlank()) {
|
||||
Text(
|
||||
@@ -1373,7 +1399,7 @@ private fun MessageBubble(
|
||||
if (mainText != null || message.attachments.isEmpty()) {
|
||||
Text(
|
||||
text = mainText ?: "[${message.type}]",
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = textColor,
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user