diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/CommonSendRoute.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/CommonSendRoute.kt index 7aaa0a1286..ed717d0c97 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/CommonSendRoute.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/CommonSendRoute.kt @@ -18,6 +18,11 @@ internal sealed class CommonSendRoute : Route { override val isEditMode: Boolean = true } + @Serializable + data object ConfirmSuccess : CommonSendRoute() { + override val isEditMode: Boolean = false + } + @Serializable data class Destination( override val isEditMode: Boolean, diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/ui/SendContent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/ui/SendContent.kt index 5edf831c46..678d84dff3 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/ui/SendContent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/common/ui/SendContent.kt @@ -6,8 +6,7 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import com.arkivanov.decompose.extensions.compose.stack.Children -import com.arkivanov.decompose.extensions.compose.stack.animation.slide -import com.arkivanov.decompose.extensions.compose.stack.animation.stackAnimation +import com.arkivanov.decompose.extensions.compose.stack.animation.* import com.arkivanov.decompose.router.stack.ChildStack import com.tangem.common.ui.navigationButtons.NavigationUM import com.tangem.core.ui.components.appbar.AppBarWithBackButtonAndIcon @@ -15,6 +14,8 @@ import com.tangem.core.ui.decompose.ComposableContentComponent import com.tangem.core.ui.extensions.resolveReference import com.tangem.core.ui.res.TangemTheme import com.tangem.features.send.v2.common.CommonSendRoute +import com.tangem.features.send.v2.send.confirm.SendConfirmComponent +import com.tangem.features.send.v2.send.success.SendConfirmSuccessComponent @Composable internal fun SendContent( @@ -32,12 +33,20 @@ internal fun SendContent( SendAppBar(navigationUM = navigationUM) Children( stack = stackState, - animation = stackAnimation(slide()), + animation = stackAnimation { child -> + when (child.instance) { + is SendConfirmSuccessComponent -> fade(minAlpha = 1.0f) + is SendConfirmComponent -> fade() + else -> slide() + } + }, modifier = Modifier.weight(1f), ) { it.instance.Content(Modifier.weight(1f)) } - SendNavigationButtons(navigationUM = navigationUM) + if (stackState.active.configuration != CommonSendRoute.ConfirmSuccess) { + SendNavigationButtons(navigationUM = navigationUM) + } } } diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt index da4ee0e26c..dd7a643b22 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/DefaultSendComponent.kt @@ -14,6 +14,7 @@ import com.arkivanov.decompose.value.subscribe import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.context.child import com.tangem.core.decompose.context.childByContext import com.tangem.core.decompose.model.getOrCreateModel import com.tangem.core.decompose.navigation.inner.InnerRouter @@ -30,11 +31,14 @@ import com.tangem.features.send.v2.common.utils.safeNextClick import com.tangem.features.send.v2.impl.R import com.tangem.features.send.v2.send.confirm.SendConfirmComponent import com.tangem.features.send.v2.send.model.SendModel +import com.tangem.features.send.v2.send.success.SendConfirmSuccessComponent import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponent import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponentParams +import com.tangem.features.send.v2.subcomponents.fee.SendFeeBlockComponent import com.tangem.features.send.v2.subcomponents.destination.DefaultSendDestinationComponent import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationComponentParams import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM +import com.tangem.features.send.v2.subcomponents.destination.DefaultSendDestinationBlockComponent import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponent import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponentParams import dagger.assisted.Assisted @@ -44,6 +48,7 @@ import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.filterIsInstance import kotlinx.coroutines.launch +@Suppress("LargeClass") internal class DefaultSendComponent @AssistedInject constructor( @Assisted appComponentContext: AppComponentContext, @Assisted private val params: SendComponent.Params, @@ -142,7 +147,8 @@ internal class DefaultSendComponent @AssistedInject constructor( is CommonSendRoute.Destination -> getDestinationComponent(factoryContext, route) is CommonSendRoute.Amount -> getAmountComponent(factoryContext, route) is CommonSendRoute.Fee -> getFeeComponent(factoryContext) - CommonSendRoute.Confirm -> getConfirmComponent(factoryContext) + is CommonSendRoute.Confirm -> getConfirmComponent(factoryContext) + is CommonSendRoute.ConfirmSuccess -> getConfirmSuccessComponent(factoryContext) } private fun getDestinationComponent( @@ -288,6 +294,9 @@ internal class DefaultSendComponent @AssistedInject constructor( callback = model, predefinedValues = model.predefinedValues, onLoadFee = model::loadFee, + onSendTransaction = { + innerRouter.replaceAll(CommonSendRoute.ConfirmSuccess) + }, ), ) } else { @@ -296,6 +305,69 @@ internal class DefaultSendComponent @AssistedInject constructor( } } + private fun getConfirmSuccessComponent(factoryContext: AppComponentContext): ComposableContentComponent { + val state = model.uiState.value + val sendAmount = (state.amountUM as? AmountState.Data)?.amountTextField?.cryptoAmount?.value + val destinationAddress = (state.destinationUM as? DestinationUM.Content)?.addressTextField?.value + val txUrl = (state.confirmUM as? ConfirmUM.Success)?.txUrl + val cryptoCurrencyStatus = model.cryptoCurrencyStatusFlow.value + val feeCryptoCurrencyStatus = model.feeCryptoCurrencyStatusFlow.value + + if (sendAmount == null || + destinationAddress == null || + txUrl == null + ) { + model.showAlertError() + return getStubComponent() + } + + val destinationBlockComponent = + DefaultSendDestinationBlockComponent( + appComponentContext = child("sendConfirmDestinationBlock"), + params = SendDestinationComponentParams.DestinationBlockParams( + state = model.uiState.value.destinationUM, + analyticsCategoryName = analyticCategoryName, + userWalletId = model.userWallet.walletId, + cryptoCurrency = cryptoCurrencyStatus.currency, + blockClickEnableFlow = MutableStateFlow(true), + predefinedValues = model.predefinedValues, + ), + onResult = { }, + onClick = {}, + ) + + val feeBlockComponent = SendFeeBlockComponent( + appComponentContext = child("sendConfirmFeeBlock"), + params = SendFeeComponentParams.FeeBlockParams( + state = model.uiState.value.feeUM, + analyticsCategoryName = analyticCategoryName, + userWallet = model.userWallet, + cryptoCurrencyStatus = cryptoCurrencyStatus, + feeCryptoCurrencyStatus = feeCryptoCurrencyStatus, + appCurrency = model.appCurrency, + sendAmount = sendAmount, + destinationAddress = destinationAddress, + blockClickEnableFlow = MutableStateFlow(true), + onLoadFee = model::loadFee, + ), + onResult = { }, + onClick = {}, + ) + + return SendConfirmSuccessComponent( + appComponentContext = factoryContext, + params = SendConfirmSuccessComponent.Params( + sendUMFlow = model.uiState, + feeBlockComponent = feeBlockComponent, + destinationBlockComponent = destinationBlockComponent, + analyticsCategoryName = analyticCategoryName, + currentRoute = currentRoute, + txUrl = txUrl, + callback = model, + ), + ) + } + private fun getStubComponent() = StubComponent() class StubComponent : ComposableContentComponent { diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt index 997ad90f2a..df576bb18f 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/SendConfirmComponent.kt @@ -155,6 +155,7 @@ internal class SendConfirmComponent( val isBalanceHidingFlow: StateFlow, val predefinedValues: PredefinedValues, val onLoadFee: suspend () -> Either, + val onSendTransaction: () -> Unit, ) interface ModelCallback { diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt index f8abef07da..e4bfe5b26f 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/model/SendConfirmModel.kt @@ -291,6 +291,7 @@ internal class SendConfirmModel @Inject constructor( isShowTapHelp = isShowTapHelp, walletName = stringReference(userWallet.name), ).transform(uiState.value.confirmUM), + confirmData = confirmData, ) } updateConfirmNotifications() @@ -384,6 +385,10 @@ internal class SendConfirmModel @Inject constructor( addTokenToWalletIfNeeded() sendBalanceUpdater.scheduleUpdates() sendAnalyticHelper.sendSuccessAnalytics(cryptoCurrency, uiState.value) + if (uiState.value.isRedesignEnabled) { + params.callback.onResult(uiState.value) + params.onSendTransaction() + } }, ) } @@ -462,23 +467,35 @@ internal class SendConfirmModel @Inject constructor( flow = uiState, flow2 = params.currentRoute, transform = { state, route -> state to route }, - ).filter { it.second is CommonSendRoute.Confirm }.onEach { (state, _) -> + ).filter { + it.second is CommonSendRoute.Confirm + }.onEach { (state, _) -> val amountUM = state.amountUM as? AmountState.Data val confirmUM = state.confirmUM - val isReadyToSend = confirmUM is ConfirmUM.Content && !confirmUM.isSending params.callback.onResult( state.copy( navigationUM = NavigationUM.Content( - title = resourceReference( - id = R.string.send_summary_title, - formatArgs = wrappedList(params.cryptoCurrencyStatus.currency.name), - ), + title = if (state.isRedesignEnabled) { + stringReference("") + } else { + resourceReference( + id = R.string.send_summary_title, + formatArgs = wrappedList(params.cryptoCurrencyStatus.currency.name), + ) + }, subtitle = if (uiState.value.isRedesignEnabled) { null } else { amountUM?.title }, - backIconRes = R.drawable.ic_close_24, + backIconRes = if (state.isRedesignEnabled) { + when (confirmUM) { + is ConfirmUM.Success -> R.drawable.ic_close_24 + else -> R.drawable.ic_back_24 + } + } else { + R.drawable.ic_close_24 + }, backIconClick = { analyticsEventHandler.send( CommonSendAnalyticEvents.CloseButtonClicked( @@ -490,31 +507,7 @@ internal class SendConfirmModel @Inject constructor( ) appRouter.pop() }, - primaryButton = NavigationButton( - textReference = when (confirmUM) { - is ConfirmUM.Success -> resourceReference(R.string.common_close) - is ConfirmUM.Content -> if (confirmUM.isSending) { - resourceReference(R.string.send_sending) - } else { - resourceReference(R.string.common_send) - } - else -> resourceReference(R.string.common_send) - }, - iconRes = R.drawable.ic_tangem_24.takeIf { isReadyToSend }, - isEnabled = confirmUM.isPrimaryButtonEnabled, - isHapticClick = isReadyToSend, - onClick = { - when (confirmUM) { - is ConfirmUM.Success -> appRouter.pop() - is ConfirmUM.Content -> if (confirmUM.isSending) { - return@NavigationButton - } else { - onSendClick() - } - else -> return@NavigationButton - } - }, - ), + primaryButton = primaryButtonUM(), prevButton = null, secondaryPairButtonsUM = ( NavigationButton( @@ -533,6 +526,36 @@ internal class SendConfirmModel @Inject constructor( }.launchIn(modelScope) } + private fun primaryButtonUM(): NavigationButton { + val confirmUM = uiState.value.confirmUM + val isReadyToSend = confirmUM is ConfirmUM.Content && !confirmUM.isSending + return NavigationButton( + textReference = when (confirmUM) { + is ConfirmUM.Success -> resourceReference(R.string.common_close) + is ConfirmUM.Content -> if (confirmUM.isSending) { + resourceReference(R.string.send_sending) + } else { + resourceReference(R.string.common_send) + } + else -> resourceReference(R.string.common_send) + }, + iconRes = R.drawable.ic_tangem_24.takeIf { isReadyToSend }, + isEnabled = confirmUM.isPrimaryButtonEnabled, + isHapticClick = isReadyToSend, + onClick = { + when (confirmUM) { + is ConfirmUM.Success -> appRouter.pop() + is ConfirmUM.Content -> if (confirmUM.isSending) { + return@NavigationButton + } else { + onSendClick() + } + else -> return@NavigationButton + } + }, + ) + } + private companion object { const val CHECK_FEE_UPDATE_DELAY = 10_000L } diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/ui/SendConfirmContent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/ui/SendConfirmContent.kt index 8dc774afd7..738c7e22ab 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/ui/SendConfirmContent.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/confirm/ui/SendConfirmContent.kt @@ -82,31 +82,36 @@ private fun LazyListScope.blocks( ) { item(key = BLOCKS_KEY) { Column(verticalArrangement = Arrangement.spacedBy(12.dp)) { - AnimatedVisibility( - visible = uiState.confirmUM is ConfirmUM.Success, - modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing12), - ) { - val wrappedConfirmUM = remember(this) { uiState.confirmUM as ConfirmUM.Success } - TransactionDoneTitle( - title = resourceReference(R.string.sent_transaction_sent_title), - subtitle = resourceReference( - R.string.send_date_format, - wrappedList( - wrappedConfirmUM.transactionDate.toTimeFormat(DateTimeFormatters.dateFormatter), - wrappedConfirmUM.transactionDate.toTimeFormat(), - ), - ), - modifier = Modifier.padding(vertical = 12.dp), - ) - } if (uiState.isRedesignEnabled) { amountBlockComponent.Content(modifier = Modifier) destinationBlockComponent.Content(modifier = Modifier) } else { + TransactionDoneTitleAnimated(uiState) destinationBlockComponent.Content(modifier = Modifier) amountBlockComponent.Content(modifier = Modifier) } feeBlockComponent.Content(modifier = Modifier) } } +} + +@Composable +internal fun TransactionDoneTitleAnimated(uiState: SendUM) { + AnimatedVisibility( + visible = uiState.confirmUM is ConfirmUM.Success, + modifier = Modifier.padding(vertical = 12.dp), + ) { + val wrappedConfirmUM = remember(this) { uiState.confirmUM as ConfirmUM.Success } + TransactionDoneTitle( + title = resourceReference(R.string.sent_transaction_sent_title), + subtitle = resourceReference( + R.string.send_date_format, + wrappedList( + wrappedConfirmUM.transactionDate.toTimeFormat(DateTimeFormatters.dateFormatter), + wrappedConfirmUM.transactionDate.toTimeFormat(), + ), + ), + modifier = Modifier.padding(vertical = 12.dp), + ) + } } \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/di/CommonSendModelModule.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/di/CommonSendModelModule.kt index d2201019ff..bff655e61d 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/di/CommonSendModelModule.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/di/CommonSendModelModule.kt @@ -4,6 +4,7 @@ import com.tangem.core.decompose.di.ModelComponent import com.tangem.core.decompose.model.Model import com.tangem.features.send.v2.send.confirm.model.SendConfirmModel import com.tangem.features.send.v2.send.model.SendModel +import com.tangem.features.send.v2.send.success.model.SendConfirmSuccessModel import dagger.Binds import dagger.Module import dagger.hilt.InstallIn @@ -23,4 +24,9 @@ internal interface CommonSendModelModule { @IntoMap @ClassKey(SendConfirmModel::class) fun provideSendConfirmModel(model: SendConfirmModel): Model + + @Binds + @IntoMap + @ClassKey(SendConfirmSuccessModel::class) + fun provideSendConfirmSuccessModel(model: SendConfirmSuccessModel): Model } \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt index c573aad0bd..3ffc5b25e0 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/model/SendModel.kt @@ -46,6 +46,7 @@ import com.tangem.features.send.v2.common.CommonSendRoute import com.tangem.features.send.v2.common.SendConfirmAlertFactory import com.tangem.features.send.v2.common.ui.state.ConfirmUM import com.tangem.features.send.v2.send.confirm.SendConfirmComponent +import com.tangem.features.send.v2.send.success.SendConfirmSuccessComponent import com.tangem.features.send.v2.send.ui.state.SendUM import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponent import com.tangem.features.send.v2.subcomponents.amount.SendAmountUpdateQRTrigger @@ -65,7 +66,8 @@ internal interface SendComponentCallback : SendAmountComponent.ModelCallback, SendFeeComponent.ModelCallback, SendDestinationComponent.ModelCallback, - SendConfirmComponent.ModelCallback + SendConfirmComponent.ModelCallback, + SendConfirmSuccessComponent.ModelCallback @Stable @ModelScoped @@ -382,5 +384,6 @@ internal class SendModel @Inject constructor( confirmUM = ConfirmUM.Empty, navigationUM = NavigationUM.Empty, isRedesignEnabled = sendFeatureToggles.isSendRedesignEnabled, + confirmData = null, ) } \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/success/SendConfirmSuccessComponent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/success/SendConfirmSuccessComponent.kt new file mode 100644 index 0000000000..c9fa321928 --- /dev/null +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/success/SendConfirmSuccessComponent.kt @@ -0,0 +1,51 @@ +package com.tangem.features.send.v2.send.success + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Modifier +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.core.ui.decompose.ComposableContentComponent +import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationBlockComponent +import com.tangem.features.send.v2.common.CommonSendRoute +import com.tangem.features.send.v2.send.success.model.SendConfirmSuccessModel +import com.tangem.features.send.v2.send.success.ui.SendConfirmSuccessContent +import com.tangem.features.send.v2.send.ui.state.SendUM +import com.tangem.features.send.v2.subcomponents.fee.SendFeeBlockComponent +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.StateFlow + +internal class SendConfirmSuccessComponent( + appComponentContext: AppComponentContext, + params: Params, +) : ComposableContentComponent, AppComponentContext by appComponentContext { + + private val model: SendConfirmSuccessModel = getOrCreateModel(params = params) + private val destinationBlockComponent: SendDestinationBlockComponent = params.destinationBlockComponent + private val feeBlockComponent: SendFeeBlockComponent = params.feeBlockComponent + + @Composable + override fun Content(modifier: Modifier) { + val state by model.uiState.collectAsState() + SendConfirmSuccessContent( + sendUM = state, + destinationBlockComponent = destinationBlockComponent, + feeBlockComponent = feeBlockComponent, + ) + } + + data class Params( + val sendUMFlow: StateFlow, + val destinationBlockComponent: SendDestinationBlockComponent, + val feeBlockComponent: SendFeeBlockComponent, + val analyticsCategoryName: String, + val currentRoute: Flow, + val txUrl: String, + val callback: ModelCallback, + ) + + interface ModelCallback { + fun onResult(sendUM: SendUM) + } +} \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/success/model/SendConfirmSuccessModel.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/success/model/SendConfirmSuccessModel.kt new file mode 100644 index 0000000000..663d111b4f --- /dev/null +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/success/model/SendConfirmSuccessModel.kt @@ -0,0 +1,104 @@ +package com.tangem.features.send.v2.send.success.model + +import androidx.compose.runtime.Stable +import com.tangem.common.routing.AppRouter +import com.tangem.common.ui.navigationButtons.NavigationButton +import com.tangem.common.ui.navigationButtons.NavigationUM +import com.tangem.core.analytics.api.AnalyticsEventHandler +import com.tangem.core.decompose.di.ModelScoped +import com.tangem.core.decompose.model.Model +import com.tangem.core.decompose.model.ParamsContainer +import com.tangem.core.navigation.share.ShareManager +import com.tangem.core.navigation.url.UrlOpener +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.stringReference +import com.tangem.features.send.v2.common.CommonSendRoute +import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents +import com.tangem.features.send.v2.common.analytics.CommonSendAnalyticEvents.SendScreenSource +import com.tangem.features.send.v2.impl.R +import com.tangem.features.send.v2.send.success.SendConfirmSuccessComponent +import com.tangem.features.send.v2.send.ui.state.SendUM +import com.tangem.utils.coroutines.CoroutineDispatcherProvider +import kotlinx.coroutines.flow.* +import javax.inject.Inject + +@Stable +@ModelScoped +internal class SendConfirmSuccessModel @Inject constructor( + paramsContainer: ParamsContainer, + override val dispatchers: CoroutineDispatcherProvider, + private val analyticsEventHandler: AnalyticsEventHandler, + private val appRouter: AppRouter, + private val urlOpener: UrlOpener, + private val shareManager: ShareManager, +) : Model() { + private val params: SendConfirmSuccessComponent.Params = paramsContainer.require() + private val _uiState = params.sendUMFlow + val uiState = _uiState + + init { + configConfirmSuccessNavigation() + } + + private fun configConfirmSuccessNavigation() { + combine( + flow = uiState, + flow2 = params.currentRoute, + transform = { state, route -> state to route }, + ).filter { it.second is CommonSendRoute.ConfirmSuccess }.onEach { (state, _) -> + params.callback.onResult( + state.copy( + navigationUM = NavigationUM.Content( + title = stringReference(""), + subtitle = null, + backIconRes = R.drawable.ic_close_24, + backIconClick = { + analyticsEventHandler.send( + CommonSendAnalyticEvents.CloseButtonClicked( + categoryName = params.analyticsCategoryName, + source = SendScreenSource.Confirm, + isFromSummary = true, + isValid = true, + ), + ) + appRouter.pop() + }, + primaryButton = NavigationButton( + textReference = resourceReference(R.string.common_close), + iconRes = null, + isEnabled = true, + isHapticClick = false, + onClick = { + appRouter.pop() + }, + ), + prevButton = null, + secondaryPairButtonsUM = NavigationButton( + textReference = resourceReference(R.string.common_explore), + iconRes = R.drawable.ic_web_24, + onClick = ::onExploreClick, + ) to NavigationButton( + textReference = resourceReference(R.string.common_share), + iconRes = R.drawable.ic_share_24, + onClick = ::onShareClick, + ), + ), + ), + ) + }.launchIn(modelScope) + } + + private fun onExploreClick() { + analyticsEventHandler.send(CommonSendAnalyticEvents.ExploreButtonClicked(params.analyticsCategoryName)) + urlOpener.openUrl(params.txUrl) + } + + private fun onShareClick() { + analyticsEventHandler.send(CommonSendAnalyticEvents.ShareButtonClicked(params.analyticsCategoryName)) + shareManager.shareText(params.txUrl) + } + + interface ModelCallback { + fun onResult(sendUM: SendUM) + } +} \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/success/ui/SendConfirmSuccessContent.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/success/ui/SendConfirmSuccessContent.kt new file mode 100644 index 0000000000..862d4e1483 --- /dev/null +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/success/ui/SendConfirmSuccessContent.kt @@ -0,0 +1,92 @@ +package com.tangem.features.send.v2.send.success.ui + +import androidx.compose.animation.* +import androidx.compose.foundation.gestures.Orientation +import androidx.compose.foundation.gestures.scrollable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.rememberScrollState +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp +import com.tangem.common.ui.amountScreen.ui.AmountBlock +import com.tangem.core.ui.components.SpacerHMax +import com.tangem.core.ui.components.transactions.TransactionDoneTitle +import com.tangem.core.ui.extensions.resourceReference +import com.tangem.core.ui.extensions.wrappedList +import com.tangem.core.ui.res.TangemTheme +import com.tangem.core.ui.utils.DateTimeFormatters +import com.tangem.core.ui.utils.toPx +import com.tangem.core.ui.utils.toTimeFormat +import com.tangem.features.send.v2.api.subcomponents.destination.SendDestinationBlockComponent +import com.tangem.features.send.v2.common.ui.SendNavigationButtons +import com.tangem.features.send.v2.common.ui.state.ConfirmUM +import com.tangem.features.send.v2.impl.R +import com.tangem.features.send.v2.send.ui.state.SendUM +import com.tangem.features.send.v2.subcomponents.fee.SendFeeBlockComponent +import kotlinx.coroutines.delay + +@Composable +internal fun SendConfirmSuccessContent( + sendUM: SendUM, + destinationBlockComponent: SendDestinationBlockComponent, + feeBlockComponent: SendFeeBlockComponent, +) { + var visible by remember { mutableStateOf(false) } + + LaunchedEffect(Unit) { + delay(ANIMATION_DELAY) + visible = true + } + + val height = ANIMATION_OFFSET.toPx().toInt() + + AnimatedVisibility( + visible = visible, + enter = slideInVertically( + initialOffsetY = { height }, + ).plus(fadeIn()), + exit = slideOutVertically().plus(fadeOut()), + label = "Animate success content", + ) { + Column { + Column( + modifier = Modifier + .padding(horizontal = TangemTheme.dimens.spacing16) + .scrollable( + state = rememberScrollState(), + orientation = Orientation.Horizontal, + ), + verticalArrangement = Arrangement.spacedBy(12.dp), + ) { + if (sendUM.confirmUM is ConfirmUM.Success) { + TransactionDoneTitle( + title = resourceReference(R.string.sent_transaction_sent_title), + subtitle = resourceReference( + R.string.send_date_format, + wrappedList( + sendUM.confirmUM.transactionDate.toTimeFormat(DateTimeFormatters.dateFormatter), + sendUM.confirmUM.transactionDate.toTimeFormat(), + ), + ), + modifier = Modifier.padding(vertical = 12.dp), + ) + } + AmountBlock( + amountState = sendUM.amountUM, + isClickDisabled = true, + isEditingDisabled = true, + onClick = {}, + ) + destinationBlockComponent.Content(modifier = Modifier) + feeBlockComponent.Content(modifier = Modifier) + } + SpacerHMax() + SendNavigationButtons(navigationUM = sendUM.navigationUM) + } + } +} + +private const val ANIMATION_DELAY = 600L +private val ANIMATION_OFFSET = (-40).dp \ No newline at end of file diff --git a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/ui/state/SendUM.kt b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/ui/state/SendUM.kt index 963bec1cf2..df436f0170 100644 --- a/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/ui/state/SendUM.kt +++ b/features/send-v2/impl/src/main/java/com/tangem/features/send/v2/send/ui/state/SendUM.kt @@ -4,6 +4,7 @@ import com.tangem.common.ui.amountScreen.models.AmountState import com.tangem.common.ui.navigationButtons.NavigationUM import com.tangem.features.send.v2.api.subcomponents.destination.entity.DestinationUM import com.tangem.features.send.v2.common.ui.state.ConfirmUM +import com.tangem.features.send.v2.send.confirm.model.ConfirmData import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM internal data class SendUM( @@ -13,4 +14,5 @@ internal data class SendUM( val confirmUM: ConfirmUM, val navigationUM: NavigationUM, val isRedesignEnabled: Boolean, + val confirmData: ConfirmData?, ) \ No newline at end of file