feat: animate pinned and composer state transitions
Some checks failed
Android CI / android (push) Failing after 4m32s
Android Release / release (push) Has started running
CI / test (push) Has been cancelled

feat: add smooth pinned-message visibility changes and voice composer mode transitions
This commit is contained in:
2026-04-05 15:28:41 +03:00
parent b20ef6e08f
commit 0f55aef179

View File

@@ -82,6 +82,8 @@ import androidx.compose.runtime.derivedStateOf
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
import androidx.compose.animation.core.LinearEasing
@@ -1069,7 +1071,18 @@ private fun ChatScreen(
)
}
val pinnedMessage = state.pinnedMessage
if (pinnedMessage != null && dismissedPinnedMessageId != pinnedMessage.id) {
AnimatedVisibility(
visible = pinnedMessage != null && dismissedPinnedMessageId != pinnedMessage?.id,
enter = fadeIn(animationSpec = tween(180)) + slideInVertically(
initialOffsetY = { -it / 3 },
animationSpec = tween(180),
),
exit = fadeOut(animationSpec = tween(120)) + slideOutVertically(
targetOffsetY = { -it / 3 },
animationSpec = tween(120),
),
) {
val resolvedPinnedMessage = pinnedMessage ?: return@AnimatedVisibility
Row(
modifier = Modifier
.fillMaxWidth()
@@ -1092,12 +1105,12 @@ private fun ChatScreen(
fontWeight = FontWeight.SemiBold,
)
Text(
text = pinnedMessage.text?.takeIf { it.isNotBlank() } ?: "[${pinnedMessage.type}]",
text = resolvedPinnedMessage.text?.takeIf { it.isNotBlank() } ?: "[${resolvedPinnedMessage.type}]",
style = MaterialTheme.typography.labelMedium,
maxLines = 2,
)
}
IconButton(onClick = { dismissedPinnedMessageId = pinnedMessage.id }) {
IconButton(onClick = { dismissedPinnedMessageId = resolvedPinnedMessage.id }) {
Icon(imageVector = Icons.Filled.Close, contentDescription = "Hide pinned")
}
}
@@ -1825,7 +1838,17 @@ private fun ChatScreen(
.padding(horizontal = 8.dp, vertical = 6.dp),
verticalArrangement = Arrangement.spacedBy(6.dp),
) {
if (state.isRecordingVoice) {
AnimatedVisibility(
visible = state.isRecordingVoice,
enter = fadeIn(animationSpec = tween(150)) + slideInVertically(
initialOffsetY = { it / 3 },
animationSpec = tween(150),
),
exit = fadeOut(animationSpec = tween(100)) + slideOutVertically(
targetOffsetY = { it / 3 },
animationSpec = tween(100),
),
) {
VoiceRecordingStatusRow(
durationMs = state.voiceRecordingDurationMs,
hint = state.voiceRecordingHint,
@@ -1833,7 +1856,18 @@ private fun ChatScreen(
onCancel = onVoiceRecordCancel,
onSend = onVoiceRecordSend,
)
} else {
}
AnimatedVisibility(
visible = !state.isRecordingVoice,
enter = fadeIn(animationSpec = tween(150)) + slideInVertically(
initialOffsetY = { it / 3 },
animationSpec = tween(150),
),
exit = fadeOut(animationSpec = tween(100)) + slideOutVertically(
targetOffsetY = { it / 3 },
animationSpec = tween(100),
),
) {
Row(
modifier = Modifier
.fillMaxWidth()