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
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ import org.bitcoinppl.cove.ui.theme.CoveColor
import org.bitcoinppl.cove.ui.theme.coveColors
import org.bitcoinppl.cove.ui.theme.isLight
import org.bitcoinppl.cove.utils.toColor
import org.bitcoinppl.cove.views.AutoSizeText
import org.bitcoinppl.cove.views.AsyncText
import org.bitcoinppl.cove.views.BalanceAutoSizeText
import org.bitcoinppl.cove.views.ImageButton
import org.bitcoinppl.cove_core.HeaderIconPresenter
Expand Down Expand Up @@ -110,9 +110,11 @@ fun TransactionDetailsScreen(
// state for confirmation polling and pull-to-refresh
var numberOfConfirmations by remember { mutableStateOf<Int?>(null) }
var isRefreshing by remember { mutableStateOf(false) }
var feeFiatFmt by remember { mutableStateOf("---") }
var sentSansFeeFiatFmt by remember { mutableStateOf("---") }
var totalSpentFiatFmt by remember { mutableStateOf("---") }

// use cached fiat values for immediate display, null shows spinner
var feeFiatFmt by remember { mutableStateOf(transactionDetails.feeFiatFmtCached()) }
var sentSansFeeFiatFmt by remember { mutableStateOf(transactionDetails.sentSansFeeFiatFmtCached()) }
var totalSpentFiatFmt by remember { mutableStateOf(transactionDetails.amountFiatFmtCached()) }

// get current color scheme (respects in-app theme toggle)
val isDark = !MaterialTheme.colorScheme.isLight
Expand All @@ -127,25 +129,28 @@ fun TransactionDetailsScreen(
}
}

// load fiat amounts
// load fiat amounts (update cached values with fresh async values)
LaunchedEffect(transactionDetails) {
feeFiatFmt =
try {
transactionDetails.feeFiatFmt()
} catch (e: Exception) {
"---"
android.util.Log.e("TransactionDetails", "Failed to fetch fiat fee amount", e)
feeFiatFmt // keep cached value on error
}
sentSansFeeFiatFmt =
try {
transactionDetails.sentSansFeeFiatFmt()
} catch (e: Exception) {
"---"
android.util.Log.e("TransactionDetails", "Failed to fetch sent sans fee fiat amount", e)
sentSansFeeFiatFmt // keep cached value on error
}
totalSpentFiatFmt =
try {
transactionDetails.amountFiatFmt()
} catch (e: Exception) {
"---"
android.util.Log.e("TransactionDetails", "Failed to fetch total fiat amount", e)
totalSpentFiatFmt // keep cached value on error
}
}

