Improve message forwarding picker
This commit is contained in:
@@ -2088,14 +2088,16 @@ private fun ChatScreen(vm: QMaxViewModel) {
|
||||
onClose = { materialsOpen = false }
|
||||
)
|
||||
}
|
||||
if (state.forwardTarget != null) {
|
||||
val forwardTarget = state.forwardTarget
|
||||
if (forwardTarget != null) {
|
||||
ForwardMessageDialog(
|
||||
message = forwardTarget,
|
||||
chats = state.chats,
|
||||
selectedChatId = chat.id,
|
||||
session = session,
|
||||
cachedAvatarPaths = state.cachedAvatarPaths,
|
||||
onCacheAvatar = vm::cacheAvatar,
|
||||
onSelect = vm::forwardTo,
|
||||
onSend = vm::forwardTo,
|
||||
onClose = vm::closeForwardPicker
|
||||
)
|
||||
}
|
||||
@@ -2103,12 +2105,13 @@ private fun ChatScreen(vm: QMaxViewModel) {
|
||||
|
||||
@Composable
|
||||
private fun ForwardMessageDialog(
|
||||
message: MessageDto,
|
||||
chats: List<ChatDto>,
|
||||
selectedChatId: String?,
|
||||
session: QMaxSession?,
|
||||
cachedAvatarPaths: Map<String, String>,
|
||||
onCacheAvatar: (String) -> Unit,
|
||||
onSelect: (ChatDto) -> Unit,
|
||||
onSend: (List<ChatDto>) -> Unit,
|
||||
onClose: () -> Unit
|
||||
) {
|
||||
val sortedChats = remember(chats) {
|
||||
@@ -2117,30 +2120,70 @@ private fun ForwardMessageDialog(
|
||||
.thenByDescending { it.lastMessageAt.orEmpty() }
|
||||
)
|
||||
}
|
||||
var query by rememberSaveable { mutableStateOf("") }
|
||||
var selectedChatIds by remember { mutableStateOf(emptySet<String>()) }
|
||||
val filteredChats = remember(sortedChats, query) {
|
||||
val term = query.trim()
|
||||
if (term.isBlank()) {
|
||||
sortedChats
|
||||
} else {
|
||||
sortedChats.filter { chat ->
|
||||
chat.title.contains(term, ignoreCase = true) ||
|
||||
chat.lastMessagePreview?.contains(term, ignoreCase = true) == true
|
||||
}
|
||||
}
|
||||
}
|
||||
val selectedChats = remember(sortedChats, selectedChatIds) {
|
||||
sortedChats.filter { it.id in selectedChatIds }
|
||||
}
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onClose,
|
||||
confirmButton = {
|
||||
TextButton(onClick = onClose) {
|
||||
Text("\u041e\u0442\u043c\u0435\u043d\u0430")
|
||||
Button(
|
||||
onClick = { onSend(selectedChats) },
|
||||
enabled = selectedChats.isNotEmpty()
|
||||
) {
|
||||
Icon(Icons.AutoMirrored.Filled.Send, contentDescription = null)
|
||||
Spacer(Modifier.width(6.dp))
|
||||
Text(if (selectedChats.size <= 1) "Отправить" else "Отправить (${selectedChats.size})")
|
||||
}
|
||||
},
|
||||
title = { Text("\u041f\u0435\u0440\u0435\u0441\u043b\u0430\u0442\u044c") },
|
||||
dismissButton = {
|
||||
TextButton(onClick = onClose) {
|
||||
Text("Отмена")
|
||||
}
|
||||
},
|
||||
title = { Text("Переслать") },
|
||||
text = {
|
||||
if (sortedChats.isEmpty()) {
|
||||
EmptyState("\u0427\u0430\u0442\u043e\u0432 \u043f\u043e\u043a\u0430 \u043d\u0435\u0442", modifier = Modifier.height(220.dp))
|
||||
} else {
|
||||
LazyColumn(Modifier.fillMaxWidth().height(430.dp)) {
|
||||
items(sortedChats, key = { it.id }) { chat ->
|
||||
ForwardChatRow(
|
||||
chat = chat,
|
||||
isCurrent = chat.id == selectedChatId,
|
||||
session = session,
|
||||
cachedAvatarPaths = cachedAvatarPaths,
|
||||
onCacheAvatar = onCacheAvatar,
|
||||
onClick = { onSelect(chat) }
|
||||
)
|
||||
HorizontalDivider(color = Color(0xFFE9EEF2), thickness = 0.6.dp, modifier = Modifier.padding(start = 56.dp))
|
||||
Column(Modifier.fillMaxWidth()) {
|
||||
ForwardMessagePreview(message)
|
||||
Spacer(Modifier.height(10.dp))
|
||||
ForwardSearchField(query = query, onQuery = { query = it })
|
||||
Spacer(Modifier.height(8.dp))
|
||||
when {
|
||||
sortedChats.isEmpty() -> EmptyState("Чатов пока нет", modifier = Modifier.height(220.dp))
|
||||
filteredChats.isEmpty() -> EmptyState("Ничего не найдено", modifier = Modifier.height(220.dp))
|
||||
else -> LazyColumn(Modifier.fillMaxWidth().height(430.dp)) {
|
||||
items(filteredChats, key = { it.id }) { chat ->
|
||||
val selected = chat.id in selectedChatIds
|
||||
ForwardChatRow(
|
||||
chat = chat,
|
||||
isCurrent = chat.id == selectedChatId,
|
||||
selected = selected,
|
||||
session = session,
|
||||
cachedAvatarPaths = cachedAvatarPaths,
|
||||
onCacheAvatar = onCacheAvatar,
|
||||
onClick = {
|
||||
selectedChatIds = if (selected) {
|
||||
selectedChatIds - chat.id
|
||||
} else {
|
||||
selectedChatIds + chat.id
|
||||
}
|
||||
}
|
||||
)
|
||||
HorizontalDivider(color = Color(0xFFE9EEF2), thickness = 0.6.dp, modifier = Modifier.padding(start = 56.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2152,6 +2195,7 @@ private fun ForwardMessageDialog(
|
||||
private fun ForwardChatRow(
|
||||
chat: ChatDto,
|
||||
isCurrent: Boolean,
|
||||
selected: Boolean,
|
||||
session: QMaxSession?,
|
||||
cachedAvatarPaths: Map<String, String>,
|
||||
onCacheAvatar: (String) -> Unit,
|
||||
@@ -2201,7 +2245,82 @@ private fun ForwardChatRow(
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(formatChatTime(chat.lastMessageAt), color = QMaxMuted, style = MaterialTheme.typography.labelSmall)
|
||||
if (selected) {
|
||||
Surface(color = QMaxBlue, shape = CircleShape, modifier = Modifier.size(26.dp)) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
Icon(Icons.Filled.Check, contentDescription = "Выбрано", tint = Color.White, modifier = Modifier.size(18.dp))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Text(formatChatTime(chat.lastMessageAt), color = QMaxMuted, style = MaterialTheme.typography.labelSmall)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ForwardMessagePreview(message: MessageDto) {
|
||||
Surface(color = Color(0xFFE7F1FA), shape = RoundedCornerShape(10.dp)) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = 10.dp, vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.width(3.dp)
|
||||
.height(38.dp)
|
||||
.background(QMaxBlue)
|
||||
)
|
||||
Spacer(Modifier.width(9.dp))
|
||||
Column(Modifier.weight(1f)) {
|
||||
Text(
|
||||
replyPreviewAuthor(message),
|
||||
color = QMaxBlue,
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Text(
|
||||
replyPreviewBody(message),
|
||||
color = QMaxMuted,
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ForwardSearchField(query: String, onQuery: (String) -> Unit) {
|
||||
Surface(color = Color(0xFFF1F1F3), shape = RoundedCornerShape(24.dp)) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().height(46.dp).padding(start = 14.dp, end = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(Icons.Filled.Search, contentDescription = null, tint = QMaxMuted, modifier = Modifier.size(23.dp))
|
||||
TextField(
|
||||
value = query,
|
||||
onValueChange = onQuery,
|
||||
placeholder = { Text("Кому переслать", color = Color(0xFF8A8E93)) },
|
||||
singleLine = true,
|
||||
colors = TextFieldDefaults.colors(
|
||||
focusedTextColor = QMaxText,
|
||||
unfocusedTextColor = QMaxText,
|
||||
focusedContainerColor = Color.Transparent,
|
||||
unfocusedContainerColor = Color.Transparent,
|
||||
focusedIndicatorColor = Color.Transparent,
|
||||
unfocusedIndicatorColor = Color.Transparent,
|
||||
cursorColor = QMaxBlue
|
||||
),
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
if (query.isNotBlank()) {
|
||||
IconButton(onClick = { onQuery("") }) {
|
||||
Icon(Icons.Filled.Close, contentDescription = "Очистить поиск", tint = QMaxMuted)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -894,15 +894,23 @@ class QMaxViewModel(private val repository: QMaxRepository) : ViewModel() {
|
||||
state.value = state.value.copy(forwardTarget = null)
|
||||
}
|
||||
|
||||
fun forwardTo(chat: ChatDto) = launchLoading(false) {
|
||||
fun forwardTo(targetChats: List<ChatDto>) = launchLoading(false) {
|
||||
val session = requireSession()
|
||||
val source = state.value.forwardTarget ?: return@launchLoading
|
||||
val forwarded = repository.forwardMessage(session, source.chatId, source.id, chat.id)
|
||||
val targets = targetChats.distinctBy { it.id }
|
||||
if (targets.isEmpty()) return@launchLoading
|
||||
val forwardedMessages = targets.map { chat ->
|
||||
repository.forwardMessage(session, source.chatId, source.id, chat.id)
|
||||
}
|
||||
val current = state.value
|
||||
state.value = if (current.selectedChat?.id == chat.id) {
|
||||
val openedChatId = current.selectedChat?.id
|
||||
val openedChatForwardedMessages = forwardedMessages.filter { it.chatId == openedChatId }
|
||||
state.value = if (openedChatForwardedMessages.isNotEmpty()) {
|
||||
current.copy(
|
||||
forwardTarget = null,
|
||||
messages = (current.messages.filterNot { it.id == forwarded.id } + forwarded).sortedBy { it.sentAt }
|
||||
messages = (current.messages.filterNot { existing ->
|
||||
openedChatForwardedMessages.any { it.id == existing.id }
|
||||
} + openedChatForwardedMessages).sortedBy { it.sentAt }
|
||||
)
|
||||
} else {
|
||||
current.copy(forwardTarget = null)
|
||||
|
||||
Reference in New Issue
Block a user