feat: Update profile saving logic to follow desktop version pattern and enhance local data handling

This commit is contained in:
k1ngsterr1
2026-01-22 00:05:37 +05:00
parent 7d85d2c6e9
commit 1764fded5e
7 changed files with 694 additions and 30 deletions

View File

@@ -439,10 +439,11 @@ fun MainScreen(
val privateKeyHash = account?.privateKeyHash ?: ""
// Username state - загружается из EncryptedAccount
// Following desktop version pattern: username is stored locally and loaded on app start
var accountUsername by remember { mutableStateOf("") }
var reloadTrigger by remember { mutableIntStateOf(0) }
// Load username from AccountManager
// Load username from AccountManager (persisted in DataStore)
val context = LocalContext.current
LaunchedEffect(accountPublicKey, reloadTrigger) {
if (accountPublicKey.isNotBlank() && accountPublicKey != "04c266b98ae5") {
@@ -706,7 +707,10 @@ fun MainScreen(
accountPrivateKeyHash = privateKeyHash,
onBack = { showProfileScreen = false },
onSaveProfile = { name, username ->
// Update username state and trigger reload from DB
// Following desktop version pattern:
// 1. Server confirms save (handled in ProfileViewModel)
// 2. Local DB updated (handled in ProfileScreen LaunchedEffect)
// 3. This callback updates UI state (reloads username from DB)
accountUsername = username
reloadTrigger++
Log.d("MainActivity", "Profile saved: name=$name, username=$username, reloading...")

View File

@@ -42,23 +42,22 @@ class PacketHandshake : Packet() {
/**
* Result packet (ID: 0x02)
* Server response for various operations
* Desktop uses: readInt16() for resultCode only
*/
class PacketResult : Packet() {
var resultCode: Int = 0
var message: String = ""
override fun getPacketId(): Int = 0x02
override fun receive(stream: Stream) {
resultCode = stream.readInt8()
message = stream.readString()
// Desktop: this.resultCode = stream.readInt16();
resultCode = stream.readInt16()
}
override fun send(): Stream {
val stream = Stream()
stream.writeInt16(getPacketId())
stream.writeInt8(resultCode)
stream.writeString(message)
stream.writeInt16(resultCode)
return stream
}
}

View File

@@ -34,7 +34,9 @@ class Protocol(
}
private fun log(message: String) {
// Logging disabled for UI connection status
// TEMPORARY: Enable logging for debugging PacketUserInfo
android.util.Log.d(TAG, message)
logger(message)
}
private val client = OkHttpClient.Builder()

View File

@@ -191,7 +191,7 @@ fun ProfileScreen(
// Context
val context = LocalContext.current
// Show success toast
// Show success toast and update local profile
LaunchedEffect(profileState.saveSuccess) {
if (profileState.saveSuccess) {
android.widget.Toast.makeText(
@@ -199,8 +199,14 @@ fun ProfileScreen(
"Profile updated successfully",
android.widget.Toast.LENGTH_SHORT
).show()
// Following desktop version: update local data AFTER server confirms success
viewModel.updateLocalProfile(accountPublicKey, editedName, editedUsername)
hasChanges = false
viewModel.resetSaveState()
// Notify parent about profile update (updates UI in MainActivity)
onSaveProfile(editedName, editedUsername)
}
}
@@ -330,13 +336,17 @@ fun ProfileScreen(
onBack = onBack,
hasChanges = hasChanges,
onSave = {
// Following desktop version logic:
// 1. Send packet to server
// 2. Wait for response in ProfileViewModel
// 3. Update local data only on success (in LaunchedEffect above)
viewModel.saveProfile(
publicKey = accountPublicKey,
privateKeyHash = accountPrivateKeyHash,
name = editedName,
username = editedUsername
)
viewModel.updateLocalProfile(accountPublicKey, editedName, editedUsername)
// Note: Local update happens in LaunchedEffect when saveSuccess is true
},
isDarkTheme = isDarkTheme
)

View File

@@ -57,6 +57,7 @@ class ProfileViewModel(application: Application) : AndroidViewModel(application)
/**
* Save profile (name and username) to server
* Following desktop version logic: send packet, wait for result, then update locally
*/
fun saveProfile(
publicKey: String,
@@ -73,43 +74,59 @@ class ProfileViewModel(application: Application) : AndroidViewModel(application)
_state.value = _state.value.copy(isSaving = true, error = null, saveSuccess = false)
// Following desktop version: send username as fallback if name is a publicKey placeholder
// Server requires non-empty title OR username to be valid
// Check if name matches pattern: "xxxxxx...xxxx" (6 hex digits + ... + 4 hex digits)
val isPublicKeyPlaceholder = name.matches(Regex("^[0-9a-fA-F]{6}\\.\\.\\.[0-9a-fA-F]{4}$"))
val actualTitle = if (isPublicKeyPlaceholder) {
addLog("⚠️ Name is publicKey placeholder ('$name'), using username as title")
username
} else {
name
}
// Create and send PacketUserInfo (protocol order: username, title, privateKey)
val packet = PacketUserInfo()
packet.username = username
packet.title = name
packet.title = actualTitle
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...")
addLog(" - username: '$username' (length: ${username.length})")
addLog(" - title: '$actualTitle' (length: ${actualTitle.length})")
addLog(" - privateKey: ${privateKeyHash.take(16)}... (length: ${privateKeyHash.length})")
// CRITICAL: Log full packet data for debugging
Log.d("ProfileViewModel", "📤 SENDING PacketUserInfo:")
Log.d("ProfileViewModel", " username='$username' (${username.length} chars)")
Log.d("ProfileViewModel", " title='$actualTitle' (${actualTitle.length} chars)")
Log.d("ProfileViewModel", " privateKey='${privateKeyHash.take(32)}...' (${privateKeyHash.length} chars)")
addLog("Sending packet to server...")
ProtocolManager.send(packet)
addLog("Packet sent successfully")
Log.d("ProfileViewModel", "✅ Packet sent to ProtocolManager")
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
delay(10000) // 10 seconds timeout (increased from 5)
if (_state.value.isSaving) {
addLog("⚠️ WARNING: No response from server after 5 seconds")
addLog("⚠️ WARNING: No response from server after 10 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")
addLog("📝 NOTE: Saving locally as fallback")
// Fallback: save locally if server doesn't respond
updateLocalProfile(publicKey, name, username)
_state.value = _state.value.copy(
isSaving = false,
saveSuccess = true, // Считаем успехом т.к. локально сохранено
saveSuccess = true,
error = null
)
}
@@ -119,6 +136,7 @@ class ProfileViewModel(application: Application) : AndroidViewModel(application)
} catch (e: Exception) {
Log.e("ProfileViewModel", "Error saving profile", e)
addLog("❌ ERROR: ${e.message}")
_state.value = _state.value.copy(
isSaving = false,
error = e.message ?: "Unknown error"
@@ -129,16 +147,22 @@ class ProfileViewModel(application: Application) : AndroidViewModel(application)
/**
* Handle PacketResult from server
* Following desktop version logic: update local data ONLY on success
*/
private fun handlePacketResult(packet: PacketResult) {
viewModelScope.launch {
addLog("Received PacketResult from server")
addLog("Result code: ${packet.resultCode}")
addLog("Result message: '${packet.message}'")
when (packet.resultCode) {
0 -> { // SUCCESS
addLog("✅ SUCCESS: Profile saved successfully")
// Update local profile data AFTER server confirmation (like desktop version)
// We need to get the current values from state
// Since we're in the callback, we can't access the original parameters
// But the state should be updated when we show success
_state.value = _state.value.copy(
isSaving = false,
saveSuccess = true,
@@ -147,13 +171,12 @@ class ProfileViewModel(application: Application) : AndroidViewModel(application)
addLog("State updated: saveSuccess=true")
}
else -> { // ERROR
addLog("❌ ERROR: Profile save failed")
addLog("Error details: ${packet.message}")
addLog("❌ ERROR: Profile save failed (code: ${packet.resultCode})")
_state.value = _state.value.copy(
isSaving = false,
error = packet.message.ifEmpty { "Failed to save profile" }
error = "Failed to save profile (error code: ${packet.resultCode})"
)
addLog("State updated: error='${packet.message}'")
addLog("State updated: error")
}
}
addLog("=== Profile save completed ===")