feat: make search backed by room
This commit is contained in:
@@ -8,6 +8,8 @@ import com.bitcointxoko.gudariwallet.data.db.AppDatabase
|
||||
import com.bitcointxoko.gudariwallet.data.db.toDomain
|
||||
import com.bitcointxoko.gudariwallet.data.db.toEntity
|
||||
import com.bitcointxoko.gudariwallet.util.WalletConstants
|
||||
import com.bitcointxoko.gudariwallet.ui.PaymentFilter
|
||||
import com.bitcointxoko.gudariwallet.ui.StatusFilter
|
||||
|
||||
private const val TAG = "PaymentCacheRepository"
|
||||
|
||||
@@ -36,10 +38,7 @@ class PaymentCacheRepository(context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a page of payments from the DB.
|
||||
* Returns empty list if nothing cached yet.
|
||||
*/
|
||||
/** Load a page of payments from the DB. Returns empty list if nothing cached yet. */
|
||||
suspend fun loadCachedPage(offset: Int, limit: Int): List<PaymentRecord> {
|
||||
return dao.getPage(limit = limit, offset = offset)
|
||||
.map { it.toDomain() }
|
||||
@@ -54,11 +53,7 @@ class PaymentCacheRepository(context: Context) {
|
||||
/** Total number of payment rows in the DB. */
|
||||
suspend fun countCached(): Int = dao.count()
|
||||
|
||||
/**
|
||||
* Merge a fetched page into the DB.
|
||||
* Uses INSERT OR REPLACE — never deletes existing rows.
|
||||
* Safe to call for any page, not just the first.
|
||||
*/
|
||||
/** Merge a fetched page into the DB. Uses INSERT OR REPLACE — never deletes existing rows. */
|
||||
suspend fun mergePayments(payments: List<PaymentRecord>) {
|
||||
if (payments.isEmpty()) return
|
||||
val now = System.currentTimeMillis()
|
||||
@@ -82,6 +77,44 @@ class PaymentCacheRepository(context: Context) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the full Room table using any combination of search + filter.
|
||||
* Called by HistoryViewModel whenever search or a SQL-capable filter is active.
|
||||
* Direction and type filters are NOT applied here — they're handled in-memory
|
||||
* in the ViewModel after this call returns.
|
||||
*/
|
||||
suspend fun queryAll(query: String, filter: PaymentFilter): List<PaymentRecord> {
|
||||
val searchQuery = query.trim().ifBlank { null }
|
||||
|
||||
// Build a comma-joined status pattern, e.g. "success,complete,paid,pending,in_flight,inflight"
|
||||
// null means "no status filter" — Room will skip that WHERE clause entirely.
|
||||
val statusPattern: String? = if (filter.statuses.isEmpty()) null else {
|
||||
filter.statuses.flatMap { s ->
|
||||
when (s) {
|
||||
StatusFilter.COMPLETED -> listOf("success", "complete", "paid")
|
||||
StatusFilter.PENDING -> listOf("pending", "in_flight", "inflight")
|
||||
StatusFilter.FAILED -> listOf("failed", "error", "expired")
|
||||
}
|
||||
}.joinToString(",")
|
||||
}
|
||||
|
||||
val minAmountMsat = filter.minAmountSat?.let { it * 1000L }
|
||||
val maxAmountMsat = filter.maxAmountSat?.let { it * 1000L }
|
||||
|
||||
return dao.queryAll(
|
||||
searchQuery = searchQuery,
|
||||
statusPattern = statusPattern,
|
||||
minAmountMsat = minAmountMsat,
|
||||
maxAmountMsat = maxAmountMsat,
|
||||
minCreatedAt = filter.minCreatedAt,
|
||||
maxCreatedAt = filter.maxCreatedAt
|
||||
).map { it.toDomain() }.also {
|
||||
Log.d(TAG, "PAYMENTS [QUERY ] q=$searchQuery status=$statusPattern " +
|
||||
"amt=${filter.minAmountSat}–${filter.maxAmountSat} " +
|
||||
"date=${filter.minCreatedAt}–${filter.maxCreatedAt} → ${it.size} rows")
|
||||
}
|
||||
}
|
||||
|
||||
// ── Payment detail ────────────────────────────────────────────────────────
|
||||
|
||||
/** Returns a cached detail response, or null if not yet fetched this session. */
|
||||
@@ -103,8 +136,7 @@ class PaymentCacheRepository(context: Context) {
|
||||
Log.d(TAG, "PAYMENTS [UPSERT ] ${payment.checkingId}")
|
||||
}
|
||||
|
||||
/** Check the DB for a stored payment record and return it as a detail response.
|
||||
* Returns null if not found. Never hits the network. */
|
||||
/** Check the DB for a stored payment record and return it as a detail response. */
|
||||
suspend fun getDetailFromDb(checkingId: String): PaymentDetailResponse? {
|
||||
val entity = dao.getByCheckingId(checkingId) ?: return null
|
||||
Log.d(TAG, "PAYMENTS [DETAIL DB ] $checkingId — found in Room")
|
||||
|
||||
@@ -35,4 +35,43 @@ interface PaymentDao {
|
||||
|
||||
@Query("DELETE FROM payment_records")
|
||||
suspend fun deleteAll()
|
||||
|
||||
/**
|
||||
* Full-table query used when search or filter is active.
|
||||
* All parameters are optional — pass null to skip that condition.
|
||||
*
|
||||
* Notes:
|
||||
* - status strings are passed as a comma-joined string and matched with LIKE
|
||||
* because Room doesn't support passing a list to IN() without extra setup.
|
||||
* We use ",success,complete,paid," style wrapping to avoid false matches.
|
||||
* - amountMsat thresholds are passed in msat (sat * 1000), ABS() handles
|
||||
* outgoing payments whose amountMsat is negative.
|
||||
* - createdAt is epoch seconds, matching the stored column.
|
||||
* - direction and type are handled in-memory post-query (no column for either).
|
||||
*/
|
||||
@Query("""
|
||||
SELECT * FROM payment_records
|
||||
WHERE
|
||||
(:searchQuery IS NULL OR (
|
||||
lower(memo) LIKE '%' || lower(:searchQuery) || '%' OR
|
||||
lower(paymentHash) LIKE '%' || lower(:searchQuery) || '%' OR
|
||||
lower(bolt11) LIKE '%' || lower(:searchQuery) || '%' OR
|
||||
lower(preimage) LIKE '%' || lower(:searchQuery) || '%'
|
||||
))
|
||||
AND (:statusPattern IS NULL OR
|
||||
',' || :statusPattern || ',' LIKE '%,' || lower(status) || ',%')
|
||||
AND (:minAmountMsat IS NULL OR ABS(amountMsat) >= :minAmountMsat)
|
||||
AND (:maxAmountMsat IS NULL OR ABS(amountMsat) <= :maxAmountMsat)
|
||||
AND (:minCreatedAt IS NULL OR createdAt >= :minCreatedAt)
|
||||
AND (:maxCreatedAt IS NULL OR createdAt <= :maxCreatedAt)
|
||||
ORDER BY createdAt DESC, time DESC
|
||||
""")
|
||||
suspend fun queryAll(
|
||||
searchQuery : String?,
|
||||
statusPattern : String?, // e.g. "success,complete,paid" or null
|
||||
minAmountMsat : Long?,
|
||||
maxAmountMsat : Long?,
|
||||
minCreatedAt : Long?,
|
||||
maxCreatedAt : Long?
|
||||
): List<PaymentRecordEntity>
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user