android: fix voice recording composer overlap
This commit is contained in:
@@ -967,3 +967,10 @@
|
||||
- Added dedicated read-only channel bottom bar (for non owner/admin):
|
||||
- compact Telegram-like controls with search + centered `Включить звук` action + notifications icon.
|
||||
- Kept existing full composer for roles allowed to post in channels (owner/admin).
|
||||
|
||||
### Step 132 - Voice recording composer overlap fix
|
||||
- Fixed composer overlap during voice recording:
|
||||
- recording status/hint is now rendered in a dedicated top block inside composer,
|
||||
- formatting toolbar is hidden while recording is active.
|
||||
- Prevented controls collision for locked-recording actions:
|
||||
- `Cancel/Send` now render on a separate row in locked state.
|
||||
|
||||
@@ -1114,6 +1114,15 @@ fun ChatScreen(
|
||||
.padding(horizontal = 8.dp, vertical = 6.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
) {
|
||||
if (state.isRecordingVoice) {
|
||||
VoiceRecordingStatusRow(
|
||||
durationMs = state.voiceRecordingDurationMs,
|
||||
hint = state.voiceRecordingHint,
|
||||
isLocked = state.isVoiceLocked,
|
||||
onCancel = onVoiceRecordCancel,
|
||||
onSend = onVoiceRecordSend,
|
||||
)
|
||||
} else {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -1185,6 +1194,7 @@ fun ChatScreen(
|
||||
enabled = state.canSendMessages,
|
||||
) { Icon(Icons.Filled.Link, contentDescription = "Link") }
|
||||
}
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -1267,30 +1277,6 @@ fun ChatScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state.isRecordingVoice) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 10.dp, vertical = 6.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = "Voice ${formatDuration(state.voiceRecordingDurationMs.toInt())}",
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
)
|
||||
Text(
|
||||
text = state.voiceRecordingHint ?: "",
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
)
|
||||
if (state.isVoiceLocked) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
Button(onClick = onVoiceRecordCancel) { Text("Cancel") }
|
||||
Button(onClick = onVoiceRecordSend) { Text("Send") }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2138,6 +2124,55 @@ private fun formatDateSeparatorLabel(date: LocalDate): String {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun VoiceRecordingStatusRow(
|
||||
durationMs: Long,
|
||||
hint: String?,
|
||||
isLocked: Boolean,
|
||||
onCancel: () -> Unit,
|
||||
onSend: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 4.dp, vertical = 2.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(6.dp),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = "Voice ${formatDuration(durationMs.toInt())}",
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
if (!hint.isNullOrBlank()) {
|
||||
Text(
|
||||
text = hint,
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
maxLines = 1,
|
||||
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (isLocked) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Button(onClick = onCancel) { Text("Cancel") }
|
||||
Button(onClick = onSend) { Text("Send") }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun TextFieldValue.insertAtCursor(value: String): TextFieldValue {
|
||||
val start = selection.min
|
||||
val end = selection.max
|
||||
|
||||
Reference in New Issue
Block a user