Updated on 2026-08-14
This commit is contained in:
parent
95f764c878
commit
9e98ebe77b
10 changed files with 370 additions and 73 deletions
|
|
@ -39,8 +39,12 @@ internal class DefaultSendComponent @AssistedInject constructor(
|
|||
popCallback = { onChildBack() },
|
||||
)
|
||||
|
||||
private val initialRoute = SendRoute.Empty
|
||||
private val currentRoute = MutableStateFlow<SendRoute>(initialRoute)
|
||||
private val initialRoute = if (params.amount == null) {
|
||||
SendRoute.Destination(isEditMode = false)
|
||||
} else {
|
||||
SendRoute.Empty
|
||||
}
|
||||
private val currentRoute = MutableStateFlow(initialRoute)
|
||||
|
||||
private val model: SendModel = getOrCreateModel(params = params, router = innerRouter)
|
||||
|
||||
|
|
@ -77,12 +81,12 @@ internal class DefaultSendComponent @AssistedInject constructor(
|
|||
private fun getDestinationComponent(factoryContext: AppComponentContext, route: SendRoute) =
|
||||
SendDestinationComponent(
|
||||
appComponentContext = factoryContext,
|
||||
params = SendDestinationComponent.Params(
|
||||
params = SendDestinationComponentParams.DestinationParams(
|
||||
state = model.uiState.value.destinationUM,
|
||||
currentRoute = currentRoute.filterIsInstance<SendRoute.Destination>(),
|
||||
analyticsCategoryName = SendAnalyticEvents.SEND_CATEGORY,
|
||||
userWallet = model.userWallet,
|
||||
cryptoCurrencyStatus = model.cryptoCurrencyStatus,
|
||||
cryptoCurrency = params.currency,
|
||||
callback = model,
|
||||
isEditMode = route.isEditMode,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ 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.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.fee.SendFeeBlockComponent
|
||||
import com.tangem.features.send.v2.subcomponents.fee.SendFeeComponentParams
|
||||
import com.tangem.features.send.v2.subcomponents.notifications.NotificationsComponent
|
||||
|
|
@ -33,22 +35,22 @@ internal class SendConfirmComponent(
|
|||
|
||||
private val blockClickEnableFlow = MutableStateFlow(false)
|
||||
|
||||
// private val destinationBlockComponent =
|
||||
// SendDestinationBlockComponent(
|
||||
// appComponentContext = child("sendConfirmDestinationBlock"),
|
||||
// params = SendDestinationComponentParams.DestinationBlockParams(
|
||||
// state = model.uiState.value.destinationUM,
|
||||
// analyticsCategoryName = params.analyticsCategoryName,
|
||||
// userWallet = params.userWallet,
|
||||
// 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,
|
||||
// ),
|
||||
// onResult = model::onDestinationResult,
|
||||
// onClick = model::showEditDestination,
|
||||
// )
|
||||
private val destinationBlockComponent =
|
||||
SendDestinationBlockComponent(
|
||||
appComponentContext = child("sendConfirmDestinationBlock"),
|
||||
params = SendDestinationComponentParams.DestinationBlockParams(
|
||||
state = model.uiState.value.destinationUM,
|
||||
analyticsCategoryName = params.analyticsCategoryName,
|
||||
userWallet = params.userWallet,
|
||||
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,
|
||||
),
|
||||
onResult = model::onDestinationResult,
|
||||
onClick = model::showEditDestination,
|
||||
)
|
||||
|
||||
private val amountBlockComponent = SendAmountBlockComponent(
|
||||
appComponentContext = child("sendConfirmAmountBlock"),
|
||||
|
|
@ -59,9 +61,8 @@ internal class SendConfirmComponent(
|
|||
cryptoCurrencyStatus = params.cryptoCurrencyStatus,
|
||||
appCurrency = params.appCurrency,
|
||||
blockClickEnableFlow = blockClickEnableFlow.asStateFlow(),
|
||||
blockEditDisabledFlow = TODO(),
|
||||
// isPredefinedValues = params.predefinedValues is Params.PredefinedValues.Content,
|
||||
// predefinedAmountValue = (params.predefinedValues as? Params.PredefinedValues.Content)?.amount,
|
||||
isPredefinedValues = params.predefinedValues is Params.PredefinedValues.Content,
|
||||
predefinedAmountValue = (params.predefinedValues as? Params.PredefinedValues.Content)?.amount,
|
||||
),
|
||||
onResult = model::onAmountResult,
|
||||
onClick = model::showEditAmount,
|
||||
|
|
@ -109,8 +110,7 @@ internal class SendConfirmComponent(
|
|||
}
|
||||
|
||||
fun updateState(state: SendUM) {
|
||||
// todo
|
||||
// destinationBlockComponent.updateState(state.destinationUM)
|
||||
destinationBlockComponent.updateState(state.destinationUM)
|
||||
amountBlockComponent.updateState(state.amountUM)
|
||||
feeBlockComponent.updateState(state.feeUM)
|
||||
model.updateState(state)
|
||||
|
|
@ -123,7 +123,7 @@ internal class SendConfirmComponent(
|
|||
|
||||
SendConfirmContent(
|
||||
sendUM = state,
|
||||
// destinationBlockComponent = destinationBlockComponent,
|
||||
destinationBlockComponent = destinationBlockComponent,
|
||||
amountBlockComponent = amountBlockComponent,
|
||||
feeBlockComponent = feeBlockComponent,
|
||||
notificationsComponent = notificationsComponent,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ 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
|
||||
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
|
||||
|
|
@ -50,7 +51,7 @@ private const val TAP_HELP_ANIMATION_DELAY = 500L
|
|||
@Composable
|
||||
internal fun SendConfirmContent(
|
||||
sendUM: SendUM,
|
||||
// destinationBlockComponent: SendDestinationBlockComponent,
|
||||
destinationBlockComponent: SendDestinationBlockComponent,
|
||||
amountBlockComponent: SendAmountBlockComponent,
|
||||
feeBlockComponent: SendFeeBlockComponent,
|
||||
notificationsComponent: NotificationsComponent,
|
||||
|
|
@ -64,7 +65,7 @@ internal fun SendConfirmContent(
|
|||
) {
|
||||
blocks(
|
||||
uiState = sendUM,
|
||||
// destinationBlockComponent = destinationBlockComponent,
|
||||
destinationBlockComponent = destinationBlockComponent,
|
||||
amountBlockComponent = amountBlockComponent,
|
||||
feeBlockComponent = feeBlockComponent,
|
||||
)
|
||||
|
|
@ -121,7 +122,7 @@ private fun SendingText(confirmUM: ConfirmUM.Content?, modifier: Modifier = Modi
|
|||
|
||||
private fun LazyListScope.blocks(
|
||||
uiState: SendUM,
|
||||
// destinationBlockComponent: SendDestinationBlockComponent,
|
||||
destinationBlockComponent: SendDestinationBlockComponent,
|
||||
amountBlockComponent: SendAmountBlockComponent,
|
||||
feeBlockComponent: SendFeeBlockComponent,
|
||||
) {
|
||||
|
|
@ -144,8 +145,7 @@ private fun LazyListScope.blocks(
|
|||
modifier = Modifier.padding(vertical = 12.dp),
|
||||
)
|
||||
}
|
||||
// todo
|
||||
// destinationBlockComponent.Content(modifier = Modifier)
|
||||
destinationBlockComponent.Content(modifier = Modifier)
|
||||
amountBlockComponent.Content(modifier = Modifier)
|
||||
feeBlockComponent.Content(modifier = Modifier)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
package com.tangem.features.send.v2.subcomponents.destination
|
||||
|
||||
import com.tangem.core.decompose.di.ModelScoped
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.core.ui.message.DialogMessage
|
||||
import com.tangem.core.ui.message.EventMessageAction
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import javax.inject.Inject
|
||||
|
||||
@ModelScoped
|
||||
internal class SendDestinationAlertFactory @Inject constructor(
|
||||
val messageSender: UiMessageSender,
|
||||
) {
|
||||
|
||||
fun getGenericErrorState(onFailedTxEmailClick: () -> Unit) {
|
||||
messageSender.send(
|
||||
DialogMessage(
|
||||
title = resourceReference(id = R.string.send_alert_transaction_failed_title),
|
||||
message = resourceReference(id = R.string.common_unknown_error),
|
||||
firstAction = EventMessageAction(
|
||||
title = resourceReference(R.string.common_support),
|
||||
onClick = onFailedTxEmailClick,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.tangem.features.send.v2.subcomponents.destination
|
||||
|
||||
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.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
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
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
||||
internal class SendDestinationBlockComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
private val params: SendDestinationComponentParams.DestinationBlockParams,
|
||||
val onClick: () -> Unit,
|
||||
val onResult: (DestinationUM) -> Unit,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: SendDestinationModel = getOrCreateModel(params = params)
|
||||
|
||||
init {
|
||||
model.uiState.onEach {
|
||||
onResult(it)
|
||||
}.launchIn(componentScope)
|
||||
}
|
||||
|
||||
fun updateState(destinationUM: DestinationUM) = model.updateState(destinationUM)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
val isClickEnabled by params.blockClickEnableFlow.collectAsStateWithLifecycle()
|
||||
|
||||
DestinationBlock(
|
||||
destinationUM = state,
|
||||
isClickDisabled = !isClickEnabled,
|
||||
isEditingDisabled = params.isPredefinedValues,
|
||||
onClick = onClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +1,34 @@
|
|||
package com.tangem.features.send.v2.subcomponents.destination
|
||||
|
||||
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.model.getOrCreateModel
|
||||
import com.tangem.core.ui.decompose.ComposableContentComponent
|
||||
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.SendNavigationModelCallback
|
||||
import com.tangem.features.send.v2.subcomponents.destination.model.SendDestinationModel
|
||||
import com.tangem.features.send.v2.subcomponents.destination.ui.SendDestinationContent
|
||||
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationUM
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
internal class SendDestinationComponent(
|
||||
appComponentContext: AppComponentContext,
|
||||
private val params: Params,
|
||||
private val params: SendDestinationComponentParams.DestinationParams,
|
||||
) : ComposableContentComponent, AppComponentContext by appComponentContext {
|
||||
|
||||
private val model: SendDestinationModel = getOrCreateModel(params = params, router = router)
|
||||
private val model: SendDestinationModel = getOrCreateModel(params = params)
|
||||
|
||||
fun updateState(state: DestinationUM) = model.updateState(state)
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state = model.uiState.collectAsStateWithLifecycle()
|
||||
val state by model.uiState.collectAsStateWithLifecycle()
|
||||
val isBalanceHidden by params.isBalanceHidingFlow.collectAsStateWithLifecycle()
|
||||
|
||||
SendDestinationContent(state = state.value, model, isBalanceHidden = false)
|
||||
SendDestinationContent(state = state, clickIntents = model, isBalanceHidden = isBalanceHidden)
|
||||
}
|
||||
|
||||
data class Params(
|
||||
val state: DestinationUM,
|
||||
val analyticsCategoryName: String,
|
||||
val userWallet: UserWallet,
|
||||
val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
val callback: ModelCallback,
|
||||
val currentRoute: Flow<SendRoute.Destination>,
|
||||
val isEditMode: Boolean,
|
||||
)
|
||||
|
||||
interface ModelCallback : SendNavigationModelCallback {
|
||||
fun onDestinationResult(destinationUM: DestinationUM)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,39 @@
|
|||
package com.tangem.features.send.v2.subcomponents.destination
|
||||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.features.send.v2.send.SendRoute
|
||||
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
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
internal sealed class SendDestinationComponentParams {
|
||||
|
||||
abstract val state: DestinationUM
|
||||
abstract val analyticsCategoryName: String
|
||||
abstract val userWallet: UserWallet
|
||||
abstract val cryptoCurrency: CryptoCurrency
|
||||
|
||||
data class DestinationParams(
|
||||
override val state: DestinationUM,
|
||||
override val analyticsCategoryName: String,
|
||||
override val userWallet: UserWallet,
|
||||
override val cryptoCurrency: CryptoCurrency,
|
||||
val isBalanceHidingFlow: StateFlow<Boolean>,
|
||||
val currentRoute: Flow<SendRoute.Destination>,
|
||||
val callback: ModelCallback,
|
||||
val isEditMode: Boolean,
|
||||
) : SendDestinationComponentParams()
|
||||
|
||||
data class DestinationBlockParams(
|
||||
override val state: DestinationUM,
|
||||
override val analyticsCategoryName: String,
|
||||
override val userWallet: UserWallet,
|
||||
override val cryptoCurrency: CryptoCurrency,
|
||||
val blockClickEnableFlow: StateFlow<Boolean>,
|
||||
val predefinedAddressValue: String?,
|
||||
val predefinedMemoValue: String?,
|
||||
val isPredefinedValues: Boolean,
|
||||
) : SendDestinationComponentParams()
|
||||
}
|
||||
|
|
@ -1,18 +1,32 @@
|
|||
package com.tangem.features.send.v2.subcomponents.destination.model
|
||||
|
||||
import androidx.compose.runtime.Stable
|
||||
import arrow.core.Either
|
||||
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
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.features.send.v2.send.ui.state.ButtonsUM
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.feedback.GetCardInfoUseCase
|
||||
import com.tangem.domain.feedback.SaveBlockchainErrorUseCase
|
||||
import com.tangem.domain.feedback.SendFeedbackEmailUseCase
|
||||
import com.tangem.domain.feedback.models.BlockchainErrorInfo
|
||||
import com.tangem.domain.feedback.models.FeedbackEmailType
|
||||
import com.tangem.domain.qrscanning.models.SourceType
|
||||
import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase
|
||||
import com.tangem.domain.qrscanning.usecases.ParseQrCodeUseCase
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyUseCase
|
||||
import com.tangem.domain.tokens.GetCurrencyStatusUpdatesUseCase
|
||||
import com.tangem.domain.tokens.GetNetworkAddressesUseCase
|
||||
import com.tangem.domain.tokens.GetPrimaryCurrencyStatusUpdatesUseCase
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.transaction.usecase.IsUtxoConsolidationAvailableUseCase
|
||||
import com.tangem.domain.transaction.usecase.ValidateWalletAddressUseCase
|
||||
import com.tangem.domain.transaction.usecase.ValidateWalletMemoUseCase
|
||||
|
|
@ -22,7 +36,10 @@ import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
|||
import com.tangem.features.send.v2.common.NavigationUM
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.send.SendRoute
|
||||
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationComponent
|
||||
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.subcomponents.destination.SendDestinationAlertFactory
|
||||
import com.tangem.features.send.v2.subcomponents.destination.SendDestinationComponentParams
|
||||
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.*
|
||||
|
|
@ -31,6 +48,7 @@ import com.tangem.features.send.v2.subcomponents.destination.ui.state.Destinatio
|
|||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.JobHolder
|
||||
import com.tangem.utils.coroutines.saveIn
|
||||
import com.tangem.utils.coroutines.waitForDelay
|
||||
import com.tangem.utils.transformer.update
|
||||
import kotlinx.coroutines.async
|
||||
import kotlinx.coroutines.awaitAll
|
||||
|
|
@ -38,10 +56,11 @@ import kotlinx.coroutines.coroutineScope
|
|||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
@Stable
|
||||
@ModelScoped
|
||||
@Suppress("LongParameterList")
|
||||
@Suppress("LongParameterList", "LargeClass")
|
||||
internal class SendDestinationModel @Inject constructor(
|
||||
paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
|
|
@ -55,43 +74,57 @@ internal class SendDestinationModel @Inject constructor(
|
|||
private val getFixedTxHistoryItemsUseCase: GetFixedTxHistoryItemsUseCase,
|
||||
private val isUtxoConsolidationAvailableUseCase: IsUtxoConsolidationAvailableUseCase,
|
||||
private val listenToQrScanningUseCase: ListenToQrScanningUseCase,
|
||||
private val getCurrencyStatusUpdatesUseCase: GetCurrencyStatusUpdatesUseCase,
|
||||
private val getPrimaryCurrencyStatusUpdatesUseCase: GetPrimaryCurrencyStatusUpdatesUseCase,
|
||||
private val saveBlockchainErrorUseCase: SaveBlockchainErrorUseCase,
|
||||
private val sendFeedbackEmailUseCase: SendFeedbackEmailUseCase,
|
||||
private val getCardInfoUseCase: GetCardInfoUseCase,
|
||||
private val parseQrCodeUseCase: ParseQrCodeUseCase,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val sendDestinationAlertFactory: SendDestinationAlertFactory,
|
||||
) : Model(), SendDestinationClickIntents {
|
||||
private val params: SendDestinationComponent.Params = paramsContainer.require()
|
||||
private val params: SendDestinationComponentParams = paramsContainer.require()
|
||||
|
||||
private val _uiState = MutableStateFlow(params.state)
|
||||
val uiState = _uiState.asStateFlow()
|
||||
|
||||
private val analyticsCategoryName = params.analyticsCategoryName
|
||||
private val userWallet = params.userWallet
|
||||
private val cryptoCurrencyStatus = params.cryptoCurrencyStatus
|
||||
private val cryptoCurrency = cryptoCurrencyStatus.currency
|
||||
private val cryptoCurrency = params.cryptoCurrency
|
||||
private var cryptoCurrencyStatus by Delegates.notNull<CryptoCurrencyStatus>()
|
||||
|
||||
private var validationJobHolder = JobHolder()
|
||||
|
||||
init {
|
||||
configDestinationNavigation()
|
||||
initialState()
|
||||
getWalletsAndRecent()
|
||||
subscribeOnCurrencyStatusUpdates()
|
||||
subscribeOnQRScannerResult()
|
||||
}
|
||||
|
||||
private fun initialState() {
|
||||
if (uiState.value is DestinationUM.Empty) {
|
||||
if ((uiState.value as? DestinationUM.Content)?.isInitialized == false) {
|
||||
_uiState.update(
|
||||
SendDestinationInitialStateTransformer(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
onAddressChange = ::onRecipientAddressValueChange,
|
||||
onMemoChange = ::onRecipientMemoValueChange,
|
||||
cryptoCurrency = cryptoCurrency,
|
||||
isInitialized = true,
|
||||
),
|
||||
)
|
||||
val params = params as? SendDestinationComponentParams.DestinationBlockParams
|
||||
if (params?.predefinedAddressValue != null && params.predefinedMemoValue != null) {
|
||||
_uiState.update(
|
||||
SendDestinationPredefinedStateTransformer(
|
||||
address = params.predefinedAddressValue,
|
||||
memo = params.predefinedMemoValue,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun updateState(state: DestinationUM) {
|
||||
if (state !is DestinationUM.Empty) {
|
||||
_uiState.value = state
|
||||
fun updateState(destinationUM: DestinationUM) {
|
||||
if (destinationUM is DestinationUM.Content && destinationUM.isInitialized) {
|
||||
_uiState.value = destinationUM
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -124,6 +157,69 @@ internal class SendDestinationModel @Inject constructor(
|
|||
)
|
||||
}
|
||||
|
||||
private fun subscribeOnCurrencyStatusUpdates() {
|
||||
val isSingleWalletWithToken = userWallet.scanResponse.cardTypesResolver.isSingleWalletWithToken()
|
||||
val isMultiCurrency = userWallet.isMultiCurrency
|
||||
getCurrenciesStatusUpdates(
|
||||
isSingleWalletWithToken = isSingleWalletWithToken,
|
||||
isMultiCurrency = isMultiCurrency,
|
||||
)
|
||||
}
|
||||
|
||||
private fun getCurrenciesStatusUpdates(isSingleWalletWithToken: Boolean, isMultiCurrency: Boolean) {
|
||||
getCurrencyStatus(
|
||||
isSingleWalletWithToken = isSingleWalletWithToken,
|
||||
isMultiCurrency = isMultiCurrency,
|
||||
).onEach { maybeCryptoCurrency ->
|
||||
maybeCryptoCurrency.fold(
|
||||
ifRight = { cryptoStatus ->
|
||||
cryptoCurrencyStatus = cryptoStatus
|
||||
getWalletsAndRecent()
|
||||
},
|
||||
ifLeft = {
|
||||
sendDestinationAlertFactory.getGenericErrorState {
|
||||
onFailedTxEmailClick(it.toString())
|
||||
}
|
||||
},
|
||||
)
|
||||
}.launchIn(modelScope)
|
||||
}
|
||||
|
||||
private fun getCurrencyStatus(
|
||||
isSingleWalletWithToken: Boolean,
|
||||
isMultiCurrency: Boolean,
|
||||
): Flow<Either<CurrencyStatusError, CryptoCurrencyStatus>> {
|
||||
return if (isMultiCurrency) {
|
||||
getCurrencyStatusUpdatesUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
currencyId = cryptoCurrency.id,
|
||||
isSingleWalletWithTokens = isSingleWalletWithToken,
|
||||
)
|
||||
} else {
|
||||
getPrimaryCurrencyStatusUpdatesUseCase(userWalletId = userWallet.walletId)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onFailedTxEmailClick(errorMessage: String? = null) {
|
||||
saveBlockchainErrorUseCase(
|
||||
error = BlockchainErrorInfo(
|
||||
errorMessage = errorMessage.orEmpty(),
|
||||
blockchainId = cryptoCurrency.network.id.value,
|
||||
derivationPath = cryptoCurrency.network.derivationPath.value,
|
||||
destinationAddress = "",
|
||||
tokenSymbol = "",
|
||||
amount = "",
|
||||
fee = "",
|
||||
),
|
||||
)
|
||||
|
||||
val cardInfo = getCardInfoUseCase(userWallet.scanResponse).getOrNull() ?: return
|
||||
|
||||
modelScope.launch {
|
||||
sendFeedbackEmailUseCase(type = FeedbackEmailType.TransactionSendingProblem(cardInfo = cardInfo))
|
||||
}
|
||||
}
|
||||
|
||||
private fun subscribeOnQRScannerResult() {
|
||||
listenToQrScanningUseCase(SourceType.SEND)
|
||||
.getOrElse { emptyFlow() }
|
||||
|
|
@ -146,13 +242,15 @@ internal class SendDestinationModel @Inject constructor(
|
|||
private fun getWalletsAndRecent() {
|
||||
combine(
|
||||
flow = getWalletsUseCase().conflate().map {
|
||||
it.toAvailableWallets()
|
||||
waitForDelay(RECENT_LOAD_DELAY) { it.toAvailableWallets() }
|
||||
},
|
||||
flow2 = getFixedTxHistoryItemsUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
currency = cryptoCurrency,
|
||||
pageSize = RECENT_TX_SIZE,
|
||||
).getOrElse { flowOf(emptyList()) }.conflate(),
|
||||
).getOrElse { flowOf(emptyList()) }.map {
|
||||
waitForDelay(RECENT_LOAD_DELAY) { it }
|
||||
}.conflate(),
|
||||
) { destinationWalletList, txHistoryList ->
|
||||
val isUtxoConsolidationAvailable = isUtxoConsolidationAvailableUseCase.invokeSync(
|
||||
userWalletId = userWallet.walletId,
|
||||
|
|
@ -240,12 +338,13 @@ internal class SendDestinationModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun saveResult() {
|
||||
val params = params as? SendDestinationComponentParams.DestinationParams ?: return
|
||||
params.callback.onDestinationResult(uiState.value)
|
||||
}
|
||||
|
||||
private fun onNextClick() {
|
||||
saveResult()
|
||||
if (params.isEditMode) {
|
||||
if ((params as? SendDestinationComponentParams.DestinationParams)?.isEditMode == true) {
|
||||
router.pop()
|
||||
} else {
|
||||
router.push(SendRoute.Amount(isEditMode = false))
|
||||
|
|
@ -253,6 +352,7 @@ internal class SendDestinationModel @Inject constructor(
|
|||
}
|
||||
|
||||
private fun configDestinationNavigation() {
|
||||
val params = params as? SendDestinationComponentParams.DestinationParams ?: return
|
||||
combine(
|
||||
flow = uiState,
|
||||
flow2 = params.currentRoute,
|
||||
|
|
@ -301,5 +401,6 @@ internal class SendDestinationModel @Inject constructor(
|
|||
|
||||
private companion object {
|
||||
const val RECENT_TX_SIZE = 100
|
||||
const val RECENT_LOAD_DELAY = 500L
|
||||
}
|
||||
}
|
||||
|
|
@ -4,30 +4,28 @@ import androidx.compose.foundation.text.KeyboardOptions
|
|||
import androidx.compose.ui.text.input.ImeAction
|
||||
import androidx.compose.ui.text.input.KeyboardType
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.Network
|
||||
import com.tangem.features.send.v2.impl.R
|
||||
import com.tangem.features.send.v2.subcomponents.destination.analytics.EnterAddressSource
|
||||
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationTextFieldUM
|
||||
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationUM
|
||||
import com.tangem.utils.transformer.Transformer
|
||||
|
||||
internal class SendDestinationInitialStateTransformer(
|
||||
val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
val onAddressChange: (String, EnterAddressSource) -> Unit,
|
||||
val onMemoChange: (String, Boolean) -> Unit,
|
||||
val cryptoCurrency: CryptoCurrency,
|
||||
val isInitialized: Boolean = false,
|
||||
) : Transformer<DestinationUM> {
|
||||
override fun transform(prevState: DestinationUM): DestinationUM {
|
||||
val memoType = when (cryptoCurrencyStatus.currency.network.transactionExtrasType) {
|
||||
val memoType = when (cryptoCurrency.network.transactionExtrasType) {
|
||||
Network.TransactionExtrasType.NONE -> null
|
||||
Network.TransactionExtrasType.MEMO -> R.string.send_extras_hint_memo
|
||||
Network.TransactionExtrasType.DESTINATION_TAG -> R.string.send_destination_tag_field
|
||||
}
|
||||
return DestinationUM.Content(
|
||||
isPrimaryButtonEnabled = false,
|
||||
isInitialized = isInitialized,
|
||||
addressTextField = DestinationTextFieldUM.RecipientAddress(
|
||||
value = "",
|
||||
onValueChange = { onAddressChange(it, EnterAddressSource.InputField) },
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.Next,
|
||||
keyboardType = KeyboardType.Text,
|
||||
|
|
@ -40,7 +38,6 @@ internal class SendDestinationInitialStateTransformer(
|
|||
memoTextField = memoType?.let {
|
||||
DestinationTextFieldUM.RecipientMemo(
|
||||
value = "",
|
||||
onValueChange = { onMemoChange(it, false) },
|
||||
keyboardOptions = KeyboardOptions(
|
||||
imeAction = ImeAction.Done,
|
||||
keyboardType = KeyboardType.Text,
|
||||
|
|
@ -55,7 +52,7 @@ internal class SendDestinationInitialStateTransformer(
|
|||
},
|
||||
wallets = loadingListState(WALLET_KEY_TAG, WALLET_DEFAULT_COUNT),
|
||||
recent = loadingListState(RECENT_KEY_TAG, RECENT_DEFAULT_COUNT),
|
||||
networkName = cryptoCurrencyStatus.currency.network.name,
|
||||
networkName = cryptoCurrency.network.name,
|
||||
isValidating = false,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,93 @@
|
|||
package com.tangem.features.send.v2.subcomponents.destination.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import com.tangem.core.ui.components.icons.identicon.IdentIcon
|
||||
import com.tangem.core.ui.extensions.resolveReference
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationTextFieldUM
|
||||
import com.tangem.features.send.v2.subcomponents.destination.ui.state.DestinationUM
|
||||
|
||||
@Composable
|
||||
internal fun DestinationBlock(
|
||||
destinationUM: DestinationUM,
|
||||
isClickDisabled: Boolean,
|
||||
isEditingDisabled: Boolean,
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
val destinationUM = destinationUM as? DestinationUM.Content ?: return
|
||||
|
||||
val backgroundColor = if (isEditingDisabled) {
|
||||
TangemTheme.colors.button.disabled
|
||||
} else {
|
||||
TangemTheme.colors.background.action
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(TangemTheme.shapes.roundedCornersXMedium)
|
||||
.background(backgroundColor)
|
||||
.clickable(enabled = !isClickDisabled && !isEditingDisabled, onClick = onClick)
|
||||
.padding(TangemTheme.dimens.spacing12),
|
||||
) {
|
||||
AddressBlock(destinationUM.addressTextField)
|
||||
MemoBlock(destinationUM.memoTextField)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AddressBlock(address: DestinationTextFieldUM.RecipientAddress) {
|
||||
Text(
|
||||
text = address.label.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing12),
|
||||
modifier = Modifier.padding(top = TangemTheme.dimens.spacing8),
|
||||
) {
|
||||
IdentIcon(
|
||||
address = address.value,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size36)
|
||||
.clip(RoundedCornerShape(TangemTheme.dimens.radius18))
|
||||
.background(TangemTheme.colors.background.tertiary),
|
||||
)
|
||||
Text(
|
||||
text = address.value,
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun MemoBlock(memo: DestinationTextFieldUM.RecipientMemo?) {
|
||||
if (memo != null && memo.value.isNotBlank()) {
|
||||
HorizontalDivider(
|
||||
color = TangemTheme.colors.icon.inactive,
|
||||
modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
Text(
|
||||
text = memo.label.resolveReference(),
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
)
|
||||
Text(
|
||||
text = memo.value,
|
||||
style = TangemTheme.typography.body2,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier.padding(top = TangemTheme.dimens.spacing8),
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue