Updated on 2026-08-14
This commit is contained in:
commit
2541461d44
954 changed files with 22834 additions and 21200 deletions
|
|
@ -9,9 +9,11 @@ import com.tangem.tap.common.ui.*
|
|||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectDialog
|
||||
import com.tangem.tap.features.details.ui.walletconnect.dialogs.*
|
||||
import com.tangem.tap.features.onboarding.OnboardingDialog
|
||||
import com.tangem.tap.features.onboarding.products.twins.ui.dialog.TwinningProcessNotCompletedDialog
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupDialog
|
||||
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.*
|
||||
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.ConfirmDiscardingBackupDialog
|
||||
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.UnfinishedBackupFoundDialog
|
||||
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.WalletActivationErrorDialog
|
||||
import com.tangem.tap.features.onboarding.products.wallet.ui.dialogs.WalletAlreadyWasUsedDialog
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.StoreSubscriber
|
||||
|
|
@ -56,10 +58,6 @@ class DialogManager : StoreSubscriber<GlobalState> {
|
|||
messageRes = R.string.nfc_error_unavailable,
|
||||
context = context,
|
||||
)
|
||||
is AppDialog.AddressInfoDialog -> AddressInfoBottomSheetDialog(state.dialog, context)
|
||||
is AppDialog.TestActionsDialog -> TestActionsBottomSheetDialog(state.dialog, context)
|
||||
is OnboardingDialog.TwinningProcessNotCompleted -> TwinningProcessNotCompletedDialog.create(context)
|
||||
is OnboardingDialog.InterruptOnboarding -> InterruptOnboardingDialog.create(context, state.dialog)
|
||||
is OnboardingDialog.WalletActivationError -> WalletActivationErrorDialog.create(context, state.dialog)
|
||||
is WalletConnectDialog.UnsupportedCard ->
|
||||
SimpleAlertDialog.create(
|
||||
|
|
@ -143,9 +141,6 @@ class DialogManager : StoreSubscriber<GlobalState> {
|
|||
messageRes = R.string.unsupported_wc_version,
|
||||
context = context,
|
||||
)
|
||||
is BackupDialog.AttestationFailed -> AttestationFailedDialog.create(context)
|
||||
is BackupDialog.AddMoreBackupCards -> AddMoreBackupCardsDialog.create(context)
|
||||
is BackupDialog.BackupInProgress -> BackupInProgressDialog.create(context)
|
||||
is BackupDialog.UnfinishedBackupFound -> UnfinishedBackupFoundDialog.create(
|
||||
context = context,
|
||||
scanResponse = state.dialog.scanResponse,
|
||||
|
|
@ -154,10 +149,6 @@ class DialogManager : StoreSubscriber<GlobalState> {
|
|||
context = context,
|
||||
unfinishedBackupScanResponse = state.dialog.scanResponse,
|
||||
)
|
||||
is BackupDialog.ResetBackupCard -> ResetBackupCardDialog.create(
|
||||
context = context,
|
||||
cardId = state.dialog.cardId,
|
||||
)
|
||||
is AppDialog.TokensAreLinkedDialog -> SimpleAlertDialog.create(
|
||||
title = context.getString(state.dialog.titleRes, state.dialog.currencySymbol),
|
||||
message = context.getString(
|
||||
|
|
|
|||
|
|
@ -1,16 +1,5 @@
|
|||
package com.tangem.tap.common
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import androidx.appcompat.widget.LinearLayoutCompat
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.tangem.tap.common.extensions.dispatchDialogHide
|
||||
import com.tangem.tap.common.redux.AppDialog
|
||||
import com.tangem.tap.store
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
|
|
@ -21,41 +10,4 @@ object TestActions {
|
|||
var testAmountInjectionForWalletManagerEnabled = false
|
||||
}
|
||||
|
||||
typealias TestAction = Pair<String, () -> Unit>
|
||||
|
||||
class TestActionsBottomSheetDialog(
|
||||
private val appDialog: AppDialog.TestActionsDialog,
|
||||
context: Context,
|
||||
) : BottomSheetDialog(context) {
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContentView(createContentView())
|
||||
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
behavior.peekHeight = 400
|
||||
setOnCancelListener {
|
||||
store.dispatchDialogHide()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createContentView(): View {
|
||||
val actionButtons = mutableListOf<View>()
|
||||
appDialog.actionsList.forEach { (actionName, action) ->
|
||||
val view = Button(context).apply {
|
||||
text = actionName
|
||||
setOnClickListener {
|
||||
action()
|
||||
cancel()
|
||||
}
|
||||
}
|
||||
actionButtons.add(view)
|
||||
}
|
||||
|
||||
return LinearLayoutCompat(context).apply {
|
||||
orientation = LinearLayoutCompat.VERTICAL
|
||||
actionButtons.forEach { addView(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
typealias TestAction = Pair<String, () -> Unit>
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import com.tangem.utils.Provider
|
||||
import com.tangem.wallet.R
|
||||
import timber.log.Timber
|
||||
|
||||
fun FragmentManager.showFragmentAllowingStateLoss(name: String, fragmentProvider: Provider<Fragment>) {
|
||||
if (backStackEntryCount > 0) {
|
||||
val currentFragmentName = getBackStackEntryAt(backStackEntryCount - 1).name
|
||||
|
||||
if (name == currentFragmentName) {
|
||||
Timber.i("Fragment $name is already at the top of the stack")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
Timber.i("Showing $name route")
|
||||
|
||||
val isPoppedBack = popBackStackImmediate(name, 0)
|
||||
|
||||
if (!isPoppedBack) {
|
||||
val fragment = fragmentProvider()
|
||||
|
||||
if (fragment is DialogFragment) {
|
||||
fragment.showDialog(fragmentManager = this, name)
|
||||
} else {
|
||||
fragment.showFragment(fragmentManager = this, name)
|
||||
}
|
||||
|
||||
Timber.i("Route $name is shown")
|
||||
} else {
|
||||
Timber.i("Route $name is found in backstack and shown")
|
||||
}
|
||||
}
|
||||
|
||||
private fun DialogFragment.showDialog(fragmentManager: FragmentManager, name: String) {
|
||||
val transaction = fragmentManager.beginTransaction()
|
||||
|
||||
try {
|
||||
transaction.addToBackStack(name)
|
||||
show(transaction, name)
|
||||
} catch (e: IllegalStateException) {
|
||||
transaction.add(this, name)
|
||||
transaction.addToBackStack(name)
|
||||
|
||||
transaction.commitAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Fragment.showFragment(fragmentManager: FragmentManager, name: String) {
|
||||
val transaction = fragmentManager.beginTransaction()
|
||||
|
||||
transaction.replace(R.id.fragment_container, this, name)
|
||||
transaction.addToBackStack(name)
|
||||
|
||||
transaction.commitAllowingStateLoss()
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
import com.tangem.common.routing.AppRouter
|
||||
import com.tangem.core.ui.extensions.stringReference
|
||||
import com.tangem.core.ui.message.SnackbarMessage
|
||||
import com.tangem.domain.common.extensions.withMainContext
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
|
|
@ -43,20 +45,16 @@ suspend fun Store<AppState>.onUserWalletSelected(userWallet: UserWallet) {
|
|||
state.globalState.tapWalletManager.onWalletSelected(userWallet)
|
||||
}
|
||||
|
||||
fun Store<*>.dispatchErrorNotification(error: TapError) {
|
||||
dispatchOnMain(GlobalAction.ShowErrorNotification(error))
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fatal used to indicate errors that should not normally occur
|
||||
*/
|
||||
fun Store<*>.dispatchDebugErrorNotification(message: String, fatal: Boolean = false) {
|
||||
fun Store<AppState>.dispatchDebugErrorNotification(message: String, fatal: Boolean = false) {
|
||||
val prefix = if (fatal) "FATAL ERROR: " else "DEBUG ERROR: "
|
||||
dispatchDebugErrorNotification(TapError.CustomError("$prefix $message"))
|
||||
}
|
||||
|
||||
fun Store<*>.dispatchDebugErrorNotification(error: TapError) {
|
||||
dispatchOnMain(GlobalAction.DebugShowErrorNotification(error))
|
||||
fun Store<AppState>.dispatchDebugErrorNotification(error: TapError) {
|
||||
inject(DaggerGraphState::uiMessageSender).send(SnackbarMessage(stringReference(error.message ?: "debug error")))
|
||||
}
|
||||
|
||||
fun Store<*>.dispatchDialogShow(dialog: StateDialog) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,11 @@
|
|||
package com.tangem.tap.common.images
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import coil.ImageLoader
|
||||
import coil.decode.GifDecoder
|
||||
import coil.decode.ImageDecoderDecoder
|
||||
import coil.memory.MemoryCache
|
||||
import coil.request.CachePolicy
|
||||
import coil.util.Logger
|
||||
|
|
@ -25,6 +28,15 @@ fun createCoilImageLoader(context: Context, logEnabled: Boolean = false): ImageL
|
|||
.build()
|
||||
}
|
||||
}
|
||||
.components {
|
||||
// According to Coil ImageDecoder API is faster and supports animated WebP and HEIF
|
||||
// https://coil-kt.github.io/coil/gifs/
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
add(ImageDecoderDecoder.Factory())
|
||||
} else {
|
||||
add(GifDecoder.Factory())
|
||||
}
|
||||
}
|
||||
.memoryCachePolicy(CachePolicy.ENABLED)
|
||||
.memoryCache {
|
||||
MemoryCache.Builder(context)
|
||||
|
|
|
|||
|
|
@ -2,9 +2,6 @@ package com.tangem.tap.common.redux
|
|||
|
||||
import com.tangem.common.extensions.VoidCallback
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.tap.common.TestAction
|
||||
import com.tangem.tap.domain.model.Currency
|
||||
import com.tangem.tap.domain.model.WalletAddressData
|
||||
import com.tangem.wallet.R
|
||||
|
||||
/**
|
||||
|
|
@ -18,15 +15,6 @@ sealed class AppDialog : StateDialog {
|
|||
val onOk: VoidCallback? = null,
|
||||
) : AppDialog()
|
||||
|
||||
internal data class AddressInfoDialog(
|
||||
val currency: Currency,
|
||||
val addressData: WalletAddressData,
|
||||
) : AppDialog()
|
||||
|
||||
data class TestActionsDialog(
|
||||
val actionsList: List<TestAction>,
|
||||
) : AppDialog()
|
||||
|
||||
data class RemoveWalletDialog(
|
||||
val currencyTitle: String,
|
||||
val onOk: () -> Unit,
|
||||
|
|
|
|||
|
|
@ -4,11 +4,6 @@ import com.tangem.tap.common.redux.global.globalReducer
|
|||
import com.tangem.tap.features.details.redux.DetailsReducer
|
||||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectReducer
|
||||
import com.tangem.tap.features.home.redux.HomeReducer
|
||||
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteReducer
|
||||
import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsReducer
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsReducer
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletReducer
|
||||
import com.tangem.tap.features.saveWallet.redux.SaveWalletReducer
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeReducer
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphReducer
|
||||
import org.rekotlin.Action
|
||||
|
|
@ -20,14 +15,9 @@ fun appReducer(action: Action, state: AppState?): AppState {
|
|||
return AppState(
|
||||
globalState = globalReducer(action, state),
|
||||
homeState = HomeReducer.reduce(action, state),
|
||||
onboardingNoteState = OnboardingNoteReducer.reduce(action, state),
|
||||
onboardingWalletState = OnboardingWalletReducer.reduce(action, state),
|
||||
onboardingOtherCardsState = OnboardingOtherCardsReducer.reduce(action, state),
|
||||
twinCardsState = TwinCardsReducer.reduce(action, state),
|
||||
detailsState = DetailsReducer.reduce(action, state),
|
||||
walletConnectState = WalletConnectReducer.reduce(action, state.walletConnectState),
|
||||
welcomeState = WelcomeReducer.reduce(action, state),
|
||||
saveWalletState = SaveWalletReducer.reduce(action, state),
|
||||
daggerGraphState = DaggerGraphReducer.reduce(action, state),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,17 +9,7 @@ import com.tangem.tap.features.details.redux.walletconnect.WalletConnectMiddlewa
|
|||
import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState
|
||||
import com.tangem.tap.features.home.redux.HomeMiddleware
|
||||
import com.tangem.tap.features.home.redux.HomeState
|
||||
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteMiddleware
|
||||
import com.tangem.tap.features.onboarding.products.note.redux.OnboardingNoteState
|
||||
import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsMiddleware
|
||||
import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOtherCardsState
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsMiddleware
|
||||
import com.tangem.tap.features.onboarding.products.twins.redux.TwinCardsState
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupMiddleware
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletMiddleware
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWalletState
|
||||
import com.tangem.tap.features.saveWallet.redux.SaveWalletMiddleware
|
||||
import com.tangem.tap.features.saveWallet.redux.SaveWalletState
|
||||
import com.tangem.tap.features.wallet.redux.middlewares.TradeCryptoMiddleware
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeMiddleware
|
||||
import com.tangem.tap.features.welcome.redux.WelcomeState
|
||||
|
|
@ -31,14 +21,9 @@ import org.rekotlin.StateType
|
|||
data class AppState(
|
||||
val globalState: GlobalState = GlobalState(),
|
||||
val homeState: HomeState = HomeState(),
|
||||
val onboardingNoteState: OnboardingNoteState = OnboardingNoteState(),
|
||||
val onboardingWalletState: OnboardingWalletState = OnboardingWalletState(),
|
||||
val onboardingOtherCardsState: OnboardingOtherCardsState = OnboardingOtherCardsState(),
|
||||
val twinCardsState: TwinCardsState = TwinCardsState(),
|
||||
val detailsState: DetailsState = DetailsState(),
|
||||
val walletConnectState: WalletConnectState = WalletConnectState(),
|
||||
val welcomeState: WelcomeState = WelcomeState(),
|
||||
val saveWalletState: SaveWalletState = SaveWalletState(),
|
||||
val daggerGraphState: DaggerGraphState = DaggerGraphState(),
|
||||
) : StateType {
|
||||
|
||||
|
|
@ -46,18 +31,12 @@ data class AppState(
|
|||
fun getMiddleware(): List<Middleware<AppState>> {
|
||||
return listOf(
|
||||
logMiddleware,
|
||||
notificationsMiddleware,
|
||||
GlobalMiddleware.handler,
|
||||
HomeMiddleware.handler,
|
||||
OnboardingNoteMiddleware.handler,
|
||||
OnboardingWalletMiddleware.handler,
|
||||
OnboardingOtherCardsMiddleware.handler,
|
||||
TwinCardsMiddleware.handler,
|
||||
DetailsMiddleware().detailsMiddleware,
|
||||
WalletConnectMiddleware().walletConnectMiddleware,
|
||||
BackupMiddleware().backupMiddleware,
|
||||
WelcomeMiddleware().middleware,
|
||||
SaveWalletMiddleware().middleware,
|
||||
LockUserWalletsTimerMiddleware().middleware,
|
||||
AccessCodeRequestPolicyMiddleware().middleware,
|
||||
DaggerGraphMiddleware.daggerGraphMiddleware,
|
||||
|
|
|
|||
|
|
@ -1,141 +0,0 @@
|
|||
package com.tangem.tap.common.redux
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.tangem.tap.common.extensions.getColor
|
||||
import com.tangem.tap.domain.ArgError
|
||||
import com.tangem.tap.domain.MultiMessageError
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.domain.assembleErrors
|
||||
import com.tangem.tap.notificationsHandler
|
||||
import com.tangem.wallet.BuildConfig
|
||||
import com.tangem.wallet.R
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.Middleware
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class NotificationsHandler(coordinatorLayout: CoordinatorLayout) {
|
||||
private val basicCoordinatorLayout = WeakReference(coordinatorLayout)
|
||||
private var baseLayout = basicCoordinatorLayout
|
||||
|
||||
private fun showNotification(message: String) {
|
||||
baseLayout.get()?.let { layout ->
|
||||
Snackbar.make(layout, message, Snackbar.LENGTH_LONG)
|
||||
.also { snackbar -> snackbar.show() }
|
||||
}
|
||||
}
|
||||
|
||||
private fun showDebugNotification(message: String) {
|
||||
baseLayout.get()?.let { layout ->
|
||||
Snackbar.make(layout, message, Snackbar.LENGTH_LONG)
|
||||
.also { snackbar ->
|
||||
snackbar.setBackgroundTint(layout.getColor(R.color.warning_warning))
|
||||
snackbar.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun showNotification(message: Int, args: List<Any>? = null) {
|
||||
baseLayout.get()?.let {
|
||||
showNotification(getMessageString(it.context, message, args))
|
||||
}
|
||||
}
|
||||
|
||||
fun showToastNotification(message: Int, args: List<Any>? = null) {
|
||||
baseLayout.get()?.let {
|
||||
Toast.makeText(it.context, getMessageString(it.context, message, args), Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
|
||||
fun showNotification(errorList: List<Pair<Int, List<Any>?>>, builder: (List<String>) -> String) {
|
||||
val context = baseLayout.get()?.context ?: return
|
||||
|
||||
val message = builder(errorList.map { getMessageString(context, it.first, it.second) })
|
||||
showNotification(message)
|
||||
}
|
||||
|
||||
fun showDebugErrorNotification(message: Int, args: List<Any>? = null) {
|
||||
baseLayout.get()?.let {
|
||||
showDebugNotification(getMessageString(it.context, message, args))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getMessageString(context: Context, message: Int, args: List<Any>?): String {
|
||||
return if (args.isNullOrEmpty()) {
|
||||
context.getString(message)
|
||||
} else {
|
||||
context.getString(message, *args.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
val notificationsMiddleware: Middleware<AppState> = { dispatch, state ->
|
||||
{ next ->
|
||||
{ action ->
|
||||
handleNotificationAction(action)
|
||||
next(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("ComplexMethod")
|
||||
private fun handleNotificationAction(action: Action) {
|
||||
if (action is Debug && !BuildConfig.DEBUG) return
|
||||
|
||||
when (action) {
|
||||
is NotificationAction -> {
|
||||
notificationsHandler?.showNotification(action.messageResource)
|
||||
}
|
||||
is ToastNotificationAction -> notificationsHandler?.showToastNotification(action.messageResource)
|
||||
is ErrorAction -> {
|
||||
when (action) {
|
||||
is Debug -> {
|
||||
val args = (action.error as? ArgError)?.args ?: listOf()
|
||||
when (action) {
|
||||
is DebugNotification -> {
|
||||
notificationsHandler?.showNotification(action.error.messageResource, args)
|
||||
}
|
||||
is DebugToastNotification -> {
|
||||
notificationsHandler?.showToastNotification(action.error.messageResource, args)
|
||||
}
|
||||
is DebugErrorAction -> {
|
||||
notificationsHandler?.showDebugErrorNotification(action.error.messageResource, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
when (action.error) {
|
||||
is MultiMessageError -> {
|
||||
val multiError = action.error as MultiMessageError
|
||||
notificationsHandler?.showNotification(multiError.assembleErrors(), multiError.builder)
|
||||
}
|
||||
else -> {
|
||||
val args = (action.error as? ArgError)?.args ?: listOf()
|
||||
notificationsHandler?.showNotification(action.error.messageResource, args)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface ToastNotificationAction : Action {
|
||||
val messageResource: Int
|
||||
}
|
||||
|
||||
interface NotificationAction : Action {
|
||||
val messageResource: Int
|
||||
}
|
||||
|
||||
interface ErrorAction : Action {
|
||||
val error: TapError
|
||||
}
|
||||
|
||||
// Processed only in the debug builds
|
||||
interface Debug
|
||||
interface DebugNotification : Debug, NotificationAction
|
||||
interface DebugToastNotification : Debug, ToastNotificationAction
|
||||
interface DebugErrorAction : Debug, ErrorAction
|
||||
|
|
@ -5,45 +5,14 @@ import com.tangem.core.analytics.models.AnalyticsParam
|
|||
import com.tangem.domain.appcurrency.model.AppCurrency
|
||||
import com.tangem.domain.models.scan.ScanResponse
|
||||
import com.tangem.domain.redux.StateDialog
|
||||
import com.tangem.tap.common.redux.DebugErrorAction
|
||||
import com.tangem.tap.common.redux.ErrorAction
|
||||
import com.tangem.tap.domain.TapError
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupStartedSource
|
||||
import org.rekotlin.Action
|
||||
|
||||
sealed class GlobalAction : Action {
|
||||
|
||||
// notifications
|
||||
data class ShowErrorNotification(override val error: TapError) : GlobalAction(), ErrorAction
|
||||
data class DebugShowErrorNotification(override val error: TapError) : GlobalAction(), DebugErrorAction
|
||||
|
||||
// dialogs
|
||||
data class ShowDialog(val stateDialog: StateDialog) : GlobalAction()
|
||||
object HideDialog : GlobalAction()
|
||||
|
||||
sealed class Onboarding : GlobalAction() {
|
||||
/**
|
||||
* Initiate an onboarding process.
|
||||
* For resuming unfinished backup of standard Wallet see
|
||||
* BackupAction.CheckForUnfinishedBackup, GlobalAction.Onboarding.StartForUnfinishedBackup
|
||||
*/
|
||||
data class Start(
|
||||
val scanResponse: ScanResponse,
|
||||
val source: BackupStartedSource,
|
||||
val canSkipBackup: Boolean = true,
|
||||
) : Onboarding()
|
||||
|
||||
/**
|
||||
* Initiate resuming of unfinished backup for standard Wallet.
|
||||
* See more BackupAction.CheckForUnfinishedBackup
|
||||
*/
|
||||
data class StartForUnfinishedBackup(val addedBackupCardsCount: Int) : Onboarding()
|
||||
|
||||
object Stop : Onboarding()
|
||||
|
||||
data class ShouldResetCardOnCreate(val shouldReset: Boolean) : Onboarding()
|
||||
}
|
||||
|
||||
object ScanFailsCounter {
|
||||
data class ChooseBehavior(
|
||||
val result: CompletionResult<ScanResponse>,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package com.tangem.tap.common.redux.global
|
||||
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.features.onboarding.OnboardingManager
|
||||
import com.tangem.utils.extensions.replaceBy
|
||||
import org.rekotlin.Action
|
||||
|
||||
|
|
@ -12,21 +11,6 @@ fun globalReducer(action: Action, state: AppState): GlobalState {
|
|||
val globalState = state.globalState
|
||||
|
||||
return when (action) {
|
||||
is GlobalAction.Onboarding.Start -> {
|
||||
val onboardingManager = OnboardingManager(action.scanResponse)
|
||||
globalState.copy(onboardingState = OnboardingState(true, onboardingManager))
|
||||
}
|
||||
is GlobalAction.Onboarding.StartForUnfinishedBackup -> {
|
||||
globalState.copy(onboardingState = OnboardingState(true, null))
|
||||
}
|
||||
is GlobalAction.Onboarding.Stop -> {
|
||||
globalState.copy(onboardingState = OnboardingState(false))
|
||||
}
|
||||
is GlobalAction.Onboarding.ShouldResetCardOnCreate -> {
|
||||
globalState.copy(
|
||||
onboardingState = globalState.onboardingState.copy(shouldResetOnCreate = action.shouldReset),
|
||||
)
|
||||
}
|
||||
is GlobalAction.ScanFailsCounter.Increment -> {
|
||||
globalState.copy(scanCardFailsCounter = globalState.scanCardFailsCounter + 1)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,8 @@ import com.tangem.domain.redux.LegacyAction
|
|||
import com.tangem.tap.common.extensions.dispatchWithMain
|
||||
import com.tangem.tap.common.extensions.inject
|
||||
import com.tangem.tap.common.redux.AppState
|
||||
import com.tangem.tap.common.redux.global.GlobalAction
|
||||
import com.tangem.tap.features.details.redux.AppSettingsState
|
||||
import com.tangem.tap.features.details.redux.DetailsAction
|
||||
import com.tangem.tap.features.onboarding.products.wallet.redux.BackupStartedSource
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.scope
|
||||
import com.tangem.tap.store
|
||||
|
|
@ -26,15 +24,6 @@ internal object LegacyMiddleware {
|
|||
{ next ->
|
||||
{ action ->
|
||||
when (action) {
|
||||
is LegacyAction.StartOnboardingProcess -> {
|
||||
store.dispatch(
|
||||
GlobalAction.Onboarding.Start(
|
||||
scanResponse = action.scanResponse,
|
||||
source = BackupStartedSource.CreateBackup,
|
||||
canSkipBackup = action.canSkipBackup,
|
||||
),
|
||||
)
|
||||
}
|
||||
is LegacyAction.PrepareDetailsScreen -> {
|
||||
val userWalletsListManager = store.inject(DaggerGraphState::generalUserWalletsListManager)
|
||||
val walletsRepository = store.inject(DaggerGraphState::walletsRepository)
|
||||
|
|
|
|||
|
|
@ -1,118 +0,0 @@
|
|||
package com.tangem.tap.common.toggleWidget
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.*
|
||||
import android.widget.ViewSwitcher
|
||||
import com.tangem.tap.common.entities.ProgressState
|
||||
import com.tangem.wallet.R
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Suppress("MagicNumber")
|
||||
class RefreshBalanceWidget(root: ViewGroup) : ViewStateWidget {
|
||||
|
||||
var isShowing: Boolean? = null
|
||||
private set
|
||||
|
||||
override val mainView: View = root.findViewById(R.id.btn_refresh_balance)
|
||||
|
||||
private val viewSwitcher: ViewSwitcher by lazy { mainView.findViewById(R.id.switcher_refresh_arrow_to_progress) }
|
||||
|
||||
private val arrowView = mainView.findViewById<View>(R.id.imv_arrow_refresh)
|
||||
|
||||
private var progressViewAnimation: Animation? = null
|
||||
|
||||
init {
|
||||
viewSwitcher.inAnimation = AlphaAnimation(0f, 1f).apply {
|
||||
duration = 400
|
||||
interpolator = LinearInterpolator()
|
||||
}
|
||||
viewSwitcher.outAnimation = AlphaAnimation(1f, 0f).apply {
|
||||
duration = 400
|
||||
interpolator = LinearInterpolator()
|
||||
}
|
||||
}
|
||||
|
||||
override fun changeState(state: WidgetState) {
|
||||
val progressState = state as? ProgressState ?: return
|
||||
|
||||
val currentStateByView = getState()
|
||||
when {
|
||||
currentStateByView == progressState -> return
|
||||
currentStateByView == ProgressState.Done -> if (progressState == ProgressState.Error) return
|
||||
}
|
||||
|
||||
mainView.isClickable = currentStateByView == ProgressState.Done || currentStateByView == ProgressState.Error
|
||||
animateState(progressState)
|
||||
viewSwitcher.showNext()
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
private fun animateState(state: ProgressState) {
|
||||
when (state) {
|
||||
ProgressState.Done, ProgressState.Error -> {
|
||||
progressViewAnimation?.cancel()
|
||||
progressViewAnimation = null
|
||||
}
|
||||
ProgressState.Loading -> {
|
||||
progressViewAnimation = RotateAnimation(
|
||||
/* fromDegrees = */
|
||||
0f,
|
||||
/* toDegrees = */
|
||||
360f,
|
||||
/* pivotXType = */
|
||||
Animation.RELATIVE_TO_SELF,
|
||||
/* pivotXValue = */
|
||||
0.5f,
|
||||
/* pivotYType = */
|
||||
Animation.RELATIVE_TO_SELF,
|
||||
/* pivotYValue = */
|
||||
0.5f,
|
||||
)
|
||||
progressViewAnimation?.duration = 700
|
||||
progressViewAnimation?.interpolator = AccelerateInterpolator()
|
||||
progressViewAnimation?.repeatCount = -1
|
||||
progressViewAnimation?.let { arrowView.startAnimation(it) }
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
fun show(show: Boolean) {
|
||||
if (show == isShowing) return
|
||||
|
||||
isShowing = show
|
||||
if (show) {
|
||||
mainView.startAnimation(ShowAnimation())
|
||||
} else {
|
||||
mainView.startAnimation(AlphaAnimation(1f, 0f).apply { fillAfter = true })
|
||||
}
|
||||
}
|
||||
|
||||
private fun isArrowRefreshActive(): Boolean = viewSwitcher.currentView.id == R.id.fl_arrow_refresh_container
|
||||
|
||||
private fun getState(): ProgressState = if (isArrowRefreshActive()) ProgressState.Done else ProgressState.Loading
|
||||
}
|
||||
|
||||
@Suppress("MagicNumber")
|
||||
class ShowAnimation : AnimationSet(true) {
|
||||
init {
|
||||
addAnimation(
|
||||
ScaleAnimation(
|
||||
0f,
|
||||
1f,
|
||||
0f,
|
||||
1f,
|
||||
Animation.RELATIVE_TO_SELF,
|
||||
0.5f,
|
||||
Animation.RELATIVE_TO_SELF,
|
||||
0.5f,
|
||||
),
|
||||
)
|
||||
addAnimation(AlphaAnimation(0f, 1f))
|
||||
duration = 300
|
||||
interpolator = AnticipateOvershootInterpolator()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
package com.tangem.tap.common.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.widget.Toast
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
import com.tangem.core.analytics.Analytics
|
||||
import com.tangem.tap.common.analytics.events.Token
|
||||
import com.tangem.tap.common.extensions.*
|
||||
import com.tangem.tap.common.redux.AppDialog
|
||||
import com.tangem.tap.domain.model.WalletAddressData
|
||||
import com.tangem.tap.proxy.redux.DaggerGraphState
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.DialogOnboardingAddressInfoBinding
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
internal class AddressInfoBottomSheetDialog(
|
||||
private val stateDialog: AppDialog.AddressInfoDialog,
|
||||
context: Context,
|
||||
) : BottomSheetDialog(context) {
|
||||
|
||||
var binding: DialogOnboardingAddressInfoBinding? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = DialogOnboardingAddressInfoBinding
|
||||
.inflate(LayoutInflater.from(context))
|
||||
setContentView(binding!!.root)
|
||||
behavior.state = BottomSheetBehavior.STATE_EXPANDED
|
||||
setOnCancelListener {
|
||||
store.dispatchDialogHide()
|
||||
binding = null
|
||||
}
|
||||
}
|
||||
|
||||
override fun show() {
|
||||
super.show()
|
||||
Analytics.send(Token.Receive.ScreenOpened(stateDialog.currency.currencySymbol))
|
||||
showData(data = stateDialog.addressData)
|
||||
}
|
||||
|
||||
private fun showData(data: WalletAddressData) = with(binding!!) {
|
||||
pseudoToolbar.imvClose.setOnClickListener {
|
||||
dismissWithAnimation = true
|
||||
cancel()
|
||||
}
|
||||
imvQrCode.setImageBitmap(data.shareUrl.toQrCode())
|
||||
tvAddress.text = data.address
|
||||
btnFlCopyAddress.setOnClickListener {
|
||||
Analytics.send(Token.Receive.ButtonCopyAddress())
|
||||
|
||||
Toast
|
||||
.makeText(context, R.string.wallet_notification_address_copied, Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
|
||||
store.inject(DaggerGraphState::clipboardManager).setText(text = data.address, isSensitive = true)
|
||||
}
|
||||
btnFlShare.setOnClickListener {
|
||||
Analytics.send(Token.Receive.ButtonShareAddress())
|
||||
store.dispatchShare(data.shareUrl)
|
||||
}
|
||||
val blockchain = stateDialog.currency.blockchain
|
||||
tvReceiveMessage.text = tvReceiveMessage.getString(
|
||||
id = R.string.address_qr_code_message_format,
|
||||
blockchain.getCoinName(),
|
||||
blockchain.currency,
|
||||
blockchain.fullName,
|
||||
)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue