Reply attachment (#195)

This commit is contained in:
2025-06-25 09:04:50 +03:00
committed by GitHub
parent 56683bea96
commit 76dd1e2ce7
15 changed files with 289 additions and 49 deletions
@@ -24,6 +24,7 @@ import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalResources
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.lifecycle.compose.LifecycleResumeEffect import androidx.lifecycle.compose.LifecycleResumeEffect
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -88,7 +89,7 @@ class MainActivity : AppCompatActivity() {
requestNotificationPermissions() requestNotificationPermissions()
setContent { setContent {
val context = LocalContext.current val resources = LocalResources.current
val userSettings: UserSettings = koinInject() val userSettings: UserSettings = koinInject()
val longPollController: LongPollController = koinInject() val longPollController: LongPollController = koinInject()
@@ -164,10 +165,10 @@ class MainActivity : AppCompatActivity() {
} }
val deviceWidthDp = remember(true) { val deviceWidthDp = remember(true) {
context.resources.displayMetrics.widthPixels.pxToDp() resources.displayMetrics.widthPixels.pxToDp()
} }
val deviceHeightDp = remember(true) { val deviceHeightDp = remember(true) {
context.resources.displayMetrics.heightPixels.pxToDp() resources.displayMetrics.heightPixels.pxToDp()
} }
val deviceWidthSize by remember(deviceWidthDp) { val deviceWidthSize by remember(deviceWidthDp) {
@@ -36,7 +36,7 @@ class VkUsersMap(
if (message.fromId > 0) map[message.fromId] if (message.fromId > 0) map[message.fromId]
else null else null
fun user(userid: Long): VkUser? = map[userId] fun user(userId: Long): VkUser? = map[userId]
companion object { companion object {
@@ -75,7 +75,13 @@ class ConversationsRepositoryImpl(
user = usersMap.messageUser(message), user = usersMap.messageUser(message),
group = groupsMap.messageGroup(message), group = groupsMap.messageGroup(message),
actionUser = usersMap.messageActionUser(message), actionUser = usersMap.messageActionUser(message),
actionGroup = groupsMap.messageActionGroup(message) actionGroup = groupsMap.messageActionGroup(message),
replyMessage = message.replyMessage?.copy(
user = usersMap.messageUser(message),
group = groupsMap.messageGroup(message),
actionUser = usersMap.messageActionUser(message),
actionGroup = groupsMap.messageActionGroup(message),
)
).also { VkMemoryCache[message.id] = it } ).also { VkMemoryCache[message.id] = it }
} }
item.conversation.asDomain(lastMessage).let { conversation -> item.conversation.asDomain(lastMessage).let { conversation ->
@@ -90,7 +90,13 @@ class MessagesRepositoryImpl(
user = usersMap.messageUser(message), user = usersMap.messageUser(message),
group = groupsMap.messageGroup(message), group = groupsMap.messageGroup(message),
actionUser = usersMap.messageActionUser(message), actionUser = usersMap.messageActionUser(message),
actionGroup = groupsMap.messageActionGroup(message) actionGroup = groupsMap.messageActionGroup(message),
replyMessage = message.replyMessage?.copy(
user = usersMap.messageUser(message),
group = groupsMap.messageGroup(message),
actionUser = usersMap.messageActionUser(message),
actionGroup = groupsMap.messageActionGroup(message),
)
).also { VkMemoryCache[message.id] = it } ).also { VkMemoryCache[message.id] = it }
} }
} }
@@ -159,7 +165,13 @@ class MessagesRepositoryImpl(
user = usersMap.messageUser(message), user = usersMap.messageUser(message),
group = groupsMap.messageGroup(message), group = groupsMap.messageGroup(message),
actionUser = usersMap.messageActionUser(message), actionUser = usersMap.messageActionUser(message),
actionGroup = groupsMap.messageActionGroup(message) actionGroup = groupsMap.messageActionGroup(message),
replyMessage = message.replyMessage?.asDomain()?.copy(
user = usersMap.messageUser(message),
group = groupsMap.messageGroup(message),
actionUser = usersMap.messageActionUser(message),
actionGroup = groupsMap.messageActionGroup(message),
)
) )
} }
@@ -105,7 +105,7 @@ fun VkMessageData.asDomain(): VkMessage = VkMessage(
actionConversationMessageId = action?.conversationMessageId, actionConversationMessageId = action?.conversationMessageId,
actionMessage = action?.message, actionMessage = action?.message,
geoType = geo?.type, geoType = geo?.type,
isImportant = important ?: false, isImportant = important == true,
updateTime = updateTime, updateTime = updateTime,
forwards = fwdMessages.orEmpty().map(VkMessageData::asDomain), forwards = fwdMessages.orEmpty().map(VkMessageData::asDomain),
attachments = attachments.map(VkAttachmentItemData::toDomain), attachments = attachments.map(VkAttachmentItemData::toDomain),
@@ -35,7 +35,7 @@ data class VkMessage(
val user: VkUser?, val user: VkUser?,
val group: VkGroupDomain?, val group: VkGroupDomain?,
val actionUser: VkUser?, val actionUser: VkUser?,
val actionGroup: VkGroupDomain? val actionGroup: VkGroupDomain?,
) { ) {
fun isPeerChat() = peerId > 2_000_000_000 fun isPeerChat() = peerId > 2_000_000_000
@@ -923,7 +923,9 @@ class MessagesHistoryViewModelImpl(
isImportant = false, isImportant = false,
forwards = null, forwards = null,
attachments = null, attachments = null,
replyMessage = null, replyMessage = when {
else -> null
},
geoType = null, geoType = null,
user = VkMemoryCache.getUser(UserConfig.userId), user = VkMemoryCache.getUser(UserConfig.userId),
group = null, group = null,
@@ -932,8 +934,6 @@ class MessagesHistoryViewModelImpl(
isPinned = false, isPinned = false,
isSpam = false, isSpam = false,
pinnedAt = null, pinnedAt = null,
// TODO: 04-Apr-25, Danil Nikolaev: implement
formatData = formatData, formatData = formatData,
) )
formatData = formatData.copy(items = emptyList()) formatData = formatData.copy(items = emptyList())
@@ -29,7 +29,10 @@ sealed class UiItem(
val isSelected: Boolean, val isSelected: Boolean,
val isPinned: Boolean, val isPinned: Boolean,
val isImportant: Boolean, val isImportant: Boolean,
val attachments: List<VkAttachment>? val attachments: List<VkAttachment>?,
val replyCmId: Long?,
val replyTitle: String?,
val replySummary: String?
) : UiItem(id, cmId) ) : UiItem(id, cmId)
data class ActionMessage( data class ActionMessage(
@@ -30,24 +30,26 @@ import coil.compose.rememberAsyncImagePainter
import coil.imageLoader import coil.imageLoader
import dev.meloda.fast.messageshistory.model.UiItem import dev.meloda.fast.messageshistory.model.UiItem
import dev.meloda.fast.model.api.domain.VkAttachment import dev.meloda.fast.model.api.domain.VkAttachment
import dev.meloda.fast.ui.theme.LocalThemeConfig
import dev.meloda.fast.ui.util.ImmutableList.Companion.toImmutableList import dev.meloda.fast.ui.util.ImmutableList.Companion.toImmutableList
@Composable @Composable
fun IncomingMessageBubble( fun IncomingMessageBubble(
enableAnimations: Boolean,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
message: UiItem.Message, message: UiItem.Message,
onClick: (VkAttachment) -> Unit = {}, onClick: (VkAttachment) -> Unit = {},
onLongClick: (VkAttachment) -> Unit = {} onLongClick: (VkAttachment) -> Unit = {},
onReplyClick: () -> Unit = {}
) { ) {
val currentOnClick by rememberUpdatedState(onClick) val currentOnClick by rememberUpdatedState(onClick)
val currentOnLongClick by rememberUpdatedState(onLongClick) val currentOnLongClick by rememberUpdatedState(onLongClick)
val currentOnReplyClick by rememberUpdatedState(onReplyClick)
Row( Row(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.then( .then(
if (LocalThemeConfig.current.enableAnimations) Modifier.animateContentSize() if (enableAnimations) Modifier.animateContentSize()
else Modifier else Modifier
), ),
) { ) {
@@ -103,8 +105,11 @@ fun IncomingMessageBubble(
isImportant = message.isImportant, isImportant = message.isImportant,
isSelected = message.isSelected, isSelected = message.isSelected,
attachments = message.attachments?.toImmutableList(), attachments = message.attachments?.toImmutableList(),
replyTitle = message.replyTitle,
replySummary = message.replySummary,
onClick = currentOnClick, onClick = currentOnClick,
onLongClick = currentOnLongClick onLongClick = currentOnLongClick,
onReplyClick = currentOnReplyClick
) )
} }
} }
@@ -7,7 +7,9 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.defaultMinSize import androidx.compose.foundation.layout.defaultMinSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.LocalContentColor import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@@ -25,11 +27,13 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.AnnotatedString import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import dev.meloda.fast.messageshistory.model.SendingStatus import dev.meloda.fast.messageshistory.model.SendingStatus
import dev.meloda.fast.messageshistory.presentation.attachments.Attachments import dev.meloda.fast.messageshistory.presentation.attachments.Attachments
import dev.meloda.fast.messageshistory.presentation.attachments.Reply
import dev.meloda.fast.model.api.domain.VkAttachment import dev.meloda.fast.model.api.domain.VkAttachment
import dev.meloda.fast.ui.theme.LocalThemeConfig import dev.meloda.fast.ui.theme.LocalThemeConfig
import dev.meloda.fast.ui.util.ImmutableList import dev.meloda.fast.ui.util.ImmutableList
@@ -48,9 +52,14 @@ fun MessageBubble(
isImportant: Boolean, isImportant: Boolean,
isSelected: Boolean, isSelected: Boolean,
attachments: ImmutableList<VkAttachment>?, attachments: ImmutableList<VkAttachment>?,
replyTitle: String?,
replySummary: String? = null,
onClick: (VkAttachment) -> Unit = {}, onClick: (VkAttachment) -> Unit = {},
onLongClick: (VkAttachment) -> Unit = {} onLongClick: (VkAttachment) -> Unit = {},
onReplyClick: () -> Unit = {}
) { ) {
val density = LocalDensity.current
val currentOnClick by rememberUpdatedState(onClick) val currentOnClick by rememberUpdatedState(onClick)
val currentOnLongClick by rememberUpdatedState(onLongClick) val currentOnLongClick by rememberUpdatedState(onLongClick)
@@ -60,6 +69,11 @@ fun MessageBubble(
} else { } else {
MaterialTheme.colorScheme.primaryContainer MaterialTheme.colorScheme.primaryContainer
} }
val replyBackgroundColor = if (!isOut) {
MaterialTheme.colorScheme.primaryContainer
} else {
MaterialTheme.colorScheme.inversePrimary
}
val contentColor = if (!isOut) { val contentColor = if (!isOut) {
MaterialTheme.colorScheme.onSurface MaterialTheme.colorScheme.onSurface
@@ -101,8 +115,38 @@ fun MessageBubble(
} }
} }
var containerWidth by remember {
mutableIntStateOf(0)
}
CompositionLocalProvider(LocalContentColor provides contentColor) { CompositionLocalProvider(LocalContentColor provides contentColor) {
Column { Column(
modifier = modifier
.wrapContentWidth()
.onGloballyPositioned {
containerWidth = it.size.width
}
) {
if (replyTitle != null) {
Reply(
modifier = Modifier
.padding(if (attachments == null || text != null) 0.dp else 4.dp)
.width(with(density) { containerWidth.toDp() }),
bottomPadding = if (attachments == null || text != null) 0.dp else 4.dp,
shape = RoundedCornerShape(
topStart = 16.dp,
topEnd = 16.dp,
bottomStart = if (attachments == null || text != null) 0.dp else 16.dp,
bottomEnd = if (attachments == null || text != null) 0.dp else 16.dp
),
onClick = onReplyClick,
title = replyTitle,
summary = replySummary,
backgroundColor = backgroundColor,
innerBackgroundColor = replyBackgroundColor
)
}
if (shouldShowBubble) { if (shouldShowBubble) {
Box( Box(
modifier = modifier modifier = modifier
@@ -111,18 +155,19 @@ fun MessageBubble(
} }
.widthIn(min = if (shouldFill) attachmentsContainerWidth.dp else 56.dp) .widthIn(min = if (shouldFill) attachmentsContainerWidth.dp else 56.dp)
.clip( .clip(
if (attachments == null) RoundedCornerShape(24.dp) RoundedCornerShape(
else RoundedCornerShape( topStart = if (replyTitle == null) 24.dp else 0.dp,
topStart = 24.dp, topEnd = if (replyTitle == null) 24.dp else 0.dp,
topEnd = 24.dp, bottomStart = if (attachments != null) 0.dp else 24.dp,
bottomStart = 0.dp, bottomEnd = if (attachments != null) 0.dp else 24.dp
bottomEnd = 0.dp
) )
) )
.background(backgroundColor) .background(backgroundColor)
.padding( .padding(
horizontal = 8.dp, start = 8.dp,
vertical = 6.dp end = 8.dp,
top = if (replyTitle != null) 0.dp else 6.dp,
bottom = if (replyTitle != null) 4.dp else 6.dp
) )
.then(if (theme.enableAnimations) Modifier.animateContentSize() else Modifier), .then(if (theme.enableAnimations) Modifier.animateContentSize() else Modifier),
) { ) {
@@ -223,6 +268,10 @@ private fun Bubble() {
isPinned = true, isPinned = true,
isImportant = true, isImportant = true,
isSelected = false, isSelected = false,
attachments = emptyImmutableList() attachments = emptyImmutableList(),
replyTitle = "Danil Nikolaev",
replySummary = "2 photos",
onClick = {},
onLongClick = {},
) )
} }
@@ -17,22 +17,22 @@ fun MessageTextContainer(
isOut: Boolean, isOut: Boolean,
isSelected: Boolean, isSelected: Boolean,
) { ) {
if (text != null) { if (text == null) return
if (isSelected) {
SelectionContainer { if (isSelected) {
MessageText( SelectionContainer {
modifier = modifier,
text = text,
isOut = isOut,
)
}
} else {
MessageText( MessageText(
modifier = modifier, modifier = modifier,
text = text, text = text,
isOut = isOut, isOut = isOut,
) )
} }
} else {
MessageText(
modifier = modifier,
text = text,
isOut = isOut,
)
} }
} }
@@ -209,13 +209,18 @@ fun MessagesList(
) )
else Modifier else Modifier
), ),
enableAnimations = theme.enableAnimations,
message = item, message = item,
onClick = { attachment -> onClick = { attachment ->
onAttachmentClick(item, attachment) onAttachmentClick(item, attachment)
}, },
onLongClick = { attachment -> onLongClick = { attachment ->
onAttachmentLongClick(item, attachment) onAttachmentLongClick(item, attachment)
},
onReplyClick = {
if (item.replyCmId != null) {
onRequestScrollToCmId(item.replyCmId)
}
} }
) )
} else { } else {
@@ -230,12 +235,18 @@ fun MessagesList(
) )
else Modifier else Modifier
), ),
enableAnimations = theme.enableAnimations,
message = item, message = item,
onClick = { attachment -> onClick = { attachment ->
onAttachmentClick(item, attachment) onAttachmentClick(item, attachment)
}, },
onLongClick = { attachment -> onLongClick = { attachment ->
onAttachmentLongClick(item, attachment) onAttachmentLongClick(item, attachment)
},
onReplyClick = {
if (item.replyCmId != null) {
onRequestScrollToCmId(item.replyCmId)
}
} }
) )
} }
@@ -2,42 +2,48 @@ package dev.meloda.fast.messageshistory.presentation
import androidx.compose.animation.animateContentSize import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import dev.meloda.fast.messageshistory.model.UiItem import dev.meloda.fast.messageshistory.model.UiItem
import dev.meloda.fast.model.api.domain.VkAttachment import dev.meloda.fast.model.api.domain.VkAttachment
import dev.meloda.fast.ui.theme.LocalThemeConfig
import dev.meloda.fast.ui.util.ImmutableList.Companion.toImmutableList import dev.meloda.fast.ui.util.ImmutableList.Companion.toImmutableList
@Composable @Composable
fun OutgoingMessageBubble( fun OutgoingMessageBubble(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
enableAnimations: Boolean,
message: UiItem.Message, message: UiItem.Message,
onClick: (VkAttachment) -> Unit = {}, onClick: (VkAttachment) -> Unit = {},
onLongClick: (VkAttachment) -> Unit = {} onLongClick: (VkAttachment) -> Unit = {},
onReplyClick: () -> Unit = {}
) { ) {
val currentOnClick by rememberUpdatedState(onClick)
val currentOnLongClick by rememberUpdatedState(onLongClick)
val currentOnReplyClick by rememberUpdatedState(onReplyClick)
Row( Row(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.then( .then(
if (LocalThemeConfig.current.enableAnimations) Modifier.animateContentSize() if (enableAnimations) Modifier.animateContentSize()
else Modifier else Modifier
), ),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End horizontalArrangement = Arrangement.End
) { ) {
Column( Row(
modifier = Modifier modifier = Modifier
.padding(end = 16.dp) .padding(end = 16.dp)
.fillMaxWidth(0.85f), .fillMaxWidth(0.85f),
verticalArrangement = Arrangement.Center, verticalAlignment = Alignment.CenterVertically,
horizontalAlignment = Alignment.End, horizontalArrangement = Arrangement.End
) { ) {
MessageBubble( MessageBubble(
modifier = Modifier, modifier = Modifier,
@@ -51,8 +57,11 @@ fun OutgoingMessageBubble(
isImportant = message.isImportant, isImportant = message.isImportant,
isSelected = message.isSelected, isSelected = message.isSelected,
attachments = message.attachments?.toImmutableList(), attachments = message.attachments?.toImmutableList(),
onClick = onClick, replyTitle = message.replyTitle,
onLongClick = onLongClick replySummary = message.replySummary,
onClick = currentOnClick,
onLongClick = currentOnLongClick,
onReplyClick = currentOnReplyClick
) )
} }
} }
@@ -0,0 +1,128 @@
package dev.meloda.fast.messageshistory.presentation.attachments
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@Composable
fun Reply(
modifier: Modifier = Modifier,
bottomPadding: Dp,
shape: Shape,
onClick: () -> Unit,
backgroundColor: Color,
innerBackgroundColor: Color,
title: String,
summary: String?
) {
Box(
modifier = modifier
.background(
color = backgroundColor,
shape = shape
)
.height(40.dp)
.padding(
top = 4.dp,
start = 4.dp,
end = 4.dp,
bottom = bottomPadding
)
) {
Row(
modifier = Modifier
.clip(RoundedCornerShape(12.dp))
.clickable(onClick = onClick)
.fillMaxSize()
.background(innerBackgroundColor)
) {
Box(
modifier = Modifier
.width(3.dp)
.fillMaxHeight()
.background(MaterialTheme.colorScheme.onBackground)
)
Spacer(modifier = Modifier.width(6.dp))
Column(
modifier = Modifier.fillMaxHeight(),
verticalArrangement = Arrangement.Center
) {
Text(
text = title,
style = MaterialTheme.typography.labelMedium,
maxLines = 1
)
summary?.let {
Text(
text = summary,
style = MaterialTheme.typography.labelSmall,
maxLines = 1
)
}
}
}
}
}
@Composable
private fun ReplyBasePreview(
backgroundColor: Color,
innerBackgroundColor: Color
) {
Reply(
modifier = Modifier.width(120.dp),
shape = RoundedCornerShape(
topStart = 12.dp,
topEnd = 12.dp
),
onClick = {},
title = "Danil Nikolaev",
summary = "2 photos",
backgroundColor = backgroundColor,
innerBackgroundColor = innerBackgroundColor,
bottomPadding = 0.dp
)
}
@Preview
@Composable
private fun IncomingReplyPreview() {
ReplyBasePreview(
backgroundColor = MaterialTheme.colorScheme.surfaceColorAtElevation(2.dp),
innerBackgroundColor = MaterialTheme.colorScheme.surfaceColorAtElevation(20.dp)
)
}
@Preview
@Composable
private fun OutgoingReplyPreview() {
ReplyBasePreview(
backgroundColor = MaterialTheme.colorScheme.primaryContainer,
innerBackgroundColor = MaterialTheme.colorScheme.inversePrimary
)
}
@@ -57,6 +57,19 @@ fun VkMessage.extractTitle(): String = when {
else -> throw IllegalStateException("Message is not from user nor group. fromId: $fromId") else -> throw IllegalStateException("Message is not from user nor group. fromId: $fromId")
} }
fun VkMessage.extractReplyTitle(): String? = replyMessage?.extractTitle()
// TODO: 24-Jun-25, Danil Nikolaev: improve
fun VkMessage.extractReplySummary(): String? = when (val message = replyMessage) {
null -> null
else -> {
when {
message.text != null -> message.text
else -> null
}
}
}
fun VkConversation.extractAvatar(): UiImage = when (peerType) { fun VkConversation.extractAvatar(): UiImage = when (peerType) {
PeerType.USER -> { PeerType.USER -> {
if (isAccount(id)) null if (isAccount(id)) null
@@ -144,7 +157,10 @@ fun VkMessage.asPresentation(
isSelected = isSelected, isSelected = isSelected,
isPinned = isPinned, isPinned = isPinned,
isImportant = isImportant, isImportant = isImportant,
attachments = attachments?.ifEmpty { null } attachments = attachments?.ifEmpty { null },
replyCmId = replyMessage?.cmId,
replyTitle = extractReplyTitle(),
replySummary = extractReplySummary()
) )
} }