Офлайн режим, начало
This commit is contained in:
@@ -15,6 +15,7 @@ import chats.domain.model.Message
|
||||
import chats.domain.model.MediaType
|
||||
import chats.domain.repository.ChatRepository
|
||||
import core.database.data.ChatDatabase
|
||||
import core.database.data.ChatDao
|
||||
import core.database.data.MessageDao
|
||||
import core.database.data.MessageEntity
|
||||
import core.database.data.SyncStatus
|
||||
@@ -51,7 +52,8 @@ class ChatRepositoryImpl @Inject constructor(
|
||||
private val api: ChatApi,
|
||||
private val tokenManager: TokenManager,
|
||||
private val serverConfig: ServerConfig,
|
||||
private val dao: MessageDao,
|
||||
private val messageDao: MessageDao,
|
||||
private val chatDao: ChatDao,
|
||||
private val database: ChatDatabase,
|
||||
private val hubClient: ChatHubClient,
|
||||
private val signalRHandler: MessageSignalRHandler,
|
||||
@@ -68,7 +70,43 @@ class ChatRepositoryImpl @Inject constructor(
|
||||
|
||||
override suspend fun getChats(): List<Chat> {
|
||||
val currentUserId = tokenManager.getUserId() ?: ""
|
||||
return api.getChats().map { it.toDomain(currentUserId, baseUrl) }
|
||||
return try {
|
||||
// Пробуем загрузить из сети
|
||||
val chats = api.getChats().map { it.toDomain(currentUserId, baseUrl) }
|
||||
// Кэшируем в Room
|
||||
val entities = chats.map { it.toEntity() }
|
||||
chatDao.insertChats(entities)
|
||||
// Кэшируем последние сообщения
|
||||
chats.forEach { chat ->
|
||||
chat.lastMessage?.let { msg ->
|
||||
messageDao.upsertMessage(msg.toEntity(gson))
|
||||
}
|
||||
}
|
||||
android.util.Log.d(TAG, "Cached ${entities.size} chats with messages")
|
||||
chats
|
||||
} catch (e: Exception) {
|
||||
android.util.Log.d(TAG, "Network load failed, using cache")
|
||||
// При ошибке - возвращаем из кэша с загрузкой последних сообщений
|
||||
chatDao.getAllChats().map { entity ->
|
||||
val lastMessage = entity.lastMessageId?.let { messageId ->
|
||||
messageDao.getMessageById(messageId)?.toDomain(baseUrl, gson)
|
||||
}
|
||||
entity.toDomain(currentUserId, baseUrl, lastMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getChatsFlow(): Flow<List<Chat>> {
|
||||
val currentUserId = tokenManager.getUserId() ?: ""
|
||||
return chatDao.getAllChatsFlow().map { entities ->
|
||||
entities.map { entity ->
|
||||
// Загружаем последнее сообщение из базы для каждого чата
|
||||
val lastMessage = entity.lastMessageId?.let { messageId ->
|
||||
messageDao.getMessageById(messageId)?.toDomain(baseUrl, gson)
|
||||
}
|
||||
entity.toDomain(currentUserId, baseUrl, lastMessage)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getMessagesPaging(chatId: String): Flow<PagingData<Message>> {
|
||||
@@ -78,15 +116,15 @@ class ChatRepositoryImpl @Inject constructor(
|
||||
initialLoadSize = 50,
|
||||
enablePlaceholders = false
|
||||
)
|
||||
|
||||
|
||||
return Pager(
|
||||
config = pagingConfig,
|
||||
pagingSourceFactory = { dao.getMessagesPagingSource(chatId) },
|
||||
pagingSourceFactory = { messageDao.getMessagesPagingSource(chatId) },
|
||||
remoteMediator = MessageRemoteMediator(
|
||||
chatId = chatId,
|
||||
api = api,
|
||||
database = database,
|
||||
dao = dao,
|
||||
dao = messageDao,
|
||||
serverConfig = serverConfig,
|
||||
tokenManager = tokenManager
|
||||
)
|
||||
@@ -96,7 +134,7 @@ class ChatRepositoryImpl @Inject constructor(
|
||||
}
|
||||
|
||||
override fun getMessagesFlow(chatId: String): Flow<List<Message>> {
|
||||
return dao.getMessages(chatId).map { entities ->
|
||||
return messageDao.getMessages(chatId).map { entities ->
|
||||
entities.map { it.toDomain(baseUrl, gson) }
|
||||
}
|
||||
}
|
||||
@@ -111,7 +149,7 @@ class ChatRepositoryImpl @Inject constructor(
|
||||
|
||||
if (messages.isNotEmpty()) {
|
||||
val entities = messages.map { it.toEntity(baseUrl, currentUserId, gson) }
|
||||
dao.upsertMessages(entities)
|
||||
messageDao.upsertMessages(entities)
|
||||
Log.d(TAG, "Cached ${entities.size} messages")
|
||||
}
|
||||
|
||||
@@ -145,7 +183,7 @@ class ChatRepositoryImpl @Inject constructor(
|
||||
editedContent = null, lastUpdated = currentTime
|
||||
)
|
||||
|
||||
dao.insertMessage(localMessage)
|
||||
messageDao.insertMessage(localMessage)
|
||||
Log.d(TAG, "Saved local message: $localId")
|
||||
MessageSyncWorker.scheduleSync(context)
|
||||
|
||||
@@ -191,20 +229,20 @@ class ChatRepositoryImpl @Inject constructor(
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error marking messages as read", e)
|
||||
}
|
||||
dao.markMessagesAsRead(chatId, lastReadSequenceId)
|
||||
messageDao.markMessagesAsRead(chatId, lastReadSequenceId)
|
||||
}
|
||||
|
||||
override suspend fun saveMessage(message: Message) {
|
||||
dao.insertMessage(message.toEntity(gson))
|
||||
messageDao.insertMessage(message.toEntity(gson))
|
||||
}
|
||||
|
||||
override suspend fun deleteLocalMessage(messageId: String) {
|
||||
dao.markAsDeletedLocally(messageId)
|
||||
messageDao.markAsDeletedLocally(messageId)
|
||||
MessageSyncWorker.scheduleSync(context)
|
||||
}
|
||||
|
||||
override suspend fun editLocalMessage(messageId: String, newContent: String) {
|
||||
dao.markAsEditedLocally(messageId, newContent)
|
||||
messageDao.markAsEditedLocally(messageId, newContent)
|
||||
MessageSyncWorker.scheduleSync(context)
|
||||
}
|
||||
|
||||
@@ -239,14 +277,14 @@ class ChatRepositoryImpl @Inject constructor(
|
||||
|
||||
override suspend fun deleteMessage(messageId: String, forEveryone: Boolean) {
|
||||
api.deleteMessage(messageId, forEveryone)
|
||||
dao.deleteMessage(messageId)
|
||||
messageDao.deleteMessage(messageId)
|
||||
}
|
||||
|
||||
override suspend fun editMessage(messageId: String, content: String): Message {
|
||||
val request = SendMessageRequest(content = content)
|
||||
val currentUserId = tokenManager.getUserId() ?: ""
|
||||
val response = api.editMessage(messageId, request)
|
||||
dao.insertMessage(response.toEntity(baseUrl, currentUserId, gson))
|
||||
messageDao.insertMessage(response.toEntity(baseUrl, currentUserId, gson))
|
||||
return response.toDomain(currentUserId, baseUrl)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,9 @@ package chats.data.repository
|
||||
|
||||
import chats.data.remote.dto.*
|
||||
import chats.domain.model.*
|
||||
import core.database.data.ChatEntity
|
||||
import core.database.data.MessageEntity
|
||||
import core.database.data.SyncStatus
|
||||
|
||||
// Mappers
|
||||
fun ChatDto.toDomain(currentUserId: String, baseUrl: String): Chat {
|
||||
@@ -23,6 +26,38 @@ fun ChatDto.toDomain(currentUserId: String, baseUrl: String): Chat {
|
||||
)
|
||||
}
|
||||
|
||||
fun Chat.toEntity(): ChatEntity {
|
||||
return ChatEntity(
|
||||
id = id,
|
||||
name = name,
|
||||
avatar = avatar,
|
||||
type = type,
|
||||
lastMessageId = lastMessage?.id,
|
||||
lastMessageText = lastMessage?.content,
|
||||
lastMessageAt = lastMessage?.createdAt?.let {
|
||||
try { java.text.SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", java.util.Locale.US).parse(it)?.time ?: 0L }
|
||||
catch (e: Exception) { 0L }
|
||||
} ?: 0L,
|
||||
unreadCount = unreadCount,
|
||||
isPinned = false
|
||||
)
|
||||
}
|
||||
|
||||
fun ChatEntity.toDomain(
|
||||
currentUserId: String,
|
||||
baseUrl: String,
|
||||
lastMessage: Message? = null
|
||||
): Chat {
|
||||
return Chat(
|
||||
id = id,
|
||||
type = type,
|
||||
name = name,
|
||||
avatar = avatar,
|
||||
unreadCount = unreadCount,
|
||||
lastMessage = lastMessage
|
||||
)
|
||||
}
|
||||
|
||||
fun Message.toEntity(gson: com.google.gson.Gson): core.database.data.MessageEntity {
|
||||
return core.database.data.MessageEntity(
|
||||
id = id,
|
||||
@@ -37,7 +72,12 @@ fun Message.toEntity(gson: com.google.gson.Gson): core.database.data.MessageEnti
|
||||
mediaJson = gson.toJson(media),
|
||||
reactionsJson = gson.toJson(reactions),
|
||||
isRead = isRead,
|
||||
replyToId = replyTo?.id
|
||||
replyToId = replyTo?.id,
|
||||
syncStatus = SyncStatus.SYNCED,
|
||||
isDeletedLocally = false,
|
||||
isEditedLocally = false,
|
||||
editedContent = null,
|
||||
lastUpdated = System.currentTimeMillis()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user