Updated on 2026-08-14
This commit is contained in:
parent
cffb0c6717
commit
81307bff30
12 changed files with 390 additions and 31 deletions
|
|
@ -4,6 +4,7 @@ import androidx.fragment.app.Fragment
|
|||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.features.details.ui.DetailsConfirmFragment
|
||||
import com.tangem.tap.features.details.ui.DetailsFragment
|
||||
import com.tangem.tap.features.home.HomeFragment
|
||||
import com.tangem.tap.features.send.ui.SendFragment
|
||||
|
|
@ -38,5 +39,6 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
|
|||
AppScreen.Wallet -> WalletFragment()
|
||||
AppScreen.Send -> SendFragment()
|
||||
AppScreen.Details -> DetailsFragment()
|
||||
AppScreen.DetailsConfirm -> DetailsConfirmFragment()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,10 @@
|
|||
package com.tangem.tap.common.redux
|
||||
|
||||
import com.tangem.tap.common.redux.global.GlobalState
|
||||
import com.tangem.tap.common.redux.global.globalMiddleware
|
||||
import com.tangem.tap.common.redux.navigation.NavigationState
|
||||
import com.tangem.tap.common.redux.navigation.navigationMiddleware
|
||||
import com.tangem.tap.features.details.redux.DetailsMiddleware
|
||||
import com.tangem.tap.features.details.redux.DetailsState
|
||||
import com.tangem.tap.features.home.redux.homeMiddleware
|
||||
import com.tangem.tap.features.send.redux.middlewares.sendMiddleware
|
||||
|
|
@ -13,18 +15,19 @@ import org.rekotlin.Middleware
|
|||
import org.rekotlin.StateType
|
||||
|
||||
data class AppState(
|
||||
val navigationState: NavigationState = NavigationState(),
|
||||
val globalState: GlobalState = GlobalState(),
|
||||
val walletState: WalletState = WalletState(),
|
||||
val sendState: SendState = SendState(),
|
||||
val detailsState: DetailsState = DetailsState()
|
||||
val navigationState: NavigationState = NavigationState(),
|
||||
val globalState: GlobalState = GlobalState(),
|
||||
val walletState: WalletState = WalletState(),
|
||||
val sendState: SendState = SendState(),
|
||||
val detailsState: DetailsState = DetailsState()
|
||||
) : StateType {
|
||||
|
||||
companion object {
|
||||
fun getMiddleware(): List<Middleware<AppState>> {
|
||||
return listOf(
|
||||
logMiddleware, navigationMiddleware, notificationsMiddleware,
|
||||
homeMiddleware, walletMiddleware, sendMiddleware
|
||||
logMiddleware, navigationMiddleware, notificationsMiddleware, globalMiddleware,
|
||||
homeMiddleware, walletMiddleware, sendMiddleware,
|
||||
DetailsMiddleware().detailsMiddleware
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,4 +9,4 @@ data class NavigationState(
|
|||
val activity: WeakReference<FragmentActivity>? = null
|
||||
) : StateType
|
||||
|
||||
enum class AppScreen { Home, Wallet, Send, Details }
|
||||
enum class AppScreen { Home, Wallet, Send, Details, DetailsConfirm }
|
||||
|
|
@ -1,10 +1,44 @@
|
|||
package com.tangem.tap.features.details.redux
|
||||
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.commands.Card
|
||||
import com.tangem.tap.common.redux.NotificationAction
|
||||
import com.tangem.tap.common.redux.global.FiatCurrencyName
|
||||
import com.tangem.tap.network.coinmarketcap.FiatCurrency
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.Action
|
||||
|
||||
sealed class DetailsAction : Action {
|
||||
|
||||
data class SetCard(val card: Card): DetailsAction()
|
||||
data class PrepareScreen(
|
||||
val card: Card,
|
||||
val wallet: Wallet?,
|
||||
val fiatCurrencyName: FiatCurrencyName,
|
||||
val fiatCurrencies: List<FiatCurrencyName>? = null,
|
||||
): DetailsAction()
|
||||
|
||||
|
||||
sealed class EraseWallet : DetailsAction() {
|
||||
object Check : EraseWallet()
|
||||
object Proceed : EraseWallet() {
|
||||
object NotAllowedByCard: EraseWallet(), NotificationAction {
|
||||
override val messageResource = R.string.details_notification_erase_wallet_not_allowed
|
||||
}
|
||||
object NotEmpty: EraseWallet(), NotificationAction {
|
||||
override val messageResource = R.string.details_notification_erase_wallet_not_possible
|
||||
}
|
||||
}
|
||||
object Confirm : EraseWallet()
|
||||
object Cancel : EraseWallet()
|
||||
object Failure : EraseWallet()
|
||||
object Success : EraseWallet()
|
||||
}
|
||||
|
||||
sealed class AppCurrencyAction : DetailsAction() {
|
||||
data class SetCurrencies(val currencies: List<FiatCurrency>) : AppCurrencyAction()
|
||||
object ChooseAppCurrency : AppCurrencyAction()
|
||||
object Cancel: AppCurrencyAction()
|
||||
data class SelectAppCurrency(val fiatCurrencyName: FiatCurrencyName): AppCurrencyAction()
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1 +1,81 @@
|
|||
package com.tangem.tap.features.details.redux
|
||||
|
||||
import com.tangem.commands.common.network.Result
|
||||
import com.tangem.common.CompletionResult
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.common.redux.navigation.AppScreen
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.wallet.redux.WalletAction
|
||||
import com.tangem.tap.network.coinmarketcap.CoinMarketCapService
|
||||
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.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.rekotlin.Middleware
|
||||
|
||||
class DetailsMiddleware {
|
||||
val detailsMiddleware: Middleware<AppState> = { dispatch, state ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
when (action) {
|
||||
is DetailsAction.PrepareScreen -> {
|
||||
scope.launch {
|
||||
val loadedCurrencies = preferencesStorage.getFiatCurrencies()
|
||||
if (loadedCurrencies.isNullOrEmpty()) {
|
||||
val response = CoinMarketCapService().getFiatCurrencies()
|
||||
withContext(Dispatchers.Main) {
|
||||
when (response) {
|
||||
is Result.Success -> {
|
||||
preferencesStorage.saveFiatCurrencies(response.data)
|
||||
store.dispatch(DetailsAction.AppCurrencyAction.SetCurrencies(response.data))
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
withContext(Dispatchers.Main) {
|
||||
store.dispatch(DetailsAction.AppCurrencyAction.SetCurrencies(loadedCurrencies))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
is DetailsAction.EraseWallet.Proceed -> {
|
||||
when (store.state.detailsState.eraseWalletState) {
|
||||
EraseWalletState.Allowed ->
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.DetailsConfirm))
|
||||
EraseWalletState.NotAllowedByCard ->
|
||||
store.dispatch(DetailsAction.EraseWallet.Proceed.NotAllowedByCard)
|
||||
EraseWalletState.NotEmpty ->
|
||||
store.dispatch(DetailsAction.EraseWallet.Proceed.NotEmpty)
|
||||
}
|
||||
}
|
||||
is DetailsAction.EraseWallet.Cancel -> {
|
||||
store.dispatch(NavigationAction.PopBackTo())
|
||||
}
|
||||
is DetailsAction.EraseWallet.Confirm -> {
|
||||
scope.launch {
|
||||
val result = tangemSdkManager.eraseWallet()
|
||||
withContext(Dispatchers.Main) {
|
||||
when (result) {
|
||||
is CompletionResult.Success -> {
|
||||
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
is DetailsAction.AppCurrencyAction.SelectAppCurrency -> {
|
||||
preferencesStorage.saveAppCurrency(action.fiatCurrencyName)
|
||||
store.dispatch(GlobalAction.ChangeAppCurrency(action.fiatCurrencyName))
|
||||
store.dispatch(WalletAction.LoadFiatRate)
|
||||
}
|
||||
}
|
||||
next(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.tap.features.details.redux
|
||||
|
||||
import com.tangem.commands.Card
|
||||
import com.tangem.commands.Settings
|
||||
import com.tangem.common.extensions.isZero
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import org.rekotlin.Action
|
||||
|
||||
|
|
@ -16,13 +18,76 @@ private fun internalReduce(action: Action, state: AppState): DetailsState {
|
|||
|
||||
var detailsState = state.detailsState
|
||||
when (action) {
|
||||
is DetailsAction.SetCard -> {
|
||||
detailsState = DetailsState(card = action.card, cardInfo = action.card.toCardInfo())
|
||||
is DetailsAction.PrepareScreen -> {
|
||||
detailsState = DetailsState(
|
||||
card = action.card, wallet = action.wallet,
|
||||
cardInfo = action.card.toCardInfo(),
|
||||
appCurrencyState = AppCurrencyState(
|
||||
action.fiatCurrencyName
|
||||
)
|
||||
)
|
||||
}
|
||||
is DetailsAction.EraseWallet -> {
|
||||
detailsState = handleEraseWallet(action, detailsState)
|
||||
}
|
||||
is DetailsAction.AppCurrencyAction -> {
|
||||
detailsState = handleAppCurrencyAction(action, detailsState)
|
||||
}
|
||||
}
|
||||
return detailsState
|
||||
}
|
||||
|
||||
private fun handleEraseWallet(action: DetailsAction.EraseWallet, state: DetailsState): DetailsState {
|
||||
return when (action) {
|
||||
DetailsAction.EraseWallet.Check -> {
|
||||
val notAllowedByCard = state.card?.settingsMask?.contains(Settings.ProhibitPurgeWallet) == true
|
||||
val notEmpty = state.wallet?.transactions?.isNullOrEmpty() != true ||
|
||||
state.wallet.amounts.toList().unzip().second.map { it.value?.isZero() }.contains(false)
|
||||
val eraseWalletState = when {
|
||||
notAllowedByCard -> EraseWalletState.NotAllowedByCard
|
||||
notEmpty -> EraseWalletState.NotEmpty
|
||||
else -> EraseWalletState.Allowed
|
||||
}
|
||||
state.copy(eraseWalletState = eraseWalletState)
|
||||
}
|
||||
DetailsAction.EraseWallet.Proceed -> {
|
||||
if (state.eraseWalletState == EraseWalletState.Allowed) {
|
||||
state.copy(confirmScreenState = ConfirmScreenState.EraseWallet)
|
||||
} else {
|
||||
state
|
||||
}
|
||||
}
|
||||
DetailsAction.EraseWallet.Cancel -> state.copy(eraseWalletState = null)
|
||||
DetailsAction.EraseWallet.Failure -> state.copy(eraseWalletState = null)
|
||||
DetailsAction.EraseWallet.Success -> state.copy(eraseWalletState = null)
|
||||
else -> state
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleAppCurrencyAction(
|
||||
action: DetailsAction.AppCurrencyAction, state: DetailsState
|
||||
): DetailsState {
|
||||
return when (action) {
|
||||
is DetailsAction.AppCurrencyAction.SetCurrencies -> {
|
||||
state.copy(appCurrencyState = state.appCurrencyState.copy(fiatCurrencies = action.currencies))
|
||||
}
|
||||
DetailsAction.AppCurrencyAction.ChooseAppCurrency -> {
|
||||
state.copy(appCurrencyState = state.appCurrencyState.copy(showAppCurrencyDialog = true))
|
||||
}
|
||||
DetailsAction.AppCurrencyAction.Cancel -> {
|
||||
state.copy(appCurrencyState = state.appCurrencyState.copy(showAppCurrencyDialog = false))
|
||||
}
|
||||
is DetailsAction.AppCurrencyAction.SelectAppCurrency -> {
|
||||
state.copy(
|
||||
appCurrencyState = state.appCurrencyState.copy(
|
||||
fiatCurrencyName = action.fiatCurrencyName, showAppCurrencyDialog = false
|
||||
)
|
||||
)
|
||||
}
|
||||
else -> state
|
||||
}
|
||||
}
|
||||
|
||||
private fun Card.toCardInfo(): CardInfo? {
|
||||
val cardId = this.cardId.chunked(4).joinToString(separator = " ")
|
||||
val issuer = this.cardData?.issuerName ?: return null
|
||||
|
|
|
|||
|
|
@ -1,11 +1,19 @@
|
|||
package com.tangem.tap.features.details.redux
|
||||
|
||||
import com.tangem.blockchain.common.Wallet
|
||||
import com.tangem.commands.Card
|
||||
import com.tangem.tap.common.entities.TapCurrency.Companion.DEFAULT_FIAT_CURRENCY
|
||||
import com.tangem.tap.common.redux.global.FiatCurrencyName
|
||||
import com.tangem.tap.network.coinmarketcap.FiatCurrency
|
||||
import org.rekotlin.StateType
|
||||
|
||||
data class DetailsState(
|
||||
val card: Card? = null,
|
||||
val cardInfo: CardInfo? = null
|
||||
val wallet: Wallet? = null,
|
||||
val cardInfo: CardInfo? = null,
|
||||
val appCurrencyState: AppCurrencyState = AppCurrencyState(),
|
||||
val eraseWalletState: EraseWalletState? = null,
|
||||
val confirmScreenState: ConfirmScreenState? = null,
|
||||
) : StateType
|
||||
|
||||
data class CardInfo(
|
||||
|
|
@ -13,3 +21,11 @@ data class CardInfo(
|
|||
val issuer: String,
|
||||
val signedHashes: Int
|
||||
)
|
||||
|
||||
enum class EraseWalletState { Allowed, NotAllowedByCard, NotEmpty }
|
||||
enum class ConfirmScreenState { EraseWallet, LongTap, AccessCode, PassCode }
|
||||
data class AppCurrencyState(
|
||||
val fiatCurrencyName: FiatCurrencyName = DEFAULT_FIAT_CURRENCY,
|
||||
val showAppCurrencyDialog: Boolean = false,
|
||||
val fiatCurrencies: List<FiatCurrency>? = null,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import androidx.activity.OnBackPressedCallback
|
|||
import androidx.fragment.app.Fragment
|
||||
import androidx.transition.TransitionInflater
|
||||
import com.tangem.tap.common.redux.navigation.NavigationAction
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.details.redux.DetailsState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
|
@ -15,6 +16,8 @@ import org.rekotlin.StoreSubscriber
|
|||
|
||||
class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<DetailsState> {
|
||||
|
||||
var currencySelectionDialog = CurrencySelectionDialog()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
|
||||
|
|
@ -50,9 +53,6 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
override fun newState(state: DetailsState) {
|
||||
if (activity == null) return
|
||||
|
||||
|
|
@ -62,6 +62,29 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
|
|||
tv_issuer.text = state.cardInfo.issuer
|
||||
tv_signed_hashes.text = state.cardInfo.signedHashes.toString()
|
||||
}
|
||||
|
||||
tv_erase_wallet.setOnClickListener {
|
||||
store.dispatch(DetailsAction.EraseWallet.Check)
|
||||
store.dispatch(DetailsAction.EraseWallet.Proceed)
|
||||
}
|
||||
|
||||
tv_app_currency.text = state.appCurrencyState.fiatCurrencyName
|
||||
|
||||
tv_app_currency_title.setOnClickListener {
|
||||
store.dispatch(DetailsAction.AppCurrencyAction.ChooseAppCurrency)
|
||||
}
|
||||
|
||||
if (state.appCurrencyState.showAppCurrencyDialog &&
|
||||
!state.appCurrencyState.fiatCurrencies.isNullOrEmpty()) {
|
||||
currencySelectionDialog.show(
|
||||
state.appCurrencyState.fiatCurrencies,
|
||||
state.appCurrencyState.fiatCurrencyName,
|
||||
requireContext()
|
||||
)
|
||||
} else {
|
||||
currencySelectionDialog.clear()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -156,10 +156,10 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
is WalletMainButton.SendButton -> R.string.wallet_button_send
|
||||
is WalletMainButton.CreateWalletButton -> R.string.wallet_button_create_wallet
|
||||
}
|
||||
btn_main.text = getString(buttonTitle)
|
||||
btn_main.isEnabled = state.mainButton.enabled
|
||||
btn_confirm.text = getString(buttonTitle)
|
||||
btn_confirm.isEnabled = state.mainButton.enabled
|
||||
|
||||
btn_main.setOnClickListener {
|
||||
btn_confirm.setOnClickListener {
|
||||
when (state.mainButton) {
|
||||
is WalletMainButton.SendButton -> store.dispatch(WalletAction.Send())
|
||||
is WalletMainButton.CreateWalletButton -> store.dispatch(WalletAction.CreateWallet)
|
||||
|
|
@ -223,7 +223,10 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
|
|||
return when (item.itemId) {
|
||||
R.id.details_menu -> {
|
||||
store.state.globalState.scanNoteResponse?.card?.let { card ->
|
||||
store.dispatch(DetailsAction.SetCard(card))
|
||||
store.dispatch(DetailsAction.PrepareScreen(
|
||||
card, store.state.walletState.wallet,
|
||||
store.state.globalState.appCurrency
|
||||
))
|
||||
store.dispatch(NavigationAction.NavigateTo(AppScreen.Details))
|
||||
true
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue