Updated on 2026-08-14

This commit is contained in:
Tangem 2024-01-31 14:08:06 +03:00
commit 8dec159cd3
126 changed files with 3168 additions and 996 deletions

View file

@ -23,9 +23,9 @@ import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import arrow.core.getOrElse
import by.kirich1409.viewbindingdelegate.viewBinding
import com.tangem.feature.qrscanning.QrScanningRouter
import com.google.android.material.snackbar.BaseTransientBottomBar
import com.google.android.material.snackbar.Snackbar
import com.tangem.core.deeplink.DeepLinksRegistry
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
import com.tangem.core.ui.event.StateEvent
@ -36,6 +36,7 @@ import com.tangem.domain.apptheme.model.AppThemeMode
import com.tangem.domain.card.ScanCardUseCase
import com.tangem.domain.card.repository.CardSdkConfigRepository
import com.tangem.domain.wallets.legacy.UserWalletsListManager
import com.tangem.feature.qrscanning.QrScanningRouter
import com.tangem.features.managetokens.navigation.ManageTokensRouter
import com.tangem.features.send.api.navigation.SendRouter
import com.tangem.features.tester.api.TesterRouter
@ -57,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
@ -138,6 +137,9 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
@Inject
lateinit var qrScanningRouter: QrScanningRouter
@Inject
lateinit var deepLinksRegistry: DeepLinksRegistry
internal val viewModel: MainViewModel by viewModels()
private lateinit var appThemeModeFlow: SharedFlow<AppThemeMode?>
@ -167,6 +169,10 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
checkForNotificationPermission()
observeStateUpdates()
if (intent != null) {
deepLinksRegistry.launch(intent)
}
}
private fun observeStateUpdates() {
@ -293,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) {
@ -332,6 +336,10 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
lifecycleScope.launch {
intentProcessor.handleIntent(intent)
}
if (intent != null) {
deepLinksRegistry.launch(intent)
}
}
override fun showSnackbar(

View file

@ -2,6 +2,7 @@ package com.tangem.tap.common.redux.legacy
import com.tangem.domain.redux.LegacyAction
import com.tangem.tap.common.feedback.RateCanBeBetterEmail
import com.tangem.tap.common.feedback.SendTransactionFailedEmail
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.store
@ -20,6 +21,11 @@ internal object LegacyMiddleware {
GlobalAction.Onboarding.Start(action.scanResponse, canSkipBackup = action.canSkipBackup),
)
}
is LegacyAction.SendEmailTransactionFailed -> {
store.state.globalState.feedbackManager?.sendEmail(
SendTransactionFailedEmail(action.errorMessage),
)
}
}
next(action)
}

View file

@ -310,4 +310,22 @@ internal object TokensDomainModule {
): GetNetworksSupportedByWallet {
return GetNetworksSupportedByWallet(repository = repository)
}
@Provides
@ViewModelScoped
fun provideGetBalanceNotEnoughForFeeWarningUseCase(
currenciesRepository: CurrenciesRepository,
dispatchers: CoroutineDispatcherProvider,
): GetBalanceNotEnoughForFeeWarningUseCase {
return GetBalanceNotEnoughForFeeWarningUseCase(currenciesRepository, dispatchers)
}
@Provides
@ViewModelScoped
fun provideIsAmountSubtractAvailableUseCase(
currenciesRepository: CurrenciesRepository,
dispatchers: CoroutineDispatcherProvider,
): IsAmountSubtractAvailableUseCase {
return IsAmountSubtractAvailableUseCase(currenciesRepository, dispatchers)
}
}

View file

@ -7,7 +7,6 @@ import com.tangem.domain.transaction.usecase.CreateTransactionUseCase
import com.tangem.domain.transaction.usecase.GetFeeUseCase
import com.tangem.domain.transaction.usecase.SendTransactionUseCase
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -20,24 +19,21 @@ internal object TransactionDomainModule {
@Provides
@ViewModelScoped
fun provideGetFeeUseCase(
walletManagersFacade: WalletManagersFacade,
dispatchers: CoroutineDispatcherProvider,
): GetFeeUseCase {
return GetFeeUseCase(walletManagersFacade, dispatchers)
fun provideGetFeeUseCase(walletManagersFacade: WalletManagersFacade): GetFeeUseCase {
return GetFeeUseCase(walletManagersFacade)
}
@Provides
@ViewModelScoped
fun provideSendTransactionUseCase(
isDemoCardUseCase: IsDemoCardUseCase,
walletManagersFacade: WalletManagersFacade,
cardSdkConfigRepository: CardSdkConfigRepository,
transactionRepository: TransactionRepository,
): SendTransactionUseCase {
return SendTransactionUseCase(
isDemoCardUseCase = isDemoCardUseCase,
cardSdkConfigRepository = cardSdkConfigRepository,
walletManagersFacade = walletManagersFacade,
transactionRepository = transactionRepository,
)
}

View file

@ -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
}
}

View file

@ -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"
// }
}

View file

@ -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,

View file

@ -1,6 +1,5 @@
package com.tangem.tap.proxy.redux
import com.tangem.feature.qrscanning.QrScanningRouter
import com.tangem.datasource.connection.NetworkConnectionManager
import com.tangem.domain.appcurrency.repository.AppCurrencyRepository
import com.tangem.domain.apptheme.repository.AppThemeModeRepository
@ -13,6 +12,7 @@ import com.tangem.domain.tokens.repository.CurrenciesRepository
import com.tangem.domain.tokens.repository.NetworksRepository
import com.tangem.domain.walletmanager.WalletManagersFacade
import com.tangem.domain.wallets.repository.WalletsRepository
import com.tangem.feature.qrscanning.QrScanningRouter
import com.tangem.features.managetokens.featuretoggles.ManageTokensFeatureToggles
import com.tangem.features.managetokens.navigation.ManageTokensRouter
import com.tangem.features.send.api.featuretoggles.SendFeatureToggles