Меню для сообщений

This commit is contained in:
Халимов Рустам
2026-04-17 23:44:11 +03:00
parent ed7521a563
commit 754e9e8ad0
10 changed files with 277 additions and 73 deletions
@@ -261,43 +261,62 @@ fun ChatDetailScreen(
Scaffold(
topBar = {
TopAppBar(
title = {
Row(verticalAlignment = Alignment.CenterVertically) {
AppAvatar(
url = state.chatAvatar,
name = state.chatName ?: chatName,
size = 36.dp,
modifier = Modifier.padding(end = 8.dp)
)
Column {
Text(state.chatName ?: chatName, style = MaterialTheme.typography.titleMedium)
if (state.isTyping) {
Text(
stringResource(R.string.typing),
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.primary
)
if (state.selectedMessageIds.isNotEmpty()) {
TopAppBar(
title = { Text("${state.selectedMessageIds.size}") },
navigationIcon = {
IconButton(onClick = { viewModel.clearSelection() }) {
Icon(Icons.Default.Close, contentDescription = "Cancel")
}
},
actions = {
IconButton(onClick = { /* TODO: Forward */ }) {
Icon(Icons.Default.Forward, contentDescription = "Forward")
}
IconButton(onClick = { /* TODO: Delete */ }) {
Icon(Icons.Default.Delete, contentDescription = "Delete")
}
}
)
} else {
TopAppBar(
title = {
Row(verticalAlignment = Alignment.CenterVertically) {
AppAvatar(
url = state.chatAvatar,
name = state.chatName ?: chatName,
size = 36.dp,
modifier = Modifier.padding(end = 8.dp)
)
Column {
Text(state.chatName ?: chatName, style = MaterialTheme.typography.titleMedium)
if (state.isTyping) {
Text(
stringResource(R.string.typing),
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.primary
)
}
}
}
},
navigationIcon = {
IconButton(onClick = onBack) {
Icon(Icons.Default.ArrowBack, contentDescription = stringResource(R.string.back))
}
},
actions = {
if (state.canCall) {
IconButton(onClick = { /* Вызов (WebRTC) */ }) {
Icon(Icons.Default.Call, contentDescription = stringResource(R.string.call))
}
IconButton(onClick = { /* Видеозвонок (WebRTC) */ }) {
Icon(Icons.Default.VideoCall, contentDescription = stringResource(R.string.video_call))
}
}
}
},
navigationIcon = {
IconButton(onClick = onBack) {
Icon(Icons.Default.ArrowBack, contentDescription = stringResource(R.string.back))
}
},
actions = {
if (state.canCall) {
IconButton(onClick = { /* Вызов (WebRTC) */ }) {
Icon(Icons.Default.Call, contentDescription = stringResource(R.string.call))
}
IconButton(onClick = { /* Видеозвонок (WebRTC) */ }) {
Icon(Icons.Default.VideoCall, contentDescription = stringResource(R.string.video_call))
}
}
}
)
)
}
}
) { paddingValues ->
Column(
@@ -345,7 +364,6 @@ fun ChatDetailScreen(
onVoiceFinished = { speed -> playNextVoiceMessage(item.message.id, speed) },
onReactionClick = { emoji -> viewModel.addReaction(item.message.id, emoji) },
onReplyClick = { reply ->
// TODO: Scroll to reply
val index = listItems.indexOfFirst {
it is MessageListItem.MessageItem && it.message.id == reply.id
}
@@ -353,6 +371,9 @@ fun ChatDetailScreen(
scope.launch { listState.animateScrollToItem(index) }
}
},
onSelect = { viewModel.toggleSelection(it) },
isSelected = state.selectedMessageIds.contains(item.message.id),
isSelectionMode = state.selectedMessageIds.isNotEmpty(),
onMediaClick = { clickedMedia ->
val isMedia = clickedMedia.type.startsWith("image") ||
clickedMedia.type.startsWith("video") ||
@@ -491,11 +512,12 @@ fun ChatDetailScreen(
}
// Панель ввода
Column(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f))
) {
if (state.selectedMessageIds.isEmpty()) {
Column(
modifier = Modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f))
) {
// Pending Attachments Bar
if (state.pendingAttachments.isNotEmpty()) {
Row(
@@ -895,6 +917,7 @@ fun ChatDetailScreen(
viewModel.loadGifCategories()
}
}
}
}
}
@@ -42,7 +42,8 @@ data class ChatDetailState(
val isUploading: Boolean = false,
val isCompressionEnabled: Boolean = true,
val inputText: String = "",
val replyingMessage: Message? = null
val replyingMessage: Message? = null,
val selectedMessageIds: Set<String> = emptySet()
)
@HiltViewModel
@@ -312,6 +313,21 @@ class ChatDetailViewModel @Inject constructor(
_state.update { it.copy(replyingMessage = message) }
}
fun toggleSelection(messageId: String) {
_state.update { s ->
val newSelection = if (s.selectedMessageIds.contains(messageId)) {
s.selectedMessageIds - messageId
} else {
s.selectedMessageIds + messageId
}
s.copy(selectedMessageIds = newSelection)
}
}
fun clearSelection() {
_state.update { it.copy(selectedMessageIds = emptySet()) }
}
fun cancelReply() {
_state.update { it.copy(replyingMessage = null) }
}