fix: LOCKED UI как в Telegram — CANCEL текст вместо ✕, без blob при lock
Telegram LOCKED layout: [timer] [waveform] [CANCEL] [⏸] [Send] Изменения: - RecordLockedControls: убрана круглая ✕ кнопка delete - Вместо неё: текст "CANCEL" синим bold 15sp (как в Telegram) - Пауза иконка увеличена 12→14dp, фон 15% alpha - Blob анимация скрыта при LOCKED/PAUSED (Telegram: solid circle) - Spacing 8→12dp между CANCEL и паузой Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -781,6 +781,17 @@ private fun VoiceWaveformBar(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Telegram-exact locked recording controls.
|
||||||
|
*
|
||||||
|
* Layout: [CANCEL text-button] [⏸/▶ circle button]
|
||||||
|
*
|
||||||
|
* - CANCEL = blue text (15sp bold, uppercase), clickable — cancels recording
|
||||||
|
* - ⏸ = small circle button (36dp), toggles pause/resume
|
||||||
|
* - No separate delete icon — CANCEL IS delete
|
||||||
|
*
|
||||||
|
* Reference: ChatActivityEnterView recordedAudioPanel + SlideTextView cancelToProgress
|
||||||
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
private fun RecordLockedControls(
|
private fun RecordLockedControls(
|
||||||
isPaused: Boolean,
|
isPaused: Boolean,
|
||||||
@@ -789,37 +800,31 @@ private fun RecordLockedControls(
|
|||||||
onTogglePause: () -> Unit,
|
onTogglePause: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val deleteBgColor = if (isDarkTheme) Color(0xFF444444) else Color(0xFFE0E0E0)
|
val cancelColor = if (isDarkTheme) Color(0xFF69CCFF) else Color(0xFF2D9CFF)
|
||||||
val deleteIconColor = if (isDarkTheme) Color.White.copy(alpha = 0.8f) else Color(0xFF666666)
|
val pauseBgColor = if (isDarkTheme) Color(0xFF69CCFF).copy(alpha = 0.15f) else Color(0xFF2D9CFF).copy(alpha = 0.1f)
|
||||||
val pauseBgColor = if (isDarkTheme) Color(0xFF69CCFF).copy(alpha = 0.3f) else Color(0xFF2D9CFF).copy(alpha = 0.2f)
|
|
||||||
val pauseIconColor = if (isDarkTheme) Color(0xFF69CCFF) else Color(0xFF2D9CFF)
|
val pauseIconColor = if (isDarkTheme) Color(0xFF69CCFF) else Color(0xFF2D9CFF)
|
||||||
|
|
||||||
Row(
|
Row(
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||||
) {
|
) {
|
||||||
// Delete button
|
// CANCEL text button — Telegram: blue bold uppercase
|
||||||
Box(
|
Text(
|
||||||
|
text = "CANCEL",
|
||||||
|
color = cancelColor,
|
||||||
|
fontSize = 15.sp,
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
maxLines = 1,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(36.dp)
|
|
||||||
.clip(CircleShape)
|
|
||||||
.background(deleteBgColor)
|
|
||||||
.clickable(
|
.clickable(
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
indication = null
|
indication = null
|
||||||
) { onDelete() },
|
) { onDelete() }
|
||||||
contentAlignment = Alignment.Center
|
.padding(horizontal = 4.dp, vertical = 8.dp)
|
||||||
) {
|
)
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Close,
|
|
||||||
contentDescription = "Delete recording",
|
|
||||||
tint = deleteIconColor,
|
|
||||||
modifier = Modifier.size(18.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pause/Resume button
|
// Pause/Resume button — circle with icon
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(36.dp)
|
.size(36.dp)
|
||||||
@@ -832,7 +837,8 @@ private fun RecordLockedControls(
|
|||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
if (isPaused) {
|
if (isPaused) {
|
||||||
Canvas(modifier = Modifier.size(12.dp)) {
|
// Play triangle
|
||||||
|
Canvas(modifier = Modifier.size(14.dp)) {
|
||||||
val path = Path().apply {
|
val path = Path().apply {
|
||||||
moveTo(size.width * 0.2f, 0f)
|
moveTo(size.width * 0.2f, 0f)
|
||||||
lineTo(size.width, size.height / 2f)
|
lineTo(size.width, size.height / 2f)
|
||||||
@@ -842,19 +848,20 @@ private fun RecordLockedControls(
|
|||||||
drawPath(path, color = pauseIconColor)
|
drawPath(path, color = pauseIconColor)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Canvas(modifier = Modifier.size(12.dp)) {
|
// Pause bars
|
||||||
val barW = size.width * 0.25f
|
Canvas(modifier = Modifier.size(14.dp)) {
|
||||||
val gap = size.width * 0.15f
|
val barW = size.width * 0.22f
|
||||||
|
val gap = size.width * 0.14f
|
||||||
drawRoundRect(
|
drawRoundRect(
|
||||||
color = pauseIconColor,
|
color = pauseIconColor,
|
||||||
topLeft = Offset(size.width / 2f - gap - barW, 0f),
|
topLeft = Offset(size.width / 2f - gap - barW, size.height * 0.1f),
|
||||||
size = androidx.compose.ui.geometry.Size(barW, size.height),
|
size = androidx.compose.ui.geometry.Size(barW, size.height * 0.8f),
|
||||||
cornerRadius = androidx.compose.ui.geometry.CornerRadius(barW / 3f)
|
cornerRadius = androidx.compose.ui.geometry.CornerRadius(barW / 3f)
|
||||||
)
|
)
|
||||||
drawRoundRect(
|
drawRoundRect(
|
||||||
color = pauseIconColor,
|
color = pauseIconColor,
|
||||||
topLeft = Offset(size.width / 2f + gap, 0f),
|
topLeft = Offset(size.width / 2f + gap, size.height * 0.1f),
|
||||||
size = androidx.compose.ui.geometry.Size(barW, size.height),
|
size = androidx.compose.ui.geometry.Size(barW, size.height * 0.8f),
|
||||||
cornerRadius = androidx.compose.ui.geometry.CornerRadius(barW / 3f)
|
cornerRadius = androidx.compose.ui.geometry.CornerRadius(barW / 3f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -2240,18 +2247,20 @@ fun MessageInputBar(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Blob: 48dp base → 1.7x = ~82dp visual (matches Telegram circleRadius 41dp)
|
// Blob: only during RECORDING (Telegram hides waves when locked)
|
||||||
VoiceButtonBlob(
|
if (recordUiState == RecordUiState.RECORDING) {
|
||||||
voiceLevel = voiceLevel,
|
VoiceButtonBlob(
|
||||||
isDarkTheme = isDarkTheme,
|
voiceLevel = voiceLevel,
|
||||||
modifier = Modifier
|
isDarkTheme = isDarkTheme,
|
||||||
.size(48.dp)
|
modifier = Modifier
|
||||||
.graphicsLayer {
|
.size(48.dp)
|
||||||
scaleX = 1.7f
|
.graphicsLayer {
|
||||||
scaleY = 1.7f
|
scaleX = 1.7f
|
||||||
clip = false
|
scaleY = 1.7f
|
||||||
}
|
clip = false
|
||||||
)
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// Solid circle: 48dp layout, scaled to 82dp visual
|
// Solid circle: 48dp layout, scaled to 82dp visual
|
||||||
val sendScale by animateFloatAsState(
|
val sendScale by animateFloatAsState(
|
||||||
|
|||||||
Reference in New Issue
Block a user