Updated on 2026-08-14

This commit is contained in:
Tangem 2020-10-12 19:00:38 +03:00
parent 6fd17ffb31
commit 11f7775787
21 changed files with 319 additions and 18 deletions

View file

@ -7,6 +7,7 @@ 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.details.ui.DetailsSecurityFragment
import com.tangem.tap.features.disclaimer.ui.DisclaimerFragment
import com.tangem.tap.features.home.HomeFragment
import com.tangem.tap.features.send.ui.SendFragment
import com.tangem.tap.features.wallet.ui.WalletFragment
@ -42,5 +43,6 @@ private fun fragmentFactory(screen: AppScreen): Fragment {
AppScreen.Details -> DetailsFragment()
AppScreen.DetailsConfirm -> DetailsConfirmFragment()
AppScreen.DetailsSecurity -> DetailsSecurityFragment()
AppScreen.Disclaimer -> DisclaimerFragment()
}
}

View file

@ -3,6 +3,7 @@ package com.tangem.tap.common.redux
import com.tangem.tap.common.redux.global.globalReducer
import com.tangem.tap.common.redux.navigation.NavigationReducer
import com.tangem.tap.features.details.redux.DetailsReducer
import com.tangem.tap.features.disclaimer.redux.DisclaimerReducer
import com.tangem.tap.features.home.redux.HomeReducer
import com.tangem.tap.features.send.redux.reducers.SendScreenReducer
import com.tangem.tap.features.wallet.redux.WalletReducer
@ -18,7 +19,8 @@ fun appReducer(action: Action, state: AppState?): AppState {
homeState = HomeReducer.reduce(action, state),
walletState = WalletReducer.reduce(action, state),
sendState = SendScreenReducer.reduce(action, state.sendState),
detailsState = DetailsReducer.reduce(action, state)
detailsState = DetailsReducer.reduce(action, state),
disclaimerState = DisclaimerReducer.reduce(action, state)
)
}

View file

@ -6,6 +6,8 @@ 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.disclaimer.redux.DisclaimerMiddleware
import com.tangem.tap.features.disclaimer.redux.DisclaimerState
import com.tangem.tap.features.home.redux.HomeState
import com.tangem.tap.features.home.redux.homeMiddleware
import com.tangem.tap.features.send.redux.middlewares.sendMiddleware
@ -21,7 +23,8 @@ data class AppState(
val homeState: HomeState = HomeState(),
val walletState: WalletState = WalletState(),
val sendState: SendState = SendState(),
val detailsState: DetailsState = DetailsState()
val detailsState: DetailsState = DetailsState(),
val disclaimerState: DisclaimerState = DisclaimerState()
) : StateType {
companion object {
@ -29,7 +32,8 @@ data class AppState(
return listOf(
logMiddleware, navigationMiddleware, notificationsMiddleware, globalMiddleware,
homeMiddleware, walletMiddleware, sendMiddleware,
DetailsMiddleware().detailsMiddleware
DetailsMiddleware().detailsMiddleware,
DisclaimerMiddleware().disclaimerMiddleware
)
}
}

View file

@ -14,5 +14,5 @@ sealed class GlobalAction : Action {
object RestoreAppCurrency : GlobalAction() {
data class Success(val appCurrency: FiatCurrencyName) : GlobalAction()
}
data class UpdateWalletSignedHashes(val walletSignedHashes: Int) : GlobalAction()
data class UpdateWalletSignedHashes(val walletSignedHashes: Int?) : GlobalAction()
}

View file

@ -9,4 +9,4 @@ data class NavigationState(
val activity: WeakReference<FragmentActivity>? = null
) : StateType
enum class AppScreen { Home, Wallet, Send, Details, DetailsConfirm, DetailsSecurity }
enum class AppScreen { Home, Wallet, Send, Details, DetailsConfirm, DetailsSecurity, Disclaimer }

View file

@ -17,6 +17,8 @@ sealed class DetailsAction : Action {
val fiatCurrencies: List<FiatCurrencyName>? = null,
): DetailsAction()
object ShowDisclaimer : DetailsAction()
sealed class EraseWallet : DetailsAction() {
object Check : EraseWallet()

View file

@ -6,6 +6,7 @@ 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.disclaimer.redux.DisclaimerAction
import com.tangem.tap.features.wallet.redux.WalletAction
import com.tangem.tap.network.coinmarketcap.CoinMarketCapService
import com.tangem.tap.preferencesStorage
@ -29,6 +30,10 @@ class DetailsMiddleware {
is DetailsAction.EraseWallet -> eraseWalletMiddleware.handle(action)
is DetailsAction.AppCurrencyAction -> appCurrencyMiddleware.handle(action)
is DetailsAction.ManageSecurity -> manageSecurityMiddleware.handle(action)
is DetailsAction.ShowDisclaimer -> {
store.dispatch(DisclaimerAction.ShowAcceptedDisclaimer)
store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer))
}
}
next(action)
}

