Updated on 2026-08-14
This commit is contained in:
commit
d78f54ac91
16 changed files with 97 additions and 17 deletions
|
|
@ -11,6 +11,7 @@ import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
|
|||
import com.tangem.domain.balancehiding.ListenToFlipsUseCase
|
||||
import com.tangem.domain.balancehiding.UpdateBalanceHidingSettingsUseCase
|
||||
import com.tangem.domain.settings.DeleteDeprecatedLogsUseCase
|
||||
import com.tangem.features.send.api.featuretoggles.SendFeatureToggles
|
||||
import com.tangem.tap.features.main.model.MainScreenState
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
|
|
@ -26,6 +27,7 @@ internal class MainViewModel @Inject constructor(
|
|||
private val reduxNavController: ReduxNavController,
|
||||
private val fetchAppCurrenciesUseCase: FetchAppCurrenciesUseCase,
|
||||
private val deleteDeprecatedLogsUseCase: DeleteDeprecatedLogsUseCase,
|
||||
private val sendFeatureToggles: SendFeatureToggles,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
|
||||
) : ViewModel(), MainIntents {
|
||||
|
|
@ -41,6 +43,7 @@ internal class MainViewModel @Inject constructor(
|
|||
|
||||
init {
|
||||
updateAppCurrencies()
|
||||
updateSendFeatureToggle()
|
||||
observeFlips()
|
||||
displayBalancesHidingStatusToast()
|
||||
displayHiddenBalancesModalNotification()
|
||||
|
|
@ -56,6 +59,12 @@ internal class MainViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private fun updateSendFeatureToggle() {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
sendFeatureToggles.fetchNewSendEnabled()
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeFlips() {
|
||||
listenToFlipsUseCase().launchIn(viewModelScope)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -82,4 +82,7 @@ interface TangemTechApi {
|
|||
@Header("card_id") cardId: String,
|
||||
@Body body: CreateUserNetworkAccountBody,
|
||||
): ApiResponse<CreateUserNetworkAccountResponse>
|
||||
|
||||
@GET("features")
|
||||
suspend fun getFeatures(): ApiResponse<FeaturesResponse>
|
||||
}
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.tangem.datasource.api.tangemTech.models
|
||||
|
||||
import com.squareup.moshi.Json
|
||||
|
||||
data class FeaturesResponse(
|
||||
@Json(name = "send") val isNewSendEnabled: Boolean,
|
||||
)
|
||||
|
|
@ -17,7 +17,7 @@
|
|||
},
|
||||
{
|
||||
"name": "LOCAL_USER_LOGS_ENABLED",
|
||||
"version": "5.10.0"
|
||||
"version": "5.11.0"
|
||||
},
|
||||
{
|
||||
"name": "GENERATE_XPUB_ENABLED",
|
||||
|
|
|
|||
|
|
@ -477,7 +477,7 @@
|
|||
<string name="send_network_fee_warning_title">Покрытие сетевой комиссии</string>
|
||||
<string name="send_notification_exceed_balance_text">Недостаточно средств для перевода, так как сумма комиссии и сумма перевода в совокупности больше имеющегося баланса</string>
|
||||
<string name="send_notification_exceed_balance_title">Недостаточно средств</string>
|
||||
<string name="send_notification_existential_deposit_button">Оставить %s</string>
|
||||
<string name="send_notification_leave_button">Оставить %s</string>
|
||||
<string name="send_notification_existential_deposit_text">Аккаунт будет удален из блокчейна, если баланс упадет ниже экзистенциального депозита. Пожалуйста, оставьте %s на балансе.</string>
|
||||
<string name="send_notification_existential_deposit_title">Экзистенциальный депозит</string>
|
||||
<string name="send_notification_fee_too_high_text">Сумма комиссии в %s раз превышает рекомендованную. Убедитесь, что указанная комиссия верна.</string>
|
||||
|
|
@ -490,7 +490,6 @@
|
|||
<string name="send_notification_invalid_reserve_amount_text">Адрес получателя не активирован. \nПожалуйста, измените сумму отправки, чтобы продолжить.</string>
|
||||
<string name="send_notification_invalid_reserve_amount_title">Сумма отправки не может быть менее %s</string>
|
||||
<string name="send_notification_reduce_by">Уменьшить на %s</string>
|
||||
<string name="send_notification_reduce_to">Уменьшить до %s</string>
|
||||
<string name="send_notification_transaction_delay_text">Обратите внимание, что при определенных параметрах комиссии возможны задержки по вашей транзакции</string>
|
||||
<string name="send_notification_transaction_delay_title">Возможны задержки по транзакции</string>
|
||||
<string name="send_notification_transaction_limit_text">Из-за ограничений %1$s в одну транзакцию может поместиться только %2$s UTXO. Это означает, что вы можете отправить только %3$s или меньше. Вам нужно уменьшить сумму.</string>
|
||||
|
|
|
|||
|
|
@ -474,7 +474,6 @@
|
|||
<string name="send_network_fee_warning_title">Network fee coverage</string>
|
||||
<string name="send_notification_exceed_balance_text">Insufficient funds for the transfer, as the total of the fee and transfer amount exceeds the existing balance</string>
|
||||
<string name="send_notification_exceed_balance_title">Total exceeds balance</string>
|
||||
<string name="send_notification_existential_deposit_button">Leave %s</string>
|
||||
<string name="send_notification_existential_deposit_text">The account will be wiped from the blockchain if a balance goes below the existential deposit. Please leave %s on your balance.</string>
|
||||
<string name="send_notification_existential_deposit_title">Existential deposit</string>
|
||||
<string name="send_notification_fee_too_high_text">The commission amount is %s times the recommended amount. Make sure that the custom settings are correct.</string>
|
||||
|
|
@ -486,8 +485,8 @@
|
|||
<string name="send_notification_invalid_minimum_amount_text">The minimum sending amount is %1$s. Please ensure that the remaining balance after sending will not be less than %2$s.</string>
|
||||
<string name="send_notification_invalid_reserve_amount_text">Target account is not created. Please change the amount to send.</string>
|
||||
<string name="send_notification_invalid_reserve_amount_title">The amount to send must be at least %s</string>
|
||||
<string name="send_notification_leave_button">Leave %s</string>
|
||||
<string name="send_notification_reduce_by">Reduce by %s</string>
|
||||
<string name="send_notification_reduce_to">Reduce to %s</string>
|
||||
<string name="send_notification_transaction_delay_text">Kindly be aware that your transaction may experience delays under specific fee settings</string>
|
||||
<string name="send_notification_transaction_delay_title">Transaction delays are possible</string>
|
||||
<string name="send_notification_transaction_limit_text">Due to %1$s limitations only %2$s UTXOs can fit in a single transaction. This means you can only send %3$s or less. You need to reduce the amount.</string>
|
||||
|
|
|
|||
|
|
@ -7,4 +7,7 @@ interface SendFeatureToggles {
|
|||
|
||||
/** Availability of redesigned send screen */
|
||||
val isRedesignedSendEnabled: Boolean
|
||||
|
||||
/** Updates remote toggle */
|
||||
suspend fun fetchNewSendEnabled()
|
||||
}
|
||||
|
|
@ -48,6 +48,7 @@ dependencies {
|
|||
implementation(projects.core.navigation)
|
||||
implementation(projects.core.analytics)
|
||||
implementation(projects.core.analytics.models)
|
||||
implementation(projects.core.datasource)
|
||||
|
||||
/** Common */
|
||||
implementation(projects.common)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package com.tangem.features.send.impl.di
|
||||
|
||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.features.send.api.featuretoggles.SendFeatureToggles
|
||||
import com.tangem.features.send.impl.featuretoggles.DefaultSendFeatureToggles
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
|
@ -18,7 +20,15 @@ internal object SendFeatureTogglesModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideSendFeatureToggles(featureTogglesManager: FeatureTogglesManager): SendFeatureToggles {
|
||||
return DefaultSendFeatureToggles(featureTogglesManager = featureTogglesManager)
|
||||
fun provideSendFeatureToggles(
|
||||
featureTogglesManager: FeatureTogglesManager,
|
||||
tangemTechApi: TangemTechApi,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
): SendFeatureToggles {
|
||||
return DefaultSendFeatureToggles(
|
||||
featureTogglesManager = featureTogglesManager,
|
||||
tangemTechApi = tangemTechApi,
|
||||
dispatchers = dispatchers,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,16 +1,41 @@
|
|||
package com.tangem.features.send.impl.featuretoggles
|
||||
|
||||
import com.tangem.core.featuretoggle.manager.FeatureTogglesManager
|
||||
import com.tangem.datasource.api.common.response.getOrThrow
|
||||
import com.tangem.datasource.api.tangemTech.TangemTechApi
|
||||
import com.tangem.features.send.api.featuretoggles.SendFeatureToggles
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import com.tangem.utils.coroutines.runCatching
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* Default implementation of Send feature toggles
|
||||
*
|
||||
* @property featureTogglesManager manager for getting information about the availability of feature toggles
|
||||
* @property tangemTechApi api to get remote feature toggle for send
|
||||
* @property dispatchers coroutine dispatchers
|
||||
*/
|
||||
internal class DefaultSendFeatureToggles(
|
||||
private val featureTogglesManager: FeatureTogglesManager,
|
||||
private val tangemTechApi: TangemTechApi,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : SendFeatureToggles {
|
||||
|
||||
private val remoteSendEnabled: MutableStateFlow<Boolean> = MutableStateFlow(true)
|
||||
|
||||
override val isRedesignedSendEnabled: Boolean
|
||||
get() = featureTogglesManager.isFeatureEnabled(name = "REDESIGNED_SEND_SCREEN_ENABLED")
|
||||
get() = featureTogglesManager.isFeatureEnabled(name = "REDESIGNED_SEND_SCREEN_ENABLED") &&
|
||||
remoteSendEnabled.value
|
||||
|
||||
override suspend fun fetchNewSendEnabled() {
|
||||
runCatching(dispatchers.io) {
|
||||
tangemTechApi.getFeatures().getOrThrow()
|
||||
}.onSuccess { response ->
|
||||
remoteSendEnabled.update { response.isNewSendEnabled }
|
||||
}.onFailure {
|
||||
Timber.e(it.localizedMessage, "Unable to fetch new send toggle")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,18 @@
|
|||
package com.tangem.features.send.impl.presentation.domain
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
/**
|
||||
* Available wallet to send
|
||||
*
|
||||
* @property name wallet name
|
||||
* @property userWalletId wallet id
|
||||
* @property address blockchain address
|
||||
*/
|
||||
@Immutable
|
||||
data class AvailableWallet(
|
||||
val name: String,
|
||||
val userWalletId: UserWalletId,
|
||||
val address: String,
|
||||
)
|
||||
|
|
@ -54,7 +54,7 @@ internal sealed class SendNotification(val config: NotificationConfig) {
|
|||
wrappedList(cryptoCurrency, utxoLimit, amountLimit),
|
||||
),
|
||||
buttonState = NotificationConfig.ButtonsState.PrimaryButtonConfig(
|
||||
text = resourceReference(R.string.send_notification_reduce_to, wrappedList(amountLimit)),
|
||||
text = resourceReference(R.string.send_notification_leave_button, wrappedList(amountLimit)),
|
||||
onClick = onConfirmClick,
|
||||
),
|
||||
)
|
||||
|
|
@ -98,7 +98,7 @@ internal sealed class SendNotification(val config: NotificationConfig) {
|
|||
title = resourceReference(R.string.send_notification_existential_deposit_title),
|
||||
subtitle = resourceReference(R.string.send_notification_existential_deposit_text, wrappedList(deposit)),
|
||||
buttonState = NotificationConfig.ButtonsState.PrimaryButtonConfig(
|
||||
text = resourceReference(R.string.send_notification_existential_deposit_button, wrappedList(deposit)),
|
||||
text = resourceReference(R.string.send_notification_leave_button, wrappedList(deposit)),
|
||||
onClick = onConfirmClick,
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -66,11 +66,12 @@ internal class SendNotificationFactory(
|
|||
amountValue = amountValue,
|
||||
feeValue = feeValue,
|
||||
)
|
||||
val sendingAmount = calculateSubtractedAmount(
|
||||
isFeeCoverage = isFeeCoverage,
|
||||
val sendingAmount = checkAndCalculateSubtractedAmount(
|
||||
isAmountSubtractAvailable = isFeeCoverage,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatusProvider(),
|
||||
amountValue = amountValue,
|
||||
feeValue = feeValue,
|
||||
reduceAmountBy = sendState.reduceAmountBy,
|
||||
)
|
||||
buildList {
|
||||
// errors
|
||||
|
|
@ -194,7 +195,7 @@ internal class SendNotificationFactory(
|
|||
val spendingAmount = if (cryptoCurrency is CryptoCurrency.Token) {
|
||||
feeAmount
|
||||
} else {
|
||||
feeAmount + receivedAmount
|
||||
receivedAmount
|
||||
}
|
||||
val currencyDeposit = currencyChecksRepository.getExistentialDeposit(
|
||||
userWalletId,
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ internal fun checkFeeCoverage(
|
|||
/**
|
||||
* Calculates subtracted amount
|
||||
*/
|
||||
internal fun calculateSubtractedAmount(
|
||||
private fun calculateSubtractedAmount(
|
||||
isFeeCoverage: Boolean,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
amountValue: BigDecimal,
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ internal class SendViewModel @Inject constructor(
|
|||
private val neverShowTapHelpUseCase: NeverShowTapHelpUseCase,
|
||||
private val getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
|
||||
private val listenToQrScanningUseCase: ListenToQrScanningUseCase,
|
||||
private val addCryptoCurrenciesUseCase: AddCryptoCurrenciesUseCase,
|
||||
currencyChecksRepository: CurrencyChecksRepository,
|
||||
isFeeApproximateUseCase: IsFeeApproximateUseCase,
|
||||
validateWalletMemoUseCase: ValidateWalletMemoUseCase,
|
||||
|
|
@ -189,6 +190,7 @@ internal class SendViewModel @Inject constructor(
|
|||
private set
|
||||
|
||||
private var userWallet: UserWallet by Delegates.notNull()
|
||||
private var userWallets: List<AvailableWallet> = emptyList()
|
||||
private var isAmountSubtractAvailable: Boolean = false
|
||||
private var isTapHelpPreviewEnabled: Boolean = false
|
||||
private var coinCryptoCurrencyStatus: CryptoCurrencyStatus by Delegates.notNull()
|
||||
|
|
@ -408,7 +410,10 @@ internal class SendViewModel @Inject constructor(
|
|||
.orEmpty()
|
||||
}.onSuccess { result ->
|
||||
combine(*result.toTypedArray()) { it }
|
||||
.onEach { uiState = stateFactory.onLoadedWalletsList(wallets = it.toList()) }
|
||||
.onEach {
|
||||
userWallets = it.filterNotNull().toList()
|
||||
uiState = stateFactory.onLoadedWalletsList(wallets = userWallets)
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(viewModelScope)
|
||||
}.onFailure {
|
||||
|
|
@ -417,7 +422,7 @@ internal class SendViewModel @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun List<UserWallet>.toAvailableWallets(): List<Flow<AvailableWallet>> =
|
||||
private suspend fun List<UserWallet>.toAvailableWallets(): List<Flow<AvailableWallet?>> =
|
||||
filterNot { it.walletId == userWalletId || it.isLocked }
|
||||
.mapNotNull { wallet ->
|
||||
val status = if (!wallet.isMultiCurrency) {
|
||||
|
|
@ -435,6 +440,7 @@ internal class SendViewModel @Inject constructor(
|
|||
AvailableWallet(
|
||||
name = wallet.name,
|
||||
address = address,
|
||||
userWalletId = wallet.walletId,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -863,11 +869,25 @@ internal class SendViewModel @Inject constructor(
|
|||
uiState = stateFactory.getSendingStateUpdate(isSending = false)
|
||||
updateTransactionStatus(txData)
|
||||
scheduleBalanceUpdate()
|
||||
addTokenToWalletIfNeeded()
|
||||
sendScreenAnalyticSender.sendTransaction()
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun addTokenToWalletIfNeeded() {
|
||||
if (cryptoCurrency !is CryptoCurrency.Token) return
|
||||
|
||||
val recipientState = uiState.getRecipientState(stateRouter.isEditState) ?: return
|
||||
val destinationAddress = recipientState.addressTextField.value
|
||||
|
||||
val maybeUserWallet = userWallets.firstOrNull { it.address == destinationAddress } ?: return
|
||||
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
addCryptoCurrenciesUseCase(userWalletId = maybeUserWallet.userWalletId, currency = cryptoCurrency)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun updateTransactionStatus(txData: TransactionData) {
|
||||
val txUrl = getExplorerTransactionUrlUseCase(
|
||||
userWalletId = userWalletId,
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@ markdown = "0.7.2"
|
|||
# endregion Other libraries
|
||||
|
||||
# region Tangem
|
||||
tangemBlockchainSdk = "release-app_5.10-618"
|
||||
tangemBlockchainSdk = "release-app_5.10-630"
|
||||
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
|
||||
tangemCardSdk = "release-app_5.10-353"
|
||||
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds ^
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue