Updated on 2026-08-14
This commit is contained in:
parent
575389311f
commit
20c2f57407
27 changed files with 292 additions and 62 deletions
|
|
@ -26,6 +26,7 @@ import com.tangem.domain.DomainLayer
|
|||
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
|
||||
import com.tangem.domain.card.ScanCardProcessor
|
||||
import com.tangem.domain.common.LogConfig
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.legacy.WalletManagersRepository
|
||||
import com.tangem.feature.learn2earn.domain.api.Learn2earnInteractor
|
||||
import com.tangem.features.tokendetails.featuretoggles.TokenDetailsFeatureToggles
|
||||
|
|
@ -41,8 +42,8 @@ import com.tangem.tap.common.chat.ChatManager
|
|||
import com.tangem.tap.common.feedback.AdditionalFeedbackInfo
|
||||
import com.tangem.tap.common.feedback.FeedbackManager
|
||||
import com.tangem.tap.common.images.createCoilImageLoader
|
||||
import com.tangem.tap.common.log.TimberFormatStrategy
|
||||
import com.tangem.tap.common.log.TangemLogCollector
|
||||
import com.tangem.tap.common.log.TimberFormatStrategy
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.appReducer
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
|
|
@ -166,6 +167,9 @@ class TapApplication : Application(), ImageLoaderFactory {
|
|||
@Inject
|
||||
lateinit var appCurrencyRepository: AppCurrencyRepository
|
||||
|
||||
@Inject
|
||||
lateinit var walletManagersFacade: WalletManagersFacade
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
|
|
@ -185,6 +189,7 @@ class TapApplication : Application(), ImageLoaderFactory {
|
|||
tokenDetailsFeatureToggles = tokenDetailsFeatureToggles,
|
||||
scanCardProcessor = scanCardProcessor,
|
||||
appCurrencyRepository = appCurrencyRepository,
|
||||
walletManagersFacade = walletManagersFacade,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.di
|
||||
|
||||
import com.tangem.core.navigation.NavigationStateHolder
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.wallets.legacy.WalletsStateHolder
|
||||
import com.tangem.tap.proxy.AppStateHolder
|
||||
import dagger.Binds
|
||||
|
|
@ -20,4 +21,8 @@ internal interface AppStateHolderModule {
|
|||
@Binds
|
||||
@Singleton
|
||||
fun bindsNavigationStateHolder(appStateHolder: AppStateHolder): NavigationStateHolder
|
||||
|
||||
@Binds
|
||||
@Singleton
|
||||
fun bindsReduxStateHolder(appStateHolder: AppStateHolder): ReduxStateHolder
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package com.tangem.tap.features.demo
|
|||
|
||||
import com.tangem.domain.demo.DemoConfig
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.tap.common.extensions.dispatchNotification
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
|
|
@ -21,8 +22,8 @@ object DemoHelper {
|
|||
|
||||
private val disabledActionFeatures = listOf(
|
||||
WalletConnectAction.StartWalletConnect::class.java,
|
||||
WalletAction.TradeCryptoAction.Buy::class.java,
|
||||
WalletAction.TradeCryptoAction.Sell::class.java,
|
||||
TradeCryptoAction.Buy::class.java,
|
||||
TradeCryptoAction.Sell::class.java,
|
||||
BackupAction.StartBackup::class.java,
|
||||
WalletAction.ExploreAddress::class.java,
|
||||
DetailsAction.ResetToFactory.Start::class.java,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
package com.tangem.tap.features.intentHandler.handlers
|
||||
|
||||
import android.content.Intent
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.tap.common.extensions.dispatchWithMain
|
||||
import com.tangem.tap.features.intentHandler.IntentHandler
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.store
|
||||
import timber.log.Timber
|
||||
|
||||
|
|
@ -22,7 +22,7 @@ class SellCurrencyIntentHandler : IntentHandler {
|
|||
|
||||
Timber.d("MoonPay Sell: $amount $currency to $destinationAddress")
|
||||
store.dispatchWithMain(
|
||||
WalletAction.TradeCryptoAction.SendCrypto(
|
||||
TradeCryptoAction.SendCrypto(
|
||||
currencyId = currency,
|
||||
amount = amount,
|
||||
destinationAddress = destinationAddress,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import com.tangem.domain.common.TapWorkarounds.isStart2Coin
|
|||
import com.tangem.domain.common.extensions.minimalAmount
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.tap.*
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.Basic
|
||||
|
|
@ -38,7 +39,6 @@ import com.tangem.tap.features.send.redux.*
|
|||
import com.tangem.tap.features.send.redux.FeeAction.RequestFee
|
||||
import com.tangem.tap.features.send.redux.states.*
|
||||
import com.tangem.tap.features.wallet.models.Currency
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.wallet.R
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
|
|
@ -254,7 +254,7 @@ private fun sendTransaction(
|
|||
),
|
||||
)
|
||||
Analytics.sendSelectedCurrencyEvent(mainCurrencyType)
|
||||
dispatch(WalletAction.TradeCryptoAction.FinishSelling(externalTransactionData.transactionId))
|
||||
dispatch(TradeCryptoAction.FinishSelling(externalTransactionData.transactionId))
|
||||
} else {
|
||||
Analytics.send(
|
||||
Basic.TransactionSent(
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import com.google.android.material.textfield.TextInputEditText
|
|||
import com.tangem.Message
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.sdk.extensions.hideSoftKeyboard
|
||||
import com.tangem.tap.common.KeyboardObserver
|
||||
import com.tangem.tap.common.analytics.events.Token
|
||||
|
|
@ -39,7 +40,6 @@ import com.tangem.tap.features.send.redux.FeeActionUi.*
|
|||
import com.tangem.tap.features.send.redux.states.FeeType
|
||||
import com.tangem.tap.features.send.redux.states.MainCurrencyType
|
||||
import com.tangem.tap.features.send.ui.stateSubscribers.SendStateSubscriber
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter
|
||||
import com.tangem.tap.mainScope
|
||||
import com.tangem.tap.store
|
||||
|
|
@ -337,9 +337,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
if (externalTransactionData == null) {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
} else {
|
||||
store.dispatch(
|
||||
WalletAction.TradeCryptoAction.FinishSelling(externalTransactionData.transactionId),
|
||||
)
|
||||
store.dispatch(TradeCryptoAction.FinishSelling(externalTransactionData.transactionId))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,12 +4,16 @@ import androidx.core.os.bundleOf
|
|||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.common.extensions.guard
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.domain.common.extensions.toCoinId
|
||||
import com.tangem.domain.common.extensions.toNetworkId
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
import com.tangem.feature.swap.presentation.SwapFragment
|
||||
import com.tangem.tap.common.analytics.events.AnalyticsParam
|
||||
import com.tangem.tap.common.analytics.events.Token
|
||||
|
|
@ -27,6 +31,7 @@ import com.tangem.tap.features.wallet.redux.WalletAction
|
|||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
|
||||
import com.tangem.tap.network.exchangeServices.buyErc20TestnetTokens
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
import kotlinx.coroutines.launch
|
||||
|
|
@ -35,19 +40,27 @@ import kotlinx.serialization.json.Json
|
|||
import com.tangem.feature.swap.domain.models.domain.Currency as SwapCurrency
|
||||
|
||||
class TradeCryptoMiddleware {
|
||||
fun handle(state: () -> AppState?, action: WalletAction.TradeCryptoAction) {
|
||||
fun handle(state: () -> AppState?, action: TradeCryptoAction) {
|
||||
if (DemoHelper.tryHandle(state, action)) return
|
||||
|
||||
when (action) {
|
||||
is WalletAction.TradeCryptoAction.Buy -> proceedBuyAction(state, action)
|
||||
is WalletAction.TradeCryptoAction.Sell -> proceedSellAction()
|
||||
is WalletAction.TradeCryptoAction.SendCrypto -> preconfigureAndOpenSendScreen(action)
|
||||
is WalletAction.TradeCryptoAction.FinishSelling -> openReceiptUrl(action.transactionId)
|
||||
is WalletAction.TradeCryptoAction.Swap -> openSwap()
|
||||
is TradeCryptoAction.Buy -> proceedBuyAction(state, action)
|
||||
is TradeCryptoAction.Sell -> proceedSellAction()
|
||||
is TradeCryptoAction.SendCrypto -> preconfigureAndOpenSendScreen(action)
|
||||
is TradeCryptoAction.FinishSelling -> openReceiptUrl(action.transactionId)
|
||||
is TradeCryptoAction.Swap -> {
|
||||
openSwap(currency = store.state.walletState.selectedWalletData?.currency?.toSwapCurrency())
|
||||
}
|
||||
is TradeCryptoAction.New.Buy -> proceedNewBuyAction(state, action)
|
||||
TradeCryptoAction.New.Send -> store.dispatch(WalletAction.Send())
|
||||
is TradeCryptoAction.New.Sell -> proceedNewSellAction(action)
|
||||
is TradeCryptoAction.New.Swap -> {
|
||||
openSwap(currency = action.cryptoCurrency.toSwapCurrency())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedBuyAction(state: () -> AppState?, action: WalletAction.TradeCryptoAction.Buy) {
|
||||
private fun proceedBuyAction(state: () -> AppState?, action: TradeCryptoAction.Buy) {
|
||||
val selectedWalletData = store.state.walletState.selectedWalletData ?: return
|
||||
val currency = chooseAppropriateCurrency(store.state.walletState) ?: return
|
||||
|
||||
|
|
@ -75,7 +88,7 @@ class TradeCryptoMiddleware {
|
|||
buyErc20TestnetTokens(
|
||||
card = card,
|
||||
walletManager = walletManager,
|
||||
token = currency.token,
|
||||
destinationAddress = currency.token.contractAddress,
|
||||
)
|
||||
}
|
||||
return
|
||||
|
|
@ -93,6 +106,56 @@ class TradeCryptoMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun proceedNewBuyAction(state: () -> AppState?, action: TradeCryptoAction.New.Buy) {
|
||||
val networkAddress = action.cryptoCurrencyStatus.value.networkAddress?.defaultAddress ?: return
|
||||
|
||||
if (action.checkUserLocation && state()?.globalState?.userCountryCode == RUSSIA_COUNTRY_CODE) {
|
||||
store.dispatchOnMain(WalletAction.DialogAction.RussianCardholdersWarningDialog())
|
||||
return
|
||||
}
|
||||
|
||||
val status = action.cryptoCurrencyStatus
|
||||
val currency = status.currency
|
||||
val blockchain = Blockchain.fromId(currency.network.id.value)
|
||||
if (currency is CryptoCurrency.Token && currency.network.isTestnet) {
|
||||
scope.launch {
|
||||
val walletManager = store.state.daggerGraphState
|
||||
.get(DaggerGraphState::walletManagersFacade)
|
||||
.getOrCreateWalletManager(
|
||||
userWallet = action.userWallet,
|
||||
blockchain = blockchain,
|
||||
derivationPath = blockchain.derivationPath(
|
||||
style = action.userWallet.scanResponse.derivationStyleProvider.getDerivationStyle(),
|
||||
),
|
||||
)
|
||||
|
||||
if (walletManager !is EthereumWalletManager) {
|
||||
store.dispatchDebugErrorNotification("Testnet tokens available only for the Ethereum")
|
||||
return@launch
|
||||
}
|
||||
|
||||
buyErc20TestnetTokens(
|
||||
card = action.userWallet.scanResponse.card,
|
||||
walletManager = walletManager,
|
||||
destinationAddress = currency.contractAddress,
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
val exchangeManager = store.state.globalState.exchangeManager
|
||||
exchangeManager.getUrl(
|
||||
action = CurrencyExchangeManager.Action.Buy,
|
||||
blockchain = blockchain,
|
||||
cryptoCurrencyName = currency.symbol,
|
||||
fiatCurrencyName = action.appCurrencyCode,
|
||||
walletAddress = networkAddress,
|
||||
)?.let {
|
||||
store.dispatchOpenUrl(it)
|
||||
Analytics.send(Token.Topup.ScreenOpened())
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedSellAction() {
|
||||
val selectedWalletData = store.state.walletState.selectedWalletData ?: return
|
||||
val currency = chooseAppropriateCurrency(store.state.walletState) ?: return
|
||||
|
|
@ -115,6 +178,22 @@ class TradeCryptoMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun proceedNewSellAction(action: TradeCryptoAction.New.Sell) {
|
||||
val networkAddress = action.cryptoCurrencyStatus.value.networkAddress?.defaultAddress ?: return
|
||||
val currency = action.cryptoCurrencyStatus.currency
|
||||
|
||||
store.state.globalState.exchangeManager.getUrl(
|
||||
action = CurrencyExchangeManager.Action.Sell,
|
||||
blockchain = Blockchain.fromId(currency.network.id.value),
|
||||
cryptoCurrencyName = currency.symbol,
|
||||
fiatCurrencyName = action.appCurrencyCode,
|
||||
walletAddress = networkAddress,
|
||||
)?.let {
|
||||
store.dispatchOpenUrl(it)
|
||||
Analytics.send(Token.Withdraw.ScreenOpened())
|
||||
}
|
||||
}
|
||||
|
||||
private fun chooseAppropriateCurrency(walletState: WalletState): Currency? {
|
||||
return if (walletState.primaryTokenData == null) {
|
||||
walletState.selectedWalletData?.currency
|
||||
|
|
@ -126,7 +205,7 @@ class TradeCryptoMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun preconfigureAndOpenSendScreen(action: WalletAction.TradeCryptoAction.SendCrypto) {
|
||||
private fun preconfigureAndOpenSendScreen(action: TradeCryptoAction.SendCrypto) {
|
||||
val selectedWalletData = store.state.walletState.selectedWalletData ?: return
|
||||
|
||||
Analytics.send(Token.ButtonSend(AnalyticsParam.CurrencyType.Currency(selectedWalletData.currency)))
|
||||
|
|
@ -160,16 +239,44 @@ class TradeCryptoMiddleware {
|
|||
)?.let { store.dispatchOpenUrl(it) }
|
||||
}
|
||||
|
||||
private fun openSwap() {
|
||||
val currency = store.state.walletState.selectedWalletData?.currency?.toSwapCurrency()
|
||||
val bundle =
|
||||
bundleOf(
|
||||
SwapFragment.CURRENCY_BUNDLE_KEY to Json.encodeToString(currency),
|
||||
SwapFragment.DERIVATION_PATH to store.state.walletState.selectedWalletData?.currency?.derivationPath,
|
||||
)
|
||||
private fun openSwap(currency: SwapCurrency?) {
|
||||
val bundle = bundleOf(
|
||||
SwapFragment.CURRENCY_BUNDLE_KEY to Json.encodeToString(currency),
|
||||
SwapFragment.DERIVATION_PATH to store.state.walletState.selectedWalletData?.currency?.derivationPath,
|
||||
)
|
||||
|
||||
store.dispatchOnMain(NavigationAction.NavigateTo(screen = AppScreen.Swap, bundle = bundle))
|
||||
}
|
||||
|
||||
private fun CryptoCurrency.toSwapCurrency(): SwapCurrency {
|
||||
val blockchain = Blockchain.fromId(network.id.value)
|
||||
|
||||
return when (this) {
|
||||
is CryptoCurrency.Coin -> {
|
||||
SwapCurrency.NativeToken(
|
||||
id = blockchain.toCoinId(),
|
||||
name = name,
|
||||
symbol = symbol,
|
||||
networkId = blockchain.toNetworkId(),
|
||||
// no need to set logoUrl for blockchain cause
|
||||
// error when form url with coinId, coinId of eth and arbitrum the same
|
||||
logoUrl = "",
|
||||
)
|
||||
}
|
||||
is CryptoCurrency.Token -> {
|
||||
SwapCurrency.NonNativeToken(
|
||||
id = id.value,
|
||||
name = name,
|
||||
symbol = symbol,
|
||||
networkId = blockchain.toNetworkId(),
|
||||
logoUrl = getIconUrl(id.value),
|
||||
contractAddress = contractAddress,
|
||||
decimalCount = decimals,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Currency.toSwapCurrency(): SwapCurrency {
|
||||
return when (this) {
|
||||
is Currency.Blockchain -> {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.tangem.core.analytics.Analytics
|
|||
import com.tangem.core.navigation.AppScreen
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.datasource.connection.NetworkConnectionManager
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.userwallets.GetCardImageUseCase
|
||||
import com.tangem.domain.wallets.legacy.lockIfLockable
|
||||
import com.tangem.tap.*
|
||||
|
|
@ -87,7 +88,7 @@ class WalletMiddleware {
|
|||
val walletState = store.state.walletState
|
||||
|
||||
when (action) {
|
||||
is WalletAction.TradeCryptoAction -> tradeCryptoMiddleware.handle(state, action)
|
||||
is TradeCryptoAction -> tradeCryptoMiddleware.handle(state, action)
|
||||
is WalletAction.Warnings -> warningsMiddleware.handle(action, globalState)
|
||||
is WalletAction.MultiWallet -> multiWalletMiddleware.handle(action, walletState)
|
||||
is WalletAction.AppCurrencyAction -> appCurrencyMiddleware.handle(action)
|
||||
|
|
|
|||
|
|
@ -68,7 +68,6 @@ private fun internalReduce(action: Action, state: AppState, appStateHolder: AppS
|
|||
error = null,
|
||||
)
|
||||
}
|
||||
is WalletAction.TradeCryptoAction -> return newState
|
||||
is WalletAction.AppCurrencyAction -> {
|
||||
newState = appCurrencyReducer.reduce(action, newState)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import com.tangem.core.analytics.Analytics
|
|||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.feature.swap.api.SwapFeatureToggleManager
|
||||
import com.tangem.feature.swap.domain.SwapInteractor
|
||||
import com.tangem.sdk.extensions.dpToPx
|
||||
|
|
@ -276,9 +277,9 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), SafeSt
|
|||
) {
|
||||
val exchangeManager = store.state.globalState.exchangeManager
|
||||
binding.rowButtons.apply {
|
||||
onBuyClick = { store.dispatch(WalletAction.TradeCryptoAction.Buy()) }
|
||||
onSellClick = { store.dispatch(WalletAction.TradeCryptoAction.Sell) }
|
||||
onSwapClick = { store.dispatch(WalletAction.TradeCryptoAction.Swap) }
|
||||
onBuyClick = { store.dispatch(TradeCryptoAction.Buy()) }
|
||||
onSellClick = { store.dispatch(TradeCryptoAction.Sell) }
|
||||
onSwapClick = { store.dispatch(TradeCryptoAction.Swap) }
|
||||
onTradeClick = {
|
||||
store.dispatch(
|
||||
WalletAction.DialogAction.ChooseTradeActionDialog(
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.tap.common.extensions.show
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.models.WalletDialog
|
||||
|
|
@ -39,15 +40,15 @@ class ChooseTradeActionBottomSheetDialog(
|
|||
|
||||
dialogBtnBuy.setOnClickListener {
|
||||
dismiss()
|
||||
store.dispatch(WalletAction.TradeCryptoAction.Buy())
|
||||
store.dispatch(TradeCryptoAction.Buy())
|
||||
}
|
||||
dialogBtnSell.setOnClickListener {
|
||||
dismiss()
|
||||
store.dispatch(WalletAction.TradeCryptoAction.Sell)
|
||||
store.dispatch(TradeCryptoAction.Sell)
|
||||
}
|
||||
dialogBtnSwap.setOnClickListener {
|
||||
dismiss()
|
||||
store.dispatch(WalletAction.TradeCryptoAction.Swap)
|
||||
store.dispatch(TradeCryptoAction.Swap)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,10 +6,10 @@ import android.view.LayoutInflater
|
|||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.core.navigation.NavigationAction
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.tap.common.analytics.events.Token
|
||||
import com.tangem.tap.common.extensions.dispatchDialogHide
|
||||
import com.tangem.tap.common.extensions.dispatchOpenUrl
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.features.wallet.redux.models.WalletDialog
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.databinding.DialogRussiansCardholdersWarningBinding
|
||||
|
|
@ -40,7 +40,7 @@ class RussianCardholdersWarningBottomSheetDialog(
|
|||
if (dialogData != null) {
|
||||
store.dispatchOpenUrl(dialogData.topUpUrl)
|
||||
} else {
|
||||
store.dispatch(WalletAction.TradeCryptoAction.Buy(checkUserLocation = false))
|
||||
store.dispatch(TradeCryptoAction.Buy(checkUserLocation = false))
|
||||
}
|
||||
dismiss()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.tap.common.analytics.events.Token
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.domain.model.WalletDataModel
|
||||
|
|
@ -151,9 +152,9 @@ class SingleWalletView : WalletView() {
|
|||
|
||||
val exchangeManager = store.state.globalState.exchangeManager
|
||||
binding?.rowButtons?.apply {
|
||||
onBuyClick = { store.dispatch(WalletAction.TradeCryptoAction.Buy()) }
|
||||
onSellClick = { store.dispatch(WalletAction.TradeCryptoAction.Sell) }
|
||||
onSwapClick = { store.dispatch(WalletAction.TradeCryptoAction.Swap) }
|
||||
onBuyClick = { store.dispatch(TradeCryptoAction.Buy()) }
|
||||
onSellClick = { store.dispatch(TradeCryptoAction.Sell) }
|
||||
onSwapClick = { store.dispatch(TradeCryptoAction.Swap) }
|
||||
onTradeClick = {
|
||||
store.dispatch(
|
||||
WalletAction.DialogAction.ChooseTradeActionDialog(
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager
|
|||
import com.tangem.blockchain.common.Amount
|
||||
import com.tangem.blockchain.common.AmountType
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.Token
|
||||
import com.tangem.blockchain.common.transaction.TransactionFee
|
||||
import com.tangem.blockchain.extensions.Result
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
|
|
@ -87,17 +86,12 @@ class CurrencyExchangeManager(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun buyErc20TestnetTokens(card: CardDTO, walletManager: EthereumWalletManager, token: Token) {
|
||||
suspend fun buyErc20TestnetTokens(card: CardDTO, walletManager: EthereumWalletManager, destinationAddress: String) {
|
||||
walletManager.safeUpdate()
|
||||
|
||||
val amountToSend = Amount(walletManager.wallet.blockchain)
|
||||
val destinationAddress = token.contractAddress
|
||||
|
||||
val feeResult = walletManager.getFee(
|
||||
amountToSend,
|
||||
destinationAddress,
|
||||
) as? Result.Success ?: return
|
||||
|
||||
val feeResult = walletManager.getFee(amountToSend, destinationAddress) as? Result.Success ?: return
|
||||
val fee = when (val feeForTx = feeResult.data) {
|
||||
is TransactionFee.Choosable -> feeForTx.minimum
|
||||
is TransactionFee.Single -> feeForTx.normal
|
||||
|
|
@ -106,8 +100,6 @@ suspend fun buyErc20TestnetTokens(card: CardDTO, walletManager: EthereumWalletMa
|
|||
val coinValue = walletManager.wallet.amounts[AmountType.Coin]?.value ?: BigDecimal.ZERO
|
||||
if (coinValue < fee.amount.value) return
|
||||
|
||||
val transaction = walletManager.createTransaction(amountToSend, fee, destinationAddress)
|
||||
|
||||
val signer = TangemSigner(
|
||||
card = card,
|
||||
tangemSdk = store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository).sdk,
|
||||
|
|
@ -121,5 +113,13 @@ suspend fun buyErc20TestnetTokens(card: CardDTO, walletManager: EthereumWalletMa
|
|||
),
|
||||
)
|
||||
}
|
||||
walletManager.send(transaction, signer)
|
||||
|
||||
walletManager.send(
|
||||
transactionData = walletManager.createTransaction(
|
||||
amount = amountToSend,
|
||||
fee = fee,
|
||||
destination = destinationAddress,
|
||||
),
|
||||
signer = signer,
|
||||
)
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@ import com.tangem.core.navigation.NavigationAction
|
|||
import com.tangem.core.navigation.NavigationStateHolder
|
||||
import com.tangem.domain.models.scan.CardDTO
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.wallets.legacy.UserWalletsListManager
|
||||
import com.tangem.domain.wallets.legacy.WalletsStateHolder
|
||||
import com.tangem.tap.common.entities.FiatCurrency
|
||||
|
|
@ -14,6 +15,7 @@ import com.tangem.tap.domain.walletStores.WalletStoresManager
|
|||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.Store
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -21,7 +23,7 @@ import javax.inject.Inject
|
|||
* Holds objects from old modules, that missing in DI graph.
|
||||
* Object sets manually to use in new modules and [AppStateHolder] proxies its to DI.
|
||||
*/
|
||||
class AppStateHolder @Inject constructor() : WalletsStateHolder, NavigationStateHolder {
|
||||
class AppStateHolder @Inject constructor() : WalletsStateHolder, NavigationStateHolder, ReduxStateHolder {
|
||||
|
||||
override var userWalletsListManager: UserWalletsListManager? = null
|
||||
set(value) {
|
||||
|
|
@ -50,4 +52,8 @@ class AppStateHolder @Inject constructor() : WalletsStateHolder, NavigationState
|
|||
override fun navigate(action: NavigationAction) {
|
||||
mainStore?.dispatch(action)
|
||||
}
|
||||
|
||||
override fun dispatch(action: Action) {
|
||||
mainStore?.dispatch(action)
|
||||
}
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
|
|||
import com.tangem.domain.card.ScanCardProcessor
|
||||
import com.tangem.domain.card.ScanCardUseCase
|
||||
import com.tangem.domain.card.repository.CardSdkConfigRepository
|
||||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.features.tester.api.TesterRouter
|
||||
import com.tangem.features.tokendetails.featuretoggles.TokenDetailsFeatureToggles
|
||||
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
|
||||
|
|
@ -33,6 +34,7 @@ data class DaggerGraphState(
|
|||
val scanCardProcessor: ScanCardProcessor? = null,
|
||||
val cardSdkConfigRepository: CardSdkConfigRepository? = null,
|
||||
val appCurrencyRepository: AppCurrencyRepository? = null,
|
||||
val walletManagersFacade: WalletManagersFacade? = null,
|
||||
) : StateType {
|
||||
|
||||
inline fun <reified T> get(getDependency: DaggerGraphState.() -> T?): T {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,8 @@
|
|||
package com.tangem.domain.redux
|
||||
|
||||
import org.rekotlin.Action
|
||||
|
||||
interface ReduxStateHolder {
|
||||
|
||||
fun dispatch(action: Action)
|
||||
}
|
||||
|
|
@ -168,7 +168,7 @@ class DefaultWalletManagersFacade(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun getOrCreateWalletManager(
|
||||
override suspend fun getOrCreateWalletManager(
|
||||
userWallet: UserWallet,
|
||||
blockchain: Blockchain,
|
||||
derivationPath: DerivationPath?,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
package com.tangem.domain.walletmanager
|
||||
|
||||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.WalletManager
|
||||
import com.tangem.crypto.hdWallet.DerivationPath
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
import com.tangem.domain.tokens.models.Network
|
||||
import com.tangem.domain.txhistory.models.PaginationWrapper
|
||||
import com.tangem.domain.txhistory.models.TxHistoryItem
|
||||
import com.tangem.domain.txhistory.models.TxHistoryState
|
||||
import com.tangem.domain.walletmanager.model.UpdateWalletManagerResult
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
// TODO: Move to its own module
|
||||
|
|
@ -60,4 +64,10 @@ interface WalletManagersFacade {
|
|||
page: Int,
|
||||
pageSize: Int,
|
||||
): PaginationWrapper<TxHistoryItem>
|
||||
|
||||
suspend fun getOrCreateWalletManager(
|
||||
userWallet: UserWallet,
|
||||
blockchain: Blockchain,
|
||||
derivationPath: DerivationPath?,
|
||||
): WalletManager?
|
||||
}
|
||||
|
|
@ -7,6 +7,7 @@ dependencies {
|
|||
|
||||
/** Project - Domain */
|
||||
implementation(projects.domain.core)
|
||||
implementation(projects.domain.models)
|
||||
implementation(projects.domain.tokens.models)
|
||||
implementation(projects.domain.wallets.models)
|
||||
implementation(projects.domain.appCurrency.models)
|
||||
|
|
@ -16,6 +17,7 @@ dependencies {
|
|||
|
||||
/** Utils */
|
||||
implementation(deps.jodatime)
|
||||
implementation(deps.reKotlin)
|
||||
|
||||
/** Tests */
|
||||
testImplementation(deps.test.junit)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
package com.tangem.domain.tokens.legacy
|
||||
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import org.rekotlin.Action
|
||||
|
||||
sealed class TradeCryptoAction : Action {
|
||||
|
||||
data class Buy(val checkUserLocation: Boolean = true) : TradeCryptoAction()
|
||||
|
||||
object Sell : TradeCryptoAction()
|
||||
|
||||
data class SendCrypto(
|
||||
val currencyId: String,
|
||||
val amount: String,
|
||||
val destinationAddress: String,
|
||||
val transactionId: String,
|
||||
) : TradeCryptoAction()
|
||||
|
||||
data class FinishSelling(val transactionId: String) : TradeCryptoAction()
|
||||
|
||||
object Swap : TradeCryptoAction()
|
||||
|
||||
sealed class New : TradeCryptoAction() {
|
||||
|
||||
data class Buy(
|
||||
val userWallet: UserWallet,
|
||||
val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
val appCurrencyCode: String,
|
||||
val checkUserLocation: Boolean = true,
|
||||
) : New()
|
||||
|
||||
data class Sell(
|
||||
val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
val appCurrencyCode: String,
|
||||
) : New()
|
||||
|
||||
object Send : New()
|
||||
|
||||
data class Swap(val cryptoCurrency: CryptoCurrency) : New()
|
||||
}
|
||||
}
|
||||
|
|
@ -40,6 +40,9 @@ data class CryptoCurrencyStatus(
|
|||
|
||||
/** The pending cryptocurrency transactions. */
|
||||
open val pendingTransactions: Set<PendingTransaction> = emptySet()
|
||||
|
||||
/** The network address */
|
||||
open val networkAddress: NetworkAddress? = null
|
||||
}
|
||||
|
||||
/** Represents the Loading state of a token, typically while fetching its details. */
|
||||
|
|
@ -72,6 +75,7 @@ data class CryptoCurrencyStatus(
|
|||
override val priceChange: BigDecimal,
|
||||
override val hasCurrentNetworkTransactions: Boolean,
|
||||
override val pendingTransactions: Set<PendingTransaction>,
|
||||
override val networkAddress: NetworkAddress?,
|
||||
) : Status()
|
||||
|
||||
/**
|
||||
|
|
@ -92,6 +96,7 @@ data class CryptoCurrencyStatus(
|
|||
override val priceChange: BigDecimal?,
|
||||
override val hasCurrentNetworkTransactions: Boolean,
|
||||
override val pendingTransactions: Set<PendingTransaction>,
|
||||
override val networkAddress: NetworkAddress?,
|
||||
) : Status()
|
||||
|
||||
/**
|
||||
|
|
@ -106,5 +111,6 @@ data class CryptoCurrencyStatus(
|
|||
override val amount: BigDecimal,
|
||||
override val hasCurrentNetworkTransactions: Boolean,
|
||||
override val pendingTransactions: Set<PendingTransaction>,
|
||||
override val networkAddress: NetworkAddress?,
|
||||
) : Status()
|
||||
}
|
||||
|
|
@ -35,6 +35,7 @@ internal class CurrencyStatusOperations(
|
|||
amount = amount,
|
||||
hasCurrentNetworkTransactions = hasCurrentNetworkTransactions,
|
||||
pendingTransactions = currentTransactions,
|
||||
networkAddress = status.address,
|
||||
)
|
||||
currency is CryptoCurrency.Token && currency.isCustom -> CryptoCurrencyStatus.Custom(
|
||||
amount = amount,
|
||||
|
|
@ -43,6 +44,7 @@ internal class CurrencyStatusOperations(
|
|||
priceChange = quote?.priceChange,
|
||||
hasCurrentNetworkTransactions = hasCurrentNetworkTransactions,
|
||||
pendingTransactions = currentTransactions,
|
||||
networkAddress = status.address,
|
||||
)
|
||||
quote == null -> CryptoCurrencyStatus.Loading
|
||||
else -> CryptoCurrencyStatus.Loaded(
|
||||
|
|
@ -52,6 +54,7 @@ internal class CurrencyStatusOperations(
|
|||
priceChange = quote.priceChange,
|
||||
hasCurrentNetworkTransactions = hasCurrentNetworkTransactions,
|
||||
pendingTransactions = currentTransactions,
|
||||
networkAddress = status.address,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -85,16 +85,22 @@ internal object MockTokensStates {
|
|||
priceChange = quote.priceChange,
|
||||
pendingTransactions = emptySet(),
|
||||
hasCurrentNetworkTransactions = false,
|
||||
networkAddress = requireNotNull(networkStatus.value as? NetworkStatus.Verified).address,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
val noQuotesTokensStatuses = loadedTokensStates.map { currency ->
|
||||
currency.copy(
|
||||
val noQuotesTokensStatuses = loadedTokensStates.map { status ->
|
||||
status.copy(
|
||||
value = CryptoCurrencyStatus.NoQuote(
|
||||
amount = currency.value.amount!!,
|
||||
amount = status.value.amount!!,
|
||||
pendingTransactions = emptySet(),
|
||||
hasCurrentNetworkTransactions = false,
|
||||
networkAddress = requireNotNull(
|
||||
value = MockNetworks.verifiedNetworksStatuses
|
||||
.first { it.networkId == status.currency.network.id }
|
||||
.value as? NetworkStatus.Verified,
|
||||
).address,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@ dependencies {
|
|||
implementation(deps.arrow.core)
|
||||
implementation(deps.jodatime)
|
||||
implementation(deps.kotlin.immutable.collections)
|
||||
implementation(deps.reKotlin)
|
||||
implementation(deps.tangem.card.core)
|
||||
implementation(deps.tangem.blockchain)
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ internal class WalletCryptoCurrencyActionsConverter(
|
|||
WalletManageButton.Sell(enabled = action.enabled, onClick = clickIntents::onSellClick)
|
||||
}
|
||||
is TokenActionsState.ActionState.Send -> {
|
||||
WalletManageButton.Send(enabled = action.enabled, onClick = clickIntents::onSellClick)
|
||||
WalletManageButton.Send(enabled = action.enabled, onClick = clickIntents::onSendClick)
|
||||
}
|
||||
is TokenActionsState.ActionState.Swap -> null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,10 +16,13 @@ import com.tangem.domain.common.CardTypesResolver
|
|||
import com.tangem.domain.common.util.cardTypesResolver
|
||||
import com.tangem.domain.common.util.derivationStyleProvider
|
||||
import com.tangem.domain.demo.IsDemoCardUseCase
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.settings.IsUserAlreadyRateAppUseCase
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyActionsUseCase
|
||||
import com.tangem.domain.tokens.GetPrimaryCurrencyUseCase
|
||||
import com.tangem.domain.tokens.GetTokenListUseCase
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.TokenList
|
||||
import com.tangem.domain.tokens.models.CryptoCurrency
|
||||
import com.tangem.domain.tokens.models.Network
|
||||
|
|
@ -78,6 +81,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
private val unlockWalletsUseCase: UnlockWalletsUseCase,
|
||||
private val getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
|
||||
private val getCryptoCurrencyActionsUseCase: GetCryptoCurrencyActionsUseCase,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : ViewModel(), DefaultLifecycleObserver, WalletClickIntents {
|
||||
|
||||
|
|
@ -111,6 +115,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
var uiState: WalletState by uiStateHolder(initialState = stateFactory.getInitialState())
|
||||
|
||||
private var wallets: List<UserWallet> by Delegates.notNull()
|
||||
private var cryptoCurrencyStatus: CryptoCurrencyStatus? = null
|
||||
|
||||
private val tokensJobHolder = JobHolder()
|
||||
private val marketPriceJobHolder = JobHolder()
|
||||
|
|
@ -219,11 +224,13 @@ internal class WalletViewModel @Inject constructor(
|
|||
private fun updateMarketPrice(userWalletId: UserWalletId, isRefreshing: Boolean) {
|
||||
getPrimaryCurrencyUseCase(userWalletId = userWalletId)
|
||||
.distinctUntilChanged()
|
||||
.onEach {
|
||||
.onEach { either ->
|
||||
uiState = stateFactory.getSingleCurrencyLoadedBalanceState(
|
||||
cryptoCurrencyEither = it,
|
||||
cryptoCurrencyEither = either,
|
||||
isRefreshing = isRefreshing,
|
||||
)
|
||||
|
||||
either.onRight { status -> cryptoCurrencyStatus = status }
|
||||
}
|
||||
.flowOn(dispatchers.io)
|
||||
.launchIn(viewModelScope)
|
||||
|
|
@ -422,11 +429,21 @@ internal class WalletViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onBuyClick() {
|
||||
// TODO: [REDACTED_JIRA]
|
||||
val state = uiState as? WalletState.ContentState ?: return
|
||||
val status = cryptoCurrencyStatus ?: return
|
||||
val wallet = getWallet(index = state.walletsListConfig.selectedWalletIndex)
|
||||
|
||||
reduxStateHolder.dispatch(
|
||||
TradeCryptoAction.New.Buy(
|
||||
userWallet = wallet,
|
||||
cryptoCurrencyStatus = status,
|
||||
appCurrencyCode = selectedAppCurrencyFlow.value.code,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun onSendClick() {
|
||||
// TODO: [REDACTED_JIRA]
|
||||
reduxStateHolder.dispatch(TradeCryptoAction.New.Send)
|
||||
}
|
||||
|
||||
override fun onReceiveClick() {
|
||||
|
|
@ -434,7 +451,14 @@ internal class WalletViewModel @Inject constructor(
|
|||
}
|
||||
|
||||
override fun onSellClick() {
|
||||
// TODO: [REDACTED_JIRA]
|
||||
val status = cryptoCurrencyStatus ?: return
|
||||
|
||||
reduxStateHolder.dispatch(
|
||||
TradeCryptoAction.New.Sell(
|
||||
cryptoCurrencyStatus = status,
|
||||
appCurrencyCode = selectedAppCurrencyFlow.value.code,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun onReloadClick() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue