feat: Enhance search functionality and user experience
- Added local account metadata handling in SearchScreen for improved "Saved Messages" search fallback. - Updated search logic to include username and account name checks when searching for the user. - Introduced search logging in SearchUsersViewModel for better debugging and tracking of search queries. - Refactored image download process in AttachmentComponents to include detailed logging for debugging. - Created AttachmentDownloadDebugLogger to manage and display download logs. - Improved DeviceVerificationBanner UI for better user engagement during device verification. - Adjusted OtherProfileScreen layout to enhance information visibility and user interaction. - Updated network security configuration to include new Let's Encrypt certificate for CDN.
This commit is contained in:
@@ -13,10 +13,15 @@ import com.rosetta.messenger.MainActivity
|
||||
import com.rosetta.messenger.R
|
||||
import com.rosetta.messenger.data.AccountManager
|
||||
import com.rosetta.messenger.data.PreferencesManager
|
||||
import com.rosetta.messenger.network.PacketPushNotification
|
||||
import com.rosetta.messenger.network.ProtocolManager
|
||||
import com.rosetta.messenger.network.PushNotificationAction
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Firebase Cloud Messaging Service для обработки push-уведомлений
|
||||
@@ -47,7 +52,13 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() {
|
||||
fun cancelNotificationForChat(context: Context, senderPublicKey: String) {
|
||||
val notificationManager =
|
||||
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
notificationManager.cancel(getNotificationIdForChat(senderPublicKey))
|
||||
val normalizedKey = senderPublicKey.trim()
|
||||
if (normalizedKey.isNotEmpty()) {
|
||||
notificationManager.cancel(getNotificationIdForChat(normalizedKey))
|
||||
}
|
||||
// Fallback: некоторые серверные payload могут прийти без sender key.
|
||||
// Для них используется ID от пустой строки — тоже очищаем при входе в диалог.
|
||||
notificationManager.cancel(getNotificationIdForChat(""))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,36 +69,79 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() {
|
||||
// Сохраняем токен локально
|
||||
saveFcmToken(token)
|
||||
|
||||
// 📤 Токен будет отправлен на сервер после успешного логина в MainActivity
|
||||
// Best-effort: если соединение уже авторизовано — сразу обновляем подписку на push.
|
||||
if (ProtocolManager.isAuthenticated()) {
|
||||
runCatching {
|
||||
ProtocolManager.send(
|
||||
PacketPushNotification().apply {
|
||||
notificationsToken = token
|
||||
action = PushNotificationAction.SUBSCRIBE
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Вызывается когда получено push-уведомление */
|
||||
override fun onMessageReceived(remoteMessage: RemoteMessage) {
|
||||
super.onMessageReceived(remoteMessage)
|
||||
|
||||
// Обрабатываем data payload
|
||||
remoteMessage.data.isNotEmpty().let {
|
||||
val type = remoteMessage.data["type"]
|
||||
val senderPublicKey = remoteMessage.data["sender_public_key"]
|
||||
val senderName =
|
||||
remoteMessage.data["sender_name"] ?: senderPublicKey?.take(10) ?: "Unknown"
|
||||
val messagePreview = remoteMessage.data["message_preview"] ?: "New message"
|
||||
var handledMessageData = false
|
||||
|
||||
when (type) {
|
||||
"new_message" -> {
|
||||
// Показываем уведомление о новом сообщении
|
||||
// Обрабатываем data payload
|
||||
if (remoteMessage.data.isNotEmpty()) {
|
||||
val data = remoteMessage.data
|
||||
val type =
|
||||
firstNonBlank(data, "type", "event", "action")
|
||||
?.lowercase(Locale.ROOT)
|
||||
.orEmpty()
|
||||
val senderPublicKey =
|
||||
firstNonBlank(
|
||||
data,
|
||||
"sender_public_key",
|
||||
"from_public_key",
|
||||
"fromPublicKey",
|
||||
"public_key",
|
||||
"publicKey"
|
||||
)
|
||||
val senderName =
|
||||
firstNonBlank(data, "sender_name", "from_title", "sender", "title", "name")
|
||||
?: senderPublicKey?.take(10)
|
||||
?: "Rosetta"
|
||||
val messagePreview =
|
||||
firstNonBlank(data, "message_preview", "message", "text", "body")
|
||||
?: "New message"
|
||||
|
||||
val isReadEvent = type == "message_read" || type == "read"
|
||||
val isMessageEvent =
|
||||
type == "new_message" ||
|
||||
type == "message" ||
|
||||
type == "newmessage" ||
|
||||
type == "msg_new"
|
||||
|
||||
when {
|
||||
isMessageEvent -> {
|
||||
showMessageNotification(senderPublicKey, senderName, messagePreview)
|
||||
handledMessageData = true
|
||||
}
|
||||
"message_read" -> {
|
||||
// Сообщение прочитано - можно обновить UI если приложение открыто
|
||||
isReadEvent -> {
|
||||
handledMessageData = true
|
||||
}
|
||||
// Fallback for servers sending data-only payload without explicit "type".
|
||||
senderPublicKey != null || data.containsKey("message_preview") || data.containsKey("message") || data.containsKey("text") -> {
|
||||
showMessageNotification(senderPublicKey, senderName, messagePreview)
|
||||
handledMessageData = true
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
// Обрабатываем notification payload (если есть)
|
||||
// Обрабатываем notification payload (если есть).
|
||||
// Для new_message используем data-ветку выше, чтобы не показывать дубликаты
|
||||
// с неуправляемым notification id.
|
||||
remoteMessage.notification?.let {
|
||||
showSimpleNotification(it.title ?: "Rosetta", it.body ?: "New message")
|
||||
if (!handledMessageData) {
|
||||
showSimpleNotification(it.title ?: "Rosetta", it.body ?: "New message")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +152,7 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() {
|
||||
messagePreview: String
|
||||
) {
|
||||
// 🔥 Не показываем уведомление если приложение открыто
|
||||
if (isAppInForeground) {
|
||||
if (isAppInForeground || !areNotificationsEnabled()) {
|
||||
return
|
||||
}
|
||||
val senderKey = senderPublicKey?.trim().orEmpty()
|
||||
@@ -144,7 +198,7 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() {
|
||||
/** Показать простое уведомление */
|
||||
private fun showSimpleNotification(title: String, body: String) {
|
||||
// 🔥 Не показываем уведомление если приложение открыто
|
||||
if (isAppInForeground) {
|
||||
if (isAppInForeground || !areNotificationsEnabled()) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -204,6 +258,22 @@ class RosettaFirebaseMessagingService : FirebaseMessagingService() {
|
||||
prefs.edit().putString("fcm_token", token).apply()
|
||||
}
|
||||
|
||||
private fun areNotificationsEnabled(): Boolean {
|
||||
return runCatching {
|
||||
runBlocking(Dispatchers.IO) {
|
||||
PreferencesManager(applicationContext).notificationsEnabled.first()
|
||||
}
|
||||
}.getOrDefault(true)
|
||||
}
|
||||
|
||||
private fun firstNonBlank(data: Map<String, String>, vararg keys: String): String? {
|
||||
for (key in keys) {
|
||||
val value = data[key]?.trim()
|
||||
if (!value.isNullOrEmpty()) return value
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/** Проверка: замьючен ли диалог для текущего аккаунта */
|
||||
private fun isDialogMuted(senderPublicKey: String): Boolean {
|
||||
if (senderPublicKey.isBlank()) return false
|
||||
|
||||
Reference in New Issue
Block a user