Enable forwarding from all attachment types

This commit is contained in:
sevenhill
2026-07-09 18:02:42 +03:00
parent c5330f3086
commit 51d4c7dd1d
@@ -2510,7 +2510,7 @@ private fun MaterialsMediaCell(
private fun MaterialsFileList(items: List<ChatAttachmentItem>, vm: QMaxViewModel) {
LazyColumn(Modifier.fillMaxSize()) {
items(items, key = { it.attachment.id }) { item ->
FileAttachment(item.attachment, vm)
FileAttachment(item.attachment, vm, onLongPress = {})
HorizontalDivider(color = Color(0xFFE7EEF3))
}
}
@@ -2521,7 +2521,7 @@ private fun MaterialsVoiceList(items: List<ChatAttachmentItem>, session: QMaxSes
LazyColumn(Modifier.fillMaxSize()) {
items(items, key = { it.attachment.id }) { item ->
val url = "${session.serverUrl.trimEnd('/')}${item.attachment.downloadPath}"
VoiceAttachment(item.attachment, url, session.accessToken)
VoiceAttachment(item.attachment, url, session.accessToken, onLongPress = {})
HorizontalDivider(color = Color(0xFFE7EEF3))
}
}
@@ -2645,7 +2645,8 @@ private fun MessageRow(
session = session,
vm = vm,
imagePreviewSources = imagePreviewSources,
mediaShape = shape
mediaShape = shape,
onLongPress = { actionsOpen = true }
)
} else {
sortedAttachments.forEach { attachment ->
@@ -2654,7 +2655,8 @@ private fun MessageRow(
session = session,
vm = vm,
imagePreviewSources = imagePreviewSources,
mediaShape = shape
mediaShape = shape,
onLongPress = { actionsOpen = true }
)
}
}
@@ -2936,7 +2938,8 @@ private fun MediaAlbumGrid(
session: QMaxSession,
vm: QMaxViewModel,
imagePreviewSources: List<String>,
mediaShape: RoundedCornerShape
mediaShape: RoundedCornerShape,
onLongPress: () -> Unit
) {
val visible = attachments.take(4)
val extraCount = (attachments.size - visible.size).coerceAtLeast(0)
@@ -2956,6 +2959,7 @@ private fun MediaAlbumGrid(
vm = vm,
imagePreviewSources = imagePreviewSources,
extraCount = extraCount.takeIf { index == visible.lastIndex },
onLongPress = onLongPress,
modifier = Modifier.weight(1f).aspectRatio(1f)
)
}
@@ -2967,6 +2971,7 @@ private fun MediaAlbumGrid(
vm = vm,
imagePreviewSources = imagePreviewSources,
extraCount = null,
onLongPress = onLongPress,
modifier = Modifier.fillMaxWidth().aspectRatio(1.85f)
)
Row(horizontalArrangement = Arrangement.spacedBy(2.dp)) {
@@ -2977,6 +2982,7 @@ private fun MediaAlbumGrid(
vm = vm,
imagePreviewSources = imagePreviewSources,
extraCount = extraCount.takeIf { index == 1 },
onLongPress = onLongPress,
modifier = Modifier.weight(1f).aspectRatio(1f)
)
}
@@ -2993,6 +2999,7 @@ private fun MediaAlbumGrid(
vm = vm,
imagePreviewSources = imagePreviewSources,
extraCount = extraCount.takeIf { isLastVisibleCell },
onLongPress = onLongPress,
modifier = Modifier.weight(1f).aspectRatio(1f)
)
}
@@ -3007,12 +3014,14 @@ private fun MediaAlbumGrid(
}
@Composable
@OptIn(ExperimentalFoundationApi::class)
private fun MediaAlbumCell(
attachment: AttachmentDto,
session: QMaxSession,
vm: QMaxViewModel,
imagePreviewSources: List<String>,
extraCount: Int?,
onLongPress: () -> Unit,
modifier: Modifier = Modifier
) {
val url = rememberAttachmentUrl(session, attachment)
@@ -3037,7 +3046,10 @@ private fun MediaAlbumCell(
contentScale = ContentScale.Crop,
modifier = Modifier
.fillMaxSize()
.clickable { vm.openImage(previewSource, imagePreviewSources) },
.combinedClickable(
onClick = { vm.openImage(previewSource, imagePreviewSources) },
onLongClick = onLongPress
),
onSuccess = {
imageLoaded = true
imageFailed = false
@@ -3091,26 +3103,29 @@ private fun AttachmentView(
session: QMaxSession,
vm: QMaxViewModel,
imagePreviewSources: List<String>,
mediaShape: RoundedCornerShape
mediaShape: RoundedCornerShape,
onLongPress: () -> Unit
) {
val url = rememberAttachmentUrl(session, attachment)
val state by vm.state
val cachedImagePath = state.cachedImagePaths[attachment.id]
when {
attachment.isContactAttachment() -> ContactAttachment(attachment, vm)
attachment.isContactAttachment() -> ContactAttachment(attachment, vm, onLongPress)
attachment.isEmojiAttachment() -> EmojiAttachment(
attachment,
url,
session.accessToken,
cachedImagePath,
vm
vm,
onLongPress
)
attachment.isStickerAttachment() -> StickerAttachment(
attachment,
url,
session.accessToken,
cachedImagePath,
vm
vm,
onLongPress
)
attachment.isImageAttachment() -> ImageAttachment(
attachment,
@@ -3119,21 +3134,24 @@ private fun AttachmentView(
cachedImagePath,
vm,
imagePreviewSources,
mediaShape
mediaShape,
onLongPress
)
attachment.isVideoAttachment() -> VideoAttachment(attachment, url, session.accessToken)
attachment.isVoiceAttachment() || attachment.isAudioAttachment() -> VoiceAttachment(attachment, url, session.accessToken)
else -> FileAttachment(attachment, vm)
attachment.isVideoAttachment() -> VideoAttachment(attachment, url, session.accessToken, onLongPress)
attachment.isVoiceAttachment() || attachment.isAudioAttachment() -> VoiceAttachment(attachment, url, session.accessToken, onLongPress)
else -> FileAttachment(attachment, vm, onLongPress)
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun StickerAttachment(
attachment: AttachmentDto,
url: String,
token: String,
cachedImagePath: String?,
vm: QMaxViewModel
vm: QMaxViewModel,
onLongPress: () -> Unit
) {
LaunchedEffect(attachment.id, url) {
vm.cacheImage(attachment)
@@ -3145,7 +3163,10 @@ private fun StickerAttachment(
modifier = Modifier
.size(156.dp)
.clip(RoundedCornerShape(6.dp))
.clickable { vm.openImage(cachedImagePath ?: url, listOf(cachedImagePath ?: url)) },
.combinedClickable(
onClick = { vm.openImage(cachedImagePath ?: url, listOf(cachedImagePath ?: url)) },
onLongClick = onLongPress
),
contentAlignment = Alignment.Center
) {
AsyncImage(
@@ -3168,13 +3189,15 @@ private fun StickerAttachment(
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun EmojiAttachment(
attachment: AttachmentDto,
url: String,
token: String,
cachedImagePath: String?,
vm: QMaxViewModel
vm: QMaxViewModel,
onLongPress: () -> Unit
) {
LaunchedEffect(attachment.id, url) {
vm.cacheImage(attachment)
@@ -3185,10 +3208,16 @@ private fun EmojiAttachment(
model = model,
contentDescription = attachment.fileName,
contentScale = ContentScale.Fit,
modifier = Modifier.size(emojiSize)
modifier = Modifier
.size(emojiSize)
.combinedClickable(
onClick = {},
onLongClick = onLongPress
)
)
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun ImageAttachment(
attachment: AttachmentDto,
@@ -3197,7 +3226,8 @@ private fun ImageAttachment(
cachedImagePath: String?,
vm: QMaxViewModel,
imagePreviewSources: List<String>,
mediaShape: RoundedCornerShape
mediaShape: RoundedCornerShape,
onLongPress: () -> Unit
) {
LaunchedEffect(attachment.id, url) {
vm.cacheImage(attachment)
@@ -3213,7 +3243,10 @@ private fun ImageAttachment(
.aspectRatio(aspectRatio)
.clip(mediaShape)
.background(Color(0xFFE1E8ED))
.clickable { vm.openImage(previewSource, imagePreviewSources) }
.combinedClickable(
onClick = { vm.openImage(previewSource, imagePreviewSources) },
onLongClick = onLongPress
)
) {
AsyncImage(
model = model,
@@ -3283,14 +3316,19 @@ private fun MediaImagePlaceholder(failed: Boolean) {
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun VideoAttachment(attachment: AttachmentDto, url: String, token: String) {
private fun VideoAttachment(attachment: AttachmentDto, url: String, token: String, onLongPress: () -> Unit) {
val player = rememberAuthorizedPlayer(url = url, token = token)
Box(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(16f / 9f)
.background(Color.Black)
.combinedClickable(
onClick = {},
onLongClick = onLongPress
)
) {
AndroidView(
factory = { context ->
@@ -3321,11 +3359,23 @@ private fun VideoAttachment(attachment: AttachmentDto, url: String, token: Strin
)
}
}
Surface(
color = Color(0x99000000),
shape = CircleShape,
modifier = Modifier
.align(Alignment.TopEnd)
.padding(6.dp)
) {
IconButton(onClick = onLongPress, modifier = Modifier.size(36.dp)) {
Icon(Icons.AutoMirrored.Filled.Forward, contentDescription = "Переслать", tint = Color.White)
}
}
}
}
@Composable
private fun VoiceAttachment(attachment: AttachmentDto, url: String, token: String) {
@OptIn(ExperimentalFoundationApi::class)
private fun VoiceAttachment(attachment: AttachmentDto, url: String, token: String, onLongPress: () -> Unit) {
val player = rememberAuthorizedPlayer(url = url, token = token)
var isPlaying by remember { mutableStateOf(false) }
var position by remember { mutableStateOf(0L) }
@@ -3343,6 +3393,10 @@ private fun VoiceAttachment(attachment: AttachmentDto, url: String, token: Strin
Row(
modifier = Modifier
.fillMaxWidth()
.combinedClickable(
onClick = {},
onLongClick = onLongPress
)
.padding(horizontal = 2.dp, vertical = 2.dp),
verticalAlignment = Alignment.CenterVertically
) {
@@ -3398,15 +3452,19 @@ private fun VoiceAttachment(attachment: AttachmentDto, url: String, token: Strin
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun ContactAttachment(attachment: AttachmentDto, vm: QMaxViewModel) {
private fun ContactAttachment(attachment: AttachmentDto, vm: QMaxViewModel, onLongPress: () -> Unit) {
val title = attachment.displayFileName()
.removeSuffix(".vcf")
.ifBlank { "\u041a\u043e\u043d\u0442\u0430\u043a\u0442" }
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { vm.openAttachment(attachment) }
.combinedClickable(
onClick = { vm.openAttachment(attachment) },
onLongClick = onLongPress
)
.padding(horizontal = 4.dp, vertical = 7.dp),
verticalAlignment = Alignment.CenterVertically
) {
@@ -3438,8 +3496,9 @@ private fun ContactAttachment(attachment: AttachmentDto, vm: QMaxViewModel) {
}
}
@OptIn(ExperimentalFoundationApi::class)
@Composable
private fun FileAttachment(attachment: AttachmentDto, vm: QMaxViewModel) {
private fun FileAttachment(attachment: AttachmentDto, vm: QMaxViewModel, onLongPress: () -> Unit) {
val title = attachment.displayFileName()
val subtitle = if (attachment.fileSizeBytes > 0L) {
"${formatFileSize(attachment.fileSizeBytes)} \u2022 \u041d\u0430\u0436\u043c\u0438\u0442\u0435, \u0447\u0442\u043e\u0431\u044b \u043e\u0442\u043a\u0440\u044b\u0442\u044c"
@@ -3449,7 +3508,10 @@ private fun FileAttachment(attachment: AttachmentDto, vm: QMaxViewModel) {
Row(
modifier = Modifier
.fillMaxWidth()
.clickable { vm.openAttachment(attachment) }
.combinedClickable(
onClick = { vm.openAttachment(attachment) },
onLongClick = onLongPress
)
.padding(horizontal = 4.dp, vertical = 6.dp),
verticalAlignment = Alignment.CenterVertically
) {