EAS-202310715274-DimasHendr.../HISTORY_CRASH_ROOT_CAUSE.md
2026-01-14 21:13:18 +07:00

146 lines
3.8 KiB
Markdown

# ✅ FIX: History Screen Close App - Root Cause & Solutions
## 🔍 Root Causes Identified
### Issue 1: **Race Condition dalam LaunchedEffect**
**Problem:**
```kotlin
LaunchedEffect(Unit) {
userPreferences.npmFlow.collect { npm ->
currentNpm = npm
}
}
LaunchedEffect(Unit) {
userPreferences.namaFlow.collect { nama ->
currentNama = nama
}
}
```
- Dua LaunchedEffect dengan key yang sama (Unit) bersaing satu sama lain
- Bisa menyebabkan currentNpm tidak ter-load dengan benar saat membuka history
- Akibat: `currentNpm` masih null saat navigasi ke history → crash
### Issue 2: **No Error Handling di Composable History**
**Problem:**
```kotlin
composable("history") {
val attendanceList by repository.getAttendanceByNpm(
currentNpm ?: "" // Bisa null jika loading belum selesai
).collectAsState(initial = emptyList())
HistoryScreen(attendanceList = attendanceList, ...)
}
```
- `currentNpm` bisa null jika Flow belum selesai di-load
- `collectAsState` bisa throw exception
- Tidak ada error handling → langsung crash
---
## ✅ Solutions Applied
### Solution 1: **Added Error Handling di LaunchedEffect**
```kotlin
LaunchedEffect(Unit) {
try {
userPreferences.npmFlow.collect { npm ->
currentNpm = npm
isUserLoaded = npm != null // Track loading state
}
} catch (e: Exception) {
currentNpm = null
isUserLoaded = false
}
}
```
### Solution 2: **Improved History Composable dengan Try-Catch & Null Safety**
```kotlin
composable("history") {
try {
val npmForHistory = currentNpm ?: ""
if (npmForHistory.isEmpty()) {
// Show user-friendly warning
Box(modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center) {
Text("NPM tidak ditemukan. Silakan login ulang.")
}
} else {
val attendanceList by repository.getAttendanceByNpm(npmForHistory)
.collectAsState(initial = emptyList())
HistoryScreen(
attendanceList = attendanceList,
onBackClick = { navController.navigateUp() }
)
}
} catch (e: Exception) {
// Fallback error UI
Box(modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center) {
Text("Error: ${e.message ?: "Tidak dapat membuka riwayat"}")
}
}
}
```
### Solution 3: **Added Required Imports**
```kotlin
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.ui.Alignment
```
---
## 📊 Changes Summary
| Komponen | Sebelum | Sesudah |
|----------|---------|--------|
| **LaunchedEffect** | ❌ No error handling | ✅ Try-catch + state tracking |
| **History Composable** | ❌ No null check | ✅ Defensive null check |
| **Error Handling** | ❌ None | ✅ Try-catch with fallback UI |
| **Loading State** | ❌ None | ✅ `isUserLoaded` flag |
| **User Feedback** | ❌ Crash | ✅ Clear error messages |
---
## 🧪 Test Scenarios
### ✅ Test 1: Normal Flow
```
1. Login dengan npm valid
2. Tunggu loading selesai
3. Buka History
✅ Expected: Riwayat ditampilkan dengan aman
```
### ✅ Test 2: Rapid Navigation
```
1. Login
2. Langsung ke history (sebelum loading selesai)
✅ Expected: Tampil "NPM tidak ditemukan..." atau loading
```
### ✅ Test 3: Data Error
```
1. Network issue saat load preferences
2. Buka History
✅ Expected: Tampil user-friendly error message
```
---
## 📝 Files Modified
- `MainActivity.kt` - Added error handling & improved history composable
## 🚀 Expected Results
✅ App tidak lagi crash saat membuka History
✅ User mendapat informasi jelas jika ada error
✅ Graceful handling untuk berbagai edge cases
✅ Proper state management untuk user preferences