Skip to content

Commit a96c04d

Browse files
committed
feat: add boosted txids tracking and display parent txids in activity details
This PR adds boosted txids tracking and displays parent transaction IDs in the activity details, matching iOS PR #199. ## Changes - Added `boostTxIds: List<String>` field to `PendingBoostActivity` to store parent transaction IDs - **CPFP**: Append child transaction ID to parent's `boostTxIds` when boosting - **RBF**: Track parent chain (existing `boostTxIds` + current txId) in `PendingBoostActivity`. Store parent txIds when creating replacement, retrieve and apply when replacement syncs - **UI**: Display boosted transaction IDs in `ActivityDetailScreen` with labels "BOOSTED TRANSACTION {num} (CPFP)" or "BOOSTED TRANSACTION {num} (RBF)" based on transaction direction ### Implementation Details 1. **PendingBoostActivity DTO**: Added `boostTxIds` field to track parent transaction IDs for RBF 2. **BoostTransactionViewModel**: - Updated `cachePendingBoostActivity()` to track parent chain for RBF (existing boostTxIds + current txId) - Updated `handleCPFPUpdate()` to append child txId to parent's boostTxIds 3. **ActivityRepo**: Updated `boostPendingActivities()` to apply tracked boostTxIds when processing pending boosts 4. **ActivityDetailScreen**: Added UI section to display boosted transaction IDs with appropriate labels This matches the behavior implemented in bitkit-ios PR #199.
1 parent 84af35c commit a96c04d

File tree

4 files changed

+68
-6
lines changed

4 files changed

+68
-6
lines changed

