Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Bitkit/Resources/Localization/en.lproj/Localizable.strings
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,8 @@
"wallet__activity_output" = "{count, plural, one {OUTPUT} other {OUTPUTS (#)}}";
"wallet__activity_boosted_cpfp" = "BOOSTED TRANSACTION {num} (CPFP)";
"wallet__activity_boosted_rbf" = "BOOSTED TRANSACTION {num} (RBF)";
"wallet__activity_boost_fee" = "Boost Fee";
"wallet__activity_boost_fee_description" = "Boosted incoming transaction";
"wallet__activity_explorer" = "Open Block Explorer";
"wallet__activity_successful" = "Successful";
"wallet__activity_invoice_note" = "Invoice note";
Expand Down
9 changes: 9 additions & 0 deletions Bitkit/Services/CoreService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,15 @@ class ActivityService {
return doesExistMap
}

func isCpfpChildTransaction(txId: String) async -> Bool {
guard await getTxIdsInBoostTxIds().contains(txId),
let activity = try? await getOnchainActivityByTxId(txid: txId)
else {
return false
}
return activity.doesExist
}

init(coreService: CoreService) {
self.coreService = coreService
}
Expand Down
6 changes: 4 additions & 2 deletions Bitkit/Views/Wallets/Activity/ActivityIcon.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ struct ActivityIcon: View {
let isBoosted: Bool
let isTransfer: Bool
let doesExist: Bool
let isCpfpChild: Bool

init(activity: Activity, size: CGFloat = 32) {
init(activity: Activity, size: CGFloat = 32, isCpfpChild: Bool = false) {
self.size = size
self.isCpfpChild = isCpfpChild
switch activity {
case let .lightning(ln):
isLightning = true
Expand Down Expand Up @@ -65,7 +67,7 @@ struct ActivityIcon: View {
backgroundColor: .red16,
size: size
)
} else if isBoosted && !(confirmed ?? false) {
} else if isCpfpChild || (isBoosted && !(confirmed ?? false)) {
CircularIcon(
icon: "timer-alt",
iconColor: .yellow,
Expand Down
16 changes: 14 additions & 2 deletions Bitkit/Views/Wallets/Activity/ActivityItemView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ struct ActivityItemView: View {
@EnvironmentObject var channelDetails: ChannelDetailsViewModel
@StateObject private var viewModel: ActivityItemViewModel
@State private var boostTxDoesExist: [String: Bool] = [:] // Maps boostTxId -> doesExist
@State private var isCpfpChild: Bool = false

init(item: Activity) {
self.item = item
Expand Down Expand Up @@ -100,6 +101,10 @@ struct ActivityItemView: View {
: t("wallet__activity_transfer_savings_done")
}

if isCpfpChild {
return t("wallet__activity_boost_fee")
}

return isSent
? t("wallet__activity_bitcoin_sent")
: t("wallet__activity_bitcoin_received")
Expand All @@ -112,9 +117,11 @@ struct ActivityItemView: View {
private var shouldDisableBoostButton: Bool {
switch viewModel.activity {
case .lightning:
// Lightning transactions can never be boosted
return true
case let .onchain(activity):
if isCpfpChild {
return true
}
if !activity.doesExist {
return true
}
Expand Down Expand Up @@ -186,7 +193,7 @@ struct ActivityItemView: View {
HStack(alignment: .bottom) {
MoneyStack(sats: amount, prefix: amountPrefix, showSymbol: false)
Spacer()
ActivityIcon(activity: viewModel.activity, size: 48)
ActivityIcon(activity: viewModel.activity, size: 48, isCpfpChild: isCpfpChild)
}
.padding(.bottom, 16)

Expand Down Expand Up @@ -221,6 +228,11 @@ struct ActivityItemView: View {
}
}
.task {
// Check if this is a CPFP child transaction
if case let .onchain(activity) = viewModel.activity {
isCpfpChild = await CoreService.shared.activity.isCpfpChildTransaction(txId: activity.txId)
}

// Load boostTxIds doesExist status to determine RBF vs CPFP
if case let .onchain(activity) = viewModel.activity,
!activity.boostTxIds.isEmpty
Expand Down
20 changes: 17 additions & 3 deletions Bitkit/Views/Wallets/Activity/ActivityRowOnchain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,15 @@ private struct ActivityStatus: View {
let txType: PaymentType
let confirmed: Bool
let isTransfer: Bool
let isCpfpChild: Bool

var body: some View {
if isTransfer {
BodyMSBText(t("wallet__activity_transfer"))
} else {
if txType == .sent {
if isCpfpChild {
BodyMSBText(t("wallet__activity_boost_fee"))
} else if txType == .sent {
BodyMSBText(t("wallet__activity_sent"))
} else {
BodyMSBText(t("wallet__activity_received"))
Expand All @@ -22,6 +25,7 @@ private struct ActivityStatus: View {
struct ActivityRowOnchain: View {
let item: OnchainActivity
let feeEstimates: FeeRates?
@State private var isCpfpChild: Bool = false

private var amountPrefix: String {
return item.txType == .sent ? "-" : "+"
Expand Down Expand Up @@ -53,6 +57,10 @@ struct ActivityRowOnchain: View {
return t("wallet__activity_removed")
}

if isCpfpChild {
return t("wallet__activity_boost_fee_description")
}

if item.isTransfer {
switch item.txType {
case .sent:
Expand All @@ -75,16 +83,22 @@ struct ActivityRowOnchain: View {

var body: some View {
HStack(spacing: 16) {
ActivityIcon(activity: .onchain(item), size: 40)
ActivityIcon(activity: .onchain(item), size: 40, isCpfpChild: isCpfpChild)

VStack(alignment: .leading, spacing: 2) {
ActivityStatus(txType: item.txType, confirmed: item.confirmed, isTransfer: item.isTransfer)
ActivityStatus(txType: item.txType, confirmed: item.confirmed, isTransfer: item.isTransfer, isCpfpChild: isCpfpChild)
.lineLimit(1)
CaptionBText(description)
.lineLimit(1)
}
.fixedSize(horizontal: false, vertical: true)

Spacer()

MoneyCell(sats: amount, prefix: amountPrefix)
}
.task {
isCpfpChild = await CoreService.shared.activity.isCpfpChildTransaction(txId: item.txId)
}
}
}
Loading