feat: animate chat action menu presentation
feat: add subtle scale and fade transitions to the reaction and context action sheet
This commit is contained in:
@@ -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.scaleIn
|
||||
import androidx.compose.animation.scaleOut
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.RepeatMode
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
@@ -1342,6 +1344,10 @@ private fun ChatScreen(
|
||||
if (actionMenuMessage != null && state.actionState.mode != MessageSelectionMode.MULTI) {
|
||||
val selected = actionMenuMessage
|
||||
if (selected != null) {
|
||||
var animateActionMenuContent by remember(selected.id) { mutableStateOf(false) }
|
||||
LaunchedEffect(selected.id) {
|
||||
animateActionMenuContent = true
|
||||
}
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = {
|
||||
actionMenuMessage = null
|
||||
@@ -1349,140 +1355,152 @@ private fun ChatScreen(
|
||||
},
|
||||
sheetState = actionSheetState,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
AnimatedVisibility(
|
||||
visible = animateActionMenuContent,
|
||||
enter = fadeIn(animationSpec = tween(150)) + scaleIn(
|
||||
initialScale = 0.94f,
|
||||
animationSpec = tween(150),
|
||||
),
|
||||
exit = fadeOut(animationSpec = tween(100)) + scaleOut(
|
||||
targetScale = 0.96f,
|
||||
animationSpec = tween(100),
|
||||
),
|
||||
) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
listOf("❤️", "👍", "👎", "🔥", "🥰", "👏", "😁").forEach { emoji ->
|
||||
Surface(
|
||||
shape = CircleShape,
|
||||
color = MaterialTheme.colorScheme.surfaceVariant,
|
||||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.clickable {
|
||||
onSelectMessage(selected)
|
||||
onToggleReaction(emoji)
|
||||
actionMenuMessage = null
|
||||
}
|
||||
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||
) {
|
||||
Text(text = emoji)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
listOf("❤️", "👍", "👎", "🔥", "🥰", "👏", "😁").forEach { emoji ->
|
||||
Surface(
|
||||
shape = CircleShape,
|
||||
color = MaterialTheme.colorScheme.surfaceVariant,
|
||||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.clickable {
|
||||
onSelectMessage(selected)
|
||||
onToggleReaction(emoji)
|
||||
actionMenuMessage = null
|
||||
}
|
||||
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||
) {
|
||||
Text(text = emoji)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.clickable {
|
||||
onReplySelected(selected)
|
||||
actionMenuMessage = null
|
||||
},
|
||||
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.35f),
|
||||
) {
|
||||
Row(
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 12.dp, vertical = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Icon(imageVector = Icons.AutoMirrored.Filled.Reply, contentDescription = null)
|
||||
Text(stringResource(id = R.string.chat_action_reply), style = MaterialTheme.typography.bodyLarge)
|
||||
}
|
||||
}
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.clickable(enabled = state.selectedCanEdit) {
|
||||
onEditSelected(selected)
|
||||
actionMenuMessage = null
|
||||
},
|
||||
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.35f),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 12.dp, vertical = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Icon(imageVector = Icons.Filled.Edit, contentDescription = null)
|
||||
Text(
|
||||
stringResource(id = R.string.chat_action_edit),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = if (state.selectedCanEdit) {
|
||||
MaterialTheme.colorScheme.onSurface
|
||||
} else {
|
||||
MaterialTheme.colorScheme.onSurfaceVariant
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.clickable {
|
||||
onReplySelected(selected)
|
||||
actionMenuMessage = null
|
||||
},
|
||||
)
|
||||
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.35f),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 12.dp, vertical = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Icon(imageVector = Icons.AutoMirrored.Filled.Reply, contentDescription = null)
|
||||
Text(stringResource(id = R.string.chat_action_reply), style = MaterialTheme.typography.bodyLarge)
|
||||
}
|
||||
}
|
||||
}
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.clickable {
|
||||
onSelectMessage(selected)
|
||||
onForwardSelected()
|
||||
actionMenuMessage = null
|
||||
},
|
||||
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.35f),
|
||||
) {
|
||||
Row(
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 12.dp, vertical = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.clickable(enabled = state.selectedCanEdit) {
|
||||
onEditSelected(selected)
|
||||
actionMenuMessage = null
|
||||
},
|
||||
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.35f),
|
||||
) {
|
||||
Icon(imageVector = Icons.AutoMirrored.Filled.Forward, contentDescription = null)
|
||||
Text(stringResource(id = R.string.chat_action_forward), style = MaterialTheme.typography.bodyLarge)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 12.dp, vertical = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Icon(imageVector = Icons.Filled.Edit, contentDescription = null)
|
||||
Text(
|
||||
stringResource(id = R.string.chat_action_edit),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = if (state.selectedCanEdit) {
|
||||
MaterialTheme.colorScheme.onSurface
|
||||
} else {
|
||||
MaterialTheme.colorScheme.onSurfaceVariant
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.clickable {
|
||||
onSelectMessage(selected)
|
||||
pendingDeleteForAll = false
|
||||
showDeleteDialog = true
|
||||
actionMenuMessage = null
|
||||
},
|
||||
color = MaterialTheme.colorScheme.errorContainer.copy(alpha = 0.45f),
|
||||
) {
|
||||
Row(
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 12.dp, vertical = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.clickable {
|
||||
onSelectMessage(selected)
|
||||
onForwardSelected()
|
||||
actionMenuMessage = null
|
||||
},
|
||||
color = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.35f),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.DeleteOutline,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
Text(
|
||||
stringResource(id = R.string.chat_action_delete),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 12.dp, vertical = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Icon(imageVector = Icons.AutoMirrored.Filled.Forward, contentDescription = null)
|
||||
Text(stringResource(id = R.string.chat_action_forward), style = MaterialTheme.typography.bodyLarge)
|
||||
}
|
||||
}
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.clickable {
|
||||
onSelectMessage(selected)
|
||||
pendingDeleteForAll = false
|
||||
showDeleteDialog = true
|
||||
actionMenuMessage = null
|
||||
},
|
||||
color = MaterialTheme.colorScheme.errorContainer.copy(alpha = 0.45f),
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 12.dp, vertical = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.DeleteOutline,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
Text(
|
||||
stringResource(id = R.string.chat_action_delete),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = MaterialTheme.colorScheme.error,
|
||||
)
|
||||
}
|
||||
}
|
||||
TextButton(
|
||||
onClick = {
|
||||
actionMenuMessage = null
|
||||
onClearSelection()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) { Text(stringResource(id = R.string.common_close)) }
|
||||
}
|
||||
TextButton(
|
||||
onClick = {
|
||||
actionMenuMessage = null
|
||||
onClearSelection()
|
||||
},
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) { Text(stringResource(id = R.string.common_close)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user