118 lines
4.2 KiB
Kotlin
118 lines
4.2 KiB
Kotlin
package chats.data.local.mappers
|
|
|
|
import chats.data.local.database.MessageEntity
|
|
import chats.domain.model.Message
|
|
import chats.domain.model.MessageStatus
|
|
import com.google.gson.Gson
|
|
import com.google.gson.reflect.TypeToken
|
|
|
|
private val gson = Gson()
|
|
|
|
/**
|
|
* Преобразует Domain Message в Entity для сохранения в Room
|
|
*/
|
|
fun Message.toEntity(status: MessageStatus = MessageStatus.SENT): MessageEntity {
|
|
return MessageEntity(
|
|
localId = this.id.takeIf { it.isNotBlank() } ?: java.util.UUID.randomUUID().toString(),
|
|
serverId = if (status == MessageStatus.SENT || status == MessageStatus.DELIVERED || status == MessageStatus.READ) this.id else null,
|
|
idempotencyKey = java.util.UUID.randomUUID().toString(),
|
|
chatId = this.chatId,
|
|
senderId = this.senderId,
|
|
senderName = this.senderName,
|
|
senderAvatar = this.senderAvatar,
|
|
content = this.content,
|
|
sequenceId = this.sequenceId.toLong(),
|
|
createdAt = this.createdAt,
|
|
mediaType = this.mediaType.name,
|
|
mediaJson = gson.toJson(this.media),
|
|
reactionsJson = gson.toJson(this.reactions),
|
|
status = status,
|
|
isPinned = this.isPinned,
|
|
isForwarded = this.isForwarded,
|
|
forwardedFromName = this.forwardedFromName,
|
|
replyToServerId = this.replyTo?.id,
|
|
createdAtMillis = parseTimestamp(this.createdAt),
|
|
updatedAtMillis = System.currentTimeMillis()
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Преобразует Entity в Domain Message для отображения в UI
|
|
*/
|
|
fun MessageEntity.toDomain(): Message {
|
|
val mediaListType = object : TypeToken<List<chats.domain.model.Media>>() {}.type
|
|
val reactionsMapType = object : TypeToken<Map<String, Int>>() {}.type
|
|
|
|
return Message(
|
|
id = this.serverId ?: this.localId,
|
|
chatId = this.chatId,
|
|
senderId = this.senderId,
|
|
senderName = this.senderName,
|
|
senderAvatar = this.senderAvatar,
|
|
content = this.content,
|
|
sequenceId = this.sequenceId.toInt(),
|
|
createdAt = this.createdAt,
|
|
media = try {
|
|
gson.fromJson<List<chats.domain.model.Media>>(this.mediaJson, mediaListType) ?: emptyList()
|
|
} catch (e: Exception) {
|
|
emptyList()
|
|
},
|
|
mediaType = try {
|
|
chats.domain.model.MediaType.valueOf(this.mediaType)
|
|
} catch (e: Exception) {
|
|
chats.domain.model.MediaType.TEXT
|
|
},
|
|
reactions = try {
|
|
gson.fromJson<Map<String, Int>>(this.reactionsJson, reactionsMapType) ?: emptyMap()
|
|
} catch (e: Exception) {
|
|
emptyMap()
|
|
},
|
|
isRead = this.status == MessageStatus.READ,
|
|
isPinned = this.isPinned,
|
|
isForwarded = this.isForwarded,
|
|
forwardedFromName = this.forwardedFromName,
|
|
replyTo = null, // replyTo загружается отдельно если нужно
|
|
status = this.status
|
|
)
|
|
}
|
|
|
|
/**
|
|
* Создает новое локальное сообщение со статусом PENDING
|
|
*/
|
|
fun createPendingMessageEntity(
|
|
chatId: String,
|
|
senderId: String,
|
|
senderName: String,
|
|
content: String?,
|
|
mediaType: String = "TEXT",
|
|
mediaJson: String = "[]",
|
|
replyToId: String? = null
|
|
): MessageEntity {
|
|
val localId = java.util.UUID.randomUUID().toString()
|
|
return MessageEntity(
|
|
localId = localId,
|
|
serverId = null,
|
|
idempotencyKey = localId, // Используем localId как idempotency key
|
|
chatId = chatId,
|
|
senderId = senderId,
|
|
senderName = senderName,
|
|
content = content,
|
|
sequenceId = -1, // Будет обновлено после получения с сервера
|
|
createdAt = java.time.ZonedDateTime.now().toString(),
|
|
mediaType = mediaType,
|
|
mediaJson = mediaJson,
|
|
status = MessageStatus.PENDING,
|
|
replyToServerId = replyToId,
|
|
createdAtMillis = System.currentTimeMillis(),
|
|
updatedAtMillis = System.currentTimeMillis()
|
|
)
|
|
}
|
|
|
|
private fun parseTimestamp(createdAt: String): Long {
|
|
return try {
|
|
java.time.ZonedDateTime.parse(createdAt).toInstant().toEpochMilli()
|
|
} catch (e: Exception) {
|
|
System.currentTimeMillis()
|
|
}
|
|
}
|