android: add reply-forward preview data foundation
Some checks are pending
CI / test (push) Has started running

This commit is contained in:
Codex
2026-03-09 13:56:27 +03:00
parent 071165c55b
commit 98e8ac8dfb
12 changed files with 68 additions and 5 deletions

View File

@@ -186,3 +186,12 @@
### Step 29 - Core base / multi-select delete execution
- Fixed multi-select delete behavior in `ChatViewModel`: `Delete` now applies to all selected messages, not only focused one.
- Added explicit guard for `Delete for all` in multi-select mode (single-message only).
### Step 30 - Core base / reply-forward preview data foundation
- Extended message DTO/Room/domain models with optional preview metadata:
- `replyPreviewText`, `replyPreviewSenderName`
- `forwardedFromDisplayName`
- sender profile fields from API payload (`senderDisplayName`, `senderUsername`, `senderAvatarUrl`)
- Added Room self-relation in `MessageLocalModel` to resolve reply preview fallback from referenced message.
- Updated message mappers and repository/realtime temporary entity creation for new model fields.
- Bumped Room schema version to `7`.

View File

@@ -16,7 +16,7 @@ import ru.daemonlord.messenger.data.message.local.entity.MessageEntity
MessageEntity::class,
MessageAttachmentEntity::class,
],
version = 6,
version = 7,
exportSchema = false,
)
abstract class MessengerDatabase : RoomDatabase() {

View File

@@ -10,10 +10,22 @@ data class MessageReadDto(
val chatId: Long,
@SerialName("sender_id")
val senderId: Long,
@SerialName("sender_display_name")
val senderDisplayName: String? = null,
@SerialName("sender_username")
val senderUsername: String? = null,
@SerialName("sender_avatar_url")
val senderAvatarUrl: String? = null,
@SerialName("reply_to_message_id")
val replyToMessageId: Long? = null,
@SerialName("reply_preview_text")
val replyPreviewText: String? = null,
@SerialName("reply_preview_sender_name")
val replyPreviewSenderName: String? = null,
@SerialName("forwarded_from_message_id")
val forwardedFromMessageId: Long? = null,
@SerialName("forwarded_from_display_name")
val forwardedFromDisplayName: String? = null,
val type: String,
val text: String? = null,
@SerialName("delivery_status")

View File

@@ -29,8 +29,14 @@ data class MessageEntity(
val senderAvatarUrl: String?,
@ColumnInfo(name = "reply_to_message_id")
val replyToMessageId: Long?,
@ColumnInfo(name = "reply_preview_text")
val replyPreviewText: String?,
@ColumnInfo(name = "reply_preview_sender_name")
val replyPreviewSenderName: String?,
@ColumnInfo(name = "forwarded_from_message_id")
val forwardedFromMessageId: Long?,
@ColumnInfo(name = "forwarded_from_display_name")
val forwardedFromDisplayName: String?,
@ColumnInfo(name = "type")
val type: String,
@ColumnInfo(name = "text")

View File

@@ -13,4 +13,9 @@ data class MessageLocalModel(
entityColumn = "message_id",
)
val attachments: List<MessageAttachmentEntity>,
@Relation(
parentColumn = "reply_to_message_id",
entityColumn = "id",
)
val replyToMessage: MessageEntity?,
)

View File

@@ -18,11 +18,14 @@ fun MessageReadDto.toEntity(): MessageEntity {
id = id,
chatId = chatId,
senderId = senderId,
senderDisplayName = null,
senderUsername = null,
senderAvatarUrl = null,
senderDisplayName = senderDisplayName,
senderUsername = senderUsername,
senderAvatarUrl = senderAvatarUrl,
replyToMessageId = replyToMessageId,
replyPreviewText = replyPreviewText,
replyPreviewSenderName = replyPreviewSenderName,
forwardedFromMessageId = forwardedFromMessageId,
forwardedFromDisplayName = forwardedFromDisplayName,
type = type,
text = text,
status = deliveryStatus,
@@ -33,6 +36,13 @@ fun MessageReadDto.toEntity(): MessageEntity {
}
fun MessageLocalModel.toDomain(currentUserId: Long?): MessageItem {
val resolvedReplyPreviewText = message.replyPreviewText
?: replyToMessage?.text
?: replyToMessage?.type?.let { "[$it]" }
val resolvedReplyPreviewSenderName = message.replyPreviewSenderName
?: replyToMessage?.senderDisplayName
?: replyToMessage?.senderUsername?.takeIf { it.isNotBlank() }?.let { "@$it" }
?: replyToMessage?.senderId?.let { "User #$it" }
return MessageItem(
id = message.id,
chatId = message.chatId,
@@ -45,7 +55,10 @@ fun MessageLocalModel.toDomain(currentUserId: Long?): MessageItem {
isOutgoing = currentUserId != null && currentUserId == message.senderId,
status = message.status,
replyToMessageId = message.replyToMessageId,
replyPreviewText = resolvedReplyPreviewText,
replyPreviewSenderName = resolvedReplyPreviewSenderName,
forwardedFromMessageId = message.forwardedFromMessageId,
forwardedFromDisplayName = message.forwardedFromDisplayName,
attachmentWaveform = message.attachmentWaveformJson.toWaveformOrNull(),
attachments = attachments.map { it.toDomain() },
)

View File

@@ -122,7 +122,10 @@ class NetworkMessageRepository @Inject constructor(
senderUsername = null,
senderAvatarUrl = null,
replyToMessageId = replyToMessageId,
replyPreviewText = null,
replyPreviewSenderName = null,
forwardedFromMessageId = null,
forwardedFromDisplayName = null,
type = "text",
text = text,
status = "pending",
@@ -208,7 +211,10 @@ class NetworkMessageRepository @Inject constructor(
senderUsername = null,
senderAvatarUrl = null,
replyToMessageId = replyToMessageId,
replyPreviewText = null,
replyPreviewSenderName = null,
forwardedFromMessageId = null,
forwardedFromDisplayName = null,
type = messageType,
text = caption,
status = "pending",

View File

@@ -12,7 +12,10 @@ data class MessageItem(
val isOutgoing: Boolean,
val status: String?,
val replyToMessageId: Long?,
val replyPreviewText: String?,
val replyPreviewSenderName: String?,
val forwardedFromMessageId: Long?,
val forwardedFromDisplayName: String?,
val attachmentWaveform: List<Int>?,
val attachments: List<MessageAttachment>,
)

View File

@@ -48,7 +48,10 @@ class HandleRealtimeEventsUseCase @Inject constructor(
senderUsername = null,
senderAvatarUrl = null,
replyToMessageId = event.replyToMessageId,
replyPreviewText = null,
replyPreviewSenderName = null,
forwardedFromMessageId = null,
forwardedFromDisplayName = null,
type = event.type ?: "text",
text = event.text,
status = null,

View File

@@ -92,7 +92,10 @@ class MessageDaoTest {
senderUsername = "user",
senderAvatarUrl = null,
replyToMessageId = null,
replyPreviewText = null,
replyPreviewSenderName = null,
forwardedFromMessageId = null,
forwardedFromDisplayName = null,
type = "text",
text = text,
status = null,

View File

@@ -66,7 +66,10 @@ class MessageActionStateTest {
isOutgoing = true,
status = "sent",
replyToMessageId = null,
replyPreviewText = null,
replyPreviewSenderName = null,
forwardedFromMessageId = null,
forwardedFromDisplayName = null,
attachmentWaveform = null,
attachments = emptyList(),
)