Updated on 2026-08-14
This commit is contained in:
parent
d995d1dd21
commit
07bf6c376c
20 changed files with 386 additions and 174 deletions
|
|
@ -58,8 +58,6 @@ import com.tangem.tap.domain.userWalletList.implementation.BiometricUserWalletsL
|
|||
import com.tangem.tap.domain.walletconnect2.domain.WalletConnectInteractor
|
||||
import com.tangem.tap.features.intentHandler.IntentProcessor
|
||||
import com.tangem.tap.features.intentHandler.handlers.BackgroundScanIntentHandler
|
||||
import com.tangem.tap.features.intentHandler.handlers.BuyCurrencyIntentHandler
|
||||
import com.tangem.tap.features.intentHandler.handlers.SellCurrencyIntentHandler
|
||||
import com.tangem.tap.features.intentHandler.handlers.WalletConnectLinkIntentHandler
|
||||
import com.tangem.tap.features.main.MainViewModel
|
||||
import com.tangem.tap.features.main.model.Toast
|
||||
|
|
@ -301,8 +299,6 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
|
|||
val hasSavedWalletsProvider = { store.state.globalState.userWalletsListManager?.hasUserWallets == true }
|
||||
intentProcessor.addHandler(BackgroundScanIntentHandler(hasSavedWalletsProvider, lifecycleScope))
|
||||
intentProcessor.addHandler(WalletConnectLinkIntentHandler())
|
||||
intentProcessor.addHandler(BuyCurrencyIntentHandler())
|
||||
intentProcessor.addHandler(SellCurrencyIntentHandler())
|
||||
}
|
||||
|
||||
private fun updateAppTheme(appThemeMode: AppThemeMode) {
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
package com.tangem.tap.features.intentHandler.handlers
|
||||
|
||||
import android.content.Intent
|
||||
import com.tangem.tap.features.intentHandler.IntentHandler
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class BuyCurrencyIntentHandler : IntentHandler {
|
||||
|
||||
override fun handleIntent(intent: Intent?): Boolean {
|
||||
// FIXME: [REDACTED_JIRA]
|
||||
// val data = intent?.data ?: return false
|
||||
// val currency = store.state.walletState.selectedCurrency ?: return false
|
||||
//
|
||||
// val successUri = Uri.parse(ExchangeUrlBuilder.SUCCESS_URL)
|
||||
// return if (data.host == successUri.host && data.authority == successUri.authority) {
|
||||
// val currencyType = AnalyticsParam.CurrencyType.Currency(currency)
|
||||
// Analytics.send(TokenScreenAnalyticsEvent.Bought(currencyType.value))
|
||||
// true
|
||||
// } else {
|
||||
// false
|
||||
// }
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
package com.tangem.tap.features.intentHandler.handlers
|
||||
|
||||
import android.content.Intent
|
||||
import com.tangem.tap.features.intentHandler.IntentHandler
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class SellCurrencyIntentHandler : IntentHandler {
|
||||
|
||||
override fun handleIntent(intent: Intent?): Boolean {
|
||||
// FIXME: [REDACTED_JIRA]
|
||||
// return try {
|
||||
// val intentData = intent?.data ?: return false
|
||||
// val transactionID = intentData.getQueryParameter(TRANSACTION_ID_PARAM) ?: return false
|
||||
// val currency = intentData.getQueryParameter(CURRENCY_CODE_PARAM) ?: return false
|
||||
// val amount = intentData.getQueryParameter(CURRENCY_AMOUNT_PARAM) ?: return false
|
||||
// val destinationAddress = intentData.getQueryParameter(DEPOSIT_WALLET_ADDRESS_PARAM) ?: return false
|
||||
//
|
||||
// Timber.d("MoonPay Sell: $amount $currency to $destinationAddress")
|
||||
// store.dispatchOnMain(
|
||||
// TradeCryptoAction.SendCrypto(
|
||||
// currencyId = currency,
|
||||
// amount = amount,
|
||||
// destinationAddress = destinationAddress,
|
||||
// transactionId = transactionID,
|
||||
// ),
|
||||
// )
|
||||
// true
|
||||
// } catch (exception: Exception) {
|
||||
// Timber.d("Not MoonPay URL")
|
||||
// false
|
||||
// }
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// private companion object {
|
||||
// private const val TRANSACTION_ID_PARAM = "transactionId"
|
||||
// private const val CURRENCY_CODE_PARAM = "baseCurrencyCode"
|
||||
// private const val CURRENCY_AMOUNT_PARAM = "baseCurrencyAmount"
|
||||
// private const val DEPOSIT_WALLET_ADDRESS_PARAM = "depositWalletAddress"
|
||||
// }
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ import com.tangem.tap.domain.TapError
|
|||
import com.tangem.tap.features.demo.DemoHelper
|
||||
import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE
|
||||
import com.tangem.tap.features.send.redux.PrepareSendScreen
|
||||
import com.tangem.tap.features.send.redux.SendAction
|
||||
import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager
|
||||
import com.tangem.tap.network.exchangeServices.buyErc20TestnetTokens
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
|
|
@ -50,19 +51,18 @@ object TradeCryptoMiddleware {
|
|||
if (DemoHelper.tryHandle(state, action)) return
|
||||
|
||||
when (action) {
|
||||
is TradeCryptoAction.SendCrypto -> preconfigureAndOpenSendScreen()
|
||||
is TradeCryptoAction.FinishSelling -> openReceiptUrl(action.transactionId)
|
||||
is TradeCryptoAction.New.Buy -> proceedNewBuyAction(state, action)
|
||||
is TradeCryptoAction.New.Sell -> proceedNewSellAction(action)
|
||||
is TradeCryptoAction.New.Swap -> openSwap(
|
||||
is TradeCryptoAction.Buy -> proceedBuyAction(state, action)
|
||||
is TradeCryptoAction.Sell -> proceedSellAction(action)
|
||||
is TradeCryptoAction.Swap -> openSwap(
|
||||
currency = action.cryptoCurrency,
|
||||
)
|
||||
is TradeCryptoAction.New.SendToken -> handleNewSendToken(action = action)
|
||||
is TradeCryptoAction.New.SendCoin -> handleNewSendCoin(action = action)
|
||||
is TradeCryptoAction.SendToken -> handleSendToken(action = action)
|
||||
is TradeCryptoAction.SendCoin -> handleSendCoin(action = action)
|
||||
}
|
||||
}
|
||||
|
||||
private fun proceedNewBuyAction(state: () -> AppState?, action: TradeCryptoAction.New.Buy) {
|
||||
private fun proceedBuyAction(state: () -> AppState?, action: TradeCryptoAction.Buy) {
|
||||
val networkAddress = action.cryptoCurrencyStatus.value.networkAddress
|
||||
?.defaultAddress
|
||||
?.let(NetworkAddress.Address::value)
|
||||
|
|
@ -119,7 +119,7 @@ object TradeCryptoMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun proceedNewSellAction(action: TradeCryptoAction.New.Sell) {
|
||||
private fun proceedSellAction(action: TradeCryptoAction.Sell) {
|
||||
val networkAddress = action.cryptoCurrencyStatus.value.networkAddress
|
||||
?.defaultAddress
|
||||
?.let(NetworkAddress.Address::value)
|
||||
|
|
@ -139,33 +139,6 @@ object TradeCryptoMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun preconfigureAndOpenSendScreen() = scope.launch {
|
||||
// FIXME: [REDACTED_JIRA]
|
||||
// val selectedWalletData = store.state.walletState.selectedWalletData ?: return
|
||||
//
|
||||
// Analytics.send(Token.ButtonSend(AnalyticsParam.CurrencyType.Currency(selectedWalletData.currency)))
|
||||
// val walletManager = store.state.walletState.getWalletManager(selectedWalletData.currency).guard {
|
||||
// FirebaseCrashlytics.getInstance().recordException(IllegalStateException("WalletManager is null"))
|
||||
// return
|
||||
// }
|
||||
//
|
||||
// store.dispatchOnMain(
|
||||
// PrepareSendScreen(
|
||||
// walletManager = walletManager,
|
||||
// coinAmount = walletManager.wallet.amounts[AmountType.Coin],
|
||||
// coinRate = selectedWalletData.fiatRate,
|
||||
// ),
|
||||
// )
|
||||
// store.dispatchOnMain(
|
||||
// SendAction.SendSpecificTransaction(
|
||||
// sendAmount = action.amount,
|
||||
// destinationAddress = action.destinationAddress,
|
||||
// transactionId = action.transactionId,
|
||||
// ),
|
||||
// )
|
||||
// store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Send))
|
||||
}
|
||||
|
||||
private fun openReceiptUrl(transactionId: String) {
|
||||
store.dispatchOnMain(NavigationAction.PopBackTo())
|
||||
store.state.globalState.exchangeManager.getSellCryptoReceiptUrl(
|
||||
|
|
@ -182,7 +155,7 @@ object TradeCryptoMiddleware {
|
|||
store.dispatchOnMain(NavigationAction.NavigateTo(screen = AppScreen.Swap, bundle = bundle))
|
||||
}
|
||||
|
||||
private fun handleNewSendToken(action: TradeCryptoAction.New.SendToken) {
|
||||
private fun handleSendToken(action: TradeCryptoAction.SendToken) {
|
||||
val currency = action.tokenCurrency
|
||||
val blockchain = Blockchain.fromId(currency.network.id.value)
|
||||
|
||||
|
|
@ -221,6 +194,17 @@ object TradeCryptoMiddleware {
|
|||
),
|
||||
)
|
||||
|
||||
val txInfo = action.transactionInfo
|
||||
if (txInfo != null) {
|
||||
store.dispatchOnMain(
|
||||
SendAction.SendSpecificTransaction(
|
||||
sendAmount = txInfo.amount,
|
||||
destinationAddress = txInfo.destinationAddress,
|
||||
transactionId = txInfo.transactionId,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
val bundle = bundleOf(
|
||||
SendRouter.CRYPTO_CURRENCY_KEY to currency,
|
||||
SendRouter.USER_WALLET_ID_KEY to action.userWallet.walletId.stringValue,
|
||||
|
|
@ -229,7 +213,7 @@ object TradeCryptoMiddleware {
|
|||
}
|
||||
}
|
||||
|
||||
private fun handleNewSendCoin(action: TradeCryptoAction.New.SendCoin) {
|
||||
private fun handleSendCoin(action: TradeCryptoAction.SendCoin) {
|
||||
val cryptoStatus = action.coinStatus
|
||||
val currency = cryptoStatus.currency
|
||||
val blockchain = Blockchain.fromId(currency.network.id.value)
|
||||
|
|
@ -278,6 +262,17 @@ object TradeCryptoMiddleware {
|
|||
is CryptoCurrency.Token -> error("Action.tokenStatus.currency is Token")
|
||||
}
|
||||
|
||||
val txInfo = action.transactionInfo
|
||||
if (txInfo != null) {
|
||||
store.dispatchOnMain(
|
||||
SendAction.SendSpecificTransaction(
|
||||
sendAmount = txInfo.amount,
|
||||
destinationAddress = txInfo.destinationAddress,
|
||||
transactionId = txInfo.transactionId,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
val bundle = bundleOf(
|
||||
SendRouter.CRYPTO_CURRENCY_KEY to currency,
|
||||
SendRouter.USER_WALLET_ID_KEY to action.userWallet.walletId.stringValue,
|
||||
|
|
|
|||
1
core/deep-links/global/.gitignore
vendored
Normal file
1
core/deep-links/global/.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
/build
|
||||
15
core/deep-links/global/build.gradle.kts
Normal file
15
core/deep-links/global/build.gradle.kts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
plugins {
|
||||
alias(deps.plugins.android.library)
|
||||
alias(deps.plugins.kotlin.android)
|
||||
id("configuration")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.tangem.core.deeplink.global"
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
/* Project */
|
||||
implementation(projects.core.deepLinks)
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package com.tangem.core.deeplink.global
|
||||
|
||||
import com.tangem.core.deeplink.DeepLink
|
||||
|
||||
class BuyCurrencyDeepLink(val onReceive: () -> Unit) : DeepLink {
|
||||
|
||||
override val uri: String = "tangem://success.tangem.com"
|
||||
|
||||
override fun onReceive(params: Map<String, String>) {
|
||||
onReceive()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
package com.tangem.core.deeplink.global
|
||||
|
||||
import com.tangem.core.deeplink.DeepLink
|
||||
|
||||
class SellCurrencyDeepLink(val onReceive: (data: Data) -> Unit) : DeepLink {
|
||||
|
||||
override val uri: String = "tangem://sell-request.tangem.com"
|
||||
|
||||
override fun onReceive(params: Map<String, String>) {
|
||||
val data = Data(
|
||||
transactionId = params["transactionId"] ?: return,
|
||||
baseCurrencyAmount = params["baseCurrencyAmount"] ?: return,
|
||||
depositWalletAddress = params["depositWalletAddress"] ?: return,
|
||||
)
|
||||
|
||||
onReceive(data)
|
||||
}
|
||||
|
||||
data class Data(
|
||||
val transactionId: String,
|
||||
val baseCurrencyAmount: String,
|
||||
val depositWalletAddress: String,
|
||||
)
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
package com.tangem.domain.tokens
|
||||
|
||||
import arrow.core.Either
|
||||
import com.tangem.domain.tokens.error.TokenListError
|
||||
import com.tangem.domain.tokens.error.mapper.mapToTokenListError
|
||||
import com.tangem.domain.tokens.error.CurrencyStatusError
|
||||
import com.tangem.domain.tokens.error.mapper.mapToCurrencyError
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.operations.CurrenciesStatusesOperations
|
||||
|
|
@ -22,7 +22,7 @@ class GetCryptoCurrencyStatusSyncUseCase(
|
|||
suspend operator fun invoke(
|
||||
userWalletId: UserWalletId,
|
||||
cryptoCurrencyId: CryptoCurrency.ID,
|
||||
): Either<TokenListError, CryptoCurrencyStatus> {
|
||||
): Either<CurrencyStatusError, CryptoCurrencyStatus> {
|
||||
val operations = CurrenciesStatusesOperations(
|
||||
userWalletId = userWalletId,
|
||||
currenciesRepository = currenciesRepository,
|
||||
|
|
@ -31,6 +31,18 @@ class GetCryptoCurrencyStatusSyncUseCase(
|
|||
)
|
||||
|
||||
return operations.getCurrencyStatusSync(cryptoCurrencyId)
|
||||
.mapLeft { error -> error.mapToTokenListError() }
|
||||
.mapLeft { error -> error.mapToCurrencyError() }
|
||||
}
|
||||
|
||||
suspend operator fun invoke(userWalletId: UserWalletId): Either<CurrencyStatusError, CryptoCurrencyStatus> {
|
||||
val operations = CurrenciesStatusesOperations(
|
||||
userWalletId = userWalletId,
|
||||
currenciesRepository = currenciesRepository,
|
||||
quotesRepository = quotesRepository,
|
||||
networksRepository = networksRepository,
|
||||
)
|
||||
|
||||
return operations.getPrimaryCurrencyStatusSync()
|
||||
.mapLeft { error -> error.mapToCurrencyError() }
|
||||
}
|
||||
}
|
||||
|
|
@ -8,28 +8,19 @@ import java.math.BigDecimal
|
|||
|
||||
sealed class TradeCryptoAction : Action {
|
||||
|
||||
data class SendCrypto(
|
||||
val currencyId: String,
|
||||
val amount: String,
|
||||
val destinationAddress: String,
|
||||
val transactionId: String,
|
||||
) : TradeCryptoAction()
|
||||
|
||||
data class FinishSelling(val transactionId: String) : TradeCryptoAction()
|
||||
|
||||
sealed class New : TradeCryptoAction() {
|
||||
|
||||
data class Buy(
|
||||
val userWallet: UserWallet,
|
||||
val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
val appCurrencyCode: String,
|
||||
val checkUserLocation: Boolean = true,
|
||||
) : New()
|
||||
) : TradeCryptoAction()
|
||||
|
||||
data class Sell(
|
||||
val cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
val appCurrencyCode: String,
|
||||
) : New()
|
||||
) : TradeCryptoAction()
|
||||
|
||||
data class SendToken(
|
||||
val userWallet: UserWallet,
|
||||
|
|
@ -37,14 +28,21 @@ sealed class TradeCryptoAction : Action {
|
|||
val tokenFiatRate: BigDecimal?,
|
||||
val coinFiatRate: BigDecimal?,
|
||||
val feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
) : New()
|
||||
val transactionInfo: TransactionInfo? = null,
|
||||
) : TradeCryptoAction()
|
||||
|
||||
data class SendCoin(
|
||||
val userWallet: UserWallet,
|
||||
val coinStatus: CryptoCurrencyStatus,
|
||||
val feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
) : New()
|
||||
val transactionInfo: TransactionInfo? = null,
|
||||
) : TradeCryptoAction()
|
||||
|
||||
data class Swap(val cryptoCurrency: CryptoCurrency) : New()
|
||||
}
|
||||
data class Swap(val cryptoCurrency: CryptoCurrency) : TradeCryptoAction()
|
||||
|
||||
data class TransactionInfo(
|
||||
val amount: String,
|
||||
val destinationAddress: String,
|
||||
val transactionId: String,
|
||||
)
|
||||
}
|
||||
|
|
@ -11,6 +11,8 @@ import com.tangem.domain.wallets.models.UserWalletId
|
|||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
// FIXME: Refactor - [REDACTED_JIRA]
|
||||
@Suppress("LargeClass")
|
||||
internal class CurrenciesStatusesOperations(
|
||||
private val currenciesRepository: CurrenciesRepository,
|
||||
private val quotesRepository: QuotesRepository,
|
||||
|
|
@ -107,6 +109,27 @@ internal class CurrenciesStatusesOperations(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun getPrimaryCurrencyStatusSync(): Either<Error, CryptoCurrencyStatus> = either {
|
||||
val currency = catch(
|
||||
block = { currenciesRepository.getSingleCurrencyWalletPrimaryCurrency(userWalletId) },
|
||||
catch = { raise(Error.DataError(it)) },
|
||||
)
|
||||
val quotes = catch(
|
||||
block = { quotesRepository.getQuoteSync(currency.id).right() },
|
||||
catch = { Error.DataError(it).left() },
|
||||
)
|
||||
val networkStatus = catch(
|
||||
block = {
|
||||
networksRepository.getNetworkStatusesSync(userWalletId, setOf(currency.network))
|
||||
.firstOrNull { it.network == currency.network }
|
||||
.right()
|
||||
},
|
||||
catch = { Error.DataError(it).left() },
|
||||
)
|
||||
|
||||
return createCurrencyStatus(currency, quotes, networkStatus)
|
||||
}
|
||||
|
||||
fun getCardCurrenciesStatusesFlow(): Flow<Either<Error, List<CryptoCurrencyStatus>>> {
|
||||
return flow {
|
||||
val nonEmptyCurrencies = recover(
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ interface NetworksRepository {
|
|||
suspend fun getNetworkStatusesSync(
|
||||
userWalletId: UserWalletId,
|
||||
networks: Set<Network>,
|
||||
refresh: Boolean,
|
||||
refresh: Boolean = false,
|
||||
): Set<NetworkStatus>
|
||||
|
||||
fun isNeedToCreateAccountWithoutReserve(network: Network): Boolean
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ dependencies {
|
|||
implementation(projects.core.analytics)
|
||||
implementation(projects.core.analytics.models)
|
||||
implementation(projects.core.datasource)
|
||||
implementation(projects.core.deepLinks)
|
||||
implementation(projects.core.deepLinks.global)
|
||||
|
||||
/** Domain modules */
|
||||
implementation(projects.domain.appCurrency)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@ import arrow.core.getOrElse
|
|||
import com.tangem.blockchain.common.Blockchain
|
||||
import com.tangem.blockchain.common.address.AddressType
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.deeplink.DeepLinksRegistry
|
||||
import com.tangem.core.deeplink.global.BuyCurrencyDeepLink
|
||||
import com.tangem.core.deeplink.global.SellCurrencyDeepLink
|
||||
import com.tangem.core.ui.components.bottomsheets.tokenreceive.AddressModel
|
||||
import com.tangem.core.ui.components.transactions.state.TxHistoryState
|
||||
import com.tangem.core.ui.extensions.resourceReference
|
||||
|
|
@ -22,6 +25,7 @@ import com.tangem.domain.redux.ReduxStateHolder
|
|||
import com.tangem.domain.settings.ShouldShowSwapPromoTokenUseCase
|
||||
import com.tangem.domain.tokens.*
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction.TransactionInfo
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.model.NetworkAddress
|
||||
|
|
@ -87,6 +91,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
private val isDemoCardUseCase: IsDemoCardUseCase,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val analyticsEventsHandler: AnalyticsEventHandler,
|
||||
deepLinksRegistry: DeepLinksRegistry,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel(), DefaultLifecycleObserver, TokenDetailsClickIntents {
|
||||
|
||||
|
|
@ -148,6 +153,34 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
var uiState: TokenDetailsState by mutableStateOf(stateFactory.getInitialState(cryptoCurrency))
|
||||
private set
|
||||
|
||||
init {
|
||||
deepLinksRegistry.registerWithViewModel(
|
||||
viewModel = this,
|
||||
deepLinks = listOf(
|
||||
BuyCurrencyDeepLink(::onBuyCurrencyDeepLink),
|
||||
SellCurrencyDeepLink(::onSellCurrencyDeepLink),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private fun onBuyCurrencyDeepLink() {
|
||||
val currency = cryptoCurrencyStatus?.currency ?: return
|
||||
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.Bought(currency.symbol))
|
||||
}
|
||||
|
||||
private fun onSellCurrencyDeepLink(data: SellCurrencyDeepLink.Data) {
|
||||
sendCurrency(
|
||||
status = cryptoCurrencyStatus ?: return,
|
||||
transactionInfo = data.let {
|
||||
TransactionInfo(
|
||||
amount = it.baseCurrencyAmount,
|
||||
transactionId = it.transactionId,
|
||||
destinationAddress = it.depositWalletAddress,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
analyticsEventsHandler.send(
|
||||
event = TokenScreenAnalyticsEvent.DetailsScreenOpened(token = cryptoCurrency.symbol),
|
||||
|
|
@ -330,7 +363,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
reduxStateHolder.dispatch(
|
||||
TradeCryptoAction.New.Buy(
|
||||
TradeCryptoAction.Buy(
|
||||
userWallet = getUserWalletUseCase(userWalletId).getOrElse { return@launch },
|
||||
cryptoCurrencyStatus = status,
|
||||
appCurrencyCode = selectedAppCurrencyFlow.value.code,
|
||||
|
|
@ -354,27 +387,31 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
override fun onSendClick() {
|
||||
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonSend(cryptoCurrency.symbol))
|
||||
|
||||
val cryptoCurrencyStatus = cryptoCurrencyStatus ?: return
|
||||
sendCurrency(status = cryptoCurrencyStatus ?: return)
|
||||
}
|
||||
|
||||
private fun sendCurrency(status: CryptoCurrencyStatus, transactionInfo: TransactionInfo? = null) {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
val maybeFeeCurrencyStatus =
|
||||
getFeePaidCryptoCurrencyStatusSyncUseCase(userWalletId, cryptoCurrencyStatus).getOrNull()
|
||||
getFeePaidCryptoCurrencyStatusSyncUseCase(userWalletId, status).getOrNull()
|
||||
|
||||
when (val currency = cryptoCurrencyStatus.currency) {
|
||||
when (val currency = status.currency) {
|
||||
is CryptoCurrency.Coin -> {
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.New.SendCoin(
|
||||
action = TradeCryptoAction.SendCoin(
|
||||
userWallet = getUserWalletUseCase(userWalletId).getOrElse { return@launch },
|
||||
coinStatus = cryptoCurrencyStatus,
|
||||
coinStatus = status,
|
||||
feeCurrencyStatus = maybeFeeCurrencyStatus,
|
||||
transactionInfo = transactionInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
is CryptoCurrency.Token -> {
|
||||
sendToken(
|
||||
tokenCurrency = currency,
|
||||
tokenFiatRate = cryptoCurrencyStatus.value.fiatRate,
|
||||
tokenFiatRate = status.value.fiatRate,
|
||||
feeCurrencyStatus = maybeFeeCurrencyStatus,
|
||||
transactionInfo = transactionInfo,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -385,6 +422,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
tokenCurrency: CryptoCurrency.Token,
|
||||
tokenFiatRate: BigDecimal?,
|
||||
feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
transactionInfo: TransactionInfo?,
|
||||
) {
|
||||
viewModelScope.launch(dispatchers.io) {
|
||||
val wallet = getUserWalletUseCase(userWalletId).getOrElse { return@launch }
|
||||
|
|
@ -399,7 +437,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
.firstOrNull()
|
||||
|
||||
reduxStateHolder.dispatchWithMain(
|
||||
action = TradeCryptoAction.New.SendToken(
|
||||
action = TradeCryptoAction.SendToken(
|
||||
userWallet = wallet,
|
||||
tokenCurrency = tokenCurrency,
|
||||
tokenFiatRate = tokenFiatRate,
|
||||
|
|
@ -408,6 +446,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
ifRight = { it.value.fiatRate },
|
||||
),
|
||||
feeCurrencyStatus = feeCurrencyStatus,
|
||||
transactionInfo = transactionInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
@ -440,7 +479,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
val status = cryptoCurrencyStatus ?: return@showErrorIfDemoModeOrElse
|
||||
|
||||
reduxStateHolder.dispatch(
|
||||
TradeCryptoAction.New.Sell(
|
||||
TradeCryptoAction.Sell(
|
||||
cryptoCurrencyStatus = status,
|
||||
appCurrencyCode = selectedAppCurrencyFlow.value.code,
|
||||
),
|
||||
|
|
@ -451,7 +490,7 @@ internal class TokenDetailsViewModel @Inject constructor(
|
|||
override fun onSwapClick() {
|
||||
analyticsEventsHandler.send(TokenScreenAnalyticsEvent.ButtonExchange(cryptoCurrency.symbol))
|
||||
|
||||
reduxStateHolder.dispatch(TradeCryptoAction.New.Swap(cryptoCurrency))
|
||||
reduxStateHolder.dispatch(TradeCryptoAction.Swap(cryptoCurrency))
|
||||
}
|
||||
|
||||
override fun onDismissDialog() {
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ dependencies {
|
|||
implementation(projects.core.analytics)
|
||||
implementation(projects.core.analytics.models)
|
||||
implementation(projects.common)
|
||||
implementation(projects.core.deepLinks)
|
||||
implementation(projects.core.deepLinks.global)
|
||||
|
||||
/** Domain modules */
|
||||
implementation(projects.domain.card)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,156 @@
|
|||
package com.tangem.feature.wallet.presentation.deeplink
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.deeplink.DeepLink
|
||||
import com.tangem.core.deeplink.DeepLinksRegistry
|
||||
import com.tangem.core.deeplink.global.BuyCurrencyDeepLink
|
||||
import com.tangem.core.deeplink.global.SellCurrencyDeepLink
|
||||
import com.tangem.domain.redux.ReduxStateHolder
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyStatusSyncUseCase
|
||||
import com.tangem.domain.tokens.GetCryptoCurrencyUseCase
|
||||
import com.tangem.domain.tokens.GetFeePaidCryptoCurrencyStatusSyncUseCase
|
||||
import com.tangem.domain.tokens.GetNetworkCoinStatusUseCase
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction
|
||||
import com.tangem.domain.tokens.legacy.TradeCryptoAction.TransactionInfo
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.tokens.model.CryptoCurrencyStatus
|
||||
import com.tangem.domain.tokens.models.analytics.TokenScreenAnalyticsEvent
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@Suppress("LongParameterList")
|
||||
internal class WalletDeepLinksHandler @Inject constructor(
|
||||
private val deepLinksRegistry: DeepLinksRegistry,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val getCryptoCurrencyUseCase: GetCryptoCurrencyUseCase,
|
||||
private val getCryptoCurrencyStatusSyncUseCase: GetCryptoCurrencyStatusSyncUseCase,
|
||||
private val getFeePaidCryptoCurrencyStatusSyncUseCase: GetFeePaidCryptoCurrencyStatusSyncUseCase,
|
||||
private val getNetworkCoinStatusUseCase: GetNetworkCoinStatusUseCase,
|
||||
private val reduxStateHolder: ReduxStateHolder,
|
||||
) {
|
||||
|
||||
private var deepLinks: List<DeepLink> = emptyList()
|
||||
|
||||
fun registerForSingleCurrencyWallets(viewModel: ViewModel, userWallet: UserWallet) {
|
||||
if (userWallet.isMultiCurrency) {
|
||||
deepLinksRegistry.unregister(deepLinks)
|
||||
} else {
|
||||
if (deepLinks.isEmpty()) {
|
||||
deepLinks = getDeepLinks(userWallet, viewModel.viewModelScope)
|
||||
}
|
||||
|
||||
deepLinksRegistry.register(deepLinks)
|
||||
}
|
||||
|
||||
viewModel.addCloseable {
|
||||
deepLinksRegistry.unregister(deepLinks)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDeepLinks(userWallet: UserWallet, scope: CoroutineScope): List<DeepLink> {
|
||||
val sellCurrencyDeepLink = SellCurrencyDeepLink(
|
||||
onReceive = { data ->
|
||||
scope.launch {
|
||||
onSellCurrencyDeepLink(userWallet, data)
|
||||
}
|
||||
},
|
||||
)
|
||||
val buyCurrencyDeepLink = BuyCurrencyDeepLink(
|
||||
onReceive = {
|
||||
scope.launch {
|
||||
onBuyCurrencyDeepLink(userWallet)
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
return listOf(sellCurrencyDeepLink, buyCurrencyDeepLink)
|
||||
}
|
||||
|
||||
private suspend fun onSellCurrencyDeepLink(userWallet: UserWallet, data: SellCurrencyDeepLink.Data) {
|
||||
val cryptoCurrencyStatus = getCryptoCurrencyStatusSyncUseCase(userWallet.walletId)
|
||||
.getOrNull() ?: return
|
||||
val feeCurrencyStatus = getFeePaidCryptoCurrencyStatusSyncUseCase(
|
||||
userWallet.walletId,
|
||||
cryptoCurrencyStatus,
|
||||
).getOrNull()
|
||||
|
||||
val transactionInfo = data.let {
|
||||
TransactionInfo(
|
||||
amount = it.baseCurrencyAmount,
|
||||
destinationAddress = it.depositWalletAddress,
|
||||
transactionId = it.transactionId,
|
||||
)
|
||||
}
|
||||
|
||||
when (cryptoCurrencyStatus.currency) {
|
||||
is CryptoCurrency.Coin -> sendCoin(
|
||||
userWallet = userWallet,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
feeCurrencyStatus = feeCurrencyStatus,
|
||||
transactionInfo = transactionInfo,
|
||||
)
|
||||
is CryptoCurrency.Token -> sendToken(
|
||||
userWallet = userWallet,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
feeCurrencyStatus = feeCurrencyStatus,
|
||||
transactionInfo = transactionInfo,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun onBuyCurrencyDeepLink(userWallet: UserWallet) {
|
||||
val cryptoCurrency = getCryptoCurrencyUseCase(userWallet.walletId).getOrNull() ?: return
|
||||
|
||||
analyticsEventHandler.send(TokenScreenAnalyticsEvent.Bought(cryptoCurrency.symbol))
|
||||
}
|
||||
|
||||
private fun sendCoin(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
transactionInfo: TransactionInfo,
|
||||
) {
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.SendCoin(
|
||||
userWallet = userWallet,
|
||||
coinStatus = cryptoCurrencyStatus,
|
||||
feeCurrencyStatus = feeCurrencyStatus,
|
||||
transactionInfo = transactionInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun sendToken(
|
||||
userWallet: UserWallet,
|
||||
cryptoCurrencyStatus: CryptoCurrencyStatus,
|
||||
feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
transactionInfo: TransactionInfo,
|
||||
) {
|
||||
val cryptoCurrency = cryptoCurrencyStatus.currency as? CryptoCurrency.Token ?: return
|
||||
val coinStatus = getNetworkCoinStatusUseCase(
|
||||
userWalletId = userWallet.walletId,
|
||||
networkId = cryptoCurrency.network.id,
|
||||
derivationPath = cryptoCurrency.network.derivationPath,
|
||||
isSingleWalletWithTokens = false,
|
||||
)
|
||||
.firstOrNull()
|
||||
?.getOrNull()
|
||||
?: return
|
||||
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.SendToken(
|
||||
userWallet = userWallet,
|
||||
tokenCurrency = cryptoCurrency,
|
||||
tokenFiatRate = cryptoCurrencyStatus.value.fiatRate,
|
||||
coinFiatRate = coinStatus.value.fiatRate,
|
||||
feeCurrencyStatus = feeCurrencyStatus,
|
||||
transactionInfo = transactionInfo,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -609,7 +609,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
|
||||
showErrorIfDemoModeOrElse {
|
||||
reduxStateHolder.dispatch(
|
||||
TradeCryptoAction.New.Buy(
|
||||
TradeCryptoAction.Buy(
|
||||
userWallet = getWallet(index = state.walletsListConfig.selectedWalletIndex),
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
appCurrencyCode = selectedAppCurrencyFlow.value.code,
|
||||
|
|
@ -623,7 +623,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
event = TokenScreenAnalyticsEvent.ButtonExchange(cryptoCurrencyStatus.currency.symbol),
|
||||
)
|
||||
|
||||
reduxStateHolder.dispatch(TradeCryptoAction.New.Swap(cryptoCurrencyStatus.currency))
|
||||
reduxStateHolder.dispatch(TradeCryptoAction.Swap(cryptoCurrencyStatus.currency))
|
||||
}
|
||||
|
||||
override fun onSingleCurrencySendClick(cryptoCurrencyStatus: CryptoCurrencyStatus?) {
|
||||
|
|
@ -641,7 +641,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
val maybeFeeCurrencyStatus =
|
||||
getFeePaidCryptoCurrencyStatusSyncUseCase(userWallet.walletId, coinStatus).getOrNull()
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.New.SendCoin(
|
||||
action = TradeCryptoAction.SendCoin(
|
||||
userWallet = userWallet,
|
||||
coinStatus = coinStatus,
|
||||
feeCurrencyStatus = maybeFeeCurrencyStatus,
|
||||
|
|
@ -665,7 +665,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
is CryptoCurrency.Coin -> {
|
||||
uiState = stateFactory.getStateWithClosedBottomSheet()
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.New.SendCoin(
|
||||
action = TradeCryptoAction.SendCoin(
|
||||
userWallet = userWallet,
|
||||
coinStatus = cryptoCurrencyStatus,
|
||||
feeCurrencyStatus = maybeFeeCurrencyStatus,
|
||||
|
|
@ -694,7 +694,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
it.onRight { coinStatus ->
|
||||
uiState = stateFactory.getStateWithClosedBottomSheet()
|
||||
reduxStateHolder.dispatchWithMain(
|
||||
action = TradeCryptoAction.New.SendToken(
|
||||
action = TradeCryptoAction.SendToken(
|
||||
userWallet = userWallet,
|
||||
tokenCurrency = requireNotNull(cryptoCurrencyStatus.currency as? CryptoCurrency.Token),
|
||||
tokenFiatRate = cryptoCurrencyStatus.value.fiatRate,
|
||||
|
|
@ -772,7 +772,7 @@ internal class WalletViewModel @Inject constructor(
|
|||
|
||||
showErrorIfDemoModeOrElse {
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.New.Sell(
|
||||
action = TradeCryptoAction.Sell(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
appCurrencyCode = selectedAppCurrencyFlow.value.code,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import com.tangem.domain.walletconnect.WalletConnectActions
|
|||
import com.tangem.domain.wallets.usecase.GetSelectedWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.domain.wallets.usecase.ShouldSaveUserWalletsUseCase
|
||||
import com.tangem.feature.wallet.presentation.deeplink.WalletDeepLinksHandler
|
||||
import com.tangem.feature.wallet.presentation.router.InnerWalletRouter
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.WalletScreenAnalyticsEvent
|
||||
import com.tangem.feature.wallet.presentation.wallet.analytics.utils.SelectedWalletAnalyticsSender
|
||||
|
|
@ -57,6 +58,7 @@ internal class WalletViewModelV2 @Inject constructor(
|
|||
private val reduxStateHolder: ReduxStateHolder,
|
||||
private val screenLifecycleProvider: ScreenLifecycleProvider,
|
||||
private val selectedWalletAnalyticsSender: SelectedWalletAnalyticsSender,
|
||||
private val walletDeepLinksHandler: WalletDeepLinksHandler,
|
||||
) : ViewModel() {
|
||||
|
||||
val uiState: StateFlow<WalletScreenState> = stateHolder.uiState
|
||||
|
|
@ -157,6 +159,11 @@ internal class WalletViewModelV2 @Inject constructor(
|
|||
|
||||
selectedWalletAnalyticsSender.send(selectedWallet)
|
||||
}
|
||||
|
||||
walletDeepLinksHandler.registerForSingleCurrencyWallets(
|
||||
viewModel = this,
|
||||
userWallet = selectedWallet,
|
||||
)
|
||||
}
|
||||
.flowOn(dispatchers.main)
|
||||
.launchIn(viewModelScope)
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
feeCurrencyStatus: CryptoCurrencyStatus?,
|
||||
) {
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.New.SendCoin(
|
||||
action = TradeCryptoAction.SendCoin(
|
||||
userWallet = userWallet,
|
||||
coinStatus = cryptoCurrencyStatus,
|
||||
feeCurrencyStatus = feeCurrencyStatus,
|
||||
|
|
@ -135,7 +135,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
.collectLatest {
|
||||
it.onRight { coinStatus ->
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.New.SendToken(
|
||||
action = TradeCryptoAction.SendToken(
|
||||
userWallet = userWallet,
|
||||
tokenCurrency = cryptoCurrency,
|
||||
tokenFiatRate = cryptoCurrencyStatus.fiatRate,
|
||||
|
|
@ -281,7 +281,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
showErrorIfDemoModeOrElse {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
reduxStateHolder.dispatch(
|
||||
action = TradeCryptoAction.New.Sell(
|
||||
action = TradeCryptoAction.Sell(
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
appCurrencyCode = getSelectedAppCurrencyUseCase.unwrap().code,
|
||||
),
|
||||
|
|
@ -300,7 +300,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
showErrorIfDemoModeOrElse {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
reduxStateHolder.dispatch(
|
||||
TradeCryptoAction.New.Buy(
|
||||
TradeCryptoAction.Buy(
|
||||
userWallet = userWallet,
|
||||
cryptoCurrencyStatus = cryptoCurrencyStatus,
|
||||
appCurrencyCode = getSelectedAppCurrencyUseCase.unwrap().code,
|
||||
|
|
@ -315,7 +315,7 @@ internal class WalletCurrencyActionsClickIntentsImplementor @Inject constructor(
|
|||
event = TokenScreenAnalyticsEvent.ButtonExchange(cryptoCurrencyStatus.currency.symbol),
|
||||
)
|
||||
|
||||
reduxStateHolder.dispatch(TradeCryptoAction.New.Swap(cryptoCurrencyStatus.currency))
|
||||
reduxStateHolder.dispatch(TradeCryptoAction.Swap(cryptoCurrencyStatus.currency))
|
||||
}
|
||||
|
||||
override fun onExploreClick() {
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ include(":core:res")
|
|||
include(":core:ui")
|
||||
include(":core:utils")
|
||||
include(":core:deep-links")
|
||||
include(":core:deep-links:global")
|
||||
// endregion Core modules
|
||||
|
||||
// region Libs modules
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue