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/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 4af8dca61..000000000 --- a/app/src/main/java/to/bitkit/data/dao/InvoiceTagDao.kt +++ /dev/null @@ -1,30 +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 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..7ac7c2833 --- /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 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") + 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/ActivityRepo.kt b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt index 943cd04af..cedb68f2b 100644 --- a/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/ActivityRepo.kt @@ -13,15 +13,20 @@ 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 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 @@ -33,6 +38,8 @@ class ActivityRepo @Inject constructor( private val coreService: CoreService, private val lightningRepo: LightningRepo, private val cacheStore: CacheStore, + private val db: AppDb, + private val addressChecker: AddressChecker, ) { val isSyncingLdkNodePayments = MutableStateFlow(false) @@ -53,8 +60,11 @@ 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() + syncTagsMetaData() boostPendingActivities() updateInProgressTransfers() isSyncingLdkNodePayments.value = false @@ -68,7 +78,7 @@ class ActivityRepo @Inject constructor( when (e) { is TimeoutCancellationException -> { isSyncingLdkNodePayments.value = 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 -> { @@ -81,34 +91,16 @@ 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) { - 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 { + return runCatching { + coreService.activity.syncLdkNodePayments(payments) + }.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) } /** @@ -117,7 +109,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()) { @@ -330,6 +322,79 @@ class ActivityRepo @Inject constructor( } } + private suspend fun syncTagsMetaData( + ) = withContext(context = bgDispatcher) { + runCatching { + if (db.tagMetadataDao().getAll().isEmpty()) return@withContext + val lastActivities = getActivities(limit = 10u).getOrNull() ?: return@withContext + Logger.debug("syncTagsMetaData called") + 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( + paymentHashOrTxId = paymentHash, + type = ActivityFilter.LIGHTNING, + txType = if (tagMetadata.isReceive) PaymentType.RECEIVED else PaymentType.SENT, + tags = tagMetadata.tags + ).onSuccess { + Logger.debug("Tags synced with success!", context = TAG) + db.tagMetadataDao().deleteByPaymentHash(paymentHash = paymentHash) + } + } + } + + 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: ${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 { + Logger.debug("Extracted address: $it", context = TAG) + db.tagMetadataDao().searchByAddress(it) + }?.let { tagMetadata -> + Logger.debug("Tags metadata found! $tagMetadata", context = TAG) + addTagsToTransaction( + paymentHashOrTxId = txDetails.txid, + type = ActivityFilter.ONCHAIN, + txType = PaymentType.RECEIVED, + tags = tagMetadata.tags + ).onSuccess { + Logger.debug("Tags synced with success! $tagMetadata", context = TAG) + db.tagMetadataDao().deleteByTxId(activity.v1.txId) + } + } + } + }.onFailure { + Logger.warn("Failed getting transaction detail", context = TAG) + } + } + + PaymentType.SENT -> { + db.tagMetadataDao().searchByTxId(activity.v1.txId)?.let { tagMetadata -> + addTagsToTransaction( + 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(activity.v1.txId) + } + } + } + } + } + } + } + } + } + private suspend fun updateInProgressTransfers() { cacheStore.data.first().inProgressTransfers.forEach { transfer -> getActivity(transfer.activityId).onSuccess { activity -> @@ -450,7 +515,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")) @@ -459,7 +524,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 } @@ -504,6 +569,37 @@ 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") + + val entity = TagMetadataEntity( + id = id, + paymentHash = paymentHash, + txId = txId, + address = address, + isReceive = isReceive, + tags = tags, + createdAt = nowTimestamp().toEpochMilli() + ) + db.tagMetadataDao().saveTagMetadata( + tagMetadata = entity + ) + Logger.debug("Tag metadata saved: $entity", context = TAG) + }.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 22492a2ee..fd9ae9a0b 100644 --- a/app/src/main/java/to/bitkit/repositories/WalletRepo.kt +++ b/app/src/main/java/to/bitkit/repositories/WalletRepo.kt @@ -21,10 +21,11 @@ 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 +import to.bitkit.ext.nowTimestamp import to.bitkit.ext.toHex import to.bitkit.ext.totalNextOutboundHtlcLimitSats import to.bitkit.models.AddressModel @@ -399,15 +400,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) @@ -431,37 +436,42 @@ 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 paymentHash = when (decoded) { + is Scanner.Lightning -> decoded.invoice.paymentHash.toHex() + is Scanner.OnChain -> decoded.extractLightningHash() + else -> null + } - paymentHashOrAddress?.let { - db.invoiceTagDao().saveInvoice( - invoiceTag = InvoiceTagEntity( - paymentHash = paymentHashOrAddress, - tags = tags, - createdAt = System.currentTimeMillis() - ) + 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) } - } catch (e: Throwable) { - Logger.error("saveInvoice error", e, context = TAG) } - } - 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) @@ -471,7 +481,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) } @@ -479,7 +489,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) } @@ -488,20 +498,19 @@ 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) } } - 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 } } diff --git a/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/ActivityListViewModel.kt index 018e98369..461c6ed8f 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.LightningRepo import to.bitkit.services.CoreService import to.bitkit.services.LdkNodeEventBus import to.bitkit.ui.screens.wallets.activity.components.ActivityTab @@ -27,9 +26,8 @@ import javax.inject.Inject class ActivityListViewModel @Inject constructor( @BgDispatcher private val bgDispatcher: CoroutineDispatcher, private val coreService: CoreService, - private val lightningRepo: LightningRepo, private val ldkNodeEventBus: LdkNodeEventBus, - private val activityRepo: ActivityRepo + private val activityRepo: ActivityRepo, ) : ViewModel() { private val _filteredActivities = MutableStateFlow?>(null) val filteredActivities = _filteredActivities.asStateFlow() @@ -224,8 +222,6 @@ class ActivityListViewModel @Inject constructor( viewModelScope.launch { activityRepo.syncActivities().onSuccess { syncState() - }.onFailure { e -> - Logger.error("Failed to sync ldk-node payments", e) } } } diff --git a/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt index d1540e62b..667ff447b 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.searchInvoice(txId = event.paymentHash).getOrNull()?.tags.orEmpty() - activityRepo.addTagsToTransaction( - paymentHashOrTxId = event.paymentHash, - type = ActivityFilter.LIGHTNING, - txType = PaymentType.RECEIVED, - tags = tags - ) - } - private fun checkGeoStatus() { viewModelScope.launch { try { @@ -940,10 +929,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) @@ -979,10 +969,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())