Complete Android client for encrypted chat platform. 78+ Kotlin files: crypto (X3DH, Double Ratchet, AES-GCM, Ed25519, X25519, RSA-PSS), network (TCP/TLS, 50 endpoints), Hilt DI, Room+SQLCipher DB, Jetpack Compose UI with Catppuccin Mocha theme. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
161 lines
5.6 KiB
Markdown
161 lines
5.6 KiB
Markdown
# Agent D: Auth Screens
|
|
|
|
## Phase: 2 (UI Shells)
|
|
## Depends on: Agent A (project), Agent B (theme + navigation)
|
|
|
|
## Context
|
|
You are building authentication screens for "Kecalek" encrypted chat app.
|
|
The login uses RSA challenge-response (no password sent to server).
|
|
Registration requires email verification code.
|
|
Device pairing allows linking a new device to an existing account.
|
|
|
|
## Task
|
|
Create Login, Register, Pairing screens and AuthViewModel skeleton.
|
|
|
|
## Files to Create
|
|
|
|
### 1. ui/auth/LoginScreen.kt
|
|
Jetpack Compose screen with:
|
|
- **App title** "Kecalek" at the top (headlineLarge)
|
|
- **Subtitle** "Encrypted Messaging" (bodyMedium, Subtext1 color)
|
|
- **Email/Username** text field (OutlinedTextField, Surface1 background)
|
|
- **Password** text field (password visibility toggle icon)
|
|
- **Login button** (filled, Lavender primary, full width)
|
|
- **"Create Account"** text button below (navigates to Register)
|
|
- **"Link Device"** text button below (navigates to Pairing)
|
|
- **Server config section** (expandable/collapsible):
|
|
- Host text field (default: "chat.ai-tech.news")
|
|
- Port text field (default: "9999")
|
|
- TLS toggle switch (default: off)
|
|
- **Loading state**: CircularProgressIndicator replacing login button
|
|
- **Error state**: Red error text below password field
|
|
- **Biometric login button** (fingerprint icon, shown only if biometric available)
|
|
|
|
**Layout**: Centered vertically, max width 400dp, padding 24dp.
|
|
|
|
### 2. ui/auth/RegisterScreen.kt
|
|
Jetpack Compose screen with:
|
|
- **Back arrow** in top bar (navigates back)
|
|
- **Title** "Create Account"
|
|
- **Username** text field
|
|
- **Email** text field
|
|
- **Password** text field (with visibility toggle)
|
|
- **Confirm Password** text field
|
|
- **Register button** (filled, Lavender, full width)
|
|
- **Confirmation code section** (shown after successful registration):
|
|
- Info text "Check your email for a verification code"
|
|
- 6-digit code input field
|
|
- Confirm button
|
|
- **Loading state** + **Error state** (same pattern as Login)
|
|
|
|
### 3. ui/auth/PairingScreen.kt
|
|
Jetpack Compose screen with:
|
|
- **Back arrow** in top bar
|
|
- **Title** "Link New Device"
|
|
- **Info text** explaining pairing process
|
|
- **8-digit pairing code** displayed prominently (headlineLarge, monospace, letter-spacing)
|
|
- **Countdown timer** or progress indicator showing poll status
|
|
- **"Waiting for authorization..."** text with animated dots
|
|
- **Cancel button** (outlined)
|
|
- **Status messages**: "Device authorized", "Pairing failed", etc.
|
|
|
|
### 4. ui/auth/AuthViewModel.kt
|
|
```kotlin
|
|
package com.kecalek.chat.ui.auth
|
|
|
|
import androidx.lifecycle.ViewModel
|
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
|
import kotlinx.coroutines.flow.MutableStateFlow
|
|
import kotlinx.coroutines.flow.StateFlow
|
|
import kotlinx.coroutines.flow.asStateFlow
|
|
import javax.inject.Inject
|
|
|
|
data class AuthUiState(
|
|
val isLoading: Boolean = false,
|
|
val error: String? = null,
|
|
val isLoggedIn: Boolean = false,
|
|
val isRegistered: Boolean = false,
|
|
val needsConfirmation: Boolean = false,
|
|
val pairingCode: String? = null,
|
|
val isPairingWaiting: Boolean = false,
|
|
val serverHost: String = "chat.ai-tech.news",
|
|
val serverPort: Int = 9999,
|
|
val useTls: Boolean = false,
|
|
val biometricAvailable: Boolean = false,
|
|
)
|
|
|
|
@HiltViewModel
|
|
class AuthViewModel @Inject constructor(
|
|
// TODO: Inject ChatClient, SessionManager
|
|
) : ViewModel() {
|
|
|
|
private val _uiState = MutableStateFlow(AuthUiState())
|
|
val uiState: StateFlow<AuthUiState> = _uiState.asStateFlow()
|
|
|
|
fun login(emailOrUsername: String, password: String) {
|
|
// TODO: Implement RSA challenge-response login
|
|
// 1. ChatClient.login(email, password)
|
|
// 2. On success: navigate to ConversationList
|
|
// 3. On failure: show error
|
|
}
|
|
|
|
fun register(username: String, email: String, password: String) {
|
|
// TODO: Implement registration
|
|
// 1. ChatClient.register(username, email, password)
|
|
// 2. On success: show confirmation code input
|
|
// 3. On failure: show error
|
|
}
|
|
|
|
fun confirmRegistration(email: String, code: String) {
|
|
// TODO: Confirm with 6-digit code
|
|
// 1. ChatClient.confirm_registration(email, code)
|
|
// 2. On success: auto-login
|
|
}
|
|
|
|
fun startPairing() {
|
|
// TODO: Start device pairing
|
|
// 1. ChatClient.pairing_start() -> get 8-digit code
|
|
// 2. Show code to user
|
|
// 3. Start polling for authorization
|
|
}
|
|
|
|
fun loginWithBiometric() {
|
|
// TODO: Biometric authentication
|
|
}
|
|
|
|
fun updateServerConfig(host: String, port: Int, useTls: Boolean) {
|
|
_uiState.value = _uiState.value.copy(
|
|
serverHost = host,
|
|
serverPort = port,
|
|
useTls = useTls,
|
|
)
|
|
}
|
|
|
|
fun clearError() {
|
|
_uiState.value = _uiState.value.copy(error = null)
|
|
}
|
|
}
|
|
```
|
|
|
|
## Reference: iOS LoginView behavior
|
|
- Login/Register are modes of the same view (iOS uses toggle tabs)
|
|
- Server configuration is expandable (collapsed by default)
|
|
- Biometric login uses Face ID/Touch ID
|
|
- Error messages appear below the form
|
|
- Loading spinner replaces the action button
|
|
|
|
## Constraints
|
|
- Use Material 3 components (OutlinedTextField, FilledTonalButton, etc.)
|
|
- Use `CatppuccinMocha` colors from theme
|
|
- Use `hiltViewModel()` for ViewModel injection
|
|
- All screens receive `navController: NavHostController` parameter
|
|
- Password fields must have visibility toggle
|
|
- Support keyboard "Done" action to submit form
|
|
- Use `rememberSaveable` for form state to survive config changes
|
|
|
|
## DO NOT
|
|
- Implement actual login/register/pairing logic (just skeleton functions)
|
|
- Handle cryptographic operations
|
|
- Store credentials or keys
|
|
- Modify navigation graph (screen placeholders already exist)
|