Офлайн режим, начало

This commit is contained in:
Халимов Рустам
2026-04-20 01:00:35 +03:00
parent 7c66e1c0c0
commit 38e7184a0d
18 changed files with 214 additions and 35 deletions
@@ -13,6 +13,23 @@ enum class SyncStatus {
FAILED // Ошибка синхронизации
}
/**
* Entity для хранения чатов в локальной базе данных
*/
@Entity(tableName = "chats")
data class ChatEntity(
@PrimaryKey val id: String,
val name: String,
val avatar: String?,
val type: String = "personal", // personal, group, saved
val lastMessageId: String? = null,
val lastMessageText: String? = null,
val lastMessageAt: Long = 0,
val unreadCount: Int = 0,
val isPinned: Boolean = false,
val lastUpdated: Long = System.currentTimeMillis()
)
/**
* Entity для хранения сообщений в локальной базе данных
* Поддерживает офлайн-работу и фоновую синхронизацию
@@ -187,10 +204,44 @@ interface MessageDao {
suspend fun exists(id: String): Boolean
}
@Database(entities = [MessageEntity::class], version = 2)
@Dao
interface ChatDao {
@Query("SELECT * FROM chats ORDER BY isPinned DESC, lastMessageAt DESC")
fun getAllChatsFlow(): Flow<List<ChatEntity>>
@Query("SELECT * FROM chats ORDER BY isPinned DESC, lastMessageAt DESC")
suspend fun getAllChats(): List<ChatEntity>
@Query("SELECT * FROM chats WHERE id = :chatId LIMIT 1")
suspend fun getChatById(chatId: String): ChatEntity?
@Query("SELECT * FROM chats WHERE id = :chatId LIMIT 1")
fun getChatByIdFlow(chatId: String): Flow<ChatEntity?>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertChat(chat: ChatEntity)
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertChats(chats: List<ChatEntity>)
@Query("DELETE FROM chats WHERE id = :chatId")
suspend fun deleteChat(chatId: String)
@Query("DELETE FROM chats")
suspend fun clearAll()
@Query("UPDATE chats SET unreadCount = :count WHERE id = :chatId")
suspend fun updateUnreadCount(chatId: String, count: Int)
@Query("UPDATE chats SET lastMessageId = :lastMessageId, lastMessageText = :lastMessageText, lastMessageAt = :lastMessageAt WHERE id = :chatId")
suspend fun updateLastMessage(chatId: String, lastMessageId: String?, lastMessageText: String?, lastMessageAt: Long)
}
@Database(entities = [MessageEntity::class, ChatEntity::class], version = 3)
@TypeConverters(SyncStatusConverter::class)
abstract class ChatDatabase : RoomDatabase() {
abstract fun messageDao(): MessageDao
abstract fun chatDao(): ChatDao
companion object {
const val DATABASE_NAME = "knot_chat_database"
@@ -37,3 +37,26 @@ val MIGRATION_1_2 = object : Migration(1, 2) {
""".trimIndent())
}
}
/**
* Миграция с версии 2 на версию 3
* Добавляет таблицу chats для кэширования списка чатов
*/
val MIGRATION_2_3 = object : Migration(2, 3) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("""
CREATE TABLE IF NOT EXISTS chats (
id TEXT PRIMARY KEY NOT NULL,
name TEXT NOT NULL,
avatar TEXT,
type TEXT NOT NULL DEFAULT 'personal',
lastMessageId TEXT,
lastMessageText TEXT,
lastMessageAt INTEGER NOT NULL DEFAULT 0,
unreadCount INTEGER NOT NULL DEFAULT 0,
isPinned INTEGER NOT NULL DEFAULT 0,
lastUpdated INTEGER NOT NULL DEFAULT 0
)
""".trimIndent())
}
}
+2 -1
View File
@@ -5,6 +5,7 @@ import androidx.room.Room
import core.database.data.ChatDatabase
import core.database.data.MessageDao
import core.database.data.MIGRATION_1_2
import core.database.data.MIGRATION_2_3
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@@ -24,7 +25,7 @@ object DatabaseModule {
ChatDatabase::class.java,
ChatDatabase.DATABASE_NAME
)
.addMigrations(MIGRATION_1_2)
.addMigrations(MIGRATION_1_2, MIGRATION_2_3)
.build()
}