Updated on 2026-08-14
This commit is contained in:
commit
3c2a006fb9
331 changed files with 8261 additions and 1670 deletions
|
|
@ -1,35 +1,34 @@
|
|||
package com.tangem.features.send.v2.send
|
||||
package com.tangem.features.send.v2.common
|
||||
|
||||
import com.tangem.core.decompose.navigation.Route
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
internal sealed class SendRoute : Route {
|
||||
internal sealed class CommonSendRoute : Route {
|
||||
|
||||
abstract val isEditMode: Boolean
|
||||
|
||||
@Serializable
|
||||
data object Empty : SendRoute() {
|
||||
data object Empty : CommonSendRoute() {
|
||||
override val isEditMode = false
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data object Confirm : SendRoute() {
|
||||
data object Confirm : CommonSendRoute() {
|
||||
override val isEditMode: Boolean = false
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class Destination(
|
||||
override val isEditMode: Boolean,
|
||||
) : SendRoute()
|
||||
) : CommonSendRoute()
|
||||
|
||||
@Serializable
|
||||
data class Amount(
|
||||
override val isEditMode: Boolean,
|
||||
) : SendRoute()
|
||||
) : CommonSendRoute()
|
||||
|
||||
@Serializable
|
||||
data class Fee(
|
||||
override val isEditMode: Boolean = true,
|
||||
) : SendRoute()
|
||||
data object Fee : CommonSendRoute() {
|
||||
override val isEditMode: Boolean = true
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.features.send.v2.common
|
||||
|
||||
sealed class PredefinedValues {
|
||||
data object Empty : PredefinedValues()
|
||||
|
||||
sealed class Content : PredefinedValues() {
|
||||
abstract val amount: String
|
||||
abstract val address: String
|
||||
abstract val memo: String?
|
||||
|
||||
data class Deeplink(
|
||||
override val amount: String,
|
||||
override val address: String,
|
||||
override val memo: String?,
|
||||
val transactionId: String,
|
||||
) : Content()
|
||||
|
||||
data class QrCode(
|
||||
override val amount: String,
|
||||
override val address: String,
|
||||
override val memo: String?,
|
||||
) : Content()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
package com.tangem.features.send.v2.common
|
||||
|
||||
import com.tangem.domain.tokens.FetchPendingTransactionsUseCase
|
||||
import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
|
||||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.features.txhistory.TxHistoryFeatureToggles
|
||||
import com.tangem.features.txhistory.entity.TxHistoryContentUpdateEmitter
|
||||
import com.tangem.utils.coroutines.DelayedWork
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class SendBalanceUpdater @AssistedInject constructor(
|
||||
private val fetchPendingTransactionsUseCase: FetchPendingTransactionsUseCase,
|
||||
private val updateDelayedNetworkStatusUseCase: UpdateDelayedNetworkStatusUseCase,
|
||||
private val getTxHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
|
||||
private val getTxHistoryItemsUseCase: GetTxHistoryItemsUseCase,
|
||||
private val txHistoryFeatureToggles: TxHistoryFeatureToggles,
|
||||
private val txHistoryContentUpdateEmitter: TxHistoryContentUpdateEmitter,
|
||||
@DelayedWork private val coroutineScope: CoroutineScope,
|
||||
@Assisted private val userWallet: UserWallet,
|
||||
@Assisted private val cryptoCurrency: CryptoCurrency,
|
||||
) {
|
||||
fun scheduleUpdates() {
|
||||
coroutineScope.launch {
|
||||
listOf(
|
||||
// we should update network to find pending tx after 1 sec
|
||||
async {
|
||||
fetchPendingTransactionsUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
networks = setOf(cryptoCurrency.network),
|
||||
)
|
||||
},
|
||||
// we should update tx history and network for new balances
|
||||
async {
|
||||
updateTxHistory()
|
||||
},
|
||||
async {
|
||||
updateNetworkStatuses()
|
||||
},
|
||||
).awaitAll()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun updateNetworkStatuses(delay: Long = BALANCE_UPDATE_DELAY) {
|
||||
updateDelayedNetworkStatusUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
network = cryptoCurrency.network,
|
||||
delayMillis = delay,
|
||||
refresh = true,
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun updateTxHistory() {
|
||||
delay(BALANCE_UPDATE_DELAY)
|
||||
val txHistoryItemsCountEither = getTxHistoryItemsCountUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
currency = cryptoCurrency,
|
||||
)
|
||||
|
||||
txHistoryItemsCountEither.onRight {
|
||||
if (txHistoryFeatureToggles.isFeatureEnabled) {
|
||||
txHistoryContentUpdateEmitter.triggerUpdate()
|
||||
} else {
|
||||
getTxHistoryItemsUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
currency = cryptoCurrency,
|
||||
refresh = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val BALANCE_UPDATE_DELAY = 11_000L
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(cryptoCurrency: CryptoCurrency, userWallet: UserWallet): SendBalanceUpdater
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,7 @@
|
|||
package com.tangem.features.send.v2.common
|
||||
|
||||
import com.tangem.features.send.v2.common.ui.state.NavigationUM
|
||||
|
||||
internal interface SendNavigationModelCallback {
|
||||
fun onNavigationResult(navigationUM: NavigationUM)
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.send.v2.send.ui
|
||||
package com.tangem.features.send.v2.common.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
|
|
@ -13,12 +13,14 @@ import com.tangem.core.ui.components.appbar.AppBarWithBackButtonAndIcon
|
|||
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.send.SendRoute
|
||||
import com.tangem.features.send.v2.common.NavigationUM
|
||||
import com.tangem.features.send.v2.send.ui.state.SendUM
|
||||
import com.tangem.features.send.v2.common.CommonSendRoute
|
||||
import com.tangem.features.send.v2.common.ui.state.NavigationUM
|
||||
|
||||
@Composable
|
||||
internal fun SendContent(state: SendUM, stackState: ChildStack<SendRoute, ComposableContentComponent>) {
|
||||
internal fun SendContent(
|
||||
navigationUM: NavigationUM,
|
||||
stackState: ChildStack<CommonSendRoute, ComposableContentComponent>,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.Companion
|
||||
.background(color = TangemTheme.colors.background.tertiary)
|
||||
|
|
@ -27,7 +29,7 @@ internal fun SendContent(state: SendUM, stackState: ChildStack<SendRoute, Compos
|
|||
.systemBarsPadding(),
|
||||
horizontalAlignment = Alignment.Companion.CenterHorizontally,
|
||||
) {
|
||||
SendAppBar(navigationUM = state.navigationUM)
|
||||
SendAppBar(navigationUM = navigationUM)
|
||||
Children(
|
||||
stack = stackState,
|
||||
animation = stackAnimation(slide()),
|
||||
|
|
@ -35,7 +37,7 @@ internal fun SendContent(state: SendUM, stackState: ChildStack<SendRoute, Compos
|
|||
) {
|
||||
it.instance.Content(Modifier.weight(1f))
|
||||
}
|
||||
SendNavigationButtons(navigationUM = state.navigationUM)
|
||||
SendNavigationButtons(navigationUM = navigationUM)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.send.v2.send.ui
|
||||
package com.tangem.features.send.v2.common.ui
|
||||
|
||||
import androidx.compose.animation.*
|
||||
import androidx.compose.foundation.background
|
||||
|
|
@ -29,7 +29,7 @@ import com.tangem.core.ui.components.buttons.common.TangemButtonsDefaults
|
|||
import com.tangem.features.send.v2.send.ui.state.ButtonsUM
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.send.v2.common.NavigationUM
|
||||
import com.tangem.features.send.v2.common.ui.state.NavigationUM
|
||||
|
||||
@Composable
|
||||
internal fun SendNavigationButtons(navigationUM: NavigationUM, modifier: Modifier = Modifier) {
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
package com.tangem.features.send.v2.common.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import com.tangem.core.ui.extensions.stringResourceSafe
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
private const val TAP_HELP_KEY = "TAP_HELP_KEY"
|
||||
private const val TAP_HELP_ANIMATION_DELAY = 500L
|
||||
|
||||
internal fun LazyListScope.tapHelp(isDisplay: Boolean, modifier: Modifier = Modifier) {
|
||||
item(key = TAP_HELP_KEY) {
|
||||
var wrappedIsDisplay by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(key1 = isDisplay) {
|
||||
delay(TAP_HELP_ANIMATION_DELAY)
|
||||
wrappedIsDisplay = isDisplay
|
||||
}
|
||||
|
||||
if (wrappedIsDisplay) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.animateItem()
|
||||
.padding(top = TangemTheme.dimens.spacing20),
|
||||
) {
|
||||
val background = TangemTheme.colors.button.secondary
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_send_hint_shape_12),
|
||||
tint = TangemTheme.colors.button.secondary,
|
||||
contentDescription = null,
|
||||
modifier = Modifier,
|
||||
)
|
||||
Text(
|
||||
text = stringResourceSafe(id = R.string.send_summary_tap_hint),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
modifier = Modifier
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(background)
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing14,
|
||||
vertical = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.send.v2.send.confirm.ui.state
|
||||
package com.tangem.features.send.v2.common.ui.state
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package com.tangem.features.send.v2.common
|
||||
package com.tangem.features.send.v2.common.ui.state
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.runtime.Immutable
|
||||
|
|
@ -2,8 +2,6 @@ package com.tangem.features.send.v2.di
|
|||
|
||||
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.subcomponents.amount.model.SendAmountModel
|
||||
import com.tangem.features.send.v2.subcomponents.destination.model.SendDestinationModel
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.SendFeeModel
|
||||
|
|
@ -16,12 +14,7 @@ import dagger.multibindings.IntoMap
|
|||
|
||||
@Module
|
||||
@InstallIn(ModelComponent::class)
|
||||
internal interface SendModelModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(SendModel::class)
|
||||
fun provideSendModel(model: SendModel): Model
|
||||
internal interface CommonSendModelModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
|
|
@ -38,11 +31,6 @@ internal interface SendModelModule {
|
|||
@ClassKey(SendFeeModel::class)
|
||||
fun provideSendFeeModel(model: SendFeeModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(SendConfirmModel::class)
|
||||
fun provideSendConfirmModel(model: SendConfirmModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(NotificationsModel::class)
|
||||
|
|
@ -2,9 +2,11 @@ package com.tangem.features.send.v2.di
|
|||
|
||||
import com.tangem.core.configtoggle.feature.FeatureTogglesManager
|
||||
import com.tangem.features.send.v2.DefaultSendFeatureToggles
|
||||
import com.tangem.features.send.v2.api.NFTSendComponent
|
||||
import com.tangem.features.send.v2.api.SendComponent
|
||||
import com.tangem.features.send.v2.api.SendFeatureToggles
|
||||
import com.tangem.features.send.v2.send.DefaultSendComponent
|
||||
import com.tangem.features.send.v2.sendnft.DefaultNFTSendComponent
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
|
|
@ -29,4 +31,8 @@ internal interface SendFeatureModuleBinds {
|
|||
@Binds
|
||||
@Singleton
|
||||
fun provideSendComponentFactory(impl: DefaultSendComponent.Factory): SendComponent.Factory
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun provideNFTSendComponentFactory(impl: DefaultNFTSendComponent.Factory): NFTSendComponent.Factory
|
||||
}
|
||||
|
|
@ -19,11 +19,13 @@ import com.tangem.core.decompose.model.getOrCreateModel
|
|||
import com.tangem.core.decompose.navigation.inner.InnerRouter
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.features.send.v2.api.SendComponent
|
||||
import com.tangem.features.send.v2.common.CommonSendRoute
|
||||
import com.tangem.features.send.v2.common.PredefinedValues
|
||||
import com.tangem.features.send.v2.common.ui.SendContent
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.send.analytics.SendAnalyticEvents
|
||||
import com.tangem.features.send.v2.send.confirm.SendConfirmComponent
|
||||
import com.tangem.features.send.v2.send.confirm.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.send.model.SendModel
|
||||
import com.tangem.features.send.v2.send.ui.SendContent
|
||||
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.destination.SendDestinationComponent
|
||||
|
|
@ -44,17 +46,17 @@ internal class DefaultSendComponent @AssistedInject constructor(
|
|||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : SendComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val stackNavigation = StackNavigation<SendRoute>()
|
||||
private val stackNavigation = StackNavigation<CommonSendRoute>()
|
||||
|
||||
private val innerRouter = InnerRouter<SendRoute>(
|
||||
private val innerRouter = InnerRouter<CommonSendRoute>(
|
||||
stackNavigation = stackNavigation,
|
||||
popCallback = { onChildBack() },
|
||||
)
|
||||
|
||||
private val initialRoute = if (params.amount == null) {
|
||||
SendRoute.Destination(isEditMode = false)
|
||||
CommonSendRoute.Destination(isEditMode = false)
|
||||
} else {
|
||||
SendRoute.Empty
|
||||
CommonSendRoute.Empty
|
||||
}
|
||||
private val currentRoute = MutableStateFlow(initialRoute)
|
||||
|
||||
|
|
@ -112,47 +114,61 @@ internal class DefaultSendComponent @AssistedInject constructor(
|
|||
|
||||
BackHandler(onBack = ::onChildBack)
|
||||
SendContent(
|
||||
state = state,
|
||||
navigationUM = state.navigationUM,
|
||||
stackState = stackState,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createChild(route: SendRoute, factoryContext: AppComponentContext) = when (route) {
|
||||
SendRoute.Empty -> getStubComponent()
|
||||
is SendRoute.Destination -> getDestinationComponent(factoryContext, route)
|
||||
is SendRoute.Amount -> getAmountComponent(factoryContext, route)
|
||||
is SendRoute.Fee -> getFeeComponent(factoryContext)
|
||||
SendRoute.Confirm -> getConfirmComponent(factoryContext)
|
||||
private fun createChild(route: CommonSendRoute, factoryContext: AppComponentContext) = when (route) {
|
||||
CommonSendRoute.Empty -> getStubComponent()
|
||||
is CommonSendRoute.Destination -> getDestinationComponent(factoryContext, route)
|
||||
is CommonSendRoute.Amount -> getAmountComponent(factoryContext, route)
|
||||
is CommonSendRoute.Fee -> getFeeComponent(factoryContext)
|
||||
CommonSendRoute.Confirm -> getConfirmComponent(factoryContext)
|
||||
}
|
||||
|
||||
private fun getDestinationComponent(factoryContext: AppComponentContext, route: SendRoute) =
|
||||
private fun getDestinationComponent(factoryContext: AppComponentContext, route: CommonSendRoute) =
|
||||
SendDestinationComponent(
|
||||
appComponentContext = factoryContext,
|
||||
params = SendDestinationComponentParams.DestinationParams(
|
||||
state = model.uiState.value.destinationUM,
|
||||
currentRoute = currentRoute.filterIsInstance<SendRoute.Destination>(),
|
||||
currentRoute = currentRoute.filterIsInstance<CommonSendRoute.Destination>(),
|
||||
isBalanceHidingFlow = model.isBalanceHiddenFlow,
|
||||
analyticsCategoryName = SendAnalyticEvents.SEND_CATEGORY,
|
||||
userWalletId = params.userWalletId,
|
||||
cryptoCurrency = params.currency,
|
||||
callback = model,
|
||||
isEditMode = route.isEditMode,
|
||||
onBackClick = ::onChildBack,
|
||||
onNextClick = {
|
||||
if (route.isEditMode) {
|
||||
onChildBack()
|
||||
} else {
|
||||
innerRouter.push(CommonSendRoute.Amount(isEditMode = false))
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
private fun getAmountComponent(factoryContext: AppComponentContext, route: SendRoute) = SendAmountComponent(
|
||||
private fun getAmountComponent(factoryContext: AppComponentContext, route: CommonSendRoute) = SendAmountComponent(
|
||||
appComponentContext = factoryContext,
|
||||
params = SendAmountComponentParams.AmountParams(
|
||||
state = model.uiState.value.amountUM,
|
||||
currentRoute = currentRoute.filterIsInstance<SendRoute.Amount>(),
|
||||
currentRoute = currentRoute.filterIsInstance<CommonSendRoute.Amount>(),
|
||||
isBalanceHidingFlow = model.isBalanceHiddenFlow,
|
||||
analyticsCategoryName = SendAnalyticEvents.SEND_CATEGORY,
|
||||
userWallet = model.userWallet,
|
||||
appCurrency = model.appCurrency,
|
||||
cryptoCurrencyStatus = model.cryptoCurrencyStatus,
|
||||
callback = model,
|
||||
isEditMode = route.isEditMode,
|
||||
predefinedAmountValue = model.predefinedAmountValue,
|
||||
predefinedValues = model.predefinedValues,
|
||||
onBackClick = ::onChildBack,
|
||||
onNextClick = {
|
||||
if (route.isEditMode) {
|
||||
onChildBack()
|
||||
} else {
|
||||
innerRouter.push(CommonSendRoute.Confirm)
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -165,7 +181,7 @@ internal class DefaultSendComponent @AssistedInject constructor(
|
|||
appComponentContext = factoryContext,
|
||||
params = SendFeeComponentParams.FeeParams(
|
||||
state = model.uiState.value.feeUM,
|
||||
currentRoute = currentRoute.filterIsInstance<SendRoute.Fee>(),
|
||||
currentRoute = currentRoute.filterIsInstance<CommonSendRoute.Fee>(),
|
||||
analyticsCategoryName = SendAnalyticEvents.SEND_CATEGORY,
|
||||
userWallet = model.userWallet,
|
||||
cryptoCurrencyStatus = model.cryptoCurrencyStatus,
|
||||
|
|
@ -174,6 +190,7 @@ internal class DefaultSendComponent @AssistedInject constructor(
|
|||
sendAmount = sendAmount,
|
||||
destinationAddress = destinationAddress,
|
||||
callback = model,
|
||||
onNextClick = ::onChildBack,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
|
|
@ -187,21 +204,21 @@ internal class DefaultSendComponent @AssistedInject constructor(
|
|||
val predefinedAddress = params.destinationAddress
|
||||
val predefinedValues =
|
||||
if (predefinedAmount != null && predefinedTxId != null && predefinedAddress != null) {
|
||||
SendConfirmComponent.Params.PredefinedValues.Content(
|
||||
PredefinedValues.Content.Deeplink(
|
||||
amount = predefinedAmount,
|
||||
address = predefinedAddress,
|
||||
tag = params.tag,
|
||||
memo = params.tag,
|
||||
transactionId = predefinedTxId,
|
||||
)
|
||||
} else {
|
||||
SendConfirmComponent.Params.PredefinedValues.Empty
|
||||
PredefinedValues.Empty
|
||||
}
|
||||
return SendConfirmComponent(
|
||||
appComponentContext = factoryContext,
|
||||
params = SendConfirmComponent.Params(
|
||||
state = model.uiState.value,
|
||||
userWallet = model.userWallet,
|
||||
currentRoute = currentRoute.filterIsInstance<SendRoute.Confirm>(),
|
||||
currentRoute = currentRoute.filterIsInstance<CommonSendRoute.Confirm>(),
|
||||
isBalanceHidingFlow = model.isBalanceHiddenFlow,
|
||||
analyticsCategoryName = SendAnalyticEvents.SEND_CATEGORY,
|
||||
cryptoCurrencyStatus = model.cryptoCurrencyStatus,
|
||||
|
|
@ -216,7 +233,7 @@ internal class DefaultSendComponent @AssistedInject constructor(
|
|||
private fun getStubComponent() = ComposableContentComponent { }
|
||||
|
||||
private fun onChildBack() {
|
||||
val isEmptyRoute = childStack.value.active.configuration == SendRoute.Empty
|
||||
val isEmptyRoute = childStack.value.active.configuration == CommonSendRoute.Empty
|
||||
val isEmptyStack = childStack.value.backStack.isEmpty()
|
||||
val isSuccess = model.uiState.value.confirmUM is ConfirmUM.Success
|
||||
|
||||
|
|
|
|||
|
|
@ -11,18 +11,20 @@ import com.tangem.core.ui.decompose.ComposableContentComponent
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.features.send.v2.send.SendRoute
|
||||
import com.tangem.features.send.v2.common.CommonSendRoute
|
||||
import com.tangem.features.send.v2.common.PredefinedValues
|
||||
import com.tangem.features.send.v2.send.confirm.model.SendConfirmModel
|
||||
import com.tangem.features.send.v2.send.confirm.ui.SendConfirmContent
|
||||
import com.tangem.features.send.v2.send.confirm.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.send.ui.state.SendUM
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountBlockComponent
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponentParams
|
||||
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationBlockComponent
|
||||
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationComponentParams
|
||||
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationComponentParams.DestinationBlockParams
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeBlockComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponentParams
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.NotificationsComponent
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.model.NotificationData
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
|
|
@ -38,15 +40,13 @@ internal class SendConfirmComponent(
|
|||
private val destinationBlockComponent =
|
||||
SendDestinationBlockComponent(
|
||||
appComponentContext = child("sendConfirmDestinationBlock"),
|
||||
params = SendDestinationComponentParams.DestinationBlockParams(
|
||||
params = DestinationBlockParams(
|
||||
state = model.uiState.value.destinationUM,
|
||||
analyticsCategoryName = params.analyticsCategoryName,
|
||||
userWalletId = params.userWallet.walletId,
|
||||
cryptoCurrency = params.cryptoCurrencyStatus.currency,
|
||||
blockClickEnableFlow = blockClickEnableFlow.asStateFlow(),
|
||||
isPredefinedValues = params.predefinedValues is Params.PredefinedValues.Content,
|
||||
predefinedAddressValue = (params.predefinedValues as? Params.PredefinedValues.Content)?.address,
|
||||
predefinedMemoValue = (params.predefinedValues as? Params.PredefinedValues.Content)?.tag,
|
||||
predefinedValues = params.predefinedValues,
|
||||
),
|
||||
onResult = model::onDestinationResult,
|
||||
onClick = model::showEditDestination,
|
||||
|
|
@ -61,8 +61,7 @@ internal class SendConfirmComponent(
|
|||
cryptoCurrencyStatus = params.cryptoCurrencyStatus,
|
||||
appCurrency = params.appCurrency,
|
||||
blockClickEnableFlow = blockClickEnableFlow.asStateFlow(),
|
||||
isPredefinedValues = params.predefinedValues is Params.PredefinedValues.Content,
|
||||
predefinedAmountValue = (params.predefinedValues as? Params.PredefinedValues.Content)?.amount,
|
||||
predefinedValues = params.predefinedValues,
|
||||
),
|
||||
onResult = model::onAmountResult,
|
||||
onClick = model::showEditAmount,
|
||||
|
|
@ -77,8 +76,8 @@ internal class SendConfirmComponent(
|
|||
cryptoCurrencyStatus = params.cryptoCurrencyStatus,
|
||||
feeCryptoCurrencyStatus = params.feeCryptoCurrencyStatus,
|
||||
appCurrency = params.appCurrency,
|
||||
sendAmount = model.enteredAmount.orZero(),
|
||||
destinationAddress = model.enteredDestination.orEmpty(),
|
||||
sendAmount = model.confirmData.enteredAmount.orZero(),
|
||||
destinationAddress = model.confirmData.enteredDestination.orEmpty(),
|
||||
blockClickEnableFlow = blockClickEnableFlow.asStateFlow(),
|
||||
),
|
||||
onResult = model::onFeeResult,
|
||||
|
|
@ -93,13 +92,15 @@ internal class SendConfirmComponent(
|
|||
cryptoCurrencyStatus = params.cryptoCurrencyStatus,
|
||||
feeCryptoCurrencyStatus = params.feeCryptoCurrencyStatus,
|
||||
appCurrency = params.appCurrency,
|
||||
destinationAddress = model.enteredDestination.orEmpty(),
|
||||
memo = model.enteredMemo,
|
||||
amountValue = model.enteredAmount.orZero(),
|
||||
reduceAmountBy = model.reduceAmountBy.orZero(),
|
||||
isIgnoreReduce = model.isIgnoreReduce,
|
||||
fee = model.fee,
|
||||
feeError = model.feeError,
|
||||
notificationData = NotificationData(
|
||||
destinationAddress = model.confirmData.enteredDestination.orEmpty(),
|
||||
memo = model.confirmData.enteredMemo,
|
||||
amountValue = model.confirmData.enteredAmount.orZero(),
|
||||
reduceAmountBy = model.confirmData.reduceAmountBy.orZero(),
|
||||
isIgnoreReduce = model.confirmData.isIgnoreReduce,
|
||||
fee = model.confirmData.fee,
|
||||
feeError = model.confirmData.feeError,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
|
|
@ -140,20 +141,10 @@ internal class SendConfirmComponent(
|
|||
val feeCryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
val appCurrency: AppCurrency,
|
||||
val callback: ModelCallback,
|
||||
val currentRoute: Flow<SendRoute.Confirm>,
|
||||
val currentRoute: Flow<CommonSendRoute.Confirm>,
|
||||
val isBalanceHidingFlow: StateFlow<Boolean>,
|
||||
val predefinedValues: PredefinedValues,
|
||||
) {
|
||||
sealed class PredefinedValues {
|
||||
data object Empty : PredefinedValues()
|
||||
data class Content(
|
||||
val transactionId: String,
|
||||
val amount: String,
|
||||
val address: String,
|
||||
val tag: String?,
|
||||
) : PredefinedValues()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
interface ModelCallback {
|
||||
fun onResult(sendUM: SendUM)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
package com.tangem.features.send.v2.send.confirm.model
|
||||
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.domain.transaction.error.GetFeeError
|
||||
import java.math.BigDecimal
|
||||
|
||||
data class ConfirmData(
|
||||
val enteredAmount: BigDecimal?,
|
||||
val reduceAmountBy: BigDecimal,
|
||||
val isIgnoreReduce: Boolean,
|
||||
val enteredDestination: String?,
|
||||
val enteredMemo: String?,
|
||||
val fee: Fee?,
|
||||
val feeError: GetFeeError?,
|
||||
)
|
||||
|
|
@ -5,7 +5,6 @@ import androidx.compose.runtime.Stable
|
|||
import arrow.core.getOrElse
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
|
|
@ -25,20 +24,17 @@ import com.tangem.domain.feedback.models.FeedbackEmailType
|
|||
import com.tangem.domain.settings.IsSendTapHelpEnabledUseCase
|
||||
import com.tangem.domain.settings.NeverShowTapHelpUseCase
|
||||
import com.tangem.domain.tokens.AddCryptoCurrenciesUseCase
|
||||
import com.tangem.domain.tokens.FetchPendingTransactionsUseCase
|
||||
import com.tangem.domain.tokens.IsAmountSubtractAvailableUseCase
|
||||
import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.transaction.error.GetFeeError
|
||||
import com.tangem.domain.transaction.usecase.CreateTransactionUseCase
|
||||
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
|
||||
import com.tangem.domain.txhistory.usecase.GetExplorerTransactionUrlUseCase
|
||||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsCountUseCase
|
||||
import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
|
||||
import com.tangem.domain.utils.convertToSdkAmount
|
||||
import com.tangem.features.send.v2.common.NavigationUM
|
||||
import com.tangem.features.send.v2.common.CommonSendRoute
|
||||
import com.tangem.features.send.v2.common.SendBalanceUpdater
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.common.ui.state.NavigationUM
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.send.SendRoute
|
||||
import com.tangem.features.send.v2.send.analytics.SendAnalyticEvents
|
||||
import com.tangem.features.send.v2.send.analytics.SendAnalyticEvents.SendScreenSource
|
||||
import com.tangem.features.send.v2.send.analytics.SendAnalyticHelper
|
||||
|
|
@ -47,7 +43,6 @@ import com.tangem.features.send.v2.send.confirm.model.transformers.SendConfirmIn
|
|||
import com.tangem.features.send.v2.send.confirm.model.transformers.SendConfirmSendingStateTransformer
|
||||
import com.tangem.features.send.v2.send.confirm.model.transformers.SendConfirmSentStateTransformer
|
||||
import com.tangem.features.send.v2.send.confirm.model.transformers.SendConfirmationNotificationsTransformer
|
||||
import com.tangem.features.send.v2.send.confirm.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.send.ui.state.ButtonsUM
|
||||
import com.tangem.features.send.v2.send.ui.state.SendUM
|
||||
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationUM
|
||||
|
|
@ -58,17 +53,13 @@ import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
|||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.NotificationsUpdateTrigger
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.model.NotificationData
|
||||
import com.tangem.features.txhistory.TxHistoryFeatureToggles
|
||||
import com.tangem.features.txhistory.entity.TxHistoryContentUpdateEmitter
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.DelayedWork
|
||||
import com.tangem.utils.extensions.orZero
|
||||
import com.tangem.utils.extensions.stripZeroPlainString
|
||||
import com.tangem.utils.transformer.update
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
import java.math.BigDecimal
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
|
|
@ -89,21 +80,15 @@ internal class SendConfirmModel @Inject constructor(
|
|||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase,
|
||||
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
|
||||
private val fetchPendingTransactionsUseCase: FetchPendingTransactionsUseCase,
|
||||
private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase,
|
||||
private val getTxHistoryItemsCountUseCase: GetTxHistoryItemsCountUseCase,
|
||||
private val isAmountSubtractAvailableUseCase: IsAmountSubtractAvailableUseCase,
|
||||
private val getTxHistoryItemsUseCase: GetTxHistoryItemsUseCase,
|
||||
private val sendFeeCheckReloadTrigger: SendFeeCheckReloadTrigger,
|
||||
private val sendFeeCheckReloadListener: SendFeeCheckReloadListener,
|
||||
private val txHistoryContentUpdateEmitter: TxHistoryContentUpdateEmitter,
|
||||
private val notificationsUpdateTrigger: NotificationsUpdateTrigger,
|
||||
private val alertFactory: SendConfirmAlertFactory,
|
||||
private val sendAnalyticHelper: SendAnalyticHelper,
|
||||
private val txHistoryFeatureToggles: TxHistoryFeatureToggles,
|
||||
@DelayedWork private val coroutineScope: CoroutineScope,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val shareManager: ShareManager,
|
||||
sendBalanceUpdaterFactory: SendBalanceUpdater.Factory,
|
||||
) : Model(), SendConfirmClickIntents {
|
||||
|
||||
private val params: SendConfirmComponent.Params = paramsContainer.require()
|
||||
|
|
@ -113,6 +98,8 @@ internal class SendConfirmModel @Inject constructor(
|
|||
private val cryptoCurrencyStatus = params.cryptoCurrencyStatus
|
||||
private val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
|
||||
private val sendBalanceUpdater = sendBalanceUpdaterFactory.create(cryptoCurrency, userWallet)
|
||||
|
||||
private val _uiState = MutableStateFlow(params.state)
|
||||
val uiState = _uiState.asStateFlow()
|
||||
|
||||
|
|
@ -125,20 +112,16 @@ internal class SendConfirmModel @Inject constructor(
|
|||
private val feeSelectorUM
|
||||
get() = feeUM?.feeSelectorUM as? FeeSelectorUM.Content
|
||||
|
||||
val enteredAmount: BigDecimal?
|
||||
get() = amountState?.amountTextField?.cryptoAmount?.value
|
||||
val reduceAmountBy: BigDecimal
|
||||
get() = amountState?.reduceAmountBy.orZero()
|
||||
val isIgnoreReduce: Boolean
|
||||
get() = amountState?.isIgnoreReduce == true
|
||||
val enteredDestination: String?
|
||||
get() = destinationUM?.addressTextField?.value
|
||||
val enteredMemo: String?
|
||||
get() = destinationUM?.memoTextField?.value
|
||||
val fee: Fee?
|
||||
get() = feeSelectorUM?.selectedFee
|
||||
val feeError: GetFeeError?
|
||||
get() = (feeUM?.feeSelectorUM as? FeeSelectorUM.Error)?.error
|
||||
val confirmData: ConfirmData
|
||||
get() = ConfirmData(
|
||||
enteredAmount = amountState?.amountTextField?.cryptoAmount?.value,
|
||||
enteredMemo = destinationUM?.memoTextField?.value,
|
||||
reduceAmountBy = amountState?.reduceAmountBy.orZero(),
|
||||
isIgnoreReduce = amountState?.isIgnoreReduce == true,
|
||||
enteredDestination = destinationUM?.addressTextField?.value,
|
||||
fee = feeSelectorUM?.selectedFee,
|
||||
feeError = (feeUM?.feeSelectorUM as? FeeSelectorUM.Error)?.error,
|
||||
)
|
||||
|
||||
private var sendIdleTimer: Long = 0L
|
||||
private var isAmountSubtractAvailable = false
|
||||
|
|
@ -183,7 +166,7 @@ internal class SendConfirmModel @Inject constructor(
|
|||
it.copy(confirmUM = confirmUM?.copy(showTapHelp = false) ?: it.confirmUM)
|
||||
}
|
||||
analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Address))
|
||||
router.push(SendRoute.Destination(isEditMode = true))
|
||||
router.push(CommonSendRoute.Destination(isEditMode = true))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -195,7 +178,7 @@ internal class SendConfirmModel @Inject constructor(
|
|||
it.copy(confirmUM = confirmUM?.copy(showTapHelp = false) ?: it.confirmUM)
|
||||
}
|
||||
analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Amount))
|
||||
router.push(SendRoute.Amount(isEditMode = true))
|
||||
router.push(CommonSendRoute.Amount(isEditMode = true))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -207,7 +190,7 @@ internal class SendConfirmModel @Inject constructor(
|
|||
it.copy(confirmUM = confirmUM?.copy(showTapHelp = false) ?: it.confirmUM)
|
||||
}
|
||||
analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Fee))
|
||||
router.push(SendRoute.Fee())
|
||||
router.push(CommonSendRoute.Fee)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -236,15 +219,15 @@ internal class SendConfirmModel @Inject constructor(
|
|||
|
||||
override fun onFailedTxEmailClick(errorMessage: String) {
|
||||
val amountValue = amountState?.amountTextField?.cryptoAmount?.value
|
||||
val feeValue = fee?.amount?.value
|
||||
val feeValue = confirmData.fee?.amount?.value
|
||||
|
||||
val receivingAmount = if (amountValue != null && feeValue != null) {
|
||||
checkAndCalculateSubtractedAmount(
|
||||
isAmountSubtractAvailable = isAmountSubtractAvailable,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
amountValue = enteredAmount.orZero(),
|
||||
amountValue = confirmData.enteredAmount.orZero(),
|
||||
feeValue = feeValue,
|
||||
reduceAmountBy = reduceAmountBy,
|
||||
reduceAmountBy = confirmData.reduceAmountBy,
|
||||
)
|
||||
} else {
|
||||
null
|
||||
|
|
@ -257,7 +240,7 @@ internal class SendConfirmModel @Inject constructor(
|
|||
errorMessage = errorMessage,
|
||||
blockchainId = cryptoCurrency.network.id.value,
|
||||
derivationPath = cryptoCurrency.network.derivationPath.value,
|
||||
destinationAddress = enteredDestination.orEmpty(),
|
||||
destinationAddress = confirmData.enteredDestination.orEmpty(),
|
||||
tokenSymbol = if (amount?.type is AmountType.Token) {
|
||||
amount.currencySymbol
|
||||
} else {
|
||||
|
|
@ -322,7 +305,7 @@ internal class SendConfirmModel @Inject constructor(
|
|||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
amountValue = amountValue,
|
||||
feeValue = feeValue,
|
||||
reduceAmountBy = reduceAmountBy.orZero(),
|
||||
reduceAmountBy = confirmData.reduceAmountBy.orZero(),
|
||||
)
|
||||
|
||||
modelScope.launch {
|
||||
|
|
@ -369,7 +352,7 @@ internal class SendConfirmModel @Inject constructor(
|
|||
ifRight = {
|
||||
updateTransactionStatus(txData)
|
||||
addTokenToWalletIfNeeded()
|
||||
scheduleUpdates()
|
||||
sendBalanceUpdater.scheduleUpdates()
|
||||
sendAnalyticHelper.sendSuccessAnalytics(cryptoCurrency, uiState.value)
|
||||
},
|
||||
)
|
||||
|
|
@ -380,7 +363,7 @@ internal class SendConfirmModel @Inject constructor(
|
|||
val wallets = destinationUM?.wallets ?: return
|
||||
|
||||
val receivingUserWallet = wallets
|
||||
.firstOrNull { it.address == enteredDestination }
|
||||
.firstOrNull { it.address == confirmData.enteredDestination }
|
||||
?: return
|
||||
|
||||
val userWalletId = receivingUserWallet.userWalletId ?: return
|
||||
|
|
@ -403,49 +386,6 @@ internal class SendConfirmModel @Inject constructor(
|
|||
_uiState.update(SendConfirmSentStateTransformer(txData, txUrl))
|
||||
}
|
||||
|
||||
private fun scheduleUpdates() {
|
||||
coroutineScope.launch {
|
||||
listOf(
|
||||
// we should update network to find pending tx after 1 sec
|
||||
async {
|
||||
fetchPendingTransactionsUseCase(userWallet.walletId, setOf(cryptoCurrency.network))
|
||||
},
|
||||
// we should update tx history and network for new balance
|
||||
async {
|
||||
updateTxHistory()
|
||||
},
|
||||
async {
|
||||
updateDelayedCurrencyStatusUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
network = cryptoCurrency.network,
|
||||
delayMillis = BALANCE_UPDATE_DELAY,
|
||||
refresh = true,
|
||||
)
|
||||
},
|
||||
).awaitAll()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun updateTxHistory() {
|
||||
delay(BALANCE_UPDATE_DELAY)
|
||||
val txHistoryItemsCountEither = getTxHistoryItemsCountUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
currency = cryptoCurrency,
|
||||
)
|
||||
|
||||
txHistoryItemsCountEither.onRight {
|
||||
if (txHistoryFeatureToggles.isFeatureEnabled) {
|
||||
txHistoryContentUpdateEmitter.triggerUpdate()
|
||||
} else {
|
||||
getTxHistoryItemsUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
currency = cryptoCurrency,
|
||||
refresh = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribeOnCheckFeeResultUpdates() {
|
||||
sendFeeCheckReloadListener.checkReloadResultFlow.onEach { isFeeResultSuccess ->
|
||||
if (isFeeResultSuccess) {
|
||||
|
|
@ -462,13 +402,13 @@ internal class SendConfirmModel @Inject constructor(
|
|||
modelScope.launch {
|
||||
notificationsUpdateTrigger.triggerUpdate(
|
||||
data = NotificationData(
|
||||
destinationAddress = enteredDestination.orEmpty(),
|
||||
memo = enteredMemo,
|
||||
amountValue = enteredAmount.orZero(),
|
||||
reduceAmountBy = reduceAmountBy.orZero(),
|
||||
isIgnoreReduce = isIgnoreReduce,
|
||||
fee = fee,
|
||||
feeError = feeError,
|
||||
destinationAddress = confirmData.enteredDestination.orEmpty(),
|
||||
memo = confirmData.enteredMemo,
|
||||
amountValue = confirmData.enteredAmount.orZero(),
|
||||
reduceAmountBy = confirmData.reduceAmountBy.orZero(),
|
||||
isIgnoreReduce = confirmData.isIgnoreReduce,
|
||||
fee = confirmData.fee,
|
||||
feeError = confirmData.feeError,
|
||||
),
|
||||
)
|
||||
_uiState.update {
|
||||
|
|
@ -554,6 +494,5 @@ internal class SendConfirmModel @Inject constructor(
|
|||
|
||||
private companion object {
|
||||
const val CHECK_FEE_UPDATE_DELAY = 10_000L
|
||||
const val BALANCE_UPDATE_DELAY = 11_000L
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
package com.tangem.features.send.v2.send.confirm.model.transformers
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.features.send.v2.send.confirm.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package com.tangem.features.send.v2.send.confirm.model.transformers
|
||||
|
||||
import com.tangem.features.send.v2.send.confirm.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.send.ui.state.SendUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
package com.tangem.features.send.v2.send.confirm.model.transformers
|
||||
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.features.send.v2.send.confirm.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.send.ui.state.SendUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class SendConfirmSentStateTransformer(
|
||||
val txData: TransactionData.Uncompiled,
|
||||
val txUrl: String,
|
||||
private val txData: TransactionData.Uncompiled,
|
||||
private val txUrl: String,
|
||||
) : Transformer<SendUM> {
|
||||
override fun transform(prevState: SendUM): SendUM {
|
||||
return prevState.copy(
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import com.tangem.domain.appcurrency.model.AppCurrency
|
|||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.send.analytics.SendAnalyticEvents
|
||||
import com.tangem.features.send.v2.send.confirm.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.checkIfFeeTooHigh
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
|
|
|
|||
|
|
@ -5,20 +5,15 @@ import androidx.compose.animation.core.tween
|
|||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
|
|
@ -26,12 +21,16 @@ import com.tangem.core.ui.components.Keyboard
|
|||
import com.tangem.core.ui.components.SpacerHMax
|
||||
import com.tangem.core.ui.components.keyboardAsState
|
||||
import com.tangem.core.ui.components.transactions.TransactionDoneTitle
|
||||
import com.tangem.core.ui.extensions.*
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resolveAnnotatedReference
|
||||
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.toTimeFormat
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.common.ui.tapHelp
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.send.confirm.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.send.ui.state.SendUM
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountBlockComponent
|
||||
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationBlockComponent
|
||||
|
|
@ -39,11 +38,8 @@ import com.tangem.features.send.v2.subcomponents.fee.SendFeeBlockComponent
|
|||
import com.tangem.features.send.v2.subcomponents.notifications
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.NotificationsComponent
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
private const val TAP_HELP_KEY = "TAP_HELP_KEY"
|
||||
private const val BLOCKS_KEY = "BLOCKS_KEY"
|
||||
private const val TAP_HELP_ANIMATION_DELAY = 500L
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
|
|
@ -148,45 +144,4 @@ private fun LazyListScope.blocks(
|
|||
feeBlockComponent.Content(modifier = Modifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.tapHelp(isDisplay: Boolean, modifier: Modifier = Modifier) {
|
||||
item(key = TAP_HELP_KEY) {
|
||||
var wrappedIsDisplay by remember { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(key1 = isDisplay) {
|
||||
delay(TAP_HELP_ANIMATION_DELAY)
|
||||
wrappedIsDisplay = isDisplay
|
||||
}
|
||||
|
||||
if (wrappedIsDisplay) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.animateItem()
|
||||
.padding(top = TangemTheme.dimens.spacing20),
|
||||
) {
|
||||
val background = TangemTheme.colors.button.secondary
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_send_hint_shape_12),
|
||||
tint = TangemTheme.colors.button.secondary,
|
||||
contentDescription = null,
|
||||
modifier = Modifier,
|
||||
)
|
||||
Text(
|
||||
text = stringResourceSafe(id = R.string.send_summary_tap_hint),
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
modifier = Modifier
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(background)
|
||||
.padding(
|
||||
horizontal = TangemTheme.dimens.spacing14,
|
||||
vertical = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.features.send.v2.send.di
|
||||
|
||||
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 dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(ModelComponent::class)
|
||||
internal interface CommonSendModelModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(SendModel::class)
|
||||
fun provideSendModel(model: SendModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(SendConfirmModel::class)
|
||||
fun provideSendConfirmModel(model: SendConfirmModel): Model
|
||||
}
|
||||
|
|
@ -29,11 +29,12 @@ import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
|||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.features.send.v2.api.SendComponent
|
||||
import com.tangem.features.send.v2.common.NavigationUM
|
||||
import com.tangem.features.send.v2.send.SendRoute
|
||||
import com.tangem.features.send.v2.send.confirm.model.SendConfirmAlertFactory
|
||||
import com.tangem.features.send.v2.common.CommonSendRoute
|
||||
import com.tangem.features.send.v2.common.PredefinedValues
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.common.ui.state.NavigationUM
|
||||
import com.tangem.features.send.v2.send.confirm.SendConfirmComponent
|
||||
import com.tangem.features.send.v2.send.confirm.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.send.confirm.model.SendConfirmAlertFactory
|
||||
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.destination.SendDestinationComponent
|
||||
|
|
@ -90,7 +91,7 @@ internal class SendModel @Inject constructor(
|
|||
var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
|
||||
var feeCryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
|
||||
var appCurrency: AppCurrency = AppCurrency.Default
|
||||
var predefinedAmountValue: String? = null
|
||||
var predefinedValues: PredefinedValues = PredefinedValues.Empty
|
||||
|
||||
private var balanceHidingJobHolder = JobHolder()
|
||||
|
||||
|
|
@ -214,7 +215,7 @@ internal class SendModel @Inject constructor(
|
|||
feeCryptoCurrencyStatus = feeCurrencyStatus
|
||||
|
||||
if (params.amount != null) {
|
||||
router.replaceAll(SendRoute.Confirm)
|
||||
router.replaceAll(CommonSendRoute.Confirm)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -227,7 +228,11 @@ internal class SendModel @Inject constructor(
|
|||
|
||||
private fun onQrCodeScanned(address: String) {
|
||||
val parsedQrCode = parseQrCodeUseCase(address, cryptoCurrency).getOrNull()
|
||||
predefinedAmountValue = parsedQrCode?.amount?.parseBigDecimal(cryptoCurrency.decimals)
|
||||
predefinedValues = PredefinedValues.Content.QrCode(
|
||||
amount = parsedQrCode?.amount?.parseBigDecimal(cryptoCurrency.decimals).orEmpty(),
|
||||
address = parsedQrCode?.address.orEmpty(),
|
||||
memo = parsedQrCode?.memo,
|
||||
)
|
||||
}
|
||||
|
||||
private fun onFailedTxEmailClick(errorMessage: String? = null) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.features.send.v2.send.ui.state
|
||||
|
||||
import com.tangem.common.ui.amountScreen.models.AmountState
|
||||
import com.tangem.features.send.v2.common.NavigationUM
|
||||
import com.tangem.features.send.v2.send.confirm.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.common.ui.state.NavigationUM
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,200 @@
|
|||
package com.tangem.features.send.v2.sendnft
|
||||
|
||||
import androidx.activity.compose.BackHandler
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.arkivanov.decompose.extensions.compose.subscribeAsState
|
||||
import com.arkivanov.decompose.router.stack.StackNavigation
|
||||
import com.arkivanov.decompose.router.stack.childStack
|
||||
import com.arkivanov.decompose.router.stack.pop
|
||||
import com.arkivanov.decompose.value.ObserveLifecycleMode
|
||||
import com.arkivanov.decompose.value.subscribe
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.childByContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.decompose.navigation.inner.InnerRouter
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.features.nft.component.NFTDetailsBlockComponent
|
||||
import com.tangem.features.send.v2.api.NFTSendComponent
|
||||
import com.tangem.features.send.v2.common.CommonSendRoute
|
||||
import com.tangem.features.send.v2.common.ui.SendContent
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.sendnft.confirm.NFTSendConfirmComponent
|
||||
import com.tangem.features.send.v2.sendnft.model.NFTSendModel
|
||||
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationComponent
|
||||
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationComponentParams
|
||||
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponentParams
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.launch
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class DefaultNFTSendComponent @AssistedInject constructor(
|
||||
@Assisted appComponentContext: AppComponentContext,
|
||||
@Assisted private val params: NFTSendComponent.Params,
|
||||
private val nftDetailsBlockComponentFactory: NFTDetailsBlockComponent.Factory,
|
||||
) : NFTSendComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val stackNavigation = StackNavigation<CommonSendRoute>()
|
||||
|
||||
private val innerRouter = InnerRouter<CommonSendRoute>(
|
||||
stackNavigation = stackNavigation,
|
||||
popCallback = { onChildBack() },
|
||||
)
|
||||
|
||||
private val initialRoute = CommonSendRoute.Empty
|
||||
private val currentRouteFlow = MutableStateFlow<CommonSendRoute>(initialRoute)
|
||||
|
||||
private val model: NFTSendModel = getOrCreateModel(params = params, router = innerRouter)
|
||||
|
||||
private val childStack = childStack(
|
||||
key = "NFTSendInnerStack",
|
||||
source = stackNavigation,
|
||||
serializer = null,
|
||||
initialConfiguration = initialRoute,
|
||||
handleBackButton = true,
|
||||
childFactory = { configuration, factoryContext ->
|
||||
createChild(
|
||||
configuration,
|
||||
childByContext(
|
||||
componentContext = factoryContext,
|
||||
router = innerRouter,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
init {
|
||||
childStack.subscribe(
|
||||
lifecycle = lifecycle,
|
||||
mode = ObserveLifecycleMode.CREATE_DESTROY,
|
||||
) { stack ->
|
||||
componentScope.launch {
|
||||
when (val activeComponent = stack.active.instance) {
|
||||
is NFTSendConfirmComponent -> if (currentRouteFlow.value.isEditMode) {
|
||||
// analyticsEventHandler.send(SendAnalyticEvents.ConfirmationScreenOpened)
|
||||
activeComponent.updateState(model.uiState.value)
|
||||
}
|
||||
is SendDestinationComponent -> {
|
||||
// analyticsEventHandler.send(SendAnalyticEvents.AddressScreenOpened)
|
||||
activeComponent.updateState(model.uiState.value.destinationUM)
|
||||
}
|
||||
is SendFeeComponent -> {
|
||||
// analyticsEventHandler.send(SendAnalyticEvents.FeeScreenOpened)
|
||||
}
|
||||
}
|
||||
currentRouteFlow.emit(stack.active.configuration)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val stackState by childStack.subscribeAsState()
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
|
||||
BackHandler(onBack = ::onChildBack)
|
||||
SendContent(
|
||||
navigationUM = state.navigationUM,
|
||||
stackState = stackState,
|
||||
)
|
||||
}
|
||||
|
||||
private fun createChild(route: CommonSendRoute, factoryContext: AppComponentContext) = when (route) {
|
||||
is CommonSendRoute.Destination -> getDestinationComponent(factoryContext, route)
|
||||
is CommonSendRoute.Fee -> getFeeComponent(factoryContext)
|
||||
CommonSendRoute.Confirm -> getConfirmComponent(factoryContext)
|
||||
else -> getStubComponent()
|
||||
}
|
||||
|
||||
private fun getDestinationComponent(factoryContext: AppComponentContext, route: CommonSendRoute) =
|
||||
SendDestinationComponent(
|
||||
appComponentContext = factoryContext,
|
||||
params = SendDestinationComponentParams.DestinationParams(
|
||||
state = model.uiState.value.destinationUM,
|
||||
currentRoute = currentRouteFlow.filterIsInstance<CommonSendRoute.Destination>(),
|
||||
isBalanceHidingFlow = model.isBalanceHiddenFlow,
|
||||
analyticsCategoryName = "", // todo
|
||||
userWalletId = params.userWalletId,
|
||||
cryptoCurrency = model.cryptoCurrency,
|
||||
callback = model,
|
||||
onBackClick = ::onChildBack,
|
||||
onNextClick = {
|
||||
if (route.isEditMode) {
|
||||
onChildBack()
|
||||
} else {
|
||||
innerRouter.push(CommonSendRoute.Confirm)
|
||||
}
|
||||
},
|
||||
),
|
||||
)
|
||||
|
||||
private fun getFeeComponent(factoryContext: AppComponentContext): ComposableContentComponent {
|
||||
val state = model.uiState.value
|
||||
val destinationAddress = (state.destinationUM as? DestinationUM.Content)?.addressTextField?.value
|
||||
return if (destinationAddress != null) {
|
||||
SendFeeComponent(
|
||||
appComponentContext = factoryContext,
|
||||
params = SendFeeComponentParams.FeeParams(
|
||||
state = model.uiState.value.feeUM,
|
||||
currentRoute = currentRouteFlow.filterIsInstance<CommonSendRoute.Fee>(),
|
||||
analyticsCategoryName = "", // todo
|
||||
userWallet = model.userWallet,
|
||||
cryptoCurrencyStatus = model.cryptoCurrencyStatus,
|
||||
feeCryptoCurrencyStatus = model.feeCryptoCurrencyStatus,
|
||||
appCurrency = model.appCurrency,
|
||||
sendAmount = BigDecimal.ZERO,
|
||||
destinationAddress = destinationAddress,
|
||||
callback = model,
|
||||
onNextClick = ::onChildBack,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
getStubComponent()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getConfirmComponent(factoryContext: AppComponentContext) = NFTSendConfirmComponent(
|
||||
appComponentContext = factoryContext,
|
||||
nftDetailsBlockComponentFactory = nftDetailsBlockComponentFactory,
|
||||
params = NFTSendConfirmComponent.Params(
|
||||
state = model.uiState.value,
|
||||
analyticsCategoryName = "", // todo
|
||||
userWallet = model.userWallet,
|
||||
nftAsset = params.nftAsset,
|
||||
nftCollectionName = params.nftCollectionName,
|
||||
cryptoCurrencyStatus = model.cryptoCurrencyStatus,
|
||||
feeCryptoCurrencyStatus = model.feeCryptoCurrencyStatus,
|
||||
appCurrency = model.appCurrency,
|
||||
callback = model,
|
||||
currentRoute = currentRouteFlow.filterIsInstance<CommonSendRoute.Confirm>(),
|
||||
isBalanceHidingFlow = model.isBalanceHiddenFlow,
|
||||
),
|
||||
)
|
||||
|
||||
private fun getStubComponent() = ComposableContentComponent { }
|
||||
|
||||
private fun onChildBack() {
|
||||
val isEmptyRoute = childStack.value.active.configuration == CommonSendRoute.Empty
|
||||
val isEmptyStack = childStack.value.backStack.isEmpty()
|
||||
val isSuccess = model.uiState.value.confirmUM is ConfirmUM.Success
|
||||
|
||||
if (isEmptyRoute || isEmptyStack || isSuccess) {
|
||||
router.pop()
|
||||
} else {
|
||||
stackNavigation.pop()
|
||||
}
|
||||
}
|
||||
|
||||
@AssistedFactory
|
||||
interface Factory : NFTSendComponent.Factory {
|
||||
override fun create(context: AppComponentContext, params: NFTSendComponent.Params): DefaultNFTSendComponent
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,147 @@
|
|||
package com.tangem.features.send.v2.sendnft.confirm
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.context.child
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.nft.models.NFTAsset
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.features.nft.component.NFTDetailsBlockComponent
|
||||
import com.tangem.features.send.v2.common.CommonSendRoute
|
||||
import com.tangem.features.send.v2.common.PredefinedValues
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.sendnft.confirm.model.NFTSendConfirmModel
|
||||
import com.tangem.features.send.v2.sendnft.confirm.ui.NFTSendConfirmContent
|
||||
import com.tangem.features.send.v2.sendnft.ui.state.NFTSendUM
|
||||
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationBlockComponent
|
||||
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationComponentParams.DestinationBlockParams
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeBlockComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponentParams
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.NotificationsComponent
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.model.NotificationData
|
||||
import kotlinx.coroutines.flow.*
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class NFTSendConfirmComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
params: Params,
|
||||
nftDetailsBlockComponentFactory: NFTDetailsBlockComponent.Factory,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: NFTSendConfirmModel = getOrCreateModel(params = params)
|
||||
|
||||
private val blockClickEnableFlow = MutableStateFlow(false)
|
||||
|
||||
private val destinationBlockComponent =
|
||||
SendDestinationBlockComponent(
|
||||
appComponentContext = child("NFTSendConfirmDestinationBlock"),
|
||||
params = DestinationBlockParams(
|
||||
state = model.uiState.value.destinationUM,
|
||||
analyticsCategoryName = params.analyticsCategoryName,
|
||||
userWalletId = params.userWallet.walletId,
|
||||
cryptoCurrency = params.cryptoCurrencyStatus.currency,
|
||||
blockClickEnableFlow = blockClickEnableFlow.asStateFlow(),
|
||||
predefinedValues = PredefinedValues.Empty,
|
||||
),
|
||||
onResult = model::onDestinationResult,
|
||||
onClick = model::showEditDestination,
|
||||
)
|
||||
|
||||
private val feeBlockComponent = SendFeeBlockComponent(
|
||||
appComponentContext = child("NFTSendConfirmFeeBlock"),
|
||||
params = SendFeeComponentParams.FeeBlockParams(
|
||||
state = model.uiState.value.feeUM,
|
||||
analyticsCategoryName = params.analyticsCategoryName,
|
||||
userWallet = params.userWallet,
|
||||
cryptoCurrencyStatus = params.cryptoCurrencyStatus,
|
||||
feeCryptoCurrencyStatus = params.feeCryptoCurrencyStatus,
|
||||
appCurrency = params.appCurrency,
|
||||
sendAmount = BigDecimal.ZERO,
|
||||
destinationAddress = model.confirmData.enteredDestination.orEmpty(),
|
||||
blockClickEnableFlow = blockClickEnableFlow.asStateFlow(),
|
||||
),
|
||||
onResult = model::onFeeResult,
|
||||
onClick = model::showEditFee,
|
||||
)
|
||||
|
||||
private val nftDetailsBlockComponent = nftDetailsBlockComponentFactory.create(
|
||||
context = child("NFTDetailsBlock"),
|
||||
params = NFTDetailsBlockComponent.Params(
|
||||
userWalletId = params.userWallet.walletId,
|
||||
nftAsset = params.nftAsset,
|
||||
nftCollectionName = params.nftCollectionName,
|
||||
),
|
||||
)
|
||||
|
||||
private val notificationsComponent = NotificationsComponent(
|
||||
appComponentContext = child("NFTSendConfirmNotifications"),
|
||||
params = NotificationsComponent.Params(
|
||||
analyticsCategoryName = params.analyticsCategoryName,
|
||||
userWalletId = params.userWallet.walletId,
|
||||
cryptoCurrencyStatus = params.cryptoCurrencyStatus,
|
||||
feeCryptoCurrencyStatus = params.feeCryptoCurrencyStatus,
|
||||
appCurrency = params.appCurrency,
|
||||
notificationData = NotificationData(
|
||||
destinationAddress = model.confirmData.enteredDestination.orEmpty(),
|
||||
memo = model.confirmData.enteredMemo,
|
||||
amountValue = BigDecimal.ZERO,
|
||||
reduceAmountBy = BigDecimal.ZERO,
|
||||
isIgnoreReduce = false,
|
||||
fee = model.confirmData.fee,
|
||||
feeError = model.confirmData.feeError,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
init {
|
||||
model.uiState.onEach { state ->
|
||||
val confirmUM = state.confirmUM as? ConfirmUM.Content
|
||||
blockClickEnableFlow.value = confirmUM?.isSending == false
|
||||
}.launchIn(componentScope)
|
||||
}
|
||||
|
||||
fun updateState(state: NFTSendUM) {
|
||||
destinationBlockComponent.updateState(state.destinationUM)
|
||||
feeBlockComponent.updateState(state.feeUM)
|
||||
model.updateState(state)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
val notificationState by notificationsComponent.state.collectAsStateWithLifecycle()
|
||||
|
||||
NFTSendConfirmContent(
|
||||
nftSendUM = state,
|
||||
destinationBlockComponent = destinationBlockComponent,
|
||||
feeBlockComponent = feeBlockComponent,
|
||||
nftDetailsBlockComponent = nftDetailsBlockComponent,
|
||||
notificationsComponent = notificationsComponent,
|
||||
notificationsUM = notificationState,
|
||||
)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val state: NFTSendUM,
|
||||
val analyticsCategoryName: String,
|
||||
val userWallet: UserWallet,
|
||||
val appCurrency: AppCurrency,
|
||||
val nftAsset: NFTAsset,
|
||||
val nftCollectionName: String,
|
||||
val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
val feeCryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
val callback: ModelCallback,
|
||||
val currentRoute: Flow<CommonSendRoute.Confirm>,
|
||||
val isBalanceHidingFlow: StateFlow<Boolean>,
|
||||
)
|
||||
|
||||
interface ModelCallback {
|
||||
fun onResult(nftSendUM: NFTSendUM)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
package com.tangem.features.send.v2.sendnft.confirm.model
|
||||
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.domain.transaction.error.GetFeeError
|
||||
|
||||
data class ConfirmData(
|
||||
val enteredDestination: String?,
|
||||
val enteredMemo: String?,
|
||||
val fee: Fee?,
|
||||
val feeError: GetFeeError?,
|
||||
)
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
package com.tangem.features.send.v2.sendnft.confirm.model
|
||||
|
||||
internal interface NFTSendConfirmClickIntents {
|
||||
|
||||
fun showEditDestination()
|
||||
|
||||
fun showEditFee()
|
||||
|
||||
fun onSendClick()
|
||||
|
||||
fun onExploreClick()
|
||||
|
||||
fun onShareClick()
|
||||
|
||||
fun onFailedTxEmailClick(errorMessage: String)
|
||||
}
|
||||
|
|
@ -0,0 +1,337 @@
|
|||
package com.tangem.features.send.v2.sendnft.confirm.model
|
||||
|
||||
import android.os.SystemClock
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.routing.AppRouter
|
||||
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.decompose.navigation.Router
|
||||
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.wrappedList
|
||||
import com.tangem.domain.settings.IsSendTapHelpEnabledUseCase
|
||||
import com.tangem.domain.settings.NeverShowTapHelpUseCase
|
||||
import com.tangem.features.send.v2.common.CommonSendRoute
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.common.ui.state.NavigationUM
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.send.analytics.SendAnalyticEvents
|
||||
import com.tangem.features.send.v2.send.analytics.SendAnalyticEvents.SendScreenSource
|
||||
import com.tangem.features.send.v2.send.ui.state.ButtonsUM
|
||||
import com.tangem.features.send.v2.sendnft.confirm.NFTSendConfirmComponent
|
||||
import com.tangem.features.send.v2.sendnft.confirm.model.transformers.NFTSendConfirmInitialStateTransformer
|
||||
import com.tangem.features.send.v2.sendnft.confirm.model.transformers.NFTSendConfirmSendingStateTransformer
|
||||
import com.tangem.features.send.v2.sendnft.confirm.model.transformers.NFTSendConfirmationNotificationsTransformer
|
||||
import com.tangem.features.send.v2.sendnft.ui.state.NFTSendUM
|
||||
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeCheckReloadListener
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeCheckReloadTrigger
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.NotificationsUpdateTrigger
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.model.NotificationData
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.transformer.update
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import java.math.BigDecimal
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@ModelScoped
|
||||
internal class NFTSendConfirmModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val router: Router,
|
||||
private val appRouter: AppRouter,
|
||||
private val isSendTapHelpEnabledUseCase: IsSendTapHelpEnabledUseCase,
|
||||
private val neverShowTapHelpUseCase: NeverShowTapHelpUseCase,
|
||||
private val notificationsUpdateTrigger: NotificationsUpdateTrigger,
|
||||
private val sendFeeCheckReloadTrigger: SendFeeCheckReloadTrigger,
|
||||
private val sendFeeCheckReloadListener: SendFeeCheckReloadListener,
|
||||
private val urlOpener: UrlOpener,
|
||||
private val shareManager: ShareManager,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
) : Model(), NFTSendConfirmClickIntents {
|
||||
|
||||
private val params: NFTSendConfirmComponent.Params = paramsContainer.require()
|
||||
|
||||
private val cryptoCurrencyStatus = params.cryptoCurrencyStatus
|
||||
|
||||
private val _uiState = MutableStateFlow(params.state)
|
||||
val uiState = _uiState.asStateFlow()
|
||||
|
||||
private val destinationUM
|
||||
get() = uiState.value.destinationUM as? DestinationUM.Content
|
||||
private val feeUM
|
||||
get() = uiState.value.feeUM as? FeeUM.Content
|
||||
private val feeSelectorUM
|
||||
get() = feeUM?.feeSelectorUM as? FeeSelectorUM.Content
|
||||
|
||||
val confirmData: ConfirmData
|
||||
get() = ConfirmData(
|
||||
enteredDestination = destinationUM?.addressTextField?.value,
|
||||
enteredMemo = destinationUM?.memoTextField?.value,
|
||||
fee = feeSelectorUM?.selectedFee,
|
||||
feeError = (feeUM?.feeSelectorUM as? FeeSelectorUM.Error)?.error,
|
||||
)
|
||||
|
||||
private var sendIdleTimer: Long = 0L
|
||||
|
||||
init {
|
||||
configConfirmNavigation()
|
||||
subscribeOnNotificationsUpdateTrigger()
|
||||
subscribeOnCheckFeeResultUpdates()
|
||||
initialState()
|
||||
}
|
||||
|
||||
fun updateState(nftSendUM: NFTSendUM) {
|
||||
_uiState.value = nftSendUM
|
||||
updateConfirmNotifications()
|
||||
}
|
||||
|
||||
fun onFeeResult(feeUM: FeeUM) {
|
||||
sendIdleTimer = SystemClock.elapsedRealtime()
|
||||
_uiState.update { it.copy(feeUM = feeUM) }
|
||||
updateConfirmNotifications()
|
||||
}
|
||||
|
||||
fun onDestinationResult(destinationUM: DestinationUM) {
|
||||
_uiState.update { it.copy(destinationUM = destinationUM) }
|
||||
updateConfirmNotifications()
|
||||
}
|
||||
|
||||
override fun showEditDestination() {
|
||||
modelScope.launch {
|
||||
neverShowTapHelpUseCase()
|
||||
_uiState.update {
|
||||
val confirmUM = it.confirmUM as? ConfirmUM.Content
|
||||
it.copy(confirmUM = confirmUM?.copy(showTapHelp = false) ?: it.confirmUM)
|
||||
}
|
||||
// analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Address))
|
||||
router.push(CommonSendRoute.Destination(isEditMode = true))
|
||||
}
|
||||
}
|
||||
|
||||
override fun showEditFee() {
|
||||
modelScope.launch {
|
||||
neverShowTapHelpUseCase()
|
||||
_uiState.update {
|
||||
val confirmUM = it.confirmUM as? ConfirmUM.Content
|
||||
it.copy(confirmUM = confirmUM?.copy(showTapHelp = false) ?: it.confirmUM)
|
||||
}
|
||||
// analyticsEventHandler.send(SendAnalyticEvents.ScreenReopened(SendScreenSource.Fee))
|
||||
router.push(CommonSendRoute.Fee)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSendClick() {
|
||||
_uiState.update(NFTSendConfirmSendingStateTransformer(isSending = true))
|
||||
if (SystemClock.elapsedRealtime() - sendIdleTimer < CHECK_FEE_UPDATE_DELAY) {
|
||||
verifyAndSendTransaction()
|
||||
} else {
|
||||
modelScope.launch {
|
||||
sendFeeCheckReloadTrigger.triggerCheckUpdate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onExploreClick() {
|
||||
val confirmUM = uiState.value.confirmUM as? ConfirmUM.Success ?: return
|
||||
// analyticsEventHandler.send(SendAnalyticEvents.ExploreButtonClicked)
|
||||
urlOpener.openUrl(confirmUM.txUrl)
|
||||
}
|
||||
|
||||
override fun onShareClick() {
|
||||
val confirmUM = uiState.value.confirmUM as? ConfirmUM.Success ?: return
|
||||
// analyticsEventHandler.send(SendAnalyticEvents.ShareButtonClicked)
|
||||
shareManager.shareText(confirmUM.txUrl)
|
||||
}
|
||||
|
||||
override fun onFailedTxEmailClick(errorMessage: String) {
|
||||
// TODO()
|
||||
}
|
||||
|
||||
private fun initialState() {
|
||||
val confirmUM = uiState.value.confirmUM
|
||||
val feeUM = uiState.value.feeUM
|
||||
|
||||
modelScope.launch {
|
||||
val isShowTapHelp = isSendTapHelpEnabledUseCase().getOrElse { false }
|
||||
if (confirmUM is ConfirmUM.Empty || feeUM is FeeUM.Empty) {
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
confirmUM = NFTSendConfirmInitialStateTransformer(
|
||||
isShowTapHelp = isShowTapHelp,
|
||||
).transform(uiState.value.confirmUM),
|
||||
)
|
||||
}
|
||||
updateConfirmNotifications()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribeOnNotificationsUpdateTrigger() {
|
||||
notificationsUpdateTrigger.hasErrorFlow
|
||||
.onEach { hasError ->
|
||||
_uiState.update {
|
||||
val feeUM = it.feeUM as? FeeUM.Content
|
||||
val feeSelectorUM = feeUM?.feeSelectorUM as? FeeSelectorUM.Content
|
||||
it.copy(
|
||||
confirmUM = (it.confirmUM as? ConfirmUM.Content)?.copy(
|
||||
isPrimaryButtonEnabled = !hasError && feeSelectorUM != null,
|
||||
) ?: it.confirmUM,
|
||||
)
|
||||
}
|
||||
}
|
||||
.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun verifyAndSendTransaction() {
|
||||
// TODO:
|
||||
}
|
||||
|
||||
// private suspend fun sendTransaction(txData: TransactionData.Uncompiled) {
|
||||
// val result = sendTransactionUseCase(
|
||||
// txData = txData,
|
||||
// userWallet = userWallet,
|
||||
// network = cryptoCurrency.network,
|
||||
// )
|
||||
//
|
||||
// _uiState.update(NFTSendConfirmSendingStateTransformer(isSending = false))
|
||||
//
|
||||
// result.fold(
|
||||
// ifLeft = { error ->
|
||||
// // alertFactory.getSendTransactionErrorState(
|
||||
// // error = error,
|
||||
// // popBack = appRouter::pop,
|
||||
// // onFailedTxEmailClick = ::onFailedTxEmailClick,
|
||||
// // )
|
||||
// analyticsEventHandler.send(SendAnalyticEvents.TransactionError(cryptoCurrency.symbol))
|
||||
// },
|
||||
// ifRight = {
|
||||
// updateTransactionStatus(txData)
|
||||
// sendBalanceUpdater.scheduleUpdates()
|
||||
// // sendAnalyticHelper.sendSuccessAnalytics(cryptoCurrency, uiState.value)
|
||||
// },
|
||||
// )
|
||||
// }
|
||||
|
||||
private fun subscribeOnCheckFeeResultUpdates() {
|
||||
sendFeeCheckReloadListener.checkReloadResultFlow.onEach { isFeeResultSuccess ->
|
||||
if (isFeeResultSuccess) {
|
||||
sendIdleTimer = SystemClock.elapsedRealtime()
|
||||
_uiState.update(NFTSendConfirmSendingStateTransformer(isSending = true))
|
||||
verifyAndSendTransaction()
|
||||
} else {
|
||||
_uiState.update(NFTSendConfirmSendingStateTransformer(isSending = false))
|
||||
}
|
||||
}.launchIn(modelScope)
|
||||
}
|
||||
|
||||
// private suspend fun updateTransactionStatus(txData: TransactionData.Uncompiled) {
|
||||
// val txUrl = getExplorerTransactionUrlUseCase(
|
||||
// userWalletId = userWallet.walletId,
|
||||
// network = cryptoCurrency.network,
|
||||
// ).getOrElse { "" }
|
||||
// _uiState.update(NFTSendConfirmSentStateTransformer(txData, txUrl))
|
||||
// }
|
||||
|
||||
private fun updateConfirmNotifications() {
|
||||
modelScope.launch {
|
||||
notificationsUpdateTrigger.triggerUpdate(
|
||||
data = NotificationData(
|
||||
destinationAddress = confirmData.enteredDestination.orEmpty(),
|
||||
memo = confirmData.enteredMemo,
|
||||
amountValue = BigDecimal.ZERO,
|
||||
reduceAmountBy = BigDecimal.ZERO,
|
||||
isIgnoreReduce = false,
|
||||
fee = confirmData.fee,
|
||||
feeError = confirmData.feeError,
|
||||
),
|
||||
)
|
||||
}
|
||||
_uiState.update {
|
||||
it.copy(
|
||||
confirmUM = NFTSendConfirmationNotificationsTransformer(
|
||||
feeUM = uiState.value.feeUM,
|
||||
analyticsEventHandler = analyticsEventHandler,
|
||||
cryptoCurrency = cryptoCurrencyStatus.currency,
|
||||
).transform(uiState.value.confirmUM),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun configConfirmNavigation() {
|
||||
combine(
|
||||
flow = uiState,
|
||||
flow2 = params.currentRoute,
|
||||
transform = { state, route -> state to route },
|
||||
).onEach { (state, _) ->
|
||||
val confirmUM = state.confirmUM
|
||||
params.callback.onResult(
|
||||
state.copy(
|
||||
navigationUM = NavigationUM.Content(
|
||||
title = resourceReference(
|
||||
id = R.string.send_summary_title,
|
||||
formatArgs = wrappedList(params.cryptoCurrencyStatus.currency.name),
|
||||
),
|
||||
subtitle = null,
|
||||
backIconRes = R.drawable.ic_close_24,
|
||||
backIconClick = {
|
||||
analyticsEventHandler.send(
|
||||
SendAnalyticEvents.CloseButtonClicked(
|
||||
source = SendScreenSource.Confirm,
|
||||
isFromSummary = true,
|
||||
isValid = confirmUM.isPrimaryButtonEnabled,
|
||||
),
|
||||
)
|
||||
appRouter.pop()
|
||||
},
|
||||
primaryButton = ButtonsUM.PrimaryButtonUM(
|
||||
text = 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)
|
||||
},
|
||||
iconResId = R.drawable.ic_tangem_24.takeIf { confirmUM is ConfirmUM.Content },
|
||||
isEnabled = confirmUM.isPrimaryButtonEnabled,
|
||||
isHapticClick = confirmUM is ConfirmUM.Content && !confirmUM.isSending,
|
||||
onClick = {
|
||||
when (confirmUM) {
|
||||
is ConfirmUM.Success -> appRouter.pop()
|
||||
is ConfirmUM.Content -> if (confirmUM.isSending) {
|
||||
return@PrimaryButtonUM
|
||||
} else {
|
||||
onSendClick()
|
||||
}
|
||||
else -> return@PrimaryButtonUM
|
||||
}
|
||||
},
|
||||
),
|
||||
prevButton = null,
|
||||
secondaryPairButtonsUM = ButtonsUM.SecondaryPairButtonsUM(
|
||||
leftText = resourceReference(R.string.common_explore),
|
||||
leftIconResId = R.drawable.ic_web_24,
|
||||
onLeftClick = ::onExploreClick,
|
||||
rightText = resourceReference(R.string.common_share),
|
||||
rightIconResId = R.drawable.ic_share_24,
|
||||
onRightClick = ::onShareClick,
|
||||
).takeIf { confirmUM is ConfirmUM.Success },
|
||||
),
|
||||
),
|
||||
)
|
||||
}.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val CHECK_FEE_UPDATE_DELAY = 10_000L
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
package com.tangem.features.send.v2.sendnft.confirm.model
|
||||
|
||||
import com.tangem.blockchain.common.TransactionData
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.sendnft.ui.state.NFTSendUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class NFTSendConfirmSentStateTransformer(
|
||||
private val txData: TransactionData.Uncompiled,
|
||||
private val txUrl: String,
|
||||
) : Transformer<NFTSendUM> {
|
||||
override fun transform(prevState: NFTSendUM): NFTSendUM {
|
||||
return prevState.copy(
|
||||
confirmUM = ConfirmUM.Success(
|
||||
transactionDate = txData.date?.timeInMillis ?: System.currentTimeMillis(),
|
||||
txUrl = txUrl,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.features.send.v2.sendnft.confirm.model.transformers
|
||||
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
|
||||
internal class NFTSendConfirmInitialStateTransformer(
|
||||
private val isShowTapHelp: Boolean,
|
||||
) : Transformer<ConfirmUM> {
|
||||
override fun transform(prevState: ConfirmUM): ConfirmUM {
|
||||
return ConfirmUM.Content(
|
||||
isSending = false,
|
||||
showTapHelp = isShowTapHelp,
|
||||
sendingFooter = TextReference.EMPTY,
|
||||
notifications = persistentListOf(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package com.tangem.features.send.v2.sendnft.confirm.model.transformers
|
||||
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.sendnft.ui.state.NFTSendUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class NFTSendConfirmSendingStateTransformer(
|
||||
val isSending: Boolean,
|
||||
) : Transformer<NFTSendUM> {
|
||||
override fun transform(prevState: NFTSendUM): NFTSendUM {
|
||||
val confirmUM = prevState.confirmUM as? ConfirmUM.Content ?: return prevState
|
||||
return prevState.copy(
|
||||
confirmUM = confirmUM.copy(
|
||||
isPrimaryButtonEnabled = !isSending,
|
||||
isSending = isSending,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package com.tangem.features.send.v2.sendnft.confirm.model.transformers
|
||||
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.ui.utils.parseToBigDecimal
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.send.analytics.SendAnalyticEvents
|
||||
import com.tangem.features.send.v2.subcomponents.fee.model.checkIfFeeTooHigh
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeSelectorUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeType
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
import kotlinx.collections.immutable.toPersistentList
|
||||
|
||||
internal class NFTSendConfirmationNotificationsTransformer(
|
||||
private val feeUM: FeeUM,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val cryptoCurrency: CryptoCurrency,
|
||||
) : Transformer<ConfirmUM> {
|
||||
override fun transform(prevState: ConfirmUM): ConfirmUM {
|
||||
val state = prevState as? ConfirmUM.Content ?: return prevState
|
||||
val feeUM = feeUM as? FeeUM.Content ?: return prevState
|
||||
return state.copy(
|
||||
notifications = buildList {
|
||||
addTooHighNotification(feeUM.feeSelectorUM)
|
||||
addTooLowNotification(feeUM)
|
||||
}.toPersistentList(),
|
||||
)
|
||||
}
|
||||
|
||||
private fun MutableList<NotificationUM>.addTooLowNotification(feeUM: FeeUM.Content) {
|
||||
val feeSelectorUM = feeUM.feeSelectorUM as? FeeSelectorUM.Content ?: return
|
||||
val multipleFees = feeSelectorUM.fees as? TransactionFee.Choosable ?: return
|
||||
val minimumValue = multipleFees.minimum.amount.value ?: return
|
||||
val customAmount = feeSelectorUM.customValues.firstOrNull() ?: return
|
||||
val customValue = customAmount.value.parseToBigDecimal(customAmount.decimals)
|
||||
if (feeSelectorUM.selectedType == FeeType.Custom && minimumValue > customValue) {
|
||||
add(NotificationUM.Warning.FeeTooLow)
|
||||
analyticsEventHandler.send(
|
||||
SendAnalyticEvents.NoticeTransactionDelays(cryptoCurrency.symbol),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MutableList<NotificationUM>.addTooHighNotification(feeSelectorUM: FeeSelectorUM) {
|
||||
if (feeSelectorUM !is FeeSelectorUM.Content) return
|
||||
|
||||
val (isFeeTooHigh, diff) = checkIfFeeTooHigh(feeSelectorUM)
|
||||
if (isFeeTooHigh) {
|
||||
add(NotificationUM.Warning.TooHigh(diff))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
package com.tangem.features.send.v2.sendnft.confirm.ui
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
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.toTimeFormat
|
||||
import com.tangem.features.nft.component.NFTDetailsBlockComponent
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.common.ui.tapHelp
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.sendnft.ui.state.NFTSendUM
|
||||
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationBlockComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeBlockComponent
|
||||
import com.tangem.features.send.v2.subcomponents.notifications
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.NotificationsComponent
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
||||
private const val BLOCKS_KEY = "BLOCKS_KEY"
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Composable
|
||||
internal fun NFTSendConfirmContent(
|
||||
nftSendUM: NFTSendUM,
|
||||
destinationBlockComponent: SendDestinationBlockComponent,
|
||||
nftDetailsBlockComponent: NFTDetailsBlockComponent,
|
||||
feeBlockComponent: SendFeeBlockComponent,
|
||||
notificationsComponent: NotificationsComponent,
|
||||
notificationsUM: ImmutableList<NotificationUM>,
|
||||
) {
|
||||
val confirmUM = nftSendUM.confirmUM as? ConfirmUM.Content
|
||||
|
||||
Column {
|
||||
LazyColumn(
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
blocks(
|
||||
nftSendUM = nftSendUM,
|
||||
destinationBlockComponent = destinationBlockComponent,
|
||||
nftDetailsBlockComponent = nftDetailsBlockComponent,
|
||||
feeBlockComponent = feeBlockComponent,
|
||||
)
|
||||
if (confirmUM != null) {
|
||||
tapHelp(isDisplay = confirmUM.showTapHelp)
|
||||
with(notificationsComponent) {
|
||||
content(
|
||||
state = notificationsUM,
|
||||
isClickDisabled = confirmUM.isSending,
|
||||
)
|
||||
}
|
||||
notifications(
|
||||
notifications = confirmUM.notifications,
|
||||
isClickDisabled = confirmUM.isSending,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun LazyListScope.blocks(
|
||||
nftSendUM: NFTSendUM,
|
||||
destinationBlockComponent: SendDestinationBlockComponent,
|
||||
nftDetailsBlockComponent: NFTDetailsBlockComponent,
|
||||
feeBlockComponent: SendFeeBlockComponent,
|
||||
) {
|
||||
item(key = BLOCKS_KEY) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
AnimatedVisibility(
|
||||
visible = nftSendUM.confirmUM is ConfirmUM.Success,
|
||||
modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
val wrappedConfirmUM = remember(this) { nftSendUM.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),
|
||||
)
|
||||
}
|
||||
destinationBlockComponent.Content(modifier = Modifier)
|
||||
|
||||
nftDetailsBlockComponent.Content(modifier = Modifier)
|
||||
|
||||
feeBlockComponent.Content(modifier = Modifier)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package com.tangem.features.send.v2.sendnft.di
|
||||
|
||||
import com.tangem.core.decompose.di.ModelComponent
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.features.send.v2.sendnft.confirm.model.NFTSendConfirmModel
|
||||
import com.tangem.features.send.v2.sendnft.model.NFTSendModel
|
||||
import dagger.Binds
|
||||
import dagger.Module
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.multibindings.ClassKey
|
||||
import dagger.multibindings.IntoMap
|
||||
|
||||
@Module
|
||||
@InstallIn(ModelComponent::class)
|
||||
internal interface NFTSendModelModule {
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(NFTSendModel::class)
|
||||
fun provideNFTSendModel(model: NFTSendModel): Model
|
||||
|
||||
@Binds
|
||||
@IntoMap
|
||||
@ClassKey(NFTSendConfirmModel::class)
|
||||
fun provideNFTSendConfirmModel(model: NFTSendConfirmModel): Model
|
||||
}
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
package com.tangem.features.send.v2.sendnft.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import arrow.core.getOrElse
|
||||
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.decompose.navigation.Router
|
||||
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.tokens.GetCryptoCurrenciesUseCase
|
||||
import com.tangem.domain.tokens.GetCurrencyStatusUpdatesUseCase
|
||||
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.features.send.v2.api.NFTSendComponent
|
||||
import com.tangem.features.send.v2.common.CommonSendRoute
|
||||
import com.tangem.features.send.v2.common.ui.state.NavigationUM
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.sendnft.confirm.NFTSendConfirmComponent
|
||||
import com.tangem.features.send.v2.sendnft.ui.state.NFTSendUM
|
||||
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationComponent
|
||||
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
internal interface SendNFTComponentCallback :
|
||||
SendFeeComponent.ModelCallback,
|
||||
SendDestinationComponent.ModelCallback,
|
||||
NFTSendConfirmComponent.ModelCallback
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
@Stable
|
||||
@ModelScoped
|
||||
internal class NFTSendModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val router: Router,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getUserWalletUseCase: GetUserWalletUseCase,
|
||||
private val getCryptoCurrenciesUseCase: GetCryptoCurrenciesUseCase,
|
||||
private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase,
|
||||
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
|
||||
) : Model(), SendNFTComponentCallback {
|
||||
|
||||
val params: NFTSendComponent.Params = paramsContainer.require()
|
||||
private val userWalletId = params.userWalletId
|
||||
private val nftAsset = params.nftAsset
|
||||
|
||||
private val _uiState = MutableStateFlow(initialState())
|
||||
val uiState = _uiState.asStateFlow()
|
||||
|
||||
private val _isBalanceHiddenFlow = MutableStateFlow(false)
|
||||
val isBalanceHiddenFlow = _isBalanceHiddenFlow.asStateFlow()
|
||||
|
||||
var cryptoCurrency: CryptoCurrency by Delegates.notNull()
|
||||
var userWallet: UserWallet by Delegates.notNull()
|
||||
var cryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
|
||||
var feeCryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
|
||||
var appCurrency: AppCurrency = AppCurrency.Default
|
||||
|
||||
init {
|
||||
subscribeOnCurrencyStatusUpdates()
|
||||
initAppCurrency()
|
||||
}
|
||||
|
||||
override fun onNavigationResult(navigationUM: NavigationUM) {
|
||||
_uiState.update { it.copy(navigationUM = navigationUM) }
|
||||
}
|
||||
|
||||
override fun onResult(nftSendUM: NFTSendUM) {
|
||||
_uiState.value = nftSendUM
|
||||
}
|
||||
|
||||
override fun onDestinationResult(destinationUM: DestinationUM) {
|
||||
_uiState.update { it.copy(destinationUM = destinationUM) }
|
||||
}
|
||||
|
||||
override fun onFeeResult(feeUM: FeeUM) {
|
||||
_uiState.update { it.copy(feeUM = feeUM) }
|
||||
}
|
||||
|
||||
private fun initAppCurrency() {
|
||||
modelScope.launch {
|
||||
appCurrency = getSelectedAppCurrencyUseCase.invokeSync().getOrElse { AppCurrency.Default }
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribeOnCurrencyStatusUpdates() {
|
||||
modelScope.launch {
|
||||
getUserWalletUseCase(params.userWalletId).fold(
|
||||
ifRight = { wallet ->
|
||||
userWallet = wallet
|
||||
|
||||
cryptoCurrency = getCryptoCurrenciesUseCase(userWalletId).getOrNull()
|
||||
?.filterIsInstance<CryptoCurrency.Coin>()
|
||||
?.firstOrNull { it.network == nftAsset.network }
|
||||
?: return@launch
|
||||
|
||||
getCurrenciesStatusUpdates(
|
||||
isSingleWalletWithToken = wallet.scanResponse.cardTypesResolver.isSingleWalletWithToken(),
|
||||
)
|
||||
},
|
||||
ifLeft = {
|
||||
// sendConfirmAlertFactory.getGenericErrorState(::onFailedTxEmailClick)
|
||||
return@launch
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCurrenciesStatusUpdates(isSingleWalletWithToken: Boolean) {
|
||||
getCurrencyStatusUpdatesUseCase(
|
||||
userWalletId = userWalletId,
|
||||
currencyId = cryptoCurrency.id,
|
||||
isSingleWalletWithTokens = isSingleWalletWithToken,
|
||||
).onEach { maybeCryptoCurrency ->
|
||||
maybeCryptoCurrency.fold(
|
||||
ifRight = { cryptoStatus ->
|
||||
cryptoCurrencyStatus = cryptoStatus
|
||||
feeCryptoCurrencyStatus = getFeePaidCryptoCurrencyStatusSyncUseCase(
|
||||
userWalletId = userWalletId,
|
||||
cryptoCurrencyStatus = cryptoStatus,
|
||||
).getOrNull() ?: cryptoStatus
|
||||
|
||||
router.replaceAll(CommonSendRoute.Destination(isEditMode = false))
|
||||
},
|
||||
ifLeft = {
|
||||
// sendConfirmAlertFactory.getGenericErrorState {
|
||||
// onFailedTxEmailClick(it.toString())
|
||||
// }
|
||||
},
|
||||
)
|
||||
}.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun initialState(): NFTSendUM = NFTSendUM(
|
||||
destinationUM = DestinationUM.Empty(),
|
||||
feeUM = FeeUM.Empty(),
|
||||
confirmUM = ConfirmUM.Empty,
|
||||
navigationUM = NavigationUM.Empty,
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
package com.tangem.features.send.v2.sendnft.ui.state
|
||||
|
||||
import com.tangem.features.send.v2.common.ui.state.NavigationUM
|
||||
import com.tangem.features.send.v2.common.ui.state.ConfirmUM
|
||||
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
|
||||
internal data class NFTSendUM(
|
||||
val destinationUM: DestinationUM,
|
||||
val feeUM: FeeUM,
|
||||
val confirmUM: ConfirmUM,
|
||||
val navigationUM: NavigationUM,
|
||||
)
|
||||
|
|
@ -9,6 +9,7 @@ import com.tangem.common.ui.amountScreen.ui.AmountBlock
|
|||
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.common.PredefinedValues
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponentParams.AmountBlockParams
|
||||
import com.tangem.features.send.v2.subcomponents.amount.model.SendAmountModel
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
|
|
@ -39,7 +40,7 @@ internal class SendAmountBlockComponent(
|
|||
AmountBlock(
|
||||
amountState = state,
|
||||
isClickDisabled = !isClickEnabled,
|
||||
isEditingDisabled = params.isPredefinedValues,
|
||||
isEditingDisabled = params.predefinedValues is PredefinedValues.Content.Deeplink,
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ import com.tangem.common.ui.amountScreen.models.AmountState
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.features.send.v2.send.SendRoute
|
||||
import com.tangem.features.send.v2.common.CommonSendRoute
|
||||
import com.tangem.features.send.v2.common.PredefinedValues
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponent.ModelCallback
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -16,7 +17,7 @@ internal sealed class SendAmountComponentParams {
|
|||
abstract val userWallet: UserWallet
|
||||
abstract val appCurrency: AppCurrency
|
||||
abstract val cryptoCurrencyStatus: CryptoCurrencyStatus
|
||||
abstract val predefinedAmountValue: String?
|
||||
abstract val predefinedValues: PredefinedValues
|
||||
|
||||
data class AmountParams(
|
||||
override val state: AmountState,
|
||||
|
|
@ -24,11 +25,12 @@ internal sealed class SendAmountComponentParams {
|
|||
override val userWallet: UserWallet,
|
||||
override val appCurrency: AppCurrency,
|
||||
override val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
override val predefinedAmountValue: String?,
|
||||
val isEditMode: Boolean,
|
||||
override val predefinedValues: PredefinedValues,
|
||||
val callback: ModelCallback,
|
||||
val currentRoute: Flow<SendRoute.Amount>,
|
||||
val currentRoute: Flow<CommonSendRoute.Amount>,
|
||||
val isBalanceHidingFlow: StateFlow<Boolean>,
|
||||
val onBackClick: () -> Unit,
|
||||
val onNextClick: () -> Unit,
|
||||
) : SendAmountComponentParams()
|
||||
|
||||
data class AmountBlockParams(
|
||||
|
|
@ -37,8 +39,7 @@ internal sealed class SendAmountComponentParams {
|
|||
override val userWallet: UserWallet,
|
||||
override val appCurrency: AppCurrency,
|
||||
override val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
override val predefinedAmountValue: String?,
|
||||
override val predefinedValues: PredefinedValues,
|
||||
val blockClickEnableFlow: StateFlow<Boolean>,
|
||||
val isPredefinedValues: Boolean,
|
||||
) : SendAmountComponentParams()
|
||||
}
|
||||
|
|
@ -13,17 +13,14 @@ 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.decompose.navigation.Router
|
||||
import com.tangem.core.ui.components.currency.icon.converter.CryptoCurrencyToIconStateConverter
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.domain.tokens.GetMinimumTransactionAmountSyncUseCase
|
||||
import com.tangem.features.send.v2.common.NavigationUM
|
||||
import com.tangem.features.send.v2.common.PredefinedValues
|
||||
import com.tangem.features.send.v2.common.ui.state.NavigationUM
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.send.SendRoute
|
||||
import com.tangem.features.send.v2.send.analytics.SendAnalyticEvents
|
||||
import com.tangem.features.send.v2.send.analytics.SendAnalyticEvents.SendScreenSource
|
||||
import com.tangem.features.send.v2.send.ui.state.ButtonsUM
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountComponentParams
|
||||
import com.tangem.features.send.v2.subcomponents.amount.SendAmountReduceListener
|
||||
|
|
@ -45,8 +42,6 @@ import javax.inject.Inject
|
|||
internal class SendAmountModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val router: Router,
|
||||
private val appRouter: AppRouter,
|
||||
private val getMinimumTransactionAmountSyncUseCase: GetMinimumTransactionAmountSyncUseCase,
|
||||
private val sendAmountReduceListener: SendAmountReduceListener,
|
||||
private val feeReloadTrigger: SendFeeReloadTrigger,
|
||||
|
|
@ -105,7 +100,10 @@ internal class SendAmountModel @Inject constructor(
|
|||
),
|
||||
)
|
||||
}
|
||||
params.predefinedAmountValue?.let(::onAmountValueChange)
|
||||
val predefinedValues = params.predefinedValues as? PredefinedValues.Content
|
||||
if (predefinedValues?.amount != null) {
|
||||
onAmountValueChange(predefinedValues.amount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -164,11 +162,6 @@ internal class SendAmountModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
saveResult()
|
||||
if ((params as? SendAmountComponentParams.AmountParams)?.isEditMode == true) {
|
||||
router.pop()
|
||||
} else {
|
||||
router.push(SendRoute.Confirm)
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribeOnAmountReduceToTriggerUpdates() {
|
||||
|
|
@ -236,20 +229,7 @@ internal class SendAmountModel @Inject constructor(
|
|||
} else {
|
||||
R.drawable.ic_close_24
|
||||
},
|
||||
backIconClick = {
|
||||
if (route.isEditMode) {
|
||||
router.pop()
|
||||
} else {
|
||||
analyticsEventHandler.send(
|
||||
SendAnalyticEvents.CloseButtonClicked(
|
||||
source = SendScreenSource.Amount,
|
||||
isFromSummary = false,
|
||||
isValid = state.isPrimaryButtonEnabled,
|
||||
),
|
||||
)
|
||||
appRouter.pop()
|
||||
}
|
||||
},
|
||||
backIconClick = { params.onBackClick() },
|
||||
primaryButton = ButtonsUM.PrimaryButtonUM(
|
||||
text = if (route.isEditMode) {
|
||||
resourceReference(R.string.common_continue)
|
||||
|
|
@ -257,7 +237,10 @@ internal class SendAmountModel @Inject constructor(
|
|||
resourceReference(R.string.common_next)
|
||||
},
|
||||
isEnabled = state.isPrimaryButtonEnabled,
|
||||
onClick = ::onAmountNext,
|
||||
onClick = {
|
||||
onAmountNext()
|
||||
params.onNextClick()
|
||||
},
|
||||
),
|
||||
prevButton = ButtonsUM.PrimaryButtonUM(
|
||||
text = TextReference.EMPTY,
|
||||
|
|
@ -265,7 +248,7 @@ internal class SendAmountModel @Inject constructor(
|
|||
isEnabled = true,
|
||||
onClick = {
|
||||
saveResult()
|
||||
router.pop()
|
||||
params.onBackClick()
|
||||
},
|
||||
).takeIf { route.isEditMode.not() },
|
||||
secondaryPairButtonsUM = null,
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
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.common.PredefinedValues
|
||||
import com.tangem.features.send.v2.subcomponents.destination.model.SendDestinationModel
|
||||
import com.tangem.features.send.v2.subcomponents.destination.ui.DestinationBlock
|
||||
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationUM
|
||||
|
|
@ -38,7 +39,7 @@ internal class SendDestinationBlockComponent(
|
|||
DestinationBlock(
|
||||
destinationUM = state,
|
||||
isClickDisabled = !isClickEnabled,
|
||||
isEditingDisabled = params.isPredefinedValues,
|
||||
isEditingDisabled = params.predefinedValues is PredefinedValues.Content.Deeplink,
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ package com.tangem.features.send.v2.subcomponents.destination
|
|||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.features.send.v2.send.SendRoute
|
||||
import com.tangem.features.send.v2.common.CommonSendRoute
|
||||
import com.tangem.features.send.v2.common.PredefinedValues
|
||||
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationComponent.ModelCallback
|
||||
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationUM
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -21,9 +22,10 @@ internal sealed class SendDestinationComponentParams {
|
|||
override val cryptoCurrency: CryptoCurrency,
|
||||
override val userWalletId: UserWalletId,
|
||||
val isBalanceHidingFlow: StateFlow<Boolean>,
|
||||
val currentRoute: Flow<SendRoute.Destination>,
|
||||
val currentRoute: Flow<CommonSendRoute.Destination>,
|
||||
val callback: ModelCallback,
|
||||
val isEditMode: Boolean,
|
||||
val onBackClick: () -> Unit,
|
||||
val onNextClick: () -> Unit,
|
||||
) : SendDestinationComponentParams()
|
||||
|
||||
data class DestinationBlockParams(
|
||||
|
|
@ -32,8 +34,6 @@ internal sealed class SendDestinationComponentParams {
|
|||
override val userWalletId: UserWalletId,
|
||||
override val cryptoCurrency: CryptoCurrency,
|
||||
val blockClickEnableFlow: StateFlow<Boolean>,
|
||||
val predefinedAddressValue: String?,
|
||||
val predefinedMemoValue: String?,
|
||||
val isPredefinedValues: Boolean,
|
||||
val predefinedValues: PredefinedValues,
|
||||
) : SendDestinationComponentParams()
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@ package com.tangem.features.send.v2.subcomponents.destination.model
|
|||
import androidx.compose.runtime.Stable
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.common.routing.AppRoute
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
|
|
@ -22,13 +21,14 @@ import com.tangem.domain.transaction.usecase.ValidateWalletMemoUseCase
|
|||
import com.tangem.domain.txhistory.usecase.GetFixedTxHistoryItemsUseCase
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.features.send.v2.common.NavigationUM
|
||||
import com.tangem.features.send.v2.common.PredefinedValues
|
||||
import com.tangem.features.send.v2.common.ui.state.NavigationUM
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.send.SendRoute
|
||||
import com.tangem.features.send.v2.send.analytics.SendAnalyticEvents
|
||||
import com.tangem.features.send.v2.send.analytics.SendAnalyticEvents.SendScreenSource
|
||||
import com.tangem.features.send.v2.send.ui.state.ButtonsUM
|
||||
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationComponentParams
|
||||
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationComponentParams.DestinationBlockParams
|
||||
import com.tangem.features.send.v2.subcomponents.destination.analytics.EnterAddressSource
|
||||
import com.tangem.features.send.v2.subcomponents.destination.analytics.SendDestinationAnalyticEvents
|
||||
import com.tangem.features.send.v2.subcomponents.destination.model.transformers.*
|
||||
|
|
@ -53,7 +53,6 @@ internal class SendDestinationModel @Inject constructor(
|
|||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val router: Router,
|
||||
private val appRouter: AppRouter,
|
||||
private val validateWalletAddressUseCase: ValidateWalletAddressUseCase,
|
||||
private val validateWalletMemoUseCase: ValidateWalletMemoUseCase,
|
||||
private val getWalletsUseCase: GetWalletsUseCase,
|
||||
|
|
@ -85,19 +84,20 @@ internal class SendDestinationModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun initialState() {
|
||||
if ((uiState.value as? DestinationUM.Content)?.isInitialized == false) {
|
||||
if ((uiState.value as? DestinationUM.Content)?.isInitialized == false || uiState.value is DestinationUM.Empty) {
|
||||
_uiState.update(
|
||||
SendDestinationInitialStateTransformer(
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
isInitialized = true,
|
||||
),
|
||||
)
|
||||
val params = params as? SendDestinationComponentParams.DestinationBlockParams
|
||||
if (params?.predefinedAddressValue != null) {
|
||||
val params = params as? DestinationBlockParams
|
||||
val predefinedValues = params?.predefinedValues as? PredefinedValues.Content.Deeplink
|
||||
if (predefinedValues?.address != null) {
|
||||
_uiState.update(
|
||||
SendDestinationPredefinedStateTransformer(
|
||||
address = params.predefinedAddressValue,
|
||||
memo = params.predefinedMemoValue,
|
||||
address = predefinedValues.address,
|
||||
memo = predefinedValues.memo,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -269,7 +269,8 @@ internal class SendDestinationModel @Inject constructor(
|
|||
|
||||
private fun autoNextFromRecipient(type: EnterAddressSource?, isValidAddress: Boolean, isValidMemo: Boolean) {
|
||||
val isRecent = type == EnterAddressSource.RecentAddress
|
||||
if (isRecent && isValidAddress && isValidMemo) onNextClick()
|
||||
if (isRecent && isValidAddress && isValidMemo) saveResult()
|
||||
(params as? SendDestinationComponentParams.DestinationParams)?.onNextClick?.invoke()
|
||||
}
|
||||
|
||||
private fun saveResult() {
|
||||
|
|
@ -277,15 +278,6 @@ internal class SendDestinationModel @Inject constructor(
|
|||
params.callback.onDestinationResult(uiState.value)
|
||||
}
|
||||
|
||||
private fun onNextClick() {
|
||||
saveResult()
|
||||
if ((params as? SendDestinationComponentParams.DestinationParams)?.isEditMode == true) {
|
||||
router.pop()
|
||||
} else {
|
||||
router.push(SendRoute.Amount(isEditMode = false))
|
||||
}
|
||||
}
|
||||
|
||||
private fun configDestinationNavigation() {
|
||||
val params = params as? SendDestinationComponentParams.DestinationParams ?: return
|
||||
combine(
|
||||
|
|
@ -303,9 +295,7 @@ internal class SendDestinationModel @Inject constructor(
|
|||
R.drawable.ic_close_24
|
||||
},
|
||||
backIconClick = {
|
||||
if (route.isEditMode) {
|
||||
router.pop()
|
||||
} else {
|
||||
if (!route.isEditMode) {
|
||||
analyticsEventHandler.send(
|
||||
SendAnalyticEvents.CloseButtonClicked(
|
||||
source = SendScreenSource.Address,
|
||||
|
|
@ -313,8 +303,8 @@ internal class SendDestinationModel @Inject constructor(
|
|||
isValid = state.isPrimaryButtonEnabled,
|
||||
),
|
||||
)
|
||||
appRouter.pop()
|
||||
}
|
||||
params.onBackClick()
|
||||
},
|
||||
additionalIconRes = R.drawable.ic_qrcode_scan_24,
|
||||
additionalIconClick = ::onQrCodeScanClick,
|
||||
|
|
@ -325,7 +315,10 @@ internal class SendDestinationModel @Inject constructor(
|
|||
resourceReference(R.string.common_next)
|
||||
},
|
||||
isEnabled = state.isPrimaryButtonEnabled,
|
||||
onClick = ::onNextClick,
|
||||
onClick = {
|
||||
saveResult()
|
||||
params.onNextClick()
|
||||
},
|
||||
),
|
||||
prevButton = null,
|
||||
secondaryPairButtonsUM = null,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
|||
|
||||
internal class SendFeeComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
private val params: SendFeeComponentParams.FeeParams,
|
||||
params: SendFeeComponentParams.FeeParams,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: SendFeeModel = getOrCreateModel(params = params)
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package com.tangem.features.send.v2.subcomponents.fee
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.features.send.v2.send.SendRoute
|
||||
import com.tangem.features.send.v2.common.CommonSendRoute
|
||||
import com.tangem.features.send.v2.subcomponents.fee.ui.state.FeeUM
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
|
@ -29,8 +29,9 @@ internal sealed class SendFeeComponentParams {
|
|||
override val appCurrency: AppCurrency,
|
||||
override val sendAmount: BigDecimal,
|
||||
override val destinationAddress: String,
|
||||
val currentRoute: Flow<SendRoute.Fee>,
|
||||
val currentRoute: Flow<CommonSendRoute.Fee>,
|
||||
val callback: SendFeeComponent.ModelCallback,
|
||||
val onNextClick: () -> Unit,
|
||||
) : SendFeeComponentParams()
|
||||
|
||||
data class FeeBlockParams(
|
||||
|
|
|
|||
|
|
@ -6,12 +6,11 @@ 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.decompose.navigation.Router
|
||||
import com.tangem.core.navigation.url.UrlOpener
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.transaction.usecase.GetFeeUseCase
|
||||
import com.tangem.domain.transaction.usecase.IsFeeApproximateUseCase
|
||||
import com.tangem.features.send.v2.common.NavigationUM
|
||||
import com.tangem.features.send.v2.common.ui.state.NavigationUM
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.send.ui.state.ButtonsUM
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeCheckReloadListener
|
||||
|
|
@ -40,7 +39,6 @@ import javax.inject.Inject
|
|||
internal class SendFeeModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
private val router: Router,
|
||||
private val isFeeApproximateUseCase: IsFeeApproximateUseCase,
|
||||
private val getFeeUseCase: GetFeeUseCase,
|
||||
private val urlOpener: UrlOpener,
|
||||
|
|
@ -140,10 +138,10 @@ internal class SendFeeModel @Inject constructor(
|
|||
),
|
||||
)
|
||||
|
||||
navigate()
|
||||
saveResult()
|
||||
}
|
||||
} else {
|
||||
navigate()
|
||||
saveResult()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -283,11 +281,6 @@ internal class SendFeeModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun navigate() {
|
||||
saveResult()
|
||||
router.pop()
|
||||
}
|
||||
|
||||
private fun configFeeNavigation() {
|
||||
val params = params as? SendFeeComponentParams.FeeParams ?: return
|
||||
combine(
|
||||
|
|
@ -300,11 +293,14 @@ internal class SendFeeModel @Inject constructor(
|
|||
title = resourceReference(R.string.common_fee_selector_title),
|
||||
subtitle = null,
|
||||
backIconRes = R.drawable.ic_back_24,
|
||||
backIconClick = router::pop,
|
||||
backIconClick = params.onNextClick,
|
||||
primaryButton = ButtonsUM.PrimaryButtonUM(
|
||||
text = resourceReference(R.string.common_continue),
|
||||
isEnabled = state.isPrimaryButtonEnabled,
|
||||
onClick = ::onNextClick,
|
||||
onClick = {
|
||||
onNextClick()
|
||||
params.onNextClick()
|
||||
},
|
||||
),
|
||||
prevButton = null,
|
||||
secondaryPairButtonsUM = null,
|
||||
|
|
|
|||
|
|
@ -2,19 +2,17 @@ package com.tangem.features.send.v2.subcomponents.notifications
|
|||
|
||||
import androidx.compose.foundation.lazy.LazyListScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import com.tangem.blockchain.common.transaction.Fee
|
||||
import com.tangem.common.ui.notifications.NotificationUM
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.model.getOrCreateModel
|
||||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.transaction.error.GetFeeError
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.features.send.v2.subcomponents.notifications
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.model.NotificationData
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.model.NotificationsModel
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import java.math.BigDecimal
|
||||
|
||||
internal class NotificationsComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
|
|
@ -45,12 +43,6 @@ internal class NotificationsComponent(
|
|||
val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
val feeCryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
val appCurrency: AppCurrency,
|
||||
val destinationAddress: String,
|
||||
val memo: String?,
|
||||
val amountValue: BigDecimal,
|
||||
val reduceAmountBy: BigDecimal,
|
||||
val isIgnoreReduce: Boolean,
|
||||
val fee: Fee?,
|
||||
val feeError: GetFeeError?,
|
||||
val notificationData: NotificationData,
|
||||
)
|
||||
}
|
||||
|
|
@ -79,13 +79,7 @@ internal class NotificationsModel @Inject constructor(
|
|||
private val currency = cryptoCurrencyStatus.currency
|
||||
private val appCurrency = params.appCurrency
|
||||
|
||||
private var destinationAddress = params.destinationAddress
|
||||
private var memo = params.memo
|
||||
private var amountValue = params.amountValue
|
||||
private var reduceAmountBy = params.reduceAmountBy
|
||||
private var isIgnoreReduce = params.isIgnoreReduce
|
||||
private var fee = params.fee
|
||||
private var feeError = params.feeError
|
||||
private var notificationData = params.notificationData
|
||||
|
||||
private val _uiState = MutableStateFlow<ImmutableList<NotificationUM>>(persistentListOf())
|
||||
val uiState = _uiState.asStateFlow()
|
||||
|
|
@ -111,14 +105,7 @@ internal class NotificationsModel @Inject constructor(
|
|||
}
|
||||
|
||||
private suspend fun updateState(data: NotificationData) {
|
||||
destinationAddress = data.destinationAddress
|
||||
memo = data.memo
|
||||
amountValue = data.amountValue
|
||||
reduceAmountBy = data.reduceAmountBy
|
||||
isIgnoreReduce = data.isIgnoreReduce
|
||||
fee = data.fee
|
||||
feeError = data.feeError
|
||||
|
||||
notificationData = data
|
||||
buildNotifications()
|
||||
}
|
||||
|
||||
|
|
@ -127,26 +114,20 @@ internal class NotificationsModel @Inject constructor(
|
|||
addFeeUnreachableNotification(
|
||||
tokenStatus = cryptoCurrencyStatus,
|
||||
coinStatus = feeCryptoCurrencyStatus,
|
||||
feeError = feeError,
|
||||
feeError = notificationData.feeError,
|
||||
onReload = {
|
||||
modelScope.launch {
|
||||
sendFeeReloadTrigger.triggerUpdate(
|
||||
feeData = SendFeeData(
|
||||
amount = amountValue,
|
||||
destinationAddress = destinationAddress,
|
||||
amount = notificationData.amountValue,
|
||||
destinationAddress = notificationData.destinationAddress,
|
||||
),
|
||||
)
|
||||
}
|
||||
},
|
||||
onClick = ::showTokenDetails,
|
||||
)
|
||||
addDomainNotifications(
|
||||
destinationAddress = destinationAddress,
|
||||
memo = memo,
|
||||
amountValue = amountValue,
|
||||
reduceAmountBy = reduceAmountBy,
|
||||
fee = fee,
|
||||
)
|
||||
addDomainNotifications()
|
||||
}
|
||||
|
||||
notificationsUpdateTrigger.callbackHasError(notifications.any { it is NotificationUM.Error })
|
||||
|
|
@ -167,13 +148,7 @@ internal class NotificationsModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun MutableList<NotificationUM>.addDomainNotifications(
|
||||
destinationAddress: String,
|
||||
memo: String?,
|
||||
amountValue: BigDecimal,
|
||||
reduceAmountBy: BigDecimal,
|
||||
fee: Fee?,
|
||||
) {
|
||||
private suspend fun MutableList<NotificationUM>.addDomainNotifications() = with(notificationData) {
|
||||
val balance = cryptoCurrencyStatus.value.amount ?: return
|
||||
val feeValue = fee?.amount?.value ?: return
|
||||
val isFeeCoverage = checkFeeCoverage(
|
||||
|
|
@ -311,7 +286,7 @@ internal class NotificationsModel @Inject constructor(
|
|||
) {
|
||||
val validationError = validateTransactionUseCase(
|
||||
userWalletId = userWalletId,
|
||||
amount = amountValue.convertToSdkAmount(cryptoCurrencyStatus.currency),
|
||||
amount = enteredAmount.convertToSdkAmount(currency),
|
||||
fee = fee,
|
||||
memo = memo,
|
||||
destination = destinationAddress,
|
||||
|
|
@ -359,7 +334,7 @@ internal class NotificationsModel @Inject constructor(
|
|||
addHighFeeWarningNotification(
|
||||
enteredAmountValue = enteredAmount,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
ignoreAmountReduce = isIgnoreReduce,
|
||||
ignoreAmountReduce = notificationData.isIgnoreReduce,
|
||||
onReduceClick = { reduceBy, reduceByDiff, _ ->
|
||||
modelScope.launch {
|
||||
sendAmountReduceTrigger.triggerReduceBy(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue