From 6f8857416588828a629874ebf4ced4020a0e9c3b Mon Sep 17 00:00:00 2001 From: ngenohkevin Date: Mon, 8 Jun 2026 07:54:01 +0300 Subject: [PATCH] v1.4.2: show Fuliza reading date + stale-data hint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 ' 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. --- app/build.gradle.kts | 4 +-- .../java/com/ledga/app/ui/home/HomeScreen.kt | 25 ++++++++++++++++++- .../com/ledga/app/ui/home/HomeViewModel.kt | 7 +++++- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index b2a4199..3e5c7c6 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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" } diff --git a/app/src/main/java/com/ledga/app/ui/home/HomeScreen.kt b/app/src/main/java/com/ledga/app/ui/home/HomeScreen.kt index c20e528..5e38ac1 100644 --- a/app/src/main/java/com/ledga/app/ui/home/HomeScreen.kt +++ b/app/src/main/java/com/ledga/app/ui/home/HomeScreen.kt @@ -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), + ) + } } } diff --git a/app/src/main/java/com/ledga/app/ui/home/HomeViewModel.kt b/app/src/main/java/com/ledga/app/ui/home/HomeViewModel.kt index 67cc85e..f44b14a 100644 --- a/app/src/main/java/com/ledga/app/ui/home/HomeViewModel.kt +++ b/app/src/main/java/com/ledga/app/ui/home/HomeViewModel.kt @@ -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())