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