Updated on 2026-08-14

This commit is contained in:
Tangem 2021-03-17 16:50:41 +03:00
parent 427fe69313
commit 04cb29ad22
13 changed files with 157 additions and 531 deletions

View file

@ -23,7 +23,7 @@ val globalMiddleware: Middleware<AppState> = { dispatch, appState ->
if (it.hideWarning(action.warning)) {
if (WarningMessagesManager.isAlreadySignedHashesWarning(action.warning)) {
//TODO: No appropriate warningMessage identification. Make it better later
store.dispatch(WalletAction.CheckSignedHashes.SaveCardId)
store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId)
}
store.dispatch(WalletAction.Warnings.SetWarnings(

View file

@ -140,7 +140,6 @@ class TapWalletManager {
suspend fun loadData(data: ScanNoteResponse) {
withContext(Dispatchers.Main) {
//TODO: I made it to WalletAction.CheckSignedHashes.CheckIfWarningNeeded
store.dispatch(WalletAction.Warnings.CheckIfNeeded)
val artworkId = data.verifyResponse?.artworkInfo?.id
if (data.walletManager != null) {

View file

@ -1,12 +1,11 @@
package com.tangem.tap.features.wallet.redux
import android.content.Context
import com.tangem.blockchain.common.*
import com.tangem.TangemError
import com.tangem.blockchain.common.Amount
import com.tangem.blockchain.common.Wallet
import com.tangem.blockchain.common.*
import com.tangem.blockchain.common.address.AddressType
import com.tangem.commands.common.card.Card
import com.tangem.tap.common.redux.ErrorAction
import com.tangem.tap.common.redux.NotificationAction
import com.tangem.tap.common.redux.global.CryptoCurrencyName
import com.tangem.tap.domain.TapError
@ -63,21 +62,20 @@ sealed class WalletAction : Action {
data class SetPrimaryToken(val token: Token) : MultiWallet()
}
sealed class CheckSignedHashes : WalletAction() {
object CheckIfWarningNeeded : CheckSignedHashes()
object CheckHashesCountOnline : CheckSignedHashes()
object NeedToCheckHashesCountOnline : CheckSignedHashes()
object ConfirmHashesCount : CheckSignedHashes()
object SaveCardId : CheckSignedHashes()
}
sealed class Warnings : WalletAction() {
object CheckHashesCount : Warnings() {
object CheckHashesCountOnline : Warnings()
object NeedToCheckHashesCountOnline : Warnings()
object ConfirmHashesCount : Warnings()
object SaveCardId : Warnings()
}
object Warnings : WalletAction() {
object CheckIfNeeded : WalletAction()
data class SetWarnings(val warningList: List<WarningMessage>) : WalletAction()
object CheckIfNeeded : Warnings()
data class SetWarnings(val warningList: List<WarningMessage>) : Warnings()
object AppRating : WalletAction() {
object SetNeverToShow : WalletAction()
object RemindLater : WalletAction()
object AppRating : Warnings() {
object SetNeverToShow : Warnings()
object RemindLater : Warnings()
}
}
@ -107,51 +105,43 @@ sealed class WalletAction : Action {
object Cancel : WalletAction()
}
object CreatePayId : WalletAction() {
data class CompleteCreatingPayId(val payId: String) : WalletAction()
data class Success(val payId: String) : WalletAction()
object EmptyField : WalletAction(), ErrorAction {
override val error = TapError.PayIdEmptyField
object EmptyField : WalletAction(), ErrorAction {
override val error = TapError.PayIdEmptyField
}
data class CopyAddress(val address: String, val context: Context) : WalletAction() {
object Success : WalletAction(), NotificationAction {
override val messageResource = R.string.wallet_notification_address_copied
}
}
data class CopyAddress(val address: String, val context: Context) : WalletAction() {
object Success : WalletAction(), NotificationAction {
override val messageResource = R.string.wallet_notification_address_copied
}
}
data class ShareAddress(val address: String, val context: Context) : WalletAction()
data class ShareAddress(val address: String, val context: Context) : WalletAction()
object ShowDialog : WalletAction() {
object QrCode : WalletAction()
object ScanFails : WalletAction()
}
object ShowDialog : WalletAction() {
object QrCode : WalletAction()
object ScanFails : WalletAction()
}
object HideDialog : WalletAction()
object HideDialog : WalletAction()
data class ExploreAddress(val exploreUrl: String, val context: Context) : WalletAction()
data class ExploreAddress(val exploreUrl: String, val context: Context) : WalletAction()
object CreateWallet : WalletAction()
object EmptyWallet : WalletAction()
object CreateWallet : WalletAction()
object EmptyWallet : WalletAction()
object Scan : WalletAction()
sealed class TopUpAction : WalletAction() {
data class TopUp(val context: Context, val toolbarColor: Int) : TopUpAction()
}
data class Send(val amount: Amount? = null) : WalletAction() {
data class ChooseCurrency(val amounts: List<Amount>?) : WalletAction()
object Cancel : WalletAction()
}
data class ChangeSelectedAddress(val type: AddressType) : WalletAction()
sealed class TopUpAction : WalletAction() {
data class TopUp(val context: Context, val toolbarColor: Int) : TopUpAction()
}
data class ChangeSelectedAddress(val type: AddressType) : WalletAction()
sealed class TwinsAction : WalletAction() {
object ShowOnboarding : TwinsAction()
object SetOnboardingShown : TwinsAction()
data class SetTwinCard(
val secondCardId: String, val number: TwinCardNumber,
val isCreatingTwinCardsAllowed: Boolean,
) : TwinsAction()
}
}
sealed class TwinsAction : WalletAction() {
object ShowOnboarding : TwinsAction()
object SetOnboardingShown : TwinsAction()
data class SetTwinCard(
val secondCardId: String, val number: TwinCardNumber,
val isCreatingTwinCardsAllowed: Boolean,
) : TwinsAction()
}
}

View file

@ -1,356 +0,0 @@
package com.tangem.tap.features.wallet.redux
import android.content.Intent
import android.net.Uri
import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.content.ContextCompat
import com.tangem.TangemSdkError
import com.tangem.blockchain.common.*
import com.tangem.blockchain.extensions.SimpleResult
import com.tangem.commands.common.card.Card
import com.tangem.commands.common.card.CardType
import com.tangem.commands.common.network.Result
import com.tangem.common.CompletionResult
import com.tangem.common.extensions.getType
import com.tangem.common.extensions.toHexString
import com.tangem.tap.common.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
import com.tangem.tap.common.extensions.copyToClipboard
import com.tangem.tap.common.extensions.isGreaterThan
import com.tangem.tap.common.redux.AppState
import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.PayIdManager
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.TopUpHelper
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager
import com.tangem.tap.domain.extensions.toSendableAmounts
import com.tangem.tap.domain.twins.TwinsHelper
import com.tangem.tap.domain.twins.isTwinCard
import com.tangem.tap.features.details.redux.DetailsAction
import com.tangem.tap.features.details.redux.twins.CreateTwinWallet
import com.tangem.tap.features.send.redux.PrepareSendScreen
import com.tangem.tap.features.wallet.models.toPendingTransactions
import com.tangem.tap.network.NetworkConnectivity
import com.tangem.tap.network.NetworkStateChanged
import com.tangem.tap.preferencesStorage
import com.tangem.tap.scope
import com.tangem.tap.store
import com.tangem.tap.tangemSdkManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.rekotlin.Action
import org.rekotlin.Middleware
import java.math.BigDecimal
class WalletMiddleware {
private val topUpMiddleware = TopUpMiddleware()
val walletMiddleware: Middleware<AppState> = { dispatch, state ->
{ next ->
{ action ->
when (action) {
is WalletAction.TopUpAction -> topUpMiddleware.handle(action)
is WalletAction.LoadWallet -> {
scope.launch {
store.state.globalState.tapWalletManager.loadWalletData()
}
}
is WalletAction.LoadPayId -> {
scope.launch {
store.state.globalState.tapWalletManager.loadPayId()
}
}
is WalletAction.LoadFiatRate -> {
scope.launch {
store.state.globalState.tapWalletManager.loadFiatRate(store.state.globalState.appCurrency)
}
}
is WalletAction.CreateWallet -> {
if (store.state.walletState.twinCardsState != null) {
store.dispatch(DetailsAction.CreateTwinWalletAction.ShowWarning(
store.state.globalState.scanNoteResponse?.card?.cardId?.let {
TwinsHelper.getTwinCardNumber(it)
},
CreateTwinWallet.CreateWallet
))
} else {
scope.launch {
val result = tangemSdkManager.createWallet(
store.state.globalState.scanNoteResponse?.card?.cardId
)
when (result) {
is CompletionResult.Success -> {
store.state.globalState.tapWalletManager
.onCardScanned(result.data)
}
}
}
}
}
is WalletAction.UpdateWallet -> {
if (store.state.walletState.state == ProgressState.Done) {
scope.launch { store.state.globalState.tapWalletManager.updateWallet() }
}
}
is WalletAction.UpdateWallet.Success -> setupWalletUpdate(action.wallet)
is WalletAction.LoadWallet.Success -> {
store.dispatch(WalletAction.CheckHashesCountOnline)
if (!store.state.walletState.updatingWallet) setupWalletUpdate(action.wallet)
tryToShowAppRatingWarning(action.wallet)
}
is WalletAction.CreatePayId.CompleteCreatingPayId -> {
scope.launch {
val cardId = store.state.globalState.scanNoteResponse?.card?.cardId
val wallet = store.state.globalState.scanNoteResponse?.walletManager?.wallet
val publicKey = store.state.globalState.scanNoteResponse?.card?.cardPublicKey
if (cardId != null && wallet != null && publicKey != null) {
val result = PayIdManager().setPayId(
cardId, publicKey.toHexString(),
action.payId, wallet.address, wallet.blockchain
)
withContext(Dispatchers.Main) {
when (result) {
is Result.Success ->
store.dispatch(WalletAction.CreatePayId.Success(action.payId))
is Result.Failure -> {
val error = result.error as? TapError
?: TapError.PayIdCreatingError
store.dispatch(WalletAction.CreatePayId.Failure(error))
}
}
}
}
}
}
is WalletAction.Scan -> {
scope.launch {
val result = tangemSdkManager.scanNote(FirebaseAnalyticsHandler)
when (result) {
is CompletionResult.Success -> {
tangemSdkManager.changeDisplayedCardIdNumbersCount(result.data.card)
store.state.globalState.tapWalletManager
.onCardScanned(result.data, true)
if (store.state.walletState.twinCardsState != null) {
val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown()
if (showOnboarding) {
store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding))
}
}
store.dispatch(WalletAction.ScanCardFinished())
}
is CompletionResult.Failure -> {
if (result.error !is TangemSdkError.UserCancelled) {
// Weird things... If you run the code below without coroutines,
// then rescanning will be impossible
scope.launch(Dispatchers.Main) {
store.dispatch(WalletAction.ScanCardFinished(result.error))
if (store.state.walletState.scanCardFailsCounter >= 2) {
store.dispatch(WalletAction.ShowDialog.ScanFails)
}
}
}
}
}
}
}
is WalletAction.LoadData -> {
scope.launch {
store.state.globalState.scanNoteResponse?.let {
store.state.globalState.tapWalletManager.loadData(it)
}
}
}
is NetworkStateChanged -> {
store.state.globalState.scanNoteResponse?.let { scanNoteResponse ->
store.dispatch(WalletAction.CheckHashesCountOnline)
scope.launch {
store.state.globalState.tapWalletManager.onCardScanned(scanNoteResponse)
}
}
}
is WalletAction.CopyAddress -> {
store.state.walletState.walletAddresses?.selectedAddress?.address?.let {
action.context.copyToClipboard(it)
store.dispatch(WalletAction.CopyAddress.Success)
}
}
is WalletAction.ExploreAddress -> {
val uri = Uri.parse(store.state.walletState.walletAddresses?.selectedAddress?.exploreUrl)
val intent = Intent(Intent.ACTION_VIEW, uri)
ContextCompat.startActivity(action.context, intent, null)
}
is WalletAction.Send -> {
val newAction = prepareSendAction(action.amount)
store.dispatch(newAction)
if (newAction is PrepareSendScreen) {
store.dispatch(NavigationAction.NavigateTo(AppScreen.Send))
}
}
is WalletAction.Warnings.CheckIfNeeded -> {
val globalState = store.state.globalState
val validator = globalState.scanNoteResponse?.walletManager as? SignatureCountValidator
globalState.scanNoteResponse?.card?.let { card ->
store.state.globalState.warningManager?.removeWarnings(WarningMessage.Origin.Local)
if (card.getType() != CardType.Release) addWarningMessage(WarningMessagesManager.devCardWarning())
if (!preferencesStorage.wasCardScannedBefore(card.cardId)) {
checkIfWarningNeeded(card, validator)?.let { addWarningMessage(it) }
}
updateWarningMessages()
}
}
is WalletAction.CheckHashesCountOnline -> checkHashesCountOnline()
is WalletAction.SaveCardId -> {
val cardId = store.state.globalState.scanNoteResponse?.card?.cardId
cardId?.let { preferencesStorage.saveScannedCardId(it) }
}
is WalletAction.TwinsAction.SetTwinCard -> {
val showOnboarding = !preferencesStorage.wasTwinsOnboardingShown()
if (showOnboarding) store.dispatch(WalletAction.TwinsAction.ShowOnboarding)
}
is WalletAction.TwinsAction.SetOnboardingShown -> {
preferencesStorage.saveTwinsOnboardingShown()
}
is WalletAction.Warnings.AppRating.RemindLater -> {
preferencesStorage.appRatingLaunchObserver.applyDelayedShowing()
}
is WalletAction.Warnings.AppRating.SetNeverToShow -> {
preferencesStorage.appRatingLaunchObserver.setNeverToShow()
}
}
next(action)
}
}
}
private fun tryToShowAppRatingWarning(wallet: Wallet) {
val nonZeroWalletsCount = wallet.amounts.filter {
it.value.value?.isGreaterThan(BigDecimal.ZERO) ?: false
}.size
if (nonZeroWalletsCount > 0) {
preferencesStorage.appRatingLaunchObserver.foundWalletWithFunds()
}
if (preferencesStorage.appRatingLaunchObserver.isReadyToShow()) {
FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.APP_RATING_DISPLAYED)
addWarningMessage(WarningMessagesManager.appRatingWarning(), true)
}
}
private fun setupWalletUpdate(wallet: Wallet) {
if (!wallet.recentTransactions.toPendingTransactions(wallet.address).isNullOrEmpty()) {
store.dispatch(WalletAction.UpdateWallet.ScheduleUpdatingWallet)
scope.launch(Dispatchers.IO) {
delay(10000)
withContext(Dispatchers.Main) {
store.dispatch(WalletAction.UpdateWallet)
}
}
}
}
private fun prepareSendAction(amount: Amount?): Action {
return if (amount != null) {
if (amount.type is AmountType.Token) {
PrepareSendScreen(store.state.walletState.wallet?.amounts?.get(AmountType.Coin), amount)
} else {
PrepareSendScreen(amount)
}
} else {
val amounts = store.state.walletState.wallet?.amounts?.toSendableAmounts()
if (amounts?.size ?: 0 > 1) {
WalletAction.Send.ChooseCurrency(amounts)
} else {
val amountToSend = amounts?.first()
PrepareSendScreen(amountToSend)
}
}
}
private fun checkIfWarningNeeded(
card: Card, signatureCountValidator: SignatureCountValidator? = null,
): WarningMessage? {
if (card.isTwinCard()) return null
return if (signatureCountValidator == null) {
if (card.walletSignedHashes ?: 0 > 0) {
WarningMessagesManager.alreadySignedHashesWarning()
} else {
store.dispatch(WalletAction.SaveCardId)
null
}
} else {
store.dispatch(WalletAction.NeedToCheckHashesCountOnline)
null
}
}
private fun checkHashesCountOnline() {
if (store.state.walletState.hashesCountVerified != false) return
if (!NetworkConnectivity.getInstance().isOnlineOrConnecting()) return
val card = store.state.globalState.scanNoteResponse?.card
if (card == null || preferencesStorage.wasCardScannedBefore(card.cardId)) return
if (card.isTwinCard()) return
val validator = store.state.globalState.scanNoteResponse?.walletManager
as? SignatureCountValidator
scope.launch {
val result = validator?.validateSignatureCount(card.walletSignedHashes ?: 0)
withContext(Dispatchers.Main) {
when (result) {
SimpleResult.Success -> {
store.dispatch(WalletAction.ConfirmHashesCount)
store.dispatch(WalletAction.SaveCardId)
}
is SimpleResult.Failure ->
if (result.error is BlockchainSdkError.SignatureCountNotMatched) {
addWarningMessage(WarningMessagesManager.alreadySignedHashesWarning(), true)
} else if (card.walletSignedHashes ?: 0 > 0) {
addWarningMessage(WarningMessagesManager.alreadySignedHashesWarning(), true)
}
}
}
}
}
private fun addWarningMessage(warning: WarningMessage, autoUpdate: Boolean = false) {
store.state.globalState.warningManager?.addWarning(warning)
if (autoUpdate) updateWarningMessages()
}
private fun updateWarningMessages() {
val warningManager = store.state.globalState.warningManager ?: return
store.dispatch(WalletAction.Warnings.SetWarnings(
warningManager.getWarnings(WarningMessage.Location.MainScreen)))
}
}
private class TopUpMiddleware {
fun handle(action: WalletAction.TopUpAction) {
when (action) {
is WalletAction.TopUpAction.TopUp -> {
val config = store.state.globalState.configManager?.config ?: return
val addresses = store.state.walletState.walletAddresses ?: return
if (addresses.list.isEmpty()) return
val defaultAddress = addresses.list[0].address
val url = TopUpHelper.getUrl(
store.state.walletState.currencyData.currencySymbol!!,
defaultAddress,
config.moonPayApiKey,
config.moonPayApiSecretKey
)
val customTabsIntent = CustomTabsIntent.Builder()
.setToolbarColor(action.toolbarColor)
.build()
customTabsIntent.launchUrl(action.context, Uri.parse(url));
}
}
}
}

View file

@ -39,7 +39,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
var newState = state.walletState
when (action) {
is WalletAction.CheckSignedHashes -> newState = handleCheckSignedHashesActions(action, newState)
is WalletAction.Warnings -> newState = handleCheckSignedHashesActions(action, newState)
is WalletAction.TwinsAction -> newState = twinsReducer.reduce(action, newState)
is WalletAction.MultiWallet -> newState = multiWalletReducer.reduce(action, newState)
@ -235,10 +235,6 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
)
}
is WalletAction.Send.Cancel -> newState = newState.copy(walletDialog = null)
is WalletAction.Warnings.SetWarnings -> newState = newState.copy(mainWarningsList = action.warningList)
is WalletAction.TopUpAction -> {
newState = newState.copy(topUpState = handleTopUpActions(action, newState.topUpState))
}
is WalletAction.TopUpAction -> return newState
is WalletAction.ChangeSelectedAddress -> {
val selectedWalletData = newState.getWalletData(newState.selectedWallet)
@ -286,13 +282,18 @@ fun createAddressList(wallet: Wallet?, walletAddresses: WalletAddresses? = null)
return WalletAddresses(listOfAddressData[indexOfSelectedWallet], listOfAddressData)
}
private fun handleCheckSignedHashesActions(action: WalletAction.CheckSignedHashes, state: WalletState): WalletState {
private fun handleCheckSignedHashesActions(action: WalletAction.Warnings, state: WalletState): WalletState {
return when (action) {
WalletAction.CheckSignedHashes.CheckHashesCountOnline -> state
WalletAction.CheckSignedHashes.CheckIfWarningNeeded -> state
WalletAction.CheckSignedHashes.ConfirmHashesCount -> state.copy(hashesCountVerified = true)
WalletAction.CheckSignedHashes.NeedToCheckHashesCountOnline -> state.copy(hashesCountVerified = false)
WalletAction.CheckSignedHashes.SaveCardId -> state
WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline -> state
WalletAction.Warnings.CheckHashesCount.ConfirmHashesCount -> state.copy(hashesCountVerified = true)
WalletAction.Warnings.CheckHashesCount.NeedToCheckHashesCountOnline -> state.copy(hashesCountVerified = false)
WalletAction.Warnings.CheckHashesCount.SaveCardId -> state
is WalletAction.Warnings.SetWarnings -> state.copy(mainWarningsList = action.warningList)
WalletAction.Warnings.CheckIfNeeded -> state
WalletAction.Warnings.AppRating -> state
WalletAction.Warnings.CheckHashesCount -> state
WalletAction.Warnings.AppRating.RemindLater -> state
WalletAction.Warnings.AppRating.SetNeverToShow -> state
}
}

View file

@ -12,7 +12,10 @@ class TopUpMiddleware {
is WalletAction.TopUpAction.TopUp -> {
val selectedWalletData = store.state.walletState.getSelectedWalletData()
val config = store.state.globalState.configManager?.config ?: return
val defaultAddress = selectedWalletData?.walletAddresses!!.list[0].address
val addresses = selectedWalletData?.walletAddresses ?: return
if (addresses.list.isEmpty()) return
val defaultAddress = addresses.list[0].address
val url = TopUpHelper.getUrl(
selectedWalletData.currencyData.currencySymbol!!,
defaultAddress,

View file

@ -3,6 +3,7 @@ package com.tangem.tap.features.wallet.redux.middlewares
import android.content.Intent
import android.net.Uri
import androidx.core.content.ContextCompat
import com.tangem.TangemSdkError
import com.tangem.blockchain.common.*
import com.tangem.common.CompletionResult
import com.tangem.tap.*
@ -22,6 +23,7 @@ import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletData
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.network.NetworkStateChanged
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.rekotlin.Action
import org.rekotlin.Middleware
@ -29,7 +31,7 @@ import org.rekotlin.Middleware
class WalletMiddleware {
private val topUpMiddleware = TopUpMiddleware()
private val twinsMiddleware = TwinsMiddleware()
private val checkSignedHashesMiddleware = CheckSignedHashesMiddleware()
private val warningsMiddleware = WarningsMiddleware()
private val multiWalletMiddleware = MultiWalletMiddleware()
val walletMiddleware: Middleware<AppState> = { dispatch, state ->
@ -40,8 +42,7 @@ class WalletMiddleware {
when (action) {
is WalletAction.TopUpAction -> topUpMiddleware.handle(action)
is WalletAction.TwinsAction -> twinsMiddleware.handle(action)
is WalletAction.CheckSignedHashes ->
checkSignedHashesMiddleware.handle(action, globalState)
is WalletAction.Warnings -> warningsMiddleware.handle(action, globalState)
is WalletAction.MultiWallet ->
multiWalletMiddleware.handle(action, walletState, globalState)
@ -57,6 +58,10 @@ class WalletMiddleware {
}
}
}
is WalletAction.LoadWallet.Success -> {
store.dispatch(WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline)
warningsMiddleware.tryToShowAppRatingWarning(action.wallet)
}
is WalletAction.LoadFiatRate -> {
scope.launch {
if (action.wallet != null) {
@ -118,9 +123,6 @@ class WalletMiddleware {
}
}
is WalletAction.LoadWallet.Success -> {
store.dispatch(WalletAction.CheckSignedHashes.CheckHashesCountOnline)
}
is WalletAction.Scan -> {
scope.launch {
val result = tangemSdkManager.scanNote(FirebaseAnalyticsHandler)
@ -135,13 +137,25 @@ class WalletMiddleware {
store.dispatch(NavigationAction.NavigateTo(AppScreen.TwinsOnboarding))
}
}
store.dispatch(WalletAction.ScanCardFinished())
}
is CompletionResult.Failure -> {
if (result.error !is TangemSdkError.UserCancelled) {
scope.launch(Dispatchers.Main) {
store.dispatch(WalletAction.ScanCardFinished(result.error))
if (store.state.walletState.scanCardFailsCounter >= 2) {
store.dispatch(WalletAction.ShowDialog.ScanFails)
}
}
}
}
}
}
}
is WalletAction.LoadData -> {
scope.launch {
val scanNoteResponse = globalState?.scanNoteResponse ?: return@launch
val scanNoteResponse = globalState?.scanNoteResponse
?: return@launch
if (!walletState?.wallets.isNullOrEmpty()) {
globalState.tapWalletManager.reloadData(scanNoteResponse)
} else {
@ -151,7 +165,7 @@ class WalletMiddleware {
}
is NetworkStateChanged -> {
globalState?.scanNoteResponse?.let { scanNoteResponse ->
store.dispatch(WalletAction.CheckSignedHashes.CheckHashesCountOnline)
store.dispatch(WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline)
scope.launch {
globalState.tapWalletManager.loadData(scanNoteResponse)
}

View file

@ -2,10 +2,14 @@ package com.tangem.tap.features.wallet.redux.middlewares
import com.tangem.blockchain.common.BlockchainSdkError
import com.tangem.blockchain.common.SignatureCountValidator
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.common.extensions.getType
import com.tangem.tap.common.analytics.AnalyticsEvent
import com.tangem.tap.common.analytics.FirebaseAnalyticsHandler
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
@ -19,11 +23,12 @@ import com.tangem.tap.store
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.math.BigDecimal
class CheckSignedHashesMiddleware {
fun handle(action: WalletAction.CheckSignedHashes, globalState: GlobalState?) {
class WarningsMiddleware {
fun handle(action: WalletAction.Warnings, globalState: GlobalState?) {
when (action) {
is WalletAction.CheckSignedHashes.CheckIfWarningNeeded -> {
is WalletAction.Warnings.CheckIfNeeded -> {
val validator = globalState?.scanNoteResponse?.walletManager as? SignatureCountValidator
globalState?.scanNoteResponse?.card?.let { card ->
globalState.warningManager?.removeWarnings(WarningMessage.Origin.Local)
@ -33,13 +38,33 @@ class CheckSignedHashesMiddleware {
}
updateWarningMessages()
}
val readyToShow = preferencesStorage.appRatingLaunchObserver.isReadyToShow()
if (readyToShow) addWarningMessage(WarningMessagesManager.appRatingWarning(), true)
}
is WalletAction.CheckSignedHashes.CheckHashesCountOnline -> checkHashesCountOnline()
is WalletAction.CheckSignedHashes.SaveCardId -> {
is WalletAction.Warnings.CheckHashesCount.CheckHashesCountOnline -> checkHashesCountOnline()
is WalletAction.Warnings.CheckHashesCount.SaveCardId -> {
val cardId = globalState?.scanNoteResponse?.card?.cardId
cardId?.let { preferencesStorage.saveScannedCardId(it) }
}
is WalletAction.Warnings.AppRating.RemindLater -> {
preferencesStorage.appRatingLaunchObserver.applyDelayedShowing()
}
is WalletAction.Warnings.AppRating.SetNeverToShow -> {
preferencesStorage.appRatingLaunchObserver.setNeverToShow()
}
}
}
public fun tryToShowAppRatingWarning(wallet: Wallet) {
val nonZeroWalletsCount = wallet.amounts.filter {
it.value.value?.isGreaterThan(BigDecimal.ZERO) ?: false
}.size
if (nonZeroWalletsCount > 0) {
preferencesStorage.appRatingLaunchObserver.foundWalletWithFunds()
}
if (preferencesStorage.appRatingLaunchObserver.isReadyToShow()) {
FirebaseAnalyticsHandler.triggerEvent(AnalyticsEvent.APP_RATING_DISPLAYED)
addWarningMessage(WarningMessagesManager.appRatingWarning(), true)
}
}
@ -52,11 +77,11 @@ class CheckSignedHashesMiddleware {
if (card.walletSignedHashes ?: 0 > 0) {
WarningMessagesManager.alreadySignedHashesWarning()
} else {
store.dispatch(WalletAction.CheckSignedHashes.SaveCardId)
store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId)
null
}
} else {
store.dispatch(WalletAction.CheckSignedHashes.NeedToCheckHashesCountOnline)
store.dispatch(WalletAction.Warnings.CheckHashesCount.NeedToCheckHashesCountOnline)
null
}
}
@ -77,8 +102,8 @@ class CheckSignedHashesMiddleware {
withContext(Dispatchers.Main) {
when (result) {
SimpleResult.Success -> {
store.dispatch(WalletAction.CheckSignedHashes.ConfirmHashesCount)
store.dispatch(WalletAction.CheckSignedHashes.SaveCardId)
store.dispatch(WalletAction.Warnings.CheckHashesCount.ConfirmHashesCount)
store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId)
}
is SimpleResult.Failure ->
if (result.error is BlockchainSdkError.SignatureCountNotMatched) {
@ -98,6 +123,6 @@ class CheckSignedHashesMiddleware {
private fun updateWarningMessages() {
val warningManager = store.state.globalState.warningManager ?: return
store.dispatch(WalletAction.SetWarnings(warningManager.getWarnings(WarningMessage.Location.MainScreen)))
store.dispatch(WalletAction.Warnings.SetWarnings(warningManager.getWarnings(WarningMessage.Location.MainScreen)))
}
}

View file

@ -31,14 +31,14 @@ class MultipleAddressUiHelper {
return when (id) {
R.id.chip_default -> {
when (blockchain) {
Blockchain.Bitcoin -> BitcoinAddressType.Segwit
Blockchain.Bitcoin, Blockchain.BitcoinTestnet -> BitcoinAddressType.Segwit
Blockchain.CardanoShelley -> CardanoAddressType.Shelley
else -> null
}
}
R.id.chip_legacy -> {
when (blockchain) {
Blockchain.Bitcoin -> BitcoinAddressType.Legacy
Blockchain.Bitcoin, Blockchain.BitcoinTestnet -> BitcoinAddressType.Legacy
Blockchain.CardanoShelley -> CardanoAddressType.Byron
else -> null
}

View file

@ -87,7 +87,7 @@ class WalletDetailsFragment : Fragment(R.layout.fragment_wallet_details), StoreS
btn_confirm.text = getString(R.string.wallet_button_send)
btn_confirm.setOnClickListener { store.dispatch(WalletAction.Send()) }
btn_share.setOnClickListener { store.dispatch(WalletAction.ShowQrCode) }
btn_share.setOnClickListener { store.dispatch(WalletAction.ShowDialog.QrCode) }
btn_top_up.setOnClickListener {
store.dispatch(

View file

@ -20,7 +20,6 @@ import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.termsOfUse.CardTou
import com.tangem.tap.domain.twins.TwinCardNumber
import com.tangem.tap.features.details.redux.DetailsAction
import com.tangem.tap.features.wallet.redux.*
import com.tangem.tap.features.wallet.ui.adapters.SpacesItemDecoration
@ -28,10 +27,6 @@ import com.tangem.tap.features.wallet.ui.adapters.WarningMessagesAdapter
import com.tangem.tap.features.wallet.ui.wallet.MultiWalletView
import com.tangem.tap.features.wallet.ui.wallet.SingleWalletView
import com.tangem.tap.features.wallet.ui.wallet.WalletView
import com.tangem.tap.features.wallet.ui.dialogs.AmountToSendDialog
import com.tangem.tap.features.wallet.ui.dialogs.PayIdDialog
import com.tangem.tap.features.wallet.ui.dialogs.QrDialog
import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_wallet.*
@ -182,79 +177,4 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
if (store.state.walletState.shouldShowDetails) inflater.inflate(R.menu.wallet, menu)
}
}
//TODO: handle addition of testnets
//class MultipleAddressUiHelper {
// companion object {
// fun typeToId(type: AddressType): Int {
// return when (type) {
// is BitcoinAddressType.Legacy -> R.id.chip_legacy
// is BitcoinAddressType.Segwit -> R.id.chip_default
// is CardanoAddressType.Byron -> R.id.chip_legacy
// is CardanoAddressType.Shelley -> R.id.chip_default
// else -> View.NO_ID
// }
// }
//
// fun idToType(id: Int, blockchain: Blockchain?): AddressType? {
// return when (id) {
// R.id.chip_default -> {
// when (blockchain) {
// Blockchain.Bitcoin, Blockchain.BitcoinTestnet -> BitcoinAddressType.Segwit
// Blockchain.CardanoShelley -> CardanoAddressType.Shelley
// else -> null
// }
// }
// R.id.chip_legacy -> {
// when (blockchain) {
// Blockchain.Bitcoin, Blockchain.BitcoinTestnet -> BitcoinAddressType.Legacy
// Blockchain.CardanoShelley -> CardanoAddressType.Byron
// else -> null
// }
// }
// else -> null
// }
// }
// }
//}
//TODO: handle scan failed dialog:
//private fun handleDialogs(walletDialog: WalletDialog?) {
// when (walletDialog) {
// is WalletDialog.QrDialog -> {
// if (walletDialog.qrCode != null && walletDialog.shareUrl != null) {
// if (dialog == null) dialog = QrDialog(requireContext()).apply {
// this.showQr(
// walletDialog.qrCode, walletDialog.shareUrl, walletDialog.currencyName
// )
// }
// }
// }
// is WalletDialog.CreatePayIdDialog -> {
// when (walletDialog.creatingPayIdState) {
// CreatingPayIdState.EnterPayId -> {
// if (dialog == null) dialog = PayIdDialog(requireContext()).apply {
// this.show()
// }
// (dialog as? PayIdDialog)?.stopProgress()
// }
// CreatingPayIdState.Waiting -> (dialog as? PayIdDialog)?.showProgress()
// }
// }
// is WalletDialog.SelectAmountToSendDialog -> {
// if (dialog == null) dialog = AmountToSendDialog(requireContext()).apply {
// this.show(walletDialog.amounts)
// }
// }
// is WalletDialog.ScanFailsDialog -> {
// if (dialog == null) dialog = ScanFailsDialog.create(requireContext()).apply {
// this.show()
// }
// }
// null -> {
// dialog?.dismiss()
// dialog = null
// }
// }
//}
}

View file

@ -1,5 +1,6 @@
package com.tangem.tap.features.wallet.ui.wallet
import android.app.Dialog
import androidx.recyclerview.widget.LinearLayoutManager
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.extensions.show
@ -7,10 +8,12 @@ import com.tangem.tap.common.redux.navigation.AppScreen
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.tokens.redux.TokensAction
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.features.wallet.redux.WalletDialog
import com.tangem.tap.features.wallet.redux.WalletState
import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.features.wallet.ui.WalletFragment
import com.tangem.tap.features.wallet.ui.adapters.WalletAdapter
import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.card_balance.*
@ -22,8 +25,11 @@ import kotlinx.android.synthetic.main.layout_wallet_long_buttons.*
class MultiWalletView : WalletView {
private var fragment: WalletFragment? = null
private var dialog: Dialog? = null
private lateinit var walletsAdapter: WalletAdapter
override fun changeWalletView(fragment: WalletFragment) {
setFragment(fragment)
onViewCreated()
@ -82,6 +88,7 @@ class MultiWalletView : WalletView {
store.dispatch(NavigationAction.NavigateTo(AppScreen.AddTokens))
}
handleErrorStates(state, fragment)
handleDialogs(state.walletDialog)
}
private fun handleErrorStates(state: WalletState, fragment: WalletFragment) {
@ -123,4 +130,20 @@ class MultiWalletView : WalletView {
fragment.btn_confirm_long.setOnClickListener { store.dispatch(WalletAction.CreateWallet) }
fragment.btn_confirm_long.text = fragment.getText(R.string.wallet_button_create_wallet)
}
private fun handleDialogs(walletDialog: WalletDialog?) {
val fragment = fragment ?: return
val context = fragment.context ?: return
when (walletDialog) {
is WalletDialog.ScanFailsDialog -> {
if (dialog == null) dialog = ScanFailsDialog.create(context).apply {
this.show()
}
}
else -> {
dialog?.dismiss()
dialog = null
}
}
}
}

View file

@ -15,6 +15,7 @@ import com.tangem.tap.features.wallet.ui.WalletFragment
import com.tangem.tap.features.wallet.ui.adapters.PendingTransactionsAdapter
import com.tangem.tap.features.wallet.ui.dialogs.AmountToSendDialog
import com.tangem.tap.features.wallet.ui.dialogs.QrDialog
import com.tangem.tap.features.wallet.ui.dialogs.ScanFailsDialog
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.card_balance.*
@ -135,7 +136,7 @@ class SingleWalletView : WalletView {
store.dispatch(WalletAction.CopyAddress(addressString, fragment.requireContext()))
}
}
fragment.btn_show_qr.setOnClickListener { store.dispatch(WalletAction.ShowQrCode) }
fragment.btn_show_qr.setOnClickListener { store.dispatch(WalletAction.ShowDialog.QrCode) }
fragment.btn_top_up.setOnClickListener {
store.dispatch(
@ -211,10 +212,11 @@ class SingleWalletView : WalletView {
private fun handleDialogs(walletDialog: WalletDialog?) {
val fragment = fragment ?: return
val context = fragment.context ?: return
when (walletDialog) {
is WalletDialog.QrDialog -> {
if (walletDialog.qrCode != null && walletDialog.shareUrl != null) {
if (dialog == null) dialog = QrDialog(fragment.requireContext()).apply {
if (dialog == null) dialog = QrDialog(context).apply {
this.showQr(
walletDialog.qrCode, walletDialog.shareUrl, walletDialog.currencyName
)
@ -222,10 +224,15 @@ class SingleWalletView : WalletView {
}
}
is WalletDialog.SelectAmountToSendDialog -> {
if (dialog == null) dialog = AmountToSendDialog(fragment.requireContext()).apply {
if (dialog == null) dialog = AmountToSendDialog(context).apply {
this.show(walletDialog.amounts)
}
}
is WalletDialog.ScanFailsDialog -> {
if (dialog == null) dialog = ScanFailsDialog.create(context).apply {
this.show()
}
}
null -> {
dialog?.dismiss()
dialog = null