From 5c02ff6fd331f1bb7dfa6db94ca85348335a151e Mon Sep 17 00:00:00 2001 From: k1ngsterr1 Date: Sun, 12 Apr 2026 00:33:26 +0500 Subject: [PATCH] =?UTF-8?q?fix:=20LOCKED=20panel=201:1=20=D1=81=20Telegram?= =?UTF-8?q?=20=E2=80=94=20=D0=BF=D0=BE=D0=BB=D0=BD=D0=BE=D1=81=D1=82=D1=8C?= =?UTF-8?q?=D1=8E=20=D0=B4=D1=80=D1=83=D0=B3=D0=BE=D0=B9=20layout=20=D0=BF?= =?UTF-8?q?=D1=80=D0=B8=20lock?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Telegram при LOCKED: таймер и dot СКРЫТЫ, вместо них: - [Delete 44dp] — красная иконка удаления слева - [Waveform] — заполняет оставшееся место - Lock→Pause кнопка наверху (отдельный overlay) - Circle = Send (без blob) При RECORDING (без изменений): - [dot][timer] [◀ Slide to cancel] [Circle+Blob] Реализация: AnimatedContent crossfade между двумя полностью разными panel layouts. RecordLockedControls больше не используется в панели — delete в самой панели, pause в LockIcon overlay. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ui/chats/input/ChatDetailInput.kt | 170 ++++++++++-------- 1 file changed, 91 insertions(+), 79 deletions(-) diff --git a/app/src/main/java/com/rosetta/messenger/ui/chats/input/ChatDetailInput.kt b/app/src/main/java/com/rosetta/messenger/ui/chats/input/ChatDetailInput.kt index 2037640..e74fdf7 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/chats/input/ChatDetailInput.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/chats/input/ChatDetailInput.kt @@ -2095,9 +2095,11 @@ fun MessageInputBar( label = "record_ui_shift" ) - // ── Telegram-style recording layout ── - // Telegram uses separate overlay layers (RecordCircle 194dp, ControlsView 250dp) - // We replicate with Box layers + graphicsLayer for overflow + // ── Telegram-exact recording layout ── + // RECORDING: [dot][timer] [◀ Slide to cancel] ... [Circle+Blob] + // LOCKED: [Delete] [Waveform 32dp] ... [Circle=Send] + Lock→Pause above + // PAUSED: [Delete] [Waveform 32dp] ... [Circle=Send] + Lock→Play above + // Timer and dot are HIDDEN in LOCKED/PAUSED (Telegram exact) Box( modifier = Modifier .fillMaxWidth() @@ -2108,93 +2110,103 @@ fun MessageInputBar( recordingInputRowY = coordinates.positionInWindow().y } ) { - // ── Layer 1: Panel bar (timer + center) ── - // Telegram: full-width bar, circle overlaps right edge - Row( + val isLockedOrPaused = recordUiState == RecordUiState.LOCKED || recordUiState == RecordUiState.PAUSED + + // Crossfade between RECORDING panel and LOCKED panel + AnimatedContent( + targetState = isLockedOrPaused, modifier = Modifier .fillMaxWidth() - .height(40.dp) - .clip(RoundedCornerShape(20.dp)) - .background(recordingPanelColor) - .padding(start = 13.dp, end = 52.dp), // 52dp = half-circle overlap - verticalAlignment = Alignment.CenterVertically - ) { - // Blink dot + timer - Row( - modifier = Modifier - .graphicsLayer { - alpha = recordUiAlpha - translationX = with(density) { recordUiShift.toPx() } - }, - verticalAlignment = Alignment.CenterVertically - ) { - if (recordUiState == RecordUiState.PAUSED) { - Box(modifier = Modifier.size(28.dp), contentAlignment = Alignment.Center) { - Box(modifier = Modifier.size(10.dp).clip(CircleShape) - .background(if (isDarkTheme) Color(0xFFFF5A5A) else Color(0xFFE84D4D))) - } - } else { - RecordBlinkDot(isDarkTheme = isDarkTheme) - } - Spacer(modifier = Modifier.width(6.dp)) - Text( - text = formatVoiceRecordTimer(voiceElapsedMs), - color = recordingTextColor, - fontSize = 15.sp, - fontWeight = FontWeight.Bold - ) - } - - Spacer(modifier = Modifier.width(12.dp)) - - // Center: SlideToCancel or Waveform+Controls - AnimatedContent( - targetState = recordUiState == RecordUiState.LOCKED || recordUiState == RecordUiState.PAUSED, - modifier = Modifier - .weight(1f) - .graphicsLayer { - alpha = recordUiAlpha - translationX = with(density) { recordUiShift.toPx() } - }, - transitionSpec = { - fadeIn(tween(200)) togetherWith fadeOut(tween(200)) - }, - label = "record_center_content" - ) { isLockedOrPaused -> - if (isLockedOrPaused) { - Row( - verticalAlignment = Alignment.CenterVertically, - modifier = Modifier.fillMaxWidth() - ) { - VoiceWaveformBar( - waves = voiceWaves, - isDarkTheme = isDarkTheme, - modifier = Modifier.weight(1f) - ) - Spacer(modifier = Modifier.width(8.dp)) - RecordLockedControls( - isPaused = recordUiState == RecordUiState.PAUSED, - isDarkTheme = isDarkTheme, - onDelete = { + .height(44.dp) + .padding(end = 52.dp), // space for circle overlay + transitionSpec = { + fadeIn(tween(200)) togetherWith fadeOut(tween(200)) + }, + label = "record_panel_mode" + ) { locked -> + if (locked) { + // ── LOCKED/PAUSED panel (Telegram: recordedAudioPanel) ── + // [Delete 44dp] [Waveform fills rest] + Row( + modifier = Modifier + .fillMaxSize() + .clip(RoundedCornerShape(22.dp)) + .background(recordingPanelColor), + verticalAlignment = Alignment.CenterVertically + ) { + // Delete button — Telegram: 44×44dp, Lottie trash icon + Box( + modifier = Modifier + .size(44.dp) + .clickable( + interactionSource = remember { MutableInteractionSource() }, + indication = null + ) { inputJumpLog("tap DELETE (locked/paused) mode=$recordMode state=$recordUiState") stopVoiceRecording(send = false) }, - onTogglePause = { - inputJumpLog("tap PAUSE/RESUME mode=$recordMode state=$recordUiState") - if (recordUiState == RecordUiState.PAUSED) { - resumeVoiceRecording() - } else { - pauseVoiceRecording() - } - } + contentAlignment = Alignment.Center + ) { + Icon( + imageVector = Icons.Default.Close, + contentDescription = "Delete recording", + tint = if (isDarkTheme) Color(0xFFFF5A5A) else Color(0xFFE84D4D), + modifier = Modifier.size(20.dp) ) } - } else { + + // Waveform — Telegram: 32dp height, fills remaining width + VoiceWaveformBar( + waves = voiceWaves, + isDarkTheme = isDarkTheme, + modifier = Modifier + .weight(1f) + .padding(end = 4.dp) + ) + } + } else { + // ── RECORDING panel ── + // [dot][timer] [◀ Slide to cancel] + Row( + modifier = Modifier + .fillMaxSize() + .clip(RoundedCornerShape(22.dp)) + .background(recordingPanelColor) + .padding(start = 13.dp), + verticalAlignment = Alignment.CenterVertically + ) { + // Blink dot + timer + Row( + modifier = Modifier + .graphicsLayer { + alpha = recordUiAlpha + translationX = with(density) { recordUiShift.toPx() } + }, + verticalAlignment = Alignment.CenterVertically + ) { + RecordBlinkDot(isDarkTheme = isDarkTheme) + Spacer(modifier = Modifier.width(6.dp)) + Text( + text = formatVoiceRecordTimer(voiceElapsedMs), + color = recordingTextColor, + fontSize = 15.sp, + fontWeight = FontWeight.Bold + ) + } + + Spacer(modifier = Modifier.width(12.dp)) + + // Slide to cancel SlideToCancel( slideDx = slideDx, cancelThresholdPx = cancelDragThresholdPx, isDarkTheme = isDarkTheme, - modifier = Modifier.fillMaxWidth() + modifier = Modifier + .weight(1f) + .graphicsLayer { + alpha = recordUiAlpha + translationX = with(density) { recordUiShift.toPx() } + } ) } }