Expand Down Expand Up @@ -268,14 +273,6 @@ fun TransactionDetailsScreen(

// format amounts
val txAmountPrimary = manager.rust.displayAmount(amount = transactionDetails.amount())
val txAmountSecondary by androidx.compose.runtime.produceState(initialValue = "---") {
value =
try {
transactionDetails.amountFiatFmt()
} catch (e: Exception) {
"---"
}
}

// details expanded from metadata
val isExpanded = metadata.detailsExpanded
Expand Down Expand Up @@ -483,11 +480,10 @@ fun TransactionDetailsScreen(

Spacer(Modifier.height(4.dp))

AutoSizeText(
txAmountSecondary,
AsyncText(
text = totalSpentFiatFmt,
color = fg.copy(alpha = 0.8f),
maxFontSize = 18.sp,
minimumScaleFactor = 0.90f,
style = MaterialTheme.typography.bodyLarge.copy(fontSize = 18.sp),
)

Spacer(Modifier.height(32.dp))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import org.bitcoinppl.cove.R
import org.bitcoinppl.cove.ui.theme.CoveColor
import org.bitcoinppl.cove.views.AsyncText
import org.bitcoinppl.cove.views.AutoSizeText
import org.bitcoinppl.cove_core.TransactionDetails
import org.bitcoinppl.cove_core.WalletMetadata
Expand All @@ -36,9 +37,9 @@ import org.bitcoinppl.cove_core.WalletMetadata
internal fun TransactionDetailsWidget(
transactionDetails: TransactionDetails,
numberOfConfirmations: Int?,
feeFiatFmt: String,
sentSansFeeFiatFmt: String,
totalSpentFiatFmt: String,
feeFiatFmt: String?,
sentSansFeeFiatFmt: String?,
totalSpentFiatFmt: String?,
metadata: WalletMetadata,
) {
val dividerColor = MaterialTheme.colorScheme.outlineVariant
Expand Down Expand Up @@ -207,10 +208,8 @@ internal fun DetailsWidget(
}
Column(horizontalAlignment = Alignment.End) {
AutoSizeText(primary, color = primaryColor, maxFontSize = 14.sp, minimumScaleFactor = 0.90f, fontWeight = FontWeight.SemiBold)
if (!secondary.isNullOrEmpty()) {
Spacer(Modifier.height(6.dp))
Text(secondary, color = sub, fontSize = 12.sp)
}
Spacer(Modifier.height(6.dp))
AsyncText(text = secondary, color = sub, style = MaterialTheme.typography.bodySmall.copy(fontSize = 12.sp))
}
}
}
29 changes: 22 additions & 7 deletions android/app/src/main/java/org/bitcoinppl/cove/views/AsyncView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,10 @@ fun AsyncText(
}

// view that runs an async operation and shows loading/content/error states
// if cachedValue is provided, show it immediately while async operation runs
@Composable
fun <T> AsyncView(
cachedValue: T? = null,
operation: suspend () -> T,
modifier: Modifier = Modifier,
errorView: @Composable () -> Unit = {},
Expand All @@ -66,19 +68,32 @@ fun <T> AsyncView(
} catch (e: CancellationException) {
throw e
} catch (e: Exception) {
Log.e("AsyncView", "Error loading async view: ${e.message}")
Log.e("AsyncView", "Error loading async view: ${e.message}", e)
Result.failure(e)
}
}

Box(modifier = modifier, contentAlignment = Alignment.Center) {
when (val r = result) {
null ->
CircularProgressIndicator(
modifier = Modifier.size(16.dp),
strokeWidth = 2.dp,
)
else -> if (r.isSuccess) content(r.getOrThrow()) else errorView()
null -> {
if (cachedValue != null) {
content(cachedValue)
} else {
CircularProgressIndicator(
modifier = Modifier.size(16.dp),
strokeWidth = 2.dp,
)
}
}
else -> {
if (r.isSuccess) {
content(r.getOrThrow())
} else if (cachedValue != null) {
content(cachedValue)
} else {
errorView()
}
}
}
}
}
66 changes: 66 additions & 0 deletions android/app/src/main/java/org/bitcoinppl/cove_core/cove.kt
Original file line number Diff line number Diff line change
Expand Up @@ -1625,6 +1625,8 @@ external fun uniffi_cove_checksum_method_transactiondetails_amount_fiat(
): Short
external fun uniffi_cove_checksum_method_transactiondetails_amount_fiat_fmt(
): Short
external fun uniffi_cove_checksum_method_transactiondetails_amount_fiat_fmt_cached(
): Short
external fun uniffi_cove_checksum_method_transactiondetails_amount_fmt(
): Short
external fun uniffi_cove_checksum_method_transactiondetails_block_number(
Expand All @@ -1635,6 +1637,8 @@ external fun uniffi_cove_checksum_method_transactiondetails_confirmation_date_ti
): Short
external fun uniffi_cove_checksum_method_transactiondetails_fee_fiat_fmt(
): Short
external fun uniffi_cove_checksum_method_transactiondetails_fee_fiat_fmt_cached(
): Short
external fun uniffi_cove_checksum_method_transactiondetails_fee_fmt(
): Short
external fun uniffi_cove_checksum_method_transactiondetails_is_confirmed(
Expand All @@ -1645,6 +1649,8 @@ external fun uniffi_cove_checksum_method_transactiondetails_is_sent(
): Short
external fun uniffi_cove_checksum_method_transactiondetails_sent_sans_fee_fiat_fmt(
): Short
external fun uniffi_cove_checksum_method_transactiondetails_sent_sans_fee_fiat_fmt_cached(
): Short
external fun uniffi_cove_checksum_method_transactiondetails_sent_sans_fee_fmt(
): Short
external fun uniffi_cove_checksum_method_transactiondetails_transaction_label(
Expand Down Expand Up @@ -2677,6 +2683,8 @@ external fun uniffi_cove_fn_method_transactiondetails_amount_fiat(`ptr`: Long,
): Long
external fun uniffi_cove_fn_method_transactiondetails_amount_fiat_fmt(`ptr`: Long,
): Long
external fun uniffi_cove_fn_method_transactiondetails_amount_fiat_fmt_cached(`ptr`: Long,uniffi_out_err: UniffiRustCallStatus,
): RustBuffer.ByValue
external fun uniffi_cove_fn_method_transactiondetails_amount_fmt(`ptr`: Long,`unit`: RustBufferBitcoinUnit.ByValue,uniffi_out_err: UniffiRustCallStatus,
): RustBuffer.ByValue
external fun uniffi_cove_fn_method_transactiondetails_block_number(`ptr`: Long,uniffi_out_err: UniffiRustCallStatus,
Expand All @@ -2687,6 +2695,8 @@ external fun uniffi_cove_fn_method_transactiondetails_confirmation_date_time(`pt
): RustBuffer.ByValue
external fun uniffi_cove_fn_method_transactiondetails_fee_fiat_fmt(`ptr`: Long,
): Long
external fun uniffi_cove_fn_method_transactiondetails_fee_fiat_fmt_cached(`ptr`: Long,uniffi_out_err: UniffiRustCallStatus,
): RustBuffer.ByValue
external fun uniffi_cove_fn_method_transactiondetails_fee_fmt(`ptr`: Long,`unit`: RustBufferBitcoinUnit.ByValue,uniffi_out_err: UniffiRustCallStatus,
): RustBuffer.ByValue
external fun uniffi_cove_fn_method_transactiondetails_is_confirmed(`ptr`: Long,uniffi_out_err: UniffiRustCallStatus,
Expand All @@ -2697,6 +2707,8 @@ external fun uniffi_cove_fn_method_transactiondetails_is_sent(`ptr`: Long,uniffi
): Byte
external fun uniffi_cove_fn_method_transactiondetails_sent_sans_fee_fiat_fmt(`ptr`: Long,
): Long
external fun uniffi_cove_fn_method_transactiondetails_sent_sans_fee_fiat_fmt_cached(`ptr`: Long,uniffi_out_err: UniffiRustCallStatus,
): RustBuffer.ByValue
external fun uniffi_cove_fn_method_transactiondetails_sent_sans_fee_fmt(`ptr`: Long,`unit`: RustBufferBitcoinUnit.ByValue,uniffi_out_err: UniffiRustCallStatus,
): RustBuffer.ByValue
external fun uniffi_cove_fn_method_transactiondetails_transaction_label(`ptr`: Long,uniffi_out_err: UniffiRustCallStatus,
Expand Down Expand Up @@ -4108,6 +4120,9 @@ private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) {
if (lib.uniffi_cove_checksum_method_transactiondetails_amount_fiat_fmt() != 17226.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
if (lib.uniffi_cove_checksum_method_transactiondetails_amount_fiat_fmt_cached() != 11182.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
if (lib.uniffi_cove_checksum_method_transactiondetails_amount_fmt() != 13996.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
Expand All @@ -4123,6 +4138,9 @@ private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) {
if (lib.uniffi_cove_checksum_method_transactiondetails_fee_fiat_fmt() != 57101.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
if (lib.uniffi_cove_checksum_method_transactiondetails_fee_fiat_fmt_cached() != 1845.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
if (lib.uniffi_cove_checksum_method_transactiondetails_fee_fmt() != 37631.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
Expand All @@ -4138,6 +4156,9 @@ private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) {
if (lib.uniffi_cove_checksum_method_transactiondetails_sent_sans_fee_fiat_fmt() != 61624.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
if (lib.uniffi_cove_checksum_method_transactiondetails_sent_sans_fee_fiat_fmt_cached() != 42399.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
if (lib.uniffi_cove_checksum_method_transactiondetails_sent_sans_fee_fmt() != 64427.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
Expand Down Expand Up @@ -20359,6 +20380,8 @@ public interface TransactionDetailsInterface {

suspend fun `amountFiatFmt`(): kotlin.String

fun `amountFiatFmtCached`(): kotlin.String?

fun `amountFmt`(`unit`: BitcoinUnit): kotlin.String

fun `blockNumber`(): kotlin.UInt?
Expand All @@ -20369,6 +20392,8 @@ public interface TransactionDetailsInterface {

suspend fun `feeFiatFmt`(): kotlin.String

fun `feeFiatFmtCached`(): kotlin.String?

fun `feeFmt`(`unit`: BitcoinUnit): kotlin.String?

fun `isConfirmed`(): kotlin.Boolean
Expand All @@ -20379,6 +20404,8 @@ public interface TransactionDetailsInterface {

suspend fun `sentSansFeeFiatFmt`(): kotlin.String

fun `sentSansFeeFiatFmtCached`(): kotlin.String?

fun `sentSansFeeFmt`(`unit`: BitcoinUnit): kotlin.String?

fun `transactionLabel`(): kotlin.String?
Expand Down Expand Up @@ -20567,6 +20594,19 @@ open class TransactionDetails: Disposable, AutoCloseable, TransactionDetailsInte
)
}

override fun `amountFiatFmtCached`(): kotlin.String? {
return FfiConverterOptionalString.lift(
callWithHandle {
uniffiRustCall() { _status ->
UniffiLib.uniffi_cove_fn_method_transactiondetails_amount_fiat_fmt_cached(
it,
_status)
}
}
)
}


override fun `amountFmt`(`unit`: BitcoinUnit): kotlin.String {
return FfiConverterString.lift(
callWithHandle {
Expand Down Expand Up @@ -20640,6 +20680,19 @@ open class TransactionDetails: Disposable, AutoCloseable, TransactionDetailsInte
)
}

override fun `feeFiatFmtCached`(): kotlin.String? {
return FfiConverterOptionalString.lift(
callWithHandle {
uniffiRustCall() { _status ->
UniffiLib.uniffi_cove_fn_method_transactiondetails_fee_fiat_fmt_cached(
it,
_status)
}
}
)
}


override fun `feeFmt`(`unit`: BitcoinUnit): kotlin.String? {
return FfiConverterOptionalString.lift(
callWithHandle {
Expand Down Expand Up @@ -20713,6 +20766,19 @@ open class TransactionDetails: Disposable, AutoCloseable, TransactionDetailsInte
)
}

override fun `sentSansFeeFiatFmtCached`(): kotlin.String? {
return FfiConverterOptionalString.lift(
callWithHandle {
uniffiRustCall() { _status ->
UniffiLib.uniffi_cove_fn_method_transactiondetails_sent_sans_fee_fiat_fmt_cached(
it,
_status)
}
}
)
}


override fun `sentSansFeeFmt`(`unit`: BitcoinUnit): kotlin.String? {
return FfiConverterOptionalString.lift(
callWithHandle {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,10 @@ struct SentDetailsExpandedView: View {

VStack(alignment: .trailing) {
Text(transactionDetails.feeFmt(unit: metadata.selectedUnit) ?? "")
AsyncView(operation: transactionDetails.feeFiatFmt) { amount in
AsyncView(
cachedValue: transactionDetails.feeFiatFmtCached(),
operation: transactionDetails.feeFiatFmt
) { amount in
Text(amount).foregroundStyle(.secondary)
.font(.caption)
.padding(.top, 2)
Expand All @@ -91,7 +94,10 @@ struct SentDetailsExpandedView: View {

VStack(alignment: .trailing) {
Text(transactionDetails.sentSansFeeFmt(unit: metadata.selectedUnit) ?? "")
AsyncView(operation: transactionDetails.sentSansFeeFiatFmt) { amount in
AsyncView(
cachedValue: transactionDetails.sentSansFeeFiatFmtCached(),
operation: transactionDetails.sentSansFeeFiatFmt
) { amount in
Text(amount).foregroundStyle(.secondary)
.font(.caption)
.padding(.top, 2)
Expand All @@ -110,7 +116,10 @@ struct SentDetailsExpandedView: View {
Spacer()
VStack(alignment: .trailing) {
Text(transactionDetails.amountFmt(unit: metadata.selectedUnit))
AsyncView(operation: transactionDetails.amountFiatFmt) { amount in
AsyncView(
cachedValue: transactionDetails.amountFiatFmtCached(),
operation: transactionDetails.amountFiatFmt
) { amount in
Text(amount).foregroundStyle(.secondary)
.font(.caption)
.padding(.top, 2)
Expand Down
Loading