Updated on 2026-08-14

This commit is contained in:
Tangem 2021-11-08 08:51:04 +00:00
commit 922fcbc338
22 changed files with 93 additions and 63 deletions

@ -1 +1 @@
Subproject commit 0cf3af21e6f441e6323a1ba127ee7518168797df
Subproject commit e80938f7d9ba378a91ac544a9170597e64afa696

View file

@ -26,6 +26,10 @@ fun Store<*>.dispatchOpenUrl(url: String) {
store.dispatch(NavigationAction.OpenUrl(url))
}
fun Store<*>.dispatchShare(url: String) {
store.dispatch(NavigationAction.Share(url))
}
fun Store<*>.dispatchNotification(resId: Int) {
scope.launch(Dispatchers.Main) {
store.dispatch(GlobalAction.ShowNotification(resId))

View file

@ -13,6 +13,6 @@ sealed class AppDialog : StateDialog {
data class AddressInfoDialog(
val addressData: AddressData,
val onCopyAddress: VoidCallback,
val onExploreAddress: VoidCallback
val onShareAddress: VoidCallback
) : AppDialog()
}

View file

@ -15,6 +15,8 @@ sealed class NavigationAction : Action {
data class OpenUrl(val url: String) : NavigationAction()
data class Share(val data: String) : NavigationAction()
data class ActivityCreated(val activity: WeakReference<FragmentActivity>) : NavigationAction()
object ActivityDestroyed : NavigationAction()
}

View file

@ -3,6 +3,7 @@ package com.tangem.tap.common.redux.navigation
import com.tangem.tap.common.CustomTabsManager
import com.tangem.tap.common.extensions.openFragment
import com.tangem.tap.common.extensions.popBackTo
import com.tangem.tap.common.extensions.shareText
import com.tangem.tap.common.redux.AppState
import org.rekotlin.Middleware
@ -31,6 +32,11 @@ val navigationMiddleware: Middleware<AppState> = { dispatch, state ->
CustomTabsManager().openUrl(action.url, it)
}
}
is NavigationAction.Share -> {
navState?.activity?.get()?.let {
it.shareText(action.data)
}
}
}
}
next(action)

View file

@ -0,0 +1,19 @@
package com.tangem.tap.domain.extensions
import com.tangem.blockchain.common.Blockchain
import com.tangem.tap.network.moonpay.MoonpayStatus
/**
[REDACTED_AUTHOR]
*/
fun MoonpayStatus.buyIsAllowed(blockchain: Blockchain): Boolean {
if (blockchain == Blockchain.Unknown || blockchain == Blockchain.BSC) return false
return isBuyAllowed && availableToBuy.contains(blockchain.currency)
}
fun MoonpayStatus.sellIsAllowed(blockchain: Blockchain): Boolean {
if (blockchain == Blockchain.Unknown || blockchain == Blockchain.BSC) return false
return isSellAllowed && availableToSell.contains(blockchain.currency)
}

View file

