Skip to content

Commit 74de74c

Browse files
authored
Merge pull request #267 from synonymdev/feat/activities-meta-data
Activities metadata
2 parents 1c077ec + 09dcc49 commit 74de74c

File tree

8 files changed

+210
-29
lines changed

8 files changed

+210
-29
lines changed

app/src/main/java/to/bitkit/data/CacheStore.kt

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import kotlinx.coroutines.flow.first
1010
import kotlinx.coroutines.flow.map
1111
import kotlinx.serialization.Serializable
1212
import to.bitkit.data.dto.PendingBoostActivity
13+
import to.bitkit.data.dto.TransactionMetadata
1314
import to.bitkit.data.serializers.AppCacheSerializer
1415
import to.bitkit.models.BackupCategory
1516
import to.bitkit.models.BackupItemStatus
@@ -113,6 +114,22 @@ class CacheStore @Inject constructor(
113114
}
114115
}
115116

117+
suspend fun addTransactionMetadata(item: TransactionMetadata) {
118+
if (item.txId in store.data.first().transactionsMetadata.map { it.txId }) return
119+
120+
store.updateData {
121+
it.copy(transactionsMetadata = it.transactionsMetadata + item)
122+
}
123+
}
124+
125+
suspend fun removeTransactionMetadata(item: TransactionMetadata) {
126+
if (item.txId !in store.data.first().transactionsMetadata.map { it.txId }) return
127+
128+
store.updateData {
129+
it.copy(transactionsMetadata = it.transactionsMetadata - item)
130+
}
131+
}
132+
116133
suspend fun reset() {
117134
store.updateData { AppCacheData() }
118135
Logger.info("Deleted all app cached data.")
@@ -135,4 +152,5 @@ data class AppCacheData(
135152
val deletedActivities: List<String> = listOf(),
136153
val activitiesPendingDelete: List<String> = listOf(),
137154
val pendingBoostActivities: List<PendingBoostActivity> = listOf(),
155+
val transactionsMetadata: List<TransactionMetadata> = listOf(),
138156
)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package to.bitkit.data.dto
2+
3+
import kotlinx.serialization.Serializable
4+
5+
@Serializable
6+
data class TransactionMetadata(
7+
val txId: String,
8+
val feeRate: UInt,
9+
val address: String,
10+
val isTransfer: Boolean,
11+
val channelId: String?,
12+
val transferTxId: String?,
13+
)

app/src/main/java/to/bitkit/data/serializers/AppCacheSerializer.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package to.bitkit.data.serializers
33
import androidx.datastore.core.Serializer
44
import kotlinx.serialization.SerializationException
55
import to.bitkit.data.AppCacheData
6-
import to.bitkit.data.SettingsData
76
import to.bitkit.di.json
87
import to.bitkit.utils.Logger
98
import java.io.InputStream

app/src/main/java/to/bitkit/repositories/ActivityRepo.kt

Lines changed: 69 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package to.bitkit.repositories
22

33
import com.synonym.bitkitcore.Activity
4+
import com.synonym.bitkitcore.Activity.Onchain
45
import com.synonym.bitkitcore.ActivityFilter
56
import com.synonym.bitkitcore.PaymentType
67
import com.synonym.bitkitcore.SortDirection
@@ -46,6 +47,7 @@ class ActivityRepo @Inject constructor(
4647
.onSuccess { payments ->
4748
Logger.debug("Got payments with success, syncing activities", context = TAG)
4849
syncLdkNodePayments(payments = payments)
50+
updateActivitiesMetadata()
4951
boostPendingActivities()
5052
isSyncingLdkNodePayments = false
5153
return@withContext Result.success(Unit)
@@ -99,7 +101,11 @@ class ActivityRepo @Inject constructor(
99101
type: ActivityFilter,
100102
txType: PaymentType,
101103
): Result<Activity> = withContext(bgDispatcher) {
102-
if (paymentHashOrTxId.isEmpty()) return@withContext Result.failure(IllegalArgumentException("paymentHashOrTxId is empty"))
104+
if (paymentHashOrTxId.isEmpty()) {
105+
return@withContext Result.failure(
106+
IllegalArgumentException("paymentHashOrTxId is empty")
107+
)
108+
}
103109

104110
return@withContext try {
105111
suspend fun findActivity(): Activity? = getActivities(
@@ -131,10 +137,15 @@ class ActivityRepo @Inject constructor(
131137
}
132138
}
133139

134-
if (activity != null) Result.success(activity) else Result.failure(IllegalStateException("Activity not found"))
140+
if (activity != null) {
141+
Result.success(activity)
142+
} else {
143+
Result.failure(IllegalStateException("Activity not found"))
144+
}
135145
} catch (e: Exception) {
136146
Logger.error(
137-
"findActivityByPaymentId error. Parameters:\n paymentHashOrTxId:$paymentHashOrTxId type:$type txType:$txType",
147+
"findActivityByPaymentId error. Parameters:" +
148+
"\n paymentHashOrTxId:$paymentHashOrTxId type:$type txType:$txType",
138149
context = TAG
139150
)
140151
Result.failure(e)
@@ -158,7 +169,15 @@ class ActivityRepo @Inject constructor(
158169
coreService.activity.get(filter, txType, tags, search, minDate, maxDate, limit, sortDirection)
159170
}.onFailure { e ->
160171
Logger.error(
161-
"getActivities error. Parameters:\nfilter:$filter txType:$txType tags:$tags search:$search minDate:$minDate maxDate:$maxDate limit:$limit sortDirection:$sortDirection",
172+
"getActivities error. Parameters:" +
173+
"\nfilter:$filter " +
174+
"txType:$txType " +
175+
"tags:$tags " +
176+
"search:$search " +
177+
"minDate:$minDate " +
178+
"maxDate:$maxDate " +
179+
"limit:$limit " +
180+
"sortDirection:$sortDirection",
162181
e = e,
163182
context = TAG
164183
)
@@ -183,12 +202,16 @@ class ActivityRepo @Inject constructor(
183202
suspend fun updateActivity(
184203
id: String,
185204
activity: Activity,
186-
forceUpdate: Boolean = false
205+
forceUpdate: Boolean = false,
187206
): Result<Unit> = withContext(bgDispatcher) {
188207
return@withContext runCatching {
189208
if (id in cacheStore.data.first().deletedActivities && !forceUpdate) {
190209
Logger.debug("Activity $id was deleted", context = TAG)
191-
return@withContext Result.failure(Exception("Activity $id was deleted. If you want update it, set forceUpdate as true"))
210+
return@withContext Result.failure(
211+
Exception(
212+
"Activity $id was deleted. If you want update it, set forceUpdate as true"
213+
)
214+
)
192215
}
193216
coreService.activity.update(id, activity)
194217
}.onFailure { e ->
@@ -197,7 +220,8 @@ class ActivityRepo @Inject constructor(
197220
}
198221

199222
/**
200-
* Updates an activity and delete other one. In case of failure in the update or deletion, the data will be cached to try again on the next sync
223+
* Updates an activity and delete other one. In case of failure in the update or deletion, the data will be cached
224+
* to try again on the next sync
201225
*/
202226
suspend fun replaceActivity(
203227
id: String,
@@ -224,7 +248,8 @@ class ActivityRepo @Inject constructor(
224248
},
225249
onFailure = { e ->
226250
Logger.error(
227-
"Update activity fail. Parameters: id:$id, activityIdToDelete:$activityIdToDelete activity:$activity",
251+
"Update activity fail. Parameters: id:$id, " +
252+
"activityIdToDelete:$activityIdToDelete activity:$activity",
228253
e = e,
229254
context = TAG
230255
)
@@ -241,6 +266,41 @@ class ActivityRepo @Inject constructor(
241266
}
242267
}
243268

269+
private suspend fun updateActivitiesMetadata() = withContext(bgDispatcher) {
270+
cacheStore.data.first().transactionsMetadata.forEach { activityMetaData ->
271+
findActivityByPaymentId(
272+
paymentHashOrTxId = activityMetaData.txId,
273+
type = ActivityFilter.ALL,
274+
txType = PaymentType.SENT
275+
).onSuccess { activityToUpdate ->
276+
Logger.debug("updateActivitiesMetaData = Activity found: ${activityToUpdate.rawId()}", context = TAG)
277+
278+
when (activityToUpdate) {
279+
is Activity.Onchain -> {
280+
val updatedActivity = Onchain(
281+
v1 = activityToUpdate.v1.copy(
282+
feeRate = activityMetaData.feeRate.toULong(),
283+
address = activityMetaData.address,
284+
isTransfer = activityMetaData.isTransfer,
285+
channelId = activityMetaData.channelId,
286+
transferTxId = activityMetaData.transferTxId
287+
)
288+
)
289+
290+
updateActivity(
291+
id = updatedActivity.v1.id,
292+
activity = updatedActivity
293+
).onSuccess {
294+
cacheStore.removeTransactionMetadata(activityMetaData)
295+
}
296+
}
297+
298+
is Activity.Lightning -> Unit
299+
}
300+
}
301+
}
302+
}
303+
244304
private suspend fun boostPendingActivities() = withContext(bgDispatcher) {
245305
cacheStore.data.first().pendingBoostActivities.forEach { pendingBoostActivity ->
246306
findActivityByPaymentId(
@@ -353,7 +413,7 @@ class ActivityRepo @Inject constructor(
353413
paymentHashOrTxId: String,
354414
type: ActivityFilter,
355415
txType: PaymentType,
356-
tags: List<String>
416+
tags: List<String>,
357417
): Result<Unit> = withContext(bgDispatcher) {
358418

359419
if (tags.isEmpty()) return@withContext Result.failure(IllegalArgumentException("No tags selected"))

app/src/main/java/to/bitkit/repositories/LightningRepo.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import org.lightningdevkit.ldknode.Txid
2727
import org.lightningdevkit.ldknode.UserChannelId
2828
import to.bitkit.data.CacheStore
2929
import to.bitkit.data.SettingsStore
30+
import to.bitkit.data.dto.TransactionMetadata
3031
import to.bitkit.data.keychain.Keychain
3132
import to.bitkit.di.BgDispatcher
3233
import to.bitkit.env.Env
@@ -495,6 +496,8 @@ class LightningRepo @Inject constructor(
495496
sats: ULong,
496497
speed: TransactionSpeed? = null,
497498
utxosToSpend: List<SpendableUtxo>? = null,
499+
isTransfer: Boolean = false,
500+
channelId: String? = null,
498501
): Result<Txid> =
499502
executeWhenNodeRunning("Send on-chain") {
500503
val transactionSpeed = speed ?: settingsStore.data.first().defaultTransactionSpeed
@@ -514,6 +517,16 @@ class LightningRepo @Inject constructor(
514517
satsPerVByte = satsPerVByte,
515518
utxosToSpend = finalUtxosToSpend,
516519
)
520+
cacheStore.addTransactionMetadata(
521+
TransactionMetadata(
522+
txId = txId,
523+
feeRate = satsPerVByte,
524+
address = address,
525+
isTransfer = isTransfer,
526+
channelId = channelId,
527+
transferTxId = txId.takeIf { isTransfer }
528+
)
529+
)
517530
syncState()
518531
Result.success(txId)
519532
}

app/src/main/java/to/bitkit/services/CoreService.kt

Lines changed: 36 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ import org.lightningdevkit.ldknode.PaymentKind
5252
import org.lightningdevkit.ldknode.PaymentStatus
5353
import to.bitkit.async.ServiceQueue
5454
import to.bitkit.data.CacheStore
55-
import to.bitkit.data.SettingsStore
5655
import to.bitkit.env.Env
5756
import to.bitkit.ext.amountSats
5857
import to.bitkit.models.LnPeer
@@ -70,7 +69,6 @@ import kotlin.random.Random
7069
class CoreService @Inject constructor(
7170
private val lightningService: LightningService,
7271
private val httpClient: HttpClient,
73-
private val settingsStore: SettingsStore,
7472
private val cacheStore: CacheStore,
7573
) {
7674
private var walletIndex: Int = 0
@@ -282,8 +280,9 @@ class ActivityService(
282280
}
283281

284282
val existingActivity = getActivityById(payment.id)
285-
if (existingActivity != null && existingActivity is Activity.Onchain && (existingActivity.v1.updatedAt
286-
?: 0u) > payment.latestUpdateTimestamp
283+
if (existingActivity != null &&
284+
existingActivity is Activity.Onchain &&
285+
(existingActivity.v1.updatedAt ?: 0u) > payment.latestUpdateTimestamp
287286
) {
288287
continue
289288
}
@@ -302,7 +301,7 @@ class ActivityService(
302301
value = payment.amountSats ?: 0u,
303302
fee = (payment.feePaidMsat ?: 0u) / 1000u,
304303
feeRate = 1u, // TODO: get from somewhere
305-
address = "todo_find_address", // TODO: find address
304+
address = "Loading...", // TODO: find address
306305
confirmed = isConfirmed,
307306
timestamp = timestamp,
308307
isBoosted = false,
@@ -330,23 +329,41 @@ class ActivityService(
330329

331330
is PaymentKind.Bolt11 -> {
332331
// Skip pending inbound payments, just means they created an invoice
333-
if (payment.status == PaymentStatus.PENDING && payment.direction == PaymentDirection.INBOUND) {
332+
if (
333+
payment.status == PaymentStatus.PENDING &&
334+
payment.direction == PaymentDirection.INBOUND
335+
) {
336+
continue
337+
}
338+
339+
val existingActivity = getActivityById(payment.id)
340+
if (
341+
existingActivity as? Activity.Lightning != null &&
342+
(existingActivity.v1.updatedAt ?: 0u) > payment.latestUpdateTimestamp
343+
) {
334344
continue
335345
}
336346

337-
val ln = LightningActivity(
338-
id = payment.id,
339-
txType = payment.direction.toPaymentType(),
340-
status = state,
341-
value = payment.amountSats ?: 0u,
342-
fee = (payment.feePaidMsat ?: 0u) / 1000u,
343-
invoice = kind.bolt11 ?: "Loading…",
344-
message = kind.description.orEmpty(),
345-
timestamp = payment.latestUpdateTimestamp,
346-
preimage = kind.preimage,
347-
createdAt = payment.latestUpdateTimestamp,
348-
updatedAt = payment.latestUpdateTimestamp,
349-
)
347+
val ln = if (existingActivity is Activity.Lightning) {
348+
existingActivity.v1.copy(
349+
updatedAt = payment.latestUpdateTimestamp,
350+
status = state
351+
)
352+
} else {
353+
LightningActivity(
354+
id = payment.id,
355+
txType = payment.direction.toPaymentType(),
356+
status = state,
357+
value = payment.amountSats ?: 0u,
358+
fee = (payment.feePaidMsat ?: 0u) / 1000u,
359+
invoice = kind.bolt11 ?: "Loading...",
360+
message = kind.description.orEmpty(),
361+
timestamp = payment.latestUpdateTimestamp,
362+
preimage = kind.preimage,
363+
createdAt = payment.latestUpdateTimestamp,
364+
updatedAt = payment.latestUpdateTimestamp,
365+
)
366+
}
350367

351368
if (getActivityById(payment.id) != null) {
352369
updateActivity(payment.id, Activity.Lightning(ln))

app/src/main/java/to/bitkit/viewmodels/TransferViewModel.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ class TransferViewModel @Inject constructor(
172172
address = order.payment.onchain.address,
173173
sats = order.feeSat,
174174
speed = speed,
175+
isTransfer = true,
176+
channelId = order.channel?.shortChannelId,
175177
)
176178
.onSuccess { txId ->
177179
cacheStore.addPaidOrder(orderId = order.id, txId = txId)

0 commit comments

Comments
 (0)