Исправлены синхронизация групп, выделение сообщений и фон чата
This commit is contained in:
@@ -53,6 +53,36 @@ class AvatarRepository(
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun normalizeOwnerKey(publicKey: String): String {
|
||||
val trimmed = publicKey.trim()
|
||||
if (trimmed.isBlank()) return trimmed
|
||||
return when {
|
||||
trimmed.startsWith("#group:") -> {
|
||||
val groupId = trimmed.removePrefix("#group:").trim()
|
||||
if (groupId.isBlank()) trimmed else "#group:$groupId"
|
||||
}
|
||||
trimmed.startsWith("group:", ignoreCase = true) -> {
|
||||
val groupId = trimmed.substringAfter(':').trim()
|
||||
if (groupId.isBlank()) trimmed else "#group:$groupId"
|
||||
}
|
||||
else -> trimmed
|
||||
}
|
||||
}
|
||||
|
||||
private fun lookupKeys(publicKey: String): List<String> {
|
||||
val normalized = normalizeOwnerKey(publicKey)
|
||||
if (normalized.isBlank()) return emptyList()
|
||||
val keys = linkedSetOf(normalized)
|
||||
if (normalized.startsWith("#group:")) {
|
||||
keys.add(normalized.removePrefix("#group:"))
|
||||
}
|
||||
val trimmed = publicKey.trim()
|
||||
if (trimmed.isNotBlank()) {
|
||||
keys.add(trimmed)
|
||||
}
|
||||
return keys.toList()
|
||||
}
|
||||
|
||||
/**
|
||||
* Получить аватары пользователя
|
||||
@@ -60,14 +90,20 @@ class AvatarRepository(
|
||||
* @param allDecode true = вся история, false = только последний (для списков)
|
||||
*/
|
||||
fun getAvatars(publicKey: String, allDecode: Boolean = false): StateFlow<List<AvatarInfo>> {
|
||||
val normalizedKey = normalizeOwnerKey(publicKey)
|
||||
val keys = lookupKeys(publicKey)
|
||||
if (normalizedKey.isBlank() || keys.isEmpty()) {
|
||||
return MutableStateFlow(emptyList<AvatarInfo>()).asStateFlow()
|
||||
}
|
||||
|
||||
// Проверяем LRU cache (accessOrder=true обновляет позицию при get)
|
||||
memoryCache[publicKey]?.let { return it.flow.asStateFlow() }
|
||||
memoryCache[normalizedKey]?.let { return it.flow.asStateFlow() }
|
||||
|
||||
// Создаем новый flow для этого пользователя
|
||||
val flow = MutableStateFlow<List<AvatarInfo>>(emptyList())
|
||||
|
||||
// Подписываемся на изменения в БД
|
||||
val job = avatarDao.getAvatars(publicKey)
|
||||
val job = avatarDao.getAvatarsByKeys(keys)
|
||||
.onEach { entities ->
|
||||
val avatars = if (allDecode) {
|
||||
// Параллельная загрузка всей истории
|
||||
@@ -86,7 +122,7 @@ class AvatarRepository(
|
||||
}
|
||||
.launchIn(repositoryScope)
|
||||
|
||||
memoryCache[publicKey] = CacheEntry(flow, job)
|
||||
memoryCache[normalizedKey] = CacheEntry(flow, job)
|
||||
return flow.asStateFlow()
|
||||
}
|
||||
|
||||
@@ -94,7 +130,9 @@ class AvatarRepository(
|
||||
* Получить последний аватар пользователя (suspend версия)
|
||||
*/
|
||||
suspend fun getLatestAvatar(publicKey: String): AvatarInfo? {
|
||||
val entity = avatarDao.getLatestAvatar(publicKey) ?: return null
|
||||
val keys = lookupKeys(publicKey)
|
||||
if (keys.isEmpty()) return null
|
||||
val entity = avatarDao.getLatestAvatarByKeys(keys) ?: return null
|
||||
return loadAndDecryptAvatar(entity)
|
||||
}
|
||||
|
||||
@@ -108,22 +146,24 @@ class AvatarRepository(
|
||||
suspend fun saveAvatar(fromPublicKey: String, base64Image: String) {
|
||||
withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val ownerKey = normalizeOwnerKey(fromPublicKey)
|
||||
if (ownerKey.isBlank()) return@withContext
|
||||
// Сохраняем файл
|
||||
val filePath = AvatarFileManager.saveAvatar(context, base64Image, fromPublicKey)
|
||||
val filePath = AvatarFileManager.saveAvatar(context, base64Image, ownerKey)
|
||||
|
||||
// Сохраняем в БД
|
||||
val entity = AvatarCacheEntity(
|
||||
publicKey = fromPublicKey,
|
||||
publicKey = ownerKey,
|
||||
avatar = filePath,
|
||||
timestamp = System.currentTimeMillis()
|
||||
)
|
||||
avatarDao.insertAvatar(entity)
|
||||
|
||||
// Очищаем старые аватары (оставляем только последние N)
|
||||
avatarDao.deleteOldAvatars(fromPublicKey, MAX_AVATAR_HISTORY)
|
||||
avatarDao.deleteOldAvatars(ownerKey, MAX_AVATAR_HISTORY)
|
||||
|
||||
// 🔄 Обновляем memory cache если он существует
|
||||
val cached = memoryCache[fromPublicKey]
|
||||
val cached = memoryCache[ownerKey]
|
||||
if (cached != null) {
|
||||
val avatarInfo = loadAndDecryptAvatar(entity)
|
||||
if (avatarInfo != null) {
|
||||
|
||||
Reference in New Issue
Block a user