From f9991622b36ac846829008ef0656469cde517e16 Mon Sep 17 00:00:00 2001 From: rasputin Date: Sat, 13 Jun 2026 14:09:33 +0200 Subject: [PATCH] feat: show contact name on payment rows --- .../gudariwallet/data/ContactRepository.kt | 3 +++ .../bitcointxoko/gudariwallet/data/db/ContactDao.kt | 10 ++++++++++ .../gudariwallet/data/db/PaymentContactName.kt | 6 ++++++ .../gudariwallet/ui/history/HistoryScreen.kt | 2 ++ .../gudariwallet/ui/history/HistoryViewModel.kt | 11 ++++++++++- .../gudariwallet/ui/history/PaymentRow.kt | 6 ++++-- 6 files changed, 35 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/bitcointxoko/gudariwallet/data/db/PaymentContactName.kt diff --git a/app/src/main/java/com/bitcointxoko/gudariwallet/data/ContactRepository.kt b/app/src/main/java/com/bitcointxoko/gudariwallet/data/ContactRepository.kt index 71652c5..3fadb87 100644 --- a/app/src/main/java/com/bitcointxoko/gudariwallet/data/ContactRepository.kt +++ b/app/src/main/java/com/bitcointxoko/gudariwallet/data/ContactRepository.kt @@ -137,4 +137,7 @@ class ContactRepository(app: Application) { fun observePaymentIdsForContact(contactId: String): Flow> = dao.observePaymentIdsForContact(contactId) + fun observeAllPaymentContactNames(): Flow> = + dao.observePaymentContactNames() + .map { list -> list.associate { it.checkingId to it.contactName } } } diff --git a/app/src/main/java/com/bitcointxoko/gudariwallet/data/db/ContactDao.kt b/app/src/main/java/com/bitcointxoko/gudariwallet/data/db/ContactDao.kt index 7d886d2..d6a167f 100644 --- a/app/src/main/java/com/bitcointxoko/gudariwallet/data/db/ContactDao.kt +++ b/app/src/main/java/com/bitcointxoko/gudariwallet/data/db/ContactDao.kt @@ -98,4 +98,14 @@ interface ContactDao { ORDER BY createdAt DESC """) fun observePaymentIdsForContact(contactId: String): Flow> + + // Returns all (checkingId, displayName) pairs that have a linked contact, + // so HistoryViewModel can build its lookup map in a single reactive query. + @Query(""" + SELECT tcl.checkingId AS checkingId, + COALESCE(c.localAlias, c.displayName, c.name) AS contactName + FROM tx_contact_links tcl + INNER JOIN contacts c ON c.id = tcl.contactId +""") + fun observePaymentContactNames(): Flow> } diff --git a/app/src/main/java/com/bitcointxoko/gudariwallet/data/db/PaymentContactName.kt b/app/src/main/java/com/bitcointxoko/gudariwallet/data/db/PaymentContactName.kt new file mode 100644 index 0000000..3180278 --- /dev/null +++ b/app/src/main/java/com/bitcointxoko/gudariwallet/data/db/PaymentContactName.kt @@ -0,0 +1,6 @@ +package com.bitcointxoko.gudariwallet.data.db + +data class PaymentContactName( + val checkingId : String, + val contactName : String +) diff --git a/app/src/main/java/com/bitcointxoko/gudariwallet/ui/history/HistoryScreen.kt b/app/src/main/java/com/bitcointxoko/gudariwallet/ui/history/HistoryScreen.kt index 83f98ce..39d9b74 100644 --- a/app/src/main/java/com/bitcointxoko/gudariwallet/ui/history/HistoryScreen.kt +++ b/app/src/main/java/com/bitcointxoko/gudariwallet/ui/history/HistoryScreen.kt @@ -77,6 +77,7 @@ fun HistoryScreen( val fiatSatsPerUnit by vm.fiatSatsPerUnit.collectAsState() val availableTypes by vm.availableTypes.collectAsState() val listState = rememberLazyListState() + val contactNames by vm.contactNames.collectAsState() var filterSheetVisible by rememberSaveable { mutableStateOf(false) } val dismissFilterSheet = { filterSheetVisible = false } @@ -234,6 +235,7 @@ fun HistoryScreen( payment = payment, fiatCurrency = fiatCurrency, fiatSatsPerUnit = fiatSatsPerUnit, + contactName = contactNames[payment.checkingId], onClick = { onPaymentClick(payment) } ) HorizontalDivider( diff --git a/app/src/main/java/com/bitcointxoko/gudariwallet/ui/history/HistoryViewModel.kt b/app/src/main/java/com/bitcointxoko/gudariwallet/ui/history/HistoryViewModel.kt index f778399..cf6d1d6 100644 --- a/app/src/main/java/com/bitcointxoko/gudariwallet/ui/history/HistoryViewModel.kt +++ b/app/src/main/java/com/bitcointxoko/gudariwallet/ui/history/HistoryViewModel.kt @@ -156,7 +156,16 @@ class HistoryViewModel( else list.filter { it.extra?.tag in f.types } } - + // ── Contact name map ───────────────────────────────────────────────────── + // Maps checkingId → display name for any payment that has a linked contact. + // Rebuilt whenever the underlying tx_contact_links or contacts tables change. + val contactNames: StateFlow> = + contactRepo.observeAllPaymentContactNames() + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5_000), + initialValue = emptyMap() + ) // ── Contact assignment ─────────────────────────────────────────────────── diff --git a/app/src/main/java/com/bitcointxoko/gudariwallet/ui/history/PaymentRow.kt b/app/src/main/java/com/bitcointxoko/gudariwallet/ui/history/PaymentRow.kt index e712df2..00b025d 100644 --- a/app/src/main/java/com/bitcointxoko/gudariwallet/ui/history/PaymentRow.kt +++ b/app/src/main/java/com/bitcointxoko/gudariwallet/ui/history/PaymentRow.kt @@ -36,6 +36,7 @@ internal fun PaymentRow( payment : PaymentRecord, fiatCurrency : String?, fiatSatsPerUnit : Double?, + contactName : String? = null, onClick : () -> Unit ) { val strings = LocalAppStrings.current @@ -70,8 +71,9 @@ internal fun PaymentRow( Column(modifier = Modifier.weight(1f)) { Text( - text = payment.memo?.takeIf { it.isNotBlank() } - ?: strings.history.paymentRowNoMemo, // ← "No memo" + text = contactName // ← contact name first + ?: payment.memo?.takeIf { it.isNotBlank() } + ?: strings.history.paymentRowNoMemo, style = MaterialTheme.typography.bodyMedium, maxLines = 1, overflow = TextOverflow.Ellipsis