Updated on 2026-08-14

This commit is contained in:
Tangem 2021-04-22 13:53:48 +03:00
parent e57d8ca630
commit 0571d8bb80
31 changed files with 507 additions and 289 deletions

View file

@ -203,6 +203,5 @@ private fun prepareAllowedSecurityOptions(card: Card): EnumSet<SecurityOption> {
private fun Card.toCardInfo(): CardInfo? {
val cardId = this.cardId.chunked(4).joinToString(separator = " ")
val issuer = this.cardData?.issuerName ?: return null
val signedHashes = this.walletSignedHashes ?: return null
return CardInfo(cardId, issuer, signedHashes)
return CardInfo(cardId, issuer)
}

View file

@ -26,7 +26,6 @@ data class DetailsState(
data class CardInfo(
val cardId: String,
val issuer: String,
val signedHashes: Int,
)
enum class EraseWalletState { Allowed, NotAllowedByCard, NotEmpty }

View file

@ -18,7 +18,7 @@ class CreateTwinWalletMiddleware {
fun handle(action: DetailsAction.CreateTwinWalletAction) {
when (action) {
is DetailsAction.CreateTwinWalletAction.ShowWarning -> {
val wallet = store.state.globalState.scanNoteResponse?.walletManager?.wallet
val wallet = store.state.detailsState.wallets.firstOrNull()
if (wallet == null) {
store.dispatch(NavigationAction.NavigateTo(AppScreen.CreateTwinWalletWarning))
return

View file

@ -74,12 +74,8 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
}
tv_card_id.text = cardId
tv_issuer.text = state.cardInfo.issuer
tv_signed_hashes.text = state.cardInfo.signedHashes.toString()
}
tv_signed_hashes.show(state.card?.isTwinCard() != true)
tv_signed_hashes_title.show(state.card?.isTwinCard() != true)
tv_disclaimer.setOnClickListener { store.dispatch(DetailsAction.ShowDisclaimer) }
tv_card_tou.show(state.cardTermsOfUseUrl != null)

View file

@ -18,6 +18,7 @@ import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.Wallet
import com.tangem.commands.common.card.Card
import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.domain.extensions.signedHashesCount
import com.tangem.tap.store
import timber.log.Timber
import java.io.File
@ -161,7 +162,7 @@ class AdditionalEmailInfo {
fun setCardInfo(card: Card) {
cardId = card.cardId
cardFirmwareVersion = card.firmwareVersion.version
signedHashesCount = card.walletSignedHashes?.toString() ?: "0"
signedHashesCount = card.signedHashesCount().toString()
}
fun setWalletsInfo(wallets: List<Wallet>) {
@ -274,7 +275,6 @@ class FeedbackEmail : EmailData {
val builder = StringBuilder()
builder.appendKeyValue("Card ID", infoHolder.cardId)
builder.appendKeyValue("Firmware version", infoHolder.cardFirmwareVersion)
builder.appendKeyValue("Signed hashes", infoHolder.signedHashesCount)
infoHolder.walletsInfo.forEach {
builder.appendKeyValue("Blockchain", it.blockchain.fullName)

View file

@ -4,14 +4,13 @@ import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras
import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder
import com.tangem.blockchain.common.*
import com.tangem.blockchain.extensions.Result
import com.tangem.blockchain.extensions.Signer
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.commands.common.card.Card
import com.tangem.tap.common.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
import com.tangem.tap.common.extensions.stripZeroPlainString
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.global.GlobalAction
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.TapWorkarounds
@ -115,11 +114,10 @@ private fun sendTransaction(
val result = (walletManager as TransactionSender).send(txData, signer)
withContext(Dispatchers.Main) {
when (result) {
is Result.Success -> {
is SimpleResult.Success -> {
tangemSdk.config.linkedTerminal = isLinkedTerminal
FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.TRANSACTION_IS_SENT, card)
dispatch(SendAction.SendSuccess)
dispatch(GlobalAction.UpdateWalletSignedHashes(result.data.walletSignedHashes))
dispatch(NavigationAction.PopBackTo())
scope.launch(Dispatchers.IO) {
withContext(Dispatchers.Main) {
@ -131,7 +129,7 @@ private fun sendTransaction(
}
}
}
is Result.Failure -> {
is SimpleResult.Failure -> {
when (result.error) {
is CreateAccountUnderfunded -> {
val error = result.error as CreateAccountUnderfunded

View file

@ -5,7 +5,6 @@ import com.tangem.tap.common.CurrencyConverter
import com.tangem.tap.features.send.redux.*
import com.tangem.tap.features.send.redux.states.IdStateHolder
import com.tangem.tap.features.send.redux.states.SendState
import com.tangem.tap.store
import org.rekotlin.Action
import java.math.BigDecimal
@ -60,8 +59,7 @@ private class EmptyReducer : SendInternalReducer {
private class PrepareSendScreenStatesReducer : SendInternalReducer {
override fun handle(action: SendScreenAction, sendState: SendState): SendState {
val prepareAction = action as PrepareSendScreen
val walletManager = action.walletManager
?: store.state.globalState.scanNoteResponse!!.walletManager!!
val walletManager = action.walletManager!!
val amountToExtract = prepareAction.tokenAmount ?: prepareAction.coinAmount!!
val decimals = amountToExtract.decimals

View file

@ -13,8 +13,9 @@ class TokensMiddleware {
{ action ->
when (action) {
is TokensAction.LoadCurrencies -> {
val cardFirmware = state()?.globalState?.scanNoteResponse?.card?.firmwareVersion
val tokens = currenciesRepository.getPopularTokens()
val blockchains = currenciesRepository.getBlockchains()
val blockchains = currenciesRepository.getBlockchains(cardFirmware)
val currencies = CurrencyListItem.createListOfCurrencies(
blockchains, tokens
)

View file

@ -10,6 +10,8 @@ import com.tangem.tap.common.redux.global.GlobalState
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.currenciesRepository
import com.tangem.tap.domain.extensions.makeWalletManagerForApp
import com.tangem.tap.domain.extensions.makeWalletManagersForApp
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.scope
@ -46,7 +48,7 @@ class MultiWalletMiddleware {
globalState?.scanNoteResponse?.card?.let { card ->
currenciesRepository.saveAddedBlockchain(card.cardId, action.blockchain)
globalState.tapWalletManager.walletManagerFactory
.makeWalletManager(card, action.blockchain)?.let {
.makeWalletManagerForApp(card, action.blockchain)?.let {
store.dispatch(WalletAction.MultiWallet.AddWalletManagers(it))
}
}
@ -66,9 +68,10 @@ class MultiWalletMiddleware {
}
}
is WalletAction.MultiWallet.FindBlockchainsInUse -> {
val blockchains = currenciesRepository.getBlockchains()
val cardFirmware = globalState?.scanNoteResponse?.card?.firmwareVersion
val blockchains = currenciesRepository.getBlockchains(cardFirmware)
.filterNot { walletState?.blockchains?.contains(it) == true }
val walletManagers = action.factory.makeWalletManagers(action.card, blockchains)
val walletManagers = action.factory.makeWalletManagersForApp(action.card, blockchains)
scope.launch {
walletManagers.map { walletManager ->

View file

@ -108,7 +108,11 @@ class WalletMiddleware {
)
when (result) {
is CompletionResult.Success -> {
globalState?.tapWalletManager?.onCardScanned(result.data)
val scanNoteResponse =
globalState?.scanNoteResponse?.copy(card = result.data)
scanNoteResponse?.let {
globalState.tapWalletManager.onCardScanned(scanNoteResponse)
}
}
}
}

View file

@ -6,7 +6,7 @@ import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.commands.common.card.Card
import com.tangem.commands.common.card.CardType
import com.tangem.commands.verifycard.VerifyCardState
import com.tangem.commands.verification.VerifyCardState
import com.tangem.common.extensions.getType
import com.tangem.tap.common.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
@ -14,6 +14,8 @@ import com.tangem.tap.common.extensions.isGreaterThan
import com.tangem.tap.common.redux.global.GlobalState
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
import com.tangem.tap.domain.extensions.getSingleWallet
import com.tangem.tap.domain.extensions.hasSignedHashes
import com.tangem.tap.domain.isMultiwalletAllowed
import com.tangem.tap.domain.twins.isTwinCard
import com.tangem.tap.features.wallet.redux.WalletAction
@ -63,13 +65,12 @@ class WarningsMiddleware {
}
private fun showCardWarningsIfNeeded(globalState: GlobalState?) {
val validator = globalState?.scanNoteResponse?.walletManager as? SignatureCountValidator
globalState?.scanNoteResponse?.card?.let { card ->
globalState.warningManager?.removeWarnings(WarningMessage.Origin.Local)
if (card.getType() != CardType.Release) {
addWarningMessage(WarningMessagesManager.devCardWarning())
} else if (!preferencesStorage.wasCardScannedBefore(card.cardId)) {
checkIfWarningNeeded(card, validator)?.let { addWarningMessage(it) }
checkIfWarningNeeded(card)?.let { warning -> addWarningMessage(warning) }
}
if (card.getType() == CardType.Release) {
if (globalState.scanNoteResponse.verifyResponse?.verificationState ==
@ -82,12 +83,14 @@ class WarningsMiddleware {
}
private fun checkIfWarningNeeded(
card: Card, signatureCountValidator: SignatureCountValidator? = null,
card: Card
): WarningMessage? {
if (card.isTwinCard() || card.isMultiwalletAllowed) return null
if (card.isTwinCard()) return null
return if (signatureCountValidator == null) {
if (card.walletSignedHashes ?: 0 > 0) {
val validator = store.state.walletState.walletManagers.firstOrNull()
as? SignatureCountValidator
return if (validator == null || card.isMultiwalletAllowed) {
if (card.hasSignedHashes()) {
WarningMessagesManager.alreadySignedHashesWarning()
} else {
store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId)
@ -108,10 +111,11 @@ class WarningsMiddleware {
if (card.isTwinCard() || card.isMultiwalletAllowed) return
val validator = store.state.globalState.scanNoteResponse?.walletManager
val validator = store.state.walletState.walletManagers.firstOrNull()
as? SignatureCountValidator
scope.launch {
val result = validator?.validateSignatureCount(card.walletSignedHashes ?: 0)
val signedHashes = card.getSingleWallet()?.signedHashes ?: 0
val result = validator?.validateSignatureCount(signedHashes)
withContext(Dispatchers.Main) {
when (result) {
SimpleResult.Success -> {
@ -121,7 +125,7 @@ class WarningsMiddleware {
is SimpleResult.Failure ->
if (result.error is BlockchainSdkError.SignatureCountNotMatched) {
addWarningMessage(WarningMessagesManager.alreadySignedHashesWarning(), true)
} else if (card.walletSignedHashes ?: 0 > 0) {
} else if (signedHashes > 0) {
addWarningMessage(WarningMessagesManager.alreadySignedHashesWarning(), true)
}
}

View file

@ -34,28 +34,26 @@ class MultiWalletView : WalletView {
override fun changeWalletView(fragment: WalletFragment) {
setFragment(fragment)
onViewCreated()
showMultiWalletView()
setupButtons()
showMultiWalletView(fragment)
setupButtons(fragment)
}
private fun showMultiWalletView() {
val fragment = fragment ?: return
fragment.tv_twin_card_number.hide()
fragment.iv_twin_card.hide()
fragment.rv_pending_transaction.hide()
fragment.l_card_balance.hide()
fragment.l_address.hide()
fragment.l_buttons_short.hide()
fragment.l_buttons_long.hide()
fragment.btn_scan_multiwallet?.show()
fragment.rv_multiwallet.show()
fragment.btn_add_token.show()
private fun showMultiWalletView(fragment: WalletFragment) = with(fragment) {
tv_twin_card_number.hide()
iv_twin_card.hide()
rv_pending_transaction.hide()
l_card_balance.hide()
l_address.hide()
l_buttons_short.hide()
l_buttons_long.hide()
btn_scan_multiwallet?.show()
rv_multiwallet.show()
btn_add_token.show()
}
private fun setupButtons() {
val fragment = fragment ?: return
fragment.btn_scan_multiwallet?.setOnClickListener { store.dispatch(WalletAction.Scan) }
private fun setupButtons(fragment: WalletFragment) = with(fragment) {
btn_scan_multiwallet?.setOnClickListener { store.dispatch(WalletAction.Scan) }
}
override fun setFragment(fragment: WalletFragment) {
@ -114,22 +112,22 @@ class MultiWalletView : WalletView {
private fun showErrorState(
fragment: WalletFragment, errorTitle: CharSequence, errorDescription: CharSequence,
) {
fragment.l_card_balance.show()
fragment.l_balance.hide()
fragment.l_balance_error.show()
fragment.rv_multiwallet.hide()
fragment.btn_add_token.hide()
fragment.tv_error_title.text = errorTitle
fragment.tv_error_descriptions.text = errorDescription
) = with(fragment) {
l_card_balance.show()
l_balance.hide()
l_balance_error.show()
rv_multiwallet.hide()
btn_add_token.hide()
tv_error_title.text = errorTitle
tv_error_descriptions.text = errorDescription
}
private fun configureButtonsForEmptyWalletState(fragment: WalletFragment) {
fragment.btn_scan_multiwallet.hide()
fragment.l_buttons_long.show()
fragment.btn_scan_long.setOnClickListener { store.dispatch(WalletAction.Scan) }
fragment.btn_confirm_long.setOnClickListener { store.dispatch(WalletAction.CreateWallet) }
fragment.btn_confirm_long.text = fragment.getText(R.string.wallet_button_create_wallet)
private fun configureButtonsForEmptyWalletState(fragment: WalletFragment) = with(fragment) {
btn_scan_multiwallet.hide()
l_buttons_long.show()
btn_scan_long.setOnClickListener { store.dispatch(WalletAction.Scan) }
btn_confirm_long.setOnClickListener { store.dispatch(WalletAction.CreateWallet) }
btn_confirm_long.text = fragment.getText(R.string.wallet_button_create_wallet)
}
private fun handleDialogs(walletDialog: StateDialog?) {

View file

@ -44,17 +44,16 @@ class SingleWalletView : WalletView {
override fun changeWalletView(fragment: WalletFragment) {
setFragment(fragment)
onViewCreated()
showSingleWalletView()
showSingleWalletView(fragment)
}
private fun showSingleWalletView() {
val fragment = fragment ?: return
fragment.rv_multiwallet.hide()
fragment.btn_add_token.hide()
fragment.btn_scan_multiwallet?.hide()
fragment.rv_pending_transaction.hide()
fragment.l_card_balance.show()
fragment.l_address.show()
private fun showSingleWalletView(fragment: WalletFragment) = with(fragment) {
rv_multiwallet.hide()
btn_add_token.hide()
btn_scan_multiwallet?.hide()
rv_pending_transaction.hide()
l_card_balance.show()
l_address.show()
}
override fun onViewCreated() {
@ -70,13 +69,15 @@ class SingleWalletView : WalletView {
}
override fun onNewState(state: WalletState) {
setupTwinCards(state.twinCardsState)
val fragment = fragment ?: return
state.primaryWallet ?: return
setupButtons(state.primaryWallet, state.twinCardsState != null)
setupAddressCard(state.primaryWallet)
setupTwinCards(state.twinCardsState, fragment)
setupButtons(state.primaryWallet, state.twinCardsState != null, fragment)
setupAddressCard(state.primaryWallet, fragment)
showPendingTransactionsIfPresent(state.primaryWallet.pendingTransactions)
setupBalance(state, state.primaryWallet)
handleDialogs(state.walletDialog)
handleDialogs(state.walletDialog, fragment)
}
private fun showPendingTransactionsIfPresent(pendingTransactions: List<PendingTransaction>) {
@ -92,43 +93,45 @@ class SingleWalletView : WalletView {
}
}
private fun setupTwinCards(twinCardsState: TwinCardsState?) {
fragment?.apply {
twinCardsState?.cardNumber?.let { cardNumber ->
this.tv_twin_card_number.show()
this.iv_twin_card.show()
val number = when (cardNumber) {
TwinCardNumber.First -> "1"
TwinCardNumber.Second -> "2"
}
this.tv_twin_card_number.text =
this.getString(R.string.wallet_twins_chip_format, number)
}
if (twinCardsState?.cardNumber == null) {
this.tv_twin_card_number.hide()
this.iv_twin_card.hide()
}
if (twinCardsState?.showTwinOnboarding == true) {
store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding))
private fun setupTwinCards(
twinCardsState: TwinCardsState?, fragment: WalletFragment
) = with(fragment) {
twinCardsState?.cardNumber?.let { cardNumber ->
tv_twin_card_number.show()
iv_twin_card.show()
val number = when (cardNumber) {
TwinCardNumber.First -> "1"
TwinCardNumber.Second -> "2"
}
tv_twin_card_number.text =
this.getString(R.string.wallet_twins_chip_format, number)
}
if (twinCardsState?.cardNumber == null) {
tv_twin_card_number.hide()
iv_twin_card.hide()
}
if (twinCardsState?.showTwinOnboarding == true) {
store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding))
}
}
private fun setupButtons(state: WalletData, isTwinsWallet: Boolean) {
val fragment = fragment ?: return
private fun setupButtons(
state: WalletData, isTwinsWallet: Boolean, fragment: WalletFragment
) = with(fragment){
setupButtonsType(state, fragment)
val btnConfirm = if (state.topUpState.allowed) {
fragment.btn_confirm_short
btn_confirm_short
} else {
fragment.btn_confirm_long
btn_confirm_long
}
val btnScan = if (state.topUpState.allowed) {
fragment.btn_scan_short
btn_scan_short
} else {
fragment.btn_scan_long
btn_scan_long
}
setupConfirmButton(state, btnConfirm, fragment, isTwinsWallet)
@ -137,27 +140,27 @@ class SingleWalletView : WalletView {
store.dispatch(WalletAction.Scan)
}
fragment.btn_copy.setOnClickListener {
btn_copy.setOnClickListener {
state.walletAddresses?.selectedAddress?.address?.let { addressString ->
store.dispatch(WalletAction.CopyAddress(addressString, fragment.requireContext()))
}
}
fragment.btn_show_qr.setOnClickListener { store.dispatch(WalletAction.ShowDialog.QrCode) }
btn_show_qr.setOnClickListener { store.dispatch(WalletAction.ShowDialog.QrCode) }
fragment.btn_top_up.setOnClickListener {
btn_top_up.setOnClickListener {
store.dispatch(
WalletAction.TopUpAction.TopUp(fragment.requireContext(), R.color.backgroundLightGray)
)
}
}
private fun setupButtonsType(state: WalletData, fragment: WalletFragment) {
private fun setupButtonsType(state: WalletData, fragment: WalletFragment) = with(fragment) {
if (state.topUpState.allowed) {
fragment.l_buttons_long.hide()
fragment.l_buttons_short.show()
l_buttons_long.hide()
l_buttons_short.show()
} else {
fragment.l_buttons_long.show()
fragment.l_buttons_short.hide()
l_buttons_long.show()
l_buttons_short.hide()
}
}
@ -185,39 +188,37 @@ class SingleWalletView : WalletView {
}
private fun setupAddressCard(state: WalletData) {
val fragment = fragment ?: return
private fun setupAddressCard(state: WalletData, fragment: WalletFragment) = with(fragment) {
if (state.walletAddresses != null && state.blockchain != null) {
fragment.l_address?.show()
l_address?.show()
if (state.shouldShowMultipleAddress()) {
(fragment.l_address as? ViewGroup)?.beginDelayedTransition()
fragment.chip_group_address_type.show()
fragment.chip_group_address_type.fitChipsByGroupWidth()
(l_address as? ViewGroup)?.beginDelayedTransition()
chip_group_address_type.show()
chip_group_address_type.fitChipsByGroupWidth()
val checkedId = MultipleAddressUiHelper.typeToId(state.walletAddresses.selectedAddress.type)
if (checkedId != View.NO_ID) fragment.chip_group_address_type.check(checkedId)
if (checkedId != View.NO_ID) chip_group_address_type.check(checkedId)
fragment.chip_group_address_type.setOnCheckedChangeListener { group, checkedId ->
chip_group_address_type.setOnCheckedChangeListener { group, checkedId ->
if (checkedId == -1) return@setOnCheckedChangeListener
val type = MultipleAddressUiHelper.idToType(checkedId, state.blockchain)
type?.let { store.dispatch(WalletAction.ChangeSelectedAddress(type)) }
}
} else {
fragment.chip_group_address_type.hide()
chip_group_address_type.hide()
}
fragment.tv_address.text = state.walletAddresses.selectedAddress.address
fragment.tv_explore?.setOnClickListener {
tv_address.text = state.walletAddresses.selectedAddress.address
tv_explore?.setOnClickListener {
store.dispatch(WalletAction.ExploreAddress(
state.walletAddresses.selectedAddress.exploreUrl,
fragment.requireContext()))
}
} else {
fragment.l_address?.hide()
l_address?.hide()
}
}
private fun handleDialogs(walletDialog: StateDialog?) {
val fragment = fragment ?: return
private fun handleDialogs(walletDialog: StateDialog?, fragment: WalletFragment) {
val context = fragment.context ?: return
when (walletDialog) {
is WalletDialog.QrDialog -> {