feat: Implement deduplication for FCM token subscription and update related logic

This commit is contained in:
2026-02-25 23:03:28 +05:00
parent b7b99cdb40
commit 48861633ee
4 changed files with 41 additions and 85 deletions

View File

@@ -32,10 +32,8 @@ import com.rosetta.messenger.crypto.CryptoManager
import com.rosetta.messenger.data.RecentSearchesManager
import com.rosetta.messenger.data.resolveAccountDisplayName
import com.rosetta.messenger.database.RosettaDatabase
import com.rosetta.messenger.network.PacketPushNotification
import com.rosetta.messenger.network.ProtocolManager
import com.rosetta.messenger.network.ProtocolState
import com.rosetta.messenger.network.PushNotificationAction
import com.rosetta.messenger.network.SearchUser
import com.rosetta.messenger.repository.AvatarRepository
import com.rosetta.messenger.ui.auth.AccountInfo
@@ -62,9 +60,7 @@ import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.withTimeoutOrNull
class MainActivity : FragmentActivity() {
private lateinit var preferencesManager: PreferencesManager
@@ -177,15 +173,6 @@ class MainActivity : FragmentActivity() {
return@setContent
}
// Ensure push token subscription is sent whenever protocol reaches AUTHENTICATED.
// This recovers token binding after reconnects and delayed handshakes.
LaunchedEffect(protocolState, currentAccount?.publicKey) {
currentAccount ?: return@LaunchedEffect
if (protocolState == ProtocolState.AUTHENTICATED) {
sendFcmTokenToServer()
}
}
RosettaAndroidTheme(darkTheme = isDarkTheme, animated = true) {
Surface(
modifier = Modifier.fillMaxSize(),
@@ -254,10 +241,6 @@ class MainActivity : FragmentActivity() {
accountManager.setLastLoggedPublicKey(it.publicKey)
}
// 📤 Отправляем FCM токен на сервер после успешной
// аутентификации
account?.let { sendFcmTokenToServer() }
// Reload accounts list
scope.launch {
val accounts = accountManager.getAllAccounts()
@@ -456,16 +439,14 @@ class MainActivity : FragmentActivity() {
saveFcmToken(token)
addFcmLog("💾 Токен сохранен локально")
if (ProtocolManager.state.value == ProtocolState.AUTHENTICATED) {
addFcmLog("🔁 Протокол уже AUTHENTICATED, отправляем токен сразу")
sendFcmTokenToServer()
}
// Token will be sent by ProtocolManager.onAuthenticated()
// when protocol reaches AUTHENTICATED state
} else {
addFcmLog("⚠️ Токен пустой")
}
// Токен будет отправлен на сервер после успешной аутентификации
// (см. вызов sendFcmTokenToServer в onAccountLogin)
// Токен будет отправлен через ProtocolManager.subscribePushTokenIfAvailable()
// при достижении состояния AUTHENTICATED
}
} catch (e: Exception) {
addFcmLog("❌ Ошибка Firebase: ${e.message}")
@@ -479,52 +460,6 @@ class MainActivity : FragmentActivity() {
prefs.edit().putString("fcm_token", token).apply()
}
/**
* Отправить FCM токен на сервер Вызывается после успешной аутентификации, когда аккаунт уже
* расшифрован
*/
private fun sendFcmTokenToServer() {
lifecycleScope.launch {
try {
val prefs = getSharedPreferences("rosetta_prefs", MODE_PRIVATE)
val token = prefs.getString("fcm_token", null)
if (token == null) {
addFcmLog("⚠️ Нет сохраненного токена для отправки")
return@launch
}
val shortToken = "${token.take(12)}...${token.takeLast(8)}"
addFcmLog("📤 Подготовка к отправке токена на сервер")
addFcmLog("⏳ Ожидание аутентификации...")
// 🔥 КРИТИЧНО: Ждем пока протокол станет AUTHENTICATED
val authenticated = withTimeoutOrNull(5000) {
ProtocolManager.state.first { it == ProtocolState.AUTHENTICATED }
}
if (authenticated == null) {
addFcmLog("❌ Таймаут аутентификации (5000ms)")
return@launch
}
addFcmLog("✅ Аутентификация успешна")
addFcmLog("📨 Отправка токена: $shortToken")
val packet =
PacketPushNotification().apply {
this.notificationsToken = token
this.action = PushNotificationAction.SUBSCRIBE
}
ProtocolManager.send(packet)
addFcmLog("✅ Пакет отправлен на сервер (ID: 0x10)")
addFcmLog("🎉 FCM токен успешно зарегистрирован!")
} catch (e: Exception) {
addFcmLog("❌ Ошибка отправки: ${e.message}")
}
}
}
}
private fun buildInitials(displayName: String): String =