app/src/main/java/to/bitkit/data/dto/PendingBoostActivity.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@ import kotlinx.serialization.Serializable
66
data class PendingBoostActivity(
77
val txId: String,
88
val updatedAt: ULong,
9-
val activityToDelete: String?
9+
val activityToDelete: String?,
10+
val boostTxIds: List<String> = emptyList()
1011
)

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,11 +428,14 @@ class ActivityRepo @Inject constructor(
428428
val updatedActivity = Activity.Onchain(
429429
v1 = newOnChainActivity.v1.copy(
430430
isBoosted = true,
431+
boostTxIds = pendingBoostActivity.boostTxIds,
431432
updatedAt = pendingBoostActivity.updatedAt
432433
)
433434
)
434435

435436
if (pendingBoostActivity.activityToDelete != null) {
437+
// RBF: Replace old activity with new one, applying parent chain boostTxIds
438+
Logger.debug("RBF: Replacing activity ${pendingBoostActivity.activityToDelete} with ${updatedActivity.v1.id}, boostTxIds: ${updatedActivity.v1.boostTxIds}", context = TAG)
436439
replaceActivity(
437440
id = updatedActivity.v1.id,
438441
activity = updatedActivity,
@@ -441,6 +444,7 @@ class ActivityRepo @Inject constructor(
441444
cacheStore.removeActivityFromPendingBoost(pendingBoostActivity)
442445
}
443446
} else {
447+
// CPFP: Update existing activity (though CPFP is handled immediately now)
444448
updateActivity(
445449
id = updatedActivity.v1.id,
446450
activity = updatedActivity

app/src/main/java/to/bitkit/ui/screens/wallets/activity/ActivityDetailScreen.kt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,45 @@ private fun ActivityDetailContent(
244244
StatusSection(item)
245245
HorizontalDivider(modifier = Modifier.padding(top = 16.dp))
246246

247+
// Boosted transaction IDs section (for onchain activities with boostTxIds)
248+
if (item is Activity.Onchain && item.v1.boostTxIds.isNotEmpty()) {
249+
item.v1.boostTxIds.forEachIndexed { index, boostTxId ->
250+
val labelRes = if (item.v1.txType == PaymentType.RECEIVED) {
251+
R.string.wallet__activity_boosted_cpfp
252+
} else {
253+
R.string.wallet__activity_boosted_rbf
254+
}
255+
256+
Column(
257+
modifier = Modifier
258+
.fillMaxWidth()
259+
.clickableAlpha(
260+
onClick = copyToClipboard(boostTxId) {
261+
onCopy(it)
262+
}
263+
)
264+
) {
265+
Caption13Up(
266+
text = stringResource(labelRes, index + 1),
267+
color = Colors.White64,
268+
modifier = Modifier.padding(top = 16.dp, bottom = 8.dp)
269+
)
270+
Row(verticalAlignment = Alignment.CenterVertically) {
271+
Icon(
272+
painter = painterResource(R.drawable.ic_timer_alt),
273+
contentDescription = null,
274+
tint = accentColor,
275+
modifier = Modifier.size(16.dp)
276+
)
277+
Spacer(modifier = Modifier.width(4.dp))
278+
BodySSB(text = boostTxId.ellipsisMiddle(24))
279+
}
280+
Spacer(modifier = Modifier.height(16.dp))
281+
HorizontalDivider()
282+
}
283+
}
284+
}
285+
247286
// Timestamp section: date and time
248287
Row(
249288
horizontalArrangement = Arrangement.spacedBy(16.dp),

app/src/main/java/to/bitkit/ui/sheets/BoostTransactionViewModel.kt

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,8 @@ class BoostTransactionViewModel @Inject constructor(
203203
destinationAddress = walletRepo.getOnchainAddress(),
204204
).fold(
205205
onSuccess = { newTxId ->
206+
// For CPFP, immediately update parent with child txId
207+
Logger.debug("CPFP successful. Appending child txId $newTxId to parent's boostTxIds", context = TAG)
206208
handleBoostSuccess(newTxId, isRBF = false)
207209
},
208210
onFailure = { error ->
@@ -283,7 +285,7 @@ class BoostTransactionViewModel @Inject constructor(
283285
/**
284286
* Updates activity based on boost type:
285287
* - RBF: Updates current activity with boost data, then replaces with new transaction
286-
* - CPFP: Simply updates the current activity
288+
* - CPFP: Updates parent activity by appending child txId to boostTxIds
287289
*/
288290
private suspend fun updateActivity(newTxId: Txid, isRBF: Boolean): Result<Unit> {
289291
Logger.debug("Updating activity for txId: $newTxId. isRBF: $isRBF", context = TAG)
@@ -294,21 +296,25 @@ class BoostTransactionViewModel @Inject constructor(
294296
return if (isRBF) {
295297
handleRBFUpdate(newTxId, currentActivity)
296298
} else {
297-
handleCPFPUpdate(currentActivity)
299+
handleCPFPUpdate(currentActivity, newTxId)
298300
}
299301
}
300302

301303
/**
302-
* Handles CPFP (Child Pays For Parent) update by simply updating the current activity
304+
* Handles CPFP (Child Pays For Parent) update by appending child txId to parent's boostTxIds
303305
*/
304-
private suspend fun handleCPFPUpdate(currentActivity: OnchainActivity): Result<Unit> {
306+
private suspend fun handleCPFPUpdate(currentActivity: OnchainActivity, childTxId: Txid): Result<Unit> {
307+
// For CPFP, append the child transaction ID to the parent's boostTxIds
305308
val updatedActivity = Activity.Onchain(
306309
v1 = currentActivity.copy(
307310
isBoosted = true,
311+
boostTxIds = currentActivity.boostTxIds + childTxId,
308312
updatedAt = nowTimestamp().toEpochMilli().toULong()
309313
)
310314
)
311315

316+
Logger.debug("CPFP: Updated parent activity with child txId $childTxId. boostTxIds: ${updatedActivity.v1.boostTxIds}", context = TAG)
317+
312318
return activityRepo.updateActivity(
313319
id = updatedActivity.v1.id,
314320
activity = updatedActivity
@@ -412,13 +418,25 @@ class BoostTransactionViewModel @Inject constructor(
412418

413419
/**
414420
* Caches activity data for pending boost operation
421+
* For RBF: stores parent chain (existing boostTxIds + current txId)
422+
* For CPFP: stores empty list (child tx is added to parent's boostTxIds during sync)
415423
*/
416424
private suspend fun cachePendingBoostActivity(newTxId: Txid, activityToDelete: String?) {
425+
val currentActivity = activity?.v1
426+
val boostTxIds = if (activityToDelete != null && currentActivity != null) {
427+
// RBF: Track full parent chain (existing boostTxIds + current txId being replaced)
428+
currentActivity.boostTxIds + currentActivity.txId
429+
} else {
430+
// CPFP: No parent tracking needed
431+
emptyList()
432+
}
433+
417434
activityRepo.addActivityToPendingBoost(
418435
PendingBoostActivity(
419436
txId = newTxId,
420437
updatedAt = nowTimestamp().toEpochMilli().toULong(),
421-
activityToDelete = activityToDelete
438+
activityToDelete = activityToDelete,
439+
boostTxIds = boostTxIds
422440
)
423441
)
424442
}

0 commit comments

Comments
 (0)