View file

@ -30,6 +30,7 @@ private fun internalReduce(action: Action, state: AppState): DetailsState {
is DetailsAction.ManageSecurity -> {
handleSecurityAction(action, detailsState)
}
else -> detailsState
}
}

View file

@ -64,6 +64,8 @@ class DetailsFragment : Fragment(R.layout.fragment_details), StoreSubscriber<Det
tv_signed_hashes.text = state.cardInfo.signedHashes.toString()
}
tv_disclaimer.setOnClickListener { store.dispatch(DetailsAction.ShowDisclaimer) }
tv_erase_wallet.setOnClickListener {
store.dispatch(DetailsAction.EraseWallet.Check)
store.dispatch(DetailsAction.EraseWallet.Proceed)

View file

@ -0,0 +1,8 @@
package com.tangem.tap.features.disclaimer.redux
import org.rekotlin.Action
sealed class DisclaimerAction: Action {
object AcceptDisclaimer : DisclaimerAction()
object ShowAcceptedDisclaimer : DisclaimerAction()
}

View file

@ -0,0 +1,24 @@
package com.tangem.tap.features.disclaimer.redux
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.preferencesStorage
import com.tangem.tap.store
import org.rekotlin.Middleware
class DisclaimerMiddleware {
val disclaimerMiddleware: Middleware<AppState> = { dispatch, state ->
{ next ->
{ action ->
when (action) {
is DisclaimerAction.AcceptDisclaimer -> {
preferencesStorage.saveDisclaimerAccepted()
store.dispatch(NavigationAction.NavigateTo(AppScreen.Wallet))
}
}
next(action)
}
}
}
}

View file

@ -0,0 +1,21 @@
package com.tangem.tap.features.disclaimer.redux
import com.tangem.tap.common.redux.AppState
import org.rekotlin.Action
class DisclaimerReducer {
companion object {
fun reduce(action: Action, state: AppState): DisclaimerState = internalReduce(action, state)
}
}
private fun internalReduce(action: Action, state: AppState): DisclaimerState {
if (action !is DisclaimerAction) return state.disclaimerState
val disclaimerState = state.disclaimerState
return when (action) {
is DisclaimerAction.AcceptDisclaimer, DisclaimerAction.ShowAcceptedDisclaimer -> {
disclaimerState.copy(accepted = true)
}
}
}

View file

@ -0,0 +1,7 @@
package com.tangem.tap.features.disclaimer.redux
import org.rekotlin.StateType
data class DisclaimerState(
val accepted: Boolean = false
) : StateType

View file

@ -0,0 +1,63 @@
package com.tangem.tap.features.disclaimer.ui
import android.os.Bundle
import android.view.View
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
import androidx.transition.TransitionInflater
import com.tangem.tap.common.extensions.hide
import com.tangem.tap.common.redux.navigation.NavigationAction
import com.tangem.tap.features.disclaimer.redux.DisclaimerAction
import com.tangem.tap.features.disclaimer.redux.DisclaimerState
import com.tangem.tap.store
import com.tangem.wallet.R
import kotlinx.android.synthetic.main.fragment_disclaimer.*
import org.rekotlin.StoreSubscriber
class DisclaimerFragment : Fragment(R.layout.fragment_disclaimer),
StoreSubscriber<DisclaimerState> {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
store.dispatch(NavigationAction.PopBackTo())
}
})
val inflater = TransitionInflater.from(requireContext())
enterTransition = inflater.inflateTransition(R.transition.slide_right)
exitTransition = inflater.inflateTransition(R.transition.fade)
}
override fun onStart() {
super.onStart()
store.subscribe(this) { state ->
state.skipRepeats { oldState, newState ->
oldState.disclaimerState == newState.disclaimerState
}.select { it.disclaimerState }
}
}
override fun onStop() {
super.onStop()
store.unsubscribe(this)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
toolbar.setNavigationOnClickListener {
store.dispatch(NavigationAction.PopBackTo())
}
setOnClickListeners()
}
private fun setOnClickListeners() {
btn_accept.setOnClickListener { store.dispatch(DisclaimerAction.AcceptDisclaimer) }
}
override fun newState(state: DisclaimerState) {
if (activity == null) return
if (state.accepted) btn_accept.hide()
}
}

