Большой пакет: поиск по сообщениям, клики по тэгам, темы обоев и UX-фиксы

Что вошло:\n- Добавлен полноценный Messages-tab в SearchScreen: поиск по тексту сообщений по всей базе, батчевый проход, параллельная дешифровка, кеш расшифровки, подсветка совпадений, сниппеты и быстрый переход в нужный диалог.\n- В Chats-tab добавлены алиасы для Saved Messages (saved/saved messages/избранное/сохраненные и др.), чтобы чат открывался по текстовому поиску даже без точного username/public key.\n- Для search-бэкенда расширен DAO: getAllMessagesPaged() для постраничного обхода сообщений аккаунта.\n- Исправлена логика клика по @тэгам в сообщениях:\n  - переход теперь ведет сразу в чат пользователя (а не в профиль);\n  - добавлен fallback-резолв username -> user через локальный диалог, кеш протокола и PacketSearch;\n  - добавлен DAO getDialogByUsername() (регистронезависимо и с игнором @).\n- Усилена обработка PacketSearch в ProtocolManager:\n  - добавлена очередь ожидания pendingSearchQueries;\n  - нормализация query (без @, lowercase);\n  - устойчивый матч ответов сервера (raw/normalized/by username);\n  - добавлены методы getCachedUserByUsername() и searchUsers().\n- Исправлен конфликт тачей между ClickableSpan и bubble-menu:\n  - в AppleEmojiText/AppleEmojiTextView добавлен callback начала тапа по span;\n  - улучшен hit-test по span (включая пограничные offset/layout fallback);\n  - suppress performClick на span-тапах;\n  - в MessageBubble добавлен тайм-guard, чтобы tap по span не открывал context menu.\n- Стабилизирован verified-бейдж в заголовке чата: агрегируется из переданного user, кеша протокола, локальной БД и серверного resolve; отображается консистентно в личных чатах.\n- Улучшен пустой экран Saved Messages при обоях: добавлена аккуратная подложка/бордер и выровненный текст, чтобы контент оставался читабельным на любом фоне.\n- Реализована автосвязка обоев между светлой/темной темами:\n  - добавлены pairGroup и mapToTheme/resolveWallpaperForTheme в ThemeWallpapers;\n  - добавлены отдельные prefs-ключи для light/dark wallpaper;\n  - MainActivity теперь автоматически подбирает и сохраняет обои под активную тему и сохраняет выбор по теме.\n- Биометрия: если на устройстве нет hardware fingerprint, экран включения биометрии не показывается (и доступность возвращает NotAvailable).\n- Небольшие UI-фиксы: поправлено позиционирование галочки в сайдбаре.\n- Техдолг: удалена неиспользуемая зависимость jsoup из build.gradle.
This commit is contained in:
2026-03-21 21:12:52 +05:00
parent c929685e04
commit 9d3e5bcb10
14 changed files with 1100 additions and 108 deletions

View File

@@ -58,6 +58,7 @@ import com.rosetta.messenger.ui.settings.OtherProfileScreen
import com.rosetta.messenger.ui.settings.ProfileScreen
import com.rosetta.messenger.ui.settings.SafetyScreen
import com.rosetta.messenger.ui.settings.ThemeScreen
import com.rosetta.messenger.ui.settings.ThemeWallpapers
import com.rosetta.messenger.ui.settings.UpdatesScreen
import com.rosetta.messenger.ui.splash.SplashScreen
import com.rosetta.messenger.ui.theme.RosettaAndroidTheme
@@ -743,6 +744,8 @@ fun MainScreen(
.backgroundBlurColorIdForAccount(accountPublicKey)
.collectAsState(initial = "avatar")
val chatWallpaperId by prefsManager.chatWallpaperId.collectAsState(initial = "")
val chatWallpaperIdLight by prefsManager.chatWallpaperIdLight.collectAsState(initial = "")
val chatWallpaperIdDark by prefsManager.chatWallpaperIdDark.collectAsState(initial = "")
val pinnedChats by prefsManager.pinnedChats.collectAsState(initial = emptySet())
// AvatarRepository для работы с аватарами
@@ -763,6 +766,29 @@ fun MainScreen(
// Coroutine scope for profile updates
val mainScreenScope = rememberCoroutineScope()
LaunchedEffect(isDarkTheme, chatWallpaperId, chatWallpaperIdLight, chatWallpaperIdDark) {
val targetWallpaperId =
ThemeWallpapers.resolveWallpaperForTheme(
currentWallpaperId = chatWallpaperId,
isDarkTheme = isDarkTheme,
darkThemeWallpaperId = chatWallpaperIdDark,
lightThemeWallpaperId = chatWallpaperIdLight
)
if (targetWallpaperId != chatWallpaperId) {
prefsManager.setChatWallpaperId(targetWallpaperId)
}
val currentThemeStored =
if (isDarkTheme) chatWallpaperIdDark else chatWallpaperIdLight
if (currentThemeStored != targetWallpaperId) {
prefsManager.setChatWallpaperIdForTheme(
isDarkTheme = isDarkTheme,
value = targetWallpaperId
)
}
}
// 🔥 Простая навигация с swipe back
Box(modifier = Modifier.fillMaxSize()) {
// Base layer - chats list (всегда видимый, чтобы его было видно при свайпе)
@@ -971,7 +997,13 @@ fun MainScreen(
onBack = { navStack = navStack.filterNot { it is Screen.Theme } },
onThemeModeChange = onThemeModeChange,
onWallpaperChange = { wallpaperId ->
mainScreenScope.launch { prefsManager.setChatWallpaperId(wallpaperId) }
mainScreenScope.launch {
prefsManager.setChatWallpaperIdForTheme(
isDarkTheme = isDarkTheme,
value = wallpaperId
)
prefsManager.setChatWallpaperId(wallpaperId)
}
}
)
}
@@ -1280,6 +1312,16 @@ fun MainScreen(
}
val biometricAccountManager = remember { AccountManager(context) }
val activity = context as? FragmentActivity
val isFingerprintSupported = remember {
biometricManager.isFingerprintHardwareAvailable()
}
if (!isFingerprintSupported) {
LaunchedEffect(Unit) {
navStack = navStack.filterNot { it is Screen.Biometric }
}
return@SwipeBackContainer
}
BiometricEnableScreen(
isDarkTheme = isDarkTheme,