From 63f95625e0345ba2c47d1cc93ad2b15ba66eb952 Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Fri, 29 Aug 2025 10:40:57 -0300 Subject: [PATCH 1/5] feat: payment error event --- app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt | 1 + .../main/java/to/bitkit/viewmodels/AppViewModel.kt | 12 ++++-------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt b/app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt index 544d76291..5e84f3835 100644 --- a/app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt +++ b/app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt @@ -79,6 +79,7 @@ fun SendSheet( is SendEffect.NavigateToWithdrawError -> navController.navigate(SendRoute.WithdrawError) is SendEffect.NavigateToFee -> navController.navigate(SendRoute.FeeRate) is SendEffect.NavigateToFeeCustom -> navController.navigate(SendRoute.FeeCustom) + is SendEffect.PaymentError -> TODO() } } } diff --git a/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt index 0ca94c5fb..a2a5eb124 100644 --- a/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt @@ -947,12 +947,7 @@ class AppViewModel @Inject constructor( lightningRepo.sync() }.onFailure { e -> Logger.error(msg = "Error sending onchain payment", e = e, context = TAG) - toast( - type = Toast.ToastType.ERROR, - title = "Error Sending", - description = e.message ?: "Unknown error" - ) - hideSheet() + setSendEffect(SendEffect.PaymentError(e)) } } @@ -976,8 +971,7 @@ class AppViewModel @Inject constructor( setSendEffect(SendEffect.PaymentSuccess()) }.onFailure { e -> Logger.error("Error sending lightning payment", e, context = TAG) - toast(e) - hideSheet() + setSendEffect(SendEffect.PaymentError(e)) } } } @@ -1502,6 +1496,8 @@ sealed class SendEffect { data object NavigateToQuickPay : SendEffect() data object NavigateToFee : SendEffect() data object NavigateToFeeCustom : SendEffect() + + data class PaymentError(val e: Throwable) : SendEffect() data class PaymentSuccess(val sheet: NewTransactionSheetDetails? = null) : SendEffect() } From 99fd973a7a7353c92d3d9c8b4b21de1a76da428e Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Fri, 29 Aug 2025 13:14:07 -0300 Subject: [PATCH 2/5] feat: stack trace parameter --- .../ui/screens/wallets/send/SendErrorScreen.kt | 14 +++++++++++--- app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt | 10 ++++++++-- .../main/java/to/bitkit/viewmodels/AppViewModel.kt | 6 +++--- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendErrorScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendErrorScreen.kt index 9ee8437f9..bfb14293c 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendErrorScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendErrorScreen.kt @@ -30,12 +30,14 @@ import to.bitkit.ui.theme.Colors @Composable fun SendErrorScreen( - errorMessage: String, + errorMessage: String?, + stackTrace: String? = null, onRetry: () -> Unit, onClose: () -> Unit, ) { Content( errorMessage = errorMessage, + stackTrace = stackTrace, onRetry = onRetry, onClose = onClose, ) @@ -43,12 +45,13 @@ fun SendErrorScreen( @Composable private fun Content( - errorMessage: String, + errorMessage: String?, + stackTrace: String?, modifier: Modifier = Modifier, onRetry: () -> Unit = {}, onClose: () -> Unit = {}, ) { - val errorText = errorMessage.ifEmpty { "Unknown error." } + val errorText = errorMessage.orEmpty().ifEmpty { "Unknown error." } Column( modifier = modifier .fillMaxSize() @@ -106,6 +109,10 @@ private fun Preview() { BottomSheetPreview { Content( errorMessage = stringResource(R.string.wallet__send_error_create_tx), + stackTrace = "Test render error\n" + + "\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in WalletsStack (at SceneView.tsx:132)", modifier = Modifier.sheetHeight(), ) } @@ -119,6 +126,7 @@ private fun PreviewUnknown() { BottomSheetPreview { Content( errorMessage = "", + stackTrace = null, modifier = Modifier.sheetHeight(), ) } diff --git a/app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt b/app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt index 5e84f3835..58be08b75 100644 --- a/app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt +++ b/app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt @@ -74,12 +74,18 @@ fun SendSheet( appViewModel.clearClipboardForAutoRead() navController.navigate(SendRoute.Success) } + is SendEffect.NavigateToQuickPay -> navController.navigate(SendRoute.QuickPay) is SendEffect.NavigateToWithdrawConfirm -> navController.navigate(SendRoute.WithdrawConfirm) is SendEffect.NavigateToWithdrawError -> navController.navigate(SendRoute.WithdrawError) is SendEffect.NavigateToFee -> navController.navigate(SendRoute.FeeRate) is SendEffect.NavigateToFeeCustom -> navController.navigate(SendRoute.FeeCustom) - is SendEffect.PaymentError -> TODO() + is SendEffect.PaymentError -> navController.navigate( + SendRoute.Error( + errorMessage = it.errorMessage, + stackTrace = it.stackTrace + ) + ) } } } @@ -299,5 +305,5 @@ sealed interface SendRoute { data object Success : SendRoute @Serializable - data class Error(val errorMessage: String) : SendRoute + data class Error(val errorMessage: String?, val stackTrace: String? = null) : SendRoute } diff --git a/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt b/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt index a2a5eb124..dca38c8d8 100644 --- a/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt +++ b/app/src/main/java/to/bitkit/viewmodels/AppViewModel.kt @@ -947,7 +947,7 @@ class AppViewModel @Inject constructor( lightningRepo.sync() }.onFailure { e -> Logger.error(msg = "Error sending onchain payment", e = e, context = TAG) - setSendEffect(SendEffect.PaymentError(e)) + setSendEffect(SendEffect.PaymentError(errorMessage = e.message, stackTrace = e.stackTraceToString())) } } @@ -971,7 +971,7 @@ class AppViewModel @Inject constructor( setSendEffect(SendEffect.PaymentSuccess()) }.onFailure { e -> Logger.error("Error sending lightning payment", e, context = TAG) - setSendEffect(SendEffect.PaymentError(e)) + setSendEffect(SendEffect.PaymentError(errorMessage = e.message, stackTrace = e.stackTraceToString())) } } } @@ -1497,7 +1497,7 @@ sealed class SendEffect { data object NavigateToFee : SendEffect() data object NavigateToFeeCustom : SendEffect() - data class PaymentError(val e: Throwable) : SendEffect() + data class PaymentError(val errorMessage: String?, val stackTrace: String) : SendEffect() data class PaymentSuccess(val sheet: NewTransactionSheetDetails? = null) : SendEffect() } From fd6b05ed768500d3fe23409acbf0af3053ec5c41 Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Fri, 29 Aug 2025 13:33:02 -0300 Subject: [PATCH 3/5] feat: display detail box --- .../screens/wallets/send/SendErrorScreen.kt | 71 ++++++++++++------- 1 file changed, 47 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendErrorScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendErrorScreen.kt index bfb14293c..6cf509550 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendErrorScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendErrorScreen.kt @@ -1,7 +1,9 @@ package to.bitkit.ui.screens.wallets.send import androidx.compose.foundation.Image +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer @@ -11,6 +13,7 @@ import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource @@ -27,6 +30,7 @@ import to.bitkit.ui.shared.modifiers.sheetHeight import to.bitkit.ui.shared.util.gradientBackground import to.bitkit.ui.theme.AppThemeSurface import to.bitkit.ui.theme.Colors +import to.bitkit.ui.theme.Shapes @Composable fun SendErrorScreen( @@ -60,45 +64,64 @@ private fun Content( ) { SheetTopBar(stringResource(R.string.wallet__send_error_tx_failed)) - Column( - modifier = Modifier - .fillMaxSize() - .padding(horizontal = 16.dp) + Box( + modifier = Modifier.weight(1f) ) { - Spacer(modifier = Modifier.height(16.dp)) - - BodyM(text = errorText, color = Colors.White64) - - Spacer(modifier = Modifier.weight(1f)) Image( painter = painterResource(R.drawable.cross), contentDescription = null, modifier = Modifier .fillMaxWidth() .height(256.dp) + .align(Alignment.Center) ) - Spacer(modifier = Modifier.weight(1f)) - Row( - horizontalArrangement = Arrangement.spacedBy(16.dp), - modifier = Modifier.fillMaxWidth() + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 16.dp) ) { - SecondaryButton( - text = stringResource(R.string.common__cancel), - onClick = onClose, + Spacer(modifier = Modifier.height(16.dp)) + + BodyM(text = errorText, color = Colors.White64) + + BodyM( + text = stackTrace.orEmpty(), + color = Colors.Red, modifier = Modifier - .weight(1f) - .testTag("Close") - ) - PrimaryButton( - text = stringResource(R.string.common__try_again), - onClick = onRetry, - modifier = Modifier.weight(1f) + .padding(top = 16.dp) + .fillMaxWidth() + .background(color = Colors.White10, shape = Shapes.medium) + .padding(16.dp) ) + + Spacer(modifier = Modifier.weight(1f)) + + } + } - Spacer(modifier = Modifier.height(16.dp)) + Row( + horizontalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier.fillMaxWidth() + ) { + SecondaryButton( + text = stringResource(R.string.common__cancel), + onClick = onClose, + fullWidth = false, + modifier = Modifier + .weight(1f) + .testTag("Close") + ) + PrimaryButton( + text = stringResource(R.string.common__try_again), + onClick = onRetry, + fullWidth = false, + modifier = Modifier.weight(1f) + ) } + + Spacer(modifier = Modifier.height(16.dp)) } } From 13954ea81c34b0c191fab913d1ecd8b43a064b50 Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Fri, 29 Aug 2025 14:07:02 -0300 Subject: [PATCH 4/5] feat: detail visibility --- .../screens/wallets/send/SendErrorScreen.kt | 111 ++++++++++++++---- 1 file changed, 88 insertions(+), 23 deletions(-) diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendErrorScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendErrorScreen.kt index 6cf509550..537886763 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendErrorScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendErrorScreen.kt @@ -1,18 +1,24 @@ package to.bitkit.ui.screens.wallets.send +import androidx.compose.animation.AnimatedVisibility import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.testTag @@ -39,11 +45,17 @@ fun SendErrorScreen( onRetry: () -> Unit, onClose: () -> Unit, ) { + var displayDetails by remember { mutableStateOf(false) } + Content( errorMessage = errorMessage, stackTrace = stackTrace, onRetry = onRetry, onClose = onClose, + displayDetails = displayDetails, + onDisplayDetails = { + displayDetails = true + } ) } @@ -51,7 +63,10 @@ fun SendErrorScreen( private fun Content( errorMessage: String?, stackTrace: String?, + displayDetails: Boolean, modifier: Modifier = Modifier, + onDisplayDetails: () -> Unit = {}, + onSendReportClick: () -> Unit = {}, onRetry: () -> Unit = {}, onClose: () -> Unit = {}, ) { @@ -60,6 +75,7 @@ private fun Content( modifier = modifier .fillMaxSize() .gradientBackground() + .verticalScroll(rememberScrollState()) .navigationBarsPadding() ) { SheetTopBar(stringResource(R.string.wallet__send_error_tx_failed)) @@ -85,15 +101,20 @@ private fun Content( BodyM(text = errorText, color = Colors.White64) - BodyM( - text = stackTrace.orEmpty(), - color = Colors.Red, - modifier = Modifier - .padding(top = 16.dp) - .fillMaxWidth() - .background(color = Colors.White10, shape = Shapes.medium) - .padding(16.dp) - ) + AnimatedVisibility( + visible = displayDetails && !stackTrace.isNullOrBlank(), + label = "" + ) { + BodyM( + text = stackTrace.orEmpty(), + color = Colors.Red, + modifier = Modifier + .padding(top = 16.dp) + .fillMaxWidth() + .background(color = Colors.White10, shape = Shapes.medium) + .padding(16.dp) + ) + } Spacer(modifier = Modifier.weight(1f)) @@ -101,23 +122,38 @@ private fun Content( } } - Row( - horizontalArrangement = Arrangement.spacedBy(16.dp), - modifier = Modifier.fillMaxWidth() + Column( + verticalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp) ) { - SecondaryButton( - text = stringResource(R.string.common__cancel), - onClick = onClose, - fullWidth = false, - modifier = Modifier - .weight(1f) - .testTag("Close") - ) + AnimatedVisibility(visible = !stackTrace.isNullOrBlank() && !displayDetails) { + SecondaryButton( + text = "Technical Details", // TODO Transifex + onClick = onDisplayDetails, + modifier = Modifier + ) + } + AnimatedVisibility(visible = !stackTrace.isNullOrBlank() && displayDetails) { + SecondaryButton( + text = "Send Anonymous Report", // TODO Transifex + onClick = onSendReportClick, + modifier = Modifier + ) + } + if (stackTrace.isNullOrBlank()) { + SecondaryButton( + text = stringResource(R.string.common__cancel), + onClick = onClose, + fullWidth = true, + modifier = Modifier + .testTag("Close") + ) + } PrimaryButton( text = stringResource(R.string.common__try_again), onClick = onRetry, - fullWidth = false, - modifier = Modifier.weight(1f) ) } @@ -135,8 +171,35 @@ private fun Preview() { stackTrace = "Test render error\n" + "\n" + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in WalletsStack (at SceneView.tsx:132)", + modifier = Modifier.sheetHeight(), + displayDetails = true, + onDisplayDetails = {} + ) + } + } +} + +@Preview(showSystemUi = true) +@Composable +private fun PreviewDontDisplayDetails() { + AppThemeSurface { + BottomSheetPreview { + Content( + errorMessage = stringResource(R.string.wallet__send_error_create_tx), + stackTrace = "Test render error\n" + + "\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + " in WalletsStack (at SceneView.tsx:132)", modifier = Modifier.sheetHeight(), + displayDetails = false, + onDisplayDetails = {} ) } } @@ -151,6 +214,8 @@ private fun PreviewUnknown() { errorMessage = "", stackTrace = null, modifier = Modifier.sheetHeight(), + displayDetails = false, + onDisplayDetails = {} ) } } From 23f08e435d5990a8dc4761abe13fa0e78c5e8f49 Mon Sep 17 00:00:00 2001 From: Joao Victor Sena Date: Fri, 29 Aug 2025 14:13:31 -0300 Subject: [PATCH 5/5] fix: message padding --- .../screens/wallets/send/SendErrorScreen.kt | 49 ++++++++++++++++++- .../java/to/bitkit/ui/sheets/SendSheet.kt | 3 ++ 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendErrorScreen.kt b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendErrorScreen.kt index 537886763..5f7af609e 100644 --- a/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendErrorScreen.kt +++ b/app/src/main/java/to/bitkit/ui/screens/wallets/send/SendErrorScreen.kt @@ -44,6 +44,7 @@ fun SendErrorScreen( stackTrace: String? = null, onRetry: () -> Unit, onClose: () -> Unit, + onSendReportClick: () -> Unit, ) { var displayDetails by remember { mutableStateOf(false) } @@ -53,6 +54,7 @@ fun SendErrorScreen( onRetry = onRetry, onClose = onClose, displayDetails = displayDetails, + onSendReportClick = onSendReportClick, onDisplayDetails = { displayDetails = true } @@ -109,7 +111,7 @@ private fun Content( text = stackTrace.orEmpty(), color = Colors.Red, modifier = Modifier - .padding(top = 16.dp) + .padding(vertical = 16.dp) .fillMaxWidth() .background(color = Colors.White10, shape = Shapes.medium) .padding(16.dp) @@ -174,6 +176,51 @@ private fun Preview() { " in Tabbar (at WalletNavigator.tsx:59)\n" + " in Tabbar (at WalletNavigator.tsx:59)\n" + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + + " in Tabbar (at WalletNavigator.tsx:59)\n" + " in WalletsStack (at SceneView.tsx:132)", modifier = Modifier.sheetHeight(), displayDetails = true, diff --git a/app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt b/app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt index 58be08b75..b6855ff46 100644 --- a/app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt +++ b/app/src/main/java/to/bitkit/ui/sheets/SendSheet.kt @@ -245,6 +245,9 @@ fun SendSheet( } else { navController.navigate(SendRoute.Success) } + }, + onSendReportClick = { + }, onClose = { appViewModel.hideSheet()