@ -100,7 +100,7 @@ class DetailsMiddleware {
store.dispatch(NavigationAction.PopBackTo())
}
is DetailsAction.EraseWallet.Confirm -> {
val card = store.state.detailsState.card ?: return
val card = store.state.detailsState.scanResponse?.card ?: return
scope.launch {
val result = tangemSdkManager.eraseWallet(card)
withContext(Dispatchers.Main) {
@ -113,7 +113,7 @@ class DetailsMiddleware {
FirebaseAnalyticsHandler.logCardSdkError(
error,
FirebaseAnalyticsHandler.ActionToLog.PurgeWallet,
card = store.state.detailsState.card
card = store.state.detailsState.scanResponse?.card
)
}
}
@ -170,7 +170,7 @@ class DetailsMiddleware {
}
}
is DetailsAction.ManageSecurity.SaveChanges -> {
val cardId = store.state.detailsState.card?.cardId
val cardId = store.state.detailsState.scanResponse?.card?.cardId
val selectedOption = store.state.detailsState.securityScreenState?.selectedOption
scope.launch {
val result = when (selectedOption) {
@ -199,7 +199,7 @@ class DetailsMiddleware {
FirebaseAnalyticsHandler.AnalyticsParam.NEW_SECURITY_OPTION to
(selectedOption?.name ?: "")
),
card = store.state.detailsState.card
card = store.state.detailsState.scanResponse?.card
)
}
store.dispatch(DetailsAction.ManageSecurity.SaveChanges.Failure)

View file

@ -41,7 +41,7 @@ private fun internalReduce(action: Action, state: AppState): DetailsState {
private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: DetailsState): DetailsState {
return DetailsState(
card = action.scanResponse.card,
scanResponse = action.scanResponse,
wallets = action.wallets,
cardInfo = action.scanResponse.card.toCardInfo(),
appCurrencyState = AppCurrencyState(action.fiatCurrencyName),
@ -52,8 +52,10 @@ private fun handlePrepareScreen(action: DetailsAction.PrepareScreen, state: Deta
private fun handleEraseWallet(action: DetailsAction.EraseWallet, state: DetailsState): DetailsState {
return when (action) {
DetailsAction.EraseWallet.Check -> {
val notAllowedByAnyWallet = state.card?.wallets?.any { it.settings.isPermanent } ?: false
val notAllowedByCard = notAllowedByAnyWallet || state.card?.isWalletDataSupported == true
val card = state.scanResponse?.card
val notAllowedByAnyWallet = card?.wallets?.any { it.settings.isPermanent } ?: false
val notAllowedByCard = notAllowedByAnyWallet ||
(card?.isWalletDataSupported == true && !state.scanResponse.isTangemNote())
val notEmpty = state.wallets.any {
it.hasPendingTransactions() || it.amounts.toSendableAmounts().isNotEmpty()
}
@ -121,7 +123,9 @@ private fun handleSecurityAction(
state.copy(securityScreenState = SecurityScreenState(currentOption = securityOption))
}
is DetailsAction.ManageSecurity.OpenSecurity -> {
if (state.card?.isStart2Coin == true) {
if (state.scanResponse?.card?.isStart2Coin == true ||
state.scanResponse?.isTangemNote() == true
) {
return state.copy(securityScreenState = state.securityScreenState?.copy(
allowedOptions = EnumSet.of(SecurityOption.LongTap),
selectedOption = state.securityScreenState.currentOption
@ -129,7 +133,7 @@ private fun handleSecurityAction(
}
val allowedSecurityOptions = prepareAllowedSecurityOptions(
state.card, state.securityScreenState?.currentOption
state.scanResponse?.card, state.securityScreenState?.currentOption
)
state.copy(securityScreenState = state.securityScreenState?.copy(
allowedOptions = allowedSecurityOptions,
@ -154,7 +158,7 @@ private fun handleSecurityAction(
state.copy(
securityScreenState = state.securityScreenState?.copy(
currentOption = state.securityScreenState.selectedOption,
allowedOptions = state.card?.let {
allowedOptions = state.scanResponse?.card?.let {
prepareAllowedSecurityOptions(
it, state.securityScreenState.selectedOption
)
@ -189,7 +193,7 @@ private fun prepareAllowedSecurityOptions(
}
private fun Card.toCardInfo(): CardInfo? {
private fun Card.toCardInfo(): CardInfo {
val cardId = this.cardId.chunked(4).joinToString(separator = " ")
val issuer = this.issuer.name
val signedHashes = this.signedHashesCount()

View file

@ -2,10 +2,10 @@ package com.tangem.tap.features.details.redux
import android.net.Uri
import com.tangem.blockchain.common.Wallet
import com.tangem.common.card.Card
import com.tangem.tap.common.entities.Button
import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY
import com.tangem.tap.common.redux.global.FiatCurrencyName
import com.tangem.tap.domain.tasks.product.ScanResponse
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.network.coinmarketcap.FiatCurrency
import com.tangem.tap.store
@ -14,7 +14,7 @@ import java.util.*
import kotlin.properties.ReadOnlyProperty
data class DetailsState(
val card: Card? = null,
val scanResponse: ScanResponse? = null,
val wallets: List<Wallet> = emptyList(),
val cardInfo: CardInfo? = null,
val appCurrencyState: AppCurrencyState = AppCurrencyState(),

View file

@ -67,7 +67,7 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
if (state.cardInfo != null) {
val cardId = if (state.isTangemTwins) {
state.card?.getTwinCardIdForUser()
state.scanResponse?.card?.getTwinCardIdForUser()
} else {
state.cardInfo.cardId
}
@ -113,13 +113,15 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
store.dispatch(GlobalAction.SendFeedback(FeedbackEmail()))
}
tv_wallet_connect.show(state.card?.isMultiwalletAllowed == true)
tv_wallet_connect.show(state.scanResponse?.card?.isMultiwalletAllowed == true)
tv_wallet_connect.setOnClickListener {
store.dispatch(NavigationAction.NavigateTo(AppScreen.WalletConnectSessions))
}
tv_security_title.setOnClickListener {
store.dispatch(DetailsAction.ManageSecurity.CheckCurrentSecurityOption(state.card?.cardId))
store.dispatch(DetailsAction.ManageSecurity.CheckCurrentSecurityOption(
state.scanResponse?.card?.cardId
))
}
val currentSecurity = when (state.securityScreenState?.currentOption) {

View file

@ -37,8 +37,8 @@ class AddressInfoBottomSheetDialog(
context.copyToClipboard(data.address)
stateDialog.onCopyAddress()
}
btn_fl_explore.setOnClickListener {
stateDialog.onExploreAddress()
btn_fl_share.setOnClickListener {
stateDialog.onShareAddress()
}
}
}

View file

@ -138,7 +138,7 @@ private fun handleNoteAction(action: Action, dispatch: DispatchFunction) {
val appDialog = AppDialog.AddressInfoDialog(
addressData,
onCopyAddress = { store.dispatchToastNotification(addressWasCopied) },
onExploreAddress = { store.dispatchOpenUrl(addressData.exploreUrl) }
onShareAddress = { store.dispatchShare(addressData.shareUrl) }
)
store.dispatchDialogShow(appDialog)
}

View file

@ -1,10 +1,9 @@
package com.tangem.tap.features.onboarding.products.note.redux
import com.tangem.blockchain.common.Blockchain
import com.tangem.blockchain.common.WalletManager
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.buyIsAllowed
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
import com.tangem.tap.network.moonpay.MoonpayStatus
import com.tangem.tap.store
import org.rekotlin.StateType
import kotlin.properties.ReadOnlyProperty
@ -28,16 +27,10 @@ data class OnboardingNoteState(
get() = steps.indexOf(currentStep)
val isBuyAllowed: Boolean by ReadOnlyProperty<Any, Boolean> { thisRef, property ->
store.state.globalState.moonpayStatus?.canBuy(walletBalance) ?: false
store.state.globalState.moonpayStatus?.buyIsAllowed(walletBalance.currency.blockchain) ?: false
}
}
fun MoonpayStatus.canBuy(walletBalance: OnboardingWalletBalance): Boolean {
if (walletBalance.currency.blockchain == Blockchain.Unknown) return false
return isBuyAllowed && availableToBuy.contains(walletBalance.currency.currencySymbol)
}
enum class OnboardingNoteStep {
None, CreateWallet, TopUpWallet, Done
}

View file

@ -243,7 +243,7 @@ private fun handle(action: Action, dispatch: DispatchFunction) {
val appDialog = AppDialog.AddressInfoDialog(
addressData,
onCopyAddress = { store.dispatchToastNotification(addressWasCopied) },
onExploreAddress = { store.dispatchOpenUrl(addressData.exploreUrl) }
onShareAddress = { store.dispatchShare(addressData.shareUrl) }
)
store.dispatchDialogShow(appDialog)
}

View file

@ -2,10 +2,10 @@ package com.tangem.tap.features.onboarding.products.twins.redux
import com.tangem.blockchain.common.WalletManager
import com.tangem.tap.domain.TapError
import com.tangem.tap.domain.extensions.buyIsAllowed
import com.tangem.tap.domain.twins.TwinCardNumber
import com.tangem.tap.domain.twins.TwinCardsManager
import com.tangem.tap.features.onboarding.OnboardingWalletBalance
import com.tangem.tap.features.onboarding.products.note.redux.canBuy
import com.tangem.tap.store
import org.rekotlin.StateType
import kotlin.properties.ReadOnlyProperty
@ -57,7 +57,7 @@ data class TwinCardsState(
get() = currentStep == TwinCardsStep.CreateSecondWallet || currentStep == TwinCardsStep.CreateThirdWallet
val isBuyAllowed: Boolean by ReadOnlyProperty<Any, Boolean> { thisRef, property ->
store.state.globalState.moonpayStatus?.canBuy(walletBalance) ?: false
store.state.globalState.moonpayStatus?.buyIsAllowed(walletBalance.currency.blockchain) ?: false
}
}

View file

@ -10,6 +10,8 @@ import com.tangem.tap.common.redux.StateDialog
import com.tangem.tap.common.redux.global.CryptoCurrencyName
import com.tangem.tap.common.toggleWidget.WidgetState
import com.tangem.tap.domain.configurable.warningMessage.WarningMessage
import com.tangem.tap.domain.extensions.buyIsAllowed
import com.tangem.tap.domain.extensions.sellIsAllowed
import com.tangem.tap.domain.extensions.toSendableAmounts
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
import com.tangem.tap.features.wallet.models.PendingTransaction
@ -17,6 +19,7 @@ import com.tangem.tap.features.wallet.models.toPendingTransactions
import com.tangem.tap.features.wallet.models.toPendingTransactionsForToken
import com.tangem.tap.features.wallet.ui.BalanceStatus
import com.tangem.tap.features.wallet.ui.BalanceWidgetData
import com.tangem.tap.network.moonpay.MoonpayStatus
import com.tangem.tap.store
import org.rekotlin.StateType
import java.math.BigDecimal
@ -144,9 +147,9 @@ data class WalletState(
fun replaceSomeWallets(newWallets: List<WalletData>): List<WalletData> {
val remainingWallets: MutableList<WalletData> = newWallets.toMutableList()
val updatedWallets = wallets.map { wallet ->
val updatedWallets = wallets.map { wallet ->
val newWallet = newWallets
.firstOrNull { wallet.currencyData.currency == it.currencyData.currency }
.firstOrNull { wallet.currencyData.currency == it.currencyData.currency }
if (newWallet == null) {
wallet
} else {
@ -217,7 +220,16 @@ data class Artwork(
data class TradeCryptoState(
val sellingAllowed: Boolean = false,
val buyingAllowed: Boolean = false,
)
) {
companion object {
fun from(moonpayStatus: MoonpayStatus?, walletData: WalletData): TradeCryptoState {
val status = moonpayStatus ?: return walletData.tradeCryptoState
val blockchain = walletData.currency?.blockchain ?: return walletData.tradeCryptoState
return TradeCryptoState(status.sellIsAllowed(blockchain), status.buyIsAllowed(blockchain))
}
}
}
data class WalletData(
val pendingTransactions: List<PendingTransaction> = emptyList(),

View file

@ -21,6 +21,8 @@ import com.tangem.tap.features.home.redux.HomeAction
import com.tangem.tap.features.send.redux.PrepareSendScreen
import com.tangem.tap.features.wallet.redux.*
import com.tangem.tap.network.NetworkStateChanged
import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll
import kotlinx.coroutines.launch
import org.rekotlin.Action
import org.rekotlin.DispatchFunction
@ -51,9 +53,9 @@ class WalletMiddleware {
is WalletAction.LoadWallet -> {
scope.launch {
if (action.blockchain == null) {
walletState.walletManagers.map { walletManager ->
globalState.tapWalletManager.loadWalletData(walletManager)
}
walletState.walletManagers.map { walletManager ->
async { globalState.tapWalletManager.loadWalletData(walletManager) }
}.awaitAll()
} else {
val walletManager = walletState.getWalletManager(action.blockchain)
walletManager?.let { globalState.tapWalletManager.loadWalletData(it) }
@ -108,7 +110,7 @@ class WalletMiddleware {
FirebaseAnalyticsHandler.logCardSdkError(
error,
FirebaseAnalyticsHandler.ActionToLog.CreateWallet,
card = store.state.detailsState.card
card = store.state.detailsState.scanResponse?.card
)
}
}

View file

@ -95,14 +95,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
currencySymbol = wallet.currencyData.currencySymbol,
),
mainButton = WalletMainButton.SendButton(false),
tradeCryptoState = TradeCryptoState(
sellingAllowed = action.moonpayStatus?.availableToSell?.contains(
wallet.currencyData.currencySymbol
) ?: wallet.tradeCryptoState.sellingAllowed,
buyingAllowed = action.moonpayStatus?.availableToBuy?.contains(
wallet.currencyData.currencySymbol
) ?: wallet.tradeCryptoState.buyingAllowed
)
tradeCryptoState = TradeCryptoState.from(action.moonpayStatus, wallet)
)
}
newState = newState.copy(
@ -123,14 +116,7 @@ private fun internalReduce(action: Action, state: AppState): WalletState {
currencySymbol = wallet.currencyData.currencySymbol,
),
mainButton = WalletMainButton.SendButton(false),
tradeCryptoState = TradeCryptoState(
sellingAllowed = action.moonpayStatus?.availableToSell?.contains(
wallet.currencyData.currencySymbol
) ?: wallet.tradeCryptoState.sellingAllowed,
buyingAllowed = action.moonpayStatus?.availableToBuy?.contains(
wallet.currencyData.currencySymbol
) ?: wallet.tradeCryptoState.buyingAllowed
)
tradeCryptoState = TradeCryptoState.from(action.moonpayStatus, wallet)
)
}
val wallets = newState.replaceSomeWallets(newWallets)

View file

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

View file

@ -164,11 +164,11 @@ class SingleWalletView : WalletView {
allowedToBuy && !allowedToSell -> R.drawable.ic_arrow_up_short_btn
!allowedToBuy && allowedToSell -> R.drawable.ic_arrow_down_short_button
allowedToBuy && allowedToSell -> R.drawable.ic_arrows_up_down_short_btn
else -> R.string.wallet_button_trade
else -> null
}
with(fragment) {
btn_trade.text = getText(text)
btn_trade.setCompoundDrawablesWithIntrinsicBounds(0, icon, 0, 0)
btn_trade.setCompoundDrawablesWithIntrinsicBounds(0, icon ?: 0, 0, 0)
btn_trade.setOnClickListener { if (action != null) store.dispatch(action) }
}
}

View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="true" android:alpha="@dimen/material_emphasis_medium" android:color="?attr/colorOnSurface"/>
<item android:state_enabled="true" android:alpha="@dimen/material_emphasis_high_type" android:color="?attr/colorOnSurface"/>
<item android:alpha="@dimen/material_emphasis_disabled" android:color="?attr/colorOnSurface"/>
</selector>

View file

@ -53,7 +53,7 @@
android:backgroundTint="@color/lightGray0"
android:paddingStart="16dp"
android:paddingEnd="16dp"
app:layout_constraintEnd_toStartOf="@+id/btn_fl_explore"
app:layout_constraintEnd_toStartOf="@+id/btn_fl_share"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline3">
@ -90,7 +90,7 @@
<FrameLayout
android:id="@+id/btn_fl_explore"
android:id="@+id/btn_fl_share"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_marginEnd="32dp"