Enable forwarding from all attachment types
This commit is contained in:
@@ -2510,7 +2510,7 @@ private fun MaterialsMediaCell(
|
|||||||
private fun MaterialsFileList(items: List<ChatAttachmentItem>, vm: QMaxViewModel) {
|
private fun MaterialsFileList(items: List<ChatAttachmentItem>, vm: QMaxViewModel) {
|
||||||
LazyColumn(Modifier.fillMaxSize()) {
|
LazyColumn(Modifier.fillMaxSize()) {
|
||||||
items(items, key = { it.attachment.id }) { item ->
|
items(items, key = { it.attachment.id }) { item ->
|
||||||
FileAttachment(item.attachment, vm)
|
FileAttachment(item.attachment, vm, onLongPress = {})
|
||||||
HorizontalDivider(color = Color(0xFFE7EEF3))
|
HorizontalDivider(color = Color(0xFFE7EEF3))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2521,7 +2521,7 @@ private fun MaterialsVoiceList(items: List<ChatAttachmentItem>, session: QMaxSes
|
|||||||
LazyColumn(Modifier.fillMaxSize()) {
|
LazyColumn(Modifier.fillMaxSize()) {
|
||||||
items(items, key = { it.attachment.id }) { item ->
|
items(items, key = { it.attachment.id }) { item ->
|
||||||
val url = "${session.serverUrl.trimEnd('/')}${item.attachment.downloadPath}"
|
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))
|
HorizontalDivider(color = Color(0xFFE7EEF3))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2645,7 +2645,8 @@ private fun MessageRow(
|
|||||||
session = session,
|
session = session,
|
||||||
vm = vm,
|
vm = vm,
|
||||||
imagePreviewSources = imagePreviewSources,
|
imagePreviewSources = imagePreviewSources,
|
||||||
mediaShape = shape
|
mediaShape = shape,
|
||||||
|
onLongPress = { actionsOpen = true }
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
sortedAttachments.forEach { attachment ->
|
sortedAttachments.forEach { attachment ->
|
||||||
@@ -2654,7 +2655,8 @@ private fun MessageRow(
|
|||||||
session = session,
|
session = session,
|
||||||
vm = vm,
|
vm = vm,
|
||||||
imagePreviewSources = imagePreviewSources,
|
imagePreviewSources = imagePreviewSources,
|
||||||
mediaShape = shape
|
mediaShape = shape,
|
||||||
|
onLongPress = { actionsOpen = true }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2936,7 +2938,8 @@ private fun MediaAlbumGrid(
|
|||||||
session: QMaxSession,
|
session: QMaxSession,
|
||||||
vm: QMaxViewModel,
|
vm: QMaxViewModel,
|
||||||
imagePreviewSources: List<String>,
|
imagePreviewSources: List<String>,
|
||||||
mediaShape: RoundedCornerShape
|
mediaShape: RoundedCornerShape,
|
||||||
|
onLongPress: () -> Unit
|
||||||
) {
|
) {
|
||||||
val visible = attachments.take(4)
|
val visible = attachments.take(4)
|
||||||
val extraCount = (attachments.size - visible.size).coerceAtLeast(0)
|
val extraCount = (attachments.size - visible.size).coerceAtLeast(0)
|
||||||
@@ -2956,6 +2959,7 @@ private fun MediaAlbumGrid(
|
|||||||
vm = vm,
|
vm = vm,
|
||||||
imagePreviewSources = imagePreviewSources,
|
imagePreviewSources = imagePreviewSources,
|
||||||
extraCount = extraCount.takeIf { index == visible.lastIndex },
|
extraCount = extraCount.takeIf { index == visible.lastIndex },
|
||||||
|
onLongPress = onLongPress,
|
||||||
modifier = Modifier.weight(1f).aspectRatio(1f)
|
modifier = Modifier.weight(1f).aspectRatio(1f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -2967,6 +2971,7 @@ private fun MediaAlbumGrid(
|
|||||||
vm = vm,
|
vm = vm,
|
||||||
imagePreviewSources = imagePreviewSources,
|
imagePreviewSources = imagePreviewSources,
|
||||||
extraCount = null,
|
extraCount = null,
|
||||||
|
onLongPress = onLongPress,
|
||||||
modifier = Modifier.fillMaxWidth().aspectRatio(1.85f)
|
modifier = Modifier.fillMaxWidth().aspectRatio(1.85f)
|
||||||
)
|
)
|
||||||
Row(horizontalArrangement = Arrangement.spacedBy(2.dp)) {
|
Row(horizontalArrangement = Arrangement.spacedBy(2.dp)) {
|
||||||
@@ -2977,6 +2982,7 @@ private fun MediaAlbumGrid(
|
|||||||
vm = vm,
|
vm = vm,
|
||||||
imagePreviewSources = imagePreviewSources,
|
imagePreviewSources = imagePreviewSources,
|
||||||
extraCount = extraCount.takeIf { index == 1 },
|
extraCount = extraCount.takeIf { index == 1 },
|
||||||
|
onLongPress = onLongPress,
|
||||||
modifier = Modifier.weight(1f).aspectRatio(1f)
|
modifier = Modifier.weight(1f).aspectRatio(1f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -2993,6 +2999,7 @@ private fun MediaAlbumGrid(
|
|||||||
vm = vm,
|
vm = vm,
|
||||||
imagePreviewSources = imagePreviewSources,
|
imagePreviewSources = imagePreviewSources,
|
||||||
extraCount = extraCount.takeIf { isLastVisibleCell },
|
extraCount = extraCount.takeIf { isLastVisibleCell },
|
||||||
|
onLongPress = onLongPress,
|
||||||
modifier = Modifier.weight(1f).aspectRatio(1f)
|
modifier = Modifier.weight(1f).aspectRatio(1f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -3007,12 +3014,14 @@ private fun MediaAlbumGrid(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
private fun MediaAlbumCell(
|
private fun MediaAlbumCell(
|
||||||
attachment: AttachmentDto,
|
attachment: AttachmentDto,
|
||||||
session: QMaxSession,
|
session: QMaxSession,
|
||||||
vm: QMaxViewModel,
|
vm: QMaxViewModel,
|
||||||
imagePreviewSources: List<String>,
|
imagePreviewSources: List<String>,
|
||||||
extraCount: Int?,
|
extraCount: Int?,
|
||||||
|
onLongPress: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val url = rememberAttachmentUrl(session, attachment)
|
val url = rememberAttachmentUrl(session, attachment)
|
||||||
@@ -3037,7 +3046,10 @@ private fun MediaAlbumCell(
|
|||||||
contentScale = ContentScale.Crop,
|
contentScale = ContentScale.Crop,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.clickable { vm.openImage(previewSource, imagePreviewSources) },
|
.combinedClickable(
|
||||||
|
onClick = { vm.openImage(previewSource, imagePreviewSources) },
|
||||||
|
onLongClick = onLongPress
|
||||||
|
),
|
||||||
onSuccess = {
|
onSuccess = {
|
||||||
imageLoaded = true
|
imageLoaded = true
|
||||||
imageFailed = false
|
imageFailed = false
|
||||||
@@ -3091,26 +3103,29 @@ private fun AttachmentView(
|
|||||||
session: QMaxSession,
|
session: QMaxSession,
|
||||||
vm: QMaxViewModel,
|
vm: QMaxViewModel,
|
||||||
imagePreviewSources: List<String>,
|
imagePreviewSources: List<String>,
|
||||||
mediaShape: RoundedCornerShape
|
mediaShape: RoundedCornerShape,
|
||||||
|
onLongPress: () -> Unit
|
||||||
) {
|
) {
|
||||||
val url = rememberAttachmentUrl(session, attachment)
|
val url = rememberAttachmentUrl(session, attachment)
|
||||||
val state by vm.state
|
val state by vm.state
|
||||||
val cachedImagePath = state.cachedImagePaths[attachment.id]
|
val cachedImagePath = state.cachedImagePaths[attachment.id]
|
||||||
when {
|
when {
|
||||||
attachment.isContactAttachment() -> ContactAttachment(attachment, vm)
|
attachment.isContactAttachment() -> ContactAttachment(attachment, vm, onLongPress)
|
||||||
attachment.isEmojiAttachment() -> EmojiAttachment(
|
attachment.isEmojiAttachment() -> EmojiAttachment(
|
||||||
attachment,
|
attachment,
|
||||||
url,
|
url,
|
||||||
session.accessToken,
|
session.accessToken,
|
||||||
cachedImagePath,
|
cachedImagePath,
|
||||||
vm
|
vm,
|
||||||
|
onLongPress
|
||||||
)
|
)
|
||||||
attachment.isStickerAttachment() -> StickerAttachment(
|
attachment.isStickerAttachment() -> StickerAttachment(
|
||||||
attachment,
|
attachment,
|
||||||
url,
|
url,
|
||||||
session.accessToken,
|
session.accessToken,
|
||||||
cachedImagePath,
|
cachedImagePath,
|
||||||
vm
|
vm,
|
||||||
|
onLongPress
|
||||||
)
|
)
|
||||||
attachment.isImageAttachment() -> ImageAttachment(
|
attachment.isImageAttachment() -> ImageAttachment(
|
||||||
attachment,
|
attachment,
|
||||||
@@ -3119,21 +3134,24 @@ private fun AttachmentView(
|
|||||||
cachedImagePath,
|
cachedImagePath,
|
||||||
vm,
|
vm,
|
||||||
imagePreviewSources,
|
imagePreviewSources,
|
||||||
mediaShape
|
mediaShape,
|
||||||
|
onLongPress
|
||||||
)
|
)
|
||||||
attachment.isVideoAttachment() -> VideoAttachment(attachment, url, session.accessToken)
|
attachment.isVideoAttachment() -> VideoAttachment(attachment, url, session.accessToken, onLongPress)
|
||||||
attachment.isVoiceAttachment() || attachment.isAudioAttachment() -> VoiceAttachment(attachment, url, session.accessToken)
|
attachment.isVoiceAttachment() || attachment.isAudioAttachment() -> VoiceAttachment(attachment, url, session.accessToken, onLongPress)
|
||||||
else -> FileAttachment(attachment, vm)
|
else -> FileAttachment(attachment, vm, onLongPress)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun StickerAttachment(
|
private fun StickerAttachment(
|
||||||
attachment: AttachmentDto,
|
attachment: AttachmentDto,
|
||||||
url: String,
|
url: String,
|
||||||
token: String,
|
token: String,
|
||||||
cachedImagePath: String?,
|
cachedImagePath: String?,
|
||||||
vm: QMaxViewModel
|
vm: QMaxViewModel,
|
||||||
|
onLongPress: () -> Unit
|
||||||
) {
|
) {
|
||||||
LaunchedEffect(attachment.id, url) {
|
LaunchedEffect(attachment.id, url) {
|
||||||
vm.cacheImage(attachment)
|
vm.cacheImage(attachment)
|
||||||
@@ -3145,7 +3163,10 @@ private fun StickerAttachment(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(156.dp)
|
.size(156.dp)
|
||||||
.clip(RoundedCornerShape(6.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
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
@@ -3168,13 +3189,15 @@ private fun StickerAttachment(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun EmojiAttachment(
|
private fun EmojiAttachment(
|
||||||
attachment: AttachmentDto,
|
attachment: AttachmentDto,
|
||||||
url: String,
|
url: String,
|
||||||
token: String,
|
token: String,
|
||||||
cachedImagePath: String?,
|
cachedImagePath: String?,
|
||||||
vm: QMaxViewModel
|
vm: QMaxViewModel,
|
||||||
|
onLongPress: () -> Unit
|
||||||
) {
|
) {
|
||||||
LaunchedEffect(attachment.id, url) {
|
LaunchedEffect(attachment.id, url) {
|
||||||
vm.cacheImage(attachment)
|
vm.cacheImage(attachment)
|
||||||
@@ -3185,10 +3208,16 @@ private fun EmojiAttachment(
|
|||||||
model = model,
|
model = model,
|
||||||
contentDescription = attachment.fileName,
|
contentDescription = attachment.fileName,
|
||||||
contentScale = ContentScale.Fit,
|
contentScale = ContentScale.Fit,
|
||||||
modifier = Modifier.size(emojiSize)
|
modifier = Modifier
|
||||||
|
.size(emojiSize)
|
||||||
|
.combinedClickable(
|
||||||
|
onClick = {},
|
||||||
|
onLongClick = onLongPress
|
||||||
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun ImageAttachment(
|
private fun ImageAttachment(
|
||||||
attachment: AttachmentDto,
|
attachment: AttachmentDto,
|
||||||
@@ -3197,7 +3226,8 @@ private fun ImageAttachment(
|
|||||||
cachedImagePath: String?,
|
cachedImagePath: String?,
|
||||||
vm: QMaxViewModel,
|
vm: QMaxViewModel,
|
||||||
imagePreviewSources: List<String>,
|
imagePreviewSources: List<String>,
|
||||||
mediaShape: RoundedCornerShape
|
mediaShape: RoundedCornerShape,
|
||||||
|
onLongPress: () -> Unit
|
||||||
) {
|
) {
|
||||||
LaunchedEffect(attachment.id, url) {
|
LaunchedEffect(attachment.id, url) {
|
||||||
vm.cacheImage(attachment)
|
vm.cacheImage(attachment)
|
||||||
@@ -3213,7 +3243,10 @@ private fun ImageAttachment(
|
|||||||
.aspectRatio(aspectRatio)
|
.aspectRatio(aspectRatio)
|
||||||
.clip(mediaShape)
|
.clip(mediaShape)
|
||||||
.background(Color(0xFFE1E8ED))
|
.background(Color(0xFFE1E8ED))
|
||||||
.clickable { vm.openImage(previewSource, imagePreviewSources) }
|
.combinedClickable(
|
||||||
|
onClick = { vm.openImage(previewSource, imagePreviewSources) },
|
||||||
|
onLongClick = onLongPress
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = model,
|
model = model,
|
||||||
@@ -3283,14 +3316,19 @@ private fun MediaImagePlaceholder(failed: Boolean) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@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)
|
val player = rememberAuthorizedPlayer(url = url, token = token)
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.aspectRatio(16f / 9f)
|
.aspectRatio(16f / 9f)
|
||||||
.background(Color.Black)
|
.background(Color.Black)
|
||||||
|
.combinedClickable(
|
||||||
|
onClick = {},
|
||||||
|
onLongClick = onLongPress
|
||||||
|
)
|
||||||
) {
|
) {
|
||||||
AndroidView(
|
AndroidView(
|
||||||
factory = { context ->
|
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
|
@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)
|
val player = rememberAuthorizedPlayer(url = url, token = token)
|
||||||
var isPlaying by remember { mutableStateOf(false) }
|
var isPlaying by remember { mutableStateOf(false) }
|
||||||
var position by remember { mutableStateOf(0L) }
|
var position by remember { mutableStateOf(0L) }
|
||||||
@@ -3343,6 +3393,10 @@ private fun VoiceAttachment(attachment: AttachmentDto, url: String, token: Strin
|
|||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
.combinedClickable(
|
||||||
|
onClick = {},
|
||||||
|
onLongClick = onLongPress
|
||||||
|
)
|
||||||
.padding(horizontal = 2.dp, vertical = 2.dp),
|
.padding(horizontal = 2.dp, vertical = 2.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
@@ -3398,15 +3452,19 @@ private fun VoiceAttachment(attachment: AttachmentDto, url: String, token: Strin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun ContactAttachment(attachment: AttachmentDto, vm: QMaxViewModel) {
|
private fun ContactAttachment(attachment: AttachmentDto, vm: QMaxViewModel, onLongPress: () -> Unit) {
|
||||||
val title = attachment.displayFileName()
|
val title = attachment.displayFileName()
|
||||||
.removeSuffix(".vcf")
|
.removeSuffix(".vcf")
|
||||||
.ifBlank { "\u041a\u043e\u043d\u0442\u0430\u043a\u0442" }
|
.ifBlank { "\u041a\u043e\u043d\u0442\u0430\u043a\u0442" }
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable { vm.openAttachment(attachment) }
|
.combinedClickable(
|
||||||
|
onClick = { vm.openAttachment(attachment) },
|
||||||
|
onLongClick = onLongPress
|
||||||
|
)
|
||||||
.padding(horizontal = 4.dp, vertical = 7.dp),
|
.padding(horizontal = 4.dp, vertical = 7.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
@@ -3438,8 +3496,9 @@ private fun ContactAttachment(attachment: AttachmentDto, vm: QMaxViewModel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
private fun FileAttachment(attachment: AttachmentDto, vm: QMaxViewModel) {
|
private fun FileAttachment(attachment: AttachmentDto, vm: QMaxViewModel, onLongPress: () -> Unit) {
|
||||||
val title = attachment.displayFileName()
|
val title = attachment.displayFileName()
|
||||||
val subtitle = if (attachment.fileSizeBytes > 0L) {
|
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"
|
"${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(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable { vm.openAttachment(attachment) }
|
.combinedClickable(
|
||||||
|
onClick = { vm.openAttachment(attachment) },
|
||||||
|
onLongClick = onLongPress
|
||||||
|
)
|
||||||
.padding(horizontal = 4.dp, vertical = 6.dp),
|
.padding(horizontal = 4.dp, vertical = 6.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
|
|||||||
Reference in New Issue
Block a user