# Profile Username & Name Logic

## Overview

This document describes how username and name are set and synchronized between the Android app and the server, following the desktop version implementation.

## Architecture

### Desktop Version (TypeScript - Reference Implementation)

Located in: `rosette-messenger-app/Архив/app/views/Profile/MyProfile.tsx`

**Flow:**

1. User edits `title` (name) and `username` in UI
2. On save, creates `PacketUserInfo` with:
   - `username` - user's username (e.g., "john123")
   - `title` - user's display name (e.g., "John Doe")
   - `privateKey` - user's private key hash
3. Sends packet to server
4. Waits for `PacketResult` (0x02)
5. **Only on SUCCESS** (resultCode = 0):
   - Updates local state via `updateUserInformation()`
   - Shows success message

**Key Protocol Details:**

- Packet ID: 0x01 (PacketUserInfo)
- Send order: username → title → privateKey
- Receive order: username → title → privateKey

---

### Android Version (Kotlin - Current Implementation)

#### 1. **Data Storage Layer** (`AccountManager.kt`)

```kotlin
data class EncryptedAccount(
    val publicKey: String,
    val encryptedPrivateKey: String,
    val encryptedSeedPhrase: String,
    val name: String,           // User's display name (title)
    val username: String?       // User's username
)
```

**Methods:**

- `updateAccountName(publicKey, name)` - Updates display name locally
- `updateAccountUsername(publicKey, username)` - Updates username locally
- `getAccount(publicKey)` - Retrieves account data

---

#### 2. **Network Layer** (`Packets.kt`)

```kotlin
class PacketUserInfo : Packet() {
    var privateKey: String = ""
    var username: String = ""
    var title: String = ""

    override fun getPacketId(): Int = 0x01

    override fun send(): Stream {
        stream.writeString(username)   // Order matches desktop
        stream.writeString(title)
        stream.writeString(privateKey)
    }
}
```

---

#### 3. **ViewModel Layer** (`ProfileViewModel.kt`)

**State Management:**

```kotlin
data class ProfileState(
    val isLoading: Boolean = false,
    val isSaving: Boolean = false,
    val error: String? = null,
    val saveSuccess: Boolean = false,
    val logs: List<String> = emptyList()
)
```

**Save Profile Flow:**

```kotlin
fun saveProfile(publicKey, privateKeyHash, name, username) {
    // 1. Create PacketUserInfo
    val packet = PacketUserInfo()
    packet.username = username
    packet.title = name
    packet.privateKey = privateKeyHash

    // 2. Send to server
    ProtocolManager.send(packet)

    // 3. Set 10s timeout (fallback: save locally if no response)

    // 4. Wait for PacketResult (handled in handlePacketResult)
}
```

**Handle Server Response:**

```kotlin
private fun handlePacketResult(packet: PacketResult) {
    when (packet.resultCode) {
        0 -> { // SUCCESS
            // Mark as successful
            // Local update happens in ProfileScreen LaunchedEffect
            _state.value = _state.value.copy(saveSuccess = true)
        }
        else -> { // ERROR
            _state.value = _state.value.copy(error = packet.message)
        }
    }
}
```

**Fallback Logic:**

- If server doesn't respond within 10 seconds:
  - Profile is saved locally anyway
  - User sees success (but warned in logs)
  - This prevents data loss if server is down

---

#### 4. **UI Layer** (`ProfileScreen.kt`)

**State Tracking:**

```kotlin
var editedName by remember { mutableStateOf(accountName) }
var editedUsername by remember { mutableStateOf(accountUsername) }
var hasChanges by remember { mutableStateOf(false) }
```

**Save Flow:**

```kotlin
onSave = {
    // 1. Send to server (via ViewModel)
    viewModel.saveProfile(
        publicKey = accountPublicKey,
        privateKeyHash = accountPrivateKeyHash,
        name = editedName,
        username = editedUsername
    )
    // Note: Local update happens AFTER success (see below)
}
```

**Success Handler:**

```kotlin
LaunchedEffect(profileState.saveSuccess) {
    if (profileState.saveSuccess) {
        // 1. Show success toast
        Toast.makeText(context, "Profile updated successfully", LENGTH_SHORT).show()

        // 2. Update local database (AFTER server confirms)
        viewModel.updateLocalProfile(accountPublicKey, editedName, editedUsername)

        // 3. Reset UI state
        hasChanges = false
        viewModel.resetSaveState()

        // 4. Notify parent (MainActivity) to reload
        onSaveProfile(editedName, editedUsername)
    }
}
```

---

#### 5. **Parent Layer** (`MainActivity.kt`)

**Username Loading:**

```kotlin
var accountUsername by remember { mutableStateOf("") }
var reloadTrigger by remember { mutableIntStateOf(0) }

// Load username from DataStore on app start or profile change
LaunchedEffect(accountPublicKey, reloadTrigger) {
    val accountManager = AccountManager(context)
    val encryptedAccount = accountManager.getAccount(accountPublicKey)
    accountUsername = encryptedAccount?.username ?: ""
}
```

**Profile Update Callback:**

```kotlin
onSaveProfile = { name, username ->
    // 1. Update local state
    accountUsername = username

    // 2. Trigger reload from DB (to ensure sync)
    reloadTrigger++
}
```

---

## Complete Flow Diagram

