diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 83e3201..ea1c1d1 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -13,8 +13,8 @@ android { applicationId = "com.iiyh.emoneyinfo" minSdk = 28 targetSdk = 36 - versionCode = 3 - versionName = "1.0.1" + versionCode = 4 + versionName = "1.0.2" vectorDrawables { useSupportLibrary = true diff --git a/app/src/main/java/com/iiyh/emoneyinfo/data/Models.kt b/app/src/main/java/com/iiyh/emoneyinfo/data/Models.kt index 749a871..58d36d4 100644 --- a/app/src/main/java/com/iiyh/emoneyinfo/data/Models.kt +++ b/app/src/main/java/com/iiyh/emoneyinfo/data/Models.kt @@ -44,6 +44,18 @@ data class TransactionItem( append(locationName) } } + + fun historyKey(): String = buildString { + append(date.time / 1000) + append('|') + append(amount) + append('|') + append(if (isCredit) 1 else 0) + append('|') + append(locationName.trim()) + append('|') + append(title) + } } data class EmoneyUiState( @@ -63,6 +75,16 @@ data class EmoneyUiState( } fun hasCardData(): Boolean = cardType != CardType.UNKNOWN || cardNumber.isNotBlank() || balance > 0 || transactions.isNotEmpty() + + fun clearingHistory(scanMessage: String): EmoneyUiState = copy( + transactions = emptyList(), + scanMessage = scanMessage + ) + + fun deduplicatingHistory(): EmoneyUiState { + val deduplicated = transactions.distinctBy { it.historyKey() } + return if (deduplicated.size == transactions.size) this else copy(transactions = deduplicated) + } } fun String.formatCardNumber(): String { diff --git a/app/src/main/java/com/iiyh/emoneyinfo/nfc/UnifiedNfcReader.kt b/app/src/main/java/com/iiyh/emoneyinfo/nfc/UnifiedNfcReader.kt index 9c0ad7f..6ef5cc1 100644 --- a/app/src/main/java/com/iiyh/emoneyinfo/nfc/UnifiedNfcReader.kt +++ b/app/src/main/java/com/iiyh/emoneyinfo/nfc/UnifiedNfcReader.kt @@ -51,7 +51,8 @@ class UnifiedNfcReader(private val context: Context) { } fun startScan() { - refreshStatus() + resetMessageJob?.cancel() + _uiState.value = _uiState.value.clearingHistory(currentScanMessage()) } fun onNewIntent(intent: Intent) { @@ -65,7 +66,7 @@ class UnifiedNfcReader(private val context: Context) { AppLog.d("EmoneyInfoNfc", "onNewIntent techs=${tag.techList.joinToString()}") val reader = readers.firstOrNull { it.canHandle(tag) } ?: return runCatching { - _uiState.value = reader.read(tag, strings) + _uiState.value = reader.read(tag, strings).deduplicatingHistory() scheduleResetToRescanHint() }.onFailure { AppLog.e("EmoneyInfoNfc", "Scan failed: ${it.message}", it) diff --git a/app/src/main/java/com/iiyh/emoneyinfo/ui/EmoneyInfoApp.kt b/app/src/main/java/com/iiyh/emoneyinfo/ui/EmoneyInfoApp.kt index 4feb8fb..cbac3f2 100644 --- a/app/src/main/java/com/iiyh/emoneyinfo/ui/EmoneyInfoApp.kt +++ b/app/src/main/java/com/iiyh/emoneyinfo/ui/EmoneyInfoApp.kt @@ -95,7 +95,11 @@ fun EmoneyInfoApp( adsEnabled = adsEnabled, showCardNumber = showCardNumber, onScanTapped = { nfcReader.startScan() }, - onViewHistoryTapped = { navController.navigate("history") }, + onViewHistoryTapped = { + if (uiState.transactions.isNotEmpty()) { + navController.navigate("history") + } + }, onSettingsTapped = { navController.navigate("settings") } ) } diff --git a/app/src/main/java/com/iiyh/emoneyinfo/ui/screens/HomeScreen.kt b/app/src/main/java/com/iiyh/emoneyinfo/ui/screens/HomeScreen.kt index ea6c908..843df06 100644 --- a/app/src/main/java/com/iiyh/emoneyinfo/ui/screens/HomeScreen.kt +++ b/app/src/main/java/com/iiyh/emoneyinfo/ui/screens/HomeScreen.kt @@ -2,6 +2,7 @@ package com.iiyh.emoneyinfo.ui.screens import androidx.compose.foundation.background import androidx.compose.foundation.clickable +import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.Image import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.Box @@ -70,6 +71,7 @@ fun HomeScreen( val clipboard = LocalClipboardManager.current val context = LocalContext.current val latestTransaction = state.transactions.firstOrNull() + val hasHistory = state.transactions.isNotEmpty() val hasCardData = state.hasCardData() val displayCardNumber = when { state.cardNumber.isBlank() -> "" @@ -288,6 +290,7 @@ fun HomeScreen( modifier = Modifier .fillMaxWidth() .clickable( + enabled = hasHistory, interactionSource = remember { MutableInteractionSource() }, indication = null, onClick = onViewHistoryTapped @@ -295,8 +298,17 @@ fun HomeScreen( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp) ) { - Icon(Icons.Default.History, contentDescription = null) - Text(stringResource(R.string.view_full_history), fontWeight = FontWeight.SemiBold) + val historyColor = if (hasHistory) { + MaterialTheme.colorScheme.primary + } else { + TextSecondary.copy(alpha = if (isSystemInDarkTheme()) 0.7f else 0.4f) + } + Icon(Icons.Default.History, contentDescription = null, tint = historyColor) + Text( + stringResource(R.string.view_full_history), + fontWeight = FontWeight.SemiBold, + color = historyColor + ) } } }