feat: add media selection mode to chat info grid
This commit is contained in:
@@ -5938,6 +5938,17 @@ private data class ChatInfoEntry(
|
||||
val previewIsVideo: Boolean = false,
|
||||
)
|
||||
|
||||
private fun buildChatInfoEntrySelectionKey(entry: ChatInfoEntry): String {
|
||||
return listOfNotNull(
|
||||
entry.type.name,
|
||||
entry.sourceMessageId?.toString(),
|
||||
entry.resourceUrl,
|
||||
entry.previewImageUrl,
|
||||
entry.title,
|
||||
).joinToString("|")
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
@Composable
|
||||
private fun ChatInfoTabContent(
|
||||
tab: ChatInfoTab,
|
||||
@@ -6013,58 +6024,153 @@ private fun ChatInfoTabContent(
|
||||
return
|
||||
}
|
||||
if (tab == ChatInfoTab.Media) {
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Fixed(3),
|
||||
var selectedMediaKeys by remember(filtered) { mutableStateOf(setOf<String>()) }
|
||||
val selectionCount = selectedMediaKeys.size
|
||||
val selectedEntries = remember(filtered, selectionCount) {
|
||||
filtered.filter { entry -> buildChatInfoEntrySelectionKey(entry) in selectedMediaKeys }
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(320.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(2.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(2.dp),
|
||||
.height(372.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp),
|
||||
) {
|
||||
items(filtered.take(120)) { entry ->
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(1f)
|
||||
.clip(RoundedCornerShape(4.dp))
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.35f))
|
||||
.clickable { onEntryClick(entry) },
|
||||
AnimatedVisibility(visible = selectionCount > 0) {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
shape = RoundedCornerShape(18.dp),
|
||||
color = MaterialTheme.colorScheme.surface.copy(alpha = 0.72f),
|
||||
) {
|
||||
if (!entry.previewImageUrl.isNullOrBlank()) {
|
||||
AsyncImage(
|
||||
model = entry.previewImageUrl,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentScale = ContentScale.Crop,
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Movie,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.size(20.dp),
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 14.dp, vertical = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.chat_info_selected_count, selectionCount),
|
||||
modifier = Modifier.weight(1f),
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
)
|
||||
if (selectedEntries.size == 1) {
|
||||
TextButton(onClick = { onEntryClick(selectedEntries.first()) }) {
|
||||
Text(stringResource(id = R.string.chat_info_open_selected))
|
||||
}
|
||||
}
|
||||
TextButton(onClick = { selectedMediaKeys = emptySet() }) {
|
||||
Text(stringResource(id = R.string.chat_info_clear_selection))
|
||||
}
|
||||
}
|
||||
if (entry.previewIsVideo) {
|
||||
}
|
||||
}
|
||||
LazyVerticalGrid(
|
||||
columns = GridCells.Adaptive(minSize = 96.dp),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
) {
|
||||
items(filtered.take(120)) { entry ->
|
||||
val selectionKey = remember(entry) { buildChatInfoEntrySelectionKey(entry) }
|
||||
val isSelected = selectionKey in selectedMediaKeys
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(1f)
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.35f))
|
||||
.combinedClickable(
|
||||
onClick = {
|
||||
if (selectionCount > 0) {
|
||||
if (isSelected) {
|
||||
selectedMediaKeys = selectedMediaKeys - selectionKey
|
||||
} else {
|
||||
selectedMediaKeys = selectedMediaKeys + selectionKey
|
||||
}
|
||||
} else {
|
||||
onEntryClick(entry)
|
||||
}
|
||||
},
|
||||
onLongClick = {
|
||||
if (isSelected) {
|
||||
selectedMediaKeys = selectedMediaKeys - selectionKey
|
||||
} else {
|
||||
selectedMediaKeys = selectedMediaKeys + selectionKey
|
||||
}
|
||||
},
|
||||
),
|
||||
) {
|
||||
if (!entry.previewImageUrl.isNullOrBlank()) {
|
||||
AsyncImage(
|
||||
model = entry.previewImageUrl,
|
||||
contentDescription = null,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentScale = ContentScale.Crop,
|
||||
)
|
||||
} else {
|
||||
Icon(
|
||||
imageVector = Icons.Filled.Movie,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.size(20.dp),
|
||||
)
|
||||
}
|
||||
if (entry.previewIsVideo) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
color = Color.Black.copy(alpha = 0.5f),
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
.padding(6.dp),
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.chat_media_badge_video),
|
||||
modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = Color.White,
|
||||
)
|
||||
}
|
||||
}
|
||||
if (isSelected) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.primary.copy(alpha = 0.22f)),
|
||||
)
|
||||
}
|
||||
Surface(
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
color = Color.Black.copy(alpha = 0.5f),
|
||||
modifier = Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
.align(Alignment.TopEnd)
|
||||
.padding(6.dp),
|
||||
shape = CircleShape,
|
||||
color = if (isSelected) {
|
||||
MaterialTheme.colorScheme.primary
|
||||
} else {
|
||||
Color.Black.copy(alpha = 0.38f)
|
||||
},
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(id = R.string.chat_media_badge_video),
|
||||
modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp),
|
||||
style = MaterialTheme.typography.labelSmall,
|
||||
color = Color.White,
|
||||
Icon(
|
||||
imageVector = if (isSelected) Icons.Filled.Check else Icons.Filled.RadioButtonUnchecked,
|
||||
contentDescription = null,
|
||||
tint = Color.White,
|
||||
modifier = Modifier
|
||||
.padding(4.dp)
|
||||
.size(14.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Text(
|
||||
text = stringResource(id = R.string.chat_info_media_selection_hint),
|
||||
style = MaterialTheme.typography.bodySmall,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -162,6 +162,10 @@
|
||||
<string name="chat_info_tab_members">Участники</string>
|
||||
<string name="chat_info_empty">Пока нет: %1$s</string>
|
||||
<string name="chat_info_no_members_data">Нет данных об участниках</string>
|
||||
<string name="chat_info_selected_count">Выбрано: %1$d</string>
|
||||
<string name="chat_info_open_selected">Открыть</string>
|
||||
<string name="chat_info_clear_selection">Сбросить</string>
|
||||
<string name="chat_info_media_selection_hint">Удерживайте медиа, чтобы перейти в режим выбора.</string>
|
||||
<string name="chat_members_header">Участники (%1$d)</string>
|
||||
<string name="chat_banned_header">Заблокированные (%1$d)</string>
|
||||
<string name="chat_member_action_promote">Повысить</string>
|
||||
|
||||
@@ -162,6 +162,10 @@
|
||||
<string name="chat_info_tab_members">Members</string>
|
||||
<string name="chat_info_empty">No %1$s yet</string>
|
||||
<string name="chat_info_no_members_data">No members data</string>
|
||||
<string name="chat_info_selected_count">%1$d selected</string>
|
||||
<string name="chat_info_open_selected">Open</string>
|
||||
<string name="chat_info_clear_selection">Clear</string>
|
||||
<string name="chat_info_media_selection_hint">Long press media to select multiple items.</string>
|
||||
<string name="chat_members_header">Members (%1$d)</string>
|
||||
<string name="chat_banned_header">Banned (%1$d)</string>
|
||||
<string name="chat_member_action_promote">Promote</string>
|
||||
|
||||
Reference in New Issue
Block a user