```
┌─────────────┐
│   USER      │
│ Edit fields │
└──────┬──────┘
       │
       ↓
┌──────────────────────────────────────────┐
│ ProfileScreen.kt                         │
│ - editedName: "John Doe"                │
│ - editedUsername: "john123"             │
│ - hasChanges: true                      │
└──────┬───────────────────────────────────┘
       │ [User clicks Save]
       ↓
┌──────────────────────────────────────────┐
│ ProfileViewModel.saveProfile()          │
│ 1. Create PacketUserInfo                │
│    - username: "john123"                │
│    - title: "John Doe"                  │
│    - privateKey: "abc123..."            │
│ 2. ProtocolManager.send(packet)         │
│ 3. Start 10s timeout                    │
└──────┬───────────────────────────────────┘
       │
       ↓
┌──────────────────────────────────────────┐
│ SERVER (via WebSocket)                  │
│ - Receives 0x01 PacketUserInfo          │
│ - Validates & saves to database         │
│ - Sends 0x02 PacketResult               │
│   - resultCode: 0 (SUCCESS)             │
│   - message: ""                         │
└──────┬───────────────────────────────────┘
       │
       ↓
┌──────────────────────────────────────────┐
│ ProfileViewModel.handlePacketResult()   │
│ - resultCode == 0 → SUCCESS             │
│ - _state.saveSuccess = true             │
└──────┬───────────────────────────────────┘
       │
       ↓
┌──────────────────────────────────────────┐
│ ProfileScreen LaunchedEffect            │
│ - Observes profileState.saveSuccess     │
│ - Calls updateLocalProfile()            │
│   → AccountManager.updateAccountName()  │
│   → AccountManager.updateAccountUsername()│
│ - Shows success Toast                   │
│ - Calls onSaveProfile() callback        │
└──────┬───────────────────────────────────┘
       │
       ↓
┌──────────────────────────────────────────┐
│ MainActivity                            │
│ - accountUsername = "john123"           │
│ - reloadTrigger++                       │
│ - LaunchedEffect re-runs                │
│ - Loads fresh data from DataStore       │
└──────────────────────────────────────────┘
```

---

## Error Handling

### Server Timeout (10 seconds)

```kotlin
if (no response after 10s) {
    // Fallback: save locally anyway
    updateLocalProfile(publicKey, name, username)
    _state.value = saveSuccess = true

    // Logs show warning:
    // "⚠️ No response from server after 10 seconds"
    // "📝 NOTE: Saving locally as fallback"
}
```

### Server Error Response

```kotlin
when (packet.resultCode) {
    else -> {
        _state.value = _state.value.copy(
            isSaving = false,
            error = packet.message
        )
        // Shows error Toast in ProfileScreen
    }
}
```

---

## Key Differences from Desktop

| Aspect         | Desktop (TypeScript)        | Android (Kotlin)                    |
| -------------- | --------------------------- | ----------------------------------- |
| Storage        | In-memory state             | DataStore (persistent)              |
| Update trigger | On PacketResult SUCCESS     | On PacketResult SUCCESS             |
| Fallback       | None (fails if server down) | Saves locally after 10s             |
| UI feedback    | State update → re-render    | StateFlow → Toast + callback        |
| Logging        | Console.log                 | Android Log + ProfileViewModel logs |

---

## Testing Checklist

- [ ] Name updates and persists across app restarts
- [ ] Username updates and persists across app restarts
- [ ] Changes show in ChatsListScreen header
- [ ] Success toast appears on save
- [ ] Error toast appears on server error
- [ ] Fallback works if server is down (10s timeout)
- [ ] Logs show correct packet flow
- [ ] hasChanges flag updates correctly
- [ ] Save button appears/disappears correctly

---

## Related Files

### Android

- `ProfileScreen.kt` - UI layer
- `ProfileViewModel.kt` - Business logic
- `MainActivity.kt` - Parent container
- `AccountManager.kt` - Data persistence
- `Packets.kt` - Protocol definitions

### Desktop (Reference)

- `MyProfile.tsx` - Desktop implementation
- `packet.userinfo.ts` - Protocol definition
- `useUserInformation.ts` - State management

---

## Protocol Specification

### PacketUserInfo (0x01)

**Client → Server (Send):**

```
┌──────────┬──────────┬───────┬────────────┐
│ Packet ID│ Username │ Title │ PrivateKey │
│  (0x01)  │ (string) │(string)│  (string)  │
└──────────┴──────────┴───────┴────────────┘
```

**Server → Client (Receive):**
Same format (not typically used - server sends PacketResult instead)

### PacketResult (0x02)

**Server → Client:**

```
┌──────────┬────────────┬─────────┐
│ Packet ID│ Result Code│ Message │
│  (0x02)  │   (int8)   │(string) │
└──────────┴────────────┴─────────┘
```

**Result Codes:**

- `0` - SUCCESS
- Other values indicate errors (message field contains details)

---

## Conclusion

The Android implementation now follows the desktop version pattern:

1. ✅ Send packet to server first
2. ✅ Wait for server confirmation
3. ✅ Update local data ONLY after success
4. ✅ Proper error handling
5. ✅ Fallback for offline/timeout scenarios

This ensures data consistency between client and server while providing a good user experience even when the server is temporarily unavailable.
