fix: payment detail not loading on filter or search when not in memory

This commit is contained in:
2026-06-03 22:36:27 +02:00
parent 432cd0e478
commit cb9961608c
6 changed files with 21 additions and 37 deletions
@@ -72,7 +72,7 @@ sealed class TabItem(val route: String, val label: String, val icon: ImageVector
private const val ROUTE_HOME = "home"
private const val ROUTE_SCANNER = "scanner"
private const val ROUTE_HISTORY = "history"
private const val ROUTE_PAYMENT_DETAIL = "payment_detail/{paymentHash}"
private const val ROUTE_PAYMENT_DETAIL = "payment_detail/{checkingId}"
@Composable
fun WalletScreen(
@@ -317,7 +317,7 @@ fun WalletScreen(
navController.popBackStack(ROUTE_HOME, inclusive = false)
},
onPaymentClick = { payment ->
navController.navigate("payment_detail/${payment.paymentHash}")
navController.navigate("payment_detail/${payment.checkingId}")
}
)
}
@@ -325,13 +325,12 @@ fun WalletScreen(
// Payment detail — back → history (natural back stack)
composable(
route = ROUTE_PAYMENT_DETAIL,
arguments = listOf(navArgument("paymentHash") { type = NavType.StringType })
arguments = listOf(navArgument("checkingId") { type = NavType.StringType })
) { backStackEntry ->
val paymentHash = backStackEntry.arguments?.getString("paymentHash") ?: ""
val historyState by historyVm.state.collectAsState()
val payment = (historyState as? HistoryState.Success)
?.payments
?.firstOrNull { it.paymentHash == paymentHash }
val checkingId = backStackEntry.arguments?.getString("checkingId") ?: ""
// val historyState by historyVm.state.collectAsState()
val historyState by historyVm.filteredState.collectAsState()
val payment = historyVm.findPayment(checkingId)
if (payment != null) {
PaymentDetailScreen(
@@ -340,7 +339,7 @@ fun WalletScreen(
onBack = { navController.popBackStack() }
)
} else {
LaunchedEffect(paymentHash) { historyVm.refresh() }
LaunchedEffect(checkingId) { historyVm.refresh() }
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
@@ -7,7 +7,6 @@ import com.bitcointxoko.gudariwallet.api.LnurlScanResponse
import com.bitcointxoko.gudariwallet.data.NodeAliasRepository
import com.bitcointxoko.gudariwallet.data.PaymentCacheRepository
import com.bitcointxoko.gudariwallet.data.WalletRepository
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
@@ -57,17 +57,10 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalClipboard
import androidx.compose.ui.unit.dp
import com.bitcointxoko.gudariwallet.api.PaymentRecord
import com.bitcointxoko.gudariwallet.ui.history.DatePreset
import com.bitcointxoko.gudariwallet.ui.history.DirectionFilter
import com.bitcointxoko.gudariwallet.ui.HistoryState
import com.bitcointxoko.gudariwallet.ui.history.HistoryViewModel
import com.bitcointxoko.gudariwallet.ui.history.PaymentFilter
import com.bitcointxoko.gudariwallet.ui.history.StatusFilter
import com.bitcointxoko.gudariwallet.ui.history.isActive
import kotlinx.coroutines.launch
// ── List screen ──────────────────────────────────────────────────────────────
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun HistoryScreen(
@@ -265,7 +258,6 @@ fun HistoryScreen(
}
}
}
// ── Sheets (outside Scaffold so they overlay correctly) ──────────────────
if (filterSheetVisible) {
FilterBottomSheet(
@@ -292,7 +284,6 @@ fun HistoryScreen(
}
// ── Search bar ───────────────────────────────────────────────────────────────
@Composable
private fun SearchBar(
query : String,
@@ -357,7 +348,6 @@ private fun SearchBar(
}
// ── Active filter chips ──────────────────────────────────────────────────────
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun ActiveFilterChips(
@@ -22,6 +22,8 @@ import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
private const val TAG = "HistoryViewModel"
@OptIn(FlowPreview::class)
class HistoryViewModel(
private val repo : WalletRepository,
@@ -30,7 +32,6 @@ class HistoryViewModel(
val fiatCurrency : StateFlow<String?>,
val fiatSatsPerUnit : StateFlow<Double?>
) : ViewModel() {
// ── Sync manager (owns _state, currentOffset, loadJob, sync loop) ─────────
private val syncManager: PaymentSyncManager = PaymentSyncManager(
repo = repo,
@@ -38,7 +39,6 @@ class HistoryViewModel(
scope = viewModelScope,
onNewPayments = { payments -> enricher.enrich(payments) }
)
private val enricher: PaymentEnricher = PaymentEnricher(
repo = repo,
aliasRepo = aliasRepo,
@@ -48,9 +48,7 @@ class HistoryViewModel(
syncManager.updateEnrichedPayment(paymentHash, pubkey, alias)
}
)
val state: StateFlow<HistoryState> = syncManager.state
fun refresh() = syncManager.refresh()
fun loadMore() = syncManager.loadMore(
filteredCanLoadMore = { (filteredState.value as? HistoryState.Success)?.canLoadMore == true }
@@ -63,10 +61,8 @@ class HistoryViewModel(
scope = viewModelScope
)
val detailState: StateFlow<DetailState> = detailDelegate.state
fun loadDetail(checkingId: String, record: PaymentRecord? = null) =
detailDelegate.load(checkingId, record)
fun clearDetail() = detailDelegate.clear()
// ── Filter state ──────────────────────────────────────────────────────────
@@ -101,11 +97,18 @@ class HistoryViewModel(
val (min, max) = preset.toEpochSecondRange() // ← delegate to PaymentFilter.kt
setDateFilter(min, max, preset)
}
fun clearDateFilter() {
_filter.update { it.copy(minCreatedAt = null, maxCreatedAt = null, datePreset = null) }
}
fun findPayment(checkingId: String): PaymentRecord? {
val fromFiltered = (filteredState.value as? HistoryState.Success)
?.payments?.firstOrNull { it.checkingId == checkingId }
if (fromFiltered != null) return fromFiltered
return (state.value as? HistoryState.Success)
?.payments?.firstOrNull { it.checkingId == checkingId }
}
// ── Room-backed query results ──────────────────────────────────────────────
private val _roomResults = MutableStateFlow<List<PaymentRecord>?>(null)
@@ -165,7 +168,6 @@ class HistoryViewModel(
}
}
}
viewModelScope.launch {
WalletNotificationService.paymentEvents.collect { event ->
Log.d(TAG, "PAYMENTS [WS EVENT ] incoming event ${event.paymentHash} — refreshing list")
@@ -173,10 +175,6 @@ class HistoryViewModel(
}
}
}
companion object {
private const val TAG = "HistoryViewModel"
}
}
// ── Factory ───────────────────────────────────────────────────────────────────
@@ -55,6 +55,5 @@ class PaymentDetailDelegate(
}
}
}
fun clear() { _state.value = DetailState.Idle }
}
@@ -33,9 +33,8 @@ internal fun PaymentRow(
fiatSatsPerUnit : Double?,
onClick : () -> Unit
) {
val semantic = semanticColors()
val isPending = isPendingStatus(payment.status)
val semantic = semanticColors()
val amountColor = paymentAmountColor(payment.status, payment.isOutgoing)
val icon = if (payment.isOutgoing) Icons.Default.ArrowUpward
else Icons.Default.ArrowDownward