From 882605d7a9464790ef5ba3ccb3ecf37f70ddd8f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Honza=20Rychnovsk=C3=BD?= Date: Wed, 13 Nov 2024 09:24:35 +0100 Subject: [PATCH] [#1657] Redesign SendConfirmation subscreens * [#1657] Improve sub-screens previews * Initial design structure changes * Bottom bar by screen stage * Bottom bar paddings * TopAppBar for all screens * Content part - success (partly) * Fix SendConfirmationContent composable scrolling * Improve SendConfirmationSending UI * SendConfirmationSending subscreen final UI * SendingConfirmationSuccess subscreen UI * SendConfirmationFailure UI * Failed/Success view transaction logic * SendConfirmationGrpcFailire UI * MultipleTrxFailure UI partly done * MultipleTrxFailure screen UI * Gradient Scaffold for subscreens * Fix static code analysis warnings * Changelogs update * Screen images update * WhatNewEs changelog update * Update Spanish translation keys --- CHANGELOG.md | 2 + docs/whatsNew/WHATS_NEW_EN.md | 2 + docs/whatsNew/WHATS_NEW_ES.md | 2 + .../zcash/ui/design/component/Scaffold.kt | 24 + .../ui/design/component/ZashiBottomBar.kt | 4 +- .../zcash/ui/design/component/ZashiButton.kt | 17 + .../ui/design/component/ZashiTopAppBar.kt | 4 +- .../res/ui/common/drawable-night/ic_close.xml | 13 + .../main/res/ui/common/drawable/ic_close.xml | 13 + .../co/electriccoin/zcash/ui/Navigation.kt | 2 +- .../viewmodel/AuthenticationViewModel.kt | 2 +- .../zcash/ui/screen/home/view/HomeView.kt | 1 - .../AndroidSendConfirmation.kt | 50 +- .../model/SendConfirmationStage.kt | 21 +- .../view/SendConfirmationView.kt | 1235 +++++++++++------ .../main/res/ui/common/values-es/strings.xml | 1 + .../src/main/res/ui/common/values/strings.xml | 1 + .../drawable-night/ic_cloud_eyes.xml | 76 + .../drawable-night/ic_face_horns.xml | 52 + .../drawable-night/ic_face_star.xml | 22 + .../drawable-night/ic_fist_punch.xml | 52 + .../drawable-night/ic_frame.xml | 15 + .../ic_multi_trx_send_failed.xml | 111 ++ .../drawable-night/ic_phone.xml | 78 ++ .../drawable-night/ic_skull.xml | 31 + .../drawable/ic_cloud_eyes.xml | 76 + .../drawable/ic_face_horns.xml | 52 + .../drawable/ic_face_star.xml | 22 + .../drawable/ic_fist_punch.xml | 52 + .../send_confirmation/drawable/ic_frame.xml | 15 + .../drawable/ic_multi_trx_send_failed.xml | 111 ++ .../send_confirmation/drawable/ic_phone.xml | 78 ++ .../send_confirmation/drawable/ic_skull.xml | 31 + .../send_confirmation/values-es/strings.xml | 51 +- .../ui/send_confirmation/values/strings.xml | 52 +- 35 files changed, 1896 insertions(+), 475 deletions(-) create mode 100644 ui-design-lib/src/main/res/ui/common/drawable-night/ic_close.xml create mode 100644 ui-design-lib/src/main/res/ui/common/drawable/ic_close.xml create mode 100644 ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_cloud_eyes.xml create mode 100644 ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_face_horns.xml create mode 100644 ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_face_star.xml create mode 100644 ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_fist_punch.xml create mode 100644 ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_frame.xml create mode 100644 ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_multi_trx_send_failed.xml create mode 100644 ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_phone.xml create mode 100644 ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_skull.xml create mode 100644 ui-lib/src/main/res/ui/send_confirmation/drawable/ic_cloud_eyes.xml create mode 100644 ui-lib/src/main/res/ui/send_confirmation/drawable/ic_face_horns.xml create mode 100644 ui-lib/src/main/res/ui/send_confirmation/drawable/ic_face_star.xml create mode 100644 ui-lib/src/main/res/ui/send_confirmation/drawable/ic_fist_punch.xml create mode 100644 ui-lib/src/main/res/ui/send_confirmation/drawable/ic_frame.xml create mode 100644 ui-lib/src/main/res/ui/send_confirmation/drawable/ic_multi_trx_send_failed.xml create mode 100644 ui-lib/src/main/res/ui/send_confirmation/drawable/ic_phone.xml create mode 100644 ui-lib/src/main/res/ui/send_confirmation/drawable/ic_skull.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index 680f7899d..1b93b3da3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ and this application adheres to [Semantic Versioning](https://semver.org/spec/v2 - The device authentication feature on the Zashi app launch has been added - Zashi app now supports Spanish language - The Flexa SDK has been adopted to enable payments using the embedded Flexa UI +- New Sending, Success, Failure, and GrpcFailure subscreens of the Send Confirmation screen have been added +- New Copy Transaction IDs feature has been added to the MultipleTransactionFailure screen ### Changelog - Shielded transactions are properly indicated in transaction history diff --git a/docs/whatsNew/WHATS_NEW_EN.md b/docs/whatsNew/WHATS_NEW_EN.md index 45a860017..066279422 100644 --- a/docs/whatsNew/WHATS_NEW_EN.md +++ b/docs/whatsNew/WHATS_NEW_EN.md @@ -13,6 +13,8 @@ directly impact users rather than highlighting other key architectural updates.* - The device authentication feature on the Zashi app launch has been added - Zashi app now supports Spanish language - The Flexa SDK has been adopted to enable payments using the embedded Flexa UI +- New Sending, Success, Failure, and GrpcFailure subscreens of the Send Confirmation screen have been added +- New Copy Transaction IDs feature has been added to the MultipleTransactionFailure screen ### Changelog - Shielded transactions are properly indicated in transaction history diff --git a/docs/whatsNew/WHATS_NEW_ES.md b/docs/whatsNew/WHATS_NEW_ES.md index 26861f044..044370aeb 100644 --- a/docs/whatsNew/WHATS_NEW_ES.md +++ b/docs/whatsNew/WHATS_NEW_ES.md @@ -13,6 +13,8 @@ directly impact users rather than highlighting other key architectural updates.* - The device authentication feature on the Zashi app launch has been added - Zashi app now supports Spanish language - The Flexa SDK has been adopted to enable payments using the embedded Flexa UI +- New Sending, Success, Failure, and GrpcFailure subscreens of the Send Confirmation screen have been added +- New Copy Transaction IDs feature has been added to the MultipleTransactionFailure screen ### Changelog - Shielded transactions are properly indicated in transaction history diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Scaffold.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Scaffold.kt index 00da70f46..8525b8583 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Scaffold.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/Scaffold.kt @@ -1,10 +1,12 @@ package co.electriccoin.zcash.ui.design.component +import androidx.compose.foundation.background import androidx.compose.foundation.layout.PaddingValues import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.tooling.preview.Preview import co.electriccoin.zcash.ui.design.theme.ZcashTheme @@ -35,3 +37,25 @@ fun BlankBgScaffold( modifier = modifier, ) } + +@Composable +fun GradientBgScaffold( + startColor: Color, + endColor: Color, + modifier: Modifier = Modifier, + topBar: @Composable () -> Unit = {}, + bottomBar: @Composable () -> Unit = {}, + snackbarHost: @Composable () -> Unit = {}, + content: @Composable (PaddingValues) -> Unit +) { + Scaffold( + containerColor = Color.Transparent, + topBar = topBar, + snackbarHost = snackbarHost, + bottomBar = bottomBar, + content = content, + modifier = + modifier + .background(zashiVerticalGradient(startColor, endColor)), + ) +} diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiBottomBar.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiBottomBar.kt index a2c3bfed9..ce7397183 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiBottomBar.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiBottomBar.kt @@ -24,10 +24,10 @@ fun ZashiBottomBar( content: @Composable () -> Unit, ) { Surface( - modifier = modifier, shape = RoundedCornerShape(topStart = 32.dp, topEnd = 32.dp), shadowElevation = 4.dp, - color = ZashiColors.Surfaces.bgPrimary + color = ZashiColors.Surfaces.bgPrimary, + modifier = modifier, ) { Column { Spacer(modifier = Modifier.height(16.dp)) diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiButton.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiButton.kt index 32b6d562b..e50c8284a 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiButton.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiButton.kt @@ -3,11 +3,13 @@ package co.electriccoin.zcash.ui.design.component import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.Image import androidx.compose.foundation.isSystemInDarkTheme +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.RowScope import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults @@ -100,6 +102,7 @@ fun ZashiButton( onClick = onClick, modifier = modifier, shape = RoundedCornerShape(12.dp), + contentPadding = PaddingValues(horizontal = 10.dp), enabled = enabled, colors = colors.toButtonColors(), border = colors.borderColor.takeIf { it != Color.Unspecified }?.let { BorderStroke(1.dp, it) }, @@ -269,3 +272,17 @@ private fun DestroyPreview() = ) } } + +@PreviewScreens +@Composable +private fun SmallWidthPreview() = + ZcashTheme { + BlankSurface { + ZashiButton( + modifier = Modifier.wrapContentWidth(), + text = "Small Width Button", + colors = ZashiButtonDefaults.destructive1Colors(), + onClick = {}, + ) + } + } diff --git a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiTopAppBar.kt b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiTopAppBar.kt index ced27319a..0ac2db50d 100644 --- a/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiTopAppBar.kt +++ b/ui-design-lib/src/main/java/co/electriccoin/zcash/ui/design/component/ZashiTopAppBar.kt @@ -12,9 +12,9 @@ import co.electriccoin.zcash.ui.design.theme.internal.TopAppBarColors @Composable @Suppress("LongParameterList") fun ZashiSmallTopAppBar( - title: String?, - subtitle: String?, modifier: Modifier = Modifier, + title: String? = null, + subtitle: String? = null, showTitleLogo: Boolean = false, colors: TopAppBarColors = ZcashTheme.colors.topAppBarColors, navigationAction: @Composable () -> Unit = {}, diff --git a/ui-design-lib/src/main/res/ui/common/drawable-night/ic_close.xml b/ui-design-lib/src/main/res/ui/common/drawable-night/ic_close.xml new file mode 100644 index 000000000..82c253972 --- /dev/null +++ b/ui-design-lib/src/main/res/ui/common/drawable-night/ic_close.xml @@ -0,0 +1,13 @@ + + + diff --git a/ui-design-lib/src/main/res/ui/common/drawable/ic_close.xml b/ui-design-lib/src/main/res/ui/common/drawable/ic_close.xml new file mode 100644 index 000000000..ff2c4aa44 --- /dev/null +++ b/ui-design-lib/src/main/res/ui/common/drawable/ic_close.xml @@ -0,0 +1,13 @@ + + + diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/Navigation.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/Navigation.kt index 8ea79ef0e..4a7598217 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/Navigation.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/Navigation.kt @@ -371,7 +371,7 @@ private fun MainActivity.NavigationHome( goScan = { navController.navigateJustOnce(ScanNavigationArgs(ScanNavigationArgs.DEFAULT)) }, goSendConfirmation = { zecSend -> navController.currentBackStackEntry?.savedStateHandle?.let { handle -> - fillInHandleForConfirmation(handle, zecSend, SendConfirmationStage.Confirmation) + fillInHandleForConfirmation(handle, zecSend, SendConfirmationStage.Prepared) } navController.navigateJustOnce(SEND_CONFIRMATION) }, diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/viewmodel/AuthenticationViewModel.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/viewmodel/AuthenticationViewModel.kt index ebac6466b..dd7e3cd6b 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/viewmodel/AuthenticationViewModel.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/common/viewmodel/AuthenticationViewModel.kt @@ -247,7 +247,7 @@ class AuthenticationViewModel( // returning to biometric authentication, such as a button. The operation was canceled // because [BiometricPrompt.ERROR_LOCKOUT] occurred too many times. BiometricPrompt.ERROR_USER_CANCELED -> { - authenticationResult.value = AuthenticationResult.Failed + authenticationResult.value = AuthenticationResult.Canceled // The following values are just for testing purposes, so we can easier reproduce other // non-success results obtained from [BiometricPrompt] // = AuthenticationResult.Failed diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/view/HomeView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/view/HomeView.kt index 6b4fc9f7b..3a6dad0aa 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/view/HomeView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/home/view/HomeView.kt @@ -87,7 +87,6 @@ fun Home( @Composable @Suppress("LongMethod") -@OptIn(ExperimentalFoundationApi::class) private fun HomeContent( pagerState: PagerState, subScreens: ImmutableList diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/sendconfirmation/AndroidSendConfirmation.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/sendconfirmation/AndroidSendConfirmation.kt index b884d3a2b..e79f72dbf 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/sendconfirmation/AndroidSendConfirmation.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/sendconfirmation/AndroidSendConfirmation.kt @@ -22,6 +22,7 @@ import cash.z.ecc.android.sdk.model.Proposal import cash.z.ecc.android.sdk.model.UnifiedSpendingKey import cash.z.ecc.android.sdk.model.ZecSend import co.electriccoin.zcash.di.koinActivityViewModel +import co.electriccoin.zcash.spackle.ClipboardManagerUtil import co.electriccoin.zcash.spackle.Twig import co.electriccoin.zcash.ui.MainActivity import co.electriccoin.zcash.ui.R @@ -124,25 +125,23 @@ internal fun WrapSendConfirmation( val (stage, setStage) = rememberSaveable(stateSaver = SendConfirmationStage.Saver) { - mutableStateOf(arguments.initialStage ?: SendConfirmationStage.Confirmation) + mutableStateOf(arguments.initialStage ?: SendConfirmationStage.Prepared) } val submissionResults = createTransactionsViewModel.submissions.collectAsState().value.toImmutableList() val onBackAction = { when (stage) { - SendConfirmationStage.Confirmation -> goBack(false) - SendConfirmationStage.Sending -> { // no action - wait until the sending is done + SendConfirmationStage.Prepared -> goBack(false) + SendConfirmationStage.Sending -> { /* No action - wait until the sending is done */ } + SendConfirmationStage.Success -> { + goHome() } - - is SendConfirmationStage.Failure -> setStage(SendConfirmationStage.Confirmation) + is SendConfirmationStage.Failure -> setStage(SendConfirmationStage.Prepared) is SendConfirmationStage.FailureGrpc -> { - setStage(SendConfirmationStage.Confirmation) goHome() } - is SendConfirmationStage.MultipleTrxFailure -> { // no action - wait until report the result - } - + is SendConfirmationStage.MultipleTrxFailure -> { /* No action - wait until the sending is done */ } is SendConfirmationStage.MultipleTrxFailureReported -> goBack(true) } } @@ -171,12 +170,12 @@ internal fun WrapSendConfirmation( submissionResults = submissionResults, snackbarHostState = snackbarHostState, onBack = onBackAction, - onContactSupport = { stageToGo, body -> + onContactSupport = { stageToGo -> val fullMessage = when (stageToGo) { is SendConfirmationStage.Failure -> { EmailUtil.formatMessage( - body = body, + body = stageToGo.stackTrace, supportInfo = supportMessage?.toSupportString(SupportInfoType.entries.toSet()) ) } @@ -209,11 +208,22 @@ internal fun WrapSendConfirmation( setStage(stageToGo) scope.launch { snackbarHostState.showSnackbar( - message = activity.getString(R.string.send_confirmation_multiple_report_unable_open_email) + message = + activity.getString( + R.string.send_confirmation_multiple_trx_failure_report_unable_open_email + ) ) } } }, + onMultipleTrxFailureIdsCopy = { idsString -> + Twig.info { "Multiple Trx IDs copied: $idsString" } + ClipboardManagerUtil.copyToClipboard( + activity.applicationContext, + activity.getString(R.string.send_confirmation_multiple_trx_failure_copy_tag), + idsString + ) + }, onConfirmation = { // Check and trigger authentication if required, or just submit transactions otherwise lifecycleScope.launch { @@ -225,7 +235,6 @@ internal fun WrapSendConfirmation( } else { runSendFundsAction( createTransactionsViewModel = createTransactionsViewModel, - goHome = goHome, // The not-null assertion operator is necessary here even if we check its // nullability before due to property is declared in different module. See more // details on the Kotlin forum @@ -238,6 +247,12 @@ internal fun WrapSendConfirmation( } } }, + onViewTransactions = { + val trxIds = submissionResults.map { it.txIdString() } + Twig.debug { "Transactions IDs passing to a new Transaction Details: $trxIds" } + // Once we implement transaction details screen we can start passing the trx ids to its destination + goHome() + }, topAppBarSubTitleState = topAppBarSubTitleState, exchangeRate = exchangeRateState, contactName = foundContact.value?.name @@ -253,7 +268,6 @@ internal fun WrapSendConfirmation( lifecycleScope.launch { runSendFundsAction( createTransactionsViewModel = createTransactionsViewModel, - goHome = goHome, // The not-null assertion operator is necessary here even if we check its // nullability before due to property is declared in different module. See more // details on the Kotlin forum @@ -280,7 +294,6 @@ internal fun WrapSendConfirmation( @Suppress("LongParameterList") suspend fun runSendFundsAction( createTransactionsViewModel: CreateTransactionsViewModel, - goHome: () -> Unit, proposal: Proposal, setStage: (SendConfirmationStage) -> Unit, spendingKey: UnifiedSpendingKey, @@ -299,7 +312,6 @@ suspend fun runSendFundsAction( Twig.debug { "Transactions submitted with result: $submitResult" } processSubmissionResult( - goHome = goHome, setStage = setStage, submitResult = submitResult ) @@ -332,13 +344,11 @@ private suspend fun submitTransactions( private fun processSubmissionResult( submitResult: SubmitResult, - setStage: (SendConfirmationStage) -> Unit, - goHome: () -> Unit + setStage: (SendConfirmationStage) -> Unit ) { when (submitResult) { SubmitResult.Success -> { - setStage(SendConfirmationStage.Confirmation) - goHome() + setStage(SendConfirmationStage.Success) } is SubmitResult.SimpleTrxFailure.SimpleTrxFailureSubmit -> { setStage(SendConfirmationStage.Failure(submitResult.toErrorMessage(), submitResult.toErrorStacktrace())) diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/sendconfirmation/model/SendConfirmationStage.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/sendconfirmation/model/SendConfirmationStage.kt index 89d15aa35..192d74c64 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/sendconfirmation/model/SendConfirmationStage.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/sendconfirmation/model/SendConfirmationStage.kt @@ -3,10 +3,12 @@ package co.electriccoin.zcash.ui.screen.sendconfirmation.model import androidx.compose.runtime.saveable.mapSaver sealed class SendConfirmationStage { - data object Confirmation : SendConfirmationStage() + data object Prepared : SendConfirmationStage() data object Sending : SendConfirmationStage() + data object Success : SendConfirmationStage() + data class Failure( val error: String, val stackTrace: String, @@ -20,18 +22,20 @@ sealed class SendConfirmationStage { fun toStringName(): String { return when (this) { - Confirmation -> TYPE_CONFIRMATION + Prepared -> TYPE_PREPARED is Failure -> TYPE_FAILURE is FailureGrpc -> TYPE_FAILURE_GRPC MultipleTrxFailure -> TYPE_MULTIPLE_TRX_FAILURE MultipleTrxFailureReported -> TYPE_MULTIPLE_TRX_FAILURE_REPORTED Sending -> TYPE_SENDING + Success -> TYPE_SUCCESS } } companion object { - private const val TYPE_CONFIRMATION = "confirmation" // $NON-NLS + private const val TYPE_PREPARED = "prepared" // $NON-NLS private const val TYPE_SENDING = "sending" // $NON-NLS + private const val TYPE_SUCCESS = "success" // $NON-NLS private const val TYPE_FAILURE = "failure" // $NON-NLS private const val TYPE_FAILURE_GRPC = "type_failure_grpc" // $NON-NLS private const val TYPE_MULTIPLE_TRX_FAILURE = "multiple_trx_failure" // $NON-NLS @@ -52,8 +56,9 @@ sealed class SendConfirmationStage { } else { val sendStageString = (it[KEY_TYPE] as String) when (sendStageString) { - TYPE_CONFIRMATION -> Confirmation + TYPE_PREPARED -> Prepared TYPE_SENDING -> Sending + TYPE_SUCCESS -> Success TYPE_FAILURE -> Failure( error = (it[KEY_ERROR] as String), @@ -72,8 +77,9 @@ sealed class SendConfirmationStage { private fun SendConfirmationStage.toSaverMap(): HashMap { val saverMap = HashMap() when (this) { - Confirmation -> saverMap[KEY_TYPE] = TYPE_CONFIRMATION + Prepared -> saverMap[KEY_TYPE] = TYPE_PREPARED Sending -> saverMap[KEY_TYPE] = TYPE_SENDING + Success -> saverMap[KEY_TYPE] = TYPE_SUCCESS is Failure -> { saverMap[KEY_TYPE] = TYPE_FAILURE saverMap[KEY_ERROR] = this.error @@ -89,14 +95,15 @@ sealed class SendConfirmationStage { fun fromStringName(stringName: String?): SendConfirmationStage { return when (stringName) { - TYPE_CONFIRMATION -> Confirmation + TYPE_PREPARED -> Prepared TYPE_SENDING -> Sending + TYPE_SUCCESS -> Success // Add the String error and stackTrace parameter storing and retrieving TYPE_FAILURE -> Failure("", "") TYPE_FAILURE_GRPC -> FailureGrpc TYPE_MULTIPLE_TRX_FAILURE -> MultipleTrxFailure TYPE_MULTIPLE_TRX_FAILURE_REPORTED -> MultipleTrxFailureReported - else -> Confirmation + else -> Prepared } } } diff --git a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/sendconfirmation/view/SendConfirmationView.kt b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/sendconfirmation/view/SendConfirmationView.kt index 46e0d836f..2244bef09 100644 --- a/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/sendconfirmation/view/SendConfirmationView.kt +++ b/ui-lib/src/main/java/co/electriccoin/zcash/ui/screen/sendconfirmation/view/SendConfirmationView.kt @@ -3,22 +3,26 @@ package co.electriccoin.zcash.ui.screen.sendconfirmation.view import androidx.compose.foundation.Image -import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.background +import androidx.compose.foundation.isSystemInDarkTheme +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.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.wrapContentWidth import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.verticalScroll import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -28,11 +32,12 @@ import androidx.compose.ui.platform.testTag import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource -import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import androidx.constraintlayout.compose.ConstraintLayout +import androidx.constraintlayout.compose.Dimension import cash.z.ecc.android.sdk.fixture.WalletAddressFixture import cash.z.ecc.android.sdk.model.FirstClassByteArray import cash.z.ecc.android.sdk.model.TransactionSubmitResult @@ -47,213 +52,40 @@ import co.electriccoin.zcash.ui.common.extension.asZecAmountTriple import co.electriccoin.zcash.ui.common.extension.totalAmount import co.electriccoin.zcash.ui.common.model.TopAppBarSubTitleState import co.electriccoin.zcash.ui.common.wallet.ExchangeRateState -import co.electriccoin.zcash.ui.design.MINIMAL_WEIGHT -import co.electriccoin.zcash.ui.design.component.AppAlertDialog -import co.electriccoin.zcash.ui.design.component.BlankBgScaffold -import co.electriccoin.zcash.ui.design.component.BlankSurface -import co.electriccoin.zcash.ui.design.component.Body import co.electriccoin.zcash.ui.design.component.ButtonState -import co.electriccoin.zcash.ui.design.component.Small -import co.electriccoin.zcash.ui.design.component.SmallTopAppBar +import co.electriccoin.zcash.ui.design.component.GradientBgScaffold import co.electriccoin.zcash.ui.design.component.StyledBalance import co.electriccoin.zcash.ui.design.component.StyledBalanceDefaults import co.electriccoin.zcash.ui.design.component.TextFieldState import co.electriccoin.zcash.ui.design.component.TopAppBarBackNavigation +import co.electriccoin.zcash.ui.design.component.ZashiBottomBar import co.electriccoin.zcash.ui.design.component.ZashiButton import co.electriccoin.zcash.ui.design.component.ZashiButtonDefaults import co.electriccoin.zcash.ui.design.component.ZashiSmallTopAppBar import co.electriccoin.zcash.ui.design.component.ZashiTextField import co.electriccoin.zcash.ui.design.component.ZashiTextFieldDefaults import co.electriccoin.zcash.ui.design.component.ZecAmountTriple +import co.electriccoin.zcash.ui.design.newcomponent.PreviewScreens import co.electriccoin.zcash.ui.design.theme.ZcashTheme import co.electriccoin.zcash.ui.design.theme.colors.ZashiColors +import co.electriccoin.zcash.ui.design.theme.dimensions.ZashiDimensions import co.electriccoin.zcash.ui.design.theme.typography.ZashiTypography import co.electriccoin.zcash.ui.design.util.scaffoldPadding import co.electriccoin.zcash.ui.design.util.stringRes import co.electriccoin.zcash.ui.fixture.ObserveFiatCurrencyResultFixture import co.electriccoin.zcash.ui.screen.exchangerate.widget.StyledExchangeLabel +import co.electriccoin.zcash.ui.screen.send.ext.abbreviated import co.electriccoin.zcash.ui.screen.sendconfirmation.SendConfirmationTag import co.electriccoin.zcash.ui.screen.sendconfirmation.model.SendConfirmationStage +import com.airbnb.lottie.compose.LottieAnimation +import com.airbnb.lottie.compose.LottieCompositionSpec +import com.airbnb.lottie.compose.LottieConstants +import com.airbnb.lottie.compose.animateLottieCompositionAsState +import com.airbnb.lottie.compose.rememberLottieComposition import kotlinx.collections.immutable.ImmutableList -import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.runBlocking -@Preview -@Composable -private fun SendConfirmationPreview() { - ZcashTheme(forceDarkMode = false) { - SendConfirmation( - snackbarHostState = SnackbarHostState(), - zecSend = - ZecSend( - destination = runBlocking { WalletAddressFixture.sapling() }, - amount = ZatoshiFixture.new(), - memo = MemoFixture.new(), - proposal = null, - ), - onConfirmation = {}, - onBack = {}, - stage = SendConfirmationStage.Confirmation, - topAppBarSubTitleState = TopAppBarSubTitleState.None, - onContactSupport = { _, _ -> }, - submissionResults = emptyList().toImmutableList(), - exchangeRate = ObserveFiatCurrencyResultFixture.new(), - contactName = "Romek" - ) - } -} - -@Preview -@Composable -private fun SendConfirmationDarkPreview() { - ZcashTheme(forceDarkMode = true) { - SendConfirmation( - snackbarHostState = SnackbarHostState(), - zecSend = - ZecSend( - destination = runBlocking { WalletAddressFixture.sapling() }, - amount = ZatoshiFixture.new(), - memo = MemoFixture.new(), - proposal = null, - ), - onConfirmation = {}, - onBack = {}, - stage = SendConfirmationStage.Confirmation, - topAppBarSubTitleState = TopAppBarSubTitleState.None, - onContactSupport = { _, _ -> }, - submissionResults = emptyList().toImmutableList(), - exchangeRate = ObserveFiatCurrencyResultFixture.new(), - contactName = "Romek" - ) - } -} - -@Preview -@Composable -private fun SendMultipleErrorPreview() { - ZcashTheme(forceDarkMode = false) { - SendConfirmation( - snackbarHostState = SnackbarHostState(), - zecSend = - ZecSend( - destination = runBlocking { WalletAddressFixture.sapling() }, - amount = ZatoshiFixture.new(), - memo = MemoFixture.new(), - proposal = null, - ), - onConfirmation = {}, - onBack = {}, - stage = SendConfirmationStage.MultipleTrxFailure, - topAppBarSubTitleState = TopAppBarSubTitleState.None, - onContactSupport = { _, _ -> }, - submissionResults = emptyList().toImmutableList(), - exchangeRate = ObserveFiatCurrencyResultFixture.new(), - contactName = "Romek" - ) - } -} - -@Preview -@Composable -private fun SendMultipleErrorDarkPreview() { - ZcashTheme(forceDarkMode = true) { - SendConfirmation( - snackbarHostState = SnackbarHostState(), - zecSend = - ZecSend( - destination = runBlocking { WalletAddressFixture.sapling() }, - amount = ZatoshiFixture.new(), - memo = MemoFixture.new(), - proposal = null, - ), - onConfirmation = {}, - onBack = {}, - stage = SendConfirmationStage.MultipleTrxFailure, - topAppBarSubTitleState = TopAppBarSubTitleState.None, - onContactSupport = { _, _ -> }, - submissionResults = emptyList().toImmutableList(), - exchangeRate = ObserveFiatCurrencyResultFixture.new(), - contactName = "Romek" - ) - } -} - -@Composable -@Preview("SendConfirmation") -private fun PreviewSendConfirmation() { - ZcashTheme(forceDarkMode = false) { - SendConfirmationContent( - zecSend = - ZecSend( - destination = runBlocking { WalletAddressFixture.sapling() }, - amount = ZatoshiFixture.new(), - memo = MemoFixture.new(), - proposal = null, - ), - onConfirmation = {}, - onBack = {}, - isSending = false, - exchangeRate = ObserveFiatCurrencyResultFixture.new(), - contactName = "Romek" - ) - } -} - -@Preview -@Composable -private fun SendMultipleTransactionFailurePreview() { - ZcashTheme(forceDarkMode = false) { - @Suppress("MagicNumber") - MultipleSubmissionFailure( - onContactSupport = {}, - // Rework this into a test fixture - submissionResults = - persistentListOf( - TransactionSubmitResult.Failure( - FirstClassByteArray("test_transaction_id_1".toByteArray()), - true, - 123, - "test transaction id failure" - ), - TransactionSubmitResult.NotAttempted( - FirstClassByteArray("test_transaction_id_2".toByteArray()) - ), - TransactionSubmitResult.NotAttempted( - FirstClassByteArray("test_transaction_id_3".toByteArray()) - ) - ) - ) - } -} - -@Preview -@Composable -private fun SendMultipleTransactionFailureDarkPreview() { - ZcashTheme(forceDarkMode = true) { - @Suppress("MagicNumber") - MultipleSubmissionFailure( - onContactSupport = {}, - // Rework this into a test fixture - submissionResults = - persistentListOf( - TransactionSubmitResult.Failure( - FirstClassByteArray("test_transaction_id_1".toByteArray()), - true, - 123, - "test transaction id failure" - ), - TransactionSubmitResult.NotAttempted( - FirstClassByteArray("test_transaction_id_2".toByteArray()) - ), - TransactionSubmitResult.NotAttempted( - FirstClassByteArray("test_transaction_id_3".toByteArray()) - ) - ) - ) - } -} - // TODO [#1260]: Cover Send screens UI with tests // TODO [#1260]: https://github.com/Electric-Coin-Company/zashi-android/issues/1260 @@ -262,16 +94,34 @@ private fun SendMultipleTransactionFailureDarkPreview() { fun SendConfirmation( onBack: () -> Unit, onConfirmation: () -> Unit, - onContactSupport: (SendConfirmationStage, String?) -> Unit, + onContactSupport: (SendConfirmationStage) -> Unit, + onMultipleTrxFailureIdsCopy: (String) -> Unit, + onViewTransactions: () -> Unit, snackbarHostState: SnackbarHostState, stage: SendConfirmationStage, submissionResults: ImmutableList, topAppBarSubTitleState: TopAppBarSubTitleState, zecSend: ZecSend, contactName: String?, - exchangeRate: ExchangeRateState + exchangeRate: ExchangeRateState, ) { - BlankBgScaffold( + val gradientColors = + Pair( + when (stage) { + SendConfirmationStage.Prepared, + SendConfirmationStage.Sending -> ZashiColors.Surfaces.bgPrimary + SendConfirmationStage.Success -> ZashiColors.Utility.SuccessGreen.utilitySuccess100 + is SendConfirmationStage.Failure, + is SendConfirmationStage.FailureGrpc, + SendConfirmationStage.MultipleTrxFailure, + SendConfirmationStage.MultipleTrxFailureReported -> ZashiColors.Utility.ErrorRed.utilityError100 + }, + ZashiColors.Surfaces.bgPrimary + ) + + GradientBgScaffold( + startColor = gradientColors.first, + endColor = gradientColors.second, topBar = { SendConfirmationTopAppBar( onBack = onBack, @@ -280,19 +130,32 @@ fun SendConfirmation( ) }, snackbarHost = { SnackbarHost(snackbarHostState) }, + bottomBar = { + SendConfirmationBottomBarContent( + onClose = onBack, + onConfirmation = onConfirmation, + onReport = onContactSupport, + onCopyTrxIds = { + onMultipleTrxFailureIdsCopy( + submissionResults.joinToString(separator = ", ") { it.txIdString() } + ) + }, + stage = stage, + ) + } ) { paddingValues -> SendConfirmationMainContent( - onBack = onBack, - onContactSupport = onContactSupport, - onConfirmation = onConfirmation, + contactName = contactName, + exchangeRate = exchangeRate, + onViewTransactions = onViewTransactions, stage = stage, submissionResults = submissionResults, zecSend = zecSend, modifier = Modifier + .fillMaxSize() + .verticalScroll(rememberScrollState()) .scaffoldPadding(paddingValues), - exchangeRate = exchangeRate, - contactName = contactName ) } } @@ -310,35 +173,49 @@ private fun SendConfirmationTopAppBar( TopAppBarSubTitleState.None -> null } when (stage) { - SendConfirmationStage.Confirmation, - SendConfirmationStage.Sending, - is SendConfirmationStage.Failure, - is SendConfirmationStage.FailureGrpc, - -> { + SendConfirmationStage.Prepared, -> { ZashiSmallTopAppBar( title = stringResource(id = R.string.send_stage_confirmation_title), subtitle = subTitle, ) } - - SendConfirmationStage.MultipleTrxFailure -> { - SmallTopAppBar( - subTitle = subTitle, - titleText = stringResource(id = R.string.send_confirmation_multiple_error_title), + SendConfirmationStage.Sending -> { + ZashiSmallTopAppBar(subtitle = subTitle) + } + SendConfirmationStage.Success -> { + ZashiSmallTopAppBar( + subtitle = subTitle, + colors = + ZcashTheme.colors.topAppBarColors.copyColors( + containerColor = Color.Transparent + ), ) } - SendConfirmationStage.MultipleTrxFailureReported -> { - SmallTopAppBar( - subTitle = subTitle, - titleText = stringResource(id = R.string.send_confirmation_multiple_error_title), + ZashiSmallTopAppBar( + subtitle = subTitle, navigationAction = { TopAppBarBackNavigation( - backText = stringResource(id = R.string.back_navigation).uppercase(), - backContentDescriptionText = stringResource(R.string.back_navigation_content_description), - onBack = onBack + backContentDescriptionText = stringResource(R.string.close_navigation_content_description), + onBack = onBack, + painter = painterResource(co.electriccoin.zcash.ui.design.R.drawable.ic_close) ) }, + colors = + ZcashTheme.colors.topAppBarColors.copyColors( + containerColor = Color.Transparent + ), + ) + } + SendConfirmationStage.MultipleTrxFailure, + is SendConfirmationStage.Failure, + is SendConfirmationStage.FailureGrpc -> { + ZashiSmallTopAppBar( + subtitle = subTitle, + colors = + ZcashTheme.colors.topAppBarColors.copyColors( + containerColor = Color.Transparent + ), ) } } @@ -348,72 +225,451 @@ private fun SendConfirmationTopAppBar( @Suppress("LongParameterList") private fun SendConfirmationMainContent( contactName: String?, + exchangeRate: ExchangeRateState, + onViewTransactions: () -> Unit, stage: SendConfirmationStage, submissionResults: ImmutableList, zecSend: ZecSend, - onBack: () -> Unit, - onContactSupport: (SendConfirmationStage, String?) -> Unit, - onConfirmation: () -> Unit, - exchangeRate: ExchangeRateState, modifier: Modifier = Modifier, ) { - when (stage) { - SendConfirmationStage.Confirmation, - SendConfirmationStage.Sending, - is SendConfirmationStage.Failure, - is SendConfirmationStage.FailureGrpc -> { - SendConfirmationContent( - contactName = contactName, - zecSend = zecSend, - onBack = onBack, - onConfirmation = onConfirmation, - isSending = stage == SendConfirmationStage.Sending, - modifier = modifier, - exchangeRate = exchangeRate - ) - if (stage is SendConfirmationStage.FailureGrpc) { - SendFailureGrpc(onDone = onBack) - } else if (stage is SendConfirmationStage.Failure) { - SendFailure( - onDone = onBack, - onReport = { status -> - // Using [SendConfirmationStage.Confirmation] to dismiss the error dialog - onContactSupport(SendConfirmationStage.Confirmation, status.stackTrace) - }, - stage = stage, + Box(modifier = modifier) { + when (stage) { + SendConfirmationStage.Prepared -> { + SendConfirmationContent( + contactName = contactName, + zecSend = zecSend, + exchangeRate = exchangeRate + ) + } + SendConfirmationStage.Sending -> { + SendingContent(destination = zecSend.destination) + } + SendConfirmationStage.Success -> { + SuccessContent( + destination = zecSend.destination, + onViewTransactions = onViewTransactions ) } + is SendConfirmationStage.Failure -> { + SendFailure(onViewTransactions = onViewTransactions) + } + is SendConfirmationStage.FailureGrpc -> { + SendGrpcFailure() + } + is SendConfirmationStage.MultipleTrxFailure, + SendConfirmationStage.MultipleTrxFailureReported -> { + MultipleSubmissionFailure(submissionResults = submissionResults) + } } + } +} - is SendConfirmationStage.MultipleTrxFailure, SendConfirmationStage.MultipleTrxFailureReported -> { - MultipleSubmissionFailure( - onContactSupport = { - onContactSupport(SendConfirmationStage.MultipleTrxFailureReported, null) - }, - submissionResults = submissionResults, - modifier = modifier +private const val TOP_BLANK_SPACE_RATIO = 0.2f + +@Composable +private fun SendingContent( + destination: WalletAddress, + modifier: Modifier = Modifier +) { + ConstraintLayout(modifier = modifier.fillMaxSize()) { + val (content, spaceTop) = createRefs() + + Spacer( + modifier = + Modifier.constrainAs(spaceTop) { + height = Dimension.percent(TOP_BLANK_SPACE_RATIO) + width = Dimension.fillToConstraints + top.linkTo(parent.top) + bottom.linkTo(content.top) + start.linkTo(parent.start) + end.linkTo(parent.end) + } + ) + + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = + Modifier + .constrainAs(content) { + top.linkTo(spaceTop.bottom) + start.linkTo(parent.start) + end.linkTo(parent.end) + width = Dimension.fillToConstraints + height = Dimension.wrapContent + } + ) { + // TODO [#1667]: Change lottie animation once we have it + // TODO [#1667]: https://github.com/Electric-Coin-Company/zashi-android/issues/1667 + val lottieRes: Int = + if (isSystemInDarkTheme()) { + co.electriccoin.zcash.ui.design.R.raw.lottie_loading_white + } else { + co.electriccoin.zcash.ui.design.R.raw.lottie_loading + } + + val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(lottieRes)) + val progress by animateLottieCompositionAsState( + iterations = LottieConstants.IterateForever, + composition = composition + ) + + LottieAnimation( + modifier = Modifier.size(200.dp), + composition = composition, + progress = { progress }, + maintainOriginalImageBounds = true + ) + + Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacing2xl)) + + Text( + fontWeight = FontWeight.SemiBold, + style = ZashiTypography.header5, + text = stringResource(id = R.string.send_confirmation_sending_title), + ) + + Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacingLg)) + + Text( + fontWeight = FontWeight.Normal, + style = ZashiTypography.textSm, + text = stringResource(id = R.string.send_confirmation_sending_subtitle, destination.abbreviated()), + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center + ) + } + } +} + +private fun provideRandomResourceFrom(resources: List) = resources.random() + +@Composable +private fun SuccessContent( + destination: WalletAddress, + onViewTransactions: () -> Unit, + modifier: Modifier = Modifier +) { + ConstraintLayout(modifier = modifier.fillMaxSize()) { + val (content, spaceTop) = createRefs() + + Spacer( + modifier = + Modifier.constrainAs(spaceTop) { + height = Dimension.percent(TOP_BLANK_SPACE_RATIO) + width = Dimension.fillToConstraints + top.linkTo(parent.top) + bottom.linkTo(content.top) + start.linkTo(parent.start) + end.linkTo(parent.end) + } + ) + + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = + Modifier + .constrainAs(content) { + top.linkTo(spaceTop.bottom) + start.linkTo(parent.start) + end.linkTo(parent.end) + width = Dimension.fillToConstraints + height = Dimension.wrapContent + } + ) { + Image( + painter = + painterResource( + id = + provideRandomResourceFrom( + listOf( + R.drawable.ic_fist_punch, + R.drawable.ic_face_star + ) + ) + ), + contentDescription = null + ) + + Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacing2xl)) + + Text( + fontWeight = FontWeight.SemiBold, + style = ZashiTypography.header5, + text = stringResource(id = R.string.send_confirmation_success_title), + ) + + Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacingLg)) + + Text( + fontWeight = FontWeight.Normal, + style = ZashiTypography.textSm, + text = stringResource(id = R.string.send_confirmation_success_subtitle, destination.abbreviated()), + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center + ) + + Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacingXl)) + + ZashiButton( + state = + ButtonState( + text = stringRes(R.string.send_confirmation_success_view_trx), + onClick = onViewTransactions, + ), + modifier = + Modifier.wrapContentWidth(), + colors = ZashiButtonDefaults.tertiaryColors() ) } } } @Composable -@Suppress("LongMethod", "LongParameterList") +private fun SendFailure( + onViewTransactions: () -> Unit, + modifier: Modifier = Modifier +) { + ConstraintLayout(modifier = modifier.fillMaxSize()) { + val (content, spaceTop) = createRefs() + + Spacer( + modifier = + Modifier.constrainAs(spaceTop) { + height = Dimension.percent(TOP_BLANK_SPACE_RATIO) + width = Dimension.fillToConstraints + top.linkTo(parent.top) + bottom.linkTo(content.top) + start.linkTo(parent.start) + end.linkTo(parent.end) + } + ) + + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = + Modifier + .constrainAs(content) { + top.linkTo(spaceTop.bottom) + start.linkTo(parent.start) + end.linkTo(parent.end) + width = Dimension.fillToConstraints + height = Dimension.wrapContent + } + ) { + Image( + painter = + painterResource( + id = + provideRandomResourceFrom( + listOf( + R.drawable.ic_skull, + R.drawable.ic_cloud_eyes, + R.drawable.ic_face_horns + ) + ) + ), + contentDescription = null + ) + + Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacing2xl)) + + Text( + fontWeight = FontWeight.SemiBold, + style = ZashiTypography.header5, + text = stringResource(id = R.string.send_confirmation_failure_title), + ) + + Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacingLg)) + + Text( + fontWeight = FontWeight.Normal, + style = ZashiTypography.textSm, + text = stringResource(id = R.string.send_confirmation_failure_subtitle), + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center + ) + + Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacingXl)) + + ZashiButton( + state = + ButtonState( + text = stringRes(R.string.send_confirmation_failure_view_trx), + onClick = onViewTransactions, + ), + modifier = + Modifier.wrapContentWidth(), + colors = ZashiButtonDefaults.tertiaryColors() + ) + } + } +} + +@Composable +private fun SendGrpcFailure(modifier: Modifier = Modifier) { + ConstraintLayout(modifier = modifier.fillMaxSize()) { + val (content, spaceTop) = createRefs() + + Spacer( + modifier = + Modifier.constrainAs(spaceTop) { + height = Dimension.percent(TOP_BLANK_SPACE_RATIO) + width = Dimension.fillToConstraints + top.linkTo(parent.top) + bottom.linkTo(content.top) + start.linkTo(parent.start) + end.linkTo(parent.end) + } + ) + + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = + Modifier + .constrainAs(content) { + top.linkTo(spaceTop.bottom) + start.linkTo(parent.start) + end.linkTo(parent.end) + width = Dimension.fillToConstraints + height = Dimension.wrapContent + } + ) { + Image( + painter = + painterResource( + provideRandomResourceFrom( + listOf( + R.drawable.ic_frame, + R.drawable.ic_phone + ) + ) + ), + contentDescription = null + ) + + Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacing2xl)) + + Text( + fontWeight = FontWeight.SemiBold, + style = ZashiTypography.header5, + text = stringResource(id = R.string.send_confirmation_failure_grpc_title), + ) + + Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacingLg)) + + Text( + fontWeight = FontWeight.Normal, + style = ZashiTypography.textSm, + text = stringResource(id = R.string.send_confirmation_failure_grpc_subtitle), + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center + ) + } + } +} + +@Composable +fun MultipleSubmissionFailure( + submissionResults: ImmutableList, + modifier: Modifier = Modifier +) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = modifier, + ) { + Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacingLg)) + + Image( + imageVector = ImageVector.vectorResource(R.drawable.ic_multi_trx_send_failed), + contentDescription = null, + ) + + Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacingXl)) + + Text( + fontWeight = FontWeight.SemiBold, + style = ZashiTypography.header6, + text = stringResource(id = R.string.send_confirmation_multiple_trx_failure_title), + color = ZashiColors.Text.textPrimary + ) + + Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacingLg)) + + Text( + style = ZashiTypography.textSm, + text = stringResource(id = R.string.send_confirmation_multiple_trx_failure_text_1), + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, + color = ZashiColors.Text.textPrimary + ) + + Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacingLg)) + + Text( + style = ZashiTypography.textSm, + text = stringResource(id = R.string.send_confirmation_multiple_trx_failure_text_2), + modifier = Modifier.fillMaxWidth(), + textAlign = TextAlign.Center, + color = ZashiColors.Text.textPrimary + ) + + Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacing4xl)) + + if (submissionResults.isNotEmpty()) { + TransactionSubmitResultWidget(submissionResults) + } + } +} + +@Composable +fun TransactionSubmitResultWidget( + submissionResults: ImmutableList, + modifier: Modifier = Modifier +) { + Column(modifier = modifier) { + Text( + text = stringResource(id = R.string.send_confirmation_multiple_trx_failure_ids_title), + fontWeight = FontWeight.Medium, + style = ZashiTypography.textSm, + color = ZashiColors.Inputs.Default.label + ) + + Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacingSm)) + + submissionResults.forEach { item -> + Text( + text = item.txIdString(), + overflow = TextOverflow.Ellipsis, + maxLines = 1, + style = ZashiTypography.textMd, + color = ZashiColors.Inputs.Default.text, + modifier = + Modifier + .background( + shape = RoundedCornerShape(ZashiDimensions.Radius.radiusIg), + color = ZashiColors.Inputs.Default.bg + ) + .padding( + horizontal = ZashiDimensions.Spacing.spacingLg, + vertical = ZashiDimensions.Spacing.spacingMd + ) + ) + Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacingMd)) + } + } +} + +@Composable +@Suppress("LongMethod") private fun SendConfirmationContent( contactName: String?, zecSend: ZecSend, - exchangeRate: ExchangeRateState, - isSending: Boolean, - onConfirmation: () -> Unit, - onBack: () -> Unit, + exchangeRate: ExchangeRateState, modifier: Modifier = Modifier, ) { Column( horizontalAlignment = Alignment.Start, - modifier = - modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()), + modifier = modifier.fillMaxSize(), ) { Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall)) @@ -571,27 +827,58 @@ private fun SendConfirmationContent( Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingUpLarge)) } + } +} - Spacer( - modifier = - Modifier - .fillMaxHeight() - .weight(MINIMAL_WEIGHT) - ) - - SendConfirmationActionButtons( - isSending = isSending, - onBack = onBack, - onConfirmation = onConfirmation - ) +@Composable +private fun SendConfirmationBottomBarContent( + onClose: () -> Unit, + onConfirmation: () -> Unit, + onReport: (SendConfirmationStage) -> Unit, + onCopyTrxIds: () -> Unit, + stage: SendConfirmationStage, + modifier: Modifier = Modifier, +) { + ZashiBottomBar(modifier = modifier) { + when (stage) { + SendConfirmationStage.Prepared -> { + SendConfirmationBottomBar( + onConfirmation = onConfirmation, + onBack = onClose, + ) + } + is SendConfirmationStage.Failure -> { + SendFailureBottomBar( + onClose = onClose, + onReport = { onReport(stage) }, + ) + } + SendConfirmationStage.FailureGrpc -> { + SendGrpcFailureBottomBar( + onClose = onClose, + ) + } + SendConfirmationStage.MultipleTrxFailure, + SendConfirmationStage.MultipleTrxFailureReported -> { + SendMultipleTrxFailureBottomBar( + onCopyTrxIds = onCopyTrxIds, + onContactSupport = onReport, + ) + } + SendConfirmationStage.Sending -> { /* No bottom bar in this stage */ } + SendConfirmationStage.Success -> { + SendSuccessBottomBar( + onClose = onClose, + ) + } + } } } @Composable -fun SendConfirmationActionButtons( +fun SendConfirmationBottomBar( onConfirmation: () -> Unit, onBack: () -> Unit, - isSending: Boolean, modifier: Modifier = Modifier ) { Column( @@ -601,28 +888,27 @@ fun SendConfirmationActionButtons( state = ButtonState( text = stringRes(R.string.send_confirmation_send_button), - onClick = onConfirmation, - isEnabled = !isSending, - isLoading = isSending, + onClick = onConfirmation ), modifier = Modifier .fillMaxWidth() + .padding(horizontal = ZashiDimensions.Spacing.spacing2xl) .testTag(SendConfirmationTag.SEND_CONFIRMATION_SEND_BUTTON) ) - Spacer(modifier = Modifier.height(12.dp)) + Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacingLg)) ZashiButton( state = ButtonState( text = stringRes(R.string.send_confirmation_back_button), onClick = onBack, - isEnabled = !isSending, ), modifier = Modifier .fillMaxWidth() + .padding(horizontal = ZashiDimensions.Spacing.spacing2xl) .testTag(SendConfirmationTag.SEND_CONFIRMATION_BACK_BUTTON), colors = ZashiButtonDefaults.tertiaryColors() ) @@ -630,161 +916,302 @@ fun SendConfirmationActionButtons( } @Composable -@Preview("SendConfirmationFailure") -private fun PreviewSendConfirmationFailure() { - ZcashTheme(forceDarkMode = false) { - BlankSurface { - SendFailure( - onDone = {}, - onReport = {}, - stage = SendConfirmationStage.Failure("Failed - network error", "Failed stackTrace"), - ) - } - } -} - -@Composable -private fun SendFailure( - onDone: () -> Unit, - onReport: (SendConfirmationStage.Failure) -> Unit, - stage: SendConfirmationStage.Failure, +fun SendMultipleTrxFailureBottomBar( + onCopyTrxIds: () -> Unit, + onContactSupport: (SendConfirmationStage) -> Unit, + modifier: Modifier = Modifier ) { - // TODO [#1276]: Once we ensure that the reason contains a localized message, we can leverage it for the UI prompt - // TODO [#1276]: Consider adding support for a specific exception in AppAlertDialog - // TODO [#1276]: https://github.com/Electric-Coin-Company/zashi-android/issues/1276 - - AppAlertDialog( - title = stringResource(id = R.string.send_confirmation_dialog_error_title), - text = { - Column( - Modifier.verticalScroll(rememberScrollState()) - ) { - Text( - text = stringResource(id = R.string.send_confirmation_dialog_error_text), - color = ZcashTheme.colors.textPrimary, - ) + Column( + modifier = modifier + ) { + ZashiButton( + state = + ButtonState( + text = stringRes(R.string.send_confirmation_multiple_trx_failure_copy_button), + onClick = onCopyTrxIds + ), + modifier = + Modifier + .fillMaxWidth() + .padding(horizontal = ZashiDimensions.Spacing.spacing2xl), + colors = ZashiButtonDefaults.tertiaryColors() + ) - if (stage.error.isNotEmpty()) { - Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault)) + Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacingSm)) - Text( - text = stage.error, - fontStyle = FontStyle.Italic, - color = ZcashTheme.colors.textPrimary, - ) - } - } - }, - confirmButtonText = stringResource(id = R.string.send_confirmation_dialog_error_ok_btn), - onConfirmButtonClick = onDone, - dismissButtonText = stringResource(id = R.string.send_confirmation_dialog_error_report_btn), - onDismissButtonClick = { onReport(stage) }, - ) + ZashiButton( + state = + ButtonState( + text = stringRes(R.string.send_confirmation_multiple_trx_failure_report_button), + onClick = { onContactSupport(SendConfirmationStage.MultipleTrxFailureReported) }, + ), + modifier = + Modifier + .fillMaxWidth() + .padding(horizontal = ZashiDimensions.Spacing.spacing2xl), + ) + } } @Composable -private fun SendFailureGrpc(onDone: () -> Unit) { - AppAlertDialog( - title = stringResource(id = R.string.send_confirmation_dialog_error_grpc_title), - text = { - Column( - Modifier.verticalScroll(rememberScrollState()) - ) { - Text( - text = stringResource(id = R.string.send_confirmation_dialog_error_grpc_text), - color = ZcashTheme.colors.textPrimary, - ) - } - }, - confirmButtonText = stringResource(id = R.string.send_confirmation_dialog_error_grpc_btn), - onConfirmButtonClick = onDone - ) +fun SendSuccessBottomBar( + onClose: () -> Unit, + modifier: Modifier = Modifier +) { + Column( + modifier = modifier + ) { + ZashiButton( + state = + ButtonState( + text = stringRes(R.string.send_confirmation_success_btn_close), + onClick = onClose + ), + modifier = + Modifier + .fillMaxWidth() + .padding(horizontal = ZashiDimensions.Spacing.spacing2xl) + ) + } } @Composable -fun MultipleSubmissionFailure( - onContactSupport: () -> Unit, - submissionResults: ImmutableList, +fun SendFailureBottomBar( + onClose: () -> Unit, + onReport: () -> Unit, modifier: Modifier = Modifier ) { Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = - modifier - .fillMaxSize() - .verticalScroll(rememberScrollState()), + modifier = modifier ) { - Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall)) - - Image( - imageVector = ImageVector.vectorResource(R.drawable.ic_zashi_logo_sign_warn), - colorFilter = ColorFilter.tint(color = ZcashTheme.colors.secondaryColor), - contentDescription = null, - ) - - Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingBig)) - - Body( - text = stringResource(id = R.string.send_confirmation_multiple_error_text_1), - textAlign = TextAlign.Center - ) - - Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault)) - - Body( - text = stringResource(id = R.string.send_confirmation_multiple_error_text_2), - textAlign = TextAlign.Center + ZashiButton( + state = + ButtonState( + text = stringRes(R.string.send_confirmation_failure_close_button), + onClick = onClose + ), + modifier = + Modifier + .fillMaxWidth() + .padding(horizontal = ZashiDimensions.Spacing.spacing2xl) ) - Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingBig)) - - if (submissionResults.isNotEmpty()) { - TransactionSubmitResultWidget(submissionResults) - } - - Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingDefault)) - - Spacer(modifier = Modifier.weight(1f, true)) + Spacer(modifier = Modifier.height(ZashiDimensions.Spacing.spacingLg)) ZashiButton( - modifier = Modifier.fillMaxWidth(), - onClick = onContactSupport, - text = stringResource(id = R.string.send_confirmation_multiple_error_btn) + state = + ButtonState( + text = stringRes(R.string.send_confirmation_failure_report_button), + onClick = onReport + ), + modifier = + Modifier + .fillMaxWidth() + .padding(horizontal = ZashiDimensions.Spacing.spacing2xl), + colors = ZashiButtonDefaults.tertiaryColors() ) - - Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingHuge)) } } @Composable -fun TransactionSubmitResultWidget( - submissionResults: ImmutableList, +fun SendGrpcFailureBottomBar( + onClose: () -> Unit, modifier: Modifier = Modifier ) { Column( - horizontalAlignment = Alignment.CenterHorizontally, modifier = modifier ) { - Small(text = stringResource(id = R.string.send_confirmation_multiple_error_trx_title)) + ZashiButton( + state = + ButtonState( + text = stringRes(R.string.send_confirmation_failure_grpc_close_button), + onClick = onClose + ), + modifier = + Modifier + .fillMaxWidth() + .padding(horizontal = ZashiDimensions.Spacing.spacing2xl) + ) + } +} - Spacer(modifier = Modifier.height(ZcashTheme.dimens.spacingSmall)) +@PreviewScreens +@Composable +private fun SendConfirmationPreview() { + ZcashTheme { + SendConfirmation( + snackbarHostState = SnackbarHostState(), + zecSend = + ZecSend( + destination = runBlocking { WalletAddressFixture.sapling() }, + amount = ZatoshiFixture.new(), + memo = MemoFixture.new(), + proposal = null, + ), + onConfirmation = {}, + onMultipleTrxFailureIdsCopy = {}, + onViewTransactions = {}, + onBack = {}, + stage = SendConfirmationStage.Prepared, + topAppBarSubTitleState = TopAppBarSubTitleState.None, + onContactSupport = { _ -> }, + submissionResults = emptyList().toImmutableList(), + exchangeRate = ObserveFiatCurrencyResultFixture.new(), + contactName = "Mom" + ) + } +} - submissionResults.forEachIndexed { index, item -> - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.Center - ) { - Small( - text = - stringResource( - id = R.string.send_confirmation_multiple_error_trx_item, - index + 1 - ), - modifier = Modifier.wrapContentSize() - ) - Spacer(modifier = Modifier.width(ZcashTheme.dimens.spacingTiny)) - Small(text = item.txIdString()) - } - } +@PreviewScreens +@Composable +private fun SendingPreview() { + ZcashTheme { + SendConfirmation( + snackbarHostState = SnackbarHostState(), + zecSend = + ZecSend( + destination = runBlocking { WalletAddressFixture.sapling() }, + amount = ZatoshiFixture.new(), + memo = MemoFixture.new(), + proposal = null, + ), + onBack = {}, + onConfirmation = {}, + onMultipleTrxFailureIdsCopy = {}, + onViewTransactions = {}, + stage = SendConfirmationStage.Sending, + topAppBarSubTitleState = TopAppBarSubTitleState.None, + onContactSupport = { _ -> }, + submissionResults = emptyList().toImmutableList(), + exchangeRate = ObserveFiatCurrencyResultFixture.new(), + contactName = "Mom" + ) + } +} + +@PreviewScreens +@Composable +private fun SuccessPreview() { + ZcashTheme { + SendConfirmation( + snackbarHostState = SnackbarHostState(), + zecSend = + ZecSend( + destination = runBlocking { WalletAddressFixture.sapling() }, + amount = ZatoshiFixture.new(), + memo = MemoFixture.new(), + proposal = null, + ), + onBack = {}, + onConfirmation = {}, + onMultipleTrxFailureIdsCopy = {}, + onViewTransactions = {}, + stage = SendConfirmationStage.Success, + topAppBarSubTitleState = TopAppBarSubTitleState.None, + onContactSupport = { _ -> }, + submissionResults = emptyList().toImmutableList(), + exchangeRate = ObserveFiatCurrencyResultFixture.new(), + contactName = "Mom" + ) + } +} + +@PreviewScreens +@Composable +private fun PreviewSendConfirmationFailure() { + ZcashTheme { + SendConfirmation( + snackbarHostState = SnackbarHostState(), + zecSend = + ZecSend( + destination = runBlocking { WalletAddressFixture.sapling() }, + amount = ZatoshiFixture.new(), + memo = MemoFixture.new(), + proposal = null, + ), + onBack = {}, + onConfirmation = {}, + onMultipleTrxFailureIdsCopy = {}, + onViewTransactions = {}, + stage = + SendConfirmationStage.Failure( + "The transaction has not been successfully created...", + "Failed stackTrace..." + ), + topAppBarSubTitleState = TopAppBarSubTitleState.None, + onContactSupport = { _ -> }, + submissionResults = emptyList().toImmutableList(), + exchangeRate = ObserveFiatCurrencyResultFixture.new(), + contactName = "Mom" + ) + } +} + +@PreviewScreens +@Composable +private fun PreviewSendConfirmationGrpcFailure() { + ZcashTheme { + SendConfirmation( + snackbarHostState = SnackbarHostState(), + zecSend = + ZecSend( + destination = runBlocking { WalletAddressFixture.sapling() }, + amount = ZatoshiFixture.new(), + memo = MemoFixture.new(), + proposal = null, + ), + onBack = {}, + onConfirmation = {}, + onMultipleTrxFailureIdsCopy = {}, + onViewTransactions = {}, + stage = SendConfirmationStage.FailureGrpc, + topAppBarSubTitleState = TopAppBarSubTitleState.None, + onContactSupport = { _ -> }, + submissionResults = emptyList().toImmutableList(), + exchangeRate = ObserveFiatCurrencyResultFixture.new(), + contactName = "Mom" + ) + } +} + +@PreviewScreens +@Composable +private fun SendMultipleErrorPreview() { + ZcashTheme { + SendConfirmation( + snackbarHostState = SnackbarHostState(), + zecSend = + ZecSend( + destination = runBlocking { WalletAddressFixture.sapling() }, + amount = ZatoshiFixture.new(), + memo = MemoFixture.new(), + proposal = null, + ), + onBack = {}, + onConfirmation = {}, + onMultipleTrxFailureIdsCopy = {}, + onViewTransactions = {}, + stage = SendConfirmationStage.MultipleTrxFailure, + topAppBarSubTitleState = TopAppBarSubTitleState.None, + onContactSupport = { _ -> }, + submissionResults = + listOf( + TransactionSubmitResult.Success(FirstClassByteArray("test_transaction_id_1".toByteArray())), + TransactionSubmitResult.Failure( + FirstClassByteArray("test_transaction_id_2".toByteArray()), + true, + Int.MIN_VALUE, + "test transaction id failure" + ), + TransactionSubmitResult.NotAttempted( + FirstClassByteArray("test_transaction_id_3".toByteArray()) + ), + TransactionSubmitResult.NotAttempted( + FirstClassByteArray("test_transaction_id_4".toByteArray()) + ) + ).toImmutableList(), + exchangeRate = ObserveFiatCurrencyResultFixture.new(), + contactName = "Romek" + ) } } diff --git a/ui-lib/src/main/res/ui/common/values-es/strings.xml b/ui-lib/src/main/res/ui/common/values-es/strings.xml index 029dc5213..0782bf1be 100644 --- a/ui-lib/src/main/res/ui/common/values-es/strings.xml +++ b/ui-lib/src/main/res/ui/common/values-es/strings.xml @@ -1,6 +1,7 @@ Atrás Atrás + Close No disponible - diff --git a/ui-lib/src/main/res/ui/common/values/strings.xml b/ui-lib/src/main/res/ui/common/values/strings.xml index cea380ba1..2d3b80e61 100644 --- a/ui-lib/src/main/res/ui/common/values/strings.xml +++ b/ui-lib/src/main/res/ui/common/values/strings.xml @@ -1,6 +1,7 @@ Back Back + Close Unavailable - diff --git a/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_cloud_eyes.xml b/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_cloud_eyes.xml new file mode 100644 index 000000000..2b3fbf7c1 --- /dev/null +++ b/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_cloud_eyes.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_face_horns.xml b/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_face_horns.xml new file mode 100644 index 000000000..33c84ce2d --- /dev/null +++ b/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_face_horns.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + diff --git a/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_face_star.xml b/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_face_star.xml new file mode 100644 index 000000000..5290f6933 --- /dev/null +++ b/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_face_star.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_fist_punch.xml b/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_fist_punch.xml new file mode 100644 index 000000000..6ac8e55ff --- /dev/null +++ b/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_fist_punch.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + diff --git a/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_frame.xml b/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_frame.xml new file mode 100644 index 000000000..1ad792084 --- /dev/null +++ b/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_frame.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_multi_trx_send_failed.xml b/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_multi_trx_send_failed.xml new file mode 100644 index 000000000..39b620f64 --- /dev/null +++ b/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_multi_trx_send_failed.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + diff --git a/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_phone.xml b/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_phone.xml new file mode 100644 index 000000000..6102899b4 --- /dev/null +++ b/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_phone.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_skull.xml b/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_skull.xml new file mode 100644 index 000000000..f1cc3983e --- /dev/null +++ b/ui-lib/src/main/res/ui/send_confirmation/drawable-night/ic_skull.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + diff --git a/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_cloud_eyes.xml b/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_cloud_eyes.xml new file mode 100644 index 000000000..a75df317e --- /dev/null +++ b/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_cloud_eyes.xml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_face_horns.xml b/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_face_horns.xml new file mode 100644 index 000000000..40114529d --- /dev/null +++ b/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_face_horns.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + diff --git a/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_face_star.xml b/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_face_star.xml new file mode 100644 index 000000000..348e90f07 --- /dev/null +++ b/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_face_star.xml @@ -0,0 +1,22 @@ + + + + + + + + + diff --git a/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_fist_punch.xml b/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_fist_punch.xml new file mode 100644 index 000000000..52474313c --- /dev/null +++ b/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_fist_punch.xml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + diff --git a/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_frame.xml b/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_frame.xml new file mode 100644 index 000000000..10403ac1c --- /dev/null +++ b/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_frame.xml @@ -0,0 +1,15 @@ + + + + + diff --git a/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_multi_trx_send_failed.xml b/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_multi_trx_send_failed.xml new file mode 100644 index 000000000..e4a1c744b --- /dev/null +++ b/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_multi_trx_send_failed.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + diff --git a/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_phone.xml b/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_phone.xml new file mode 100644 index 000000000..c0803add4 --- /dev/null +++ b/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_phone.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_skull.xml b/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_skull.xml new file mode 100644 index 000000000..111575a3d --- /dev/null +++ b/ui-lib/src/main/res/ui/send_confirmation/drawable/ic_skull.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + diff --git a/ui-lib/src/main/res/ui/send_confirmation/values-es/strings.xml b/ui-lib/src/main/res/ui/send_confirmation/values-es/strings.xml index efad2f3d4..01436b2f6 100644 --- a/ui-lib/src/main/res/ui/send_confirmation/values-es/strings.xml +++ b/ui-lib/src/main/res/ui/send_confirmation/values-es/strings.xml @@ -9,23 +9,39 @@ Enviar Cancelar - Transacción Fallida - Ocurrió un error y el intento de enviar fondos falló. Inténtelo de nuevo, por favor. - OK - Reportar - - Error de Conexión - Zashi encontró algunos problemas de conexión al enviar la transacción a la red. Volverá a intentarlo durante los próximos minutos. - OK - - Error de Transacción - Enviar a este destinatario requirió múltiples transacciones, pero solo algunas de ellas se completaron con éxito. Tus fondos están seguros, pero deben recuperarse con la ayuda del soporte del equipo de Zashi. - Por favor, usa el botón de abajo para contactarnos y recuperar tus fondos. Tu mensaje para nosotros estará pre-rellenado con todos los datos necesarios para resolver este problema. - IDs DE TRANSACCIÓN: - - %1$d. ID: + Sending… + Your tokens are being sent to\n + %1$s - Contactar Soporte + + Sent! + Your tokens were successfully sent to\n + %1$s + + View Transaction + Close + + Send Failed + There was an error attempting to send tokens.\n + Try it again, please. + View Transaction + Close + Report + + Connection Error + Zashi encountered connection issues when submitting the + transaction. It will retry in the next few minutes. + Close + + Send Failed + Send to this recipient required multiple + transactions but only some of them succeeded and the rest failed. Your funds are safe but need to be + recovered with assistance from our side. + Please use the button below to contact us. it + automatically prepares all the data we need in order to help you restore your funds. + Copy transaction IDs + Contact Support + Transaction IDs Hola, equipo de Zashi.\n\nAl enviar una transacción a una dirección TEX, encontré un estado de error. Me pongo en contacto para recibir orientación sobre cómo recuperar mis fondos.\n\nGracias. Estados de la transacción: @@ -41,6 +57,7 @@ código: %3$d, descripción: %4$s - No se pudo iniciar la aplicación de correo. + Unable to launch email app. + Transaction IDs diff --git a/ui-lib/src/main/res/ui/send_confirmation/values/strings.xml b/ui-lib/src/main/res/ui/send_confirmation/values/strings.xml index ef716e458..94cbf1ec7 100644 --- a/ui-lib/src/main/res/ui/send_confirmation/values/strings.xml +++ b/ui-lib/src/main/res/ui/send_confirmation/values/strings.xml @@ -9,28 +9,39 @@ Send Cancel - Transaction Failed - An error occurred and the attempt to send funds failed. Try it again, please. - OK - Report - - Connection Error - Zashi encountered some connection issues when submitting - the transaction to the network. It will retry during the next few minutes. - OK + Sending… + Your tokens are being sent to\n + %1$s + - Transaction error - Sending to this recipient required multiple transactions, - but only some of them succeeded. Your funds are safe, but they need to be recovered with the help of Zashi - team support. - Please use the button below to contact us and recover your - funds. Your message to us will be pre-populated with all the data we need to resolve this issue. - TRANSACTION IDs: - - %1$d. ID: + Sent! + Your tokens were successfully sent to\n + %1$s - Contact Support + View Transaction + Close + + Send Failed + There was an error attempting to send tokens.\n + Try it again, please. + View Transaction + Close + Report + + Connection Error + Zashi encountered connection issues when submitting the + transaction. It will retry in the next few minutes. + Close + Send Failed + Send to this recipient required multiple + transactions but only some of them succeeded and the rest failed. Your funds are safe but need to be + recovered with assistance from our side. + Please use the button below to contact us. it + automatically prepares all the data we need in order to help you restore your funds. + Copy transaction IDs + Contact Support + Transaction IDs Hi, Zashi Team.\n\nWhile sending a transaction to a TEX address, I encountered an error state. I\'m reaching out to get guidance on how to recover my funds.\n\nThank you. @@ -47,6 +58,7 @@ code: %3$d, description: %4$s - Unable to launch email app. + Unable to launch email app. + Transaction IDs