Files
forkmessager/client-mobile/chats/data/local/paging/MessageRemoteMediator.kt
Халимов Рустам df4feeeee5 Правки
2026-05-13 13:48:34 +03:00

104 lines
4.4 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package chats.data.local.paging
import androidx.paging.ExperimentalPagingApi
import androidx.paging.LoadType
import androidx.paging.PagingState
import androidx.paging.RemoteMediator
import androidx.room.withTransaction
import chats.data.local.dao.ChatDao
import chats.data.local.dao.MessageDao
import chats.data.local.database.AppDatabase
import chats.data.local.database.MessageEntity
import chats.data.local.mappers.toEntity
import chats.data.repository.toDomain as dtoToDomain
import chats.data.remote.api.ChatApi
import chats.domain.model.MessageStatus
import core.network.ServerConfig
import core.security.TokenManager
/**
* RemoteMediator для синхронизации сообщений с сервером.
* Работает с Room через withTransaction для атомарности.
*/
@OptIn(ExperimentalPagingApi::class)
class MessageRemoteMediator(
private val chatId: String,
private val messageDao: MessageDao,
private val chatDao: ChatDao,
private val chatApi: ChatApi,
private val tokenManager: TokenManager,
private val appDatabase: AppDatabase,
private val serverConfig: ServerConfig
) : RemoteMediator<Int, MessageEntity>() {
companion object {
private const val PAGE_SIZE = 30
}
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, MessageEntity>
): RemoteMediator.MediatorResult {
return try {
val currentUserId = tokenManager.getUserId() ?: ""
val baseUrl = serverConfig.getBaseUrl().removeSuffix("/api/")
val cursor = when (loadType) {
LoadType.REFRESH -> {
// При refresh загружаем новые сообщения после последнего локального
messageDao.getLastMessage(chatId)?.serverId
}
LoadType.PREPEND -> {
// Загружаем более старые сообщения перед первым локальным
messageDao.getFirstMessage(chatId)?.serverId
}
LoadType.APPEND -> {
// Загружаем новые сообщения после последнего локального
messageDao.getLastMessage(chatId)?.serverId
}
}
val remoteMessages = chatApi.getMessages(chatId, cursor = cursor, limit = PAGE_SIZE)
val endOfPaginationReached = remoteMessages.size < PAGE_SIZE
appDatabase.withTransaction {
if (loadType == LoadType.REFRESH) {
// При полном обновлении можно очистить старые SENT/DELIVERED/READ,
// но оставить локальные PENDING/FAILED
// messageDao.deleteMessagesByChatId(chatId) // Опционально
}
val entities = remoteMessages.map { dto ->
val existing = messageDao.getMessageByServerId(dto.id)
if (existing != null) {
// Обновляем существующее, сохраняя localId
existing.copy(
content = dto.content,
reactionsJson = com.google.gson.Gson().toJson(
dto.reactions?.associate { it.emoji to it.count } ?: emptyMap<String, Int>()
),
status = if (dto.senderId == currentUserId) MessageStatus.SENT else MessageStatus.DELIVERED,
updatedAtMillis = System.currentTimeMillis()
)
} else {
// Создаём новое
dto.dtoToDomain(currentUserId, baseUrl).toEntity(MessageStatus.SENT).copy(
localId = java.util.UUID.randomUUID().toString(),
serverId = dto.id,
idempotencyKey = dto.id
)
}
}
messageDao.insertMessages(entities)
}
RemoteMediator.MediatorResult.Success(endOfPaginationReached = endOfPaginationReached)
} catch (e: Exception) {
RemoteMediator.MediatorResult.Error(e)
}
}
override suspend fun initialize(): InitializeAction = InitializeAction.SKIP_INITIAL_REFRESH
}