v1.4.2: show Fuliza reading date + stale-data hint

The Fuliza card showed a bare outstanding figure with no recency, so a
stale reading (e.g. an old full-clear at 0) looked current. It now shows
'as of <date>' and, when the latest Fuliza SMS is over a day old, prompts
re-running Import SMS history — Safaricom moved Fuliza to a dedicated
sender, so older messages need a one-time re-import to be read in.
This commit is contained in:
ngenohkevin
2026-06-08 07:54:01 +03:00
parent 4e58bd8a8b
commit 6f88574165
3 changed files with 32 additions and 4 deletions
+2 -2
View File
@@ -27,8 +27,8 @@ android {
applicationId = "com.ledga.app"
minSdk = 26
targetSdk = 34
versionCode = 11
versionName = "1.4.1"
versionCode = 12
versionName = "1.4.2"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
@@ -58,6 +58,7 @@ import com.ledga.app.ui.theme.LedgaText
import com.ledga.app.ui.theme.Radius
import com.ledga.app.ui.theme.Space
import com.ledga.app.util.CurrencyFormatter
import com.ledga.app.util.DateUtils
@Composable
fun HomeScreen(
@@ -229,6 +230,7 @@ fun HomeScreen(
FulizaStatusCard(
outstanding = state.fulizaOutstanding,
available = state.fulizaAvailable,
asOf = state.fulizaOutstandingAt,
)
}
@@ -439,7 +441,7 @@ private fun UpdateBanner(
}
@Composable
private fun FulizaStatusCard(outstanding: Double?, available: Double?) {
private fun FulizaStatusCard(outstanding: Double?, available: Double?, asOf: Long?) {
BentoCard(title = "Fuliza") {
Row(
modifier = Modifier.fillMaxWidth(),
@@ -457,6 +459,13 @@ private fun FulizaStatusCard(outstanding: Double?, available: Double?) {
color = if ((outstanding ?: 0.0) > 0) LedgaDanger
else MaterialTheme.colorScheme.onSurface,
)
asOf?.let {
Text(
text = "as of ${DateUtils.formatRelativeDate(it).lowercase()}",
style = LedgaText.Caption,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
}
}
Column(modifier = Modifier.weight(1f)) {
Text(
@@ -491,5 +500,19 @@ private fun FulizaStatusCard(outstanding: Double?, available: Double?) {
)
}
}
// If the latest Fuliza SMS we have is more than a day old, the figure
// may be behind reality (Safaricom now sends Fuliza from a separate
// sender; older messages need a one-time re-import to be read in).
val stale = asOf != null &&
System.currentTimeMillis() - asOf > java.util.concurrent.TimeUnit.DAYS.toMillis(1)
if (stale) {
Text(
text = "No recent Fuliza SMS read. Run “Import SMS history” in You → Data " +
"to pull in newer Fuliza messages.",
style = LedgaText.Caption,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(top = Space.s3),
)
}
}
}
@@ -63,6 +63,8 @@ data class HomeUiState(
val fulizaOutstanding: Double? = null,
/** Latest known available Fuliza limit (borrowable) — null when never seen. */
val fulizaAvailable: Double? = null,
/** Timestamp of the SMS the outstanding figure came from — drives the "as of" caption. */
val fulizaOutstandingAt: Long? = null,
)
@OptIn(ExperimentalCoroutinesApi::class)
@@ -171,7 +173,10 @@ class HomeViewModel @Inject constructor(
}.combine(settingsRepository.getSelectedAccountId()) { state, accountId ->
state.copy(selectedAccountId = accountId)
}.combine(transactionRepository.getLatestFulizaOutstanding()) { state, tx ->
state.copy(fulizaOutstanding = tx?.fulizaOutstanding)
state.copy(
fulizaOutstanding = tx?.fulizaOutstanding,
fulizaOutstandingAt = tx?.timestamp,
)
}.combine(transactionRepository.getLatestFulizaLimit()) { state, tx ->
state.copy(fulizaAvailable = tx?.fulizaLimit)
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), HomeUiState())