Updated on 2026-08-14

This commit is contained in:
Tangem 2023-09-06 14:02:14 +03:00
commit 4d1edf8d04
38 changed files with 345 additions and 221 deletions

View file

@ -8,6 +8,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.lifecycle.lifecycleScope
import by.kirich1409.viewbindingdelegate.viewBinding
import com.google.android.material.snackbar.Snackbar
import com.tangem.core.navigation.AppScreen
@ -180,7 +181,12 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
private fun initIntentHandlers() {
val hasSavedWalletsProvider = { store.state.globalState.userWalletsListManager?.hasUserWallets == true }
intentProcessor.addHandler(BackgroundScanIntentHandler(hasSavedWalletsProvider))
intentProcessor.addHandler(
BackgroundScanIntentHandler(
hasSavedWalletsProvider,
lifecycleScope,
),
)
intentProcessor.addHandler(WalletConnectLinkIntentHandler())
intentProcessor.addHandler(BuyCurrencyIntentHandler())
intentProcessor.addHandler(SellCurrencyIntentHandler())
@ -222,7 +228,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
*/
cardSdkLifecycleObserver.onCreate(context = this)
scope.launch {
lifecycleScope.launch {
intentProcessor.handleIntent(intent)
}
}
@ -294,8 +300,11 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
if (store.state.globalState.userWalletsListManager?.hasUserWallets == true) {
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Welcome))
store.dispatchOnMain(WelcomeAction.SetInitialIntent(intentWhichStartedActivity))
scope.launch {
val handler = BackgroundScanIntentHandler(hasSavedUserWalletsProvider = { true })
lifecycleScope.launch {
val handler = BackgroundScanIntentHandler(
hasSavedUserWalletsProvider = { true },
lifecycleCoroutineScope = lifecycleScope,
)
val isBackgroundScanHandled = handler.handleIntent(intentWhichStartedActivity)
val hasNotIncompletedBackup = !backupService.hasIncompletedBackup
if (!isBackgroundScanHandled && hasNotIncompletedBackup) {
@ -304,7 +313,7 @@ class MainActivity : AppCompatActivity(), SnackbarHandler, ActivityResultCallbac
}
} else {
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Home))
scope.launch {
lifecycleScope.launch {
intentProcessor.handleIntent(intentWhichStartedActivity)
}
}

View file