View file

@ -54,6 +54,4 @@ class HomeFragment : Fragment(R.layout.fragment_home), StoreSubscriber<HomeState
val inflater = TransitionInflater.from(requireContext())
exitTransition = inflater.inflateTransition(R.transition.fade)
}
}

View file

@ -34,7 +34,7 @@ val homeMiddleware: Middleware<AppState> = { dispatch, state ->
is CompletionResult.Success -> {
store.dispatch(GlobalAction.RestoreAppCurrency)
store.state.globalState.tapWalletManager.onCardScanned(result.data)
navigateToWallet()
showDisclaimerOrNavigateToWallet()
}
}
}
@ -51,6 +51,15 @@ val homeMiddleware: Middleware<AppState> = { dispatch, state ->
}
}
private fun showDisclaimerOrNavigateToWallet() {
if (preferencesStorage.wasDisclaimerAccepted()) {
navigateToWallet()
} else {
store.dispatch(NavigationAction.NavigateTo(AppScreen.Disclaimer))
}
}
private fun navigateToWallet() {
if (store.state.navigationState.backStack.isNotEmpty()) {
val currentScreen = store.state.navigationState.backStack.last()

View file

@ -45,7 +45,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
setHasOptionsMenu(true)
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
store.dispatch(NavigationAction.PopBackTo())
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
}
})
val inflater = TransitionInflater.from(requireContext())
@ -72,7 +72,7 @@ class WalletFragment : Fragment(R.layout.fragment_wallet), StoreSubscriber<Walle
(activity as? AppCompatActivity)?.setSupportActionBar(toolbar)
toolbar.setNavigationOnClickListener {
store.dispatch(NavigationAction.PopBackTo())
store.dispatch(NavigationAction.PopBackTo(AppScreen.Home))
}
btn_scan.setOnClickListener {

View file

@ -65,12 +65,22 @@ class PreferencesStorage(applicationContext: Application) {
private fun restoreScannedCardIds(): String =
preferences.getString(SCANNED_CARDS_IDS_KEY, "") ?: ""
fun saveDisclaimerAccepted() {
preferences.edit().putBoolean(DISCLAIMER_ACCEPTED_KEY, true).apply()
}
fun wasDisclaimerAccepted(): Boolean {
return preferences.getBoolean(DISCLAIMER_ACCEPTED_KEY, false)
}
companion object {
private const val PREFERENCES_NAME = "tapPrefs"
private const val APP_CURRENCY_KEY = "appCurrency"
private const val FIAT_CURRENCIES_KEY = "fiatCurrencies"
private const val FIRST_LAUNCH_CHECK_KEY = "firstLaunchCheck"
private const val SCANNED_CARDS_IDS_KEY = "scannedCardIds"
private const val DISCLAIMER_ACCEPTED_KEY = "disclaimerAccepted"
}
}