diff --git a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt index 967026bf4c..5c04f34268 100644 --- a/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt +++ b/app/src/main/java/com/tangem/tap/ApplicationEntryPoint.kt @@ -32,7 +32,6 @@ import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase import com.tangem.features.details.DetailsFeatureToggles import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles -import com.tangem.features.send.api.featuretoggles.SendFeatureToggles import com.tangem.tap.domain.walletconnect2.domain.WalletConnectSessionsRepository import com.tangem.tap.features.customtoken.api.featuretoggles.CustomTokenFeatureToggles import com.tangem.tap.proxy.AppStateHolder @@ -82,8 +81,6 @@ interface ApplicationEntryPoint { fun getWalletsRepository(): WalletsRepository - fun getSendFeatureToggles(): SendFeatureToggles - fun getOneTimeEventFilter(): OneTimeEventFilter fun getGeneralUserWalletsListManager(): UserWalletsListManager diff --git a/app/src/main/java/com/tangem/tap/TangemApplication.kt b/app/src/main/java/com/tangem/tap/TangemApplication.kt index eaca995355..4e62a55ab3 100644 --- a/app/src/main/java/com/tangem/tap/TangemApplication.kt +++ b/app/src/main/java/com/tangem/tap/TangemApplication.kt @@ -47,7 +47,6 @@ import com.tangem.domain.wallets.repository.WalletsRepository import com.tangem.domain.wallets.usecase.GenerateWalletNameUseCase import com.tangem.features.details.DetailsFeatureToggles import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles -import com.tangem.features.send.api.featuretoggles.SendFeatureToggles import com.tangem.tap.common.analytics.AnalyticsFactory import com.tangem.tap.common.analytics.api.AnalyticsHandlerBuilder import com.tangem.tap.common.analytics.handlers.amplitude.AmplitudeAnalyticsHandler @@ -140,9 +139,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { private val walletsRepository: WalletsRepository get() = entryPoint.getWalletsRepository() - private val sendFeatureToggles: SendFeatureToggles - get() = entryPoint.getSendFeatureToggles() - private val oneTimeEventFilter: OneTimeEventFilter get() = entryPoint.getOneTimeEventFilter() @@ -271,7 +267,6 @@ abstract class TangemApplication : Application(), ImageLoaderFactory { appThemeModeRepository = appThemeModeRepository, balanceHidingRepository = balanceHidingRepository, walletsRepository = walletsRepository, - sendFeatureToggles = sendFeatureToggles, generalUserWalletsListManager = generalUserWalletsListManager, wasTwinsOnboardingShownUseCase = wasTwinsOnboardingShownUseCase, saveTwinsOnboardingShownUseCase = saveTwinsOnboardingShownUseCase, diff --git a/app/src/main/java/com/tangem/tap/common/CurrencyConverter.kt b/app/src/main/java/com/tangem/tap/common/CurrencyConverter.kt deleted file mode 100644 index d30c440661..0000000000 --- a/app/src/main/java/com/tangem/tap/common/CurrencyConverter.kt +++ /dev/null @@ -1,36 +0,0 @@ -package com.tangem.tap.common - -import com.tangem.common.extensions.isZero -import com.tangem.tap.common.extensions.scaleToFiat -import java.math.BigDecimal -import java.math.RoundingMode - -/** -[REDACTED_AUTHOR] - */ -class CurrencyConverter( - private val rateValue: BigDecimal, - private val decimals: Int, -) { - private val roundingMode = RoundingMode.HALF_UP - - fun toFiat(crypto: BigDecimal, fiatDecimals: Int = 2): BigDecimal { - return toFiatUnscaled(crypto).setScale(fiatDecimals, roundingMode) - } - - fun toFiatUnscaled(crypto: BigDecimal): BigDecimal { - return rateValue.multiply(crypto).setScale(decimals, roundingMode) - } - - fun toFiatWithPrecision(crypto: BigDecimal): BigDecimal { - return toFiatUnscaled(crypto).scaleToFiat(true) - } - - fun toCrypto(fiat: BigDecimal): BigDecimal { - if (fiat.isZero()) return fiat - - val fiatValue = fiat.setScale(rateValue.scale(), RoundingMode.UP) - val cryptoValue = fiatValue.divide(rateValue, RoundingMode.UP) - return cryptoValue.setScale(decimals, roundingMode) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/GlobalLayoutStateHandler.kt b/app/src/main/java/com/tangem/tap/common/GlobalLayoutStateHandler.kt deleted file mode 100644 index 0a51404cf9..0000000000 --- a/app/src/main/java/com/tangem/tap/common/GlobalLayoutStateHandler.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.tangem.tap.common - -import android.view.View -import android.view.ViewTreeObserver -import timber.log.Timber - -/** -[REDACTED_AUTHOR] - */ -class GlobalLayoutStateHandler( - private val view: T, - attachImmediately: Boolean = true, -) : ViewTreeObserver.OnGlobalLayoutListener { - - var onStateChanged: ((T) -> Unit)? = null - - private var isAttached: Boolean = false - - init { - if (attachImmediately) attach() - } - - private fun attach() { - if (isAttached) { - Timber.d("Already attached") - return - } - - isAttached = true - view.viewTreeObserver.addOnGlobalLayoutListener(this) - } - - fun detach() { - view.viewTreeObserver.removeOnGlobalLayoutListener(this) - isAttached = false - } - - override fun onGlobalLayout() { - onStateChanged?.invoke(view) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/KeyboardObserver.kt b/app/src/main/java/com/tangem/tap/common/KeyboardObserver.kt deleted file mode 100644 index 47f579ca1b..0000000000 --- a/app/src/main/java/com/tangem/tap/common/KeyboardObserver.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.tangem.tap.common - -import android.app.Activity -import android.graphics.Rect -import android.util.DisplayMetrics -import android.view.ViewTreeObserver.OnGlobalLayoutListener -import kotlin.math.absoluteValue - -class KeyboardObserver(activity: Activity) { - - private val decorView = activity.window.decorView - private val windowManager = activity.windowManager - private val originalWindowHeight: Int = getWindowHeight() - private val onGlobalLayoutListener: OnGlobalLayoutListener = OnGlobalLayoutListener { onGlobalLayout() } - - private var onKeyboardListener: ((Boolean) -> Unit)? = null - private var lastIsShow = false - private var lastWindowHeight = getWindowHeight() - - fun registerListener(listener: (Boolean) -> Unit) { - decorView.viewTreeObserver.addOnGlobalLayoutListener(onGlobalLayoutListener) - onKeyboardListener = listener - } - - fun unregisterListener() { - decorView.viewTreeObserver.removeOnGlobalLayoutListener(onGlobalLayoutListener) - onKeyboardListener = null - } - - private fun getWindowHeight() = Rect().apply { decorView.getWindowVisibleDisplayFrame(this) }.bottom - - private fun onGlobalLayout() { - val currentWindowHeight = getWindowHeight() - if (isSoftKeyChanged()) { - lastWindowHeight = currentWindowHeight - return - } - - lastWindowHeight = currentWindowHeight - val isShow = originalWindowHeight != currentWindowHeight - if (lastIsShow == isShow) return - - lastIsShow = isShow - onKeyboardListener?.invoke(isShow) - } - - private fun isSoftKeyChanged() = (lastWindowHeight - getWindowHeight()).absoluteValue == getSoftKeyButtonHeight() - - private fun getSoftKeyButtonHeight(): Int { - val applicationDisplayHeight = DisplayMetrics().apply { - windowManager.defaultDisplay.getMetrics(this) - }.heightPixels - - val realDisplayHeight = DisplayMetrics().apply { - windowManager.defaultDisplay.getRealMetrics(this) - }.heightPixels - - return realDisplayHeight - applicationDisplayHeight - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt b/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt index 3b5ae220eb..56c286492d 100644 --- a/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt +++ b/app/src/main/java/com/tangem/tap/common/analytics/events/Token.kt @@ -22,44 +22,6 @@ sealed class Token( class ButtonShareAddress : Receive("Button - Share Address") } - sealed class Send( - event: String, - params: Map = mapOf(), - error: Throwable? = null, - ) : Token("Token / Send", event, params, error) { - - class ScreenOpened : Send(event = "Send Screen Opened") - class ButtonPaste : Send(event = "Button - Paste") - class ButtonQRCode : Send(event = "Button - QR Code") - class ButtonSwapCurrency : Send(event = "Button - Swap Currency") - - class AddressEntered(sourceType: SourceType, validationResult: ValidationResult) : Send( - event = "Address Entered", - params = mapOf( - "Source" to sourceType.name, - "Validation" to validationResult.name, - ), - ) { - enum class SourceType { - QRCode, PasteButton, PastePopup - } - - enum class ValidationResult { - Success, Fail - } - } - - class SelectedCurrency(currency: CurrencyType) : Send( - event = "Selected Currency", - params = mapOf("Type" to currency.value), - ) { - - enum class CurrencyType(val value: String) { - Token(value = "Token"), AppCurrency(value = "App Currency") - } - } - } - sealed class Topup( event: String, params: Map = mapOf(), diff --git a/app/src/main/java/com/tangem/tap/common/entities/Button.kt b/app/src/main/java/com/tangem/tap/common/entities/Button.kt deleted file mode 100644 index b0aca7f994..0000000000 --- a/app/src/main/java/com/tangem/tap/common/entities/Button.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.tangem.tap.common.entities - -import com.tangem.tap.features.send.redux.states.ButtonState - -open class Button(val enabled: Boolean) - -open class IndeterminateProgressButton( - val state: ButtonState, -) : Button(state != ButtonState.DISABLED) { - - val progressState: ProgressState - get() = when (state) { - ButtonState.PROGRESS -> ProgressState.Loading - else -> ProgressState.Done - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Bitmap.kt b/app/src/main/java/com/tangem/tap/common/extensions/Bitmap.kt deleted file mode 100644 index 76eea0c892..0000000000 --- a/app/src/main/java/com/tangem/tap/common/extensions/Bitmap.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.tangem.tap.common.extensions - -import android.graphics.Bitmap -import java.io.ByteArrayOutputStream - -@Suppress("MagicNumber") -fun Bitmap.toByteArray(): ByteArray { - val stream = ByteArrayOutputStream() - this.compress(Bitmap.CompressFormat.JPEG, 20, stream) - return stream.toByteArray() -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt index a2206cb3bc..773bca1bd5 100644 --- a/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt +++ b/app/src/main/java/com/tangem/tap/common/extensions/Specific.kt @@ -1,6 +1,5 @@ package com.tangem.tap.common.extensions -import com.tangem.common.extensions.isZero import java.math.BigDecimal import java.math.RoundingMode import java.text.DecimalFormat @@ -25,20 +24,4 @@ fun BigDecimal.toFormattedString( fun BigDecimal.stripZeroPlainString(): String = this.stripTrailingZeros().toPlainString() -// 0.00 -> 0.00 -// 0.00002345 -> 0.00002 -// 1.00002345 -> 1.00 -// 1.45002345 -> 1.45 -fun BigDecimal.scaleToFiat(applyPrecision: Boolean = false): BigDecimal { - if (this.isZero()) return this - - val scaledFiat = this.setScale(2, RoundingMode.DOWN) - return if (scaledFiat.isZero() && applyPrecision) this.setPrecision(1) else scaledFiat -} - -fun BigDecimal.setPrecision(precision: Int, roundingMode: RoundingMode = RoundingMode.DOWN): BigDecimal { - if (precision == precision() || scale() <= precision) return this - return this.setScale(scale() - precision() + precision, roundingMode) -} - fun BigDecimal.isPositive(): Boolean = this.signum() == 1 \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/extensions/WebView.kt b/app/src/main/java/com/tangem/tap/common/extensions/WebView.kt deleted file mode 100644 index 1afe53b3ba..0000000000 --- a/app/src/main/java/com/tangem/tap/common/extensions/WebView.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.tangem.tap.common.extensions - -import android.webkit.WebView - -fun WebView.stop() { - stopLoading() - pauseTimers() -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/TestLeapfrogFragment.kt b/app/src/main/java/com/tangem/tap/common/leapfrogWidget/TestLeapfrogFragment.kt deleted file mode 100644 index 8253e64b37..0000000000 --- a/app/src/main/java/com/tangem/tap/common/leapfrogWidget/TestLeapfrogFragment.kt +++ /dev/null @@ -1,72 +0,0 @@ -package com.tangem.tap.common.leapfrogWidget - -import android.os.Bundle -import android.view.View -import android.widget.FrameLayout -import androidx.fragment.app.Fragment -import androidx.transition.TransitionInflater -import by.kirich1409.viewbindingdelegate.viewBinding -import com.tangem.sdk.ui.widget.leapfrogWidget.LeapfrogWidget -import com.tangem.sdk.ui.widget.leapfrogWidget.LeapfrogWidgetState -import com.tangem.tap.domain.twins.TwinsCardWidget -import com.tangem.wallet.R -import com.tangem.wallet.databinding.TestLeapfrogFragmentBinding - -class TestLeapfrogFragment : Fragment(R.layout.test_leapfrog_fragment) { - - private lateinit var twinsCardWidget: TwinsCardWidget - private val binding: TestLeapfrogFragmentBinding by viewBinding(TestLeapfrogFragmentBinding::bind) - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - val inflater = TransitionInflater.from(requireContext()) - exitTransition = inflater.inflateTransition(R.transition.fade) - } - - @Suppress("MagicNumber") - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - val leapfrogContainer: FrameLayout = view.findViewById(R.id.leapfrog_views_container) - val leapfrog = LeapfrogWidget(leapfrogContainer) - twinsCardWidget = TwinsCardWidget(leapfrog) { 200f } - - binding.btnTwinWelcome.setOnClickListener { - twinsCardWidget.toWelcome() - } - binding.btnTwinToLeapfrog.setOnClickListener { - twinsCardWidget.toLeapfrog() - } - binding.btnTwinActivate.setOnClickListener { - twinsCardWidget.toActivate() - } - - binding.btnLpInit.setOnClickListener { - leapfrog.initViews() - } - binding.btnLpUnfold.setOnClickListener { - leapfrog.unfold() - } - binding.btnLpFold.setOnClickListener { - leapfrog.fold() - } - binding.btnLpLeap.setOnClickListener { - leapfrog.leap() - } - binding.btnLpLeapBack.setOnClickListener { - leapfrog.leapBack() - } - } - - override fun onStart() { - super.onStart() - leapfrogWidgetState?.let { twinsCardWidget.leapfrogWidget.applyState(it) } - } - - override fun onStop() { - super.onStop() - leapfrogWidgetState = twinsCardWidget.leapfrogWidget.getState() - } -} - -private var leapfrogWidgetState: LeapfrogWidgetState? = null \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/recyclerView/SpaceItemDecoration.kt b/app/src/main/java/com/tangem/tap/common/recyclerView/SpaceItemDecoration.kt deleted file mode 100644 index 4ea08ea19e..0000000000 --- a/app/src/main/java/com/tangem/tap/common/recyclerView/SpaceItemDecoration.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.tangem.tap.common.recyclerView - -import android.graphics.Rect -import android.view.View -import androidx.recyclerview.widget.RecyclerView -import com.tangem.sdk.extensions.dpToPx - -class SpaceItemDecoration( - private val horizontalSpaceDp: Float, - private val verticalSpaceDp: Float, -) : RecyclerView.ItemDecoration() { - - private lateinit var space: Space - - override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) { - if (state.itemCount == 0) return - if (!::space.isInitialized) { - space = Space( - view.dpToPx(horizontalSpaceDp).toInt(), - view.dpToPx(verticalSpaceDp).toInt(), - ) - } - - outRect.left = space.horizontal - outRect.right = space.horizontal - - when (state.itemCount) { - 1 -> { - outRect.top = space.vertical - outRect.bottom = space.vertical - } - else -> { - val adapterPosition = parent.getChildAdapterPosition(view) - if (adapterPosition == -1) return - - when (adapterPosition) { - 0 -> { - // first - outRect.top = space.vertical - outRect.bottom = space.vertical / 2 - } - state.itemCount - 1 -> { - // last - outRect.top = space.vertical / 2 - outRect.bottom = space.vertical - } - else -> { - // middle - outRect.top = space.vertical / 2 - outRect.bottom = space.vertical / 2 - } - } - } - } - } - - private data class Space( - val horizontal: Int, - val vertical: Int, - ) - - companion object { - fun all(dp: Float): SpaceItemDecoration = SpaceItemDecoration(dp, dp) - fun vertical(dp: Float): SpaceItemDecoration = SpaceItemDecoration(0f, dp) - fun horizontal(dp: Float): SpaceItemDecoration = SpaceItemDecoration(dp, 0f) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt index d047620b5e..cdc2bed475 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppReducer.kt @@ -10,7 +10,6 @@ import com.tangem.tap.features.onboarding.products.otherCards.redux.OnboardingOt 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.send.redux.reducers.SendScreenReducer import com.tangem.tap.features.tokens.legacy.redux.TokensReducer import com.tangem.tap.features.welcome.redux.WelcomeReducer import com.tangem.tap.proxy.AppStateHolder @@ -28,7 +27,6 @@ fun appReducer(action: Action, state: AppState?, appStateHolder: AppStateHolder) onboardingWalletState = OnboardingWalletReducer.reduce(action, state), onboardingOtherCardsState = OnboardingOtherCardsReducer.reduce(action, state), twinCardsState = TwinCardsReducer.reduce(action, state), - sendState = SendScreenReducer.reduce(action, state.sendState), detailsState = DetailsReducer.reduce(action, state), disclaimerState = DisclaimerReducer.reduce(action, state), tokensState = TokensReducer.reduce(action, state), diff --git a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt index 2a83cbf4df..bdcb0bd1c1 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/AppState.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/AppState.kt @@ -25,8 +25,6 @@ import com.tangem.tap.features.onboarding.products.wallet.redux.OnboardingWallet 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.send.redux.middlewares.SendMiddleware -import com.tangem.tap.features.send.redux.states.SendState import com.tangem.tap.features.tokens.legacy.redux.TokensState import com.tangem.tap.features.wallet.redux.middlewares.TradeCryptoMiddleware import com.tangem.tap.features.welcome.redux.WelcomeMiddleware @@ -44,7 +42,6 @@ data class AppState( val onboardingWalletState: OnboardingWalletState = OnboardingWalletState(), val onboardingOtherCardsState: OnboardingOtherCardsState = OnboardingOtherCardsState(), val twinCardsState: TwinCardsState = TwinCardsState(), - val sendState: SendState = SendState(), val detailsState: DetailsState = DetailsState(), val disclaimerState: DisclaimerState = DisclaimerState(), val tokensState: TokensState = TokensState(), @@ -77,7 +74,6 @@ data class AppState( OnboardingWalletMiddleware.handler, OnboardingOtherCardsMiddleware.handler, TwinCardsMiddleware.handler, - SendMiddleware().sendMiddleware, DetailsMiddleware().detailsMiddleware, DisclaimerMiddleware().disclaimerMiddleware, WalletConnectMiddleware().walletConnectMiddleware, diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt index 58d13a6d79..614b69566d 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalAction.kt @@ -12,7 +12,6 @@ import com.tangem.tap.common.redux.DebugErrorAction import com.tangem.tap.common.redux.ErrorAction import com.tangem.tap.common.redux.NotificationAction import com.tangem.tap.domain.TapError -import com.tangem.tap.domain.configurable.warningMessage.WarningMessage import com.tangem.tap.domain.configurable.warningMessage.WarningMessagesManager import org.rekotlin.Action @@ -71,8 +70,6 @@ sealed class GlobalAction : Action { val walletPublicKey: ByteArray, ) : GlobalAction() - data class HideWarningMessage(val warning: WarningMessage) : GlobalAction() - data class SetConfigManager(val configManager: ConfigManager) : GlobalAction() data class SetWarningManager(val warningManager: WarningMessagesManager) : GlobalAction() data class SetFeedbackManager(val feedbackManager: LegacyFeedbackManager) : GlobalAction() diff --git a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt index 9c6882deda..43d85d7fc5 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/global/GlobalMiddleware.kt @@ -12,7 +12,6 @@ import com.tangem.domain.models.scan.ScanResponse import com.tangem.domain.redux.StateDialog import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppState -import com.tangem.tap.features.send.redux.SendAction import com.tangem.tap.network.exchangeServices.BuyExchangeService import com.tangem.tap.network.exchangeServices.CardExchangeRules import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager @@ -56,18 +55,6 @@ private fun handleAction(action: Action, appState: () -> AppState?) { is GlobalAction.RestoreAppCurrency -> { restoreAppCurrency() } - is GlobalAction.HideWarningMessage -> { - store.state.globalState.warningManager?.let { - if (it.hideWarning(action.warning)) { - // if (WarningMessagesManager.isAlreadySignedHashesWarning()) { - // // TODO: No appropriate warningMessage identification. Make it better later - // store.dispatch(WalletAction.Warnings.CheckHashesCount.SaveCardId) - // } - - store.dispatch(SendAction.Warnings.Update) - } - } - } is GlobalAction.SendEmail -> { store.state.globalState.feedbackManager?.sendEmail( feedbackData = action.feedbackData, diff --git a/app/src/main/java/com/tangem/tap/common/snackBar/MaxAmountSnackbar.kt b/app/src/main/java/com/tangem/tap/common/snackBar/MaxAmountSnackbar.kt deleted file mode 100644 index 939fc17c61..0000000000 --- a/app/src/main/java/com/tangem/tap/common/snackBar/MaxAmountSnackbar.kt +++ /dev/null @@ -1,97 +0,0 @@ -package com.tangem.tap.common.snackBar - -import android.content.Context -import android.util.AttributeSet -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.FrameLayout -import androidx.constraintlayout.widget.ConstraintLayout -import androidx.coordinatorlayout.widget.CoordinatorLayout -import androidx.core.view.ViewCompat -import androidx.core.view.WindowInsetsCompat -import androidx.core.view.updateLayoutParams -import com.google.android.material.snackbar.BaseTransientBottomBar -import com.google.android.material.snackbar.ContentViewCallback -import com.google.android.material.snackbar.Snackbar -import com.tangem.sdk.extensions.dpToPx -import com.tangem.wallet.R - -/** -[REDACTED_AUTHOR] - */ -class MaxAmountSnackbar( - parent: ViewGroup, - content: MaxAmountSnackbarView, -) : BaseTransientBottomBar(parent, content, content) { - - companion object { - - fun make(view: View, onClick: () -> Unit): MaxAmountSnackbar { - val parent = view.findSuitableParent() ?: throw IllegalArgumentException( - "No suitable parent found from the given view. Please provide a valid view.", - ) - val inflater = LayoutInflater.from(parent.context) - val customView = inflater.inflate(R.layout.view_snackbar_max_amount, parent, false) as MaxAmountSnackbarView - customView.setOnClickListener { onClick() } - - return MaxAmountSnackbar(parent, customView).apply { - updateBottomMargin() - duration = Snackbar.LENGTH_INDEFINITE - } - } - - private fun MaxAmountSnackbar.updateBottomMargin() { - ViewCompat.setOnApplyWindowInsetsListener(this.view) { _, insets -> - val imeInsets = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom - val bottomInsets = insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom - - this.view.updateLayoutParams { - bottomMargin = imeInsets - bottomInsets + context.dpToPx(dp = 8f).toInt() - } - - insets - } - } - - private fun View?.findSuitableParent(): ViewGroup? { - var view = this - var fallback: ViewGroup? = null - do { - if (view is CoordinatorLayout) { - return view - } else if (view is FrameLayout) { - if (view.id == android.R.id.content) { - return view - } else { - fallback = view - } - } - - if (view != null) { - val parent = view.parent - view = if (parent is View) parent else null - } - } while (view != null) - return fallback - } - } -} - -class MaxAmountSnackbarView @JvmOverloads constructor( - context: Context, - attrs: AttributeSet? = null, - defStyleAttr: Int = 0, -) : ConstraintLayout(context, attrs, defStyleAttr), ContentViewCallback { - - init { - View.inflate(context, R.layout.view_snackbar_max_amount_content, this) - clipToPadding = false - } - - override fun animateContentIn(delay: Int, duration: Int) { - } - - override fun animateContentOut(delay: Int, duration: Int) { - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/text/DecimalDigitsInputFilter.kt b/app/src/main/java/com/tangem/tap/common/text/DecimalDigitsInputFilter.kt deleted file mode 100644 index 2201e794cc..0000000000 --- a/app/src/main/java/com/tangem/tap/common/text/DecimalDigitsInputFilter.kt +++ /dev/null @@ -1,60 +0,0 @@ -package com.tangem.tap.common.text - -import android.text.InputFilter -import android.text.Spanned -import java.util.regex.Pattern - -/** -[REDACTED_AUTHOR] - */ -class DecimalDigitsInputFilter( - digitsBeforeDecimal: Int, - digitsAfterDecimal: Int, - private val decimalSeparator: String, -) : InputFilter { - private val pattern: Pattern = Pattern.compile( - "(([1-9]{1}[0-9]{0,${digitsBeforeDecimal - 1}})?||[0]{1})" + - "((\\$decimalSeparator[0-9]{0,$digitsAfterDecimal})?)||(\\$decimalSeparator)?", - ) - - override fun filter( - source: CharSequence, - sourceStart: Int, - sourceEnd: Int, - destination: Spanned, - destinationStart: Int, - destinationEnd: Int, - ): CharSequence? { - val destString = destination.toString() - val prefix = destString.substring(0, destinationStart) - val suffix = destString.substring(destinationEnd, destString.length) - val newDestination = prefix + suffix - - val resultPrefix = newDestination.substring(0, destinationStart) - val resultSuffix = newDestination.substring(destinationStart, newDestination.length) - val result = resultPrefix + source.toString() + resultSuffix - - return if (pattern.matcher(result).matches()) { - null - } else { - val replacedWithAppropriateDecimalSeparator = setDecimalSeparator(result, decimalSeparator) - if (pattern.matcher(replacedWithAppropriateDecimalSeparator).matches()) { - decimalSeparator - } else { - "" - } - } - } - - companion object { - fun setDecimalSeparator(value: String, decimalSeparator: String): String { - if (value.contains(decimalSeparator)) return value - - return if (decimalSeparator == ".") { - value.replace(",", decimalSeparator) - } else { - value.replace(".", decimalSeparator) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/text/Truncate.kt b/app/src/main/java/com/tangem/tap/common/text/Truncate.kt deleted file mode 100644 index 9131ec1023..0000000000 --- a/app/src/main/java/com/tangem/tap/common/text/Truncate.kt +++ /dev/null @@ -1,130 +0,0 @@ -package com.tangem.tap.common.text - -import android.widget.TextView -import com.tangem.tap.common.extensions.isEven - -/** -[REDACTED_AUTHOR] - */ -enum class TruncateType { - START, MIDDLE, END -} - -interface Truncate { - fun apply(tv: TextView, text: String, with: String): String - - companion object { - fun create(type: TruncateType): Truncate { - return when (type) { - TruncateType.START -> TruncateStart() - TruncateType.MIDDLE -> TruncateMiddle() - TruncateType.END -> TruncateEnd() - } - } - } -} - -abstract class BaseTruncate : Truncate { - protected var hasBeenTruncated = false - - override fun apply(tv: TextView, text: String, with: String): String { - val roughLength = getRoughFitLength(tv, text) - val fittedText = preciseFitting(tv, roughTruncate(text, roughLength), with) - return if (hasBeenTruncated) attachWith(fittedText, with) else fittedText - } - - private fun getRoughFitLength(tv: TextView, text: String): Int { - val existingSpace = tv.measuredWidth - (tv.paddingStart + tv.paddingEnd) - val textWillTakeSpace = tv.paint.measureText(text) - val overSizeRatio: Float = textWillTakeSpace / existingSpace - - val maxLengthOfText = text.length / overSizeRatio - if (text.length <= maxLengthOfText) return text.length - - return maxLengthOfText.toInt() - } - - private fun preciseFitting(tv: TextView, text: String, with: String): String { - if (!hasBeenTruncated) return text - - val spaceForText = tv.measuredWidth - (tv.paddingStart + tv.paddingEnd) - - var fittedText = text - while (tv.paint.measureText(fittedText + with) > spaceForText) { - fittedText = preciseTruncate(fittedText) - } - return fittedText - } - - protected abstract fun roughTruncate(text: String, residualLength: Int): String - protected abstract fun preciseTruncate(text: String): String - protected abstract fun attachWith(text: String, with: String): String -} - -class TruncateStart : BaseTruncate() { - override fun roughTruncate(text: String, residualLength: Int): String { - if (text.length <= residualLength) return text - - hasBeenTruncated = true - return text.substring(residualLength, text.length) - } - - override fun preciseTruncate(text: String): String = text.substring(1, text.length) - - override fun attachWith(text: String, with: String): String = with + text -} - -class TruncateMiddle : BaseTruncate() { - - override fun roughTruncate(text: String, residualLength: Int): String { - if (text.length <= residualLength || residualLength < 0) return text - - hasBeenTruncated = true - val halfOfResidualLength = residualLength / 2 - val leftSide = text.substring(0, halfOfResidualLength) - val rightSide = text.substring(text.length - halfOfResidualLength, text.length) - - return leftSide + rightSide - } - - override fun preciseTruncate(text: String): String { - val middlePosition = text.length / 2 - return if (text.length.isEven()) { - val leftSide = text.substring(0, middlePosition - 1) - val rightSide = text.substring(middlePosition, text.length) - leftSide + rightSide - } else { - val leftSide = text.substring(0, middlePosition) - val rightSide = text.substring(middlePosition + 1, text.length) - leftSide + rightSide - } - } - - override fun attachWith(text: String, with: String): String { - val cuttingPosition = text.length / 2 - val leftSide = text.substring(0, cuttingPosition) - val rightSide = text.substring(cuttingPosition, text.length) - return leftSide + with + rightSide - } -} - -class TruncateEnd : BaseTruncate() { - override fun roughTruncate(text: String, residualLength: Int): String { - if (text.length <= residualLength) return text - - hasBeenTruncated = true - return text.substring(0, residualLength) - } - - override fun preciseTruncate(text: String): String = text.substring(0, text.length - 1) - - override fun attachWith(text: String, with: String): String = text + with -} - -fun TextView.truncateWith(text: String, type: TruncateType, with: String = "..."): String { - val truncate = Truncate.create(type) - return truncate.apply(this, text, with) -} - -fun TextView.truncateMiddleWith(text: String, with: String = "..."): String = - this.truncateWith(text, TruncateType.MIDDLE, with) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt deleted file mode 100644 index 22817c3f93..0000000000 --- a/app/src/main/java/com/tangem/tap/common/toggleWidget/IndeterminateProgressButtonWidget.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.tangem.tap.common.toggleWidget - -import android.graphics.drawable.Drawable -import android.view.View -import com.google.android.material.button.MaterialButton -import com.tangem.tap.common.entities.ProgressState -import com.tangem.tap.common.extensions.hide -import com.tangem.tap.common.extensions.show - -/** -[REDACTED_AUTHOR] - */ -open class IndeterminateProgressButtonWidget( - private val button: MaterialButton, - private val progress: View, - initialState: ProgressState = ProgressState.Done, -) : ViewStateWidget { - - private var text: CharSequence = button.text - private var icon: Drawable? = button.icon - private var iconGravity: Int? = button.iconGravity - - init { - if (initialState != ProgressState.Done) changeState(initialState) - } - - var isEnabled: Boolean - get() = button.isEnabled - set(value) { - button.isEnabled = value - } - - override val mainView: View = button - - override fun changeState(state: WidgetState) { - val progressState = state as? ProgressState ?: return - - when (progressState) { - ProgressState.Done, ProgressState.Error -> switchToNone() - ProgressState.Loading -> switchToProgress() - else -> {} - } - } - - protected open fun switchToNone() { - button.isClickable = true - button.text = text - button.icon = icon - iconGravity?.let { button.iconGravity = it } - - progress.hide() - } - - protected open fun switchToProgress() { - button.isClickable = false - button.text = "" - button.icon = null - - progress.show() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt index 70b2540317..1fc37a0267 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt @@ -22,38 +22,14 @@ sealed class TapError( object UnknownError : TapError(R.string.send_error_unknown) open class CustomError(val customMessage: String) : TapError(R.string.common_custom_string, listOf(customMessage)) - data class UnsupportedState( - val stateError: String, - ) : TapError(R.string.common_custom_string, listOf("Unsupported state: $stateError")) object NoInternetConnection : TapError(R.string.wallet_notification_no_internet) - object AmountExceedsBalance : TapError(R.string.send_validation_amount_exceeds_balance) - data class AmountLowerExistentialDeposit( - override val args: List, - ) : TapError(R.string.send_error_minimum_balance_format) - - object FeeExceedsBalance : TapError(R.string.send_validation_invalid_fee) - object TotalExceedsBalance : TapError(R.string.send_validation_invalid_total) - object InvalidAmountValue : TapError(R.string.send_validation_invalid_amount) - object InvalidFeeValue : TapError(R.string.send_error_invalid_fee_value) - data class DustAmount(override val args: List) : TapError(R.string.send_error_dust_amount_format) - object DustChange : TapError(R.string.send_error_dust_change) sealed class WalletManager { class NoAccountError(amountToCreateAccount: String) : CustomError(amountToCreateAccount) class InternalError(message: String) : CustomError(message) object BlockchainIsUnreachableTryLater : TapError(R.string.wallet_balance_blockchain_unreachable_try_later) } - - sealed class WalletConnect { - object UnsupportedDapp : TapError(R.string.wallet_connect_error_unsupported_dapp) - object UnsupportedLink : TapError(R.string.wallet_connect_error_failed_to_connect) - } - - data class ValidateTransactionErrors( - override val errorList: List, - override val builder: (List) -> String, - ) : TapError(-1), MultiMessageError } sealed class TapSdkError(override val messageResId: Int?) : TangemError(code = 50100) { diff --git a/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt b/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt index fd2b740b37..60ea643487 100644 --- a/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/main/MainViewModel.kt @@ -18,7 +18,6 @@ import com.tangem.domain.settings.IncrementAppLaunchCounterUseCase import com.tangem.domain.staking.FetchStakingTokensUseCase import com.tangem.domain.walletmanager.WalletManagersFacade import com.tangem.domain.wallets.legacy.UserWalletsListManager -import com.tangem.features.send.api.featuretoggles.SendFeatureToggles import com.tangem.features.staking.api.featuretoggles.StakingFeatureToggles import com.tangem.tap.common.extensions.setContext import com.tangem.tap.features.main.model.MainScreenState @@ -42,7 +41,6 @@ internal class MainViewModel @Inject constructor( private val blockchainSDKFactory: BlockchainSDKFactory, private val userWalletsListManager: UserWalletsListManager, private val walletManagersFacade: WalletManagersFacade, - private val sendFeatureToggles: SendFeatureToggles, private val feedbackManagerFeatureToggles: FeedbackManagerFeatureToggles, private val dispatchers: CoroutineDispatcherProvider, private val stakingFeatureToggles: StakingFeatureToggles, @@ -68,7 +66,6 @@ internal class MainViewModel @Inject constructor( viewModelScope.launch(dispatchers.main) { incrementAppLaunchCounterUseCase() } updateAppCurrencies() - updateSendFeatureToggle() observeFlips() displayBalancesHidingStatusToast() displayHiddenBalancesModalNotification() @@ -130,12 +127,6 @@ internal class MainViewModel @Inject constructor( } } - private fun updateSendFeatureToggle() { - viewModelScope.launch(dispatchers.main) { - sendFeatureToggles.fetchNewSendEnabled() - } - } - private fun observeFlips() { listenToFlipsUseCase().launchIn(viewModelScope) } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt b/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt deleted file mode 100644 index 35ee864987..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/redux/SendScreenAction.kt +++ /dev/null @@ -1,215 +0,0 @@ -package com.tangem.tap.features.send.redux - -import com.tangem.Message -import com.tangem.blockchain.common.Amount -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.FeePaidCurrency -import com.tangem.blockchain.common.WalletManager -import com.tangem.blockchain.common.transaction.TransactionFee -import com.tangem.common.core.TangemSdkError -import com.tangem.domain.models.scan.ScanResponse -import com.tangem.domain.redux.StateDialog -import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.tap.common.analytics.events.Token.Send.AddressEntered -import com.tangem.tap.common.redux.ToastNotificationAction -import com.tangem.tap.domain.TapError -import com.tangem.tap.domain.configurable.warningMessage.WarningMessage -import com.tangem.tap.features.send.redux.states.ButtonState -import com.tangem.tap.features.send.redux.states.FeeType -import com.tangem.tap.features.send.redux.states.MainCurrencyType -import com.tangem.wallet.R -import org.rekotlin.Action -import java.math.BigDecimal - -/** -[REDACTED_AUTHOR] - */ -interface SendScreenAction : Action -interface SendScreenActionUi : SendScreenAction - -object ReleaseSendState : Action - -data class PrepareSendScreen( - val walletManager: WalletManager, - val feePaidCurrency: FeePaidCurrency, - val currency: CryptoCurrency, - val coinAmount: Amount?, - val coinRate: BigDecimal?, - val tokenAmount: Amount? = null, - val tokenRate: BigDecimal? = null, - val feeCurrencyRate: BigDecimal? = null, - val feeCurrencyDecimals: Int = 0, -) : SendScreenAction - -// Address -sealed class AddressActionUi : SendScreenActionUi { - data class HandleUserInput(val data: String) : AddressActionUi() - data class PasteAddress(val data: String, val sourceType: AddressEntered.SourceType) : AddressActionUi() - data class CheckClipboard(val data: String?) : AddressActionUi() - data class CheckAddress(val sourceType: AddressEntered.SourceType?) : AddressActionUi() - data class SetTruncateHandler(val handler: (String) -> String) : AddressActionUi() - data class TruncateOrRestore(val truncate: Boolean) : AddressActionUi() -} - -sealed class TransactionExtrasAction : SendScreenActionUi { - data class Prepare( - val blockchain: Blockchain, - val walletAddress: String, - val xrpTag: String?, - ) : TransactionExtrasAction() - - object Release : TransactionExtrasAction() - - @Deprecated("Only in legacy send screen") - sealed class XlmMemo : TransactionExtrasAction() { - // data class ChangeSelectedMemo(val memoType: XlmMemoType) : XlmMemo() - data class HandleUserInput(val data: String) : XlmMemo() - } - - @Deprecated("Only in legacy send screen") - sealed class BinanceMemo : TransactionExtrasAction() { - data class HandleUserInput(val data: String) : BinanceMemo() - } - - @Deprecated("Only in legacy send screen") - sealed class XrpDestinationTag : TransactionExtrasAction() { - data class HandleUserInput(val data: String) : XrpDestinationTag() - } - - @Deprecated("Only in legacy send screen") - sealed class TonMemo : TransactionExtrasAction() { - data class HandleUserInput(val data: String) : TonMemo() - } - - @Deprecated("Only in legacy send screen") - sealed class CosmosMemo : TransactionExtrasAction() { - data class HandleUserInput(val data: String) : CosmosMemo() - } - - @Deprecated("Only in legacy send screen") - sealed class HederaMemo : TransactionExtrasAction() { - data class HandleUserInput(val data: String) : HederaMemo() - } - - @Deprecated("Only in legacy send screen") - sealed class AlgorandMemo : TransactionExtrasAction() { - data class HandleUserInput(val data: String) : AlgorandMemo() - } -} - -sealed class AddressVerifyAction : SendScreenAction { - enum class Error { - ADDRESS_INVALID_OR_UNSUPPORTED_BY_BLOCKCHAIN, - ADDRESS_SAME_AS_WALLET, - } - - data class ChangePasteBtnEnableState(val isEnabled: Boolean) : AddressVerifyAction() - - sealed class AddressVerification : AddressVerifyAction() { - data class SetAddressError(val error: Error?) : AddressVerification() - data class SetWalletAddress(val address: String, val isUserInput: Boolean) : AddressVerification() - } -} - -// Amount to send -sealed class AmountActionUi : SendScreenActionUi { - data class HandleUserInput(val data: String) : AmountActionUi() - object CheckAmountToSend : AmountActionUi() - object SetMaxAmount : AmountActionUi() - data class SetMainCurrency(val mainCurrency: MainCurrencyType) : AmountActionUi() - object ToggleMainCurrency : AmountActionUi() -} - -sealed class AmountAction : SendScreenAction { - data class SetAmount(val amountCrypto: BigDecimal, val isUserInput: Boolean) : AmountAction() - data class SetAmountError(val error: TapError?) : AmountAction() - data class SetDecimalSeparator(val separator: String) : AmountAction() - data class HideBalance(val hide: Boolean) : AmountAction() -} - -// Fee -sealed class FeeActionUi : SendScreenActionUi { - object ToggleControlsVisibility : FeeActionUi() - data class ChangeSelectedFee(val feeType: FeeType) : FeeActionUi() - class ChangeIncludeFee(val isIncluded: Boolean) : FeeActionUi() -} - -sealed class FeeAction : SendScreenAction { - - object RequestFee : FeeAction() - sealed class FeeCalculation : FeeAction() { - data class SetFeeResult(val fee: TransactionFee) : FeeCalculation() - object ClearResult : FeeCalculation() - } - - data class ChangeLayoutVisibility( - val main: Boolean? = null, - val controls: Boolean? = null, - val chipGroup: Boolean? = null, - ) : FeeAction() -} - -sealed class ReceiptAction : SendScreenAction { - object RefreshReceipt : ReceiptAction() -} - -sealed class SendActionUi : SendScreenActionUi { - data class SendAmountToRecipient(val messageForSigner: Message) : SendScreenActionUi - object CheckIfTransactionDataWasProvided : SendScreenActionUi -} - -sealed class SendAction : SendScreenAction { - - data class ChangeSendButtonState(val state: ButtonState) : SendAction() - object SendSuccess : SendAction(), ToastNotificationAction { - override val messageResource: Int = R.string.send_transaction_success - } - - sealed class Dialog : SendAction(), StateDialog { - data class TezosWarningDialog( - val reduceCallback: () -> Unit, - val sendAllCallback: () -> Unit, - val reduceAmount: BigDecimal, - ) : Dialog() - - data class KaspaWarningDialog( - val maxOutputs: Int, - val maxAmount: BigDecimal, - val onOk: () -> Unit, - ) : Dialog() - - data class ChiaWarningDialog( - val blockchainName: String, - val maxOutputs: Int, - val maxAmount: BigDecimal, - val onOk: () -> Unit, - ) : Dialog() - - sealed class SendTransactionFails : Dialog() { - data class CardSdkError(val error: TangemSdkError, val scanResponse: ScanResponse) : Dialog() - data class BlockchainSdkError( - val error: com.tangem.blockchain.common.BlockchainSdkError, - val scanResponse: ScanResponse, - ) : Dialog() - } - - data class RequestFeeError( - val error: com.tangem.blockchain.common.BlockchainSdkError, - val scanResponse: ScanResponse, - val onRetry: () -> Unit, - ) : Dialog() - - object Hide : Dialog() - } - - sealed class Warnings : SendAction() { - object Update : SendAction() - data class Set(val warningList: List) : SendAction() - } - - data class SendSpecificTransaction( - val sendAmount: String, - val destinationAddress: String, - val transactionId: String, - ) : SendAction() -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressMiddleware.kt deleted file mode 100644 index 96cf7c9c6f..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressMiddleware.kt +++ /dev/null @@ -1,225 +0,0 @@ -package com.tangem.tap.features.send.redux.middlewares - -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.Wallet -import com.tangem.blockchain.common.WalletManager -import com.tangem.core.analytics.Analytics -import com.tangem.tap.common.analytics.events.Token.Send.AddressEntered -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.features.send.redux.* -import com.tangem.tap.features.send.redux.AddressVerifyAction.AddressVerification.SetAddressError -import com.tangem.tap.features.send.redux.AddressVerifyAction.AddressVerification.SetWalletAddress -import com.tangem.tap.features.send.redux.AddressVerifyAction.Error -import com.tangem.tap.mainScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import org.rekotlin.Action -import org.rekotlin.DispatchFunction - -/** -[REDACTED_AUTHOR] - */ -internal class AddressMiddleware { - - private val addressValidator = AddressValidator() - - fun handle(action: AddressActionUi, appState: AppState?, dispatch: (Action) -> Unit) { - when (action) { - is AddressActionUi.HandleUserInput -> handleUserInput(action.data, appState, dispatch) - is AddressActionUi.PasteAddress -> pasteAddress(action.data, action.sourceType, dispatch) - is AddressActionUi.CheckClipboard -> verifyClipboard(action.data, appState, dispatch) - is AddressActionUi.CheckAddress -> verifyAddress(action.sourceType, appState, dispatch) - else -> return - } - } - - private fun handleUserInput(input: String, appState: AppState?, dispatch: DispatchFunction) { - val sendState = appState?.sendState ?: return - if (input == sendState.addressState.viewFieldValue.value) return - - setAddressAndCheck(data = input, sourceType = null, isUserInput = true, dispatch = dispatch) - } - - private fun pasteAddress(data: String, sourceType: AddressEntered.SourceType, dispatch: (Action) -> Unit) { - setAddressAndCheck(data = data, sourceType = sourceType, isUserInput = false, dispatch = dispatch) - } - - private fun setAddressAndCheck( - data: String, - sourceType: AddressEntered.SourceType?, - isUserInput: Boolean, - dispatch: (Action) -> Unit, - ) { - dispatch(SetWalletAddress(data, isUserInput)) - dispatch(AddressActionUi.CheckAddress(sourceType)) - } - - private fun verifyAddress( - sourceType: AddressEntered.SourceType?, - appState: AppState?, - dispatch: (Action) -> Unit, - ) { - val sendState = appState?.sendState ?: return - val walletManager = sendState.walletManager ?: return - val address = sendState.addressState.normalFieldValue ?: return - val isUserInput = sendState.addressState.viewFieldValue.isFromUserInput - - verifyAddress( - address = address, - walletManager = walletManager, - isUserInput = isUserInput, - dispatch = dispatch, - sourceType = sourceType, - ) - } - - private fun verifyAddress( - address: String, - walletManager: WalletManager, - sourceType: AddressEntered.SourceType?, - isUserInput: Boolean, - dispatch: (Action) -> Unit, - ) { - val wallet = walletManager.wallet - - mainScope.launch { - val failReason = withContext(Dispatchers.IO) { - addressValidator.validateAddress(walletManager, address) - } - - if (failReason == null) { - dispatchSuccessValidationActions( - address = address, - wallet = wallet, - sourceType = sourceType, - isUserInput = isUserInput, - dispatch = dispatch, - ) - } else { - dispatchFailedValidationActions( - failReason = failReason, - sourceType = sourceType, - dispatch = dispatch, - ) - } - } - } - - private fun dispatchSuccessValidationActions( - address: String, - wallet: Wallet, - sourceType: AddressEntered.SourceType?, - isUserInput: Boolean, - dispatch: (Action) -> Unit, - ) { - val addressSchemeSplit = when (wallet.blockchain) { - Blockchain.BitcoinCash, Blockchain.Kaspa -> listOf(address) - else -> address.split(":") - } - - val noSchemeAddress = when (addressSchemeSplit.size) { - 1 -> address // no scheme - 2 -> { // scheme - if (wallet.blockchain.validateShareScheme(addressSchemeSplit[0])) { - addressSchemeSplit[1] - } else { - sourceType?.let { - Analytics.send( - event = AddressEntered( - sourceType = sourceType, - validationResult = AddressEntered.ValidationResult.Fail, - ), - ) - } - dispatch(SetAddressError(Error.ADDRESS_INVALID_OR_UNSUPPORTED_BY_BLOCKCHAIN)) - return - } - } - else -> { // invalid URI - sourceType?.let { - Analytics.send( - event = AddressEntered( - sourceType = sourceType, - validationResult = AddressEntered.ValidationResult.Fail, - ), - ) - } - dispatch(SetAddressError(Error.ADDRESS_INVALID_OR_UNSUPPORTED_BY_BLOCKCHAIN)) - return - } - } - - val supposedAddress = noSchemeAddress.removeShareUriQuery() // TODO: parse query? - - noSchemeAddress.getQueryParameter("amount")?.toBigDecimalOrNull()?.let { - dispatch(AmountAction.SetAmount(it, false)) - dispatch(AmountActionUi.CheckAmountToSend) - } - dispatch(SetWalletAddress(supposedAddress, isUserInput)) - dispatch(TransactionExtrasAction.Prepare(wallet.blockchain, address, null)) - dispatch(FeeAction.RequestFee) - sourceType?.let { - Analytics.send( - event = AddressEntered( - sourceType = sourceType, - validationResult = AddressEntered.ValidationResult.Success, - ), - ) - } - } - - private fun dispatchFailedValidationActions( - failReason: Error, - sourceType: AddressEntered.SourceType?, - dispatch: (Action) -> Unit, - ) { - dispatch(SetAddressError(failReason)) - dispatch(TransactionExtrasAction.Release) - sourceType?.let { - Analytics.send( - event = AddressEntered( - sourceType = sourceType, - validationResult = AddressEntered.ValidationResult.Fail, - ), - ) - } - } - - private fun String.removeShareUriQuery(): String = this.substringBefore("?") - - private fun String.getQueryParameter(name: String): String? { - return this.substringAfter("?").splitToMap("&", "=")[name] - } - - private fun verifyClipboard(input: String?, appState: AppState?, dispatch: DispatchFunction) { - val address = input ?: return - val walletManager = appState?.sendState?.walletManager ?: return - - val internalDispatcher: (Action) -> Unit = { - when (it) { - is SetWalletAddress -> { - dispatch(AddressVerifyAction.ChangePasteBtnEnableState(true)) - } - is SetAddressError -> { - dispatch(AddressVerifyAction.ChangePasteBtnEnableState(false)) - } - } - } - - verifyAddress( - address = address, - walletManager = walletManager, - sourceType = null, - isUserInput = false, - dispatch = internalDispatcher, - ) - } -} - -fun String.splitToMap(firstDelimiter: String, secondDelimiter: String): Map { - return this - .split(firstDelimiter) - .map { it.split(secondDelimiter) } - .associate { it.first() to it.last() } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressValidator.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressValidator.kt deleted file mode 100644 index 195250f789..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressValidator.kt +++ /dev/null @@ -1,70 +0,0 @@ -package com.tangem.tap.features.send.redux.middlewares - -import com.tangem.blockchain.blockchains.near.NearWalletManager -import com.tangem.blockchain.blockchains.near.network.NearAccount -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.Wallet -import com.tangem.blockchain.common.WalletManager -import com.tangem.blockchain.extensions.Result -import com.tangem.tap.features.send.redux.AddressVerifyAction - -internal class AddressValidator { - - suspend fun validateAddress(walletManager: WalletManager, address: String): AddressVerifyAction.Error? { - val blockchain = walletManager.wallet.blockchain - val wallet = walletManager.wallet - - return if (blockchain.isNear()) { - validateNearAddress(walletManager, address) - } else { - validateAddress(wallet, address) - } - } - - private suspend fun validateNearAddress( - walletManager: WalletManager, - address: String, - ): AddressVerifyAction.Error? { - // implicit address validation - if (address.length == NEAR_IMPLICIT_ADDRESS_LENGTH && hexRegex.matches(address)) { - return validateAddress(walletManager.wallet, address) - } - - // named address validation - if (address.length in NEAR_MIN_ADDRESS_LENGTH until NEAR_IMPLICIT_ADDRESS_LENGTH && - nearAddressRegex.matches(address) - ) { - val result = (walletManager as? NearWalletManager)?.getAccount(address) - return if (result is Result.Success && result.data is NearAccount.Full) { - null - } else { - AddressVerifyAction.Error.ADDRESS_INVALID_OR_UNSUPPORTED_BY_BLOCKCHAIN - } - } - - return AddressVerifyAction.Error.ADDRESS_INVALID_OR_UNSUPPORTED_BY_BLOCKCHAIN - } - - private fun Blockchain.isNear(): Boolean { - return this == Blockchain.Near || this == Blockchain.NearTestnet - } - - private fun validateAddress(wallet: Wallet, address: String): AddressVerifyAction.Error? { - return if (wallet.blockchain.validateAddress(address)) { - if (wallet.addresses.all { it.value != address }) { - null - } else { - AddressVerifyAction.Error.ADDRESS_SAME_AS_WALLET - } - } else { - AddressVerifyAction.Error.ADDRESS_INVALID_OR_UNSUPPORTED_BY_BLOCKCHAIN - } - } - - companion object { - private const val NEAR_MIN_ADDRESS_LENGTH = 2 - private const val NEAR_IMPLICIT_ADDRESS_LENGTH = 64 - private val hexRegex = Regex("^[0-9a-f]+$") - private val nearAddressRegex = Regex("^(([a-z\\d]+[\\-_])*[a-z\\d]+\\.)*([a-z\\d]+[\\-_])*[a-z\\d]+\$") - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt deleted file mode 100644 index 0564218bcd..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AmountMiddleware.kt +++ /dev/null @@ -1,131 +0,0 @@ -package com.tangem.tap.features.send.redux.middlewares - -import com.tangem.blockchain.common.Amount -import com.tangem.blockchain.common.TransactionError -import com.tangem.common.extensions.isZero -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.features.send.redux.AmountAction -import com.tangem.tap.features.send.redux.AmountActionUi -import com.tangem.tap.features.send.redux.FeeAction -import com.tangem.tap.features.send.redux.FeeActionUi -import com.tangem.tap.features.send.redux.ReceiptAction -import com.tangem.tap.features.send.redux.SendAction -import com.tangem.tap.features.send.redux.states.MainCurrencyType -import com.tangem.tap.features.send.redux.states.SendState -import org.rekotlin.Action -import java.math.BigDecimal -import java.util.* - -/** -[REDACTED_AUTHOR] - */ -class AmountMiddleware { - - fun handle(action: AmountActionUi, appState: AppState?, dispatch: (Action) -> Unit) { - when (action) { - is AmountActionUi.HandleUserInput -> handleUserInput(action.data, appState, dispatch) - is AmountActionUi.CheckAmountToSend -> checkAmountToSend(appState, dispatch) - is AmountActionUi.SetMaxAmount -> setMaxAmount(appState, dispatch) - is AmountActionUi.ToggleMainCurrency -> toggleMainCurrency(appState, dispatch) - is AmountActionUi.SetMainCurrency -> return - } - } - - private fun handleUserInput(data: String, appState: AppState?, dispatch: (Action) -> Unit) { - val sendState = appState?.sendState ?: return - val amountState = sendState.amountState - - var input = amountState.toBigDecimalSeparator(data) - input = if (input == ".") "0.0" else input - val inputValue = when { - input.isEmpty() || input == "0" -> BigDecimal.ZERO - else -> BigDecimal(input) - } - if (inputValue.isZero() && amountState.amountToSendCrypto.isZero()) return - - val inputValueCrypto = if (amountState.mainCurrency.type == MainCurrencyType.CRYPTO) { - inputValue - } else { - sendState.convertFiatToExtractCrypto(inputValue) - } - - dispatch(AmountAction.SetAmount(inputValueCrypto, true)) - dispatch(AmountActionUi.CheckAmountToSend) - dispatch(FeeAction.RequestFee) - } - - private fun checkAmountToSend(appState: AppState?, dispatch: (Action) -> Unit) { - val sendState = appState?.sendState ?: return - val walletManager = sendState.walletManager ?: return - val typedAmount = sendState.amountState.amountToExtract ?: return - - val inputCrypto = sendState.amountState.amountToSendCrypto - if (sendState.amountState.viewAmountValue.value == "0" && inputCrypto.isZero()) { - dispatch(ReceiptAction.RefreshReceipt) - dispatch(SendAction.ChangeSendButtonState(sendState.getButtonState())) - return - } - - val amountToSend = Amount(typedAmount, sendState.getTotalAmountToSend(inputCrypto)) - val transactionErrors = walletManager.validateTransaction(amountToSend, sendState.feeState.currentFee?.amount) - val amountFieldErrors = filterErrorsForAmountField(transactionErrors) - if (amountFieldErrors.isEmpty()) { - dispatch(AmountAction.SetAmountError(null)) - } else { - dispatch(AmountAction.SetAmountError(createValidateTransactionError(amountFieldErrors, walletManager))) - } - dispatch(ReceiptAction.RefreshReceipt) - dispatch(SendAction.ChangeSendButtonState(sendState.getButtonState())) - } - - private fun setMaxAmount(appState: AppState?, dispatch: (Action) -> Unit) { - val sendState = appState?.sendState ?: return - - dispatch(AmountAction.SetAmount(sendState.amountState.balanceCrypto, false)) - if (sendState.amountState.canIncludeFee()) { - dispatch(FeeAction.ChangeLayoutVisibility(main = true, controls = true)) - dispatch(FeeActionUi.ChangeIncludeFee(true)) - } - - dispatch(AmountActionUi.CheckAmountToSend) - if (SendState.isReadyToRequestFee()) dispatch(FeeAction.RequestFee) - } - - private fun toggleMainCurrency(appState: AppState?, dispatch: (Action) -> Unit) { - val amountState = appState?.sendState?.amountState ?: return - if (!appState.sendState.coinIsConvertible()) return - - val type = if (amountState.mainCurrency.type == MainCurrencyType.FIAT) { - MainCurrencyType.CRYPTO - } else { - MainCurrencyType.FIAT - } - - dispatch(AmountActionUi.SetMainCurrency(type)) - } -} - -private fun filterErrorsForAmountField(errors: EnumSet): EnumSet { - val showIntoAmountField = EnumSet.noneOf(TransactionError::class.java) - errors.forEach { - when (it) { - TransactionError.AmountExceedsBalance -> { - showIntoAmountField.remove(TransactionError.TotalExceedsBalance) - showIntoAmountField.add(it) - } - TransactionError.FeeExceedsBalance -> { - showIntoAmountField.remove(TransactionError.TotalExceedsBalance) - showIntoAmountField.add(it) - } - TransactionError.TotalExceedsBalance -> { - val notAcceptable = listOf(TransactionError.AmountExceedsBalance, TransactionError.FeeExceedsBalance) - if (!showIntoAmountField.containsAll(notAcceptable)) showIntoAmountField.add(it) - showIntoAmountField.remove(TransactionError.AmountLowerExistentialDeposit) - } - else -> showIntoAmountField.add(it) - } - } - showIntoAmountField.remove(TransactionError.TezosSendAll) - - return showIntoAmountField -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt deleted file mode 100644 index ea4092c252..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/RequestFeeMiddleware.kt +++ /dev/null @@ -1,132 +0,0 @@ -package com.tangem.tap.features.send.redux.middlewares - -import com.tangem.blockchain.common.Amount -import com.tangem.blockchain.common.AmountType -import com.tangem.blockchain.common.BlockchainSdkError -import com.tangem.blockchain.common.WalletManager -import com.tangem.blockchain.common.transaction.TransactionFee -import com.tangem.blockchain.extensions.Result -import com.tangem.common.extensions.isZero -import com.tangem.domain.demo.DemoTransactionSender -import com.tangem.domain.feedback.models.BlockchainErrorInfo -import com.tangem.tap.common.extensions.inject -import com.tangem.tap.common.extensions.stripZeroPlainString -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.features.demo.isDemoCard -import com.tangem.tap.features.send.redux.AmountActionUi -import com.tangem.tap.features.send.redux.FeeAction -import com.tangem.tap.features.send.redux.ReceiptAction -import com.tangem.tap.features.send.redux.SendAction -import com.tangem.tap.features.send.redux.states.SendState -import com.tangem.tap.proxy.redux.DaggerGraphState -import com.tangem.tap.scope -import com.tangem.tap.store -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import org.rekotlin.DispatchFunction -import java.math.BigDecimal - -/** -[REDACTED_AUTHOR] - */ -class RequestFeeMiddleware { - - @Suppress("CyclomaticComplexMethod") - fun handle(appState: AppState?, dispatch: DispatchFunction) { - val sendState = appState?.sendState ?: return - val walletManager = sendState.walletManager ?: return - val scanResponse = appState.globalState.scanResponse ?: return - - if (!SendState.isReadyToRequestFee()) { - dispatch(FeeAction.FeeCalculation.ClearResult) - dispatch(ReceiptAction.RefreshReceipt) - dispatch(SendAction.ChangeSendButtonState(sendState.getButtonState())) - return - } - val typedAmount = sendState.amountState.amountToExtract ?: return - - val destinationAddress = sendState.addressState.destinationWalletAddress!! - val destinationAmount = Amount(typedAmount, sendState.amountState.amountToSendCrypto) - val txSender = if (scanResponse.isDemoCard()) { - DemoTransactionSender(walletManager) - } else { - walletManager - } - scope.launch { - val feeResult = txSender.getFee(destinationAmount, destinationAddress) - withContext(Dispatchers.Main) { - when (feeResult) { - is Result.Success -> { - val result = feeResult.data -// val result = FeeMock.getFee(walletManager.wallet.blockchain) - dispatch(FeeAction.FeeCalculation.SetFeeResult(result)) - when (result) { - is TransactionFee.Single -> { - val fee = result.normal.amount.value ?: BigDecimal.ZERO - if (fee.isZero()) { - dispatch(FeeAction.ChangeLayoutVisibility(main = false)) - } else { - dispatch(FeeAction.ChangeLayoutVisibility(main = true, chipGroup = false)) - } - } - is TransactionFee.Choosable -> { - dispatch(FeeAction.ChangeLayoutVisibility(main = true, chipGroup = true)) - } - } - } - is Result.Failure -> { - dispatch(FeeAction.FeeCalculation.ClearResult) - dispatch(FeeAction.ChangeLayoutVisibility(main = false)) - - val featureToggles = store.inject(DaggerGraphState::feedbackManagerFeatureToggles) - if (featureToggles.isLocalLogsEnabled) { - saveBlockchainError(feeResult, destinationAddress, destinationAmount, walletManager) - } else { - store.state.globalState.feedbackManager?.infoHolder?.updateOnSendError( - walletManager = walletManager, - amountToSend = destinationAmount, - feeAmount = null, - destinationAddress = destinationAddress, - ) - } - - val blockchainSdkError = feeResult.error as? BlockchainSdkError ?: return@withContext - dispatch( - SendAction.Dialog.RequestFeeError( - error = blockchainSdkError, - scanResponse = scanResponse, - onRetry = { dispatch(FeeAction.RequestFee) }, - ), - ) - } - } - dispatch(AmountActionUi.CheckAmountToSend) - } - } - } - - private fun saveBlockchainError( - feeResult: Result.Failure, - destinationAddress: String, - destinationAmount: Amount, - walletManager: WalletManager, - ) { - store.inject(DaggerGraphState::saveBlockchainErrorUseCase).invoke( - error = BlockchainErrorInfo( - errorMessage = (feeResult.error as? BlockchainSdkError)?.customMessage - ?: "It isn't BlockchainSdkError", - blockchainId = walletManager.wallet.blockchain.id, - derivationPath = walletManager.wallet.publicKey.derivationPath?.rawPath ?: "", - destinationAddress = destinationAddress, - tokenSymbol = if (destinationAmount.type is AmountType.Token) { - destinationAmount.currencySymbol - } else { - "" - }, - amount = destinationAmount.value?.stripZeroPlainString() ?: "0", - fee = null, - ), - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt deleted file mode 100644 index ecb0e071d8..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/SendMiddleware.kt +++ /dev/null @@ -1,455 +0,0 @@ -package com.tangem.tap.features.send.redux.middlewares - -import com.google.firebase.crashlytics.FirebaseCrashlytics -import com.tangem.blockchain.blockchains.algorand.AlgorandTransactionExtras -import com.tangem.blockchain.blockchains.binance.BinanceTransactionExtras -import com.tangem.blockchain.blockchains.cosmos.CosmosTransactionExtras -import com.tangem.blockchain.blockchains.hedera.HederaTransactionExtras -import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider -import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras -import com.tangem.blockchain.blockchains.ton.TonTransactionExtras -import com.tangem.blockchain.blockchains.xrp.XrpTransactionBuilder.XrpTransactionExtras -import com.tangem.blockchain.common.* -import com.tangem.blockchain.common.transaction.Fee -import com.tangem.blockchain.extensions.Result -import com.tangem.blockchainsdk.utils.minimalAmount -import com.tangem.common.core.TangemSdkError -import com.tangem.common.routing.AppRouter -import com.tangem.core.analytics.Analytics -import com.tangem.core.analytics.models.AnalyticsParam -import com.tangem.core.analytics.models.Basic -import com.tangem.domain.common.TapWorkarounds.isStart2Coin -import com.tangem.domain.common.extensions.withMainContext -import com.tangem.domain.demo.DemoTransactionSender -import com.tangem.domain.feedback.models.BlockchainErrorInfo -import com.tangem.domain.models.scan.CardDTO -import com.tangem.domain.tokens.legacy.TradeCryptoAction -import com.tangem.tap.common.analytics.events.Token -import com.tangem.tap.common.analytics.events.Token.Send.SelectedCurrency.CurrencyType -import com.tangem.tap.common.extensions.* -import com.tangem.tap.common.redux.AppDialog -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.domain.TangemSigner -import com.tangem.tap.domain.TapError -import com.tangem.tap.domain.configurable.warningMessage.WarningMessage -import com.tangem.tap.features.demo.isDemoCard -import com.tangem.tap.features.send.redux.* -import com.tangem.tap.features.send.redux.FeeAction.RequestFee -import com.tangem.tap.features.send.redux.states.* -import com.tangem.tap.proxy.redux.DaggerGraphState -import com.tangem.tap.scope -import com.tangem.tap.store -import com.tangem.utils.extensions.DELAY_SDK_DIALOG_CLOSE -import com.tangem.wallet.R -import kotlinx.coroutines.delay -import kotlinx.coroutines.launch -import org.rekotlin.Action -import org.rekotlin.Middleware -import java.util.EnumSet - -/** -[REDACTED_AUTHOR] - */ -class SendMiddleware { - val sendMiddleware: Middleware = { dispatch, appState -> - { nextDispatch -> - { action -> - when (action) { - is AddressActionUi -> AddressMiddleware().handle(action, appState(), dispatch) - is AmountActionUi -> AmountMiddleware().handle(action, appState(), dispatch) - is RequestFee -> RequestFeeMiddleware().handle(appState(), dispatch) - is SendActionUi.SendAmountToRecipient -> - verifyAndSendTransaction(action, appState(), dispatch) - is SendAction.Warnings.Update -> updateWarnings(dispatch) - is SendActionUi.CheckIfTransactionDataWasProvided -> { - val transactionData = appState()?.sendState?.externalTransactionData - if (transactionData != null) { - store.dispatchOnMain( - AddressVerifyAction.AddressVerification.SetWalletAddress( - address = transactionData.destinationAddress, - isUserInput = false, - ), - ) - store.dispatchOnMain(AmountActionUi.SetMainCurrency(MainCurrencyType.CRYPTO)) - store.dispatchOnMain(AmountActionUi.HandleUserInput(transactionData.amount)) - store.dispatchOnMain( - AmountAction.SetAmount( - transactionData.amount.toBigDecimal(), - false, - ), - ) - } - } - } - nextDispatch(action) - } - } - } -} - -private fun verifyAndSendTransaction( - action: SendActionUi.SendAmountToRecipient, - appState: AppState?, - dispatch: (Action) -> Unit, -) { - val sendState = appState?.sendState ?: return - val walletManager = sendState.walletManager ?: return - val card = appState.globalState.scanResponse?.card ?: return - val destinationAddress = sendState.addressState.destinationWalletAddress ?: return - val typedAmount = sendState.amountState.amountToExtract ?: return - val fee = sendState.feeState.currentFee ?: return - - val amountToSend = Amount(typedAmount, sendState.getTotalAmountToSend()) - - val transactionErrors = walletManager.validateTransaction(amountToSend, fee.amount) - when { - transactionErrors.contains(TransactionError.TezosSendAll) -> { - val reduceAmount = walletManager.wallet.blockchain.minimalAmount() - dispatch( - SendAction.Dialog.TezosWarningDialog( - reduceCallback = { - dispatch(AmountAction.SetAmount(typedAmount.value!!.minus(reduceAmount), false)) - dispatch(AmountActionUi.CheckAmountToSend) - }, - sendAllCallback = { - sendTransaction( - action = action, - walletManager = walletManager, - amountToSend = amountToSend, - fee = fee, - feeType = sendState.feeState.selectedFeeType, - destinationAddress = destinationAddress, - transactionExtras = sendState.transactionExtrasState, - card = card, - externalTransactionData = sendState.externalTransactionData, - mainCurrencyType = sendState.amountState.mainCurrency.type, - dispatch = dispatch, - ) - }, - reduceAmount, - ), - ) - } - else -> { - sendTransaction( - action = action, - walletManager = walletManager, - amountToSend = amountToSend, - fee = fee, - feeType = sendState.feeState.selectedFeeType, - destinationAddress = destinationAddress, - transactionExtras = sendState.transactionExtrasState, - card = card, - externalTransactionData = sendState.externalTransactionData, - mainCurrencyType = sendState.amountState.mainCurrency.type, - dispatch = dispatch, - ) - } - } -} - -@Suppress("LongParameterList", "LongMethod", "ComplexMethod") -private fun sendTransaction( - action: SendActionUi.SendAmountToRecipient, - walletManager: WalletManager, - amountToSend: Amount, - fee: Fee, - feeType: FeeType, - destinationAddress: String, - transactionExtras: TransactionExtrasState, - card: CardDTO, - externalTransactionData: ExternalTransactionData?, - mainCurrencyType: MainCurrencyType, - dispatch: (Action) -> Unit, -) { - dispatch(SendAction.ChangeSendButtonState(ButtonState.PROGRESS)) - var txData = walletManager.createTransaction(amountToSend, fee, destinationAddress) - - transactionExtras.xlmMemo?.memo?.let { txData = txData.copy(extras = StellarTransactionExtras(it)) } - transactionExtras.binanceMemo?.memo?.let { txData = txData.copy(extras = BinanceTransactionExtras(it.toString())) } - transactionExtras.xrpDestinationTag?.tag?.let { txData = txData.copy(extras = XrpTransactionExtras(it)) } - transactionExtras.cosmosMemoState?.memo?.let { txData = txData.copy(extras = CosmosTransactionExtras(it)) } - transactionExtras.tonMemoState?.memo?.let { txData = txData.copy(extras = TonTransactionExtras(it)) } - transactionExtras.hederaMemoState?.memo?.let { txData = txData.copy(extras = HederaTransactionExtras(it)) } - transactionExtras.algorandMemoState?.memo?.let { txData = txData.copy(extras = AlgorandTransactionExtras(it)) } - - scope.launch { - // TODO: Risky commented this part, unknown logic, need to test if removed - // TODO: [REDACTED_JIRA] - // val updateWalletResult = walletManager.safeUpdate() - // if (updateWalletResult is Result.Failure) { - // withMainContext { - // when (val error = updateWalletResult.error) { - // is TapError -> store.dispatchErrorNotification(error) - // is BlockchainSdkError -> { - // updateFeedbackManagerInfo( - // walletManager = walletManager, - // amountToSend = amountToSend, - // feeAmount = fee.amount, - // destinationAddress = destinationAddress, - // ) - // dispatch(SendAction.Dialog.SendTransactionFails.BlockchainSdkError(error = error)) - // } - // else -> { - // val tapError = if (error.message == null) { - // TapError.UnknownError - // } else { - // TapError.CustomError(error.message!!) - // } - // store.dispatchErrorNotification(tapError) - // } - // } - // dispatch(SendAction.ChangeSendButtonState(ButtonState.ENABLED)) - // } - // return@launch - // } - - val tangemSdk = store.inject(DaggerGraphState::cardSdkConfigRepository).sdk - val linkedTerminalState = tangemSdk.config.linkedTerminal - if (card.isStart2Coin) { - tangemSdk.config.linkedTerminal = false - } - - val signer = TangemSigner( - card = card, - tangemSdk = tangemSdk, - initialMessage = action.messageForSigner, - ) { signResponse -> - store.dispatch( - GlobalAction.UpdateWalletSignedHashes( - walletSignedHashes = signResponse.totalSignedHashes, - walletPublicKey = walletManager.wallet.publicKey.seedKey, - remainingSignatures = signResponse.remainingSignatures, - ), - ) - } - val sendResult = try { - if (card.isDemoCard()) { - DemoTransactionSender(walletManager).send(txData, signer) - } else { - (walletManager as TransactionSender).send(txData, signer) - } - } catch (ex: Exception) { - FirebaseCrashlytics.getInstance().recordException(ex) - delay(DELAY_SDK_DIALOG_CLOSE) - withMainContext { - tangemSdk.config.linkedTerminal = linkedTerminalState - dispatch(SendAction.ChangeSendButtonState(ButtonState.ENABLED)) - store.dispatchErrorNotification(TapError.CustomError(ex.localizedMessage ?: "Unknown error")) - } - return@launch - } - - withMainContext { - dispatch(SendAction.ChangeSendButtonState(ButtonState.ENABLED)) - tangemSdk.config.linkedTerminal = linkedTerminalState - - when (sendResult) { - is Result.Success -> { - dispatch(SendAction.SendSuccess) - - if (externalTransactionData != null) { - Analytics.send( - Basic.TransactionSent( - sentFrom = AnalyticsParam.TxSentFrom.Sell, - memoType = getMemoType(transactionExtras), - ), - ) - Analytics.sendSelectedCurrencyEvent(mainCurrencyType) - dispatch(TradeCryptoAction.FinishSelling(externalTransactionData.transactionId)) - } else { - Analytics.send( - Basic.TransactionSent( - sentFrom = AnalyticsParam.TxSentFrom.Send( - blockchain = walletManager.wallet.blockchain.fullName, - token = amountToSend.currencySymbol, - feeType = feeType.convertToAnalyticsFeeType(), - ), - memoType = getMemoType(transactionExtras), - ), - ) - Analytics.sendSelectedCurrencyEvent(mainCurrencyType) - store.dispatchNavigationAction(AppRouter::pop) - } - } - is Result.Failure -> { - updateFeedbackManagerInfo( - sendResult = sendResult.error, - walletManager = walletManager, - amountToSend = amountToSend, - feeAmount = fee.amount, - destinationAddress = destinationAddress, - ) - val error = sendResult.error as? BlockchainSdkError ?: return@withMainContext - - when (error) { - is BlockchainSdkError.WrappedTangemError -> { - val tangemSdkError = error.tangemError as? TangemSdkError ?: return@withMainContext - if (tangemSdkError is TangemSdkError.UserCancelled) return@withMainContext - - dispatch( - SendAction.Dialog.SendTransactionFails.CardSdkError( - error = tangemSdkError, - scanResponse = store.inject(DaggerGraphState::generalUserWalletsListManager) - .selectedUserWalletSync?.scanResponse - ?: error("ScanResponse must be not null"), - ), - ) - } - is BlockchainSdkError.CreateAccountUnderfunded -> { - // from XLM, XRP, Polkadot - dispatch( - SendAction.Dialog.SendTransactionFails.BlockchainSdkError( - error = error, - scanResponse = store.inject(DaggerGraphState::generalUserWalletsListManager) - .selectedUserWalletSync?.scanResponse - ?: error("ScanResponse must be not null"), - ), - ) - } - is BlockchainSdkError.Kaspa.UtxoAmountError -> { - dispatch( - SendAction.Dialog.KaspaWarningDialog( - maxOutputs = error.maxOutputs, - maxAmount = error.maxAmount, - onOk = { - dispatch(AmountAction.SetAmount(error.maxAmount, isUserInput = false)) - dispatch(AmountActionUi.CheckAmountToSend) - }, - ), - ) - } - is BlockchainSdkError.Chia.UtxoAmountError -> { - dispatch( - SendAction.Dialog.ChiaWarningDialog( - blockchainName = Blockchain.Chia.fullName, - maxOutputs = error.maxOutputs, - maxAmount = error.maxAmount, - onOk = { - dispatch(AmountAction.SetAmount(error.maxAmount, isUserInput = false)) - dispatch(AmountActionUi.CheckAmountToSend) - }, - ), - ) - } - else -> { - when { - error.customMessage.contains(DemoTransactionSender.ID) -> { - store.dispatchDialogShow( - AppDialog.SimpleOkDialogRes( - headerId = R.string.common_done, - messageId = R.string.alert_demo_feature_disabled, - onOk = { store.dispatchNavigationAction(AppRouter::pop) }, - ), - ) - } - else -> { - dispatch( - SendAction.Dialog.SendTransactionFails.BlockchainSdkError( - error = error, - scanResponse = store.inject(DaggerGraphState::generalUserWalletsListManager) - .selectedUserWalletSync?.scanResponse - ?: error("ScanResponse must be not null"), - ), - ) - } - } - } - } - } - } - } - } -} - -private fun getMemoType(transactionExtras: TransactionExtrasState): Basic.TransactionSent.MemoType { - return when { - transactionExtras.isEmpty() -> Basic.TransactionSent.MemoType.Empty - transactionExtras.isNull() -> Basic.TransactionSent.MemoType.Null - else -> Basic.TransactionSent.MemoType.Full - } -} - -private fun Analytics.sendSelectedCurrencyEvent(mainCurrencyType: MainCurrencyType) { - send( - Token.Send.SelectedCurrency( - currency = when (mainCurrencyType) { - MainCurrencyType.FIAT -> CurrencyType.AppCurrency - MainCurrencyType.CRYPTO -> CurrencyType.Token - }, - ), - ) -} - -private fun updateFeedbackManagerInfo( - walletManager: WalletManager, - amountToSend: Amount, - feeAmount: Amount, - destinationAddress: String, - sendResult: BlockchainError, -) { - val featureToggles = store.inject(DaggerGraphState::feedbackManagerFeatureToggles) - if (featureToggles.isLocalLogsEnabled) { - store.inject(DaggerGraphState::saveBlockchainErrorUseCase).invoke( - error = BlockchainErrorInfo( - errorMessage = (sendResult as? BlockchainSdkError)?.customMessage ?: "It isn't BlockchainSdkError", - blockchainId = walletManager.wallet.blockchain.id, - derivationPath = walletManager.wallet.publicKey.derivationPath?.rawPath ?: "", - destinationAddress = destinationAddress, - tokenSymbol = if (amountToSend.type is AmountType.Token) { - amountToSend.currencySymbol - } else { - "" - }, - amount = amountToSend.value?.stripZeroPlainString() ?: "0", - fee = feeAmount.value?.stripZeroPlainString() ?: "0", - ), - ) - } else { - store.state.globalState.feedbackManager?.infoHolder?.updateOnSendError( - walletManager = walletManager, - amountToSend = amountToSend, - feeAmount = feeAmount, - destinationAddress = destinationAddress, - ) - } -} - -fun createValidateTransactionError( - errorList: EnumSet, - walletManager: WalletManager, -): TapError.ValidateTransactionErrors { - val tapErrors = errorList.map { - when (it) { - TransactionError.AmountExceedsBalance -> TapError.AmountExceedsBalance - TransactionError.AmountLowerExistentialDeposit -> { - if (walletManager is ExistentialDepositProvider) { - val args = listOf(walletManager.getExistentialDeposit().stripZeroPlainString()) - TapError.AmountLowerExistentialDeposit(args) - } else { - TapError.UnknownError - } - } - TransactionError.FeeExceedsBalance -> TapError.FeeExceedsBalance - TransactionError.TotalExceedsBalance -> TapError.TotalExceedsBalance - TransactionError.InvalidAmountValue -> TapError.InvalidAmountValue - TransactionError.InvalidFeeValue -> TapError.InvalidFeeValue - TransactionError.DustAmount -> { - val args = listOf(walletManager.dustValue?.stripZeroPlainString() ?: "0") - TapError.DustAmount(args) - } - TransactionError.DustChange -> TapError.DustChange - else -> TapError.UnknownError - } - } - return TapError.ValidateTransactionErrors(tapErrors) { it.joinToString("\r\n") } -} - -private fun updateWarnings(dispatch: (Action) -> Unit) { - val warningsManager = store.state.globalState.warningManager ?: return - val blockchain = store.state.sendState.walletManager?.wallet?.blockchain ?: return - - val warnings = warningsManager.getWarnings(WarningMessage.Location.SendScreen, listOf(blockchain)) - dispatch(SendAction.Warnings.Set(warnings)) -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressReducer.kt deleted file mode 100644 index a5b44bff0f..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressReducer.kt +++ /dev/null @@ -1,52 +0,0 @@ -package com.tangem.tap.features.send.redux.reducers - -import com.tangem.tap.features.send.redux.AddressActionUi -import com.tangem.tap.features.send.redux.AddressVerifyAction -import com.tangem.tap.features.send.redux.AddressVerifyAction.AddressVerification -import com.tangem.tap.features.send.redux.SendScreenAction -import com.tangem.tap.features.send.redux.states.AddressState -import com.tangem.tap.features.send.redux.states.InputViewValue -import com.tangem.tap.features.send.redux.states.SendState - -/** -[REDACTED_AUTHOR] - */ -class AddressReducer : SendInternalReducer { - override fun handle(action: SendScreenAction, sendState: SendState): SendState = when (action) { - is AddressActionUi -> handleUiAction(action, sendState, sendState.addressState) - is AddressVerifyAction -> handleAction(action, sendState, sendState.addressState) - else -> sendState - } - - private fun handleUiAction(action: AddressActionUi, sendState: SendState, state: AddressState): SendState { - val result = when (action) { - is AddressActionUi.HandleUserInput -> state - is AddressActionUi.SetTruncateHandler -> state.copy(truncateHandler = action.handler) - is AddressActionUi.TruncateOrRestore -> { - val value = if (action.truncate) state.truncatedFieldValue ?: "" else state.normalFieldValue ?: "" - state.copy(viewFieldValue = state.viewFieldValue.copy(value = value)) - } - is AddressActionUi.PasteAddress -> return sendState - is AddressActionUi.CheckClipboard -> return sendState - is AddressActionUi.CheckAddress -> return sendState - } - return updateLastState(sendState.copy(addressState = result), result) - } - - private fun handleAction(action: AddressVerifyAction, sendState: SendState, state: AddressState): SendState { - val result = when (action) { - is AddressVerification.SetWalletAddress -> { - state.copy( - viewFieldValue = InputViewValue(action.address, action.isUserInput), - normalFieldValue = action.address, - truncatedFieldValue = state.truncate(action.address), - destinationWalletAddress = action.address, - error = null, - ) - } - is AddressVerifyAction.ChangePasteBtnEnableState -> state.copy(pasteIsEnabled = action.isEnabled) - is AddressVerification.SetAddressError -> state.copy(error = action.error, destinationWalletAddress = null) - } - return updateLastState(sendState.copy(addressState = result), result) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AmountReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AmountReducer.kt deleted file mode 100644 index 645fc6ec32..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AmountReducer.kt +++ /dev/null @@ -1,99 +0,0 @@ -package com.tangem.tap.features.send.redux.reducers - -import com.tangem.common.extensions.isZero -import com.tangem.tap.common.extensions.stripZeroPlainString -import com.tangem.tap.features.send.redux.AmountAction -import com.tangem.tap.features.send.redux.AmountActionUi -import com.tangem.tap.features.send.redux.AmountActionUi.SetMainCurrency -import com.tangem.tap.features.send.redux.SendScreenAction -import com.tangem.tap.features.send.redux.states.AmountState -import com.tangem.tap.features.send.redux.states.InputViewValue -import com.tangem.tap.features.send.redux.states.MainCurrencyType -import com.tangem.tap.features.send.redux.states.SendState -import com.tangem.utils.StringsSigns.STARS -import java.math.BigDecimal - -/** -[REDACTED_AUTHOR] - */ -class AmountReducer : SendInternalReducer { - override fun handle(action: SendScreenAction, sendState: SendState): SendState = when (action) { - is AmountActionUi -> handleUiAction(action, sendState, sendState.amountState) - is AmountAction -> handleAction(action, sendState, sendState.amountState) - else -> sendState - } - - private fun handleUiAction(action: AmountActionUi, sendState: SendState, state: AmountState): SendState { - val result = when (action) { - is SetMainCurrency -> { - val currencyCanBeSwitched = sendState.mainCurrencyCanBeSwitched() - - when (val currency = if (currencyCanBeSwitched) action.mainCurrency else MainCurrencyType.CRYPTO) { - MainCurrencyType.FIAT -> { - val fiatToSend = if (state.amountToSendCrypto.isZero()) { - BigDecimal.ZERO - } else { - sendState.convertExtractCryptoToFiat(state.amountToSendCrypto) - } - val rescaledBalance = sendState.convertExtractCryptoToFiat(state.balanceCrypto, true) - val viewValue = state.restoreDecimalSeparator(fiatToSend.stripZeroPlainString()) - state.copy( - viewAmountValue = InputViewValue(viewValue), - viewBalanceValue = if (state.hideBalance) STARS else rescaledBalance.stripZeroPlainString(), - mainCurrency = state.createMainCurrency(currency, true), - maxLengthOfAmount = sendState.getDecimals(currency), - cursorAtTheSamePosition = false, - ) - } - MainCurrencyType.CRYPTO -> { - val viewValue = state.restoreDecimalSeparator(state.amountToSendCrypto.stripZeroPlainString()) - state.copy( - viewAmountValue = InputViewValue(viewValue), - viewBalanceValue = state.balanceCrypto.stripZeroPlainString(), - mainCurrency = state.createMainCurrency(currency, currencyCanBeSwitched), - maxLengthOfAmount = sendState.getDecimals(currency), - cursorAtTheSamePosition = false, - ) - } - } - } - else -> return sendState - } - - return updateLastState(sendState.copy(amountState = result), result) - } - - private fun handleAction(action: AmountAction, sendState: SendState, state: AmountState): SendState { - val result = when (action) { - is AmountAction.SetAmount -> { - val amount = if (state.mainCurrency.type == MainCurrencyType.CRYPTO) { - action.amountCrypto - } else { - sendState.convertExtractCryptoToFiat(action.amountCrypto, true) - } - val viewValue = state.restoreDecimalSeparator(amount.stripZeroPlainString()) - state.copy( - viewAmountValue = InputViewValue(viewValue, action.isUserInput), - amountToSendCrypto = action.amountCrypto, - cursorAtTheSamePosition = true, - error = null, - ) - } - is AmountAction.SetAmountError -> state.copy(error = action.error) - is AmountAction.SetDecimalSeparator -> state.copy(decimalSeparator = action.separator) - is AmountAction.HideBalance -> { - val rescaledBalance = if (state.mainCurrency.type == MainCurrencyType.CRYPTO) { - state.balanceCrypto - } else { - sendState.convertExtractCryptoToFiat(state.balanceCrypto, true) - } - - state.copy( - hideBalance = action.hide, - viewBalanceValue = if (action.hide) STARS else rescaledBalance.stripZeroPlainString(), - ) - } - } - return updateLastState(sendState.copy(amountState = result), result) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/FeeReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/FeeReducer.kt deleted file mode 100644 index e4ac2c2376..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/FeeReducer.kt +++ /dev/null @@ -1,122 +0,0 @@ -package com.tangem.tap.features.send.redux.reducers - -import com.tangem.blockchain.common.transaction.Fee -import com.tangem.blockchain.common.transaction.TransactionFee -import com.tangem.tap.common.entities.ProgressState -import com.tangem.tap.features.send.redux.FeeAction -import com.tangem.tap.features.send.redux.FeeActionUi -import com.tangem.tap.features.send.redux.SendScreenAction -import com.tangem.tap.features.send.redux.states.FeeState -import com.tangem.tap.features.send.redux.states.FeeType -import com.tangem.tap.features.send.redux.states.SendState - -/** -[REDACTED_AUTHOR] - */ -class FeeReducer : SendInternalReducer { - - override fun handle(action: SendScreenAction, sendState: SendState): SendState = when (action) { - is FeeActionUi -> handleUiAction(action, sendState, sendState.feeState) - is FeeAction -> handleAction(action, sendState, sendState.feeState) - else -> sendState - } - - private fun handleUiAction(action: FeeActionUi, sendState: SendState, state: FeeState): SendState { - val result = when (action) { - is FeeActionUi.ToggleControlsVisibility -> { - state.copy(controlsLayoutIsVisible = !state.controlsLayoutIsVisible) - } - is FeeActionUi.ChangeSelectedFee -> { - val currentFee = state.fees?.let { - createValueOfFeeAmount(action.feeType, it) - } - state.copy( - selectedFeeType = action.feeType, - currentFee = currentFee, - ) - } - is FeeActionUi.ChangeIncludeFee -> state.copy(feeIsIncluded = action.isIncluded) - } - return updateLastState(sendState.copy(feeState = result), result) - } - - private fun handleAction(action: FeeAction, sendState: SendState, state: FeeState): SendState { - val result = when (action) { - is FeeAction.RequestFee -> { - state.copy(progressState = ProgressState.Loading) - } - is FeeAction.ChangeLayoutVisibility -> { - fun getVisibility(current: Boolean, proposed: Boolean?): Boolean = proposed ?: current - state.copy( - mainLayoutIsVisible = getVisibility(state.mainLayoutIsVisible, action.main), - controlsLayoutIsVisible = getVisibility(state.controlsLayoutIsVisible, action.controls), - feeChipGroupIsVisible = getVisibility(state.feeChipGroupIsVisible, action.chipGroup), - ) - } - is FeeAction.FeeCalculation.SetFeeResult -> { - when (val fees = action.fee) { - is TransactionFee.Single -> { - val feeType = FeeType.SINGLE - val currentFee = createValueOfFeeAmount(feeType, fees) - - state.copy( - selectedFeeType = feeType, - fees = fees, - currentFee = currentFee, - feeIsApproximate = isFeeApproximate(sendState), - ) - } - is TransactionFee.Choosable -> { - val feeType = getCurrentFeeType(state) - val currentFee = createValueOfFeeAmount(feeType, fees) - - state.copy( - selectedFeeType = feeType, - fees = fees, - currentFee = currentFee, - feeIsApproximate = isFeeApproximate(sendState), - ) - } - }.copy( - progressState = ProgressState.Done, - ) - } - FeeAction.FeeCalculation.ClearResult -> { - state.copy( - fees = null, - currentFee = null, - progressState = ProgressState.Done, - ) - } - } - - return updateLastState(sendState.copy(feeState = result), result) - } - - private fun createValueOfFeeAmount(feeType: FeeType, transactionFee: TransactionFee): Fee { - return when (transactionFee) { - is TransactionFee.Single -> { - transactionFee.normal - } - is TransactionFee.Choosable -> { - when (feeType) { - FeeType.SINGLE -> transactionFee.normal - FeeType.LOW -> transactionFee.minimum - FeeType.NORMAL -> transactionFee.normal - FeeType.PRIORITY -> transactionFee.priority - } - } - } - } - - private fun isFeeApproximate(sendState: SendState): Boolean { - val blockchain = sendState.walletManager?.wallet?.blockchain ?: return false - - val amountType = sendState.amountState.typeOfAmount - return blockchain.isFeeApproximate(amountType) - } - - private fun getCurrentFeeType(state: FeeState): FeeType { - return if (state.selectedFeeType == FeeType.SINGLE) FeeType.NORMAL else state.selectedFeeType - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt deleted file mode 100644 index 8d1d46d4cd..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/ReceiptReducer.kt +++ /dev/null @@ -1,408 +0,0 @@ -package com.tangem.tap.features.send.redux.reducers - -import com.tangem.blockchain.common.AmountType -import com.tangem.blockchain.common.FeePaidCurrency -import com.tangem.blockchain.common.Wallet -import com.tangem.core.ui.utils.BigDecimalFormatter -import com.tangem.tap.common.extensions.scaleToFiat -import com.tangem.tap.common.extensions.stripZeroPlainString -import com.tangem.tap.features.send.redux.ReceiptAction.RefreshReceipt -import com.tangem.tap.features.send.redux.SendScreenAction -import com.tangem.tap.features.send.redux.states.* -import com.tangem.tap.store -import com.tangem.utils.StringsSigns.LOWER_SIGN -import java.math.BigDecimal - -/** -[REDACTED_AUTHOR] - */ -@Suppress("LargeClass") -class ReceiptReducer : SendInternalReducer { - - private lateinit var sendState: SendState - private lateinit var amountState: AmountState - private lateinit var feeState: FeeState - - override fun handle(action: SendScreenAction, sendState: SendState): SendState { - this.sendState = sendState - this.amountState = sendState.amountState - this.feeState = sendState.feeState - - return when (action) { - is RefreshReceipt -> handleRefresh(sendState.receiptState) - else -> sendState - } - } - - private fun handleRefresh(state: ReceiptState): SendState { - val wallet = sendState.walletManager?.wallet ?: return sendState - val feePaidCurrency = wallet.blockchain.feePaidCurrency() - - val layoutType = determineLayoutType(amountState.mainCurrency.type, amountState.typeOfAmount, feePaidCurrency) - val symbols = determineSymbols(wallet, amountState.typeOfAmount, feePaidCurrency) - val showBlank = !SendState.isReadyToSend() - val result = state.copy( - visibleTypeOfReceipt = layoutType, - mainCurrency = amountState.mainCurrency, - fiat = createFiatType(symbols, showBlank), - crypto = createCryptoType(symbols, showBlank), - tokenFiat = createTokenFiatType(symbols, showBlank), - tokenCrypto = createTokenCryptoType(symbols, showBlank), - customTokenFiat = createCustomTokenFiat(symbols, showBlank), - customTokenCrypto = createCustomTokenCrypto(symbols, showBlank), - sameCurrencyFiat = createSameCurrencyFiat(symbols, showBlank), - sameCurrencyCrypto = createSameCurrencyCrypto(symbols, showBlank), - ) - return updateLastState(sendState.copy(receiptState = result), result) - } - - private fun createFiatType(symbols: ReceiptSymbols, showBlank: Boolean): ReceiptFiat { - val feeCrypto = feeState.getCurrentFeeValue() - val feeFiat = convertToFiatPrecision(feeCrypto) - - if (showBlank) { - return ReceiptFiat("0", feeFiat, "0", "0", symbols) - } - - return if (feeState.feeIsIncluded) { - val amountFiat = convertToFiatPrecision(amountState.amountToSendCrypto.minus(feeCrypto)) - val totalFiat = convertToFiatPrecision(amountState.amountToSendCrypto) - ReceiptFiat( - amountFiat = amountFiat, - feeFiat = feeFiat, - totalFiat = totalFiat, - willSentCrypto = amountState.amountToSendCrypto.stripZeroPlainString(), - symbols = symbols, - ) - } else { - val totalAmountCrypto = amountState.amountToSendCrypto.plus(feeCrypto) - val amountFiat = convertToFiatPrecision(amountState.amountToSendCrypto) - val totalFiat = convertToFiatPrecision(totalAmountCrypto) - ReceiptFiat( - amountFiat = amountFiat, - feeFiat = feeFiat, - totalFiat = totalFiat, - willSentCrypto = totalAmountCrypto.stripZeroPlainString(), - symbols = symbols, - ) - } - } - - private fun createCryptoType(symbols: ReceiptSymbols, showBlank: Boolean): ReceiptCrypto { - val feeCrypto = feeState.getCurrentFeeValue() - val feeFiat = convertToFiatPrecision(feeCrypto) - if (showBlank) { - return ReceiptCrypto("0", feeCrypto.stripZeroPlainString(), "0", "0", "0", symbols) - } - - if (feeState.feeIsIncluded) { - return ReceiptCrypto( - amountCrypto = amountState.amountToSendCrypto.minus(feeCrypto).stripZeroPlainString(), - feeCrypto = feeCrypto.stripZeroPlainString().addPrecisionSign(), - totalCrypto = amountState.amountToSendCrypto.stripZeroPlainString(), - feeFiat = feeFiat, - willSentFiat = convertToFiatPrecision(amountState.amountToSendCrypto), - symbols = symbols, - ) - } else { - val totalCrypto = amountState.amountToSendCrypto.plus(feeCrypto) - return ReceiptCrypto( - amountCrypto = amountState.amountToSendCrypto.stripZeroPlainString(), - feeCrypto = feeCrypto.stripZeroPlainString().addPrecisionSign(), - totalCrypto = totalCrypto.stripZeroPlainString(), - feeFiat = feeFiat, - willSentFiat = convertToFiatPrecision(totalCrypto), - symbols = symbols, - ) - } - } - - private fun createTokenFiatType(symbols: ReceiptSymbols, showBlank: Boolean): ReceiptTokenFiat { - val feeCoin = feeState.getCurrentFeeValue() - if (showBlank) { - val feeFiat = convertToFiatPrecision(feeCoin) - return ReceiptTokenFiat("0", feeFiat, "0", "0", "0", symbols) - } - - val tokensToSend = amountState.amountToSendCrypto - - return if (sendState.coinIsConvertible() && sendState.tokenIsConvertible()) { - val feeFiat = sendState.coinConverter!!.toFiatUnscaled(feeCoin) - val amountFiat = sendState.tokenConverter!!.toFiatUnscaled(tokensToSend) - val totalFiat = amountFiat.plus(feeFiat) - ReceiptTokenFiat( - amountFiat = amountFiat.scaleToFiat(true).stripZeroPlainString(), - feeFiat = feeFiat.scaleToFiat(true).stripZeroPlainString().addPrecisionSign(), - totalFiat = totalFiat.scaleToFiat(true).stripZeroPlainString(), - willSentToken = tokensToSend.stripZeroPlainString(), - willSentFeeCoin = feeCoin.stripZeroPlainString(), - symbols = symbols, - ) - } else { - ReceiptTokenFiat( - amountFiat = BigDecimalFormatter.EMPTY_BALANCE_SIGN, - feeFiat = BigDecimalFormatter.EMPTY_BALANCE_SIGN, - totalFiat = BigDecimalFormatter.EMPTY_BALANCE_SIGN, - willSentToken = tokensToSend.stripZeroPlainString(), - willSentFeeCoin = feeCoin.stripZeroPlainString(), - symbols = symbols, - ) - } - } - - private fun createTokenCryptoType(symbols: ReceiptSymbols, showBlank: Boolean): ReceiptTokenCrypto { - val feeCoin = feeState.getCurrentFeeValue() - if (showBlank) { - return ReceiptTokenCrypto("0", feeCoin.stripZeroPlainString(), "0", symbols) - } - val tokensToSend = amountState.amountToSendCrypto - - return if (sendState.coinIsConvertible() && sendState.tokenIsConvertible()) { - val tokenFiat = sendState.tokenConverter!!.toFiatUnscaled(tokensToSend) - val feeFiat = sendState.coinConverter!!.toFiatUnscaled(feeCoin) - val totalFiat = tokenFiat.plus(feeFiat) - - ReceiptTokenCrypto( - amountToken = tokensToSend.stripZeroPlainString(), - feeCoin = feeCoin.stripZeroPlainString().addPrecisionSign(), - totalFiat = totalFiat.scaleToFiat(true).stripZeroPlainString(), - symbols = symbols, - ) - } else { - ReceiptTokenCrypto( - amountToken = tokensToSend.stripZeroPlainString(), - feeCoin = feeCoin.stripZeroPlainString().addPrecisionSign(), - totalFiat = BigDecimalFormatter.EMPTY_BALANCE_SIGN, - symbols = symbols, - ) - } - } - - private fun createCustomTokenCrypto(symbols: ReceiptSymbols, showBlank: Boolean): ReceiptTokenCrypto { - val feeValue = feeState.getCurrentFeeValue() - if (showBlank) { - return ReceiptTokenCrypto("0", feeValue.stripZeroPlainString(), "0", symbols) - } - val tokensToSend = amountState.amountToSendCrypto - val amountType = amountState.typeOfAmount - val currencyConverter = when { - amountType is AmountType.Token && sendState.feeIsConvertible() -> sendState.tokenConverter - amountType is AmountType.Coin && sendState.feeIsConvertible() -> sendState.coinConverter - else -> null - } - - return if (currencyConverter != null) { - val currencyFiat = currencyConverter.toFiatUnscaled(tokensToSend) - val feeFiat = sendState.customFeeConverter!!.toFiatUnscaled(feeValue) - val totalFiat = currencyFiat.plus(feeFiat) - - ReceiptTokenCrypto( - amountToken = tokensToSend.stripZeroPlainString(), - feeCoin = feeValue.stripZeroPlainString().addPrecisionSign(), - totalFiat = totalFiat.scaleToFiat(true).stripZeroPlainString(), - symbols = symbols, - ) - } else { - ReceiptTokenCrypto( - amountToken = tokensToSend.stripZeroPlainString(), - feeCoin = feeValue.stripZeroPlainString().addPrecisionSign(), - totalFiat = BigDecimalFormatter.EMPTY_BALANCE_SIGN, - symbols = symbols, - ) - } - } - - private fun createCustomTokenFiat(symbols: ReceiptSymbols, showBlank: Boolean): ReceiptTokenFiat { - val feeCoin = feeState.getCurrentFeeValue() - if (showBlank) { - val feeFiat = convertToFiatPrecision(feeCoin) - return ReceiptTokenFiat("0", feeFiat, "0", "0", "0", symbols) - } - - val tokensToSend = amountState.amountToSendCrypto - - val amountType = amountState.typeOfAmount - val currencyConverter = when { - amountType is AmountType.Token && sendState.feeIsConvertible() -> sendState.tokenConverter - amountType is AmountType.Coin && sendState.feeIsConvertible() -> sendState.coinConverter - else -> null - } - - return if (currencyConverter != null) { - val feeFiat = sendState.customFeeConverter!!.toFiatUnscaled(feeCoin) - val amountFiat = currencyConverter.toFiatUnscaled(tokensToSend) - val totalFiat = amountFiat.plus(feeFiat) - ReceiptTokenFiat( - amountFiat = amountFiat.scaleToFiat(true).stripZeroPlainString(), - feeFiat = feeFiat.scaleToFiat(true).stripZeroPlainString().addPrecisionSign(), - totalFiat = totalFiat.scaleToFiat(true).stripZeroPlainString(), - willSentToken = tokensToSend.scaleToFiat(true).stripZeroPlainString(), - willSentFeeCoin = feeCoin.stripZeroPlainString(), - symbols = symbols, - ) - } else { - ReceiptTokenFiat( - amountFiat = BigDecimalFormatter.EMPTY_BALANCE_SIGN, - feeFiat = BigDecimalFormatter.EMPTY_BALANCE_SIGN, - totalFiat = BigDecimalFormatter.EMPTY_BALANCE_SIGN, - willSentToken = tokensToSend.stripZeroPlainString(), - willSentFeeCoin = feeCoin.stripZeroPlainString(), - symbols = symbols, - ) - } - } - - private fun createSameCurrencyCrypto(symbols: ReceiptSymbols, showBlank: Boolean): ReceiptCrypto { - val feeCrypto = feeState.getCurrentFeeValue() - val feeFiat = convertToFiatPrecision(feeCrypto) - if (showBlank) { - return ReceiptCrypto("0", feeCrypto.stripZeroPlainString(), "0", "0", "0", symbols) - } - val isToken = amountState.typeOfAmount is AmountType.Token - - if (feeState.feeIsIncluded) { - return ReceiptCrypto( - amountCrypto = amountState.amountToSendCrypto.minus(feeCrypto).stripZeroPlainString(), - feeCrypto = feeCrypto.stripZeroPlainString().addPrecisionSign(), - totalCrypto = amountState.amountToSendCrypto.stripZeroPlainString(), - feeFiat = feeFiat, - willSentFiat = convertToFiatPrecision(value = amountState.amountToSendCrypto, isToken = isToken), - symbols = symbols, - ) - } else { - val totalCrypto = amountState.amountToSendCrypto.plus(feeCrypto) - return ReceiptCrypto( - amountCrypto = amountState.amountToSendCrypto.stripZeroPlainString(), - feeCrypto = feeCrypto.stripZeroPlainString().addPrecisionSign(), - totalCrypto = totalCrypto.stripZeroPlainString(), - feeFiat = feeFiat, - willSentFiat = convertToFiatPrecision(value = totalCrypto, isToken = isToken), - symbols = symbols, - ) - } - } - - private fun createSameCurrencyFiat(symbols: ReceiptSymbols, showBlank: Boolean): ReceiptFiat { - val feeCrypto = feeState.getCurrentFeeValue() - val isToken = amountState.typeOfAmount is AmountType.Token - val feeFiat = convertToFiatPrecision(feeCrypto, isToken = isToken) - - if (showBlank) { - return ReceiptFiat("0", feeFiat, "0", "0", symbols) - } - - return if (feeState.feeIsIncluded) { - val amountFiat = convertToFiatPrecision(amountState.amountToSendCrypto.minus(feeCrypto), isToken = isToken) - val totalFiat = convertToFiatPrecision(amountState.amountToSendCrypto, isToken = isToken) - ReceiptFiat( - amountFiat = amountFiat, - feeFiat = feeFiat, - totalFiat = totalFiat, - willSentCrypto = amountState.amountToSendCrypto.scaleToFiat(true).stripZeroPlainString(), - symbols = symbols, - ) - } else { - val totalAmountCrypto = amountState.amountToSendCrypto.plus(feeCrypto) - val amountFiat = convertToFiatPrecision(amountState.amountToSendCrypto, isToken = isToken) - val totalFiat = convertToFiatPrecision(totalAmountCrypto, isToken = isToken) - ReceiptFiat( - amountFiat = amountFiat, - feeFiat = feeFiat, - totalFiat = totalFiat, - willSentCrypto = totalAmountCrypto.scaleToFiat().stripZeroPlainString(), - symbols = symbols, - ) - } - } - - private fun determineSymbols( - wallet: Wallet, - amountType: AmountType, - feePaidCurrency: FeePaidCurrency, - ): ReceiptSymbols { - return ReceiptSymbols( - fiat = store.state.globalState.appCurrency.code, - crypto = wallet.blockchain.currency, - token = when (amountType) { - is AmountType.Token -> amountType.token.symbol - else -> null - }, - fee = when (feePaidCurrency) { - FeePaidCurrency.Coin -> wallet.blockchain.currency - FeePaidCurrency.SameCurrency -> store.state.sendState.currency?.symbol - is FeePaidCurrency.Token -> feePaidCurrency.token.symbol - is FeePaidCurrency.FeeResource -> feePaidCurrency.currency - }, - ) - } - - private fun determineLayoutType( - mainCurrencyType: MainCurrencyType, - amountType: AmountType, - feePaidCurrency: FeePaidCurrency, - ): ReceiptLayoutType { - return when (mainCurrencyType) { - MainCurrencyType.FIAT -> determineFiatLayoutType(feePaidCurrency, amountType) - MainCurrencyType.CRYPTO -> determineCryptoLayoutType(feePaidCurrency, amountType) - } - } - - private fun determineFiatLayoutType(feePaidCurrency: FeePaidCurrency, amountType: AmountType): ReceiptLayoutType { - return when (feePaidCurrency) { - is FeePaidCurrency.Token -> { - val amountToken = (amountType as? AmountType.Token)?.token - val sameToken = - feePaidCurrency.token.contractAddress.equals(amountToken?.contractAddress, ignoreCase = true) - if (sameToken) ReceiptLayoutType.SAME_CURRENCY_FIAT else ReceiptLayoutType.FEE_IN_CUSTOM_TOKEN_FIAT - } - FeePaidCurrency.Coin -> when (amountType) { - AmountType.Coin -> ReceiptLayoutType.FIAT - is AmountType.Token -> ReceiptLayoutType.TOKEN_FIAT - is AmountType.FeeResource, - AmountType.Reserve, - -> ReceiptLayoutType.UNKNOWN - } - FeePaidCurrency.SameCurrency -> ReceiptLayoutType.SAME_CURRENCY_FIAT - is FeePaidCurrency.FeeResource -> ReceiptLayoutType.SAME_CURRENCY - } - } - - private fun determineCryptoLayoutType(feePaidCurrency: FeePaidCurrency, amountType: AmountType): ReceiptLayoutType { - return when (feePaidCurrency) { - is FeePaidCurrency.Token -> { - val amountToken = (amountType as? AmountType.Token)?.token - val sameToken = - feePaidCurrency.token.contractAddress.equals(amountToken?.contractAddress, ignoreCase = true) - if (sameToken) ReceiptLayoutType.SAME_CURRENCY else ReceiptLayoutType.FEE_IN_CUSTOM_TOKEN - } - FeePaidCurrency.SameCurrency -> ReceiptLayoutType.SAME_CURRENCY - FeePaidCurrency.Coin -> when (amountType) { - AmountType.Coin -> ReceiptLayoutType.CRYPTO - is AmountType.Token -> ReceiptLayoutType.TOKEN_CRYPTO - is AmountType.FeeResource, - AmountType.Reserve, - -> ReceiptLayoutType.UNKNOWN - } - is FeePaidCurrency.FeeResource -> ReceiptLayoutType.UNKNOWN - } - } - - private fun convertToFiatPrecision(value: BigDecimal, isToken: Boolean = false): String { - return when { - !isToken && sendState.coinIsConvertible() -> { - sendState.coinConverter!!.toFiatWithPrecision(value).stripZeroPlainString() - } - isToken && sendState.tokenIsConvertible() -> { - sendState.tokenConverter!!.toFiatWithPrecision(value).stripZeroPlainString() - } - else -> { - BigDecimalFormatter.EMPTY_BALANCE_SIGN - } - } - } - - private fun String.addPrecisionSign(): String { - val result = if (feeState.feeIsApproximate) "$LOWER_SIGN $this" else this - return result.trim() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt deleted file mode 100644 index 9ac9254fec..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt +++ /dev/null @@ -1,133 +0,0 @@ -package com.tangem.tap.features.send.redux.reducers - -import com.tangem.blockchain.common.AmountType -import com.tangem.blockchain.common.FeePaidCurrency -import com.tangem.tap.common.CurrencyConverter -import com.tangem.tap.common.entities.IndeterminateProgressButton -import com.tangem.tap.features.send.redux.* -import com.tangem.tap.features.send.redux.states.ExternalTransactionData -import com.tangem.tap.features.send.redux.states.IdStateHolder -import com.tangem.tap.features.send.redux.states.SendState -import org.rekotlin.Action -import java.math.BigDecimal - -/** -[REDACTED_AUTHOR] - */ -interface SendInternalReducer { - fun handle(action: SendScreenAction, sendState: SendState): SendState -} - -object SendScreenReducer { - fun reduce(incomingAction: Action, sendState: SendState): SendState { - if (incomingAction is ReleaseSendState) return SendState() - val action = incomingAction as? SendScreenAction ?: return sendState - - val reducer: SendInternalReducer = when (action) { - is PrepareSendScreen -> PrepareSendScreenStatesReducer() - is AddressActionUi, is AddressVerifyAction -> AddressReducer() - is TransactionExtrasAction -> TransactionExtrasReducer() - is AmountActionUi, is AmountAction -> AmountReducer() - is FeeActionUi, is FeeAction -> FeeReducer() - is ReceiptAction -> ReceiptReducer() - is SendAction -> SendReducer() - else -> EmptyReducer() - } - - return reducer.handle(action, sendState) - } -} - -private class SendReducer : SendInternalReducer { - override fun handle(action: SendScreenAction, sendState: SendState): SendState { - val result = when (action) { - is SendAction.ChangeSendButtonState -> { - sendState.copy(sendButtonState = IndeterminateProgressButton(action.state)) - } - is SendAction.Dialog.TezosWarningDialog -> sendState.copy(dialog = action) - is SendAction.Dialog.KaspaWarningDialog -> sendState.copy(dialog = action) - is SendAction.Dialog.ChiaWarningDialog -> sendState.copy(dialog = action) - is SendAction.Dialog.SendTransactionFails.CardSdkError -> sendState.copy(dialog = action) - is SendAction.Dialog.SendTransactionFails.BlockchainSdkError -> sendState.copy(dialog = action) - is SendAction.Dialog.RequestFeeError -> sendState.copy(dialog = action) - is SendAction.Dialog.Hide -> sendState.copy(dialog = null) - is SendAction.Warnings.Set -> sendState.copy(sendWarningsList = action.warningList) - is SendAction.SendSpecificTransaction -> handleSendSpecificTransactionAction(action, sendState) - is SendAction.SendSuccess -> sendState.copy(isSuccessSend = true) - else -> return sendState - } - - return updateLastState(result, result) - } - - private fun handleSendSpecificTransactionAction( - action: SendAction.SendSpecificTransaction, - state: SendState, - ): SendState { - return state.copy( - externalTransactionData = - ExternalTransactionData(action.sendAmount, action.destinationAddress, action.transactionId), - feeState = state.feeState.copy( - includeFeeSwitcherIsEnabled = false, - ), - amountState = state.amountState.copy( - inputIsEnabled = false, - ), - addressState = state.addressState.copy( - inputIsEnabled = false, - ), - ) - } -} - -private class EmptyReducer : SendInternalReducer { - override fun handle(action: SendScreenAction, sendState: SendState): SendState = sendState -} - -private class PrepareSendScreenStatesReducer : SendInternalReducer { - override fun handle(action: SendScreenAction, sendState: SendState): SendState { - val prepareAction = action as PrepareSendScreen - val walletManager = action.walletManager - val amountToExtract = prepareAction.tokenAmount ?: prepareAction.coinAmount!! - val decimals = amountToExtract.decimals - val canIncludeFee = canIncludeFee( - typeOfAmount = amountToExtract.type, - feePaidCurrency = action.feePaidCurrency, - ) - - return sendState.copy( - walletManager = walletManager, - coinConverter = action.coinRate?.let { CurrencyConverter(it, decimals) }, - tokenConverter = action.tokenRate?.let { CurrencyConverter(it, decimals) }, - customFeeConverter = action.feeCurrencyRate?.let { CurrencyConverter(it, action.feeCurrencyDecimals) }, - amountState = sendState.amountState.copy( - amountToExtract = amountToExtract, - typeOfAmount = amountToExtract.type, - balanceCrypto = amountToExtract.value ?: BigDecimal.ZERO, - ), - feeState = sendState.feeState.copy(includeFeeSwitcherIsEnabled = canIncludeFee), - canIncludeFee = canIncludeFee, - currency = action.currency, - ) - } - - private fun canIncludeFee(typeOfAmount: AmountType, feePaidCurrency: FeePaidCurrency): Boolean { - return when (feePaidCurrency) { - FeePaidCurrency.Coin -> typeOfAmount == AmountType.Coin - FeePaidCurrency.SameCurrency -> true - is FeePaidCurrency.Token -> { - val sendToken = (typeOfAmount as? AmountType.Token)?.token ?: return false - - sendToken.contractAddress.equals(feePaidCurrency.token.contractAddress, ignoreCase = true) && - sendToken.name.equals(feePaidCurrency.token.name, ignoreCase = true) && - sendToken.symbol.equals(feePaidCurrency.token.symbol, ignoreCase = true) - } - is FeePaidCurrency.FeeResource -> false - } - } -} - -internal fun updateLastState(sendState: SendState, lastChangedState: IdStateHolder): SendState { - sendState.lastChangedStates.add(lastChangedState.stateId) - return sendState -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/TransactionExtrasReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/TransactionExtrasReducer.kt deleted file mode 100644 index c781547049..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/TransactionExtrasReducer.kt +++ /dev/null @@ -1,201 +0,0 @@ -package com.tangem.tap.features.send.redux.reducers - -import com.tangem.blockchain.blockchains.stellar.StellarMemo -import com.tangem.blockchain.common.Blockchain -import com.tangem.tap.features.send.redux.SendScreenAction -import com.tangem.tap.features.send.redux.TransactionExtrasAction.* -import com.tangem.tap.features.send.redux.states.* - -/** -[REDACTED_AUTHOR] - */ -class TransactionExtrasReducer : SendInternalReducer { - override fun handle(action: SendScreenAction, sendState: SendState): SendState { - return when (action) { - is Prepare -> handleInitialization(action, sendState) - Release -> handleRelease(sendState) - is XlmMemo -> handleXlmMemo(action, sendState, sendState.transactionExtrasState) - is BinanceMemo -> handleBinanceMemo(action, sendState, sendState.transactionExtrasState) - is XrpDestinationTag -> handleXrpTag(action, sendState, sendState.transactionExtrasState) - is TonMemo -> handleTonMemo(action, sendState, sendState.transactionExtrasState) - is CosmosMemo -> handleCosmosMemo(action, sendState, sendState.transactionExtrasState) - is HederaMemo -> handleHederaMemo(action, sendState, sendState.transactionExtrasState) - is AlgorandMemo -> handleAlgorandMemo(action, sendState, sendState.transactionExtrasState) - else -> sendState - } - } - - private fun handleInitialization(action: Prepare, sendState: SendState): SendState { - val emptyResult = TransactionExtrasState() - val result = when (action.blockchain) { - Blockchain.XRP -> { - val address = action.walletAddress.substringAfter(":") - // 'r' - without tag, 'x' - with tag - if (address.startsWith("r", true)) { - val tag = action.xrpTag?.toLongOrNull() - if (tag == null) { - TransactionExtrasState(xrpDestinationTag = XrpDestinationTagState()) - } else { - TransactionExtrasState( - xrpDestinationTag = XrpDestinationTagState( - viewFieldValue = InputViewValue("$tag", false), - tag = tag, - ), - ) - } - } else { - emptyResult - } - } - Blockchain.Stellar -> TransactionExtrasState(xlmMemo = XlmMemoState()) - Blockchain.Binance -> TransactionExtrasState(binanceMemo = BinanceMemoState()) - Blockchain.TON, Blockchain.TONTestnet -> TransactionExtrasState(tonMemoState = TonMemoState()) - Blockchain.Cosmos, - Blockchain.TerraV1, - Blockchain.TerraV2, - -> TransactionExtrasState(cosmosMemoState = CosmosMemoState()) - Blockchain.Hedera, Blockchain.HederaTestnet -> TransactionExtrasState(hederaMemoState = HederaMemoState()) - Blockchain.Algorand, Blockchain.AlgorandTestnet -> TransactionExtrasState( - algorandMemoState = AlgorandMemoState(), - ) - else -> emptyResult - } - return updateLastState(sendState.copy(transactionExtrasState = result), result) - } - - private fun handleRelease(sendState: SendState): SendState { - val result = TransactionExtrasState() - return updateLastState(sendState.copy(transactionExtrasState = result), result) - } - - private fun handleXlmMemo(action: XlmMemo, sendState: SendState, infoState: TransactionExtrasState): SendState { - fun clearMemo(memo: XlmMemoState): XlmMemoState = memo.copy(text = null, id = null, error = null) - - val result = when (action) { - is XlmMemo.HandleUserInput -> { - val inputViewValue = InputViewValue(action.data, true) - var memo = infoState.xlmMemo?.copy(viewFieldValue = inputViewValue) - ?: XlmMemoState(inputViewValue) - memo = clearMemo(memo) - memo = when (memo.selectedMemoType) { - XlmMemoType.TEXT -> { - if (XlmMemoState.isAssignableValue(action.data)) { - memo.copy(text = StellarMemo.Text(action.data)) - } else { - memo.copy(error = TransactionExtraError.INVALID_XLM_MEMO) - } - } - XlmMemoType.ID -> { - if (XlmMemoState.isAssignableValue(action.data)) { - memo.copy(id = StellarMemo.Id(action.data.toBigInteger())) - } else { - memo.copy(error = TransactionExtraError.INVALID_XLM_MEMO) - } - } - } - infoState.copy(xlmMemo = memo) - } - } - return updateLastState(sendState.copy(transactionExtrasState = result), result) - } - - private fun handleBinanceMemo( - action: BinanceMemo, - sendState: SendState, - infoState: TransactionExtrasState, - ): SendState { - val result = when (action) { - is BinanceMemo.HandleUserInput -> { - val tag = action.data.toBigIntegerOrNull() - if (tag != null) { - val input = InputViewValue(action.data, true) - val tagState = BinanceMemoState(input, tag) - infoState.copy(binanceMemo = tagState) - } else { - infoState - } - } - } - return updateLastState(sendState.copy(transactionExtrasState = result), result) - } - - private fun handleXrpTag( - action: XrpDestinationTag, - sendState: SendState, - infoState: TransactionExtrasState, - ): SendState { - val result = when (action) { - is XrpDestinationTag.HandleUserInput -> { - val tag = action.data.toLongOrNull() - if (tag != null) { - val input = InputViewValue(action.data, true) - val tagState = if (tag <= XrpDestinationTagState.MAX_NUMBER) { - XrpDestinationTagState(input, tag) - } else { - XrpDestinationTagState(input, error = TransactionExtraError.INVALID_DESTINATION_TAG) - } - infoState.copy(xrpDestinationTag = tagState) - } else { - infoState - } - } - } - return updateLastState(sendState.copy(transactionExtrasState = result), result) - } - - private fun handleTonMemo(action: TonMemo, sendState: SendState, infoState: TransactionExtrasState): SendState { - val result = when (action) { - is TonMemo.HandleUserInput -> { - val memo = action.data - val input = InputViewValue(memo, true) - infoState.copy(tonMemoState = TonMemoState(input, memo)) - } - } - return updateLastState(sendState.copy(transactionExtrasState = result), result) - } - - private fun handleCosmosMemo( - action: CosmosMemo, - sendState: SendState, - infoState: TransactionExtrasState, - ): SendState { - val result = when (action) { - is CosmosMemo.HandleUserInput -> { - val memo = action.data - val input = InputViewValue(memo, true) - infoState.copy(cosmosMemoState = CosmosMemoState(input, memo)) - } - } - return updateLastState(sendState.copy(transactionExtrasState = result), result) - } - - private fun handleHederaMemo( - action: HederaMemo, - sendState: SendState, - infoState: TransactionExtrasState, - ): SendState { - val result = when (action) { - is HederaMemo.HandleUserInput -> { - val memo = action.data - val input = InputViewValue(memo, true) - infoState.copy(hederaMemoState = HederaMemoState(input, memo)) - } - } - return updateLastState(sendState.copy(transactionExtrasState = result), result) - } - - private fun handleAlgorandMemo( - action: AlgorandMemo, - sendState: SendState, - infoState: TransactionExtrasState, - ): SendState { - val result = when (action) { - is AlgorandMemo.HandleUserInput -> { - val memo = action.data - val input = InputViewValue(memo, true) - infoState.copy(algorandMemoState = AlgorandMemoState(input, memo)) - } - } - return updateLastState(sendState.copy(transactionExtrasState = result), result) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressState.kt deleted file mode 100644 index 3310a772ce..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressState.kt +++ /dev/null @@ -1,150 +0,0 @@ -package com.tangem.tap.features.send.redux.states - -import androidx.core.text.isDigitsOnly -import com.tangem.blockchain.blockchains.stellar.StellarMemo -import com.tangem.tap.features.send.redux.AddressVerifyAction -import java.math.BigInteger - -data class AddressState( - val viewFieldValue: InputViewValue = InputViewValue(""), - val normalFieldValue: String? = null, - val truncatedFieldValue: String? = null, - val destinationWalletAddress: String? = null, - val error: AddressVerifyAction.Error? = null, - val truncateHandler: ((String) -> String)? = null, - val pasteIsEnabled: Boolean = false, - val inputIsEnabled: Boolean = true, -) : SendScreenState { - - override val stateId: StateId = StateId.ADDRESS_PAY_ID - - fun truncate(value: String): String = truncateHandler?.invoke(value) ?: value - - fun isReady(): Boolean = error == null && destinationWalletAddress?.isNotEmpty() ?: false -} - -@Deprecated("Legacy") -data class TransactionExtrasState( - val xlmMemo: XlmMemoState? = null, - val binanceMemo: BinanceMemoState? = null, - val xrpDestinationTag: XrpDestinationTagState? = null, - val tonMemoState: TonMemoState? = null, - val cosmosMemoState: CosmosMemoState? = null, - val hederaMemoState: HederaMemoState? = null, - val algorandMemoState: AlgorandMemoState? = null, -) : IdStateHolder { - override val stateId: StateId = StateId.TRANSACTION_EXTRAS - - fun isNull(): Boolean { - return xlmMemo == null && binanceMemo == null && xrpDestinationTag == null && tonMemoState == null && - cosmosMemoState == null && hederaMemoState == null && algorandMemoState == null - } - - fun isEmpty(): Boolean { - val isXlmEmpty = xlmMemo?.viewFieldValue?.value?.isEmpty() ?: false - val isBinanceEmpty = binanceMemo?.viewFieldValue?.value?.isEmpty() ?: false - val isXrpEmpty = xrpDestinationTag?.viewFieldValue?.value?.isEmpty() ?: false - val isTonEmpty = tonMemoState?.viewFieldValue?.value?.isEmpty() ?: false - val isCosmosEmpty = cosmosMemoState?.viewFieldValue?.value?.isEmpty() ?: false - val isHederaEmpty = hederaMemoState?.viewFieldValue?.value?.isEmpty() ?: false - val isAlgorandEmpty = algorandMemoState?.viewFieldValue?.value?.isEmpty() ?: false - - return isXlmEmpty || - isBinanceEmpty || - isXrpEmpty || - isTonEmpty || - isCosmosEmpty || - isHederaEmpty || - isAlgorandEmpty - } -} - -enum class XlmMemoType { - TEXT, ID -} - -data class XlmMemoState( - val viewFieldValue: InputViewValue = InputViewValue(""), - val text: StellarMemo.Text? = null, - val id: StellarMemo.Id? = null, - val error: TransactionExtraError? = null, -) { - val memo: StellarMemo? - get() = when (selectedMemoType) { - XlmMemoType.TEXT -> text - XlmMemoType.ID -> id - } - - val selectedMemoType: XlmMemoType - get() = determineMemoType(viewFieldValue.value) - - companion object { - fun determineMemoType(value: String): XlmMemoType = when { - value.isNotEmpty() && value.isDigitsOnly() -> XlmMemoType.ID - else -> XlmMemoType.TEXT - } - - @Suppress("MagicNumber") - fun isAssignableValue(value: String): Boolean = when (determineMemoType(value)) { - XlmMemoType.TEXT -> { - // from org.stellar.sdk.MemoText - value.toByteArray().size <= 28 - } - XlmMemoType.ID -> { - try { - // from com.tangem.blockchain.blockchains.stellar.StellarMemo.toStellarSdkMemo - value.toBigInteger() in BigInteger.ZERO..Long.MAX_VALUE.toBigInteger() * 2.toBigInteger() - } catch (ex: NumberFormatException) { - false - } - } - } - } -} - -data class BinanceMemoState( - val viewFieldValue: InputViewValue = InputViewValue(""), - val memo: BigInteger? = null, - val error: TransactionExtraError? = null, -) - -// tag must contains only digits -data class XrpDestinationTagState( - val viewFieldValue: InputViewValue = InputViewValue(""), - val tag: Long? = null, - val error: TransactionExtraError? = null, -) { - companion object { - const val MAX_NUMBER: Long = 4294967295 - } -} - -data class TonMemoState( - val viewFieldValue: InputViewValue = InputViewValue(""), - val memo: String? = null, - val error: TransactionExtraError? = null, -) - -data class CosmosMemoState( - val viewFieldValue: InputViewValue = InputViewValue(""), - val memo: String? = null, - val error: TransactionExtraError? = null, -) - -data class HederaMemoState( - val viewFieldValue: InputViewValue = InputViewValue(""), - val memo: String? = null, - val error: TransactionExtraError? = null, -) - -data class AlgorandMemoState( - val viewFieldValue: InputViewValue = InputViewValue(""), - val memo: String? = null, - val error: TransactionExtraError? = null, -) - -enum class TransactionExtraError { - INVALID_DESTINATION_TAG, - INVALID_XLM_MEMO, - INVALID_BINANCE_MEMO, -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt deleted file mode 100644 index 27ae5f8a33..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/FeeState.kt +++ /dev/null @@ -1,43 +0,0 @@ -package com.tangem.tap.features.send.redux.states - -import com.tangem.blockchain.common.transaction.Fee -import com.tangem.blockchain.common.transaction.TransactionFee -import com.tangem.core.analytics.models.AnalyticsParam -import com.tangem.tap.common.entities.ProgressState -import java.math.BigDecimal - -/** -[REDACTED_AUTHOR] - */ -enum class FeeType { - SINGLE, LOW, NORMAL, PRIORITY; -} - -fun FeeType.convertToAnalyticsFeeType(): AnalyticsParam.FeeType { - return when (this) { - FeeType.SINGLE -> AnalyticsParam.FeeType.Fixed - FeeType.LOW -> AnalyticsParam.FeeType.Min - FeeType.NORMAL -> AnalyticsParam.FeeType.Normal - FeeType.PRIORITY -> AnalyticsParam.FeeType.Max - } -} - -data class FeeState( - val selectedFeeType: FeeType = FeeType.NORMAL, - val fees: TransactionFee? = null, - val currentFee: Fee? = null, - val feeIsIncluded: Boolean = false, - val feeIsApproximate: Boolean = false, - val mainLayoutIsVisible: Boolean = false, - val controlsLayoutIsVisible: Boolean = false, - val feeChipGroupIsVisible: Boolean = true, - val includeFeeSwitcherIsEnabled: Boolean = true, - val progressState: ProgressState = ProgressState.Done, -) : SendScreenState { - - override val stateId: StateId = StateId.FEE - - fun isReady(): Boolean = currentFee != null - - fun getCurrentFeeValue(): BigDecimal = currentFee?.amount?.value ?: BigDecimal.ZERO -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/ReceiptState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/ReceiptState.kt deleted file mode 100644 index f23d760f1a..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/ReceiptState.kt +++ /dev/null @@ -1,69 +0,0 @@ -package com.tangem.tap.features.send.redux.states - -// Shows only one type of the layout -enum class ReceiptLayoutType { - UNKNOWN, - FIAT, - CRYPTO, - TOKEN_FIAT, - TOKEN_CRYPTO, - FEE_IN_CUSTOM_TOKEN, - FEE_IN_CUSTOM_TOKEN_FIAT, - SAME_CURRENCY, - SAME_CURRENCY_FIAT, -} - -data class ReceiptState( - val visibleTypeOfReceipt: ReceiptLayoutType? = null, - val fiat: ReceiptFiat? = null, - val crypto: ReceiptCrypto? = null, - val tokenFiat: ReceiptTokenFiat? = null, - val tokenCrypto: ReceiptTokenCrypto? = null, - val customTokenFiat: ReceiptTokenFiat? = null, - val customTokenCrypto: ReceiptTokenCrypto? = null, - val sameCurrencyFiat: ReceiptFiat? = null, - val sameCurrencyCrypto: ReceiptCrypto? = null, - val mainCurrency: MainCurrency? = null, -) : SendScreenState { - override val stateId: StateId = StateId.RECEIPT -} - -data class ReceiptSymbols( - val fiat: String, - val crypto: String, - val token: String? = null, - val fee: String? = null, -) - -data class ReceiptFiat( - val amountFiat: String, - val feeFiat: String, - val totalFiat: String, - val willSentCrypto: String, - val symbols: ReceiptSymbols, -) - -data class ReceiptCrypto( - val amountCrypto: String, - val feeCrypto: String, - val totalCrypto: String, - val feeFiat: String, - val willSentFiat: String, - val symbols: ReceiptSymbols, -) - -data class ReceiptTokenCrypto( - val amountToken: String, - val feeCoin: String, - val totalFiat: String, - val symbols: ReceiptSymbols, -) - -data class ReceiptTokenFiat( - val amountFiat: String, - val feeFiat: String, - val totalFiat: String, - val willSentToken: String, - val willSentFeeCoin: String, - val symbols: ReceiptSymbols, -) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt deleted file mode 100644 index 115ab92034..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/SendState.kt +++ /dev/null @@ -1,196 +0,0 @@ -package com.tangem.tap.features.send.redux.states - -import com.tangem.blockchain.common.Amount -import com.tangem.blockchain.common.AmountType -import com.tangem.blockchain.common.WalletManager -import com.tangem.common.extensions.isZero -import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.redux.StateDialog -import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.tap.common.CurrencyConverter -import com.tangem.tap.common.entities.IndeterminateProgressButton -import com.tangem.tap.common.text.DecimalDigitsInputFilter -import com.tangem.tap.domain.TapError -import com.tangem.tap.domain.configurable.warningMessage.WarningMessage -import com.tangem.tap.store -import org.rekotlin.StateType -import java.math.BigDecimal - -/** -[REDACTED_AUTHOR] - */ - -interface IdStateHolder { - val stateId: StateId -} - -enum class StateId { - SEND_SCREEN, ADDRESS_PAY_ID, TRANSACTION_EXTRAS, AMOUNT, FEE, RECEIPT -} - -interface SendScreenState : StateType, IdStateHolder - -data class SendState( - val walletManager: WalletManager? = null, - val currency: CryptoCurrency? = null, - val coinConverter: CurrencyConverter? = null, - val tokenConverter: CurrencyConverter? = null, - val customFeeConverter: CurrencyConverter? = null, - val lastChangedStates: LinkedHashSet = linkedSetOf(), - val addressState: AddressState = AddressState(), - val transactionExtrasState: TransactionExtrasState = TransactionExtrasState(), - val amountState: AmountState = AmountState(), - val feeState: FeeState = FeeState(), - val receiptState: ReceiptState = ReceiptState(), - val sendWarningsList: List = listOf(), - val sendButtonState: IndeterminateProgressButton = IndeterminateProgressButton(ButtonState.DISABLED), - val dialog: StateDialog? = null, - val externalTransactionData: ExternalTransactionData? = null, - val isSuccessSend: Boolean = false, - val canIncludeFee: Boolean = false, -) : SendScreenState { - - override val stateId: StateId = StateId.SEND_SCREEN - - fun getDecimals(type: MainCurrencyType): Int = when (type) { - MainCurrencyType.FIAT -> 2 - MainCurrencyType.CRYPTO -> amountState.amountToExtract?.decimals ?: 0 - } - - private fun convertFiatToCoin(value: BigDecimal): BigDecimal { - return if (!this.coinIsConvertible()) value else coinConverter!!.toCrypto(value) - } - - private fun convertFiatToToken(value: BigDecimal): BigDecimal { - return if (!this.tokenIsConvertible()) value else tokenConverter!!.toCrypto(value) - } - - private fun convertCoinToFiat(value: BigDecimal, scaleWithPrecision: Boolean = false): BigDecimal { - if (!this.coinIsConvertible()) return value - - val converter = coinConverter!! - return if (!scaleWithPrecision) converter.toFiat(value) else converter.toFiatWithPrecision(value) - } - - private fun convertTokenToFiat(value: BigDecimal, scaleWithPrecision: Boolean = false): BigDecimal { - if (!this.tokenIsConvertible()) return value - - val converter = tokenConverter!! - return if (!scaleWithPrecision) converter.toFiat(value) else converter.toFiatWithPrecision(value) - } - - fun convertFiatToExtractCrypto(fiatValue: BigDecimal): BigDecimal = when (amountState.typeOfAmount) { - AmountType.Coin -> convertFiatToCoin(fiatValue) - is AmountType.Token -> convertFiatToToken(fiatValue) - is AmountType.FeeResource, - AmountType.Reserve, - -> fiatValue - } - - fun convertExtractCryptoToFiat(cryptoValue: BigDecimal, scaleWithPrecision: Boolean = false): BigDecimal { - return when (amountState.typeOfAmount) { - AmountType.Coin -> convertCoinToFiat(cryptoValue, scaleWithPrecision) - is AmountType.Token -> convertTokenToFiat(cryptoValue, scaleWithPrecision) - is AmountType.FeeResource, - AmountType.Reserve, - -> cryptoValue - } - } - - fun getButtonState(): ButtonState = if (isReadyToSend()) ButtonState.ENABLED else ButtonState.DISABLED - - fun getTotalAmountToSend(value: BigDecimal = amountState.amountToSendCrypto): BigDecimal { - val needToExtractFee = amountState.canIncludeFee() && feeState.feeIsIncluded - return if (needToExtractFee) value.minus(feeState.getCurrentFeeValue()) else value - } - - fun coinIsConvertible(): Boolean = coinConverter != null - fun tokenIsConvertible(): Boolean = tokenConverter != null - fun feeIsConvertible(): Boolean = customFeeConverter != null - - fun mainCurrencyCanBeSwitched(): Boolean { - return when (amountState.typeOfAmount) { - AmountType.Coin -> coinIsConvertible() - is AmountType.Token -> tokenIsConvertible() - is AmountType.FeeResource, - AmountType.Reserve, - -> false - } - } - - companion object { - private fun addressIsReady(): Boolean = store.state.sendState.addressState.isReady() - - private fun amountIsReady(): Boolean = store.state.sendState.amountState.isReady() - - fun isReadyToRequestFee(): Boolean = addressIsReady() && amountIsReady() - - fun isReadyToSend(): Boolean = addressIsReady() && amountIsReady() && - store.state.sendState.feeState.isReady() - } -} - -enum class ButtonState { - ENABLED, DISABLED, PROGRESS -} - -data class AmountState( - val amountToExtract: Amount? = null, - val typeOfAmount: AmountType = AmountType.Coin, - val viewAmountValue: InputViewValue = InputViewValue(BigDecimal.ZERO.toPlainString()), - val viewBalanceValue: String = BigDecimal.ZERO.toPlainString(), - val mainCurrency: MainCurrency = MainCurrency(MainCurrencyType.FIAT, AppCurrency.Default.code), - val amountToSendCrypto: BigDecimal = BigDecimal.ZERO, - val balanceCrypto: BigDecimal = BigDecimal.ZERO, - val hideBalance: Boolean = false, - val cursorAtTheSamePosition: Boolean = true, - val maxLengthOfAmount: Int = 2, - val decimalSeparator: String = ".", - val error: TapError? = null, - val inputIsEnabled: Boolean = true, -) : SendScreenState { - - override val stateId: StateId = StateId.AMOUNT - - fun isReady(): Boolean = error == null && !amountToSendCrypto.isZero() - - fun canIncludeFee(): Boolean = store.state.sendState.canIncludeFee - - fun createMainCurrency(type: MainCurrencyType, canSwitched: Boolean): MainCurrency { - return if (!canSwitched) { - MainCurrency(type, amountToExtract?.currencySymbol ?: "NONE", false) - } else { - when (type) { - MainCurrencyType.FIAT -> MainCurrency(type, store.state.globalState.appCurrency.code) - MainCurrencyType.CRYPTO -> MainCurrency(type, amountToExtract?.currencySymbol ?: "NONE") - } - } - } - - fun toBigDecimalSeparator(value: String): String { - return value.replace(",", ".") - } - - fun restoreDecimalSeparator(value: String): String { - return DecimalDigitsInputFilter.setDecimalSeparator(value, decimalSeparator) - } -} - -data class InputViewValue(val value: String, val isFromUserInput: Boolean = false) -enum class MainCurrencyType { - FIAT, CRYPTO -} - -data class MainCurrency( - val type: MainCurrencyType, - val currencySymbol: String, - val isEnabled: Boolean = true, -) - -data class ExternalTransactionData( - val amount: String, - val destinationAddress: String, - val transactionId: String, - val canAmountBeModified: Boolean = false, - val canDestinationAddressBeModified: Boolean = false, -) \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/EditTextCustomPaste.kt b/app/src/main/java/com/tangem/tap/features/send/ui/EditTextCustomPaste.kt deleted file mode 100644 index d93147b24b..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/ui/EditTextCustomPaste.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.tangem.tap.features.send.ui - -import android.R -import android.content.Context -import android.util.AttributeSet -import com.google.android.material.textfield.TextInputEditText - -class EditTextCustomPaste : TextInputEditText { - - private var onSystemPasteButtonClickListener: (() -> Unit)? = null - - constructor(context: Context) : super(context) - - constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) - - constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) - - fun setOnSystemPasteButtonClickListener(callback: () -> Unit) { - onSystemPasteButtonClickListener = callback - } - - override fun onTextContextMenuItem(id: Int): Boolean { - val isConsumed = super.onTextContextMenuItem(id) - - if (id == R.id.paste) onSystemPasteButtonClickListener?.invoke() - - return isConsumed - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt deleted file mode 100644 index 34650ad800..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendFragment.kt +++ /dev/null @@ -1,407 +0,0 @@ -@file:Suppress("MagicNumber") - -package com.tangem.tap.features.send.ui - -import android.content.Context -import android.os.Bundle -import android.text.method.DigitsKeyListener -import android.view.View -import android.view.inputmethod.EditorInfo -import android.widget.EditText -import androidx.core.view.postDelayed -import androidx.core.widget.addTextChangedListener -import androidx.fragment.app.viewModels -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle -import androidx.recyclerview.widget.LinearLayoutManager -import androidx.recyclerview.widget.RecyclerView -import by.kirich1409.viewbindingdelegate.viewBinding -import com.google.android.material.textfield.TextInputEditText -import com.tangem.Message -import com.tangem.common.routing.AppRoute -import com.tangem.common.routing.AppRouter -import com.tangem.core.analytics.Analytics -import com.tangem.domain.appcurrency.model.AppCurrency -import com.tangem.domain.qrscanning.models.SourceType -import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase -import com.tangem.domain.qrscanning.usecases.ParseQrCodeUseCase -import com.tangem.domain.tokens.legacy.TradeCryptoAction -import com.tangem.sdk.extensions.hideSoftKeyboard -import com.tangem.tap.common.KeyboardObserver -import com.tangem.tap.common.analytics.events.Token -import com.tangem.tap.common.extensions.dispatchNavigationAction -import com.tangem.tap.common.extensions.getFromClipboard -import com.tangem.tap.common.extensions.setOnImeActionListener -import com.tangem.tap.common.recyclerView.SpaceItemDecoration -import com.tangem.tap.common.snackBar.MaxAmountSnackbar -import com.tangem.tap.common.text.truncateMiddleWith -import com.tangem.tap.common.toggleWidget.IndeterminateProgressButtonWidget -import com.tangem.tap.common.toggleWidget.ViewStateWidget -import com.tangem.tap.features.BaseStoreFragment -import com.tangem.tap.features.addBackPressHandler -import com.tangem.tap.features.send.redux.* -import com.tangem.tap.features.send.redux.AddressActionUi.* -import com.tangem.tap.features.send.redux.AmountActionUi.* -import com.tangem.tap.features.send.redux.FeeActionUi.* -import com.tangem.tap.features.send.redux.states.FeeType -import com.tangem.tap.features.send.redux.states.MainCurrencyType -import com.tangem.tap.features.send.ui.adapters.WarningMessagesAdapter -import com.tangem.tap.features.send.ui.stateSubscribers.SendStateSubscriber -import com.tangem.tap.store -import com.tangem.wallet.R -import com.tangem.wallet.databinding.FragmentSendBinding -import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.FlowPreview -import kotlinx.coroutines.channels.awaitClose -import kotlinx.coroutines.flow.* -import kotlinx.coroutines.launch -import java.text.DecimalFormatSymbols -import javax.inject.Inject - -private const val EDIT_TEXT_INPUT_DEBOUNCE = 400L - -/** -[REDACTED_AUTHOR] - */ -@Suppress("LargeClass") -@OptIn(FlowPreview::class) -@AndroidEntryPoint -class SendFragment : BaseStoreFragment(R.layout.fragment_send) { - - private val viewModel by viewModels() - - lateinit var sendBtn: ViewStateWidget - - private lateinit var etAmountToSend: TextInputEditText - private lateinit var warningsAdapter: WarningMessagesAdapter - - private val sendSubscriber = SendStateSubscriber(this) - private lateinit var keyboardObserver: KeyboardObserver - - val binding: FragmentSendBinding by viewBinding(FragmentSendBinding::bind) - - @Inject - lateinit var listenToQrScanningUseCase: ListenToQrScanningUseCase - - @Inject - lateinit var parseQrCodeUseCase: ParseQrCodeUseCase - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - lifecycle.addObserver(viewModel) - sendSubscriber.initViewModel(viewModel) - Analytics.send(Token.Send.ScreenOpened()) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - viewLifecycleOwner.lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.STARTED) { - subscribeToTransactionExtrasFields() - subscribeToAddressField() - subscribeToAmountField() - } - } - - addBackPressHandler(this) - - etAmountToSend = view.findViewById(R.id.etAmountToSend) - - initSendButtonStates() - setupAddressLayout() - setupAmountLayout() - setupFeeLayout() - setupWarningMessages() - store.dispatch(SendActionUi.CheckIfTransactionDataWasProvided) - } - - private fun initSendButtonStates() = with(binding) { - btnSend.setOnClickListener { - store.dispatch( - SendActionUi.SendAmountToRecipient( - Message(getString(R.string.initial_message_sign_header)), - ), - ) - } - sendBtn = IndeterminateProgressButtonWidget(btnSend, progress) - } - - private fun setupAddressLayout() = with(binding.lSendAddress) { - store.dispatch(SetTruncateHandler { etAddress.truncateMiddleWith(it, "...") }) - store.dispatch(CheckClipboard(requireContext().getFromClipboard()?.toString())) - - etAddress.apply { - setOnSystemPasteButtonClickListener { - store.dispatch( - PasteAddress( - data = requireContext().getFromClipboard()?.toString() ?: "", - sourceType = Token.Send.AddressEntered.SourceType.PastePopup, - ), - ) - } - - setOnFocusChangeListener { _, hasFocus -> - store.dispatch(TruncateOrRestore(!hasFocus)) - } - } - - imvPaste.setOnClickListener { - Analytics.send(Token.Send.ButtonPaste()) - store.dispatch( - PasteAddress( - data = requireContext().getFromClipboard()?.toString() ?: "", - sourceType = Token.Send.AddressEntered.SourceType.PasteButton, - ), - ) - store.dispatch(TruncateOrRestore(!etAddress.isFocused)) - } - imvQrCode.setOnClickListener { - Analytics.send(Token.Send.ButtonQRCode()) - - store.dispatchNavigationAction { - push(AppRoute.QrScanning(source = SourceType.SEND)) - } - } - } - - private fun CoroutineScope.subscribeToAddressField() = with(binding.lSendAddress) { - etAddress.inputtedTextAsFlow() - .debounce(EDIT_TEXT_INPUT_DEBOUNCE) - .filter { store.state.sendState.addressState.viewFieldValue.value != it } - .onEach { - store.dispatch(AddressActionUi.HandleUserInput(it)) - } - .launchIn(this@subscribeToAddressField) - } - - private fun CoroutineScope.subscribeToAmountField() { - etAmountToSend.inputtedTextAsFlow() - .debounce(EDIT_TEXT_INPUT_DEBOUNCE) - .filter { store.state.sendState.amountState.viewAmountValue.value != it && it.isNotEmpty() } - .onEach { store.dispatch(AmountActionUi.HandleUserInput(it)) } - .launchIn(this) - } - - private fun CoroutineScope.subscribeToTransactionExtrasFields() = with(binding.lSendAddress) { - // TODO: [REDACTED_TASK_KEY] - etXlmMemo.inputtedTextAsFlow() - .debounce(EDIT_TEXT_INPUT_DEBOUNCE) - .filter { - val info = store.state.sendState.transactionExtrasState - info.xlmMemo?.viewFieldValue?.value != it - } - .onEach { store.dispatch(TransactionExtrasAction.XlmMemo.HandleUserInput(it)) } - .launchIn(this@subscribeToTransactionExtrasFields) - - etDestinationTag.inputtedTextAsFlow() - .debounce(EDIT_TEXT_INPUT_DEBOUNCE) - .filter { - val info = store.state.sendState.transactionExtrasState - info.xrpDestinationTag?.viewFieldValue?.value != it - } - .onEach { store.dispatch(TransactionExtrasAction.XrpDestinationTag.HandleUserInput(it)) } - .launchIn(this@subscribeToTransactionExtrasFields) - - etBinanceMemo.inputtedTextAsFlow() - .debounce(EDIT_TEXT_INPUT_DEBOUNCE) - .filter { - val info = store.state.sendState.transactionExtrasState - info.binanceMemo?.viewFieldValue?.value != it - } - .onEach { store.dispatch(TransactionExtrasAction.BinanceMemo.HandleUserInput(it)) } - .launchIn(this@subscribeToTransactionExtrasFields) - - etTonMemo.inputtedTextAsFlow() - .debounce(EDIT_TEXT_INPUT_DEBOUNCE) - .filter { - val info = store.state.sendState.transactionExtrasState - info.tonMemoState?.viewFieldValue?.value != it - } - .onEach { store.dispatch(TransactionExtrasAction.TonMemo.HandleUserInput(it)) } - .launchIn(this@subscribeToTransactionExtrasFields) - - etCosmosMemo.inputtedTextAsFlow() - .debounce(EDIT_TEXT_INPUT_DEBOUNCE) - .filter { - val info = store.state.sendState.transactionExtrasState - info.cosmosMemoState?.viewFieldValue?.value != it - } - .onEach { store.dispatch(TransactionExtrasAction.CosmosMemo.HandleUserInput(it)) } - .launchIn(this@subscribeToTransactionExtrasFields) - - etHederaMemo.inputtedTextAsFlow() - .debounce(EDIT_TEXT_INPUT_DEBOUNCE) - .filter { - val info = store.state.sendState.transactionExtrasState - info.hederaMemoState?.viewFieldValue?.value != it - } - .onEach { store.dispatch(TransactionExtrasAction.HederaMemo.HandleUserInput(it)) } - .launchIn(this@subscribeToTransactionExtrasFields) - - etAlgorandMemo.inputtedTextAsFlow() - .debounce(EDIT_TEXT_INPUT_DEBOUNCE) - .filter { - val info = store.state.sendState.transactionExtrasState - info.algorandMemoState?.viewFieldValue?.value != it - } - .onEach { store.dispatch(TransactionExtrasAction.AlgorandMemo.HandleUserInput(it)) } - .launchIn(this@subscribeToTransactionExtrasFields) - } - - private fun setupAmountLayout() { - store.dispatch(SetMainCurrency(restoreMainCurrency())) - store.dispatch(ReceiptAction.RefreshReceipt) - store.dispatch(SendAction.ChangeSendButtonState(store.state.sendState.getButtonState())) - - val decimalSeparator = DecimalFormatSymbols.getInstance().decimalSeparator.toString() - store.dispatch(AmountAction.SetDecimalSeparator(decimalSeparator)) - - binding.lSendAmount.tvAmountCurrency.setOnClickListener { - Analytics.send(Token.Send.ButtonSwapCurrency()) - store.dispatch(ToggleMainCurrency) - store.dispatch(ReceiptAction.RefreshReceipt) - store.dispatch(SendAction.ChangeSendButtonState(store.state.sendState.getButtonState())) - } - - val maxAmountSnackbar = MaxAmountSnackbar.make(etAmountToSend) { - etAmountToSend.clearFocus() - etAmountToSend.postDelayed(200) { etAmountToSend.hideSoftKeyboard() } - store.dispatch(SetMaxAmount) - } - var snackbarControlledByChangingFocus = false - keyboardObserver = KeyboardObserver(requireActivity()) - keyboardObserver.registerListener { isShow -> - if (snackbarControlledByChangingFocus) return@registerListener - - if (isShow) { - if (etAmountToSend.isFocused && !maxAmountSnackbar.isShown) maxAmountSnackbar.show() - } else { - if (maxAmountSnackbar.isShown) maxAmountSnackbar.dismiss() - } - } - - etAmountToSend.keyListener = DigitsKeyListener.getInstance("0123456789,.") - etAmountToSend.setOnFocusChangeListener { _, hasFocus -> - snackbarControlledByChangingFocus = true - if (hasFocus) { - etAmountToSend.postDelayed(200) { - maxAmountSnackbar.show() - snackbarControlledByChangingFocus = false - } - } else { - etAmountToSend.postDelayed(350) { - maxAmountSnackbar.dismiss() - snackbarControlledByChangingFocus = false - } - } - } - - val prevFocusChangeListener = etAmountToSend.onFocusChangeListener - etAmountToSend.setOnFocusChangeListener { v, hasFocus -> - prevFocusChangeListener.onFocusChange(v, hasFocus) - if (hasFocus && etAmountToSend.text?.toString() == "0") etAmountToSend.setText("") - if (!hasFocus && etAmountToSend.text?.toString() == "") etAmountToSend.setText("0") - } - - etAmountToSend.setOnImeActionListener(EditorInfo.IME_ACTION_DONE) { - it.hideSoftKeyboard() - it.clearFocus() - } - } - - private fun setupFeeLayout() = with(binding.clNetworkFee) { - flExpandCollapse.flExpandCollapse.setOnClickListener { - store.dispatch(ToggleControlsVisibility) - } - chipGroup.check(FeeUiHelper.toId(FeeType.NORMAL)) - chipGroup.setOnCheckedChangeListener { _, checkedId -> - if (checkedId == -1) return@setOnCheckedChangeListener - - store.dispatch(ChangeSelectedFee(FeeUiHelper.toType(checkedId))) - store.dispatch(CheckAmountToSend) - } - swIncludeFee.setOnCheckedChangeListener { _, isChecked -> - store.dispatch(ChangeIncludeFee(isChecked)) - store.dispatch(CheckAmountToSend) - } - } - - private fun setupWarningMessages() = with(binding) { - warningsAdapter = WarningMessagesAdapter() - val layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false) - rvWarningMessages.layoutManager = layoutManager - rvWarningMessages.addItemDecoration(SpaceItemDecoration.all(16f)) - rvWarningMessages.adapter = warningsAdapter - - store.dispatch(SendAction.Warnings.Update) - } - - override fun subscribeToStore() { - store.subscribe(sendSubscriber) { appState -> - appState.skipRepeats { oldState, newState -> - oldState.sendState == newState.sendState - }.select { it.sendState } - } - storeSubscribersList.add(sendSubscriber) - } - - private fun restoreMainCurrency(): MainCurrencyType { - val sp = requireContext().getSharedPreferences("SendScreen", Context.MODE_PRIVATE) - val mainCurrency = sp.getString("mainCurrency", AppCurrency.Default.code) - return MainCurrencyType.values() - .firstOrNull { it.name.equals(mainCurrency!!, ignoreCase = true) } - ?: MainCurrencyType.CRYPTO - } - - fun saveMainCurrency(type: MainCurrencyType) { - val sp = requireContext().getSharedPreferences("SendScreen", Context.MODE_PRIVATE) - sp.edit().putString("mainCurrency", type.name).apply() - } - - override fun handleOnBackPressed() { - val externalTransactionData = store.state.sendState.externalTransactionData - if (externalTransactionData == null) { - store.dispatchNavigationAction(AppRouter::pop) - } else { - store.dispatch(TradeCryptoAction.FinishSelling(externalTransactionData.transactionId)) - } - } - - override fun onDestroyView() { - keyboardObserver.unregisterListener() - super.onDestroyView() - } - - override fun onDestroy() { - store.dispatch(ReleaseSendState) - lifecycle.removeObserver(viewModel) - super.onDestroy() - } -} - -fun EditText.inputtedTextAsFlow(): Flow = callbackFlow { - val watcher = addTextChangedListener { editable -> trySend(editable?.toString() ?: "") } - awaitClose { removeTextChangedListener(watcher) } -} - -object FeeUiHelper { - fun toId(fee: FeeType): Int { - return when (fee) { - FeeType.SINGLE -> View.NO_ID - FeeType.LOW -> R.id.chipLow - FeeType.NORMAL -> R.id.chipNormal - FeeType.PRIORITY -> R.id.chipPriority - } - } - - fun toType(id: Int): FeeType { - return when (id) { - R.id.chipLow -> FeeType.LOW - R.id.chipNormal -> FeeType.NORMAL - R.id.chipPriority -> FeeType.PRIORITY - else -> FeeType.NORMAL - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/SendViewModel.kt b/app/src/main/java/com/tangem/tap/features/send/ui/SendViewModel.kt deleted file mode 100644 index 881e9f6df2..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/ui/SendViewModel.kt +++ /dev/null @@ -1,140 +0,0 @@ -package com.tangem.tap.features.send.ui - -import androidx.lifecycle.* -import arrow.core.getOrElse -import com.tangem.common.routing.AppRoute -import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase -import com.tangem.domain.qrscanning.models.SourceType -import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase -import com.tangem.domain.qrscanning.usecases.ParseQrCodeUseCase -import com.tangem.domain.tokens.FetchPendingTransactionsUseCase -import com.tangem.domain.tokens.UpdateDelayedNetworkStatusUseCase -import com.tangem.domain.tokens.model.CryptoCurrency -import com.tangem.domain.tokens.model.Network -import com.tangem.domain.wallets.models.UserWallet -import com.tangem.domain.wallets.usecase.GetSelectedWalletSyncUseCase -import com.tangem.tap.common.analytics.events.Token -import com.tangem.tap.di.DelayedWork -import com.tangem.tap.features.send.redux.AddressActionUi -import com.tangem.tap.features.send.redux.AmountAction -import com.tangem.tap.proxy.AppStateHolder -import com.tangem.tap.store -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import dagger.hilt.android.lifecycle.HiltViewModel -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.emptyFlow -import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.launch -import timber.log.Timber -import javax.inject.Inject - -@Suppress("LongParameterList") -@HiltViewModel -internal class SendViewModel @Inject constructor( - private val dispatchers: CoroutineDispatcherProvider, - private val appStateHolder: AppStateHolder, - private val getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase, - private val updateDelayedCurrencyStatusUseCase: UpdateDelayedNetworkStatusUseCase, - private val getSelectedWalletSyncUseCase: GetSelectedWalletSyncUseCase, - private val fetchPendingTransactionsUseCase: FetchPendingTransactionsUseCase, - private val listenToQrScanningUseCase: ListenToQrScanningUseCase, - private val parseQrCodeUseCase: ParseQrCodeUseCase, - @DelayedWork private val coroutineScope: CoroutineScope, - savedStateHandle: SavedStateHandle, -) : ViewModel(), DefaultLifecycleObserver { - - init { - listenToQrScanningUseCase(SourceType.SEND) - .getOrElse { emptyFlow() } - .onEach(::onQRCodeScanned) - .launchIn(viewModelScope) - } - - private val cryptoCurrency: CryptoCurrency? = savedStateHandle[AppRoute.Send.CRYPTO_CURRENCY_KEY] - - override fun onCreate(owner: LifecycleOwner) { - getBalanceHidingSettingsUseCase() - .flowWithLifecycle(owner.lifecycle) - .onEach { - appStateHolder.mainStore?.dispatch(AmountAction.HideBalance(it.isBalanceHidden)) - } - .flowOn(dispatchers.main) - .launchIn(viewModelScope) - } - - fun updateCurrencyDelayed() { - if (cryptoCurrency != null) { - coroutineScope.launch { - getSelectedWalletSyncUseCase() - .fold( - ifLeft = { Timber.e(it.toString()) }, - ifRight = { wallet -> - // we should update network to find pending tx after 1 sec - updateForPendingTx(wallet, cryptoCurrency.network) - // we should update network for new balance - updateForBalance(wallet, cryptoCurrency.network) - }, - ) - } - } else { - Timber.w("$TAG: cryptoCurrency is null, legacy flow") - } - } - - private suspend fun updateForPendingTx(userWallet: UserWallet, network: Network) { - fetchPendingTransactionsUseCase(userWallet.walletId, setOf(network)) - } - - private suspend fun updateForBalance(userWallet: UserWallet, network: Network) { - updateDelayedCurrencyStatusUseCase( - userWalletId = userWallet.walletId, - network = network, - delayMillis = UPDATE_BALANCE_DELAY_MILLIS, - refresh = true, - ) - } - - private fun onQRCodeScanned(qrScanResult: String) { - if (cryptoCurrency != null) { - parseQrCodeUseCase(qrScanResult, cryptoCurrency).fold( - ifRight = { parsedCode -> - store.dispatch( - AddressActionUi.PasteAddress( - data = parsedCode.address, - sourceType = Token.Send.AddressEntered.SourceType.QRCode, - ), - ) - parsedCode.amount?.let { amount -> - store.dispatch(AmountAction.SetAmount(amount, isUserInput = false)) - } - // parsedCode.memo?.let { } - }, - ifLeft = { - store.dispatch( - AddressActionUi.PasteAddress( - data = qrScanResult, - sourceType = Token.Send.AddressEntered.SourceType.QRCode, - ), - ) - Timber.w(it) - }, - ) - } else { - store.dispatch( - AddressActionUi.PasteAddress( - data = qrScanResult, - sourceType = Token.Send.AddressEntered.SourceType.QRCode, - ), - ) - } - - store.dispatch(AddressActionUi.TruncateOrRestore(truncate = true)) - } - - companion object { - private const val UPDATE_BALANCE_DELAY_MILLIS = 11000L - private const val TAG = "SendViewModel" - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/adapters/WarningMessagesAdapter.kt b/app/src/main/java/com/tangem/tap/features/send/ui/adapters/WarningMessagesAdapter.kt deleted file mode 100644 index 2703f44c00..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/ui/adapters/WarningMessagesAdapter.kt +++ /dev/null @@ -1,122 +0,0 @@ -package com.tangem.tap.features.send.ui.adapters - -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.recyclerview.widget.DiffUtil -import androidx.recyclerview.widget.ListAdapter -import androidx.recyclerview.widget.RecyclerView -import com.google.android.play.core.review.ReviewManagerFactory -import com.tangem.core.analytics.Analytics -import com.tangem.tap.common.analytics.events.AnalyticsParam -import com.tangem.tap.common.analytics.events.MainScreen -import com.tangem.tap.common.extensions.* -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.domain.configurable.warningMessage.WarningMessage -import com.tangem.tap.store -import com.tangem.wallet.R -import com.tangem.wallet.databinding.LayoutWarningCardActionBinding -import timber.log.Timber - -// TODO: Delete with SendFeatureToggles -@Deprecated(message = "Used only in old send screen") -class WarningMessagesAdapter : ListAdapter(DiffUtilCallback) { - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): WarningMessageVH { - val binding = LayoutWarningCardActionBinding.inflate( - LayoutInflater.from(parent.context), - parent, - false, - ) - return WarningMessageVH(binding) - } - - override fun onBindViewHolder(holder: WarningMessageVH, position: Int) { - holder.bind(currentList[position]) - } - - object DiffUtilCallback : DiffUtil.ItemCallback() { - override fun areContentsTheSame(oldItem: WarningMessage, newItem: WarningMessage) = oldItem == newItem - - override fun areItemsTheSame(oldItem: WarningMessage, newItem: WarningMessage) = oldItem == newItem - } -} - -class WarningMessageVH(val binding: LayoutWarningCardActionBinding) : RecyclerView.ViewHolder(binding.root) { - - fun bind(warning: WarningMessage) { - setBgColor(warning.priority) - setText(warning) - setupControlButtons(warning) - } - - private fun setText(warning: WarningMessage) = with(binding.warningContentContainer) { - fun getString(resId: Int?, default: String, formatArgs: String? = null) = - if (resId == null) default else root.getString(resId, formatArgs) - - tvTitle.text = getString( - resId = warning.titleResId, - default = warning.title, - formatArgs = warning.titleFormatArg, - ) - - tvMessage.text = getString( - resId = warning.messageResId, - default = warning.message, - formatArgs = warning.messageFormatArg, - ) - } - - private fun setBgColor(priority: WarningMessage.Priority) { - val color = when (priority) { - WarningMessage.Priority.Info -> R.color.warning_info - WarningMessage.Priority.Warning -> R.color.warning_warning - WarningMessage.Priority.Critical -> R.color.warning_critical - } - binding.warningCardAction.setCardBackgroundColor(binding.root.getColor(color)) - } - - @Suppress("LongMethod") - private fun setupControlButtons(warning: WarningMessage) = when (warning.type) { - WarningMessage.Type.Permanent, WarningMessage.Type.TestCard -> { - binding.groupControlsTemporary.hide() - binding.btnClose.hide() - } - WarningMessage.Type.Temporary -> { - binding.groupControlsTemporary.show() - binding.btnClose.hide() - - val buttonAction = View.OnClickListener { - store.dispatch(GlobalAction.HideWarningMessage(warning)) - } - val buttonTitle = binding.root.getString( - warning.buttonTextId ?: R.string.how_to_got_it_button, - ) - binding.btnGotIt.setOnClickListener(buttonAction) - binding.btnGotIt.text = buttonTitle - } - WarningMessage.Type.AppRating -> { - binding.groupControlsTemporary.hide() - binding.btnClose.show() - binding.btnClose.setOnClickListener { - Analytics.send(MainScreen.NoticeRateAppButton(AnalyticsParam.RateApp.Closed)) - store.dispatch(GlobalAction.HideWarningMessage(warning)) - } - binding.btnReallyCool.setOnClickListener { - val activity = binding.root.context.getActivity() ?: return@setOnClickListener - - Analytics.send(MainScreen.NoticeRateAppButton(AnalyticsParam.RateApp.Liked)) - val reviewManager = ReviewManagerFactory.create(activity) - val task = reviewManager.requestReviewFlow() - task.addOnCompleteListener { - if (!it.isSuccessful) { - Timber.e(task.exception) - } - }.addOnFailureListener { - Timber.e(it) - } - store.dispatch(GlobalAction.HideWarningMessage(warning)) - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/ChiaWarningDialog.kt b/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/ChiaWarningDialog.kt deleted file mode 100644 index b4ba056146..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/ChiaWarningDialog.kt +++ /dev/null @@ -1,32 +0,0 @@ -package com.tangem.tap.features.send.ui.dialogs - -import android.content.Context -import androidx.appcompat.app.AlertDialog -import com.tangem.tap.features.send.redux.SendAction -import com.tangem.tap.store -import com.tangem.wallet.R - -// TODO: [REDACTED_JIRA] -object ChiaWarningDialog { - - fun create(context: Context, dialog: SendAction.Dialog.ChiaWarningDialog): AlertDialog { - return AlertDialog.Builder(context).apply { - setTitle(R.string.common_warning) - setMessage( - context.getString( - R.string.common_utxo_validate_withdrawal_message_warning, - dialog.blockchainName, - dialog.maxOutputs, - dialog.maxAmount.toPlainString(), - ), - ) - setPositiveButton(R.string.common_ok) { _, _ -> - dialog.onOk() - } - setOnDismissListener { - store.dispatch(SendAction.Dialog.Hide) - } - } - .create() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/KaspaWarningDialog.kt b/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/KaspaWarningDialog.kt deleted file mode 100644 index a86e233c8b..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/KaspaWarningDialog.kt +++ /dev/null @@ -1,31 +0,0 @@ -package com.tangem.tap.features.send.ui.dialogs - -import android.content.Context -import androidx.appcompat.app.AlertDialog -import com.tangem.blockchain.common.Blockchain -import com.tangem.tap.features.send.redux.SendAction -import com.tangem.tap.store -import com.tangem.wallet.R - -object KaspaWarningDialog { - - fun create(context: Context, dialog: SendAction.Dialog.KaspaWarningDialog): AlertDialog { - return AlertDialog.Builder(context).apply { - setTitle(R.string.common_warning) - setMessage( - context.getString( - R.string.common_utxo_validate_withdrawal_message_warning, - Blockchain.Kaspa.fullName, - dialog.maxOutputs, - dialog.maxAmount.toPlainString(), - ), - ) - setPositiveButton(R.string.common_ok) { _, _ -> - dialog.onOk() - } - setOnDismissListener { - store.dispatch(SendAction.Dialog.Hide) - } - }.create() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/RequestFeeErrorDialog.kt b/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/RequestFeeErrorDialog.kt deleted file mode 100644 index bb8a5ea183..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/RequestFeeErrorDialog.kt +++ /dev/null @@ -1,38 +0,0 @@ -package com.tangem.tap.features.send.ui.dialogs - -import android.content.Context -import androidx.appcompat.app.AlertDialog -import com.tangem.core.analytics.Analytics -import com.tangem.core.analytics.models.AnalyticsParam -import com.tangem.core.analytics.models.Basic -import com.tangem.tap.common.feedback.SendTransactionFailedEmail -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.features.send.redux.SendAction -import com.tangem.tap.store -import com.tangem.wallet.R - -/** -[REDACTED_AUTHOR] - */ -object RequestFeeErrorDialog { - fun create(context: Context, dialog: SendAction.Dialog.RequestFeeError): AlertDialog { - val errorMessage = dialog.error.customMessage - - return AlertDialog.Builder(context).apply { - setTitle(R.string.common_fee_error) - setMessage(context.getString(R.string.alert_failed_to_send_transaction_message, errorMessage)) - setNegativeButton(R.string.details_row_title_contact_to_support) { _, _ -> - Analytics.send(Basic.ButtonSupport(AnalyticsParam.ScreensSources.Send)) - store.dispatch( - GlobalAction.SendEmail( - feedbackData = SendTransactionFailedEmail(errorMessage), - scanResponse = dialog.scanResponse, - ), - ) - } - setPositiveButton(R.string.common_retry) { _, _ -> dialog.onRetry() } - setNeutralButton(R.string.common_cancel) { _, _ -> } - setOnDismissListener { store.dispatch(SendAction.Dialog.Hide) } - }.create() - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/SendTransactionFailsDialog.kt b/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/SendTransactionFailsDialog.kt deleted file mode 100644 index a768ea033b..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/SendTransactionFailsDialog.kt +++ /dev/null @@ -1,65 +0,0 @@ -package com.tangem.tap.features.send.ui.dialogs - -import android.content.Context -import androidx.appcompat.app.AlertDialog -import com.tangem.blockchain.common.Blockchain -import com.tangem.blockchain.common.BlockchainSdkError -import com.tangem.common.module.ModuleMessageConverter -import com.tangem.core.analytics.Analytics -import com.tangem.core.analytics.models.AnalyticsParam -import com.tangem.core.analytics.models.Basic -import com.tangem.domain.models.scan.ScanResponse -import com.tangem.sdk.extensions.localizedDescription -import com.tangem.tap.common.extensions.stripZeroPlainString -import com.tangem.tap.common.feedback.SendTransactionFailedEmail -import com.tangem.tap.common.redux.global.GlobalAction -import com.tangem.tap.features.send.redux.SendAction -import com.tangem.tap.store -import com.tangem.wallet.R - -/** -[REDACTED_AUTHOR] - */ -object SendTransactionFailsDialog { - fun create(context: Context, dialog: SendAction.Dialog.SendTransactionFails.CardSdkError): AlertDialog { - return create(context, dialog.error.localizedDescription(context), dialog.scanResponse) - } - - fun create(context: Context, dialog: SendAction.Dialog.SendTransactionFails.BlockchainSdkError): AlertDialog { - val errorConverter = BlockchainSdkErrorConverter(context) - return create(context, errorConverter.convert(dialog.error), dialog.scanResponse) - } - - private fun create(context: Context, errorMessage: String, scanResponse: ScanResponse): AlertDialog { - return AlertDialog.Builder(context).apply { - setTitle(R.string.alert_failed_to_send_transaction_title) - setMessage(context.getString(R.string.alert_failed_to_send_transaction_message, errorMessage)) - setNeutralButton(R.string.details_row_title_contact_to_support) { _, _ -> - Analytics.send(Basic.ButtonSupport(AnalyticsParam.ScreensSources.Send)) - store.dispatch(GlobalAction.SendEmail(SendTransactionFailedEmail(errorMessage), scanResponse)) - } - setPositiveButton(R.string.common_cancel) { _, _ -> } - setOnDismissListener { store.dispatch(SendAction.Dialog.Hide) } - }.create() - } -} - -private class BlockchainSdkErrorConverter( - private val context: Context, -) : ModuleMessageConverter { - - override fun convert(message: BlockchainSdkError): String { - return when (message) { - is BlockchainSdkError.CreateAccountUnderfunded -> { - val resStringId = when (message.blockchain) { - Blockchain.Polkadot, Blockchain.PolkadotTestnet -> R.string.no_account_polkadot - else -> R.string.send_error_no_target_account - } - val reserveValueString = message.minReserve.value?.stripZeroPlainString() ?: "0" - val argument = "$reserveValueString ${message.minReserve.currencySymbol}" - context.getString(resStringId, argument) - } - else -> message.customMessage - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/TezosWarningDialog.kt b/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/TezosWarningDialog.kt deleted file mode 100644 index afb6276cb7..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/ui/dialogs/TezosWarningDialog.kt +++ /dev/null @@ -1,29 +0,0 @@ -package com.tangem.tap.features.send.ui.dialogs - -import android.content.Context -import androidx.appcompat.app.AlertDialog -import com.tangem.tap.features.send.redux.SendAction -import com.tangem.tap.store -import com.tangem.wallet.R - -class TezosWarningDialog(context: Context) : AlertDialog(context) { - - companion object { - fun create(context: Context, showDialogData: SendAction.Dialog.TezosWarningDialog): AlertDialog { - val reduceAmount = showDialogData.reduceAmount.toPlainString() - return Builder(context).apply { - setTitle(R.string.common_warning) - setMessage(context.getString(R.string.xtz_withdrawal_message_warning, reduceAmount)) - setNegativeButton(R.string.xtz_withdrawal_message_ignore) { _, _ -> - showDialogData.sendAllCallback() - } - setPositiveButton(context.getString(R.string.xtz_withdrawal_message_reduce, reduceAmount)) { _, _ -> - showDialogData.reduceCallback() - } - setOnDismissListener { - store.dispatch(SendAction.Dialog.Hide) - } - }.create() - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/FragmentStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/FragmentStateSubscriber.kt deleted file mode 100644 index 17dfb7a5fc..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/FragmentStateSubscriber.kt +++ /dev/null @@ -1,21 +0,0 @@ -package com.tangem.tap.features.send.ui.stateSubscribers - -import com.tangem.tap.features.BaseStoreFragment -import org.rekotlin.StateType -import org.rekotlin.StoreSubscriber -import java.lang.ref.WeakReference - -/** -[REDACTED_AUTHOR] - */ -abstract class FragmentStateSubscriber(fragment: BaseStoreFragment) : StoreSubscriber { - private val weakFragment: WeakReference = WeakReference(fragment) - - abstract fun updateWithNewState(fg: BaseStoreFragment, state: S) - - override fun newState(state: S) { - val fg = weakFragment.get() ?: return - - updateWithNewState(fg, state) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt b/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt deleted file mode 100644 index 81899d8e65..0000000000 --- a/app/src/main/java/com/tangem/tap/features/send/ui/stateSubscribers/SendStateSubscriber.kt +++ /dev/null @@ -1,530 +0,0 @@ -package com.tangem.tap.features.send.ui.stateSubscribers - -import android.app.Dialog -import android.content.Context -import android.text.SpannableStringBuilder -import android.view.View -import android.view.ViewGroup -import androidx.core.text.bold -import com.tangem.common.extensions.remove -import com.tangem.core.ui.utils.BigDecimalFormatter -import com.tangem.tap.common.entities.ProgressState -import com.tangem.tap.common.extensions.* -import com.tangem.tap.common.redux.getMessageString -import com.tangem.tap.common.text.DecimalDigitsInputFilter -import com.tangem.tap.domain.MultiMessageError -import com.tangem.tap.domain.assembleErrors -import com.tangem.tap.features.BaseStoreFragment -import com.tangem.tap.features.send.redux.AddressVerifyAction.Error -import com.tangem.tap.features.send.redux.SendAction -import com.tangem.tap.features.send.redux.states.* -import com.tangem.tap.features.send.ui.FeeUiHelper -import com.tangem.tap.features.send.ui.SendFragment -import com.tangem.tap.features.send.ui.SendViewModel -import com.tangem.tap.features.send.ui.adapters.WarningMessagesAdapter -import com.tangem.tap.features.send.ui.dialogs.* -import com.tangem.wallet.R - -/** -[REDACTED_AUTHOR] - */ -@Suppress("LargeClass") -internal class SendStateSubscriber( - fragment: BaseStoreFragment, -) : FragmentStateSubscriber(fragment) { - - private var dialog: Dialog? = null - private var sendViewModel: SendViewModel? = null - fun initViewModel(viewModel: SendViewModel) { - sendViewModel = viewModel - } - - override fun updateWithNewState(fg: BaseStoreFragment, state: SendState) { - fg.view ?: return - if (fg !is SendFragment) return - if (state.isSuccessSend) { - sendViewModel?.updateCurrencyDelayed() - return - } - - val lastChangedStates = state.lastChangedStates.toList() - state.lastChangedStates.clear() - (fg as? SendFragment)?.binding?.mainSendContainer?.beginDelayedTransition() - lastChangedStates.forEach { - when (it) { - StateId.SEND_SCREEN -> handleSendScreen(fg, state) - StateId.ADDRESS_PAY_ID -> handleAddressState(fg, state.addressState) - StateId.TRANSACTION_EXTRAS -> handleTransactionExtrasState(fg, state.transactionExtrasState) - StateId.AMOUNT -> handleAmountState(fg, state.amountState) - StateId.FEE -> handleFeeState(fg, state.feeState) - StateId.RECEIPT -> handleReceiptState(fg, state.receiptState, state.feeState.progressState) - } - } - } - - @Deprecated("Legacy") - @Suppress("ComplexMethod", "LongMethod") - private fun handleTransactionExtrasState(fg: SendFragment, infoState: TransactionExtrasState) = - with(fg.binding.lSendAddress) { - fun showView(view: View, info: Any?) { - view.show(info != null) - } - showView(xlmMemoContainer, infoState.xlmMemo) - showView(xrpDestinationTagContainer, infoState.xrpDestinationTag) - showView(binanceMemoContainer, infoState.binanceMemo) - showView(tonMemoContainer, infoState.tonMemoState) - showView(cosmosMemoContainer, infoState.cosmosMemoState) - showView(hederaMemoContainer, infoState.hederaMemoState) - showView(algorandMemoContainer, infoState.algorandMemoState) - - infoState.xlmMemo?.let { - if (!it.viewFieldValue.isFromUserInput) etXlmMemo.setText(it.viewFieldValue.value) - if (it.error != null) { - if (it.error == TransactionExtraError.INVALID_XLM_MEMO) { - tilXlmMemo.error = fg.getText(R.string.send_extras_error_invalid_memo) - } - } else { - tilXlmMemo.error = null - } - } - infoState.xrpDestinationTag?.let { - if (infoState.xrpDestinationTag.error != null) { - if (infoState.xrpDestinationTag.error == TransactionExtraError.INVALID_DESTINATION_TAG) { - tilDestinationTag.error = - fg.getText(R.string.send_extras_error_invalid_destination_tag) - } - } else { - tilDestinationTag.error = null - } - if (!it.viewFieldValue.isFromUserInput) { - etDestinationTag.setText(it.viewFieldValue.value) - } - } - infoState.binanceMemo?.let { - if (infoState.binanceMemo.error != null) { - if (infoState.binanceMemo.error == TransactionExtraError.INVALID_BINANCE_MEMO) { - tilBinanceMemo.error = fg.getText(R.string.send_extras_error_invalid_memo) - } - } else { - tilBinanceMemo.error = null - } - if (!it.viewFieldValue.isFromUserInput) { - etBinanceMemo.setText(it.viewFieldValue.value) - } - } - infoState.tonMemoState?.let { - if (infoState.tonMemoState.error != null) { - tilTonMemo.error = fg.getText(R.string.send_extras_error_invalid_memo) - } else { - tilBinanceMemo.error = null - } - if (!it.viewFieldValue.isFromUserInput) { - etTonMemo.setText(it.viewFieldValue.value) - } - } - infoState.cosmosMemoState?.let { - if (infoState.cosmosMemoState.error != null) { - tilCosmosMemo.error = fg.getText(R.string.send_extras_error_invalid_memo) - } else { - tilBinanceMemo.error = null - } - if (!it.viewFieldValue.isFromUserInput) { - etCosmosMemo.setText(it.viewFieldValue.value) - } - } - infoState.hederaMemoState?.let { - if (infoState.hederaMemoState.error != null) { - tilHederaMemo.error = fg.getText(R.string.send_extras_error_invalid_memo) - } else { - tilHederaMemo.error = null - } - if (!it.viewFieldValue.isFromUserInput) { - etHederaMemo.setText(it.viewFieldValue.value) - } - } - infoState.algorandMemoState?.let { - if (infoState.algorandMemoState.error != null) { - tilAlgorandMemo.error = fg.getText(R.string.send_extras_error_invalid_memo) - } else { - tilAlgorandMemo.error = null - } - if (!it.viewFieldValue.isFromUserInput) { - etAlgorandMemo.setText(it.viewFieldValue.value) - } - } - } - - @Suppress("ComplexMethod") - private fun handleSendScreen(fg: SendFragment, state: SendState) = with(fg.binding) { - when (state.dialog) { - is SendAction.Dialog.TezosWarningDialog -> { - if (dialog == null) { - dialog = TezosWarningDialog.create(fg.requireContext(), state.dialog) - dialog?.show() - } - } - is SendAction.Dialog.KaspaWarningDialog -> { - if (dialog == null) { - dialog = KaspaWarningDialog.create(fg.requireContext(), state.dialog) - dialog?.show() - } - } - is SendAction.Dialog.ChiaWarningDialog -> { - if (dialog == null) { - dialog = ChiaWarningDialog.create(fg.requireContext(), state.dialog) - dialog?.show() - } - } - is SendAction.Dialog.SendTransactionFails.CardSdkError -> { - if (dialog == null) { - dialog = SendTransactionFailsDialog.create(fg.requireContext(), state.dialog) - dialog?.show() - } - } - is SendAction.Dialog.SendTransactionFails.BlockchainSdkError -> { - if (dialog == null) { - dialog = SendTransactionFailsDialog.create(fg.requireContext(), state.dialog) - dialog?.show() - } - } - is SendAction.Dialog.RequestFeeError -> { - if (dialog == null) { - dialog = RequestFeeErrorDialog.create(fg.requireContext(), state.dialog) - dialog?.show() - } - } - else -> { - dialog?.dismiss() - dialog = null - } - } - - fg.sendBtn.changeState(state.sendButtonState.progressState) - fg.sendBtn.mainView.isEnabled = state.sendButtonState.enabled - - val rv = rvWarningMessages - val adapter = rv.adapter as? WarningMessagesAdapter ?: return - - adapter.submitList(state.sendWarningsList) - rv.show(state.sendWarningsList.isNotEmpty()) - - toolbar.title = fg.getString( - R.string.send_title_currency_format, - state.amountState.mainCurrency.currencySymbol, - ) - } - - private fun handleAddressState(fg: SendFragment, state: AddressState) { - with(fg.binding.lSendAddress) { - fun parseError(context: Context, error: Error?): String? { - val resId = when (error) { - Error.ADDRESS_INVALID_OR_UNSUPPORTED_BY_BLOCKCHAIN -> R.string.send_validation_invalid_address - Error.ADDRESS_SAME_AS_WALLET -> R.string.send_error_address_same_as_wallet - else -> null - } - return if (resId == null) null else context.getString(resId, "", "") - } - - imvPaste.isEnabled = state.pasteIsEnabled - - val et = etAddress - val til = tilAddress - val parsedError = parseError(til.context, state.error) - - til.isEnabled = state.inputIsEnabled - imvPaste.show(state.inputIsEnabled) - imvQrCode.show(state.inputIsEnabled) - flPaste.show(state.inputIsEnabled) - flQrCode.show(state.inputIsEnabled) - - til.hint = til.getString(R.string.send_destination_hint_address) - til.error = parsedError - til.isErrorEnabled = parsedError != null - til.helperText = state.destinationWalletAddress - til.isHelperTextEnabled = parsedError == null - - if (!state.viewFieldValue.isFromUserInput) et.update(state.viewFieldValue.value) - } - } - - private fun handleAmountState(fg: SendFragment, state: AmountState) = with(fg.binding.lSendAmount) { - if (state.error != null) { - val context = fg.requireContext() - val message = when (state.error) { - is MultiMessageError -> { - val multiError = state.error as MultiMessageError - val messageList = multiError.assembleErrors() - .map { getMessageString(context, it.first, it.second) } - multiError.builder(messageList) - } - else -> context.getString(state.error.messageResource) - } - tilAmountToSend.enableError(true, message) - } else { - tilAmountToSend.enableError(false) - } - - val filter = DecimalDigitsInputFilter( - digitsBeforeDecimal = 40, - digitsAfterDecimal = state.maxLengthOfAmount, - decimalSeparator = state.decimalSeparator, - ) - etAmountToSend.filters = arrayOf(filter) - val amountToSend = state.viewAmountValue - if (!amountToSend.isFromUserInput) etAmountToSend.update(amountToSend.value) - - tvAmountCurrency.update(state.mainCurrency.currencySymbol) - (fg as? SendFragment)?.saveMainCurrency(state.mainCurrency.type) - - val balanceText = when (state.mainCurrency.type) { - MainCurrencyType.FIAT -> fg.getString( - R.string.common_balance, - "${state.viewBalanceValue} ${state.mainCurrency.currencySymbol}", - ).remove(":") - MainCurrencyType.CRYPTO -> fg.getString( - R.string.common_balance, - "${state.mainCurrency.currencySymbol} ${state.viewBalanceValue}", - ) - } - - tvBalance.update(balanceText) - - tilAmountToSend.isEnabled = state.inputIsEnabled - - val imageRes = if (state.inputIsEnabled) R.drawable.ic_arrows_up_down else 0 - tvAmountCurrency.setCompoundDrawablesWithIntrinsicBounds(0, 0, imageRes, 0) - val textColor = if (state.inputIsEnabled) R.color.accent else R.color.text_secondary - tvAmountCurrency.setTextColor(fg.getColor(textColor)) - } - - @Suppress("MagicNumber") - private fun handleFeeState(fg: SendFragment, state: FeeState) { - with(fg.binding.clNetworkFee) { - fg.view?.findViewById(R.id.clNetworkFee)?.show(state.mainLayoutIsVisible) - flExpandCollapse.imvExpandCollapse.rotation = if (state.controlsLayoutIsVisible) 0f else 180f - llFeeControlsContainer.show(state.controlsLayoutIsVisible) - chipGroup.show(state.feeChipGroupIsVisible) - - swIncludeFee.isEnabled = state.includeFeeSwitcherIsEnabled - if (swIncludeFee.isChecked != state.feeIsIncluded) { - swIncludeFee.isChecked = state.feeIsIncluded - } - - val chipId = FeeUiHelper.toId(state.selectedFeeType) - if (chipGroup.checkedChipId != chipId && chipId != View.NO_ID) chipGroup.check(chipId) - } - with(fg.binding.clReceiptContainer) { - when (state.progressState) { - ProgressState.Loading -> { - tvReceiptFeeValue.hide() - pbReceiptFee.show() - } - ProgressState.Done -> { - pbReceiptFee.hide() - tvReceiptFeeValue.show() - } - else -> {} - } - } - } - - @Suppress("LongMethod", "ComplexMethod") - private fun handleReceiptState(fg: SendFragment, state: ReceiptState, feeProgressState: ProgressState) { - with(fg.binding.clReceiptContainer) { - val mainLayout = clReceiptContainer as ViewGroup - val totalLayout = llTotalContainer.llTotal as ViewGroup - val totalTokenLayout = llTotalContainer.flTotalTokenCrypto as ViewGroup - - fun getString(id: Int, vararg formatStrings: String): String = mainLayout.getString(id, *formatStrings) - - fun roughOrEmpty(value: String): String { - return if (value == BigDecimalFormatter.EMPTY_BALANCE_SIGN) value else "$ROUGH_SIGN $value" - } - - when (feeProgressState) { - ProgressState.Loading -> { - tvReceiptFeeValue.hide() - pbReceiptFee.show() - } - ProgressState.Done -> { - pbReceiptFee.hide() - tvReceiptFeeValue.show() - } - else -> {} - } - - when (state.visibleTypeOfReceipt) { - ReceiptLayoutType.FIAT -> { - val receipt = state.fiat ?: return - - totalLayout.show(true) - totalTokenLayout.show(false) - tvReceiptAmountValue.update("${receipt.amountFiat} ${receipt.symbols.fiat}") - tvReceiptFeeValue.update("${receipt.feeFiat} ${receipt.symbols.fiat}") - llTotalContainer.tvTotalValue.post { - llTotalContainer.tvTotalValue.update( - "${roughOrEmpty(receipt.totalFiat)} ${receipt.symbols.fiat}", - ) - } - - val willSent = getString( - R.string.send_total_subtitle_format, - "${receipt.willSentCrypto} ${receipt.symbols.crypto}", - ) - llTotalContainer.tvWillBeSentValue.update(willSent) - } - ReceiptLayoutType.CRYPTO -> { - val receipt = state.crypto ?: return - - totalLayout.show(true) - totalTokenLayout.show(false) - tvReceiptAmountValue.update("${receipt.amountCrypto} ${receipt.symbols.crypto}") - tvReceiptFeeValue.update("${receipt.feeCrypto} ${receipt.symbols.crypto}") - llTotalContainer.tvTotalValue.post { - llTotalContainer.tvTotalValue.update("${receipt.totalCrypto} ${receipt.symbols.crypto}") - } - - if (receipt.willSentFiat == BigDecimalFormatter.EMPTY_BALANCE_SIGN) { - llTotalContainer.tvWillBeSentValue.hide() - } else { - llTotalContainer.tvWillBeSentValue.show() - llTotalContainer.tvWillBeSentValue.update( - getString( - R.string.send_total_subtitle_fiat_format, - "${receipt.willSentFiat} ${receipt.symbols.fiat}", - "${receipt.feeFiat} ${receipt.symbols.fiat}", - ), - ) - } - } - ReceiptLayoutType.TOKEN_FIAT -> { - val receipt = state.tokenFiat ?: return - - totalLayout.show(true) - totalTokenLayout.show(false) - tvReceiptAmountValue.update("${receipt.amountFiat} ${receipt.symbols.fiat}") - tvReceiptFeeValue.update("${receipt.feeFiat} ${receipt.symbols.fiat}") - llTotalContainer.tvTotalValue.post { - llTotalContainer.tvTotalValue.update( - "${roughOrEmpty(receipt.totalFiat)} ${receipt.symbols.fiat}", - ) - } - - val willSent = getString( - R.string.send_total_subtitle_asset_format, - "${receipt.symbols.token ?: ""} ${receipt.willSentToken}", - "${receipt.symbols.crypto} ${receipt.willSentFeeCoin}", - ) - llTotalContainer.tvWillBeSentValue.update(willSent) - } - ReceiptLayoutType.TOKEN_CRYPTO -> { - val receipt = state.tokenCrypto ?: return - - totalLayout.show(false) - totalTokenLayout.show(true) - - tvReceiptAmountValue.update("${receipt.amountToken} ${receipt.symbols.token}") - tvReceiptFeeValue.update("${receipt.feeCoin} ${receipt.symbols.crypto}") - - val willSent = SpannableStringBuilder() - .bold { - append(roughOrEmpty(receipt.totalFiat)).append(" ") - append(receipt.symbols.fiat) - } - llTotalContainer.tvTotalTokenCryptoValue.update(willSent.toString()) - } - ReceiptLayoutType.FEE_IN_CUSTOM_TOKEN -> { - val receipt = state.customTokenCrypto ?: return - - totalLayout.show(false) - totalTokenLayout.show(true) - - tvReceiptAmountValue.update( - "${receipt.amountToken} ${receipt.symbols.token ?: receipt.symbols.crypto}", - ) - tvReceiptFeeValue.update("${receipt.feeCoin} ${receipt.symbols.fee}") - - val willSent = SpannableStringBuilder() - .bold { - append(roughOrEmpty(receipt.totalFiat)).append(" ") - append(receipt.symbols.fiat) - } - llTotalContainer.tvTotalTokenCryptoValue.update(willSent.toString()) - } - ReceiptLayoutType.FEE_IN_CUSTOM_TOKEN_FIAT -> { - val receipt = state.customTokenFiat ?: return - - totalLayout.show(true) - totalTokenLayout.show(false) - tvReceiptAmountValue.update("${receipt.amountFiat} ${receipt.symbols.fiat}") - tvReceiptFeeValue.update("${receipt.feeFiat} ${receipt.symbols.fiat}") - llTotalContainer.tvTotalValue.post { - llTotalContainer.tvTotalValue.update( - "${roughOrEmpty(receipt.totalFiat)} ${receipt.symbols.fiat}", - ) - } - - val willSent = getString( - R.string.send_total_subtitle_asset_format, - "${receipt.symbols.token ?: receipt.symbols.crypto} ${receipt.willSentToken}", - "${receipt.symbols.fee} ${receipt.willSentFeeCoin}", - ) - llTotalContainer.tvWillBeSentValue.update(willSent) - } - - ReceiptLayoutType.SAME_CURRENCY -> { - val receipt = state.sameCurrencyCrypto ?: return - - totalLayout.show(true) - totalTokenLayout.show(false) - tvReceiptAmountValue.update( - "${receipt.amountCrypto} ${receipt.symbols.token ?: receipt.symbols.crypto}", - ) - tvReceiptFeeValue.update("${receipt.feeCrypto} ${receipt.symbols.token ?: receipt.symbols.crypto}") - llTotalContainer.tvTotalValue.post { - llTotalContainer.tvTotalValue.update( - "${receipt.totalCrypto} ${receipt.symbols.token ?: receipt.symbols.crypto}", - ) - } - - if (receipt.willSentFiat == BigDecimalFormatter.EMPTY_BALANCE_SIGN) { - llTotalContainer.tvWillBeSentValue.hide() - } else { - llTotalContainer.tvWillBeSentValue.show() - llTotalContainer.tvWillBeSentValue.update( - getString( - R.string.send_total_subtitle_fiat_format, - "${receipt.willSentFiat} ${receipt.symbols.fiat}", - "${receipt.feeFiat} ${receipt.symbols.fiat}", - ), - ) - } - } - ReceiptLayoutType.SAME_CURRENCY_FIAT -> { - val receipt = state.sameCurrencyFiat ?: return - - totalLayout.show(true) - totalTokenLayout.show(false) - tvReceiptAmountValue.update("${receipt.amountFiat} ${receipt.symbols.fiat}") - tvReceiptFeeValue.update("${receipt.feeFiat} ${receipt.symbols.fiat}") - llTotalContainer.tvTotalValue.post { - llTotalContainer.tvTotalValue.update( - "${roughOrEmpty(receipt.totalFiat)} ${receipt.symbols.fiat}", - ) - } - - val willSent = getString( - R.string.send_total_subtitle_format, - "${receipt.willSentCrypto} ${receipt.symbols.token ?: receipt.symbols.crypto}", - ) - llTotalContainer.tvWillBeSentValue.update(willSent) - } - ReceiptLayoutType.UNKNOWN, null -> {} - } - } - } - - private companion object { - const val ROUGH_SIGN = "≈" - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt index abb2a86125..cfb77e6180 100644 --- a/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/wallet/redux/middlewares/TradeCryptoMiddleware.kt @@ -1,8 +1,6 @@ package com.tangem.tap.features.wallet.redux.middlewares -import com.google.firebase.crashlytics.FirebaseCrashlytics import com.tangem.blockchain.blockchains.ethereum.EthereumWalletManager -import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Blockchain import com.tangem.common.routing.AppRoute import com.tangem.common.routing.AppRouter @@ -17,11 +15,8 @@ import com.tangem.tap.common.apptheme.MutableAppThemeModeHolder import com.tangem.tap.common.extensions.* import com.tangem.tap.common.redux.AppDialog import com.tangem.tap.common.redux.AppState -import com.tangem.tap.domain.TapError import com.tangem.tap.features.demo.DemoHelper import com.tangem.tap.features.home.RUSSIA_COUNTRY_CODE -import com.tangem.tap.features.send.redux.PrepareSendScreen -import com.tangem.tap.features.send.redux.SendAction import com.tangem.tap.network.exchangeServices.CurrencyExchangeManager import com.tangem.tap.network.exchangeServices.buyErc20TestnetTokens import com.tangem.tap.proxy.redux.DaggerGraphState @@ -45,9 +40,6 @@ object TradeCryptoMiddleware { } } - private val isSendRedesignedEnabled: Boolean - get() = store.inject(getDependency = DaggerGraphState::sendFeatureToggles).isRedesignedSendEnabled - @Suppress("LongMethod", "CyclomaticComplexMethod") private fun handle(state: () -> AppState?, action: TradeCryptoAction) { if (DemoHelper.tryHandle(state, action)) return @@ -62,20 +54,8 @@ object TradeCryptoMiddleware { cryptoCurrencyId = action.cryptoCurrencyId, yield = action.yield, ) - is TradeCryptoAction.SendToken -> { - if (isSendRedesignedEnabled) { - handleNewSendToken(action = action) - } else { - handleSendToken(action = action) - } - } - is TradeCryptoAction.SendCoin -> { - if (isSendRedesignedEnabled) { - handleNewSendCoin(action = action) - } else { - handleSendCoin(action = action) - } - } + is TradeCryptoAction.SendToken -> handleNewSendToken(action = action) + is TradeCryptoAction.SendCoin -> handleNewSendCoin(action = action) } } @@ -177,135 +157,6 @@ object TradeCryptoMiddleware { } } - private fun handleSendToken(action: TradeCryptoAction.SendToken) { - val currency = action.tokenCurrency - val blockchain = Blockchain.fromId(currency.network.id.value) - - scope.launch { - val walletManager = store.inject(DaggerGraphState::walletManagersFacade) - .getOrCreateWalletManager( - userWalletId = action.userWallet.walletId, - blockchain = blockchain, - derivationPath = currency.network.derivationPath.value, - ) - - if (walletManager == null) { - val error = TapError.UnsupportedState(stateError = "WalletManager is null") - FirebaseCrashlytics.getInstance().recordException(IllegalStateException(error.stateError)) - store.dispatchErrorNotification(error) - return@launch - } - - val sendableAmount = walletManager.wallet.amounts.values.firstOrNull { - val amountType = it.type - amountType is AmountType.Token && amountType.token.contractAddress == currency.contractAddress - } - - store.dispatchOnMain( - action = PrepareSendScreen( - walletManager = walletManager, - feePaidCurrency = walletManager.wallet.blockchain.feePaidCurrency(), - currency = currency, - coinAmount = walletManager.wallet.amounts[AmountType.Coin], - coinRate = action.coinFiatRate, - tokenAmount = sendableAmount, - tokenRate = action.tokenFiatRate, - feeCurrencyRate = action.feeCurrencyStatus?.value?.fiatRate, - feeCurrencyDecimals = action.feeCurrencyStatus?.currency?.decimals ?: 0, - ), - ) - - val txInfo = action.transactionInfo - if (txInfo != null) { - store.dispatchOnMain( - SendAction.SendSpecificTransaction( - sendAmount = txInfo.amount, - destinationAddress = txInfo.destinationAddress, - transactionId = txInfo.transactionId, - ), - ) - } - - val route = AppRoute.Send( - currency = currency, - userWalletId = action.userWallet.walletId, - ) - - store.dispatchNavigationAction { push(route) } - } - } - - private fun handleSendCoin(action: TradeCryptoAction.SendCoin) { - if (action.transactionInfo?.tag != null) { - // avoid open old send if memo exists - return - } - val cryptoStatus = action.coinStatus - val currency = cryptoStatus.currency - val blockchain = Blockchain.fromId(currency.network.id.value) - - scope.launch { - val walletManager = store.inject(DaggerGraphState::walletManagersFacade) - .getOrCreateWalletManager( - userWalletId = action.userWallet.walletId, - blockchain = blockchain, - derivationPath = currency.network.derivationPath.value, - ) - - if (walletManager == null) { - val error = TapError.UnsupportedState(stateError = "WalletManager is null") - FirebaseCrashlytics.getInstance().recordException(IllegalStateException(error.stateError)) - store.dispatchErrorNotification(error) - return@launch - } - - val sendableAmounts = walletManager.wallet.amounts.values.filter { it.type == AmountType.Coin } - when (currency) { - is CryptoCurrency.Coin -> { - val amountToSend = sendableAmounts.find { it.currencySymbol == currency.symbol } - - if (amountToSend == null) { - val error = TapError.UnsupportedState(stateError = "Amount to send is null") - FirebaseCrashlytics.getInstance() - .recordException(IllegalStateException(error.stateError)) - store.dispatchErrorNotification(error) - return@launch - } - - store.dispatchOnMain( - action = PrepareSendScreen( - walletManager = walletManager, - feePaidCurrency = walletManager.wallet.blockchain.feePaidCurrency(), - currency = currency, - coinAmount = amountToSend, - coinRate = cryptoStatus.value.fiatRate, - feeCurrencyRate = action.feeCurrencyStatus?.value?.fiatRate, - feeCurrencyDecimals = action.feeCurrencyStatus?.currency?.decimals ?: 0, - ), - ) - } - is CryptoCurrency.Token -> error("Action.tokenStatus.currency is Token") - } - - val txInfo = action.transactionInfo - if (txInfo != null) { - store.dispatchOnMain( - SendAction.SendSpecificTransaction( - sendAmount = txInfo.amount, - destinationAddress = txInfo.destinationAddress, - transactionId = txInfo.transactionId, - ), - ) - } - - val route = AppRoute.Send( - currency = currency, - userWalletId = action.userWallet.walletId, - ) - store.dispatchNavigationAction { push(route) } - } - } - private fun handleNewSendToken(action: TradeCryptoAction.SendToken) { handleNewSend( userWalletId = action.userWallet.walletId, diff --git a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt index ee435bc6ec..45b2035717 100644 --- a/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt +++ b/app/src/main/java/com/tangem/tap/proxy/redux/DaggerGraphState.kt @@ -32,7 +32,6 @@ import com.tangem.feature.qrscanning.QrScanningRouter import com.tangem.features.details.DetailsFeatureToggles import com.tangem.features.pushnotifications.api.featuretoggles.PushNotificationsFeatureToggles import com.tangem.features.pushnotifications.api.navigation.PushNotificationsRouter -import com.tangem.features.send.api.featuretoggles.SendFeatureToggles import com.tangem.features.send.api.navigation.SendRouter import com.tangem.features.staking.api.navigation.StakingRouter import com.tangem.features.tester.api.TesterRouter @@ -64,7 +63,6 @@ data class DaggerGraphState( val balanceHidingRepository: BalanceHidingRepository? = null, val walletsRepository: WalletsRepository? = null, val networksRepository: NetworksRepository? = null, - val sendFeatureToggles: SendFeatureToggles? = null, val sendRouter: SendRouter? = null, val qrScanningRouter: QrScanningRouter? = null, val currenciesRepository: CurrenciesRepository? = null, diff --git a/app/src/main/res/layout/fragment_send.xml b/app/src/main/res/layout/fragment_send.xml deleted file mode 100644 index a7f8c54c42..0000000000 --- a/app/src/main/res/layout/fragment_send.xml +++ /dev/null @@ -1,140 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/layout_send_address.xml b/app/src/main/res/layout/layout_send_address.xml deleted file mode 100644 index 1256f33037..0000000000 --- a/app/src/main/res/layout/layout_send_address.xml +++ /dev/null @@ -1,329 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/layout_send_amount.xml b/app/src/main/res/layout/layout_send_amount.xml deleted file mode 100644 index b8f987e234..0000000000 --- a/app/src/main/res/layout/layout_send_amount.xml +++ /dev/null @@ -1,71 +0,0 @@ - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/layout_send_fee.xml b/app/src/main/res/layout/layout_send_fee.xml deleted file mode 100644 index 66c3b128f1..0000000000 --- a/app/src/main/res/layout/layout_send_fee.xml +++ /dev/null @@ -1,93 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/layout_send_receipt.xml b/app/src/main/res/layout/layout_send_receipt.xml deleted file mode 100644 index 97e7afbb0f..0000000000 --- a/app/src/main/res/layout/layout_send_receipt.xml +++ /dev/null @@ -1,87 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/view_snackbar_max_amount.xml b/app/src/main/res/layout/view_snackbar_max_amount.xml deleted file mode 100644 index 141e31aa66..0000000000 --- a/app/src/main/res/layout/view_snackbar_max_amount.xml +++ /dev/null @@ -1,5 +0,0 @@ - - \ No newline at end of file diff --git a/app/src/main/res/layout/view_snackbar_max_amount_content.xml b/app/src/main/res/layout/view_snackbar_max_amount_content.xml deleted file mode 100644 index de8cdfa27e..0000000000 --- a/app/src/main/res/layout/view_snackbar_max_amount_content.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json index bde5433abf..c3f822ed71 100644 --- a/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json +++ b/core/featuretoggles/src/main/assets/configs/feature_toggles_config.json @@ -3,10 +3,6 @@ "name": "NEW_CARD_SCANNING_ENABLED", "version": "undefined" }, - { - "name": "REDESIGNED_SEND_SCREEN_ENABLED", - "version": "5.10.0" - }, { "name": "LOCAL_USER_LOGS_ENABLED", "version": "5.13.0" diff --git a/features/send/api/src/main/kotlin/com/tangem/features/send/api/featuretoggles/SendFeatureToggles.kt b/features/send/api/src/main/kotlin/com/tangem/features/send/api/featuretoggles/SendFeatureToggles.kt deleted file mode 100644 index 453a79b60c..0000000000 --- a/features/send/api/src/main/kotlin/com/tangem/features/send/api/featuretoggles/SendFeatureToggles.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.tangem.features.send.api.featuretoggles - -/** - * Send feature toggles - */ -interface SendFeatureToggles { - - /** Availability of redesigned send screen */ - val isRedesignedSendEnabled: Boolean - - /** Updates remote toggle */ - suspend fun fetchNewSendEnabled() -} \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/di/SendFeatureTogglesModule.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/di/SendFeatureTogglesModule.kt deleted file mode 100644 index 60c8b2822c..0000000000 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/di/SendFeatureTogglesModule.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.tangem.features.send.impl.di - -import com.tangem.core.featuretoggle.manager.FeatureTogglesManager -import com.tangem.datasource.api.tangemTech.TangemTechApi -import com.tangem.features.send.api.featuretoggles.SendFeatureToggles -import com.tangem.features.send.impl.featuretoggles.DefaultSendFeatureToggles -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton - -/** - * DI module provides implementation of [SendFeatureToggles] - */ -@Module -@InstallIn(SingletonComponent::class) -internal object SendFeatureTogglesModule { - - @Provides - @Singleton - fun provideSendFeatureToggles( - featureTogglesManager: FeatureTogglesManager, - tangemTechApi: TangemTechApi, - dispatchers: CoroutineDispatcherProvider, - ): SendFeatureToggles { - return DefaultSendFeatureToggles( - featureTogglesManager = featureTogglesManager, - tangemTechApi = tangemTechApi, - dispatchers = dispatchers, - ) - } -} \ No newline at end of file diff --git a/features/send/impl/src/main/java/com/tangem/features/send/impl/featuretoggles/DefaultSendFeatureToggles.kt b/features/send/impl/src/main/java/com/tangem/features/send/impl/featuretoggles/DefaultSendFeatureToggles.kt deleted file mode 100644 index b36cba4cbd..0000000000 --- a/features/send/impl/src/main/java/com/tangem/features/send/impl/featuretoggles/DefaultSendFeatureToggles.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.tangem.features.send.impl.featuretoggles - -import com.tangem.core.featuretoggle.manager.FeatureTogglesManager -import com.tangem.datasource.api.common.response.getOrThrow -import com.tangem.datasource.api.tangemTech.TangemTechApi -import com.tangem.features.send.api.featuretoggles.SendFeatureToggles -import com.tangem.utils.coroutines.CoroutineDispatcherProvider -import com.tangem.utils.coroutines.runCatching -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.update -import timber.log.Timber - -/** - * Default implementation of Send feature toggles - * - * @property featureTogglesManager manager for getting information about the availability of feature toggles - * @property tangemTechApi api to get remote feature toggle for send - * @property dispatchers coroutine dispatchers - */ -internal class DefaultSendFeatureToggles( - private val featureTogglesManager: FeatureTogglesManager, - private val tangemTechApi: TangemTechApi, - private val dispatchers: CoroutineDispatcherProvider, -) : SendFeatureToggles { - - private val remoteSendEnabled: MutableStateFlow = MutableStateFlow(true) - - override val isRedesignedSendEnabled: Boolean - get() = featureTogglesManager.isFeatureEnabled(name = "REDESIGNED_SEND_SCREEN_ENABLED") && - remoteSendEnabled.value - - override suspend fun fetchNewSendEnabled() { - runCatching(dispatchers.io) { - tangemTechApi.getFeatures().getOrThrow() - }.onSuccess { response -> - remoteSendEnabled.update { response.isNewSendEnabled } - }.onFailure { - Timber.e(it.localizedMessage, "Unable to fetch new send toggle") - } - } -} \ No newline at end of file