Refactor SwipeBackContainer for improved performance and readability

- Added lazy composition to skip setup until the screen is first opened, reducing allocations.
- Cleaned up code formatting for better readability.
- Enhanced comments for clarity on functionality.
- Streamlined gesture handling logic for swipe detection and animation.
This commit is contained in:
2026-02-08 07:34:25 +05:00
parent 58b754d5ba
commit 11a8ff7644
5 changed files with 1744 additions and 1679 deletions

View File

@@ -8,15 +8,15 @@ import androidx.room.migration.Migration
import androidx.sqlite.db.SupportSQLiteDatabase
@Database(
entities = [
EncryptedAccountEntity::class,
MessageEntity::class,
DialogEntity::class,
BlacklistEntity::class,
AvatarCacheEntity::class
],
version = 10,
exportSchema = false
entities =
[
EncryptedAccountEntity::class,
MessageEntity::class,
DialogEntity::class,
BlacklistEntity::class,
AvatarCacheEntity::class],
version = 11,
exportSchema = false
)
abstract class RosettaDatabase : RoomDatabase() {
abstract fun accountDao(): AccountDao
@@ -26,93 +26,140 @@ abstract class RosettaDatabase : RoomDatabase() {
abstract fun avatarDao(): AvatarDao
companion object {
@Volatile
private var INSTANCE: RosettaDatabase? = null
private val MIGRATION_4_5 = object : Migration(4, 5) {
override fun migrate(database: SupportSQLiteDatabase) {
// Добавляем новые столбцы для индикаторов прочтения
database.execSQL("ALTER TABLE dialogs ADD COLUMN last_message_from_me INTEGER NOT NULL DEFAULT 0")
database.execSQL("ALTER TABLE dialogs ADD COLUMN last_message_delivered INTEGER NOT NULL DEFAULT 0")
database.execSQL("ALTER TABLE dialogs ADD COLUMN last_message_read INTEGER NOT NULL DEFAULT 0")
}
}
private val MIGRATION_5_6 = object : Migration(5, 6) {
override fun migrate(database: SupportSQLiteDatabase) {
// Добавляем поле username в encrypted_accounts
database.execSQL("ALTER TABLE encrypted_accounts ADD COLUMN username TEXT")
}
}
private val MIGRATION_6_7 = object : Migration(6, 7) {
override fun migrate(database: SupportSQLiteDatabase) {
// Создаем таблицу для кэша аватаров
database.execSQL("""
@Volatile private var INSTANCE: RosettaDatabase? = null
private val MIGRATION_4_5 =
object : Migration(4, 5) {
override fun migrate(database: SupportSQLiteDatabase) {
// Добавляем новые столбцы для индикаторов прочтения
database.execSQL(
"ALTER TABLE dialogs ADD COLUMN last_message_from_me INTEGER NOT NULL DEFAULT 0"
)
database.execSQL(
"ALTER TABLE dialogs ADD COLUMN last_message_delivered INTEGER NOT NULL DEFAULT 0"
)
database.execSQL(
"ALTER TABLE dialogs ADD COLUMN last_message_read INTEGER NOT NULL DEFAULT 0"
)
}
}
private val MIGRATION_5_6 =
object : Migration(5, 6) {
override fun migrate(database: SupportSQLiteDatabase) {
// Добавляем поле username в encrypted_accounts
database.execSQL("ALTER TABLE encrypted_accounts ADD COLUMN username TEXT")
}
}
private val MIGRATION_6_7 =
object : Migration(6, 7) {
override fun migrate(database: SupportSQLiteDatabase) {
// Создаем таблицу для кэша аватаров
database.execSQL(
"""
CREATE TABLE IF NOT EXISTS avatar_cache (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
public_key TEXT NOT NULL,
avatar TEXT NOT NULL,
timestamp INTEGER NOT NULL
)
""")
database.execSQL("CREATE INDEX IF NOT EXISTS index_avatar_cache_public_key_timestamp ON avatar_cache (public_key, timestamp)")
}
}
private val MIGRATION_7_8 = object : Migration(7, 8) {
override fun migrate(database: SupportSQLiteDatabase) {
// Удаляем таблицу avatar_delivery (больше не нужна)
database.execSQL("DROP TABLE IF EXISTS avatar_delivery")
}
}
"""
)
database.execSQL(
"CREATE INDEX IF NOT EXISTS index_avatar_cache_public_key_timestamp ON avatar_cache (public_key, timestamp)"
)
}
}
private val MIGRATION_7_8 =
object : Migration(7, 8) {
override fun migrate(database: SupportSQLiteDatabase) {
// Удаляем таблицу avatar_delivery (больше не нужна)
database.execSQL("DROP TABLE IF EXISTS avatar_delivery")
}
}
/**
* 🔥 МИГРАЦИЯ 8->9: Очищаем blob из attachments (как в desktop)
* Blob слишком большой для SQLite CursorWindow (2MB лимит)
* Просто обнуляем attachments - изображения перескачаются с CDN
* 🔥 МИГРАЦИЯ 8->9: Очищаем blob из attachments (как в desktop) Blob слишком большой для
* SQLite CursorWindow (2MB лимит) Просто обнуляем attachments - изображения перескачаются с
* CDN
*/
private val MIGRATION_8_9 = object : Migration(8, 9) {
override fun migrate(database: SupportSQLiteDatabase) {
// Очищаем все attachments с большими blob'ами
// Они будут перескачаны с CDN при открытии
database.execSQL("""
private val MIGRATION_8_9 =
object : Migration(8, 9) {
override fun migrate(database: SupportSQLiteDatabase) {
// Очищаем все attachments с большими blob'ами
// Они будут перескачаны с CDN при открытии
database.execSQL(
"""
UPDATE messages
SET attachments = '[]'
WHERE length(attachments) > 10000
""")
}
}
"""
)
}
}
/**
* 🔥 МИГРАЦИЯ 9->10: Повторная очистка blob из attachments
* Для пользователей которые уже были на версии 9
* 🔥 МИГРАЦИЯ 9->10: Повторная очистка blob из attachments Для пользователей которые уже
* были на версии 9
*/
private val MIGRATION_9_10 = object : Migration(9, 10) {
override fun migrate(database: SupportSQLiteDatabase) {
// Очищаем все attachments с большими blob'ами
database.execSQL("""
private val MIGRATION_9_10 =
object : Migration(9, 10) {
override fun migrate(database: SupportSQLiteDatabase) {
// Очищаем все attachments с большими blob'ами
database.execSQL(
"""
UPDATE messages
SET attachments = '[]'
WHERE length(attachments) > 10000
""")
}
}
"""
)
}
}
/**
* 🚀 МИГРАЦИЯ 10->11: Денормализация — кэш attachments последнего сообщения в dialogs
* Устраняет N+1 проблему: ранее для каждого диалога делался отдельный запрос к messages
*/
private val MIGRATION_10_11 =
object : Migration(10, 11) {
override fun migrate(database: SupportSQLiteDatabase) {
// Добавляем столбец для кэша attachments последнего сообщения
database.execSQL(
"ALTER TABLE dialogs ADD COLUMN last_message_attachments TEXT NOT NULL DEFAULT '[]'"
)
}
}
fun getDatabase(context: Context): RosettaDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
RosettaDatabase::class.java,
"rosetta_secure.db"
)
.setJournalMode(JournalMode.WRITE_AHEAD_LOGGING) // WAL mode for performance
.addMigrations(MIGRATION_4_5, MIGRATION_5_6, MIGRATION_6_7, MIGRATION_7_8, MIGRATION_8_9, MIGRATION_9_10)
.fallbackToDestructiveMigration() // Для разработки - только если миграция не найдена
.build()
INSTANCE = instance
instance
}
return INSTANCE
?: synchronized(this) {
val instance =
Room.databaseBuilder(
context.applicationContext,
RosettaDatabase::class.java,
"rosetta_secure.db"
)
.setJournalMode(
JournalMode.WRITE_AHEAD_LOGGING
) // WAL mode for performance
.addMigrations(
MIGRATION_4_5,
MIGRATION_5_6,
MIGRATION_6_7,
MIGRATION_7_8,
MIGRATION_8_9,
MIGRATION_9_10,
MIGRATION_10_11
)
.fallbackToDestructiveMigration() // Для разработки - только
// если миграция не
// найдена
.build()
INSTANCE = instance
instance
}
}
}
}