feat: Enhance avatar management with detailed logging and error handling

This commit is contained in:
2026-01-24 00:26:23 +05:00
parent b08bea2c14
commit 1367864008
11 changed files with 107 additions and 324 deletions

View File

@@ -6,6 +6,10 @@ import kotlinx.coroutines.flow.Flow
/**
* Entity для кэша аватаров - хранит пути к зашифрованным файлам
* Совместимо с desktop версией (AvatarProvider)
*
* Desktop логика:
* - Аватары передаются как attachment в сообщениях (AttachmentType.AVATAR)
* - Локальное хранение: SQLite + зашифрованные файлы
*/
@Entity(
tableName = "avatar_cache",
@@ -27,35 +31,12 @@ data class AvatarCacheEntity(
val timestamp: Long // Unix timestamp
)
/**
* Entity для трекинга доставки аватаров
* Отслеживает кому уже был отправлен текущий аватар
*/
@Entity(
tableName = "avatar_delivery",
indices = [
Index(value = ["public_key", "account"], unique = true)
]
)
data class AvatarDeliveryEntity(
@PrimaryKey(autoGenerate = true)
val id: Long = 0,
@ColumnInfo(name = "public_key")
val publicKey: String, // Публичный ключ получателя
@ColumnInfo(name = "account")
val account: String // Публичный ключ отправителя (мой аккаунт)
)
/**
* DAO для работы с аватарами
*/
@Dao
interface AvatarDao {
// ============ Avatar Cache ============
/**
* Получить все аватары пользователя (отсортированные по времени)
*/
@@ -81,7 +62,7 @@ interface AvatarDao {
suspend fun insertAvatar(avatar: AvatarCacheEntity)
/**
* Удалить все аватары пользователя (при смене аватара)
* Удалить все аватары пользователя
*/
@Query("DELETE FROM avatar_cache WHERE public_key = :publicKey")
suspend fun deleteAvatars(publicKey: String)
@@ -100,30 +81,4 @@ interface AvatarDao {
)
""")
suspend fun deleteOldAvatars(publicKey: String, keepCount: Int = 5)
// ============ Avatar Delivery ============
/**
* Проверить доставлен ли аватар контакту
*/
@Query("SELECT COUNT(*) > 0 FROM avatar_delivery WHERE public_key = :publicKey AND account = :account")
suspend fun isAvatarDelivered(publicKey: String, account: String): Boolean
/**
* Отметить аватар как доставленный
*/
@Insert(onConflict = OnConflictStrategy.IGNORE)
suspend fun markAvatarDelivered(delivery: AvatarDeliveryEntity)
/**
* Удалить все записи о доставке для аккаунта (при смене аватара)
*/
@Query("DELETE FROM avatar_delivery WHERE account = :account")
suspend fun clearDeliveryForAccount(account: String)
/**
* Получить список контактов, которым доставлен аватар
*/
@Query("SELECT public_key FROM avatar_delivery WHERE account = :account")
suspend fun getDeliveredContacts(account: String): List<String>
}

View File

@@ -13,10 +13,9 @@ import androidx.sqlite.db.SupportSQLiteDatabase
MessageEntity::class,
DialogEntity::class,
BlacklistEntity::class,
AvatarCacheEntity::class,
AvatarDeliveryEntity::class
AvatarCacheEntity::class
],
version = 7,
version = 8,
exportSchema = false
)
abstract class RosettaDatabase : RoomDatabase() {
@@ -58,16 +57,13 @@ abstract class RosettaDatabase : RoomDatabase() {
)
""")
database.execSQL("CREATE INDEX IF NOT EXISTS index_avatar_cache_public_key_timestamp ON avatar_cache (public_key, timestamp)")
// Создаем таблицу для трекинга доставки аватаров
database.execSQL("""
CREATE TABLE IF NOT EXISTS avatar_delivery (
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
public_key TEXT NOT NULL,
account TEXT NOT NULL
)
""")
database.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS index_avatar_delivery_public_key_account ON avatar_delivery (public_key, account)")
}
}
private val MIGRATION_7_8 = object : Migration(7, 8) {
override fun migrate(database: SupportSQLiteDatabase) {
// Удаляем таблицу avatar_delivery (больше не нужна)
database.execSQL("DROP TABLE IF EXISTS avatar_delivery")
}
}
@@ -79,7 +75,7 @@ abstract class RosettaDatabase : RoomDatabase() {
"rosetta_secure.db"
)
.setJournalMode(JournalMode.WRITE_AHEAD_LOGGING) // WAL mode for performance
.addMigrations(MIGRATION_4_5, MIGRATION_5_6, MIGRATION_6_7)
.addMigrations(MIGRATION_4_5, MIGRATION_5_6, MIGRATION_6_7, MIGRATION_7_8)
.fallbackToDestructiveMigration() // Для разработки - только если миграция не найдена
.build()
INSTANCE = instance