v1.3.5: reparse progress spinner + Today default on Home

- Re-parse ALL now shows a spinner + "Re-parsing all transactions…"
  while running, so taps don't feel ignored. Button is hidden during
  the work; reappears after the result line.
- Empty result (no transactions to re-parse) shows a hint pointing at
  "Import SMS history" instead of a confusing "0 of 0".
- Home period selector defaults to Today instead of Month — the spec
  showed Month first, but in practice users want to see today's
  spending at a glance and switch out only when needed.
This commit is contained in:
ngenohkevin
2026-05-16 22:08:59 +03:00
parent 6571cd0497
commit f83f644cd3
4 changed files with 43 additions and 8 deletions
+2 -2
View File
@@ -27,8 +27,8 @@ android {
applicationId = "com.ledga.app"
minSdk = 26
targetSdk = 34
versionCode = 7
versionName = "1.3.4"
versionCode = 8
versionName = "1.3.5"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -51,7 +51,7 @@ data class HomeUiState(
val donutSegments: List<DonutSegment> = emptyList(),
val categoryBreakdown: List<CategoryBreakdownItem> = emptyList(),
val recentTransactions: List<TransactionWithCategory> = emptyList(),
val selectedPeriod: Period = Period.THIS_MONTH,
val selectedPeriod: Period = Period.TODAY,
val monthLabel: String = "",
val updateAvailable: GitHubRelease? = null,
/** True when the background worker has already downloaded the APK. */
@@ -99,7 +99,7 @@ class HomeViewModel @Inject constructor(
}
}
private val _selectedPeriod = MutableStateFlow(Period.THIS_MONTH)
private val _selectedPeriod = MutableStateFlow(Period.TODAY)
private val timeRange = _selectedPeriod.flatMapLatest { period ->
val now = System.currentTimeMillis()
@@ -112,10 +112,20 @@ class SettingsViewModel @Inject constructor(
private val _reparseAllResult = MutableStateFlow<ReparseResult?>(null)
val reparseAllResult: StateFlow<ReparseResult?> = _reparseAllResult
private val _reparseAllRunning = MutableStateFlow(false)
val reparseAllRunning: StateFlow<Boolean> = _reparseAllRunning
/** Re-runs MpesaSmsParser over every stored row to pick up parser fixes. */
fun reparseAll() {
if (_reparseAllRunning.value) return
_reparseAllRunning.value = true
_reparseAllResult.value = null
viewModelScope.launch {
_reparseAllResult.value = transactionRepository.reparseAllTransactions()
try {
_reparseAllResult.value = transactionRepository.reparseAllTransactions()
} finally {
_reparseAllRunning.value = false
}
}
}
@@ -76,6 +76,7 @@ fun YouScreen(
val importStatus by viewModel.importStatus.collectAsState()
val reparseResult by viewModel.reparseResult.collectAsState()
val reparseAllResult by viewModel.reparseAllResult.collectAsState()
val reparseAllRunning by viewModel.reparseAllRunning.collectAsState()
val exportResult by viewModel.exportResult.collectAsState()
val fileImportResult by viewModel.fileImportResult.collectAsState()
val backupStatus by viewModel.backupStatus.collectAsState()
@@ -216,11 +217,35 @@ fun YouScreen(
}
TextButtonRow("View unparsed messages", onNavigateToUnparsed)
}
TextButtonRow("Re-parse ALL transactions") {
viewModel.reparseAll()
if (reparseAllRunning) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 10.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(10.dp),
) {
androidx.compose.material3.CircularProgressIndicator(
modifier = Modifier.size(16.dp),
strokeWidth = 2.dp,
color = LedgaAccent,
)
Text(
text = "Re-parsing all transactions…",
style = LedgaText.BodyL,
color = MaterialTheme.colorScheme.onSurface,
)
}
} else {
TextButtonRow("Re-parse ALL transactions") {
viewModel.reparseAll()
}
}
reparseAllResult?.let {
StatusLine("Re-parsed ${it.fixed} of ${it.total} (${it.stillUnknown} skipped)")
StatusLine(
if (it.total == 0) "No transactions yet — try Import SMS history first."
else "Re-parsed ${it.fixed} of ${it.total} (${it.stillUnknown} skipped)"
)
}
}