feat: animate pinned and composer state transitions
feat: add smooth pinned-message visibility changes and voice composer mode transitions
This commit is contained in:
@@ -82,6 +82,8 @@ import androidx.compose.runtime.derivedStateOf
|
|||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
|
import androidx.compose.animation.slideInVertically
|
||||||
|
import androidx.compose.animation.slideOutVertically
|
||||||
import androidx.compose.animation.scaleIn
|
import androidx.compose.animation.scaleIn
|
||||||
import androidx.compose.animation.scaleOut
|
import androidx.compose.animation.scaleOut
|
||||||
import androidx.compose.animation.core.LinearEasing
|
import androidx.compose.animation.core.LinearEasing
|
||||||
@@ -1069,7 +1071,18 @@ private fun ChatScreen(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
val pinnedMessage = state.pinnedMessage
|
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(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -1092,12 +1105,12 @@ private fun ChatScreen(
|
|||||||
fontWeight = FontWeight.SemiBold,
|
fontWeight = FontWeight.SemiBold,
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = pinnedMessage.text?.takeIf { it.isNotBlank() } ?: "[${pinnedMessage.type}]",
|
text = resolvedPinnedMessage.text?.takeIf { it.isNotBlank() } ?: "[${resolvedPinnedMessage.type}]",
|
||||||
style = MaterialTheme.typography.labelMedium,
|
style = MaterialTheme.typography.labelMedium,
|
||||||
maxLines = 2,
|
maxLines = 2,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
IconButton(onClick = { dismissedPinnedMessageId = pinnedMessage.id }) {
|
IconButton(onClick = { dismissedPinnedMessageId = resolvedPinnedMessage.id }) {
|
||||||
Icon(imageVector = Icons.Filled.Close, contentDescription = "Hide pinned")
|
Icon(imageVector = Icons.Filled.Close, contentDescription = "Hide pinned")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1825,7 +1838,17 @@ private fun ChatScreen(
|
|||||||
.padding(horizontal = 8.dp, vertical = 6.dp),
|
.padding(horizontal = 8.dp, vertical = 6.dp),
|
||||||
verticalArrangement = Arrangement.spacedBy(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(
|
VoiceRecordingStatusRow(
|
||||||
durationMs = state.voiceRecordingDurationMs,
|
durationMs = state.voiceRecordingDurationMs,
|
||||||
hint = state.voiceRecordingHint,
|
hint = state.voiceRecordingHint,
|
||||||
@@ -1833,7 +1856,18 @@ private fun ChatScreen(
|
|||||||
onCancel = onVoiceRecordCancel,
|
onCancel = onVoiceRecordCancel,
|
||||||
onSend = onVoiceRecordSend,
|
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(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|||||||
Reference in New Issue
Block a user