From 2c1f6af22c223e3eb6558ef8ba78a7aec876b1f1 Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Sun, 24 Aug 2025 15:24:59 -0300 Subject: [PATCH 01/19] feat: attach tags on activity sync --- .../java/to/bitkit/data/dao/InvoiceTagDao.kt | 3 ++ .../to/bitkit/repositories/ActivityRepo.kt | 2 +- .../java/to/bitkit/repositories/WalletRepo.kt | 6 ++++ .../viewmodels/ActivityListViewModel.kt | 29 +++++++++++++++++-- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/to/bitkit/data/dao/InvoiceTagDao.kt b/app/src/main/java/to/bitkit/data/dao/InvoiceTagDao.kt index 4af8dca61..bdee6734c 100644 --- a/app/src/main/java/to/bitkit/data/dao/InvoiceTagDao.kt +++ b/app/src/main/java/to/bitkit/data/dao/InvoiceTagDao.kt @@ -13,6 +13,9 @@ interface InvoiceTagDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun saveInvoice(invoiceTag: InvoiceTagEntity) + @Query("SELECT * FROM invoice_tag") + suspend fun getAll(): List + @Query("SELECT * FROM invoice_tag WHERE paymentHash = :paymentHash LIMIT 1") suspend fun searchInvoice(paymentHash: String): InvoiceTagEntity? diff --git a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt index b870eaab7..91cc1ca2f 100644 --- a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt @@ -105,7 +105,7 @@ class ActivityRepo @Inject constructor( suspend fun findActivityByPaymentId( paymentHashOrTxId: String, type: ActivityFilter, - txType: PaymentType, + txType: PaymentType?, retry: Boolean = true, ): Result = withContext(bgDispatcher) { if (paymentHashOrTxId.isEmpty()) { diff --git a/app/src/main/java/to/bitkit/repositories/WalletRepo.kt b/app/src/main/java/to/bitkit/repositories/WalletRepo.kt index 151c6703b..505765764 100644 --- a/app/src/main/java/to/bitkit/repositories/WalletRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/WalletRepo.kt @@ -453,6 +453,12 @@ class WalletRepo @Inject constructor( } } + suspend fun getAllInvoiceTags() : Result> = withContext(bgDispatcher) { + return@withContext runCatching { + db.invoiceTagDao().getAll() + } + } + suspend fun searchInvoice(txId: Txid): Result = withContext(bgDispatcher) { return@withContext try { val invoiceTag = db.invoiceTagDao().searchInvoice(paymentHash = txId) ?: return@withContext Result.failure( diff --git a/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt index 018e98369..593e3c3d2 100644 --- a/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt @@ -15,8 +15,9 @@ import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import to.bitkit.di.BgDispatcher +import to.bitkit.ext.rawId import to.bitkit.repositories.ActivityRepo -import to.bitkit.repositories.LightningRepo +import to.bitkit.repositories.WalletRepo import to.bitkit.services.CoreService import to.bitkit.services.LdkNodeEventBus import to.bitkit.ui.screens.wallets.activity.components.ActivityTab @@ -27,9 +28,9 @@ import javax.inject.Inject class ActivityListViewModel @Inject constructor( @BgDispatcher private val bgDispatcher: CoroutineDispatcher, private val coreService: CoreService, - private val lightningRepo: LightningRepo, + private val walletRepo: WalletRepo, private val ldkNodeEventBus: LdkNodeEventBus, - private val activityRepo: ActivityRepo + private val activityRepo: ActivityRepo, ) : ViewModel() { private val _filteredActivities = MutableStateFlow?>(null) val filteredActivities = _filteredActivities.asStateFlow() @@ -224,6 +225,7 @@ class ActivityListViewModel @Inject constructor( viewModelScope.launch { activityRepo.syncActivities().onSuccess { syncState() + syncActivityTags() }.onFailure { e -> Logger.error("Failed to sync ldk-node payments", e) } @@ -243,4 +245,25 @@ class ActivityListViewModel @Inject constructor( syncState() } } + + private fun syncActivityTags() { + viewModelScope.launch { + walletRepo.getAllInvoiceTags().onSuccess { invoiceTags -> + invoiceTags.forEach { tagEntity -> + activityRepo.findActivityByPaymentId( + paymentHashOrTxId = tagEntity.paymentHash, + type = ActivityFilter.ALL, + txType = null + ).onSuccess { activity -> + activityRepo.addTagsToActivity( + activityId = activity.rawId(), + tags = tagEntity.tags + ).onSuccess { + walletRepo.deleteInvoice(tagEntity.paymentHash) + } + } + } + } + } + } } From 552cccbf36fd1dd1f63e5f4efc08b5844debe056 Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Mon, 25 Aug 2025 09:11:13 -0300 Subject: [PATCH 02/19] refactor: call addTagsToTransaction --- .../java/to/bitkit/repositories/ActivityRepo.kt | 4 ++-- .../to/bitkit/viewmodels/ActivityListViewModel.kt | 15 +++++---------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt index d5ab2d2ba..df9f52712 100644 --- a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt @@ -451,7 +451,7 @@ class ActivityRepo @Inject constructor( suspend fun addTagsToTransaction( paymentHashOrTxId: String, type: ActivityFilter, - txType: PaymentType, + txType: PaymentType?, tags: List, ): Result = withContext(bgDispatcher) { if (tags.isEmpty()) return@withContext Result.failure(IllegalArgumentException("No tags selected")) @@ -460,7 +460,7 @@ class ActivityRepo @Inject constructor( type = type, txType = txType ).onSuccess { activity -> - return@withContext addTagsToActivity(activity.rawId(), tags = tags) + addTagsToActivity(activity.rawId(), tags = tags) }.onFailure { e -> return@withContext Result.failure(e) }.map { Unit } diff --git a/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt index 593e3c3d2..5484f43e8 100644 --- a/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt @@ -15,7 +15,6 @@ import kotlinx.coroutines.flow.debounce import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import to.bitkit.di.BgDispatcher -import to.bitkit.ext.rawId import to.bitkit.repositories.ActivityRepo import to.bitkit.repositories.WalletRepo import to.bitkit.services.CoreService @@ -250,17 +249,13 @@ class ActivityListViewModel @Inject constructor( viewModelScope.launch { walletRepo.getAllInvoiceTags().onSuccess { invoiceTags -> invoiceTags.forEach { tagEntity -> - activityRepo.findActivityByPaymentId( + activityRepo.addTagsToTransaction( paymentHashOrTxId = tagEntity.paymentHash, type = ActivityFilter.ALL, - txType = null - ).onSuccess { activity -> - activityRepo.addTagsToActivity( - activityId = activity.rawId(), - tags = tagEntity.tags - ).onSuccess { - walletRepo.deleteInvoice(tagEntity.paymentHash) - } + txType = null, + tags = tagEntity.tags + ).onSuccess { + walletRepo.deleteInvoice(tagEntity.paymentHash) } } } From 5643d43b79d66019b9c498023e21b41a220915bc Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Tue, 26 Aug 2025 09:44:27 -0300 Subject: [PATCH 03/19] feat: rename InvoiceTagEntity.kt to TagMetadataEntity.kt and add more parameters --- app/src/main/java/to/bitkit/data/AppDb.kt | 10 ++-- .../java/to/bitkit/data/dao/InvoiceTagDao.kt | 33 ---------- .../java/to/bitkit/data/dao/TagMetadataDao.kt | 60 +++++++++++++++++++ .../bitkit/data/entities/InvoiceTagEntity.kt | 11 ---- .../bitkit/data/entities/TagMetadataEntity.kt | 21 +++++++ .../java/to/bitkit/repositories/WalletRepo.kt | 29 +++++---- .../java/to/bitkit/viewmodels/AppViewModel.kt | 2 +- 7 files changed, 103 insertions(+), 63 deletions(-) delete mode 100644 app/src/main/java/to/bitkit/data/dao/InvoiceTagDao.kt create mode 100644 app/src/main/java/to/bitkit/data/dao/TagMetadataDao.kt delete mode 100644 app/src/main/java/to/bitkit/data/entities/InvoiceTagEntity.kt create mode 100644 app/src/main/java/to/bitkit/data/entities/TagMetadataEntity.kt diff --git a/app/src/main/java/to/bitkit/data/AppDb.kt b/app/src/main/java/to/bitkit/data/AppDb.kt index dd3a20a77..0e3216741 100644 --- a/app/src/main/java/to/bitkit/data/AppDb.kt +++ b/app/src/main/java/to/bitkit/data/AppDb.kt @@ -19,22 +19,22 @@ import dagger.assisted.AssistedInject import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.flow.Flow import to.bitkit.BuildConfig -import to.bitkit.data.dao.InvoiceTagDao +import to.bitkit.data.dao.TagMetadataDao import to.bitkit.data.entities.ConfigEntity -import to.bitkit.data.entities.InvoiceTagEntity +import to.bitkit.data.entities.TagMetadataEntity import to.bitkit.data.typeConverters.StringListConverter @Database( entities = [ ConfigEntity::class, - InvoiceTagEntity::class, + TagMetadataEntity::class, ], - version = 2, + version = 3, ) @TypeConverters(StringListConverter::class) abstract class AppDb : RoomDatabase() { abstract fun configDao(): ConfigDao - abstract fun invoiceTagDao(): InvoiceTagDao + abstract fun tagMetadataDao(): TagMetadataDao companion object { private const val DB_NAME = "${BuildConfig.APPLICATION_ID}.sqlite" diff --git a/app/src/main/java/to/bitkit/data/dao/InvoiceTagDao.kt b/app/src/main/java/to/bitkit/data/dao/InvoiceTagDao.kt deleted file mode 100644 index bdee6734c..000000000 --- a/app/src/main/java/to/bitkit/data/dao/InvoiceTagDao.kt +++ /dev/null @@ -1,33 +0,0 @@ -package to.bitkit.data.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import to.bitkit.data.entities.InvoiceTagEntity - -@Dao -interface InvoiceTagDao { - - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun saveInvoice(invoiceTag: InvoiceTagEntity) - - @Query("SELECT * FROM invoice_tag") - suspend fun getAll(): List - - @Query("SELECT * FROM invoice_tag WHERE paymentHash = :paymentHash LIMIT 1") - suspend fun searchInvoice(paymentHash: String): InvoiceTagEntity? - - @Delete - suspend fun deleteInvoice(invoiceTag: InvoiceTagEntity) - - @Query("DELETE FROM invoice_tag WHERE paymentHash = :paymentHash") - suspend fun deleteInvoiceByPaymentHash(paymentHash: String) - - @Query("DELETE FROM invoice_tag") - suspend fun deleteAllInvoices() - - @Query("DELETE FROM invoice_tag WHERE createdAt < :expirationTimeStamp") - suspend fun deleteExpiredInvoices(expirationTimeStamp: Long) -} diff --git a/app/src/main/java/to/bitkit/data/dao/TagMetadataDao.kt b/app/src/main/java/to/bitkit/data/dao/TagMetadataDao.kt new file mode 100644 index 000000000..d2ee57b35 --- /dev/null +++ b/app/src/main/java/to/bitkit/data/dao/TagMetadataDao.kt @@ -0,0 +1,60 @@ +package to.bitkit.data.dao + +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import to.bitkit.data.entities.TagMetadataEntity + +@Dao +interface TagMetadataDao { + + @Insert(onConflict = OnConflictStrategy.REPLACE) + suspend fun saveTagMetadata(tagMetadata: TagMetadataEntity) + + @Query("SELECT * FROM tag_metadata") + suspend fun getAll(): List + + // Search by payment hash (for invoices) + @Query("SELECT * FROM tag_metadata WHERE paymentHash = :paymentHash LIMIT 1") + suspend fun searchByPaymentHash(paymentHash: String): TagMetadataEntity? + + // Search by transaction ID + @Query("SELECT * FROM tag_metadata WHERE txId = :txId LIMIT 1") + suspend fun searchByTxId(txId: String): TagMetadataEntity? + + // Search by address + @Query("SELECT * FROM tag_metadata WHERE address = :address") + suspend fun searchByAddress(address: String): List + + // Search by primary key (id) + @Query("SELECT * FROM tag_metadata WHERE id = :id LIMIT 1") + suspend fun searchById(id: String): TagMetadataEntity? + + // Get all receive transactions + @Query("SELECT * FROM tag_metadata WHERE isReceive = 1") + suspend fun getAllReceiveTransactions(): List + + // Get all send transactions + @Query("SELECT * FROM tag_metadata WHERE isReceive = 0") + suspend fun getAllSendTransactions(): List + + @Delete + suspend fun deleteTagMetadata(tagMetadata: TagMetadataEntity) + + @Query("DELETE FROM tag_metadata WHERE paymentHash = :paymentHash") + suspend fun deleteByPaymentHash(paymentHash: String) + + @Query("DELETE FROM tag_metadata WHERE txId = :txId") + suspend fun deleteByTxId(txId: String) + + @Query("DELETE FROM tag_metadata WHERE id = :id") + suspend fun deleteById(id: String) + + @Query("DELETE FROM tag_metadata") + suspend fun deleteAll() + + @Query("DELETE FROM tag_metadata WHERE createdAt < :expirationTimeStamp") + suspend fun deleteExpired(expirationTimeStamp: Long) +} diff --git a/app/src/main/java/to/bitkit/data/entities/InvoiceTagEntity.kt b/app/src/main/java/to/bitkit/data/entities/InvoiceTagEntity.kt deleted file mode 100644 index 168037177..000000000 --- a/app/src/main/java/to/bitkit/data/entities/InvoiceTagEntity.kt +++ /dev/null @@ -1,11 +0,0 @@ -package to.bitkit.data.entities - -import androidx.room.Entity -import androidx.room.PrimaryKey - -@Entity(tableName = "invoice_tag") -data class InvoiceTagEntity( - @PrimaryKey val paymentHash: String, - val tags: List, - val createdAt: Long -) diff --git a/app/src/main/java/to/bitkit/data/entities/TagMetadataEntity.kt b/app/src/main/java/to/bitkit/data/entities/TagMetadataEntity.kt new file mode 100644 index 000000000..374f9c5cf --- /dev/null +++ b/app/src/main/java/to/bitkit/data/entities/TagMetadataEntity.kt @@ -0,0 +1,21 @@ +package to.bitkit.data.entities + +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "tag_metadata") +/** + * @param id This will be paymentHash, txId, or address depending on context + * @param txId on-chain transaction id + * @param address on-chain address + * @param isReceive true for receive, false for send + * */ +data class TagMetadataEntity( + @PrimaryKey val id: String, + val paymentHash: String? = null, + val txId: String? = null, + val address: String, + val isReceive: Boolean, + val tags: List, + val createdAt: Long, +) diff --git a/app/src/main/java/to/bitkit/repositories/WalletRepo.kt b/app/src/main/java/to/bitkit/repositories/WalletRepo.kt index 505765764..5854520ef 100644 --- a/app/src/main/java/to/bitkit/repositories/WalletRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/WalletRepo.kt @@ -20,7 +20,7 @@ import org.lightningdevkit.ldknode.Txid import to.bitkit.data.AppDb import to.bitkit.data.CacheStore import to.bitkit.data.SettingsStore -import to.bitkit.data.entities.InvoiceTagEntity +import to.bitkit.data.entities.TagMetadataEntity import to.bitkit.data.keychain.Keychain import to.bitkit.di.BgDispatcher import to.bitkit.env.Env @@ -440,10 +440,12 @@ class WalletRepo @Inject constructor( } paymentHashOrAddress?.let { - db.invoiceTagDao().saveInvoice( - invoiceTag = InvoiceTagEntity( - paymentHash = paymentHashOrAddress, + db.tagMetadataDao().saveTagMetadata( + tagMetadata = TagMetadataEntity( + id = paymentHashOrAddress, tags = tags, + address = "", + isReceive = true, createdAt = System.currentTimeMillis() ) ) @@ -453,17 +455,18 @@ class WalletRepo @Inject constructor( } } - suspend fun getAllInvoiceTags() : Result> = withContext(bgDispatcher) { + suspend fun getAllInvoiceTags(): Result> = withContext(bgDispatcher) { return@withContext runCatching { - db.invoiceTagDao().getAll() + db.tagMetadataDao().getAll() } } - suspend fun searchInvoice(txId: Txid): Result = withContext(bgDispatcher) { + suspend fun searchInvoiceByPaymentHash(paymentHash: String): Result = withContext(bgDispatcher) { return@withContext try { - val invoiceTag = db.invoiceTagDao().searchInvoice(paymentHash = txId) ?: return@withContext Result.failure( - Exception("Invoice not found") - ) + val invoiceTag = + db.tagMetadataDao().searchByPaymentHash(paymentHash = paymentHash) ?: return@withContext Result.failure( + Exception("Invoice not found") + ) Result.success(invoiceTag) } catch (e: Throwable) { Logger.error("searchInvoice error", e, context = TAG) @@ -473,7 +476,7 @@ class WalletRepo @Inject constructor( suspend fun deleteInvoice(txId: Txid) = withContext(bgDispatcher) { try { - db.invoiceTagDao().deleteInvoiceByPaymentHash(paymentHash = txId) + db.tagMetadataDao().deleteByPaymentHash(paymentHash = txId) } catch (e: Throwable) { Logger.error("deleteInvoice error", e, context = TAG) } @@ -481,7 +484,7 @@ class WalletRepo @Inject constructor( suspend fun deleteAllInvoices() = withContext(bgDispatcher) { try { - db.invoiceTagDao().deleteAllInvoices() + db.tagMetadataDao().deleteAll() } catch (e: Throwable) { Logger.error("deleteAllInvoices error", e, context = TAG) } @@ -490,7 +493,7 @@ class WalletRepo @Inject constructor( suspend fun deleteExpiredInvoices() = withContext(bgDispatcher) { try { val twoDaysAgoMillis = Clock.System.now().minus(2.days).toEpochMilliseconds() - db.invoiceTagDao().deleteExpiredInvoices(expirationTimeStamp = twoDaysAgoMillis) + db.tagMetadataDao().deleteExpired(expirationTimeStamp = twoDaysAgoMillis) } catch (e: Throwable) { Logger.error("deleteExpiredInvoices error", e, context = TAG) } diff --git a/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt index f5e8302f1..10bb66ed7 100644 --- a/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt @@ -261,7 +261,7 @@ class AppViewModel @Inject constructor( } private suspend fun handleTags(event: Event.PaymentReceived) { - val tags = walletRepo.searchInvoice(txId = event.paymentHash).getOrNull()?.tags.orEmpty() + val tags = walletRepo.searchInvoiceByPaymentHash(paymentHash = event.paymentHash).getOrNull()?.tags.orEmpty() activityRepo.addTagsToTransaction( paymentHashOrTxId = event.paymentHash, type = ActivityFilter.LIGHTNING, From b46a40cd75628cfbf07ee98119e0604e5c86c501 Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Tue, 26 Aug 2025 09:50:39 -0300 Subject: [PATCH 04/19] feat: rename InvoiceTagEntity.kt to TagMetadataEntity.kt and add more parameters --- app/schemas/to.bitkit.data.AppDb/3.json | 90 +++++++++++++++++++ .../viewmodels/ActivityListViewModel.kt | 16 ++-- 2 files changed, 99 insertions(+), 7 deletions(-) create mode 100644 app/schemas/to.bitkit.data.AppDb/3.json diff --git a/app/schemas/to.bitkit.data.AppDb/3.json b/app/schemas/to.bitkit.data.AppDb/3.json new file mode 100644 index 000000000..2dd3f383a --- /dev/null +++ b/app/schemas/to.bitkit.data.AppDb/3.json @@ -0,0 +1,90 @@ +{ + "formatVersion": 1, + "database": { + "version": 3, + "identityHash": "e8c2a3893862c335833d1329bd45b666", + "entities": [ + { + "tableName": "config", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`walletIndex` INTEGER NOT NULL, PRIMARY KEY(`walletIndex`))", + "fields": [ + { + "fieldPath": "walletIndex", + "columnName": "walletIndex", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "walletIndex" + ] + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "tag_metadata", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `paymentHash` TEXT, `txId` TEXT, `address` TEXT NOT NULL, `isReceive` INTEGER NOT NULL, `tags` TEXT NOT NULL, `createdAt` INTEGER NOT NULL, PRIMARY KEY(`id`))", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "paymentHash", + "columnName": "paymentHash", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "txId", + "columnName": "txId", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "address", + "columnName": "address", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "isReceive", + "columnName": "isReceive", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tags", + "columnName": "tags", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "createdAt", + "columnName": "createdAt", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e8c2a3893862c335833d1329bd45b666')" + ] + } +} \ No newline at end of file diff --git a/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt index 5484f43e8..e6b70f977 100644 --- a/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt @@ -249,13 +249,15 @@ class ActivityListViewModel @Inject constructor( viewModelScope.launch { walletRepo.getAllInvoiceTags().onSuccess { invoiceTags -> invoiceTags.forEach { tagEntity -> - activityRepo.addTagsToTransaction( - paymentHashOrTxId = tagEntity.paymentHash, - type = ActivityFilter.ALL, - txType = null, - tags = tagEntity.tags - ).onSuccess { - walletRepo.deleteInvoice(tagEntity.paymentHash) + tagEntity.paymentHash?.let { + activityRepo.addTagsToTransaction( + paymentHashOrTxId = it, + type = ActivityFilter.ALL, + txType = null, + tags = tagEntity.tags + ).onSuccess { + walletRepo.deleteInvoice(tagEntity.paymentHash) + } } } } From 43dc6bef2c20c47eee95b06ff6745a340a87bb2d Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Tue, 26 Aug 2025 10:23:52 -0300 Subject: [PATCH 05/19] feat: save on-chain address --- .../java/to/bitkit/repositories/WalletRepo.kt | 55 ++++++++++--------- 1 file changed, 30 insertions(+), 25 deletions(-) diff --git a/app/src/main/java/to/bitkit/repositories/WalletRepo.kt b/app/src/main/java/to/bitkit/repositories/WalletRepo.kt index 5854520ef..85323feb8 100644 --- a/app/src/main/java/to/bitkit/repositories/WalletRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/WalletRepo.kt @@ -395,15 +395,19 @@ class WalletRepo @Inject constructor( } else { setBolt11("") } - + val address = getOnchainAddress() val newBip21 = buildBip21Url( - bitcoinAddress = getOnchainAddress(), + bitcoinAddress = address, amountSats = _walletState.value.bip21AmountSats, message = description.ifBlank { Env.DEFAULT_INVOICE_MESSAGE }, lightningInvoice = getBolt11() ) setBip21(newBip21) - saveInvoiceWithTags(bip21Invoice = newBip21, tags = _walletState.value.selectedTags) + saveInvoiceWithTags( + bip21Invoice = newBip21, + onChainAddress = address, + tags = _walletState.value.selectedTags + ) Result.success(Unit) } catch (e: Throwable) { Logger.error("Update BIP21 invoice error", e, context = TAG) @@ -427,33 +431,34 @@ class WalletRepo @Inject constructor( } } - suspend fun saveInvoiceWithTags(bip21Invoice: String, tags: List) = withContext(bgDispatcher) { - if (tags.isEmpty()) return@withContext + suspend fun saveInvoiceWithTags(bip21Invoice: String, onChainAddress: String, tags: List) = + withContext(bgDispatcher) { + if (tags.isEmpty()) return@withContext - try { - deleteExpiredInvoices() - val decoded = decode(bip21Invoice) - val paymentHashOrAddress = when (decoded) { - is Scanner.Lightning -> decoded.invoice.paymentHash.toHex() - is Scanner.OnChain -> decoded.extractLightningHashOrAddress() - else -> null - } + try { + deleteExpiredInvoices() + val decoded = decode(bip21Invoice) + val paymentHashOrAddress = when (decoded) { + is Scanner.Lightning -> decoded.invoice.paymentHash.toHex() + is Scanner.OnChain -> decoded.extractLightningHashOrAddress() + else -> null + } - paymentHashOrAddress?.let { - db.tagMetadataDao().saveTagMetadata( - tagMetadata = TagMetadataEntity( - id = paymentHashOrAddress, - tags = tags, - address = "", - isReceive = true, - createdAt = System.currentTimeMillis() + paymentHashOrAddress?.let { + db.tagMetadataDao().saveTagMetadata( + tagMetadata = TagMetadataEntity( + id = paymentHashOrAddress, + tags = tags, + address = onChainAddress, + isReceive = true, + createdAt = System.currentTimeMillis() + ) ) - ) + } + } catch (e: Throwable) { + Logger.error("saveInvoice error", e, context = TAG) } - } catch (e: Throwable) { - Logger.error("saveInvoice error", e, context = TAG) } - } suspend fun getAllInvoiceTags(): Result> = withContext(bgDispatcher) { return@withContext runCatching { From 43c9c6f65b24ffaae88d92a7afc9d8ce109b39db Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Tue, 26 Aug 2025 10:48:22 -0300 Subject: [PATCH 06/19] refactor: implement batch sync --- .../to/bitkit/repositories/ActivityRepo.kt | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt index df9f52712..3ff756187 100644 --- a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt @@ -13,6 +13,7 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeout import org.lightningdevkit.ldknode.PaymentDetails +import to.bitkit.data.AppDb import to.bitkit.data.CacheStore import to.bitkit.data.dto.InProgressTransfer import to.bitkit.data.dto.PendingBoostActivity @@ -33,6 +34,7 @@ class ActivityRepo @Inject constructor( private val coreService: CoreService, private val lightningRepo: LightningRepo, private val cacheStore: CacheStore, + private val db: AppDb, ) { var isSyncingLdkNodePayments = MutableStateFlow(false) private set @@ -54,10 +56,13 @@ class ActivityRepo @Inject constructor( return@withContext lightningRepo.getPayments() .onSuccess { payments -> Logger.debug("Got payments with success, syncing activities", context = TAG) - syncLdkNodePayments(payments = payments) + syncLdkNodePayments(payments = payments).onFailure { e -> + return@withContext Result.failure(e) + } updateActivitiesMetadata() boostPendingActivities() updateInProgressTransfers() + syncTagsMetaData() isSyncingLdkNodePayments = MutableStateFlow(false) return@withContext Result.success(Unit) }.onFailure { e -> @@ -83,33 +88,18 @@ class ActivityRepo @Inject constructor( /** * Business logic: Syncs LDK node payments with proper error handling and counting */ - private suspend fun syncLdkNodePayments(payments: List) { - var addedCount = 0 - var updatedCount = 0 - var latestCaughtError: Throwable? = null - - for (payment in payments) { - try { - val existentActivity = coreService.activity.getActivity(payment.id) - val wasUpdate = existentActivity != null - - // Delegate the actual sync to the service layer - coreService.activity.syncLdkNodePayments(listOf(payment)) - - if (wasUpdate) { - updatedCount++ - } else { - addedCount++ - } - } catch (e: Throwable) { - Logger.error("Error syncing LDK payment:", e, context = TAG) - latestCaughtError = e - } + private suspend fun syncLdkNodePayments( + payments: List, + ): Result, List>> { + //TODO Reduce getActivity calls + val paymentsToAdd = payments.filter { payment -> coreService.activity.getActivity(payment.id) != null } + val paymentsToUpdate = payments.filter { it !in paymentsToAdd } + return runCatching { + coreService.activity.syncLdkNodePayments(payments) + Pair(paymentsToAdd, paymentsToUpdate) + }.onFailure { e -> + Logger.error("Error syncing LDK payment:", e, context = TAG) } - - latestCaughtError?.let { throw it } - - Logger.info("Synced LDK payments - Added: $addedCount - Updated: $updatedCount", context = TAG) } /** @@ -331,6 +321,17 @@ class ActivityRepo @Inject constructor( } } + private suspend fun syncTagsMetaData() = withContext(context = bgDispatcher) { + // 1. Check for new activities + // 2. Filter receive + // 2.1 Lightning -> search for payment hash + // 2.2 OnChain -> get payment detail -> output -> search for address + // 3. Filter sent + // 3.1 Lightning -> search for payment hash + // 3.2 OnChain -> search for tx ID + // 4. Delete successfull addedd + } + private suspend fun updateInProgressTransfers() { cacheStore.data.first().inProgressTransfers.forEach { transfer -> getActivity(transfer.activityId).onSuccess { activity -> From 21735b1401c7546ad811da425843ace27d73abb2 Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Tue, 26 Aug 2025 11:11:06 -0300 Subject: [PATCH 07/19] feat: lightning tag sync --- .../to/bitkit/repositories/ActivityRepo.kt | 42 +++++++++++++++++-- 1 file changed, 38 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt index 3ff756187..c2af83fc4 100644 --- a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt @@ -13,6 +13,8 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeout import org.lightningdevkit.ldknode.PaymentDetails +import org.lightningdevkit.ldknode.PaymentDirection +import org.lightningdevkit.ldknode.PaymentKind import to.bitkit.data.AppDb import to.bitkit.data.CacheStore import to.bitkit.data.dto.InProgressTransfer @@ -56,13 +58,13 @@ class ActivityRepo @Inject constructor( return@withContext lightningRepo.getPayments() .onSuccess { payments -> Logger.debug("Got payments with success, syncing activities", context = TAG) - syncLdkNodePayments(payments = payments).onFailure { e -> + val syncResult = syncLdkNodePayments(payments = payments).onFailure { e -> return@withContext Result.failure(e) } updateActivitiesMetadata() boostPendingActivities() updateInProgressTransfers() - syncTagsMetaData() + syncResult.getOrNull()?.let { syncTagsMetaData(it.first) } isSyncingLdkNodePayments = MutableStateFlow(false) return@withContext Result.success(Unit) }.onFailure { e -> @@ -91,7 +93,7 @@ class ActivityRepo @Inject constructor( private suspend fun syncLdkNodePayments( payments: List, ): Result, List>> { - //TODO Reduce getActivity calls + // TODO Reduce getActivity calls val paymentsToAdd = payments.filter { payment -> coreService.activity.getActivity(payment.id) != null } val paymentsToUpdate = payments.filter { it !in paymentsToAdd } return runCatching { @@ -321,7 +323,9 @@ class ActivityRepo @Inject constructor( } } - private suspend fun syncTagsMetaData() = withContext(context = bgDispatcher) { + private suspend fun syncTagsMetaData( + newPayments: List, + ) = withContext(context = bgDispatcher) { // 1. Check for new activities // 2. Filter receive // 2.1 Lightning -> search for payment hash @@ -330,6 +334,36 @@ class ActivityRepo @Inject constructor( // 3.1 Lightning -> search for payment hash // 3.2 OnChain -> search for tx ID // 4. Delete successfull addedd + + runCatching { + if (db.tagMetadataDao().getAll().isEmpty()) return@withContext + + newPayments.forEach { payment -> + when (val kind = payment.kind) { + is PaymentKind.Bolt11 -> { + val paymentHash = kind.hash + db.tagMetadataDao().searchByPaymentHash(paymentHash = paymentHash)?.let { tagMetadata -> + addTagsToTransaction( + paymentHashOrTxId = paymentHash, + type = ActivityFilter.LIGHTNING, + txType = if (tagMetadata.isReceive) PaymentType.RECEIVED else PaymentType.SENT, + tags = tagMetadata.tags + ).onSuccess { + db.tagMetadataDao().deleteByPaymentHash(paymentHash = paymentHash) + } + } + } + + is PaymentKind.Onchain -> { + when (payment.direction) { + PaymentDirection.INBOUND -> TODO() + PaymentDirection.OUTBOUND -> TODO() + } + } + else -> Unit + } + } + } } private suspend fun updateInProgressTransfers() { From 00e7548362903419cf8a2f074c4d50ae88176558 Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Tue, 26 Aug 2025 11:17:51 -0300 Subject: [PATCH 08/19] feat: on-chain outbound sync --- .../java/to/bitkit/repositories/ActivityRepo.kt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt index c2af83fc4..70812f989 100644 --- a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt @@ -357,7 +357,18 @@ class ActivityRepo @Inject constructor( is PaymentKind.Onchain -> { when (payment.direction) { PaymentDirection.INBOUND -> TODO() - PaymentDirection.OUTBOUND -> TODO() + PaymentDirection.OUTBOUND -> { + db.tagMetadataDao().searchByTxId(kind.txid)?.let { tagMetadata -> + addTagsToTransaction( + paymentHashOrTxId = kind.txid, + type = ActivityFilter.ONCHAIN, + txType = if (tagMetadata.isReceive) PaymentType.RECEIVED else PaymentType.SENT, + tags = tagMetadata.tags + ).onSuccess { + db.tagMetadataDao().deleteByTxId(kind.txid) + } + } + } } } else -> Unit From cd4d1b9fa674e940d9f17f81ef36e1e22658087c Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Tue, 26 Aug 2025 11:33:19 -0300 Subject: [PATCH 09/19] feat: on-chain inbound sync --- .../java/to/bitkit/data/dao/TagMetadataDao.kt | 4 +-- .../to/bitkit/repositories/ActivityRepo.kt | 33 ++++++++++++------- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/app/src/main/java/to/bitkit/data/dao/TagMetadataDao.kt b/app/src/main/java/to/bitkit/data/dao/TagMetadataDao.kt index d2ee57b35..7ac7c2833 100644 --- a/app/src/main/java/to/bitkit/data/dao/TagMetadataDao.kt +++ b/app/src/main/java/to/bitkit/data/dao/TagMetadataDao.kt @@ -25,8 +25,8 @@ interface TagMetadataDao { suspend fun searchByTxId(txId: String): TagMetadataEntity? // Search by address - @Query("SELECT * FROM tag_metadata WHERE address = :address") - suspend fun searchByAddress(address: String): List + @Query("SELECT * FROM tag_metadata WHERE address = :address ORDER BY createdAt DESC LIMIT 1") + suspend fun searchByAddress(address: String): TagMetadataEntity? // Search by primary key (id) @Query("SELECT * FROM tag_metadata WHERE id = :id LIMIT 1") diff --git a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt index 70812f989..4cba491d6 100644 --- a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt @@ -24,6 +24,7 @@ import to.bitkit.di.BgDispatcher import to.bitkit.ext.matchesPaymentId import to.bitkit.ext.rawId import to.bitkit.services.CoreService +import to.bitkit.utils.AddressChecker import to.bitkit.utils.Logger import javax.inject.Inject import javax.inject.Singleton @@ -37,6 +38,7 @@ class ActivityRepo @Inject constructor( private val lightningRepo: LightningRepo, private val cacheStore: CacheStore, private val db: AppDb, + private val addressChecker: AddressChecker, ) { var isSyncingLdkNodePayments = MutableStateFlow(false) private set @@ -326,15 +328,6 @@ class ActivityRepo @Inject constructor( private suspend fun syncTagsMetaData( newPayments: List, ) = withContext(context = bgDispatcher) { - // 1. Check for new activities - // 2. Filter receive - // 2.1 Lightning -> search for payment hash - // 2.2 OnChain -> get payment detail -> output -> search for address - // 3. Filter sent - // 3.1 Lightning -> search for payment hash - // 3.2 OnChain -> search for tx ID - // 4. Delete successfull addedd - runCatching { if (db.tagMetadataDao().getAll().isEmpty()) return@withContext @@ -356,13 +349,30 @@ class ActivityRepo @Inject constructor( is PaymentKind.Onchain -> { when (payment.direction) { - PaymentDirection.INBOUND -> TODO() + PaymentDirection.INBOUND -> { + // TODO Temporary solution while whe ldk-node doesn't return the txId directly + runCatching { addressChecker.getTransaction(kind.txid) }.onSuccess { txDetails -> + txDetails.vout.firstOrNull()?.scriptpubkey_address?.let { + db.tagMetadataDao().searchByAddress(it) + }?.let { tagMetadata -> + addTagsToTransaction( + paymentHashOrTxId = kind.txid, + type = ActivityFilter.ONCHAIN, + txType = PaymentType.RECEIVED, + tags = tagMetadata.tags + ).onSuccess { + db.tagMetadataDao().deleteByTxId(kind.txid) + } + } + } + } + PaymentDirection.OUTBOUND -> { db.tagMetadataDao().searchByTxId(kind.txid)?.let { tagMetadata -> addTagsToTransaction( paymentHashOrTxId = kind.txid, type = ActivityFilter.ONCHAIN, - txType = if (tagMetadata.isReceive) PaymentType.RECEIVED else PaymentType.SENT, + txType = PaymentType.SENT, tags = tagMetadata.tags ).onSuccess { db.tagMetadataDao().deleteByTxId(kind.txid) @@ -371,6 +381,7 @@ class ActivityRepo @Inject constructor( } } } + else -> Unit } } From de10591d624a534260a5a3cbab4c7ed6bb92c743 Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Tue, 26 Aug 2025 11:40:24 -0300 Subject: [PATCH 10/19] refactor: remove unused code --- .../viewmodels/ActivityListViewModel.kt | 22 ------------------- 1 file changed, 22 deletions(-) diff --git a/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt index e6b70f977..5d2d22f10 100644 --- a/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt @@ -16,7 +16,6 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import to.bitkit.di.BgDispatcher import to.bitkit.repositories.ActivityRepo -import to.bitkit.repositories.WalletRepo import to.bitkit.services.CoreService import to.bitkit.services.LdkNodeEventBus import to.bitkit.ui.screens.wallets.activity.components.ActivityTab @@ -27,7 +26,6 @@ import javax.inject.Inject class ActivityListViewModel @Inject constructor( @BgDispatcher private val bgDispatcher: CoroutineDispatcher, private val coreService: CoreService, - private val walletRepo: WalletRepo, private val ldkNodeEventBus: LdkNodeEventBus, private val activityRepo: ActivityRepo, ) : ViewModel() { @@ -224,7 +222,6 @@ class ActivityListViewModel @Inject constructor( viewModelScope.launch { activityRepo.syncActivities().onSuccess { syncState() - syncActivityTags() }.onFailure { e -> Logger.error("Failed to sync ldk-node payments", e) } @@ -244,23 +241,4 @@ class ActivityListViewModel @Inject constructor( syncState() } } - - private fun syncActivityTags() { - viewModelScope.launch { - walletRepo.getAllInvoiceTags().onSuccess { invoiceTags -> - invoiceTags.forEach { tagEntity -> - tagEntity.paymentHash?.let { - activityRepo.addTagsToTransaction( - paymentHashOrTxId = it, - type = ActivityFilter.ALL, - txType = null, - tags = tagEntity.tags - ).onSuccess { - walletRepo.deleteInvoice(tagEntity.paymentHash) - } - } - } - } - } - } } From 732d12ec61e04f97f579e9fe1edefdae2199c08d Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Tue, 26 Aug 2025 11:42:51 -0300 Subject: [PATCH 11/19] refactor: remove unused code --- app/src/main/java/to/bitkit/repositories/WalletRepo.kt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/app/src/main/java/to/bitkit/repositories/WalletRepo.kt b/app/src/main/java/to/bitkit/repositories/WalletRepo.kt index 85323feb8..e198df582 100644 --- a/app/src/main/java/to/bitkit/repositories/WalletRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/WalletRepo.kt @@ -460,12 +460,6 @@ class WalletRepo @Inject constructor( } } - suspend fun getAllInvoiceTags(): Result> = withContext(bgDispatcher) { - return@withContext runCatching { - db.tagMetadataDao().getAll() - } - } - suspend fun searchInvoiceByPaymentHash(paymentHash: String): Result = withContext(bgDispatcher) { return@withContext try { val invoiceTag = From 5713e3c7130eb06981940e282cd494c52a6dfac8 Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Tue, 26 Aug 2025 12:19:59 -0300 Subject: [PATCH 12/19] refactor: remove unnecessary code --- .../to/bitkit/repositories/ActivityRepo.kt | 33 +++++++++++++++++++ .../java/to/bitkit/repositories/WalletRepo.kt | 3 +- .../java/to/bitkit/viewmodels/AppViewModel.kt | 29 ++++++---------- 3 files changed, 45 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt index 4cba491d6..c61332048 100644 --- a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt @@ -1,5 +1,6 @@ package to.bitkit.repositories +import androidx.room.PrimaryKey import com.synonym.bitkitcore.Activity import com.synonym.bitkitcore.Activity.Onchain import com.synonym.bitkitcore.ActivityFilter @@ -20,12 +21,15 @@ import to.bitkit.data.CacheStore import to.bitkit.data.dto.InProgressTransfer import to.bitkit.data.dto.PendingBoostActivity import to.bitkit.data.dto.TransferType +import to.bitkit.data.entities.TagMetadataEntity import to.bitkit.di.BgDispatcher import to.bitkit.ext.matchesPaymentId +import to.bitkit.ext.nowTimestamp import to.bitkit.ext.rawId import to.bitkit.services.CoreService import to.bitkit.utils.AddressChecker import to.bitkit.utils.Logger +import java.security.InvalidParameterException import javax.inject.Inject import javax.inject.Singleton @@ -562,6 +566,35 @@ class ActivityRepo @Inject constructor( } } + suspend fun saveTagsMetadata( + id: String, + paymentHash: String? = null, + txId: String? = null, + address: String, + isReceive: Boolean, + tags: List, + ): Result = withContext(bgDispatcher) { + return@withContext runCatching { + + if (tags.isEmpty()) throw InvalidParameterException("tags must not be empty") + + db.tagMetadataDao().saveTagMetadata( + tagMetadata = TagMetadataEntity( + id = id, + paymentHash = paymentHash, + txId = txId, + address = address, + isReceive = isReceive, + tags = tags, + createdAt = nowTimestamp().toEpochMilli() + ) + ) + }.onFailure { e -> + Logger.error("getAllAvailableTags error", e, context = TAG) + } + } + + // MARK: - Development/Testing Methods /** diff --git a/app/src/main/java/to/bitkit/repositories/WalletRepo.kt b/app/src/main/java/to/bitkit/repositories/WalletRepo.kt index e198df582..26652d2cd 100644 --- a/app/src/main/java/to/bitkit/repositories/WalletRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/WalletRepo.kt @@ -24,6 +24,7 @@ import to.bitkit.data.entities.TagMetadataEntity import to.bitkit.data.keychain.Keychain import to.bitkit.di.BgDispatcher import to.bitkit.env.Env +import to.bitkit.ext.nowTimestamp import to.bitkit.ext.toHex import to.bitkit.ext.totalNextOutboundHtlcLimitSats import to.bitkit.models.AddressModel @@ -451,7 +452,7 @@ class WalletRepo @Inject constructor( tags = tags, address = onChainAddress, isReceive = true, - createdAt = System.currentTimeMillis() + createdAt = nowTimestamp().toEpochMilli() ) ) } diff --git a/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt index 10bb66ed7..1b39744fe 100644 --- a/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt @@ -193,7 +193,6 @@ class AppViewModel @Inject constructor( try { when (event) { // TODO Create individual sheet for each type of event is Event.PaymentReceived -> { - handleTags(event) showNewTransactionSheet( NewTransactionSheetDetails( type = NewTransactionSheetType.LIGHTNING, @@ -260,16 +259,6 @@ class AppViewModel @Inject constructor( } } - private suspend fun handleTags(event: Event.PaymentReceived) { - val tags = walletRepo.searchInvoiceByPaymentHash(paymentHash = event.paymentHash).getOrNull()?.tags.orEmpty() - activityRepo.addTagsToTransaction( - paymentHashOrTxId = event.paymentHash, - type = ActivityFilter.LIGHTNING, - txType = PaymentType.RECEIVED, - tags = tags - ) - } - private fun checkGeoStatus() { viewModelScope.launch { try { @@ -927,10 +916,11 @@ class AppViewModel @Inject constructor( sendOnchain(validatedAddress.address, amount) .onSuccess { txId -> val tags = _sendUiState.value.selectedTags - activityRepo.addTagsToTransaction( - paymentHashOrTxId = txId, - type = ActivityFilter.ONCHAIN, - txType = PaymentType.SENT, + activityRepo.saveTagsMetadata( + id = txId, + txId = txId, + address = validatedAddress.address, + isReceive = false, tags = tags ) Logger.info("Onchain send result txid: $txId", context = TAG) @@ -966,10 +956,11 @@ class AppViewModel @Inject constructor( sendLightning(bolt11, paymentAmount).onSuccess { paymentHash -> Logger.info("Lightning send result payment hash: $paymentHash", context = TAG) val tags = _sendUiState.value.selectedTags - activityRepo.addTagsToTransaction( - paymentHashOrTxId = paymentHash, - type = ActivityFilter.LIGHTNING, - txType = PaymentType.SENT, + activityRepo.saveTagsMetadata( + id = paymentHash, + paymentHash = paymentHash, + address = _sendUiState.value.address, + isReceive = false, tags = tags ) setSendEffect(SendEffect.PaymentSuccess()) From 1d67ca266bcff5cd82b1521ae93f7298936eb7a2 Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Tue, 26 Aug 2025 14:21:36 -0300 Subject: [PATCH 13/19] chore: remove log --- app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt index 5d2d22f10..461c6ed8f 100644 --- a/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt @@ -222,8 +222,6 @@ class ActivityListViewModel @Inject constructor( viewModelScope.launch { activityRepo.syncActivities().onSuccess { syncState() - }.onFailure { e -> - Logger.error("Failed to sync ldk-node payments", e) } } } From 830363258658f2dc80b27c5f23f8c949726e9f32 Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Tue, 26 Aug 2025 14:22:08 -0300 Subject: [PATCH 14/19] fix: added activity filter --- .../to/bitkit/repositories/ActivityRepo.kt | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt index c61332048..2251272fb 100644 --- a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt @@ -70,7 +70,7 @@ class ActivityRepo @Inject constructor( updateActivitiesMetadata() boostPendingActivities() updateInProgressTransfers() - syncResult.getOrNull()?.let { syncTagsMetaData(it.first) } + syncResult.getOrNull()?.let { syncTagsMetaData(it) } isSyncingLdkNodePayments = MutableStateFlow(false) return@withContext Result.success(Unit) }.onFailure { e -> @@ -82,7 +82,7 @@ class ActivityRepo @Inject constructor( when (e) { is TimeoutCancellationException -> { isSyncingLdkNodePayments = MutableStateFlow(false) - Logger.error("Timeout waiting for sync to complete, forcing reset", e, context = TAG) + Logger.warn("Timeout waiting for sync to complete, forcing reset", context = TAG) } else -> { @@ -95,16 +95,18 @@ class ActivityRepo @Inject constructor( /** * Business logic: Syncs LDK node payments with proper error handling and counting + * @return a list with added payments */ private suspend fun syncLdkNodePayments( payments: List, - ): Result, List>> { + ): Result> { // TODO Reduce getActivity calls - val paymentsToAdd = payments.filter { payment -> coreService.activity.getActivity(payment.id) != null } + val paymentsToAdd = payments.filter { payment -> coreService.activity.getActivity(payment.id) == null } val paymentsToUpdate = payments.filter { it !in paymentsToAdd } return runCatching { coreService.activity.syncLdkNodePayments(payments) - Pair(paymentsToAdd, paymentsToUpdate) + Logger.debug("syncLdkNodePayments added ${paymentsToAdd.count()}, updated ${paymentsToUpdate.count()}") + paymentsToAdd }.onFailure { e -> Logger.error("Error syncing LDK payment:", e, context = TAG) } @@ -578,17 +580,19 @@ class ActivityRepo @Inject constructor( if (tags.isEmpty()) throw InvalidParameterException("tags must not be empty") + val entity = TagMetadataEntity( + id = id, + paymentHash = paymentHash, + txId = txId, + address = address, + isReceive = isReceive, + tags = tags, + createdAt = nowTimestamp().toEpochMilli() + ) db.tagMetadataDao().saveTagMetadata( - tagMetadata = TagMetadataEntity( - id = id, - paymentHash = paymentHash, - txId = txId, - address = address, - isReceive = isReceive, - tags = tags, - createdAt = nowTimestamp().toEpochMilli() - ) + tagMetadata = entity ) + Logger.debug("Tag metadata saved: $entity", context = TAG) }.onFailure { e -> Logger.error("getAllAvailableTags error", e, context = TAG) } From 17c9a0059ed412292ca10ee94df9de80ecaca0e5 Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Tue, 26 Aug 2025 14:22:31 -0300 Subject: [PATCH 15/19] fix: payment hash extraction --- .../java/to/bitkit/repositories/WalletRepo.kt | 30 ++++++++++--------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/to/bitkit/repositories/WalletRepo.kt b/app/src/main/java/to/bitkit/repositories/WalletRepo.kt index 26652d2cd..83b6ab2f9 100644 --- a/app/src/main/java/to/bitkit/repositories/WalletRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/WalletRepo.kt @@ -439,22 +439,25 @@ class WalletRepo @Inject constructor( try { deleteExpiredInvoices() val decoded = decode(bip21Invoice) - val paymentHashOrAddress = when (decoded) { + val paymentHash = when (decoded) { is Scanner.Lightning -> decoded.invoice.paymentHash.toHex() - is Scanner.OnChain -> decoded.extractLightningHashOrAddress() + is Scanner.OnChain -> decoded.extractLightningHash() else -> null } - paymentHashOrAddress?.let { + paymentHash?.let { + val entity = TagMetadataEntity( + id = paymentHash, + paymentHash = paymentHash, + tags = tags, + address = onChainAddress, + isReceive = true, + createdAt = nowTimestamp().toEpochMilli() + ) db.tagMetadataDao().saveTagMetadata( - tagMetadata = TagMetadataEntity( - id = paymentHashOrAddress, - tags = tags, - address = onChainAddress, - isReceive = true, - createdAt = nowTimestamp().toEpochMilli() - ) + tagMetadata = entity ) + Logger.debug("Tag metadata saved: $entity", context = TAG) } } catch (e: Throwable) { Logger.error("saveInvoice error", e, context = TAG) @@ -499,14 +502,13 @@ class WalletRepo @Inject constructor( } } - private suspend fun Scanner.OnChain.extractLightningHashOrAddress(): String { - val address = this.invoice.address - val lightningInvoice: String = this.invoice.params?.get("lightning") ?: address + private suspend fun Scanner.OnChain.extractLightningHash(): String? { + val lightningInvoice: String = this.invoice.params?.get("lightning") ?: return null val decoded = decode(lightningInvoice) return when (decoded) { is Scanner.Lightning -> decoded.invoice.paymentHash.toHex() - else -> address + else -> null } } From 4ac4c25c44e03185ed49d061827c7acb0f624f1c Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Tue, 26 Aug 2025 15:07:58 -0300 Subject: [PATCH 16/19] chore: add logs --- .../to/bitkit/repositories/ActivityRepo.kt | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt index 2251272fb..e08a47660 100644 --- a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt @@ -336,7 +336,7 @@ class ActivityRepo @Inject constructor( ) = withContext(context = bgDispatcher) { runCatching { if (db.tagMetadataDao().getAll().isEmpty()) return@withContext - + Logger.debug("syncTagsMetaData called") newPayments.forEach { payment -> when (val kind = payment.kind) { is PaymentKind.Bolt11 -> { @@ -348,6 +348,7 @@ class ActivityRepo @Inject constructor( txType = if (tagMetadata.isReceive) PaymentType.RECEIVED else PaymentType.SENT, tags = tagMetadata.tags ).onSuccess { + Logger.debug("Tags synced with success! $tagMetadata", context = TAG) db.tagMetadataDao().deleteByPaymentHash(paymentHash = paymentHash) } } @@ -358,18 +359,23 @@ class ActivityRepo @Inject constructor( PaymentDirection.INBOUND -> { // TODO Temporary solution while whe ldk-node doesn't return the txId directly runCatching { addressChecker.getTransaction(kind.txid) }.onSuccess { txDetails -> - txDetails.vout.firstOrNull()?.scriptpubkey_address?.let { - db.tagMetadataDao().searchByAddress(it) - }?.let { tagMetadata -> - addTagsToTransaction( - paymentHashOrTxId = kind.txid, - type = ActivityFilter.ONCHAIN, - txType = PaymentType.RECEIVED, - tags = tagMetadata.tags - ).onSuccess { - db.tagMetadataDao().deleteByTxId(kind.txid) + txDetails.vout.forEach { vOut -> + vOut.scriptpubkey_address?.let { + db.tagMetadataDao().searchByAddress(it) + }?.let { tagMetadata -> + addTagsToTransaction( + paymentHashOrTxId = kind.txid, + type = ActivityFilter.ONCHAIN, + txType = PaymentType.RECEIVED, + tags = tagMetadata.tags + ).onSuccess { + Logger.debug("Tags synced with success! $tagMetadata", context = TAG) + db.tagMetadataDao().deleteByTxId(kind.txid) + } } } + }.onFailure { + Logger.warn("Failed getting transaction detail", context = TAG) } } @@ -381,6 +387,7 @@ class ActivityRepo @Inject constructor( txType = PaymentType.SENT, tags = tagMetadata.tags ).onSuccess { + Logger.debug("Tags synced with success! $tagMetadata", context = TAG) db.tagMetadataDao().deleteByTxId(kind.txid) } } From 905924fb34a374fe16dea6283fbaed6a6d3e8483 Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Wed, 27 Aug 2025 08:39:44 -0300 Subject: [PATCH 17/19] chore: add logs --- .../to/bitkit/repositories/ActivityRepo.kt | 11 +++++--- .../java/to/bitkit/repositories/WalletRepo.kt | 26 +++++++++---------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt index cfedd7373..5e8839e65 100644 --- a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt @@ -340,13 +340,14 @@ class ActivityRepo @Inject constructor( is PaymentKind.Bolt11 -> { val paymentHash = kind.hash db.tagMetadataDao().searchByPaymentHash(paymentHash = paymentHash)?.let { tagMetadata -> + Logger.debug("Tags metadata found! $tagMetadata", context = TAG) addTagsToTransaction( paymentHashOrTxId = paymentHash, type = ActivityFilter.LIGHTNING, txType = if (tagMetadata.isReceive) PaymentType.RECEIVED else PaymentType.SENT, tags = tagMetadata.tags ).onSuccess { - Logger.debug("Tags synced with success! $tagMetadata", context = TAG) + Logger.debug("Tags synced with success!", context = TAG) db.tagMetadataDao().deleteByPaymentHash(paymentHash = paymentHash) } } @@ -355,14 +356,18 @@ class ActivityRepo @Inject constructor( is PaymentKind.Onchain -> { when (payment.direction) { PaymentDirection.INBOUND -> { - // TODO Temporary solution while whe ldk-node doesn't return the txId directly + // TODO Temporary solution while whe ldk-node doesn't return the address directly + Logger.debug("Fetching data for txId: ${kind.txid}", context = TAG) runCatching { addressChecker.getTransaction(kind.txid) }.onSuccess { txDetails -> + Logger.debug("Tx detail fetched with success: $txDetails", context = TAG) txDetails.vout.forEach { vOut -> vOut.scriptpubkey_address?.let { + Logger.debug("Extracted address: $it", context = TAG) db.tagMetadataDao().searchByAddress(it) }?.let { tagMetadata -> + Logger.debug("Tags metadata found! $tagMetadata", context = TAG) addTagsToTransaction( - paymentHashOrTxId = kind.txid, + paymentHashOrTxId = txDetails.txid, type = ActivityFilter.ONCHAIN, txType = PaymentType.RECEIVED, tags = tagMetadata.tags diff --git a/app/src/main/java/to/bitkit/repositories/WalletRepo.kt b/app/src/main/java/to/bitkit/repositories/WalletRepo.kt index 83b6ab2f9..8c95e9daa 100644 --- a/app/src/main/java/to/bitkit/repositories/WalletRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/WalletRepo.kt @@ -445,20 +445,18 @@ class WalletRepo @Inject constructor( else -> null } - paymentHash?.let { - val entity = TagMetadataEntity( - id = paymentHash, - paymentHash = paymentHash, - tags = tags, - address = onChainAddress, - isReceive = true, - createdAt = nowTimestamp().toEpochMilli() - ) - db.tagMetadataDao().saveTagMetadata( - tagMetadata = entity - ) - Logger.debug("Tag metadata saved: $entity", context = TAG) - } + val entity = TagMetadataEntity( + id = paymentHash ?: onChainAddress, + paymentHash = paymentHash, + tags = tags, + address = onChainAddress, + isReceive = true, + createdAt = nowTimestamp().toEpochMilli() + ) + db.tagMetadataDao().saveTagMetadata( + tagMetadata = entity + ) + Logger.debug("Tag metadata saved: $entity", context = TAG) } catch (e: Throwable) { Logger.error("saveInvoice error", e, context = TAG) } From dc113fdc170e6157a0e024049ccea1b88ee16561 Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Wed, 27 Aug 2025 10:14:02 -0300 Subject: [PATCH 18/19] chore: update tags async --- app/src/main/java/to/bitkit/repositories/ActivityRepo.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt index 5e8839e65..58f654b2c 100644 --- a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt @@ -6,10 +6,13 @@ import com.synonym.bitkitcore.ActivityFilter import com.synonym.bitkitcore.PaymentType import com.synonym.bitkitcore.SortDirection import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeout import org.lightningdevkit.ldknode.PaymentDetails @@ -44,6 +47,7 @@ class ActivityRepo @Inject constructor( private val addressChecker: AddressChecker, ) { val isSyncingLdkNodePayments = MutableStateFlow(false) + private val scope = CoroutineScope(bgDispatcher + SupervisorJob()) val inProgressTransfers = cacheStore.data.map { it.inProgressTransfers } @@ -65,10 +69,12 @@ class ActivityRepo @Inject constructor( val syncResult = syncLdkNodePayments(payments = payments).onFailure { e -> return@withContext Result.failure(e) } + scope.launch { + syncResult.getOrNull()?.let { syncTagsMetaData(it) } + } updateActivitiesMetadata() boostPendingActivities() updateInProgressTransfers() - syncResult.getOrNull()?.let { syncTagsMetaData(it) } isSyncingLdkNodePayments.value = false return@withContext Result.success(Unit) }.onFailure { e -> From 9e5b8976f8bdd192df78959144784de14f65f449 Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Wed, 27 Aug 2025 11:03:22 -0300 Subject: [PATCH 19/19] fix: get last activities instead of payments to reduce the amount of data --- .../to/bitkit/repositories/ActivityRepo.kt | 51 +++++++------------ 1 file changed, 18 insertions(+), 33 deletions(-) diff --git a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt index 58f654b2c..cedb68f2b 100644 --- a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt @@ -6,18 +6,13 @@ import com.synonym.bitkitcore.ActivityFilter import com.synonym.bitkitcore.PaymentType import com.synonym.bitkitcore.SortDirection import kotlinx.coroutines.CoroutineDispatcher -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.coroutines.withTimeout import org.lightningdevkit.ldknode.PaymentDetails -import org.lightningdevkit.ldknode.PaymentDirection -import org.lightningdevkit.ldknode.PaymentKind import to.bitkit.data.AppDb import to.bitkit.data.CacheStore import to.bitkit.data.dto.InProgressTransfer @@ -47,7 +42,6 @@ class ActivityRepo @Inject constructor( private val addressChecker: AddressChecker, ) { val isSyncingLdkNodePayments = MutableStateFlow(false) - private val scope = CoroutineScope(bgDispatcher + SupervisorJob()) val inProgressTransfers = cacheStore.data.map { it.inProgressTransfers } @@ -66,13 +60,11 @@ class ActivityRepo @Inject constructor( return@withContext lightningRepo.getPayments() .onSuccess { payments -> Logger.debug("Got payments with success, syncing activities", context = TAG) - val syncResult = syncLdkNodePayments(payments = payments).onFailure { e -> + syncLdkNodePayments(payments = payments).onFailure { e -> return@withContext Result.failure(e) } - scope.launch { - syncResult.getOrNull()?.let { syncTagsMetaData(it) } - } updateActivitiesMetadata() + syncTagsMetaData() boostPendingActivities() updateInProgressTransfers() isSyncingLdkNodePayments.value = false @@ -103,14 +95,9 @@ class ActivityRepo @Inject constructor( */ private suspend fun syncLdkNodePayments( payments: List, - ): Result> { - // TODO Reduce getActivity calls - val paymentsToAdd = payments.filter { payment -> coreService.activity.getActivity(payment.id) == null } - val paymentsToUpdate = payments.filter { it !in paymentsToAdd } + ): Result { return runCatching { coreService.activity.syncLdkNodePayments(payments) - Logger.debug("syncLdkNodePayments added ${paymentsToAdd.count()}, updated ${paymentsToUpdate.count()}") - paymentsToAdd }.onFailure { e -> Logger.error("Error syncing LDK payment:", e, context = TAG) } @@ -336,15 +323,15 @@ class ActivityRepo @Inject constructor( } private suspend fun syncTagsMetaData( - newPayments: List, ) = withContext(context = bgDispatcher) { runCatching { if (db.tagMetadataDao().getAll().isEmpty()) return@withContext + val lastActivities = getActivities(limit = 10u).getOrNull() ?: return@withContext Logger.debug("syncTagsMetaData called") - newPayments.forEach { payment -> - when (val kind = payment.kind) { - is PaymentKind.Bolt11 -> { - val paymentHash = kind.hash + lastActivities.forEach { activity -> + when (activity) { + is Activity.Lightning -> { + val paymentHash = activity.rawId() db.tagMetadataDao().searchByPaymentHash(paymentHash = paymentHash)?.let { tagMetadata -> Logger.debug("Tags metadata found! $tagMetadata", context = TAG) addTagsToTransaction( @@ -359,12 +346,12 @@ class ActivityRepo @Inject constructor( } } - is PaymentKind.Onchain -> { - when (payment.direction) { - PaymentDirection.INBOUND -> { + is Onchain -> { + when (activity.v1.txType) { + PaymentType.RECEIVED -> { // TODO Temporary solution while whe ldk-node doesn't return the address directly - Logger.debug("Fetching data for txId: ${kind.txid}", context = TAG) - runCatching { addressChecker.getTransaction(kind.txid) }.onSuccess { txDetails -> + Logger.debug("Fetching data for txId: ${activity.v1.txId}", context = TAG) + runCatching { addressChecker.getTransaction(activity.v1.txId) }.onSuccess { txDetails -> Logger.debug("Tx detail fetched with success: $txDetails", context = TAG) txDetails.vout.forEach { vOut -> vOut.scriptpubkey_address?.let { @@ -379,7 +366,7 @@ class ActivityRepo @Inject constructor( tags = tagMetadata.tags ).onSuccess { Logger.debug("Tags synced with success! $tagMetadata", context = TAG) - db.tagMetadataDao().deleteByTxId(kind.txid) + db.tagMetadataDao().deleteByTxId(activity.v1.txId) } } } @@ -388,23 +375,21 @@ class ActivityRepo @Inject constructor( } } - PaymentDirection.OUTBOUND -> { - db.tagMetadataDao().searchByTxId(kind.txid)?.let { tagMetadata -> + PaymentType.SENT -> { + db.tagMetadataDao().searchByTxId(activity.v1.txId)?.let { tagMetadata -> addTagsToTransaction( - paymentHashOrTxId = kind.txid, + paymentHashOrTxId = activity.v1.txId, type = ActivityFilter.ONCHAIN, txType = PaymentType.SENT, tags = tagMetadata.tags ).onSuccess { Logger.debug("Tags synced with success! $tagMetadata", context = TAG) - db.tagMetadataDao().deleteByTxId(kind.txid) + db.tagMetadataDao().deleteByTxId(activity.v1.txId) } } } } } - - else -> Unit } } }