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:
|
- Added top mini audio strip under pinned area:
|
||||||
- shows latest audio/voice context from loaded chat,
|
- shows latest audio/voice context from loaded chat,
|
||||||
- includes play affordance, speed badge, and dismiss action.
|
- 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.OutlinedTextField
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
|
import androidx.compose.material3.TextField
|
||||||
|
import androidx.compose.material3.TextFieldDefaults
|
||||||
import androidx.compose.material3.rememberModalBottomSheetState
|
import androidx.compose.material3.rememberModalBottomSheetState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
@@ -1012,9 +1014,9 @@ fun ChatScreen(
|
|||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.navigationBarsPadding()
|
.navigationBarsPadding()
|
||||||
.imePadding()
|
.imePadding()
|
||||||
.padding(horizontal = 10.dp, vertical = 8.dp),
|
.padding(horizontal = 10.dp, vertical = 6.dp),
|
||||||
color = MaterialTheme.colorScheme.surface.copy(alpha = 0.92f),
|
color = MaterialTheme.colorScheme.surface.copy(alpha = 0.95f),
|
||||||
shape = RoundedCornerShape(24.dp),
|
shape = RoundedCornerShape(22.dp),
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -1023,30 +1025,49 @@ fun ChatScreen(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||||
) {
|
) {
|
||||||
IconButton(
|
Surface(
|
||||||
onClick = { /* emoji picker step */ },
|
shape = CircleShape,
|
||||||
enabled = state.canSendMessages,
|
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.14f),
|
||||||
) {
|
) {
|
||||||
Icon(
|
IconButton(
|
||||||
imageVector = Icons.Filled.EmojiEmotions,
|
onClick = { /* emoji picker step */ },
|
||||||
contentDescription = "Emoji",
|
enabled = state.canSendMessages,
|
||||||
)
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Filled.EmojiEmotions,
|
||||||
|
contentDescription = "Emoji",
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
OutlinedTextField(
|
TextField(
|
||||||
value = state.inputText,
|
value = state.inputText,
|
||||||
onValueChange = onInputChanged,
|
onValueChange = onInputChanged,
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
placeholder = { Text("Message") },
|
placeholder = { Text("Message") },
|
||||||
|
shape = RoundedCornerShape(14.dp),
|
||||||
maxLines = 4,
|
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(
|
Surface(
|
||||||
onClick = onPickMedia,
|
shape = CircleShape,
|
||||||
enabled = state.canSendMessages && !state.isUploadingMedia && !state.isRecordingVoice,
|
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.14f),
|
||||||
) {
|
) {
|
||||||
if (state.isUploadingMedia) {
|
IconButton(
|
||||||
CircularProgressIndicator(modifier = Modifier.size(18.dp), strokeWidth = 2.dp)
|
onClick = onPickMedia,
|
||||||
} else {
|
enabled = state.canSendMessages && !state.isUploadingMedia && !state.isRecordingVoice,
|
||||||
Icon(imageVector = Icons.Filled.AttachFile, contentDescription = "Attach")
|
) {
|
||||||
|
if (state.isUploadingMedia) {
|
||||||
|
CircularProgressIndicator(modifier = Modifier.size(18.dp), strokeWidth = 2.dp)
|
||||||
|
} else {
|
||||||
|
Icon(imageVector = Icons.Filled.AttachFile, contentDescription = "Attach")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val canSend = state.canSendMessages &&
|
val canSend = state.canSendMessages &&
|
||||||
@@ -1054,11 +1075,16 @@ fun ChatScreen(
|
|||||||
!state.isUploadingMedia &&
|
!state.isUploadingMedia &&
|
||||||
state.inputText.isNotBlank()
|
state.inputText.isNotBlank()
|
||||||
if (canSend) {
|
if (canSend) {
|
||||||
IconButton(
|
Surface(
|
||||||
onClick = onSendClick,
|
shape = CircleShape,
|
||||||
enabled = state.canSendMessages && !state.isUploadingMedia,
|
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 {
|
} else {
|
||||||
VoiceHoldToRecordButton(
|
VoiceHoldToRecordButton(
|
||||||
@@ -1291,7 +1317,7 @@ private fun MessageBubble(
|
|||||||
}
|
}
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth(0.84f)
|
.fillMaxWidth(0.8f)
|
||||||
.widthIn(min = 82.dp)
|
.widthIn(min = 82.dp)
|
||||||
.background(
|
.background(
|
||||||
color = when {
|
color = when {
|
||||||
@@ -1305,8 +1331,8 @@ private fun MessageBubble(
|
|||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
onLongClick = onLongPress,
|
onLongClick = onLongPress,
|
||||||
)
|
)
|
||||||
.padding(horizontal = 10.dp, vertical = 8.dp),
|
.padding(horizontal = 10.dp, vertical = 7.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
verticalArrangement = Arrangement.spacedBy(3.dp),
|
||||||
) {
|
) {
|
||||||
if (!isOutgoing && !message.senderDisplayName.isNullOrBlank()) {
|
if (!isOutgoing && !message.senderDisplayName.isNullOrBlank()) {
|
||||||
Text(
|
Text(
|
||||||
@@ -1373,7 +1399,7 @@ private fun MessageBubble(
|
|||||||
if (mainText != null || message.attachments.isEmpty()) {
|
if (mainText != null || message.attachments.isEmpty()) {
|
||||||
Text(
|
Text(
|
||||||
text = mainText ?: "[${message.type}]",
|
text = mainText ?: "[${message.type}]",
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
color = textColor,
|
color = textColor,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user