Ускорено подключение, исправлен кэш участников групп и обновлена версия до 1.1.5

This commit is contained in:
2026-03-09 20:20:27 +05:00
parent 41faa98130
commit c2e27cf543
9 changed files with 39 additions and 52 deletions

View File

@@ -23,8 +23,8 @@ val gitShortSha = safeGitOutput("rev-parse", "--short", "HEAD") ?: "unknown"
// ═══════════════════════════════════════════════════════════
// Rosetta versioning — bump here on each release
// ═══════════════════════════════════════════════════════════
val rosettaVersionName = "1.1.4"
val rosettaVersionCode = 16 // Increment on each release
val rosettaVersionName = "1.1.5"
val rosettaVersionCode = 17 // Increment on each release
android {
namespace = "com.rosetta.messenger"

View File

@@ -17,20 +17,17 @@ object ReleaseNotes {
val RELEASE_NOTICE = """
Update v$VERSION_PLACEHOLDER
Профиль и группы
- Фиксированные табы в профиле и группах
- Fast-scroll с отображением даты в медиа-галерее
- Поддержка Apple Emoji в аватарах и интерфейсе
- Восстановление ключей шифрования группы по инвайт-ссылке
Подключение
- Ускорен старт соединения и handshake при входе в аккаунт
- Логика reconnect синхронизирована с desktop-поведением
- Обновлён серверный endpoint на основной production (wss)
Аватары
- Улучшено отображение аватаров: поддержка текста с эмодзи
- Улучшена логика отображения в компоненте AvatarImage
Группы
- Добавлено предзагруженное кэширование участников группы
- Убран скачок "0 members" при повторном открытии группы
Исправления
- Исправлен переход по своему тэгу в группах
- Убрана лишняя подсветка в чатах
- Корректное отображение fast-scroll при изменении размера экрана
Интерфейс
- Исправлено вертикальное выравнивание verified-галочки в списке чатов
""".trimIndent()
fun getNotice(version: String): String =

View File

@@ -31,8 +31,8 @@ object ProtocolManager {
private const val DEBUG_LOG_FLUSH_DELAY_MS = 60L
private const val TYPING_INDICATOR_TIMEOUT_MS = 3_000L
// Server address - same as React Native version
private const val SERVER_ADDRESS = "ws://46.28.71.12:3000"
// Desktop parity: use the same primary WebSocket endpoint as desktop client.
private const val SERVER_ADDRESS = "wss://wss.rosetta.im"
private const val DEVICE_PREFS = "rosetta_protocol"
private const val DEVICE_ID_KEY = "device_id"
private const val DEVICE_ID_LENGTH = 128

View File

@@ -163,11 +163,8 @@ class AuthStateManager(
// Step 8: Connect and authenticate with protocol
ProtocolManager.connect()
// Give WebSocket time to connect before authenticating
kotlinx.coroutines.delay(500)
ProtocolManager.authenticate(keyPair.publicKey, privateKeyHash)
ProtocolManager.reconnectNowIfNeeded("auth_state_create")
Result.success(decryptedAccount)
} catch (e: Exception) {
@@ -210,11 +207,8 @@ class AuthStateManager(
// Connect and authenticate with protocol
ProtocolManager.connect()
// Give WebSocket time to connect before authenticating
kotlinx.coroutines.delay(500)
ProtocolManager.authenticate(decryptedAccount.publicKey, decryptedAccount.privateKeyHash)
ProtocolManager.reconnectNowIfNeeded("auth_state_unlock")
Result.success(decryptedAccount)
} catch (e: Exception) {

View File

@@ -2,20 +2,24 @@ package com.rosetta.messenger.ui.auth
import com.rosetta.messenger.network.ProtocolManager
import com.rosetta.messenger.network.ProtocolState
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.withTimeoutOrNull
internal fun startAuthHandshakeFast(publicKey: String, privateKeyHash: String) {
// Desktop parity: start connection+handshake immediately, without artificial waits.
ProtocolManager.connect()
ProtocolManager.authenticate(publicKey, privateKeyHash)
ProtocolManager.reconnectNowIfNeeded("auth_fast_start")
}
internal suspend fun awaitAuthHandshakeState(
publicKey: String,
privateKeyHash: String,
attempts: Int = 2,
timeoutMs: Long = 25_000L
): ProtocolState? {
repeat(attempts) {
ProtocolManager.disconnect()
delay(200)
ProtocolManager.authenticate(publicKey, privateKeyHash)
repeat(attempts) { attempt ->
startAuthHandshakeFast(publicKey, privateKeyHash)
val state = withTimeoutOrNull(timeoutMs) {
ProtocolManager.state.first {
@@ -26,6 +30,7 @@ internal suspend fun awaitAuthHandshakeState(
if (state != null) {
return state
}
ProtocolManager.reconnectNowIfNeeded("auth_handshake_retry_${attempt + 1}")
}
return null
}

View File

@@ -527,17 +527,10 @@ fun SetPasswordScreen(
keyPair.privateKey
)
val handshakeState =
awaitAuthHandshakeState(
keyPair.publicKey,
privateKeyHash
)
if (handshakeState == null) {
error =
"Failed to connect to server. Please try again."
isCreating = false
return@launch
}
startAuthHandshakeFast(
keyPair.publicKey,
privateKeyHash
)
accountManager.setCurrentAccount(keyPair.publicKey)

View File

@@ -116,12 +116,7 @@ val decryptedPrivateKey = CryptoManager.decryptWithPassword(
name = selectedAccount.name
)
val handshakeState = awaitAuthHandshakeState(account.publicKey, privateKeyHash)
if (handshakeState == null) {
onError("Failed to connect to server")
onUnlocking(false)
return
}
startAuthHandshakeFast(account.publicKey, privateKeyHash)
accountManager.setCurrentAccount(account.publicKey)
onSuccess(decryptedAccount)

View File

@@ -3891,7 +3891,7 @@ fun DialogItemContent(
VerifiedBadge(
verified = if (dialog.verified > 0) dialog.verified else 1,
size = 16,
modifier = Modifier.offset(y = (-1).dp)
modifier = Modifier.offset(y = (-2).dp)
)
}
// 🔒 Красная иконка замочка для заблокированных пользователей

View File

@@ -395,7 +395,7 @@ fun GroupInfoScreen(
var showEncryptionPage by rememberSaveable(dialogPublicKey) { mutableStateOf(false) }
var encryptionKey by rememberSaveable(dialogPublicKey) { mutableStateOf("") }
var encryptionKeyLoading by remember { mutableStateOf(false) }
var membersLoading by remember { mutableStateOf(false) }
var membersLoading by remember(dialogPublicKey) { mutableStateOf(false) }
var isMuted by remember { mutableStateOf(false) }
var showGroupAvatarPicker by rememberSaveable(dialogPublicKey) { mutableStateOf(false) }
var showGroupAvatarViewer by rememberSaveable(dialogPublicKey) { mutableStateOf(false) }
@@ -414,18 +414,21 @@ fun GroupInfoScreen(
val initialDiskMembersCache = remember(membersCacheKey) {
GroupMembersDiskCache.getAny(context, membersCacheKey)
}
val hasInitialMembersCache = remember(initialMemoryMembersCache, initialDiskMembersCache) {
initialMemoryMembersCache != null || initialDiskMembersCache != null
}
val initialMembers = remember(initialMemoryMembersCache, initialDiskMembersCache) {
initialMemoryMembersCache?.members ?: initialDiskMembersCache?.members.orEmpty()
}
var members by remember(dialogPublicKey) { mutableStateOf(initialMembers) }
var members by remember(dialogPublicKey, membersCacheKey) { mutableStateOf(initialMembers) }
val memberInfoByKey =
remember(dialogPublicKey) {
remember(dialogPublicKey, membersCacheKey) {
mutableStateMapOf<String, SearchUser>().apply {
initialMemoryMembersCache?.memberInfoByKey?.let { putAll(it) }
}
}
// Real online status from PacketOnlineState (0x05), NOT from SearchUser.online
val memberOnlineStatus = remember(dialogPublicKey) { mutableStateMapOf<String, Boolean>() }
val memberOnlineStatus = remember(dialogPublicKey, membersCacheKey) { mutableStateMapOf<String, Boolean>() }
val groupEntity by produceState<com.rosetta.messenger.database.GroupEntity?>(
initialValue = null,
@@ -1089,7 +1092,7 @@ fun GroupInfoScreen(
)
Text(
text = if (membersLoading) {
text = if (membersLoading || (members.isEmpty() && !hasInitialMembersCache)) {
"Loading members..."
} else {
"${members.size} members, $onlineCount online"