@@ -33,12 +33,14 @@ import org.lightningdevkit.ldknode.TransactionDetails
3333import to.bitkit.data.CacheStore
3434import to.bitkit.data.dto.PendingBoostActivity
3535import to.bitkit.di.BgDispatcher
36+ import to.bitkit.di.IoDispatcher
3637import to.bitkit.ext.amountOnClose
3738import to.bitkit.ext.matchesPaymentId
3839import to.bitkit.ext.nowMillis
3940import to.bitkit.ext.nowTimestamp
4041import to.bitkit.ext.rawId
4142import to.bitkit.models.ActivityBackupV1
43+ import to.bitkit.models.PubkyPublicKeyFormat
4244import to.bitkit.services.CoreService
4345import to.bitkit.utils.AppError
4446import to.bitkit.utils.Logger
@@ -55,6 +57,7 @@ private const val MS_SYNC_TIMEOUT = 40_000L
5557@Singleton
5658class ActivityRepo @Inject constructor(
5759 @BgDispatcher private val bgDispatcher : CoroutineDispatcher ,
60+ @IoDispatcher private val ioDispatcher : CoroutineDispatcher ,
5861 private val coreService : CoreService ,
5962 private val lightningRepo : LightningRepo ,
6063 private val blocktankRepo : BlocktankRepo ,
@@ -338,6 +341,79 @@ class ActivityRepo @Inject constructor(
338341 }
339342 }
340343
344+ suspend fun contactActivities (publicKey : String ): Result <List <Activity >> = withContext(ioDispatcher) {
345+ runCatching {
346+ val normalizedKey = PubkyPublicKeyFormat .normalized(publicKey) ? : publicKey
347+ getActivities(
348+ filter = ActivityFilter .ALL ,
349+ sortDirection = SortDirection .DESC ,
350+ ).getOrThrow().filter { activity ->
351+ when (activity) {
352+ is Activity .Lightning -> PubkyPublicKeyFormat .matches(activity.v1.contact, normalizedKey)
353+ is Activity .Onchain -> PubkyPublicKeyFormat .matches(activity.v1.contact, normalizedKey)
354+ }
355+ }
356+ }.onFailure {
357+ Logger .error(" Failed to load contact activities for '$publicKey '" , it, context = TAG )
358+ }
359+ }
360+
361+ suspend fun setContact (
362+ contactPublicKey : String ,
363+ forPaymentId : String ,
364+ syncLdkPayments : Boolean = true,
365+ ): Result <Unit > = withContext(ioDispatcher) {
366+ runCatching {
367+ if (syncLdkPayments) {
368+ lightningRepo.getPayments().onSuccess {
369+ syncLdkNodePayments(it).getOrThrow()
370+ }.getOrThrow()
371+ }
372+
373+ val normalizedKey = PubkyPublicKeyFormat .normalized(contactPublicKey) ? : contactPublicKey
374+ val activity = findActivityForPaymentId(forPaymentId, syncLdkPayments)
375+ if (activity == null ) {
376+ Logger .warn(
377+ " Skipped setting contact for payment '$forPaymentId ' because activity was not found" ,
378+ context = TAG ,
379+ )
380+ return @runCatching
381+ }
382+ if (PubkyPublicKeyFormat .matches(activity.contact(), normalizedKey)) {
383+ return @runCatching
384+ }
385+
386+ val updatedAt = nowTimestamp().epochSecond.toULong()
387+ val updatedActivity = activity.withContact(normalizedKey, updatedAt)
388+ updateActivity(updatedActivity.rawId(), updatedActivity).getOrThrow()
389+ }.onFailure {
390+ Logger .error(" Failed to set contact for payment '$forPaymentId '" , it, context = TAG )
391+ }
392+ }
393+
394+ private suspend fun findActivityForPaymentId (forPaymentId : String , syncLdkPayments : Boolean ): Activity ? {
395+ val activity = getActivityByPaymentId(forPaymentId)
396+ if (activity != null ) return activity
397+ if (! syncLdkPayments) return null
398+
399+ syncActivities().getOrThrow()
400+ return getActivityByPaymentId(forPaymentId)
401+ }
402+
403+ private suspend fun getActivityByPaymentId (forPaymentId : String ): Activity ? =
404+ coreService.activity.getActivity(forPaymentId)
405+ ? : getOnchainActivityByTxId(forPaymentId)?.let { Activity .Onchain (it) }
406+
407+ private fun Activity.contact (): String? = when (this ) {
408+ is Activity .Lightning -> v1.contact
409+ is Activity .Onchain -> v1.contact
410+ }
411+
412+ private fun Activity.withContact (normalizedKey : String , updatedAt : ULong ): Activity = when (this ) {
413+ is Activity .Lightning -> Activity .Lightning (v1.copy(contact = normalizedKey, updatedAt = updatedAt))
414+ is Activity .Onchain -> Activity .Onchain (v1.copy(contact = normalizedKey, updatedAt = updatedAt))
415+ }
416+
341417 suspend fun getClosedChannels (
342418 sortDirection : SortDirection = SortDirection .ASC ,
343419 ): Result <List <ClosedChannelDetails >> = withContext(bgDispatcher) {
@@ -515,6 +591,7 @@ class ActivityRepo @Inject constructor(
515591 message = " " ,
516592 timestamp = now,
517593 preimage = null ,
594+ contact = null ,
518595 createdAt = now,
519596 updatedAt = null ,
520597 seenAt = null ,
0 commit comments