Updated on 2026-08-14
This commit is contained in:
parent
a1d92a5448
commit
0142f675ad
12 changed files with 428 additions and 55 deletions
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ internal class SendConfirmComponent(
|
|||
val isBalanceHidingFlow: StateFlow<Boolean>,
|
||||
val predefinedValues: PredefinedValues,
|
||||
val onLoadFee: suspend () -> Either<GetFeeError, TransactionFee>,
|
||||
val onSendTransaction: () -> Unit,
|
||||
)
|
||||
|
||||
interface ModelCallback {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
|
|
@ -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<SendUM>,
|
||||
val destinationBlockComponent: SendDestinationBlockComponent,
|
||||
val feeBlockComponent: SendFeeBlockComponent,
|
||||
val analyticsCategoryName: String,
|
||||
val currentRoute: Flow<CommonSendRoute>,
|
||||
val txUrl: String,
|
||||
val callback: ModelCallback,
|
||||
)
|
||||
|
||||
interface ModelCallback {
|
||||
fun onResult(sendUM: SendUM)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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?,
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue