From dcfbb020beb19b9447a119cc39585553ab982831 Mon Sep 17 00:00:00 2001 From: k1ngsterr1 Date: Wed, 21 Jan 2026 03:09:39 +0500 Subject: [PATCH] feat: Update ProfileNavigationItem to use a rounded corner shape with increased radius --- .../com/rosetta/messenger/MainActivity.kt | 24 +++++++- .../rosetta/messenger/data/AccountManager.kt | 26 +++++++- .../database/EncryptedAccountEntity.kt | 5 +- .../messenger/database/RosettaDatabase.kt | 11 +++- .../com/rosetta/messenger/network/Packets.kt | 17 +++--- .../messenger/ui/settings/ProfileScreen.kt | 4 +- .../messenger/ui/settings/ProfileViewModel.kt | 60 ++++++++++++------- 7 files changed, 105 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/com/rosetta/messenger/MainActivity.kt b/app/src/main/java/com/rosetta/messenger/MainActivity.kt index 43f8b9b..e8a990d 100644 --- a/app/src/main/java/com/rosetta/messenger/MainActivity.kt +++ b/app/src/main/java/com/rosetta/messenger/MainActivity.kt @@ -19,6 +19,7 @@ import androidx.compose.material3.Surface import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext import androidx.core.content.ContextCompat import androidx.lifecycle.lifecycleScope import com.google.firebase.FirebaseApp @@ -436,6 +437,21 @@ fun MainScreen( val accountPublicKey = account?.publicKey ?: "04c266b98ae5" val accountPrivateKey = account?.privateKey ?: "" val privateKeyHash = account?.privateKeyHash ?: "" + + // Username state - загружается из EncryptedAccount + var accountUsername by remember { mutableStateOf("") } + var reloadTrigger by remember { mutableIntStateOf(0) } + + // Load username from AccountManager + val context = LocalContext.current + LaunchedEffect(accountPublicKey, reloadTrigger) { + if (accountPublicKey.isNotBlank() && accountPublicKey != "04c266b98ae5") { + val accountManager = AccountManager(context) + val encryptedAccount = accountManager.getAccount(accountPublicKey) + accountUsername = encryptedAccount?.username ?: "" + Log.d("MainActivity", "Loaded username from DB: $accountUsername") + } + } // Состояние протокола для передачи в SearchScreen val protocolState by ProtocolManager.state.collectAsState() @@ -660,13 +676,15 @@ fun MainScreen( ProfileScreen( isDarkTheme = isDarkTheme, accountName = accountName, - accountUsername = "", // TODO: Get from account + accountUsername = accountUsername, accountPublicKey = accountPublicKey, accountPrivateKeyHash = privateKeyHash, onBack = { showProfileScreen = false }, onSaveProfile = { name, username -> - // Profile saved via ViewModel - Log.d("MainActivity", "Profile saved: name=$name, username=$username") + // Update username state and trigger reload from DB + accountUsername = username + reloadTrigger++ + Log.d("MainActivity", "Profile saved: name=$name, username=$username, reloading...") }, onLogout = onLogout, onNavigateToTheme = { diff --git a/app/src/main/java/com/rosetta/messenger/data/AccountManager.kt b/app/src/main/java/com/rosetta/messenger/data/AccountManager.kt index b6bfc63..53a08ec 100644 --- a/app/src/main/java/com/rosetta/messenger/data/AccountManager.kt +++ b/app/src/main/java/com/rosetta/messenger/data/AccountManager.kt @@ -100,13 +100,31 @@ class AccountManager(private val context: Context) { } } + /** + * Update account username + */ + suspend fun updateAccountUsername(publicKey: String, username: String) { + val account = getAccount(publicKey) ?: return + val updatedAccount = account.copy(username = username) + saveAccount(updatedAccount) + } + + /** + * Update account name + */ + suspend fun updateAccountName(publicKey: String, name: String) { + val account = getAccount(publicKey) ?: return + val updatedAccount = account.copy(name = name) + saveAccount(updatedAccount) + } + suspend fun clearAll() { context.accountDataStore.edit { it.clear() } } private fun serializeAccounts(accounts: List): String { return accounts.joinToString("|||") { account -> - "${account.publicKey}::${account.encryptedPrivateKey}::${account.encryptedSeedPhrase}::${account.name}" + "${account.publicKey}::${account.encryptedPrivateKey}::${account.encryptedSeedPhrase}::${account.name}::${account.username ?: ""}" } } @@ -120,7 +138,8 @@ class AccountManager(private val context: Context) { publicKey = parts[0], encryptedPrivateKey = parts[1], encryptedSeedPhrase = parts[2], - name = parts[3] + name = parts[3], + username = parts.getOrNull(4)?.takeIf { it.isNotBlank() } ) } else null } @@ -131,7 +150,8 @@ data class EncryptedAccount( val publicKey: String, val encryptedPrivateKey: String, val encryptedSeedPhrase: String, - val name: String = "Account" + val name: String = "Account", + val username: String? = null ) data class DecryptedAccount( diff --git a/app/src/main/java/com/rosetta/messenger/database/EncryptedAccountEntity.kt b/app/src/main/java/com/rosetta/messenger/database/EncryptedAccountEntity.kt index 3e30745..51c70b6 100644 --- a/app/src/main/java/com/rosetta/messenger/database/EncryptedAccountEntity.kt +++ b/app/src/main/java/com/rosetta/messenger/database/EncryptedAccountEntity.kt @@ -34,5 +34,8 @@ data class EncryptedAccountEntity( val lastUsed: String? = null, @ColumnInfo(name = "is_active") - val isActive: Boolean = true + val isActive: Boolean = true, + + @ColumnInfo(name = "username") + val username: String? = null ) diff --git a/app/src/main/java/com/rosetta/messenger/database/RosettaDatabase.kt b/app/src/main/java/com/rosetta/messenger/database/RosettaDatabase.kt index 8d29f41..1e77509 100644 --- a/app/src/main/java/com/rosetta/messenger/database/RosettaDatabase.kt +++ b/app/src/main/java/com/rosetta/messenger/database/RosettaDatabase.kt @@ -14,7 +14,7 @@ import androidx.sqlite.db.SupportSQLiteDatabase DialogEntity::class, BlacklistEntity::class ], - version = 5, + version = 6, exportSchema = false ) abstract class RosettaDatabase : RoomDatabase() { @@ -35,6 +35,13 @@ abstract class RosettaDatabase : RoomDatabase() { database.execSQL("ALTER TABLE dialogs ADD COLUMN last_message_read INTEGER NOT NULL DEFAULT 0") } } + + private val MIGRATION_5_6 = object : Migration(5, 6) { + override fun migrate(database: SupportSQLiteDatabase) { + // Добавляем поле username в encrypted_accounts + database.execSQL("ALTER TABLE encrypted_accounts ADD COLUMN username TEXT") + } + } fun getDatabase(context: Context): RosettaDatabase { return INSTANCE ?: synchronized(this) { @@ -44,7 +51,7 @@ abstract class RosettaDatabase : RoomDatabase() { "rosetta_secure.db" ) .setJournalMode(JournalMode.WRITE_AHEAD_LOGGING) // WAL mode for performance - .addMigrations(MIGRATION_4_5) + .addMigrations(MIGRATION_4_5, MIGRATION_5_6) .fallbackToDestructiveMigration() // Для разработки - только если миграция не найдена .build() INSTANCE = instance diff --git a/app/src/main/java/com/rosetta/messenger/network/Packets.kt b/app/src/main/java/com/rosetta/messenger/network/Packets.kt index aedb03e..519fb18 100644 --- a/app/src/main/java/com/rosetta/messenger/network/Packets.kt +++ b/app/src/main/java/com/rosetta/messenger/network/Packets.kt @@ -113,30 +113,27 @@ data class SearchUser( /** * User Info packet (ID: 0x01) * Get/Set user information + * Protocol order: username, title, privateKey (matching TypeScript version) */ class PacketUserInfo : Packet() { - var publicKey: String = "" - var title: String = "" + var privateKey: String = "" var username: String = "" - var verified: Int = 0 - var online: Int = 0 + var title: String = "" override fun getPacketId(): Int = 0x01 override fun receive(stream: Stream) { - publicKey = stream.readString() - title = stream.readString() username = stream.readString() - verified = stream.readInt8() - online = stream.readInt8() + title = stream.readString() + privateKey = stream.readString() } override fun send(): Stream { val stream = Stream() stream.writeInt16(getPacketId()) - stream.writeString(publicKey) - stream.writeString(title) stream.writeString(username) + stream.writeString(title) + stream.writeString(privateKey) return stream } } diff --git a/app/src/main/java/com/rosetta/messenger/ui/settings/ProfileScreen.kt b/app/src/main/java/com/rosetta/messenger/ui/settings/ProfileScreen.kt index 43f4e5b..5b4c688 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/settings/ProfileScreen.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/settings/ProfileScreen.kt @@ -412,7 +412,7 @@ fun ProfileScreen( name = editedName, username = editedUsername ) - viewModel.updateLocalAccountName(accountPublicKey, editedName) + viewModel.updateLocalProfile(accountPublicKey, editedName, editedUsername) }, isDarkTheme = isDarkTheme ) @@ -903,7 +903,7 @@ fun ProfileNavigationItem( Box( modifier = Modifier .size(36.dp) - .clip(RoundedCornerShape(8.dp)) + .clip(RoundedCornerShape(16.dp)) .background(iconBackground), contentAlignment = Alignment.Center ) { diff --git a/app/src/main/java/com/rosetta/messenger/ui/settings/ProfileViewModel.kt b/app/src/main/java/com/rosetta/messenger/ui/settings/ProfileViewModel.kt index 9471222..f5398a1 100644 --- a/app/src/main/java/com/rosetta/messenger/ui/settings/ProfileViewModel.kt +++ b/app/src/main/java/com/rosetta/messenger/ui/settings/ProfileViewModel.kt @@ -13,6 +13,7 @@ import com.rosetta.messenger.network.ProtocolManager import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch +import kotlinx.coroutines.delay data class ProfileState( val isLoading: Boolean = false, @@ -72,18 +73,47 @@ class ProfileViewModel(application: Application) : AndroidViewModel(application) _state.value = _state.value.copy(isSaving = true, error = null, saveSuccess = false) - // Create and send PacketUserInfo + // Create and send PacketUserInfo (protocol order: username, title, privateKey) val packet = PacketUserInfo() - packet.publicKey = publicKey - packet.title = name packet.username = username + packet.title = name + packet.privateKey = privateKeyHash addLog("Packet created: PacketUserInfo") addLog("Packet ID: 0x${packet.getPacketId().toString(16).uppercase()}") + addLog("Packet data:") + addLog(" - username: '$username'") + addLog(" - title: '$name'") + addLog(" - privateKey: ${privateKeyHash.take(16)}...") addLog("Sending packet to server...") ProtocolManager.send(packet) addLog("Packet sent successfully") + addLog("Waiting for PacketResult (0x02) from server...") + + // 🔥 ВРЕМЕННОЕ РЕШЕНИЕ: Сохраняем локально сразу (как в React Native) + // Это нужно потому что сервер может не ответить, если пользователь не создан в БД + addLog("💾 Saving to local database (temporary workaround)...") + updateLocalProfile(publicKey, name, username) + addLog("✅ Local database updated") + + // Set timeout for server response + viewModelScope.launch { + delay(5000) // 5 seconds timeout + if (_state.value.isSaving) { + addLog("⚠️ WARNING: No response from server after 5 seconds") + addLog("This might indicate:") + addLog(" 1. Server did not accept the packet") + addLog(" 2. Server did not send PacketResult back") + addLog(" 3. Network issue") + addLog("📝 NOTE: Profile saved locally, but NOT confirmed by server") + _state.value = _state.value.copy( + isSaving = false, + saveSuccess = true, // Считаем успехом т.к. локально сохранено + error = null + ) + } + } // Wait for response (handled in handlePacketResult) @@ -115,10 +145,6 @@ class ProfileViewModel(application: Application) : AndroidViewModel(application) error = null ) addLog("State updated: saveSuccess=true") - - // Update local account name if needed - // (username is stored on server only) - } else -> { // ERROR addLog("❌ ERROR: Profile save failed") @@ -135,24 +161,16 @@ class ProfileViewModel(application: Application) : AndroidViewModel(application) } /** - * Update local account name + * Update local account name and username */ - fun updateLocalAccountName(publicKey: String, name: String) { + fun updateLocalProfile(publicKey: String, name: String, username: String) { viewModelScope.launch { try { - val account = accountManager.getAccount(publicKey) - if (account != null) { - val updatedAccount = EncryptedAccount( - publicKey = account.publicKey, - encryptedPrivateKey = account.encryptedPrivateKey, - encryptedSeedPhrase = account.encryptedSeedPhrase, - name = name - ) - accountManager.saveAccount(updatedAccount) - Log.d("ProfileViewModel", "Local account name updated") - } + accountManager.updateAccountName(publicKey, name) + accountManager.updateAccountUsername(publicKey, username) + Log.d("ProfileViewModel", "Local profile updated: name=$name, username=$username") } catch (e: Exception) { - Log.e("ProfileViewModel", "Error updating local account name", e) + Log.e("ProfileViewModel", "Error updating local profile", e) } } }