@ -134,9 +134,12 @@ class TangemSdkManager(private val cardSdkConfigRepository: CardSdkConfigReposit
return runTaskAsyncReturnOnMain(DeriveMultipleWalletPublicKeysTask(derivations), cardId)
}
suspend fun resetToFactorySettings(cardId: String): CompletionResult<CardDTO> {
suspend fun resetToFactorySettings(
cardId: String,
allowsRequestAccessCodeFromRepository: Boolean,
): CompletionResult<CardDTO> {
return runTaskAsyncReturnOnMain(
runnable = ResetToFactorySettingsTask(),
runnable = ResetToFactorySettingsTask(allowsRequestAccessCodeFromRepository),
cardId = cardId,
initialMessage = Message(resources.getString(R.string.card_settings_reset_card_to_factory)),
)

View file

@ -8,7 +8,9 @@ import com.tangem.common.extensions.guard
import com.tangem.operations.backup.ResetBackupCommand
import com.tangem.operations.wallet.PurgeWalletCommand
class ResetToFactorySettingsTask : CardSessionRunnable<Card> {
class ResetToFactorySettingsTask(
override val allowsRequestAccessCodeFromRepository: Boolean,
) : CardSessionRunnable<Card> {
override fun run(session: CardSession, callback: (result: CompletionResult<Card>) -> Unit) {
deleteWallets(session, callback)

View file

@ -247,7 +247,10 @@ private class ScanWalletProcessor(
derivationStyleProvider: DerivationStyleProvider,
): List<BlockchainNetwork> {
val userTokensRepository = userTokensRepository ?: return emptyList()
val blockchainsToDerive = userTokensRepository.loadBlockchainsToDerive(card)
val blockchainsToDerive = userTokensRepository.loadBlockchainsToDerive(
card,
derivationStyleProvider.getDerivationStyle(),
)
.toMutableList()
.ifEmpty {
mutableListOf(

View file

@ -28,18 +28,23 @@ class UserTokensRepository(
private val networkConnectionManager: NetworkConnectionManager,
) {
suspend fun getUserTokens(card: CardDTO): List<Currency> = withContext(dispatchers.io) {
val userWalletId = getUserWalletId(card) ?: return@withContext emptyList()
suspend fun getUserTokens(card: CardDTO, derivationStyle: DerivationStyle?): List<Currency> =
withContext(dispatchers.io) {
val userWalletId = getUserWalletId(card) ?: return@withContext emptyList()
if (DemoHelper.isDemoCardId(card.cardId)) {
return@withContext loadTokensOffline(userWalletId = userWalletId).ifEmpty(::loadDemoCurrencies)
if (DemoHelper.isDemoCardId(card.cardId)) {
return@withContext loadTokensOffline(userWalletId = userWalletId).ifEmpty {
loadDemoCurrencies(
derivationStyle,
)
}
}
if (!networkConnectionManager.isOnline) return@withContext loadTokensOffline(userWalletId = userWalletId)
return@withContext remoteGetUserTokens(userWalletId = userWalletId)
}
if (!networkConnectionManager.isOnline) return@withContext loadTokensOffline(userWalletId = userWalletId)
return@withContext remoteGetUserTokens(userWalletId = userWalletId)
}
suspend fun saveUserTokens(card: CardDTO, tokens: List<Currency>) = withContext(dispatchers.io) {
val userWalletId = getUserWalletId(card) ?: return@withContext
val userTokens = tokens.toUserTokensResponse()
@ -48,28 +53,29 @@ class UserTokensRepository(
}
// FIXME: Move to data layer
suspend fun loadBlockchainsToDerive(card: CardDTO): List<BlockchainNetwork> = withContext(dispatchers.io) {
val userWalletId = getUserWalletId(card) ?: return@withContext emptyList()
val blockchainNetworks = loadTokensOffline(userWalletId = userWalletId).toBlockchainNetworks()
suspend fun loadBlockchainsToDerive(card: CardDTO, derivationStyle: DerivationStyle?): List<BlockchainNetwork> =
withContext(dispatchers.io) {
val userWalletId = getUserWalletId(card) ?: return@withContext emptyList()
val blockchainNetworks = loadTokensOffline(userWalletId = userWalletId).toBlockchainNetworks()
if (DemoHelper.isDemoCardId(card.cardId)) {
return@withContext blockchainNetworks.ifEmpty(loadDemoCurrencies()::toBlockchainNetworks)
if (DemoHelper.isDemoCardId(card.cardId)) {
return@withContext blockchainNetworks.ifEmpty(loadDemoCurrencies(derivationStyle)::toBlockchainNetworks)
}
return@withContext blockchainNetworks
}
return@withContext blockchainNetworks
}
private fun loadTokensOffline(userWalletId: String): List<Currency> {
return storageService.getUserTokens(userWalletId = userWalletId) ?: emptyList()
}
// FIXME: Move to user wallet config
private fun loadDemoCurrencies(): List<Currency> {
private fun loadDemoCurrencies(derivationStyle: DerivationStyle?): List<Currency> {
return DemoHelper.config.demoBlockchains
.map { blockchain ->
BlockchainNetwork(
blockchain = blockchain,
derivationPath = blockchain.derivationPath(DerivationStyle.LEGACY)?.rawPath,
derivationPath = blockchain.derivationPath(derivationStyle)?.rawPath,
tokens = emptyList(),
)
}

View file

@ -3,6 +3,7 @@ package com.tangem.tap.domain.walletStores.implementation
import com.tangem.blockchain.common.WalletManager
import com.tangem.blockchain.common.address.AddressType
import com.tangem.common.*
import com.tangem.domain.common.util.derivationStyleProvider
import com.tangem.domain.wallets.legacy.WalletManagersRepository
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
@ -126,8 +127,9 @@ internal class DefaultWalletStoresManager(
private suspend fun fetchMultiWallets(userWallet: UserWallet): CompletionResult<Unit> {
val scanResponse = userWallet.scanResponse
val derivationStyle = scanResponse.derivationStyleProvider.getDerivationStyle()
val userTokens = withContext(Dispatchers.IO) {
userTokensRepository.getUserTokens(scanResponse.card)
userTokensRepository.getUserTokens(scanResponse.card, derivationStyle)
}
val userWalletId = userWallet.walletId

View file

@ -123,7 +123,7 @@ class DetailsMiddleware {
scope.launch {
val userWalletId = UserWalletIdBuilder.card(card).build()
tangemSdkManager.resetToFactorySettings(card.cardId)
tangemSdkManager.resetToFactorySettings(card.cardId, true)
.flatMap { userWalletsListManager.delete(listOfNotNull(userWalletId)) }
.flatMap { tangemSdkManager.deleteSavedUserCodes(setOf(card.cardId)) }
.doOnSuccess {

View file

@ -13,6 +13,7 @@ import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope
import com.tangem.core.analytics.Analytics
import com.tangem.core.navigation.AppScreen
import com.tangem.core.navigation.NavigationAction
@ -26,6 +27,7 @@ import com.tangem.tap.features.home.redux.Stories
import com.tangem.tap.features.tokens.legacy.redux.TokensAction
import com.tangem.tap.store
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import org.rekotlin.StoreSubscriber
@AndroidEntryPoint
@ -94,7 +96,11 @@ class HomeFragment : Fragment(), StoreSubscriber<HomeState> {
onLearn2earnClick = learn2earnViewModel.uiState.storyScreenState.onClick,
onScanButtonClick = {
Analytics.send(IntroductionProcess.ButtonScanCard())
store.dispatch(HomeAction.ReadCard())
lifecycleScope.launch {
store.dispatch(
HomeAction.ReadCard(lifecycleCoroutineScope = lifecycleScope),
)
}
},
onShopButtonClick = {
Analytics.send(IntroductionProcess.ButtonBuyCards())

View file

@ -23,6 +23,7 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import androidx.compose.ui.unit.dp
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.tangem.core.ui.res.TangemColorPalette
import com.tangem.core.ui.res.TangemTheme
import com.tangem.feature.learn2earn.presentation.ui.Learn2earnStoriesScreen
import com.tangem.tap.features.home.compose.content.*
@ -169,7 +170,11 @@ private fun StoriesScreenContent(config: StoriesScreenContentConfig, modifier: M
.height(17.dp)
.alpha(if (hideContent) 0f else 1f)
.align(Alignment.Start),
colorFilter = if (config.currentStory.isDarkBackground) null else ColorFilter.tint(Color.Black),
colorFilter = if (config.currentStory.isDarkBackground) {
null
} else {
ColorFilter.tint(TangemColorPalette.Dark6)
},
)
when (config.currentStory) {
Stories.OneInchPromo -> Learn2earnStoriesScreen(config.onLearn2earnClick)

View file

@ -1,5 +1,6 @@
package com.tangem.tap.features.home.redux
import androidx.lifecycle.LifecycleCoroutineScope
import com.tangem.core.analytics.models.AnalyticsEvent
import com.tangem.tap.common.analytics.events.AnalyticsParam
import com.tangem.tap.common.analytics.events.Basic
@ -13,8 +14,16 @@ sealed class HomeAction : Action {
data class InsertStory(val position: Int, val story: Stories) : HomeAction()
/**
* Action for scanning card
*
* @property analyticsEvent analytics event
* @property lifecycleCoroutineScope lifecycle scope. It will be canceled when lifecycle-aware component is
* destroyed.
*/
data class ReadCard(
val analyticsEvent: AnalyticsEvent? = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.Introduction),
val lifecycleCoroutineScope: LifecycleCoroutineScope,
) : HomeAction()
data class ScanInProgress(val scanInProgress: Boolean) : HomeAction()

View file

@ -1,5 +1,6 @@
package com.tangem.tap.features.home.redux
import androidx.lifecycle.LifecycleCoroutineScope
import com.tangem.common.doOnFailure
import com.tangem.common.doOnResult
import com.tangem.common.doOnSuccess
@ -62,7 +63,7 @@ private fun handleHomeAction(action: Action) {
store.dispatch(GlobalAction.FetchUserCountry)
}
is HomeAction.ReadCard -> {
readCard(action.analyticsEvent)
readCard(action.analyticsEvent, action.lifecycleCoroutineScope)
}
is HomeAction.GoToShop -> {
Analytics.send(Shop.ScreenOpened())
@ -77,32 +78,34 @@ private fun handleHomeAction(action: Action) {
}
}
private fun readCard(analyticsEvent: AnalyticsEvent?) = scope.launch {
delay(timeMillis = 200)
store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository).setAccessCodeRequestPolicy(
isBiometricsRequestPolicy = preferencesStorage.shouldSaveAccessCodes,
)
private fun readCard(analyticsEvent: AnalyticsEvent?, lifecycleCoroutineScope: LifecycleCoroutineScope) {
lifecycleCoroutineScope.launch {
delay(timeMillis = 200)
store.state.daggerGraphState.get(DaggerGraphState::cardSdkConfigRepository).setAccessCodeRequestPolicy(
isBiometricsRequestPolicy = preferencesStorage.shouldSaveAccessCodes,
)
store.state.daggerGraphState.get(DaggerGraphState::scanCardProcessor).scan(
analyticsEvent = analyticsEvent,
onProgressStateChange = { showProgress ->
if (showProgress) {
changeButtonState(ButtonState.PROGRESS)
} else {
store.state.daggerGraphState.get(DaggerGraphState::scanCardProcessor).scan(
analyticsEvent = analyticsEvent,
onProgressStateChange = { showProgress ->
if (showProgress) {
changeButtonState(ButtonState.PROGRESS)
} else {
changeButtonState(ButtonState.ENABLED)
}
},
onScanStateChange = { scanInProgress ->
store.dispatch(HomeAction.ScanInProgress(scanInProgress))
},
onFailure = {
Timber.e(it, "Unable to scan card")
changeButtonState(ButtonState.ENABLED)
}
},
onScanStateChange = { scanInProgress ->
store.dispatch(HomeAction.ScanInProgress(scanInProgress))
},
onFailure = {
Timber.e(it, "Unable to scan card")
changeButtonState(ButtonState.ENABLED)
},
onSuccess = { scanResponse ->
proceedWithScanResponse(scanResponse)
},
)
},
onSuccess = { scanResponse ->
proceedWithScanResponse(scanResponse)
},
)
}
}
private fun proceedWithScanResponse(scanResponse: ScanResponse) = scope.launch {

View file

@ -4,11 +4,11 @@ import android.content.Intent
import android.nfc.NfcAdapter
import android.nfc.Tag
import android.os.Build
import androidx.lifecycle.LifecycleCoroutineScope
import com.tangem.tap.common.extensions.dispatchWithMain
import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.tap.features.intentHandler.IntentHandler
import com.tangem.tap.features.welcome.redux.WelcomeAction
import com.tangem.tap.scope
import com.tangem.tap.store
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@ -18,6 +18,7 @@ import kotlinx.coroutines.launch
*/
class BackgroundScanIntentHandler(
private val hasSavedUserWalletsProvider: () -> Boolean,
private val lifecycleCoroutineScope: LifecycleCoroutineScope,
) : IntentHandler {
private val nfcActions = arrayOf(
@ -40,12 +41,12 @@ class BackgroundScanIntentHandler(
intent.action = null
if (hasSavedUserWalletsProvider.invoke()) {
// TODO: Remove delay after [REDACTED_JIRA]
scope.launch {
lifecycleCoroutineScope.launch {
delay(timeMillis = 200)
store.dispatchWithMain(WelcomeAction.ProceedWithCard)
store.dispatchWithMain(WelcomeAction.ProceedWithCard(lifecycleCoroutineScope))
}
} else {
store.dispatchWithMain(HomeAction.ReadCard())
store.dispatchWithMain(HomeAction.ReadCard(lifecycleCoroutineScope = lifecycleCoroutineScope))
}
return true

View file

@ -1,6 +1,7 @@
package com.tangem.tap.features.onboarding.products.wallet.redux
import android.net.Uri
import androidx.lifecycle.LifecycleCoroutineScope
import com.tangem.common.CompletionResult
import com.tangem.feature.onboarding.data.model.CreateWalletResponse
import com.tangem.feature.onboarding.presentation.wallet2.analytics.SeedPhraseSource
@ -17,7 +18,7 @@ sealed class OnboardingWalletAction : Action {
) : OnboardingWalletAction()
object Done : OnboardingWalletAction()
object FinishOnboarding : OnboardingWalletAction()
data class FinishOnboarding(val lifecycleCoroutineScope: LifecycleCoroutineScope) : OnboardingWalletAction()
object ResumeBackup : OnboardingWalletAction()

View file

@ -146,12 +146,12 @@ private fun handleWalletAction(action: Action) {
is CompletionResult.Failure -> Unit
}
}
OnboardingWalletAction.FinishOnboarding -> {
is OnboardingWalletAction.FinishOnboarding -> {
store.dispatch(GlobalAction.Onboarding.Stop)
if (scanResponse == null) {
store.dispatch(NavigationAction.PopBackTo())
store.dispatch(HomeAction.ReadCard())
store.dispatch(HomeAction.ReadCard(lifecycleCoroutineScope = action.lifecycleCoroutineScope))
} else {
val backupState = store.state.onboardingWalletState.backupState
val updatedScanResponse = updateScanResponseAfterBackup(scanResponse, backupState)
@ -460,7 +460,7 @@ private fun handleBackupAction(appState: () -> AppState?, action: BackupAction)
}
is BackupAction.ResetBackupCard -> {
scope.launch { tangemSdkManager.resetToFactorySettings(action.cardId) }
scope.launch { tangemSdkManager.resetToFactorySettings(action.cardId, false) }
}
else -> Unit

View file

@ -11,6 +11,7 @@ import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.MenuProvider
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import androidx.transition.TransitionInflater
import androidx.transition.TransitionManager
import by.kirich1409.viewbindingdelegate.viewBinding
@ -428,7 +429,9 @@ class OnboardingWalletFragment :
layoutButtonsCommon.btnWalletAlternativeAction.hide()
layoutButtonsCommon.btnWalletMainAction.setOnClickListener {
showConfetti(false)
store.dispatch(OnboardingWalletAction.FinishOnboarding)
lifecycleScope.launch {
store.dispatch(OnboardingWalletAction.FinishOnboarding(lifecycleCoroutineScope = lifecycleScope))
}
}
animator.showSuccess {

View file

@ -155,6 +155,13 @@ sealed class SendAction : SendScreenAction {
val onOk: () -> Unit,
) : Dialog()
data class ChiaWarningDialog(
val blockchainName: String,
val maxOutputs: Int,
val maxAmount: BigDecimal,
val onOk: () -> Unit,
) : Dialog()
sealed class SendTransactionFails : Dialog() {
data class CardSdkError(val error: TangemSdkError) : Dialog()
data class BlockchainSdkError(val error: com.tangem.blockchain.common.BlockchainSdkError) : Dialog()

View file

@ -307,6 +307,19 @@ private fun sendTransaction(
),
)
}
is BlockchainSdkError.Chia.UtxoAmountError -> {
dispatch(
SendAction.Dialog.ChiaWarningDialog(
blockchainName = Blockchain.Chia.fullName,
maxOutputs = error.maxOutputs,
maxAmount = error.maxAmount,
onOk = {
dispatch(AmountAction.SetAmount(error.maxAmount, isUserInput = false))
dispatch(AmountActionUi.CheckAmountToSend)
},
),
)
}
else -> {
when {
error.customMessage.contains(DemoTransactionSender.ID) -> {

View file

@ -46,6 +46,7 @@ private class SendReducer : SendInternalReducer {
}
is SendAction.Dialog.TezosWarningDialog -> sendState.copy(dialog = action)
is SendAction.Dialog.KaspaWarningDialog -> sendState.copy(dialog = action)
is SendAction.Dialog.ChiaWarningDialog -> sendState.copy(dialog = action)
is SendAction.Dialog.SendTransactionFails.CardSdkError -> sendState.copy(dialog = action)
is SendAction.Dialog.SendTransactionFails.BlockchainSdkError -> sendState.copy(dialog = action)
is SendAction.Dialog.RequestFeeError -> sendState.copy(dialog = action)

View file

@ -0,0 +1,32 @@
package com.tangem.tap.features.send.ui.dialogs
import android.content.Context
import androidx.appcompat.app.AlertDialog
import com.tangem.tap.features.send.redux.SendAction
import com.tangem.tap.store
import com.tangem.wallet.R
// TODO: [REDACTED_JIRA]
object ChiaWarningDialog {
fun create(context: Context, dialog: SendAction.Dialog.ChiaWarningDialog): AlertDialog {
return AlertDialog.Builder(context).apply {
setTitle(R.string.common_warning)
setMessage(
context.getString(
R.string.common_utxo_validate_withdrawal_message_warning,
dialog.blockchainName,
dialog.maxOutputs,
dialog.maxAmount.toPlainString(),
),
)
setPositiveButton(R.string.common_ok) { _, _ ->
dialog.onOk()
}
setOnDismissListener {
store.dispatch(SendAction.Dialog.Hide)
}
}
.create()
}
}

View file

@ -18,10 +18,7 @@ import com.tangem.tap.features.send.redux.SendAction
import com.tangem.tap.features.send.redux.states.*
import com.tangem.tap.features.send.ui.FeeUiHelper
import com.tangem.tap.features.send.ui.SendFragment
import com.tangem.tap.features.send.ui.dialogs.KaspaWarningDialog
import com.tangem.tap.features.send.ui.dialogs.RequestFeeErrorDialog
import com.tangem.tap.features.send.ui.dialogs.SendTransactionFailsDialog
import com.tangem.tap.features.send.ui.dialogs.TezosWarningDialog
import com.tangem.tap.features.send.ui.dialogs.*
import com.tangem.tap.features.wallet.redux.ProgressState
import com.tangem.tap.features.wallet.redux.utils.ROUGH_SIGN
import com.tangem.tap.features.wallet.redux.utils.UNKNOWN_AMOUNT_SIGN
@ -124,6 +121,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
}
}
@Suppress("ComplexMethod")
private fun handleSendScreen(fg: SendFragment, state: SendState) = with(fg.binding) {
when (state.dialog) {
is SendAction.Dialog.TezosWarningDialog -> {
@ -138,6 +136,12 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
dialog?.show()
}
}
is SendAction.Dialog.ChiaWarningDialog -> {
if (dialog == null) {
dialog = ChiaWarningDialog.create(fg.requireContext(), state.dialog)
dialog?.show()
}
}
is SendAction.Dialog.SendTransactionFails.CardSdkError -> {
if (dialog == null) {
dialog = SendTransactionFailsDialog.create(fg.requireContext(), state.dialog)

View file

@ -1,6 +1,7 @@
package com.tangem.tap.features.wallet.redux
import android.content.Context
import androidx.lifecycle.LifecycleCoroutineScope
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.address.AddressType
import com.tangem.core.analytics.models.AnalyticsEvent
@ -73,7 +74,10 @@ sealed class WalletAction : Action {
class CheckRemainingSignatures(val remainingSignatures: Int?) : Warnings()
}
data class Scan(val onScanSuccessEvent: AnalyticsEvent?) : WalletAction()
data class Scan(
val onScanSuccessEvent: AnalyticsEvent?,
val lifecycleScope: LifecycleCoroutineScope,
) : WalletAction()
data class Send(val amount: Amount? = null) : WalletAction()
@ -109,7 +113,7 @@ sealed class WalletAction : Action {
data class ExploreAddress(val exploreUrl: String, val context: Context) : WalletAction()
object CreateWallet : WalletAction()
object ChangeWallet : WalletAction()
data class ChangeWallet(val lifecycleScope: LifecycleCoroutineScope) : WalletAction()
object ShowSaveWalletIfNeeded : WalletAction()
sealed class TradeCryptoAction : WalletAction() {

View file

@ -1,5 +1,6 @@
package com.tangem.tap.features.wallet.redux.middlewares
import androidx.lifecycle.LifecycleCoroutineScope
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.AmountType
@ -114,9 +115,9 @@ class WalletMiddleware {
}
is WalletAction.Scan -> {
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
scope.launch {
action.lifecycleScope.launch {
delay(timeMillis = 700)
store.dispatchOnMain(HomeAction.ReadCard(action.onScanSuccessEvent))
store.dispatchOnMain(HomeAction.ReadCard(action.onScanSuccessEvent, action.lifecycleScope))
}
}
is WalletAction.LoadData,
@ -225,7 +226,7 @@ class WalletMiddleware {
showSaveWalletIfNeeded()
}
is WalletAction.ChangeWallet -> {
changeWallet(walletState)
changeWallet(walletState, action.lifecycleScope)
}
is WalletAction.UserWalletChanged -> Unit
is WalletAction.WalletStoresChanged -> {
@ -378,7 +379,7 @@ class WalletMiddleware {
}
}
private fun changeWallet(state: WalletState) {
private fun changeWallet(state: WalletState, lifecycleScope: LifecycleCoroutineScope) {
when {
state.canSaveUserWallets -> {
Analytics.send(MainScreen.ButtonMyWallets())
@ -386,7 +387,12 @@ class WalletMiddleware {
}
else -> {
Analytics.send(MainScreen.ButtonScanCard())
store.dispatch(WalletAction.Scan(Basic.CardWasScanned(AnalyticsParam.ScannedFrom.Main)))
store.dispatch(
WalletAction.Scan(
onScanSuccessEvent = Basic.CardWasScanned(AnalyticsParam.ScannedFrom.Main),
lifecycleScope = lifecycleScope,
),
)
}
}
}

View file

@ -141,7 +141,11 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), SafeStoreSubscriber<W
(activity as? AppCompatActivity)?.setSupportActionBar(binding.toolbar)
binding.toolbar.setNavigationOnClickListener(
OneTouchClickListener { store.dispatch(WalletAction.ChangeWallet) },
OneTouchClickListener {
lifecycleScope.launch {
store.dispatch(WalletAction.ChangeWallet(lifecycleScope))
}
},
)
setupWarningsRecyclerView()
walletView.changeWalletView(this, binding)

View file

@ -1,6 +1,7 @@
package com.tangem.tap.features.welcome.redux
import android.content.Intent
import androidx.lifecycle.LifecycleCoroutineScope
import com.tangem.common.core.TangemError
import org.rekotlin.Action
@ -10,7 +11,7 @@ internal sealed interface WelcomeAction : Action {
data class Error(val error: TangemError) : WelcomeAction
}
object ProceedWithCard : WelcomeAction {
data class ProceedWithCard(val lifecycleCoroutineScope: LifecycleCoroutineScope) : WelcomeAction {
object Success : WelcomeAction
data class Error(val error: TangemError) : WelcomeAction
}

View file

@ -1,5 +1,6 @@
package com.tangem.tap.features.welcome.redux
import androidx.lifecycle.LifecycleCoroutineScope
import com.tangem.common.core.TangemSdkError
import com.tangem.common.doOnFailure
import com.tangem.common.doOnResult
@ -40,7 +41,7 @@ internal class WelcomeMiddleware {
private fun handleAction(action: WelcomeAction, state: WelcomeState) {
when (action) {
is WelcomeAction.ProceedWithBiometrics -> proceedWithBiometrics(state)
is WelcomeAction.ProceedWithCard -> proceedWithCard(state)
is WelcomeAction.ProceedWithCard -> proceedWithCard(state, action.lifecycleCoroutineScope)
is WelcomeAction.ClearUserWallets -> disableUserWalletsSaving()
else -> Unit
}
@ -77,25 +78,27 @@ internal class WelcomeMiddleware {
}
}
private fun proceedWithCard(state: WelcomeState) = scope.launch {
scanCardInternal { scanResponse ->
val userWallet = UserWalletBuilder(scanResponse).build() ?: return@scanCardInternal
private fun proceedWithCard(state: WelcomeState, lifecycleCoroutineScope: LifecycleCoroutineScope) {
lifecycleCoroutineScope.launch {
scanCardInternal { scanResponse ->
val userWallet = UserWalletBuilder(scanResponse).build() ?: return@scanCardInternal
userWalletsListManager.save(userWallet, canOverride = true)
.doOnFailure { error ->
Timber.e(error, "Unable to save user wallet")
store.dispatchOnMain(WelcomeAction.ProceedWithCard.Error(error))
}
.doOnSuccess {
store.dispatchOnMain(SignInAction.SetSignInType(Basic.SignedIn.SignInType.Card))
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet))
store.dispatchOnMain(WelcomeAction.ProceedWithCard.Success)
store.onUserWalletSelected(userWallet = userWallet)
state.intent?.let {
WalletConnectLinkIntentHandler().handleIntent(it)
userWalletsListManager.save(userWallet, canOverride = true)
.doOnFailure { error ->
Timber.e(error, "Unable to save user wallet")
store.dispatchOnMain(WelcomeAction.ProceedWithCard.Error(error))
}
}
.doOnSuccess {
store.dispatchOnMain(SignInAction.SetSignInType(Basic.SignedIn.SignInType.Card))
store.dispatchOnMain(NavigationAction.NavigateTo(AppScreen.Wallet))
store.dispatchOnMain(WelcomeAction.ProceedWithCard.Success)
store.onUserWalletSelected(userWallet = userWallet)
state.intent?.let {
WalletConnectLinkIntentHandler().handleIntent(it)
}
}
}
}
}

View file

@ -7,17 +7,12 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBarsPadding
import androidx.compose.material.SnackbarHost
import androidx.compose.material.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.colorResource
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import com.tangem.core.analytics.Analytics
import com.tangem.core.ui.components.SystemBarsEffect
import com.tangem.core.ui.fragments.ComposeFragment
@ -67,7 +62,7 @@ internal class WelcomeFragment : ComposeFragment<WelcomeScreenState>() {
showUnlockProgress = state.showUnlockWithBiometricsProgress,
showScanCardProgress = state.showUnlockWithCardProgress,
onUnlockClick = viewModel::unlockWallets,
onScanCardClick = viewModel::scanCard,
onScanCardClick = { viewModel.scanCard(lifecycleCoroutineScope = lifecycleScope) },
)
SnackbarHost(

View file

@ -1,5 +1,6 @@
package com.tangem.tap.features.welcome.ui
import androidx.lifecycle.LifecycleCoroutineScope
import androidx.lifecycle.ViewModel
import com.tangem.common.core.TangemError
import com.tangem.core.analytics.Analytics
@ -30,9 +31,9 @@ internal class WelcomeViewModel : ViewModel(), StoreSubscriber<WelcomeState> {
store.dispatch(WelcomeAction.ProceedWithBiometrics)
}
fun scanCard() {
fun scanCard(lifecycleCoroutineScope: LifecycleCoroutineScope) {
Analytics.send(SignIn.ButtonCardSignIn())
store.dispatch(WelcomeAction.ProceedWithCard)
store.dispatch(WelcomeAction.ProceedWithCard(lifecycleCoroutineScope))
}
fun closeError() {

View file

@ -40,7 +40,7 @@ class UserWalletManagerImpl(
val userTokensRepository =
requireNotNull(appStateHolder.userTokensRepository) { "userTokensRepository is null" }
return if (card != null) {
userTokensRepository.getUserTokens(card)
userTokensRepository.getUserTokens(card, null) // refactor in [REDACTED_JIRA]
.filter {
val checkCustom = if (isExcludeCustom) {
!it.isCustomCurrency(null)

View file

@ -3,18 +3,19 @@
android:height="108dp"
android:viewportWidth="146"
android:viewportHeight="146">
<group android:scaleX="0.33"
android:scaleY="0.33"
android:translateX="48.91"
android:translateY="48.91">
<path
android:pathData="M103.72,0H42.28C32.59,0 27.75,0 24.05,1.89C20.79,3.55 18.14,6.19 16.49,9.45C14.6,13.15 14.6,17.99 14.6,27.68V37.96H131.4V27.68C131.4,17.99 131.4,13.15 129.51,9.45C127.85,6.19 125.21,3.54 121.96,1.89C118.25,0 113.41,0 103.72,0Z"
android:fillColor="#1E1E1E"/>
<path
android:pathData="M92.46,70.08H131.4V118.32C131.4,128.01 131.4,132.85 129.51,136.55C127.85,139.81 125.21,142.46 121.95,144.11C118.25,146 113.4,146 103.71,146H92.47L92.46,70.08Z"
android:fillColor="#1E1E1E"/>
<path
android:pathData="M14.6,70.08H53.53V146H42.28C32.59,146 27.75,146 24.05,144.11C20.79,142.46 18.15,139.81 16.49,136.55C14.6,132.85 14.6,128.01 14.6,118.32V70.08Z"
android:fillColor="#1E1E1E"/>
</group>
<group
android:scaleX="0.33"
android:scaleY="0.33"
android:translateX="48.91"
android:translateY="48.91">
<path
android:fillColor="#1E1E1E"
android:pathData="M103.72,0H42.28C32.59,0 27.75,0 24.05,1.89C20.79,3.55 18.14,6.19 16.49,9.45C14.6,13.15 14.6,17.99 14.6,27.68V37.96H131.4V27.68C131.4,17.99 131.4,13.15 129.51,9.45C127.85,6.19 125.21,3.54 121.96,1.89C118.25,0 113.41,0 103.72,0Z" />
<path
android:fillColor="#1E1E1E"
android:pathData="M92.46,70.08H131.4V118.32C131.4,128.01 131.4,132.85 129.51,136.55C127.85,139.81 125.21,142.46 121.95,144.11C118.25,146 113.4,146 103.71,146H92.47L92.46,70.08Z" />
<path
android:fillColor="#1E1E1E"
android:pathData="M14.6,70.08H53.53V146H42.28C32.59,146 27.75,146 24.05,144.11C20.79,142.46 18.15,139.81 16.49,136.55C14.6,132.85 14.6,128.01 14.6,118.32V70.08Z" />
</group>
</vector>

View file

@ -1,33 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="44dp"
android:height="13dp"
android:viewportWidth="44"
android:viewportHeight="13">
<path
android:pathData="M5.3925,5.4421L5.3925,9.8495L7.366,9.8495C7.4425,9.8487 7.5156,9.8187 7.5697,9.766C7.6238,9.7133 7.6545,9.6421 7.6554,9.5676L7.6554,5.4421L5.3925,5.4421Z"
android:fillColor="@android:color/white"/>
<path
android:pathData="M0.9796,9.5676C0.9805,9.6421 1.0112,9.7133 1.0653,9.766C1.1194,9.8187 1.1925,9.8487 1.2691,9.8495L3.2425,9.8495L3.2425,5.4421L0.9796,5.4421L0.9796,9.5676Z"
android:fillColor="@android:color/white"/>
<path
android:pathData="M7.6554,3.182L7.6554,1.2109C7.6554,1.1343 7.6255,1.0607 7.5721,1.0065C7.5187,0.9523 7.4463,0.9219 7.3708,0.9219L1.2644,0.9219C1.1889,0.9219 1.1166,0.9523 1.0632,1.0065C1.0098,1.0607 0.9798,1.1343 0.9798,1.2109L0.9798,3.182L7.6554,3.182Z"
android:fillColor="@android:color/white"/>
<path
android:pathData="M18.2145,3.8623C17.8055,3.6911 17.3623,3.6145 16.9187,3.6383C16.3325,3.6367 15.7479,3.7001 15.1759,3.8271L15.1759,4.8288C15.6319,4.775 16.0903,4.744 16.5494,4.736C16.7956,4.7249 17.0421,4.7487 17.2816,4.8064C17.3582,4.8291 17.4278,4.8702 17.4843,4.9261C17.5409,4.9819 17.5826,5.0507 17.6055,5.1264C17.6777,5.3956 17.7082,5.674 17.6961,5.9521L17.6961,6.2466C17.1551,6.2178 16.7243,6.2018 16.4295,6.2018C16.1151,6.1865 15.8016,6.2469 15.516,6.3778C15.2912,6.5041 15.1233,6.71 15.0463,6.9539C14.9395,7.3099 14.8914,7.6805 14.9038,8.0516C14.864,8.5294 14.9773,9.0075 15.2277,9.4182C15.3603,9.5688 15.5273,9.686 15.7149,9.76C15.9025,9.8341 16.1054,9.8628 16.3065,9.8439C16.543,9.8439 17.2783,9.8246 17.6023,9.4022C17.6559,9.335 17.7015,9.262 17.7383,9.1846L17.7739,9.7479L18.966,9.7479L18.966,6.0226C18.9834,5.5406 18.93,5.0588 18.8073,4.592C18.7638,4.437 18.6888,4.2923 18.5869,4.1669C18.485,4.0414 18.3583,3.9378 18.2145,3.8623ZM17.6832,7.4595C17.7167,7.7981 17.6586,8.1394 17.5148,8.4485C17.4445,8.5375 17.3484,8.6034 17.2394,8.6373C17.1162,8.6717 16.9885,8.6879 16.8604,8.6853C16.6603,8.7107 16.458,8.6579 16.2968,8.5381C16.1883,8.3545 16.1428,8.1412 16.1671,7.93C16.16,7.7598 16.1775,7.5896 16.219,7.4244C16.2316,7.3765 16.2543,7.3319 16.2855,7.2933C16.3168,7.2547 16.3559,7.223 16.4004,7.2003C16.5227,7.1521 16.6544,7.1312 16.7859,7.1395L17.6832,7.1395L17.6832,7.4595Z"
android:fillColor="@android:color/white"/>
<path
android:pathData="M28.5312,4.2026C28.5065,4.1598 28.4792,4.1187 28.4495,4.0794C28.0566,3.5603 27.1199,3.6382 27.1199,3.6382C26.8105,3.6281 26.5062,3.7214 26.2524,3.9042C25.996,4.1515 25.8305,4.4827 25.784,4.8417C25.5997,6.0976 25.5997,7.3749 25.784,8.6308C25.8287,8.9857 25.9895,9.3142 26.2398,9.5619C26.5131,9.765 26.8468,9.8626 27.1828,9.8376C27.1828,9.8376 27.2016,9.8376 27.2141,9.8376C27.2141,9.8376 28.0597,9.9057 28.4714,9.471C28.4795,9.826 28.4564,10.1811 28.4023,10.5318C28.3884,10.6253 28.3525,10.7138 28.2977,10.7896C28.2429,10.8654 28.1709,10.9262 28.088,10.9666C27.8262,11.0582 27.5499,11.0967 27.2739,11.0801L25.9757,11.0574L25.9757,12.0728C26.2149,12.1187 26.4562,12.1523 26.6987,12.1734C26.9973,12.2025 27.2802,12.2155 27.5505,12.2155C28.0537,12.2535 28.5569,12.1409 28.9996,11.8911C29.1544,11.7808 29.2862,11.6396 29.3872,11.4757C29.4881,11.3119 29.5562,11.1288 29.5873,10.9373C29.688,10.3136 29.7311,9.6814 29.7162,9.0493L29.7162,3.7323L28.5595,3.7323L28.5312,4.2026ZM28.3866,8.141C28.3673,8.3042 28.2886,8.4537 28.1666,8.5594C28.0239,8.6484 27.8583,8.6904 27.6919,8.6795C27.5059,8.6952 27.3195,8.6558 27.1544,8.5659C27.0258,8.4343 26.9514,8.2564 26.947,8.0695C26.8758,7.1909 26.8758,6.3076 26.947,5.4289C26.9515,5.2481 27.0236,5.076 27.1482,4.9488C27.3164,4.8621 27.5046,4.8251 27.6919,4.8417C27.8692,4.827 28.0458,4.8763 28.1917,4.9812C28.3215,5.1224 28.3994,5.306 28.4117,5.5003C28.4593,5.9169 28.4782,6.3364 28.4683,6.7558C28.4839,7.2191 28.4565,7.683 28.3866,8.141Z"
android:fillColor="@android:color/white"/>
<path
android:pathData="M34.5404,3.8346C34.1306,3.5968 33.6597,3.4902 33.1893,3.5287C32.7005,3.494 32.2126,3.6073 31.787,3.8541C31.4771,4.0813 31.2687,4.4243 31.2075,4.8078C31.1063,5.4337 31.0634,6.0679 31.0794,6.7021C31.0627,7.3375 31.1078,7.9731 31.2139,8.5996C31.2467,8.7891 31.3169,8.9698 31.4204,9.1309C31.5238,9.2921 31.6583,9.4302 31.8158,9.5369C32.2648,9.7724 32.7686,9.8782 33.2726,9.8428C33.5935,9.8421 33.9141,9.8236 34.2331,9.7875C34.4977,9.7629 34.7599,9.7172 35.0175,9.6509L35.0175,8.6321C34.4284,8.6939 33.9449,8.7265 33.5671,8.7265C33.2881,8.7431 33.0084,8.709 32.7411,8.6256C32.6588,8.5899 32.5865,8.5342 32.5306,8.4632C32.4748,8.3922 32.4371,8.3082 32.421,8.2187C32.3635,7.8595 32.3399,7.4955 32.3505,7.1317L35.1456,7.1317L35.1456,6.689C35.1609,6.0364 35.1277,5.3836 35.0463,4.7362C35.0005,4.3798 34.8188,4.0561 34.5404,3.8346ZM33.9321,6.1683L32.3505,6.1683C32.3503,5.8308 32.3728,5.4937 32.4178,5.1593C32.424,5.0798 32.4465,5.0024 32.484,4.9324C32.5216,4.8623 32.5732,4.801 32.6355,4.7524C32.9642,4.6181 33.3312,4.6181 33.66,4.7524C33.7208,4.8026 33.7708,4.8649 33.8072,4.9354C33.8435,5.0059 33.8654,5.0832 33.8713,5.1626C33.9211,5.4953 33.9414,5.8319 33.9321,6.1683Z"
android:fillColor="@android:color/white"/>
<path
android:pathData="M43.0589,4.5379C43.0049,4.2729 42.8609,4.0339 42.6507,3.8605C42.3989,3.6963 42.0997,3.6177 41.7986,3.6369C41.7986,3.6369 41.7693,3.6369 41.7499,3.6369C41.7499,3.6369 40.7779,3.5826 40.3761,4.0746C40.3324,4.13 40.2934,4.1887 40.2595,4.2503C40.1947,4.0846 40.0821,3.9413 39.9355,3.8382C39.6825,3.693 39.3918,3.624 39.0996,3.64C39.0996,3.64 38.1275,3.5826 37.7258,4.0778C37.6809,4.1323 37.6418,4.1912 37.6091,4.2535L37.5767,3.7359L36.3941,3.7359L36.3941,9.7365L37.6901,9.7365L37.6901,6.6084C37.6834,6.233 37.6975,5.8576 37.7323,5.4837C37.7406,5.2996 37.81,5.1233 37.9299,4.982C38.0732,4.866 38.2573,4.8109 38.4418,4.8286C38.6058,4.8156 38.7704,4.8465 38.9181,4.9181C38.9727,4.9699 39.0163,5.0319 39.0463,5.1004C39.0764,5.169 39.0923,5.2428 39.0931,5.3175C39.1272,5.6872 39.1412,6.0583 39.1351,6.4295L39.1351,9.7365L40.4312,9.7365L40.4312,6.6084C40.4312,6.0876 40.4312,5.7137 40.4701,5.4837C40.4788,5.2985 40.5507,5.1217 40.6742,4.982C40.8206,4.8672 41.0063,4.8123 41.1926,4.8286C41.3577,4.8157 41.5233,4.8466 41.6721,4.9181C41.7262,4.9702 41.7692,5.0323 41.7986,5.1008C41.8281,5.1694 41.8435,5.2431 41.8439,5.3175C41.8797,5.6871 41.8937,6.0583 41.886,6.4295L41.886,9.7365L43.182,9.7365L43.182,5.784C43.1892,5.3654 43.148,4.9473 43.0589,4.5379Z"
android:fillColor="@android:color/white"/>
<path
android:pathData="M23.9733,3.8677C23.7029,3.6964 23.3827,3.6156 23.0609,3.6376C23.0609,3.6376 23.0346,3.6376 23.0215,3.6376C23.0215,3.6376 22.0369,3.5865 21.6332,4.0755C21.6044,4.11 21.5781,4.1462 21.5544,4.1842L21.5249,3.7335L20.3269,3.7335L20.3269,9.7365L21.6398,9.7365L21.6398,6.6071C21.6328,6.2337 21.647,5.8602 21.6824,5.4883C21.6921,5.3027 21.7662,5.1258 21.8925,4.9865C22.0509,4.8661 22.2504,4.809 22.4504,4.8267C22.625,4.8143 22.8,4.8451 22.9592,4.9162C23.0182,4.9673 23.0659,5.0297 23.0992,5.0995C23.1325,5.1694 23.1507,5.2452 23.1528,5.3221C23.1903,5.6896 23.2057,6.0589 23.1988,6.4281L23.1988,9.7365L24.5116,9.7365L24.5116,5.84C24.5219,5.4113 24.4856,4.9827 24.4032,4.5614C24.3496,4.2876 24.1971,4.0415 23.9733,3.8677Z"
android:fillColor="@android:color/white"/>
<path
android:pathData="M13.3195,1.9385L11.99,1.9385L11.99,3.6595L11.1628,3.6595L11.1628,4.7597L11.99,4.7597L11.99,9.7361L13.3195,9.7361L13.3195,4.7597L14.3309,4.7597L14.3309,3.6595L13.3195,3.6595L13.3195,1.9385Z"
android:fillColor="@android:color/white"/>
android:width="51dp"
android:height="11dp"
android:viewportWidth="74"
android:viewportHeight="16">
<path
android:fillColor="#ffffff"
android:fillType="evenOdd"
android:pathData="M2.924,0H9.413C10.436,0 10.948,0 11.339,0.199C11.683,0.374 11.962,0.654 12.137,0.998C12.337,1.389 12.337,1.9 12.337,2.924V2.924V4.009H0V2.924C0,1.9 0,1.389 0.199,0.998C0.374,0.654 0.654,0.375 0.998,0.199C1.389,0 1.9,0 2.924,0ZM12.337,7.402H8.224L8.225,7.971V15.421H9.413H9.413C10.436,15.421 10.948,15.421 11.339,15.221C11.683,15.047 11.962,14.767 12.137,14.423C12.337,14.032 12.337,13.52 12.337,12.497V7.402ZM4.111,7.402H0V12.497C0,13.52 0,14.032 0.199,14.423C0.375,14.767 0.654,15.047 0.998,15.221C1.389,15.421 1.9,15.421 2.924,15.421H2.924H4.111V7.402ZM22.85,12.479C21.201,12.479 20.346,11.591 20.346,10.022V5.488H19.236V4.046H20.346V2.223H22.264V4.046H24.087V5.488H22.264V9.848C22.264,10.577 22.612,10.91 23.215,10.91C23.595,10.91 23.881,10.862 24.15,10.751V12.273C23.849,12.384 23.437,12.479 22.85,12.479ZM28.298,11.116C29.471,11.116 30.343,10.498 30.343,9.436V8.611H29.36C27.901,8.611 26.966,8.944 26.966,9.959C26.966,10.656 27.347,11.116 28.298,11.116ZM27.838,12.495C26.316,12.495 25.064,11.766 25.064,10.038C25.064,8.104 26.934,7.406 29.312,7.406H30.343V6.978C30.343,5.869 29.962,5.314 28.837,5.314C27.838,5.314 27.347,5.789 27.251,6.614H25.397C25.555,4.68 27.093,3.871 28.948,3.871C30.802,3.871 32.261,4.632 32.261,6.899V12.337H30.374V11.322C29.835,12.035 29.106,12.495 27.838,12.495ZM33.841,4.046V12.337H35.759V7.454C35.759,6.138 36.631,5.472 37.709,5.472C38.85,5.472 39.358,6.043 39.358,7.295V12.337H41.276V7.121C41.276,4.838 40.103,3.871 38.438,3.871C37.075,3.871 36.155,4.553 35.759,5.361V4.046H33.841ZM46.72,10.529C48.051,10.529 49.018,9.578 49.018,7.993V7.882C49.018,6.313 48.162,5.377 46.783,5.377C45.356,5.377 44.532,6.408 44.532,7.914V8.041C44.532,9.578 45.483,10.529 46.72,10.529ZM46.656,15.38C44.056,15.38 42.883,14.207 42.661,12.622H44.595C44.754,13.446 45.388,13.922 46.64,13.922C48.13,13.922 48.955,13.177 48.955,11.687V10.466C48.495,11.259 47.465,12.004 46.244,12.004C44.167,12.004 42.566,10.45 42.566,8.056V7.945C42.566,5.615 44.151,3.871 46.292,3.871C47.655,3.871 48.479,4.49 48.955,5.314V4.046H50.873V11.718C50.857,14.16 49.224,15.38 46.656,15.38ZM52.249,8.278C52.249,10.894 54.009,12.495 56.45,12.495C58.574,12.495 59.985,11.544 60.255,9.8H58.4C58.257,10.609 57.655,11.068 56.498,11.068C55.071,11.068 54.278,10.181 54.215,8.611H60.286V8.056C60.286,5.092 58.431,3.871 56.371,3.871C54.009,3.871 52.249,5.583 52.249,8.151V8.278ZM58.384,7.327H54.246C54.437,6.043 55.213,5.266 56.371,5.266C57.56,5.266 58.289,5.9 58.384,7.327ZM61.651,12.337V4.046H63.569V5.314C63.965,4.537 64.869,3.871 66.089,3.871C67.167,3.871 68.023,4.331 68.436,5.361C69.07,4.331 70.211,3.871 71.241,3.871C72.763,3.871 74,4.807 74,7.089V12.337H72.081V7.216C72.081,5.996 71.558,5.472 70.607,5.472C69.656,5.472 68.784,6.107 68.784,7.375V12.337H66.866V7.216C66.866,5.996 66.327,5.472 65.392,5.472C64.441,5.472 63.569,6.107 63.569,7.375V12.337H61.651Z" />
</vector>

View file

@ -1,15 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="96dp"
android:height="96dp"
android:viewportWidth="96"
android:viewportHeight="96">
android:viewportWidth="98"
android:viewportHeight="98">
<path
android:fillColor="#1E1E1E"
android:pathData="M56.27,47.27V78H69.56C70.07,78 70.57,77.8 70.93,77.43C71.3,77.07 71.5,76.57 71.5,76.06V47.27H56.27Z" />
android:pathData="M59.285,24.464H38.715C35.469,24.464 33.848,24.464 32.607,25.095C31.517,25.652 30.629,26.536 30.076,27.626C29.444,28.867 29.444,30.488 29.444,33.734V37.175H68.555V33.734C68.555,30.488 68.555,28.867 67.924,27.626C67.367,26.536 66.483,25.648 65.393,25.095C64.152,24.464 62.531,24.464 59.285,24.464Z" />
<path
android:fillColor="#1E1E1E"
android:pathData="M25.5,76.06C25.5,76.57 25.71,77.07 26.07,77.43C26.43,77.8 26.93,78 27.44,78H40.73V47.27H25.5V76.06Z" />
android:pathData="M55.516,47.93H68.554V64.082C68.554,67.328 68.554,68.949 67.923,70.19C67.366,71.28 66.482,72.168 65.392,72.721C64.151,73.353 62.53,73.353 59.284,73.353H55.52L55.516,47.93Z" />
<path
android:fillColor="#1E1E1E"
android:pathData="M71.5,32.2V18.94C71.5,18.43 71.29,17.93 70.93,17.57C70.56,17.2 70.07,17 69.55,17H27.45C26.94,17 26.44,17.2 26.07,17.57C25.71,17.93 25.5,18.43 25.5,18.94V32.2H71.5Z" />
android:pathData="M29.444,47.93H42.479V73.353H38.715C35.469,73.353 33.848,73.353 32.607,72.721C31.517,72.168 30.633,71.28 30.076,70.19C29.444,68.949 29.444,67.328 29.444,64.082V47.93Z" />
</vector>

View file

@ -1,15 +1,15 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="96dp"
android:height="96dp"
android:viewportWidth="96"
android:viewportHeight="96">
android:viewportWidth="450"
android:viewportHeight="450">
<path
android:pathData="M56.271,47.274V78H69.557C70.073,78 70.567,77.795 70.931,77.431C71.295,77.066 71.5,76.572 71.5,76.056V47.274H56.271Z"
android:fillColor="#ffffff"/>
android:fillColor="#ffffff"
android:pathData="M284.96,82H165.04C146.12,82 136.67,82 129.44,85.68C123.08,88.93 117.91,94.08 114.68,100.44C111,107.67 111,117.12 111,136.04V156.1H339V136.04C339,117.12 339,107.67 335.32,100.44C332.07,94.08 326.92,88.91 320.56,85.68C313.33,82 303.88,82 284.96,82Z" />
<path
android:pathData="M25.5,76.056C25.5,76.572 25.705,77.066 26.069,77.431C26.433,77.795 26.927,78 27.443,78H40.729V47.274H25.5V76.056Z"
android:fillColor="#ffffff"/>
android:fillColor="#ffffff"
android:pathData="M262.99,218.8H338.99V312.96C338.99,331.88 338.99,341.33 335.31,348.56C332.07,354.92 326.91,360.09 320.56,363.32C313.32,367 303.87,367 284.95,367H263.01L262.99,218.8Z" />
<path
android:pathData="M71.5,32.204V18.94C71.5,18.425 71.294,17.932 70.928,17.568C70.562,17.204 70.065,17 69.547,17H27.453C26.935,17 26.438,17.204 26.072,17.568C25.706,17.932 25.5,18.425 25.5,18.94V32.204H71.5Z"
android:fillColor="#ffffff"/>
android:fillColor="#ffffff"
android:pathData="M111,218.8H186.99V367H165.04C146.12,367 136.67,367 129.44,363.32C123.08,360.09 117.93,354.92 114.68,348.56C111,341.33 111,331.88 111,312.96V218.8Z" />
</vector>

View file

@ -8,7 +8,6 @@
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_wallet_main_action"
style="@style/TapPrimaryIconButton"
android:layout_width="204dp"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/home_bottom_fl_scan_card_container_margin_bottom"
app:layout_constraintBottom_toBottomOf="parent"
@ -20,7 +19,6 @@
<com.google.android.material.button.MaterialButton
android:id="@+id/btn_wallet_alternative_action"
style="@style/TapTextButton"
android:layout_width="204dp"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/home_bottom_btn_alternative_action_margin_bottom"
app:layout_constraintBottom_toBottomOf="parent"

View file

@ -3,6 +3,9 @@
<string name="address_type_default">По умолчанию</string>
<string name="address_type_legacy">Устаревший</string>
<string name="common_fee_error">Не удалось получить комиссию</string>
<string name="common_utxo_validate_withdrawal_message_warning">Из-за ограничений %1$s в одну транзакцию может поместиться только %2$d UTXO. Это означает, что вы можете отправить только %3$s или меньше. Вам нужно уменьшить сумму.</string>
<string name="eth_gas_required_exceeds_allowance">Недостаточно средств для совершения транзакции. Пожалуйста, пополните свой аккаунт.</string>
<string name="generic_error_code">Произошла ошибка. Код: %s.</string>
<string name="kaspa_withdrawal_message_warning">Из-за ограничений Kaspa в одну транзакцию может поместиться только %1$d UTXO. Это означает, что вы можете отправить только %2$s или меньше. Вам нужно уменьшить сумму.</string>
<string name="no_account_generic">Пополните счет на %1$s+ %2$s, чтобы создать аккаунт</string>
<string name="no_account_polkadot">Аккаунт получателя не активирован. Отправьте %s или более для активации аккаунта.</string>

View file

@ -568,5 +568,5 @@
<string name="welcome_unlock">Войти с %s</string>
<string name="welcome_unlock_card">Сканировать карту</string>
<string name="welcome_unlock_description">Используйте %s или отсканируйте карту для входа в приложение</string>
<string name="welcome_unlock_title">C возвращением!</string>
<string name="welcome_unlock_title">С возвращением!</string>
</resources>

View file

@ -3,6 +3,9 @@
<string name="address_type_default">Default</string>
<string name="address_type_legacy">Legacy</string>
<string name="common_fee_error">Failed to get fee</string>
<string name="common_utxo_validate_withdrawal_message_warning">Due to %1$s limitations only %2$d UTXOs can fit in a single transaction. This means you can only send %3$s or less. You need to reduce the amount.</string>
<string name="eth_gas_required_exceeds_allowance">Not enough funds for the transaction. Please top up your account.</string>
<string name="generic_error_code">An error occurred. Code: %s.</string>
<string name="kaspa_withdrawal_message_warning">Due to Kaspa limitations only %1$d UTXOs can fit in a single transaction. This means you can only send %2$s or less. You need to reduce the amount.</string>
<string name="no_account_generic">Load %1$s+ %2$s to create account</string>
<string name="no_account_polkadot">Destination account is not active. Send %s or more to activate the account.</string>

View file

@ -1,12 +1,20 @@
package com.tangem.core.ui.components.buttons.common
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.requiredSizeIn
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.SpacerW
import com.tangem.core.ui.res.TangemTheme
@Suppress("LongParameterList")
@ -30,67 +38,77 @@ fun TangemButton(
elevation = elevation,
shape = size.toShape(),
colors = colors,
contentPadding = if (showProgress) ButtonDefaults.ContentPadding else size.toContentPadding(icon = icon),
contentPadding = size.toContentPadding(icon = icon),
) {
ButtonContent(
text = text,
textStyle = textStyle,
ButtonContentContainer(
buttonIcon = icon,
colors = colors,
iconPadding = size.toIconPadding(),
showProgress = showProgress,
enabled = enabled,
size = size,
progressIndicator = {
CircularProgressIndicator(
modifier = Modifier.buttonContentSize(textStyle),
color = colors.contentColor(enabled = enabled).value,
strokeWidth = TangemTheme.dimens.size4,
)
},
text = {
Text(
modifier = Modifier.alignByBaseline(),
text = text,
style = textStyle,
color = colors.contentColor(enabled = enabled).value,
textAlign = TextAlign.Center,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
},
icon = { iconResId ->
Icon(
modifier = Modifier.buttonContentSize(textStyle),
painter = painterResource(id = iconResId),
tint = colors.contentColor(enabled = enabled).value,
contentDescription = null,
)
},
)
}
}
@Suppress("LongParameterList")
@Composable
private fun ButtonContent(
text: String,
textStyle: TextStyle,
private inline fun RowScope.ButtonContentContainer(
buttonIcon: TangemButtonIconPosition,
colors: ButtonColors,
size: TangemButtonSize,
enabled: Boolean,
iconPadding: Dp,
showProgress: Boolean,
progressIndicator: @Composable RowScope.() -> Unit,
text: @Composable RowScope.() -> Unit,
icon: @Composable RowScope.(Int) -> Unit,
) {
val icon = @Composable { iconResId: Int ->
Icon(
modifier = Modifier.size(TangemTheme.dimens.size20),
painter = painterResource(id = iconResId),
tint = colors.contentColor(enabled = enabled).value,
contentDescription = null,
)
if (showProgress) {
progressIndicator()
} else {
if (buttonIcon is TangemButtonIconPosition.Start) {
icon(buttonIcon.iconResId)
SpacerW(width = iconPadding)
}
text()
if (buttonIcon is TangemButtonIconPosition.End) {
SpacerW(width = iconPadding)
icon(buttonIcon.iconResId)
}
}
}
private fun Modifier.buttonContentSize(buttonTextStyle: TextStyle): Modifier = composed {
val minContentElementSize = TangemTheme.dimens.size20
val maxContentElementSize = remember(key1 = buttonTextStyle.lineHeight) {
buttonTextStyle.lineHeight.value.dp + 4.dp
}
if (showProgress) {
Box(modifier = Modifier.wrapContentSize()) {
CircularProgressIndicator(
modifier = Modifier
.align(Alignment.Center)
.size(TangemTheme.dimens.size24),
color = colors.contentColor(enabled = enabled).value,
strokeWidth = TangemTheme.dimens.size4,
)
}
} else {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(size.toIconPadding()),
) {
if (buttonIcon is TangemButtonIconPosition.Start) {
icon(buttonIcon.iconResId)
}
Text(
text = text,
style = textStyle,
color = colors.contentColor(enabled = enabled).value,
maxLines = 1,
)
if (buttonIcon is TangemButtonIconPosition.End) {
icon(buttonIcon.iconResId)
}
}
}
this.requiredSizeIn(
minWidth = minContentElementSize,
minHeight = minContentElementSize,
maxWidth = maxContentElementSize,
maxHeight = maxContentElementSize,
)
}

View file

@ -80,7 +80,7 @@ okHttp-prettyLogging = "3.1.0"
# endregion Other libraries
# region Tangem
tangemBlockchainSdk = "release-app_4.10-336"
tangemBlockchainSdk = "release-app_4.10-341"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "release-app_4.10-292"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds