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 14af065b7b..c02184fd51 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 @@ -2,7 +2,7 @@ package com.tangem.tap.common.redux import com.tangem.tap.common.redux.global.globalReducer import com.tangem.tap.common.redux.navigation.NavigationReducer -import com.tangem.tap.features.send.redux.reducers.SendReducer +import com.tangem.tap.features.send.redux.reducers.SendScreenReducer import com.tangem.tap.features.details.redux.DetailsReducer import com.tangem.tap.features.wallet.redux.WalletReducer import org.rekotlin.Action @@ -15,7 +15,7 @@ fun appReducer(action: Action, state: AppState?): AppState { navigationState = NavigationReducer.reduce(action, state), globalState = globalReducer(action, state), walletState = WalletReducer.reduce(action, state), - sendState = SendReducer.reduce(action, state.sendState), + sendState = SendScreenReducer.reduce(action, state.sendState), detailsState = DetailsReducer.reduce(action, state) ) } diff --git a/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt b/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt index 9511364f8a..177089ffba 100644 --- a/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/common/redux/NotificationsMiddleware.kt @@ -1,5 +1,6 @@ package com.tangem.tap.common.redux +import android.widget.Toast import androidx.coordinatorlayout.widget.CoordinatorLayout import com.google.android.material.snackbar.Snackbar import com.tangem.tap.domain.TapError @@ -32,6 +33,12 @@ class NotificationsHandler(coordinatorLayout: CoordinatorLayout) { showNotification(it.context.getString(message)) } } + + fun showToastNotification(message: Int) { + baseLayout.get()?.let { + Toast.makeText(it.context, it.context.getString(message), Toast.LENGTH_LONG).show() + } + } } val notificationsMiddleware: Middleware = { dispatch, state -> @@ -40,6 +47,9 @@ val notificationsMiddleware: Middleware = { dispatch, state -> if (action is NotificationAction) { notificationsHandler?.showNotification(action.messageResource) } + if (action is ToastNotificationAction) { + notificationsHandler?.showToastNotification(action.messageResource) + } if (action is ErrorAction) { notificationsHandler?.showNotification(action.error.localizedMessage) } @@ -48,6 +58,10 @@ val notificationsMiddleware: Middleware = { dispatch, state -> } } +interface ToastNotificationAction : Action { + val messageResource: Int +} + interface NotificationAction : Action { val messageResource: Int } 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 new file mode 100644 index 0000000000..bb19069a99 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/text/DecimalDigitsInputFilter.kt @@ -0,0 +1,26 @@ +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) : InputFilter { + private val pattern: Pattern = Pattern.compile("(([1-9]{1}[0-9]{0," + (digitsBeforeDecimal - 1) + "})?||[0]{1})((\\.[0-9]{0," + digitsAfterDecimal + "})?)||(\\.)?") + + 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 + + val hasMatches = pattern.matcher(result).matches() + return if (hasMatches) null else "" + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/ToggleWidget.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/ToggleWidget.kt new file mode 100644 index 0000000000..153a5eddc8 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/toggleWidget/ToggleWidget.kt @@ -0,0 +1,85 @@ +package com.tangem.merchant.common.toggleWidget + +import android.view.View +import android.view.ViewGroup + + +/** +[REDACTED_AUTHOR] + */ +interface ToggleState + +interface StateModifier { + fun stateChanged(container: ViewGroup, view: View, state: ToggleState) +} + +interface ToggleView { + val mainViewModifiers: MutableList + val toggleViewModifiers: MutableList + + fun setState(state: ToggleState, andApply: Boolean = true) + fun applyState() + fun getView(): View + fun getMainView(): View + fun getToggleView(): View +} + +class ToggleWidget : ToggleView { + private val container: ViewGroup + private val mainView: View + private val toggleView: View + + private var state: ToggleState + + constructor( + container: ViewGroup, + mainView: View, + toggleView: View, + initialState: ToggleState, + mainViewModifier: List = mutableListOf(), + loadingViewModifier: List = mutableListOf() + ) { + this.container = container + this.mainView = mainView + this.toggleView = toggleView + this.state = initialState + this.mainViewModifiers.addAll(mainViewModifier) + this.toggleViewModifiers.addAll(loadingViewModifier) + } + + constructor( + container: ViewGroup, + mainViewId: Int, + toggleViewId: Int, + initialState: ToggleState, + mainViewModifier: List = mutableListOf(), + loadingViewModifier: List = mutableListOf() + ) { + this.container = container + this.mainView = container.findViewById(mainViewId) + this.toggleView = container.findViewById(toggleViewId) + this.state = initialState + this.mainViewModifiers.addAll(mainViewModifier) + this.toggleViewModifiers.addAll(loadingViewModifier) + } + + override val mainViewModifiers: MutableList = mutableListOf() + + override val toggleViewModifiers: MutableList = mutableListOf() + + override fun setState(state: ToggleState, andApply: Boolean) { + this.state = state + if (andApply) applyState() + } + + override fun applyState() { + mainViewModifiers.forEach { it.stateChanged(container, mainView, state) } + toggleViewModifiers.forEach { it.stateChanged(container, toggleView, state) } + } + + override fun getView(): View = container + + override fun getMainView(): View = mainView + + override fun getToggleView(): View = toggleView +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/common/toggleWidget/progressModifiers.kt b/app/src/main/java/com/tangem/tap/common/toggleWidget/progressModifiers.kt new file mode 100644 index 0000000000..bc66fa830b --- /dev/null +++ b/app/src/main/java/com/tangem/tap/common/toggleWidget/progressModifiers.kt @@ -0,0 +1,145 @@ +package com.tangem.tap.common.toggleWidget + +import android.graphics.drawable.Drawable +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import android.widget.TextView +import androidx.core.widget.TextViewCompat +import com.google.android.material.button.MaterialButton +import com.tangem.merchant.common.toggleWidget.StateModifier +import com.tangem.merchant.common.toggleWidget.ToggleState +import com.tangem.tap.common.extensions.beginDelayedTransition + +/** +[REDACTED_AUTHOR] + */ + +sealed class ProgressState : ToggleState { + class Progress : ProgressState() + class None : ProgressState() +} + +class ReplaceTextStateModifier( + private val initialText: String, + private val replaceText: String = "" +) : StateModifier { + + override fun stateChanged(container: ViewGroup, view: View, state: ToggleState) { + val tv = view as? TextView ?: return + + when (state) { + is ProgressState.Progress -> { + tv.text = replaceText + } + is ProgressState.None -> { + tv.text = initialText + } + } + } +} + +class TextViewDrawableStateModifier( + private val initialDrawable: Drawable?, + private val replaceDrawable: Drawable?, + private val position: Int +) : StateModifier { + companion object { + val LEFT = 1 + val RIGHT = 2 + } + + override fun stateChanged(container: ViewGroup, view: View, state: ToggleState) { + val drawable = when (state) { + is ProgressState.Progress -> replaceDrawable + is ProgressState.None -> initialDrawable + else -> null + } + val drawableChanger = getChanger(view) + drawableChanger?.change(drawable, position) + } + + private fun getChanger(view: View): DrawableChanger? = when (view) { + is MaterialButton -> MaterialButtonChanger(view) + is Button -> TextViewChanger(view) + is TextView -> TextViewChanger(view) + else -> null + } + + internal interface DrawableChanger { + fun change(drawable: Drawable?, position: Int) + } + + internal class TextViewChanger(private val view: TextView) : DrawableChanger { + override fun change(drawable: Drawable?, position: Int) { + when (position) { + LEFT -> setLeft(drawable) + RIGHT -> setRight(drawable) + } + } + + private fun setLeft(drawable: Drawable?) { + if (drawable == null) { + TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(view, 0, 0, 0, 0) + } else { + TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(view, drawable, null, null, null) + } + } + + private fun setRight(drawable: Drawable?) { + if (drawable == null) { + TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(view, 0, 0, 0, 0) + } else { + TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(view, null, null, drawable, null) + } + } + } + + internal class MaterialButtonChanger(private val view: MaterialButton) : DrawableChanger { + override fun change(drawable: Drawable?, position: Int) { + when (position) { + LEFT -> setLeft(drawable) + RIGHT -> setRight(drawable) + } + } + + private fun setLeft(drawable: Drawable?) { + view.iconGravity = MaterialButton.ICON_GRAVITY_START + view.icon = drawable + } + + private fun setRight(drawable: Drawable?) { + view.iconGravity = MaterialButton.ICON_GRAVITY_END + view.icon = drawable + } + } +} + +class ShowHideStateModifier( + private val isShowOnLoading: Boolean = true, + private val typeOfHiding: Int = View.INVISIBLE +) : StateModifier { + + override fun stateChanged(container: ViewGroup, view: View, state: ToggleState) { + container.beginDelayedTransition() + view.visibility = when (state) { + is ProgressState.Progress -> if (isShowOnLoading) View.VISIBLE else typeOfHiding + is ProgressState.None -> if (isShowOnLoading) typeOfHiding else View.VISIBLE + else -> return + } + } +} + +class ClickableStateModifier( + private val isClickableOnLoading: Boolean = false +) : StateModifier { + + override fun stateChanged(container: ViewGroup, view: View, state: ToggleState) { + view.isClickable = when (state) { + is ProgressState.Progress -> isClickableOnLoading + is ProgressState.None -> !isClickableOnLoading + else -> return + + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt index 0b76964de9..9fdfca3cd7 100644 --- a/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt +++ b/app/src/main/java/com/tangem/tap/domain/TangemSdkManager.kt @@ -2,8 +2,6 @@ package com.tangem.tap.domain import androidx.activity.ComponentActivity import com.tangem.* -import com.tangem.blockchain.common.Amount -import com.tangem.blockchain.common.WalletManager import com.tangem.commands.CommandResponse import com.tangem.common.CompletionResult import com.tangem.common.extensions.CardType @@ -11,7 +9,6 @@ import com.tangem.tangem_sdk_new.extensions.init import com.tangem.tap.domain.tasks.CreateWalletAndRescanTask import com.tangem.tap.domain.tasks.ScanNoteResponse import com.tangem.tap.domain.tasks.ScanNoteTask -import com.tangem.tap.domain.tasks.SendTask import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import java.util.* @@ -31,21 +28,6 @@ class TangemSdkManager(val activity: ComponentActivity) { return runTaskAsyncReturnOnMain(CreateWalletAndRescanTask()) } - suspend fun send( - walletManager: WalletManager, - recipientAddress: String, - amountToSend: Amount, - feeAmount: Amount - ): CompletionResult { - return withContext(Dispatchers.IO) { - suspendCoroutine { continuation -> - tangemSdk.startSessionWithRunnable(SendTask(walletManager, recipientAddress, amountToSend, feeAmount)) { - continuation.resume(it) - } - } - } - } - private suspend fun runTaskAsync( runnable: CardSessionRunnable, cardId: String? = null, initialMessage: Message? = null ): CompletionResult = 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 29147d93c6..4f98257223 100644 --- a/app/src/main/java/com/tangem/tap/domain/TapErrors.kt +++ b/app/src/main/java/com/tangem/tap/domain/TapErrors.kt @@ -10,5 +10,6 @@ sealed class TapError(@StringRes val localizedMessage: Int): Throwable() { object UnknownBlockchain: TapError(R.string.wallet_unknown_blockchain) object NoInternetConnection: TapError(R.string.notification_no_internet) object InsufficientBalance: TapError(R.string.error_insufficient_balance) - object BlockchainInternalError: TapError(R.string.error_insufficient_balance) + object BlockchainInternalError: TapError(R.string.error_blockchain_internal) + object UnknownError: TapError(R.string.error_unknown) } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/domain/tasks/SendTask.kt b/app/src/main/java/com/tangem/tap/domain/tasks/SendTask.kt deleted file mode 100644 index e5cdbf959a..0000000000 --- a/app/src/main/java/com/tangem/tap/domain/tasks/SendTask.kt +++ /dev/null @@ -1,114 +0,0 @@ -package com.tangem.tap.domain.tasks - -import com.tangem.CardSession -import com.tangem.CardSessionRunnable -import com.tangem.TangemError -import com.tangem.blockchain.common.Amount -import com.tangem.blockchain.common.TransactionSender -import com.tangem.blockchain.common.TransactionSigner -import com.tangem.blockchain.common.WalletManager -import com.tangem.blockchain.extensions.SimpleResult -import com.tangem.commands.CommandResponse -import com.tangem.commands.SignCommand -import com.tangem.commands.SignResponse -import com.tangem.common.CompletionResult -import com.tangem.tap.scope -import kotlinx.coroutines.launch -import kotlinx.coroutines.suspendCancellableCoroutine -import timber.log.Timber -import kotlin.coroutines.resume - -class SendTask( - private val walletManager: WalletManager, - private val recipientAddress: String, - private val amountToSend: Amount, - private val feeAmount: Amount, -) : CardSessionRunnable { - - override val requiresPin2: Boolean = false - - override fun run(session: CardSession, callback: (result: CompletionResult) -> Unit) { - val txSender = walletManager as TransactionSender - - val verifyResult = walletManager.validateTransaction(amountToSend, feeAmount) - if (verifyResult.isNotEmpty()) { - callback(CompletionResult.Failure(InsufficientBalance())) - return - } - val txData = walletManager.createTransaction(amountToSend, feeAmount, recipientAddress) - - scope.launch { - when (val result = txSender.send(txData, SessionTransactionSigner(session))) { - is SimpleResult.Success -> callback(CompletionResult.Success(SendResponse())) - is SimpleResult.Failure -> { - callback(CompletionResult.Failure(BlockchainInternalErrorConverter.convert(result.error))) - } - } - } - } -} - -class SendResponse : CommandResponse - -class SessionTransactionSigner( - private val session: CardSession -) : TransactionSigner { - override suspend fun sign(hashes: Array, cardId: String): CompletionResult = - suspendCancellableCoroutine { continuation -> - Timber.d("sign transaction...") - SignCommand(hashes).run(session) { - if (continuation.isActive) { - continuation.resume(it) - } - } - } -} - -abstract class SendError : TangemError { -} - -class UnknownError : SendError() { - override val code: Int = 1000 - override var customMessage: String = "Unknown error" -} - -open class ThrowableError(throwable: Throwable?) : SendError() { - override val code: Int = 1001 - override var customMessage: String = throwable?.localizedMessage ?: "Unknown exception" -} - -class InsufficientBalance( - override var customMessage: String = "Insufficient balance" -) : SendError() { - override val code: Int = 1021 -} - -class BlockchainInternalError( - override var customMessage: String -) : SendError() { - override val code: Int = 2000 -} - -class BlockchainInternalErrorConverter { - companion object { - - private val stellarInternalErrors = mapOf( - "tx_bad_seq" to "Sequence number does not match source account", - "tx_too_late" to "The ledger closeTime was after the maxTime", - "tx_failedop_no_destination" to "The destination account does not exist", - "tx_no_source_account" to "Source account not found" - ) - - fun convert(throwable: Throwable?): TangemError { - val message = throwable?.message ?: return ThrowableError(throwable) - - val customMessage = getInternalBlockchainErrorMessage(message) - return if (customMessage == null) ThrowableError(throwable) - else BlockchainInternalError(customMessage) - } - - private fun getInternalBlockchainErrorMessage(message: String): String? { - return stellarInternalErrors[message] - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt b/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt index 83182d57ac..fc2fef7c7b 100644 --- a/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt +++ b/app/src/main/java/com/tangem/tap/features/send/BaseStoreFragment.kt @@ -20,7 +20,7 @@ abstract class BaseStoreFragment(layoutId: Int) : Fragment(layoutId) { abstract fun subscribeToStore() - private lateinit var mainView: View + protected lateinit var mainView: View protected val storeSubscribersList = mutableListOf>() override fun onCreate(savedInstanceState: Bundle?) { 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 index 1f76fa2399..ba4308edc8 100644 --- 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 @@ -2,9 +2,12 @@ package com.tangem.tap.features.send.redux import com.tangem.blockchain.common.Amount import com.tangem.tap.common.redux.ErrorAction +import com.tangem.tap.common.redux.ToastNotificationAction import com.tangem.tap.domain.TapError import com.tangem.tap.features.send.redux.states.FeeType import com.tangem.tap.features.send.redux.states.MainCurrencyType +import com.tangem.tap.features.send.redux.states.SendButtonState +import com.tangem.wallet.R import org.rekotlin.Action import java.math.BigDecimal @@ -37,6 +40,9 @@ sealed class AddressPayIdVerifyAction : SendScreenAction { ADDRESS_SAME_AS_WALLET } + data class VerifyClipboard(val data: String?) : AddressPayIdVerifyAction() + data class ChangePasteBtnEnableState(val isEnabled: Boolean) : AddressPayIdVerifyAction() + sealed class PayIdVerification : AddressPayIdVerifyAction() { data class SetError(val payId: String, val error: Error) : PayIdVerification() data class SetPayIdWalletAddress(val payId: String, val payIdWalletAddress: String) : PayIdVerification() @@ -103,16 +109,11 @@ sealed class SendActionUi : SendScreenActionUi { } sealed class SendAction : SendScreenAction { - enum class Error { - INSUFFICIENT_BALANCE, BLOCKCHAIN_INTERNAL + + data class ChangeSendButtonState(val state: SendButtonState) : SendAction() + object SendSuccess : SendAction(), ToastNotificationAction { + override val messageResource: Int = R.string.send_transaction_complete } - object SendSuccess : SendAction() - - data class SendError(val sendError: Error) : SendAction(), ErrorAction { - override val error: TapError = when (sendError) { - Error.INSUFFICIENT_BALANCE -> TapError.InsufficientBalance - Error.BLOCKCHAIN_INTERNAL -> TapError.BlockchainInternalError - } - } + data class SendError(override val error: TapError) : SendAction(), ErrorAction } \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt index 60e0077661..ab345e9e35 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/middlewares/AddressPayIdMiddleware.kt @@ -17,7 +17,12 @@ import org.rekotlin.DispatchFunction [REDACTED_AUTHOR] */ internal class AddressPayIdMiddleware { - fun handle(data: String, appState: AppState?, dispatch: DispatchFunction) { + fun handle(data: String?, appState: AppState?, dispatch: DispatchFunction) { + if (data == null) { + dispatch(AddressVerification.SetError("", Error.ADDRESS_INVALID_OR_UNSUPPORTED_BY_BLOCKCHAIN)) + return + } + val sendState = appState?.sendState ?: return val walletManager = sendState.walletManager ?: return if (data == sendState.addressPayIdState.etFieldValue) return 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 index eb86509ec1..259051b8a4 100644 --- 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 @@ -6,6 +6,7 @@ import com.tangem.tap.common.extensions.isGreaterThanOrEqual import com.tangem.tap.common.redux.AppState import com.tangem.tap.features.send.redux.AmountAction 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.store import org.rekotlin.Action @@ -43,6 +44,7 @@ class AmountMiddleware { dispatch(AmountAction.AmountVerification.SetError(checkResult.amount, checkResult.error)) } dispatch(ReceiptAction.RefreshReceipt) + dispatch(SendAction.ChangeSendButtonState(sendState.getButtonState())) } } 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 index 331a83fe37..f1d202cf46 100644 --- 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 @@ -9,6 +9,7 @@ import com.tangem.common.extensions.isZero import com.tangem.tap.common.redux.AppState 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.scope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -29,6 +30,7 @@ class RequestFeeMiddleware { dispatch(FeeAction.FeeCalculation.SetFeeError(FeeAction.Error.ADDRESS_OR_AMOUNT_IS_EMPTY)) dispatch(FeeAction.ChangeLayoutVisibility(main = false, controls = true, chipGroup = true)) dispatch(ReceiptAction.RefreshReceipt) + dispatch(SendAction.ChangeSendButtonState(sendState.getButtonState())) return } @@ -67,6 +69,7 @@ class RequestFeeMiddleware { } } dispatch(ReceiptAction.RefreshReceipt) + dispatch(SendAction.ChangeSendButtonState(sendState.getButtonState())) } } 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 index 03645a9444..3f55dfddd5 100644 --- 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 @@ -1,16 +1,24 @@ package com.tangem.tap.features.send.redux.middlewares import com.tangem.blockchain.common.Amount -import com.tangem.common.CompletionResult +import com.tangem.blockchain.common.TransactionSender +import com.tangem.blockchain.extensions.Signer +import com.tangem.blockchain.extensions.SimpleResult import com.tangem.tap.common.redux.AppState +import com.tangem.tap.common.redux.navigation.NavigationAction +import com.tangem.tap.domain.TapError import com.tangem.tap.features.send.redux.AddressPayIdActionUi.ChangeAddressOrPayId +import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.* import com.tangem.tap.features.send.redux.AmountActionUi.CheckAmountToSend import com.tangem.tap.features.send.redux.FeeAction.RequestFee import com.tangem.tap.features.send.redux.SendAction import com.tangem.tap.features.send.redux.SendActionUi +import com.tangem.tap.features.send.redux.states.SendButtonState import com.tangem.tap.scope -import com.tangem.tap.tangemSdkManager +import com.tangem.tap.tangemSdk +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.rekotlin.Action import org.rekotlin.Middleware @@ -22,6 +30,18 @@ val sendMiddleware: Middleware = { dispatch, appState -> { action -> when (action) { is ChangeAddressOrPayId -> AddressPayIdMiddleware().handle(action.data, appState(), dispatch) + is VerifyClipboard -> { + AddressPayIdMiddleware().handle(action.data, appState()) { + when (it) { + is AddressVerification.SetWalletAddress, is PayIdVerification.SetPayIdWalletAddress -> { + dispatch(ChangePasteBtnEnableState(true)) + } + is AddressVerification.SetError, is PayIdVerification.SetError -> { + dispatch(ChangePasteBtnEnableState(false)) + } + } + } + } is CheckAmountToSend -> AmountMiddleware().handle(action.data, appState(), dispatch) is RequestFee -> RequestFeeMiddleware().handle(appState(), dispatch) is SendActionUi.SendAmountToRecipient -> verifyAndSendTransaction(appState(), dispatch) @@ -41,15 +61,44 @@ private fun verifyAndSendTransaction(appState: AppState?, dispatch: (Action) -> val feeAmount = Amount(sendState.feeState.getCurrentFee(), blockchain) val amountToSend = Amount(sendState.amountState.amountToSendCrypto, blockchain, recipientAddress) + val txSender = walletManager as TransactionSender + + val verifyResult = walletManager.validateTransaction(amountToSend, feeAmount) + if (verifyResult.isNotEmpty()) { + dispatch(SendAction.SendError(TapError.InsufficientBalance)) + return + } + + dispatch(SendAction.ChangeSendButtonState(SendButtonState.PROGRESS)) + val txData = walletManager.createTransaction(amountToSend, feeAmount, recipientAddress) scope.launch { - when (val sendResult = tangemSdkManager.send(walletManager, recipientAddress, amountToSend, feeAmount)) { - is CompletionResult.Success -> dispatch(SendAction.SendSuccess) - is CompletionResult.Failure -> { - when (sendResult.error.code) { - 1021 -> dispatch(SendAction.SendError(SendAction.Error.INSUFFICIENT_BALANCE)) - 1001, 2000 -> dispatch(SendAction.SendError(SendAction.Error.BLOCKCHAIN_INTERNAL)) + val result = txSender.send(txData, Signer(tangemSdk)) + withContext(Dispatchers.Main) { + when (result) { + is SimpleResult.Success -> { + dispatch(SendAction.SendSuccess) + dispatch(NavigationAction.PopBackTo()) + } + is SimpleResult.Failure -> { + when (result.error) { + is Throwable -> { + val message = (result.error as Throwable).message + when { + message == null -> { + dispatch(SendAction.SendError(TapError.UnknownError)) + } + message.contains("50002") -> { + // user was cancelled the operation by closing the Sdk bottom sheet + } + else -> { + dispatch(SendAction.SendError(TapError.BlockchainInternalError)) + } + } + } + } } } + dispatch(SendAction.ChangeSendButtonState(SendButtonState.ENABLED)) } } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressPayIdReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressPayIdReducer.kt index 72b7e5e05b..cc5fa3430f 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressPayIdReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/AddressPayIdReducer.kt @@ -36,6 +36,8 @@ class AddressPayIdReducer : SendInternalReducer { is PayIdVerification.SetError -> state.copyPayIdError(action.payId, action.error) is AddressVerification.SetWalletAddress -> state.copyWalletAddress(action.address) is AddressVerification.SetError -> state.copyError(action.address, action.error) + is AddressPayIdVerifyAction.ChangePasteBtnEnableState -> state.copy(pasteIsEnabled = action.isEnabled) + is AddressPayIdVerifyAction.VerifyClipboard -> state } return updateLastState(sendState.copy(addressPayIdState = result), result) } 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 index c4019ff028..efeae4552c 100644 --- 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 @@ -43,6 +43,7 @@ class AmountReducer : SendInternalReducer { viewAmountValue = fiatToSend.stripZeroPlainString(), viewBalanceValue = converter.toFiat(state.balanceCrypto).stripZeroPlainString(), mainCurrency = Value(MainCurrencyType.FIAT, TapCurrency.main), + maxLengthOfAmount = sendState.getDecimals(action.mainCurrency), cursorAtTheSamePosition = false ) } @@ -52,6 +53,7 @@ class AmountReducer : SendInternalReducer { viewAmountValue = state.amountToSendCrypto.stripZeroPlainString(), viewBalanceValue = state.balanceCrypto.stripZeroPlainString(), mainCurrency = mainCurrency, + maxLengthOfAmount = sendState.getDecimals(action.mainCurrency), cursorAtTheSamePosition = false ) } 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 index 0268a051d0..0776c99fe4 100644 --- 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 @@ -4,6 +4,7 @@ import com.tangem.blockchain.common.AmountType import com.tangem.blockchain.common.Wallet import com.tangem.tap.common.CurrencyConverter import com.tangem.tap.common.entities.TapCurrency +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.* @@ -29,14 +30,14 @@ class ReceiptReducer : SendInternalReducer { val layoutType = determineLayoutType(amountState.mainCurrency.value, amountState.typeOfAmount) val symbols = determineSymbols(wallet) + val showBlank = !sendState.isReadyToSend() val result = state.copy( visibleTypeOfReceipt = layoutType, mainCurrencyType = sendState.amountState.mainCurrency, - mainLayoutIsVisible = sendState.isReadyToSend(), - fiat = createFiatType(converter, amountState, feeState, symbols), - crypto = createCryptoType(converter, amountState, feeState, symbols), - tokenFiat = createTokenFiatType(converter, amountState, feeState, symbols), - tokenCrypto = createTokenCryptoType(converter, amountState, feeState, symbols) + fiat = createFiatType(converter, amountState, feeState, symbols, showBlank), + crypto = createCryptoType(converter, amountState, feeState, symbols, showBlank), + tokenFiat = createTokenFiatType(converter, amountState, feeState, symbols, showBlank), + tokenCrypto = createTokenCryptoType(converter, amountState, feeState, symbols, showBlank) ) return updateLastState(sendState.copy(receiptState = result), result) } @@ -45,19 +46,24 @@ class ReceiptReducer : SendInternalReducer { converter: CurrencyConverter, amountState: AmountState, feeState: FeeState, - symbols: ReceiptSymbols + symbols: ReceiptSymbols, + showBlank: Boolean ): ReceiptFiat { val feeCrypto = feeState.getCurrentFee() val feeFiat = converter.toFiat(feeCrypto) + if (showBlank) { + return ReceiptFiat("0", feeFiat.stripZeroPlainString(), feeFiat.stripZeroPlainString(), "0", symbols) + } + return if (feeState.feeIsIncluded) { val amountFiat = converter.toFiat(amountState.amountToSendCrypto.minus(feeCrypto)) val totalFiat = converter.toFiat(amountState.amountToSendCrypto) ReceiptFiat( - amountFiat = amountFiat, - feeFiat = feeFiat, - totalFiat = totalFiat, - willSentCrypto = amountState.amountToSendCrypto.stripTrailingZeros(), + amountFiat = amountFiat.stripZeroPlainString(), + feeFiat = feeFiat.stripZeroPlainString(), + totalFiat = totalFiat.stripZeroPlainString(), + willSentCrypto = amountState.amountToSendCrypto.stripZeroPlainString(), symbols = symbols ) } else { @@ -65,10 +71,10 @@ class ReceiptReducer : SendInternalReducer { val amountFiat = converter.toFiat(amountState.amountToSendCrypto) val totalFiat = converter.toFiat(totalAmountCrypto) ReceiptFiat( - amountFiat = amountFiat, - feeFiat = feeFiat, - totalFiat = totalFiat, - willSentCrypto = totalAmountCrypto.stripTrailingZeros(), + amountFiat = amountFiat.stripZeroPlainString(), + feeFiat = feeFiat.stripZeroPlainString(), + totalFiat = totalFiat.stripZeroPlainString(), + willSentCrypto = totalAmountCrypto.stripZeroPlainString(), symbols = symbols ) } @@ -78,25 +84,32 @@ class ReceiptReducer : SendInternalReducer { converter: CurrencyConverter, amountState: AmountState, feeState: FeeState, - symbols: ReceiptSymbols + symbols: ReceiptSymbols, + showBlank: Boolean ): ReceiptCrypto { val feeCrypto = feeState.getCurrentFee() + if (showBlank) { + return ReceiptCrypto("0", feeCrypto.stripZeroPlainString(), "0", converter.toFiat(feeCrypto).stripZeroPlainString(), "0", symbols) + } + if (feeState.feeIsIncluded) { return ReceiptCrypto( - amountCrypto = amountState.amountToSendCrypto.minus(feeCrypto).stripTrailingZeros(), - feeCrypto = feeCrypto.stripTrailingZeros(), - totalCrypto = amountState.amountToSendCrypto.stripTrailingZeros(), - willSentFiat = converter.toFiat(amountState.amountToSendCrypto), + amountCrypto = amountState.amountToSendCrypto.minus(feeCrypto).stripZeroPlainString(), + feeCrypto = feeCrypto.stripZeroPlainString(), + totalCrypto = amountState.amountToSendCrypto.stripZeroPlainString(), + feeFiat = converter.toFiat(feeCrypto).stripZeroPlainString(), + willSentFiat = converter.toFiat(amountState.amountToSendCrypto).stripZeroPlainString(), symbols = symbols ) } else { val totalCrypto = amountState.amountToSendCrypto.plus(feeCrypto) return ReceiptCrypto( - amountCrypto = amountState.amountToSendCrypto.stripTrailingZeros(), - feeCrypto = feeCrypto.stripTrailingZeros(), - totalCrypto = totalCrypto.stripTrailingZeros(), - willSentFiat = converter.toFiat(totalCrypto), + amountCrypto = amountState.amountToSendCrypto.stripZeroPlainString(), + feeCrypto = feeCrypto.stripZeroPlainString(), + totalCrypto = totalCrypto.stripZeroPlainString(), + feeFiat = converter.toFiat(feeCrypto).stripZeroPlainString(), + willSentFiat = converter.toFiat(totalCrypto).stripZeroPlainString(), symbols = symbols ) } @@ -106,17 +119,23 @@ class ReceiptReducer : SendInternalReducer { converter: CurrencyConverter, amountState: AmountState, feeState: FeeState, - symbols: ReceiptSymbols + symbols: ReceiptSymbols, + showBlank: Boolean ): ReceiptTokenFiat { val feeCrypto = feeState.getCurrentFee() val amountFiat = converter.toFiat(amountState.amountToSendCrypto) val feeFiat = converter.toFiat(feeCrypto) + + if (showBlank) { + return ReceiptTokenFiat("0", feeFiat.stripZeroPlainString(), "0", "0", "0", symbols) + } + return ReceiptTokenFiat( - amountFiat = amountFiat, - feeFiat = feeFiat, - totalFiat = amountFiat.plus(feeFiat), - willSentTokenCrypto = amountState.amountToSendCrypto.stripTrailingZeros(), - willSentFeeCrypto = feeCrypto.stripTrailingZeros(), + amountFiat = amountFiat.stripZeroPlainString(), + feeFiat = feeFiat.stripZeroPlainString(), + totalFiat = amountFiat.plus(feeFiat).stripZeroPlainString(), + willSentTokenCrypto = amountState.amountToSendCrypto.stripZeroPlainString(), + willSentFeeCrypto = feeCrypto.stripZeroPlainString(), symbols = symbols ) } @@ -125,14 +144,19 @@ class ReceiptReducer : SendInternalReducer { converter: CurrencyConverter, amountState: AmountState, feeState: FeeState, - symbols: ReceiptSymbols + symbols: ReceiptSymbols, + showBlank: Boolean ): ReceiptTokenCrypto { val feeCrypto = feeState.getCurrentFee() val totalFiat = converter.toFiat(amountState.amountToSendCrypto).plus(converter.toFiat(feeCrypto)) + if (showBlank) { + return ReceiptTokenCrypto("0", feeCrypto.stripZeroPlainString(), "0", symbols) + } + return ReceiptTokenCrypto( - amountToken = amountState.amountToSendCrypto.stripTrailingZeros(), - feeCrypto = feeCrypto.stripTrailingZeros(), - totalFiat = totalFiat.stripTrailingZeros(), + amountToken = amountState.amountToSendCrypto.stripZeroPlainString(), + feeCrypto = feeCrypto.stripZeroPlainString(), + totalFiat = totalFiat.stripZeroPlainString(), symbols = symbols ) } diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendReducer.kt b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt similarity index 77% rename from app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendReducer.kt rename to app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt index 4f5d12a94b..06b4e51957 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendReducer.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/reducers/SendScreenReducer.kt @@ -3,11 +3,10 @@ package com.tangem.tap.features.send.redux.reducers import com.tangem.blockchain.common.WalletManager import com.tangem.tap.common.CurrencyConverter import com.tangem.tap.features.send.redux.* +import com.tangem.tap.features.send.redux.states.IdStateHolder import com.tangem.tap.features.send.redux.states.SendState import com.tangem.tap.store import org.rekotlin.Action -import org.rekotlin.StateType -import timber.log.Timber import java.math.BigDecimal /** @@ -17,7 +16,7 @@ interface SendInternalReducer { fun handle(action: SendScreenAction, sendState: SendState): SendState } -class SendReducer { +class SendScreenReducer { companion object { fun reduce(incomingAction: Action, sendState: SendState): SendState { if (incomingAction is ReleaseSendState) return SendState() @@ -29,18 +28,26 @@ class SendReducer { is AmountActionUi, is AmountAction -> AmountReducer() is FeeActionUi, is FeeAction -> FeeReducer() is ReceiptAction -> ReceiptReducer() + is SendAction -> SendReducer() else -> EmptyReducer() } - - val newState = reducer.handle(action, sendState).copy(sendButtonIsEnabled = sendState.isReadyToSend()) - Timber.i("${newState.lastChangedStateType}.") - - return newState + 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 = action.state) + else -> return sendState + } + + return updateLastState(result, result) + } +} + private class EmptyReducer : SendInternalReducer { override fun handle(action: SendScreenAction, sendState: SendState): SendState = sendState } @@ -63,5 +70,7 @@ private class PrepareSendScreenStatesReducer : SendInternalReducer { } } -internal fun updateLastState(sendState: SendState, lastChangedState: StateType): SendState = - sendState.copy(lastChangedStateType = lastChangedState) +internal fun updateLastState(sendState: SendState, lastChangedState: IdStateHolder): SendState { + sendState.lastChangedStates.add(lastChangedState.stateId) + return sendState +} diff --git a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt index 1b6583fdd5..e7b081c183 100644 --- a/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt +++ b/app/src/main/java/com/tangem/tap/features/send/redux/states/AddressPayIdState.kt @@ -1,7 +1,6 @@ package com.tangem.tap.features.send.redux.states import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction -import org.rekotlin.StateType data class AddressPayIdState( val etFieldValue: String? = null, @@ -9,8 +8,10 @@ data class AddressPayIdState( val truncatedFieldValue: String? = null, val recipientWalletAddress: String? = null, val error: AddressPayIdVerifyAction.Error? = null, - val truncateHandler: ((String) -> String)? = null -) : StateType { + val truncateHandler: ((String) -> String)? = null, + val pasteIsEnabled: Boolean = false, + override val stateId: StateId = StateId.ADDRESS_PAY_ID +) : SendScreenState { fun isReady(): Boolean = error == null && recipientWalletAddress?.isNotEmpty() ?: false 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 index 2a0f019897..2c95438ca3 100644 --- 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 @@ -2,7 +2,6 @@ package com.tangem.tap.features.send.redux.states import com.tangem.blockchain.common.Amount import com.tangem.tap.features.send.redux.FeeAction -import org.rekotlin.StateType import java.math.BigDecimal /** @@ -20,8 +19,9 @@ data class FeeState( val mainLayoutIsVisible: Boolean = false, val controlsLayoutIsVisible: Boolean = true, val feeChipGroupIsVisible: Boolean = true, - val error: FeeAction.Error? = null -) : StateType { + val error: FeeAction.Error? = null, + override val stateId: StateId = StateId.FEE +) : SendScreenState { fun isReady(): Boolean = error == null && currentFee != null fun getCurrentFee(): BigDecimal = currentFee?.value?.value ?: BigDecimal.ZERO 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 index 7efe70ff14..321cfe43b8 100644 --- 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 @@ -1,8 +1,5 @@ package com.tangem.tap.features.send.redux.states -import org.rekotlin.StateType -import java.math.BigDecimal - // Shows only one type of the layout enum class ReceiptLayoutType { UNKNOWN, FIAT, CRYPTO, TOKEN_FIAT, TOKEN_CRYPTO @@ -15,8 +12,8 @@ data class ReceiptState( val tokenFiat: ReceiptTokenFiat? = null, val tokenCrypto: ReceiptTokenCrypto? = null, val mainCurrencyType: Value? = null, - val mainLayoutIsVisible: Boolean = false, -) : StateType + override val stateId: StateId = StateId.RECEIPT +) : SendScreenState data class ReceiptSymbols( val fiat: String, @@ -25,33 +22,34 @@ data class ReceiptSymbols( ) data class ReceiptFiat( - val amountFiat: BigDecimal, - val feeFiat: BigDecimal, - val totalFiat: BigDecimal, - val willSentCrypto: BigDecimal, + val amountFiat: String, + val feeFiat: String, + val totalFiat: String, + val willSentCrypto: String, val symbols: ReceiptSymbols ) data class ReceiptCrypto( - val amountCrypto: BigDecimal, - val feeCrypto: BigDecimal, - val totalCrypto: BigDecimal, - val willSentFiat: BigDecimal, + val amountCrypto: String, + val feeCrypto: String, + val totalCrypto: String, + val feeFiat: String, + val willSentFiat: String, val symbols: ReceiptSymbols ) data class ReceiptTokenCrypto( - val amountToken: BigDecimal, - val feeCrypto: BigDecimal, - val totalFiat: BigDecimal, + val amountToken: String, + val feeCrypto: String, + val totalFiat: String, val symbols: ReceiptSymbols ) data class ReceiptTokenFiat( - val amountFiat: BigDecimal, - val feeFiat: BigDecimal, - val totalFiat: BigDecimal, - val willSentTokenCrypto: BigDecimal, - val willSentFeeCrypto: BigDecimal, + val amountFiat: String, + val feeFiat: String, + val totalFiat: String, + val willSentTokenCrypto: String, + val willSentFeeCrypto: 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 index 8a23874e94..4d3cc3cd98 100644 --- 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 @@ -14,17 +14,29 @@ import java.math.BigDecimal /** [REDACTED_AUTHOR] */ + +interface IdStateHolder { + val stateId: StateId +} + +enum class StateId { + SEND_SCREEN, ADDRESS_PAY_ID, AMOUNT, FEE, RECEIPT +} + +interface SendScreenState : StateType, IdStateHolder + data class SendState( val amount: Amount? = null, val walletManager: WalletManager? = null, val currencyConverter: CurrencyConverter = CurrencyConverter(BigDecimal.ONE), - val lastChangedStateType: StateType = NoneState(), + val lastChangedStates: LinkedHashSet = linkedSetOf(), val addressPayIdState: AddressPayIdState = AddressPayIdState(), val amountState: AmountState = AmountState(), val feeState: FeeState = FeeState(), val receiptState: ReceiptState = ReceiptState(), - val sendButtonIsEnabled: Boolean = false, -) : StateType { + val sendButtonState: SendButtonState = SendButtonState.DISABLED, + override val stateId: StateId = StateId.SEND_SCREEN +) : SendScreenState { fun isReadyToSend(): Boolean { val sendState = store.state.sendState @@ -32,9 +44,18 @@ data class SendState( } fun addressPayIdIsReady(): Boolean = store.state.sendState.addressPayIdState.isReady() + + fun getDecimals(type: MainCurrencyType): Int = when (type) { + MainCurrencyType.FIAT -> 2 + MainCurrencyType.CRYPTO -> amount?.decimals ?: 0 + } + + fun getButtonState(): SendButtonState = if (isReadyToSend()) SendButtonState.ENABLED else SendButtonState.DISABLED } -class NoneState : StateType +enum class SendButtonState { + ENABLED, DISABLED, PROGRESS +} data class AmountState( val viewAmountValue: String = BigDecimal.ZERO.toPlainString(), @@ -44,8 +65,10 @@ data class AmountState( val amountToSendCrypto: BigDecimal = BigDecimal.ZERO, val balanceCrypto: BigDecimal = BigDecimal.ZERO, val cursorAtTheSamePosition: Boolean = true, - val error: AmountAction.Error? = null -) : StateType { + val maxLengthOfAmount: Int = 2, + val error: AmountAction.Error? = null, + override val stateId: StateId = StateId.AMOUNT +) : SendScreenState { fun isReady(): Boolean = error == null && !amountToSendCrypto.isZero() } 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 index 1fd7e7664d..5665e0ffa2 100644 --- 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 @@ -8,30 +8,28 @@ import android.view.inputmethod.EditorInfo import android.widget.EditText import androidx.core.view.postDelayed import androidx.core.widget.addTextChangedListener +import com.tangem.merchant.common.toggleWidget.ToggleWidget import com.tangem.tangem_sdk_new.extensions.hideSoftKeyboard import com.tangem.tap.common.KeyboardObserver import com.tangem.tap.common.entities.TapCurrency +import com.tangem.tap.common.extensions.getDrawableCompat import com.tangem.tap.common.extensions.getFromClipboard import com.tangem.tap.common.extensions.setOnImeActionListener import com.tangem.tap.common.qrCodeScan.ScanQrCodeActivity import com.tangem.tap.common.snackBar.MaxAmountSnackbar import com.tangem.tap.common.text.truncateMiddleWith +import com.tangem.tap.common.toggleWidget.* import com.tangem.tap.features.send.BaseStoreFragment +import com.tangem.tap.features.send.redux.* import com.tangem.tap.features.send.redux.AddressPayIdActionUi.* 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.ReleaseSendState -import com.tangem.tap.features.send.redux.SendActionUi 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.stateSubscribers.SendStateSubscriber import com.tangem.tap.mainScope import com.tangem.tap.store import com.tangem.wallet.R -import kotlinx.android.synthetic.main.btn_paste.* -import kotlinx.android.synthetic.main.btn_qr_code.* import kotlinx.android.synthetic.main.fragment_send.* import kotlinx.android.synthetic.main.layout_send_address_payid.* import kotlinx.android.synthetic.main.layout_send_amount.* @@ -45,12 +43,21 @@ import kotlinx.coroutines.flow.* */ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { + lateinit var sendBtn: ToggleWidget + + private fun initSendButtonStates() { + sendBtn = ToggleWidget(flSendButtonContainer, btnSend, progress, ProgressState.None()) + sendBtn.setupSendButtonStateModifiers(requireContext()) + sendBtn.setState(ProgressState.None()) + } + private val sendSubscriber = SendStateSubscriber(this) private lateinit var keyboardObserver: KeyboardObserver override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + initSendButtonStates() setupAddressOrPayIdLayout() setupAmountLayout() setupFeeLayout() @@ -61,7 +68,8 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { } private fun setupAddressOrPayIdLayout() { - store.dispatch(SetTruncateHandler { etAddressOrPayId.truncateMiddleWith(it, " *** ") }) + store.dispatch(SetTruncateHandler { etAddressOrPayId.truncateMiddleWith(it, "...") }) + store.dispatch(AddressPayIdVerifyAction.VerifyClipboard(requireContext().getFromClipboard()?.toString())) etAddressOrPayId.setOnFocusChangeListener { v, hasFocus -> store.dispatch(TruncateOrRestore(!hasFocus)) @@ -92,16 +100,22 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { if (requestCode != ScanQrCodeActivity.SCAN_QR_REQUEST_CODE) return val scannedCode = data?.getStringExtra(ScanQrCodeActivity.SCAN_RESULT) ?: "" + if (scannedCode.isEmpty()) return + store.dispatch(ChangeAddressOrPayId(scannedCode)) store.dispatch(TruncateOrRestore(!etAddressOrPayId.isFocused)) - view?.postDelayed(200) { store.dispatch(FeeAction.RequestFee) } + store.dispatch(FeeAction.RequestFee) } private fun setupAmountLayout() { store.dispatch(SetMainCurrency(restoreMainCurrency())) + store.dispatch(ReceiptAction.RefreshReceipt) + store.dispatch(SendAction.ChangeSendButtonState(store.state.sendState.getButtonState())) + tvAmountCurrency.setOnClickListener { store.dispatch(ToggleMainCurrency) store.dispatch(ReceiptAction.RefreshReceipt) + store.dispatch(SendAction.ChangeSendButtonState(store.state.sendState.getButtonState())) } val maxAmountSnackbar = MaxAmountSnackbar.make(etAmountToSend) { @@ -140,7 +154,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) { 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() == "0") etAmountToSend.setText("") if (!hasFocus && etAmountToSend.text?.toString() == "") etAmountToSend.setText("0") } @@ -229,5 +243,15 @@ class FeeUiHelper { } } - +private fun ToggleWidget.setupSendButtonStateModifiers(context: Context) { + mainViewModifiers.clear() + mainViewModifiers.add(ReplaceTextStateModifier(context.getString(R.string.send_btn_send), "")) + mainViewModifiers.add( + TextViewDrawableStateModifier( + context.getDrawableCompat(R.drawable.ic_arrow_right), null, TextViewDrawableStateModifier.RIGHT + )) + mainViewModifiers.add(ClickableStateModifier()) + toggleViewModifiers.clear() + toggleViewModifiers.add(ShowHideStateModifier()) +} 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 index ec819fae5d..c4ee8d767a 100644 --- 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 @@ -2,13 +2,14 @@ package com.tangem.tap.features.send.ui.stateSubscribers import android.content.Context import android.text.SpannableStringBuilder -import android.util.TypedValue import android.view.ViewGroup import androidx.core.text.bold import com.tangem.tap.common.extensions.beginDelayedTransition import com.tangem.tap.common.extensions.enableError import com.tangem.tap.common.extensions.show import com.tangem.tap.common.extensions.update +import com.tangem.tap.common.text.DecimalDigitsInputFilter +import com.tangem.tap.common.toggleWidget.ProgressState import com.tangem.tap.features.send.BaseStoreFragment import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.Error import com.tangem.tap.features.send.redux.AmountAction @@ -34,14 +35,32 @@ import kotlinx.android.synthetic.main.layout_send_receipt.* class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber(fragment) { override fun updateWithNewState(fg: BaseStoreFragment, state: SendState) { - when (state.lastChangedStateType) { - is FeeState -> handleFeeState(fg, state.feeState) - is AddressPayIdState -> handleAddressPayIdState(fg, state.addressPayIdState) - is AmountState -> handleAmountState(fg, state.amountState) - is ReceiptState -> handleReceiptState(fg, state.receiptState) + val lastChangedStates = state.lastChangedStates.toList() + state.lastChangedStates.clear() + lastChangedStates.forEach { + when (it) { + StateId.ADDRESS_PAY_ID -> handleAddressPayIdState(fg, state.addressPayIdState) + StateId.AMOUNT -> handleAmountState(fg, state.amountState) + StateId.FEE -> handleFeeState(fg, state.feeState) + StateId.RECEIPT -> handleReceiptState(fg, state.receiptState) + } } - fg.btnSend.isEnabled = state.sendButtonIsEnabled + val sendFragment = (fg as? SendFragment) ?: return + when (state.sendButtonState) { + SendButtonState.ENABLED -> { + fg.btnSend.isEnabled = true + sendFragment.sendBtn.setState(ProgressState.None(), true) + } + SendButtonState.DISABLED -> { + fg.btnSend.isEnabled = false + sendFragment.sendBtn.setState(ProgressState.None(), true) + } + SendButtonState.PROGRESS -> { + fg.btnSend.isEnabled = true + sendFragment.sendBtn.setState(ProgressState.Progress(), true) + } + } } private fun handleAddressPayIdState(fg: BaseStoreFragment, state: AddressPayIdState) { @@ -58,6 +77,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber return if (resId == null) null else context.getString(resId) } + fg.imvPaste.isEnabled = state.pasteIsEnabled val et = fg.etAddressOrPayId val til = fg.tilAddressOrPayId @@ -91,22 +111,25 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber } } + fg.etAmountToSend.filters = arrayOf(DecimalDigitsInputFilter(12, state.maxLengthOfAmount)) val amountToSend = state.viewAmountValue - fg.tvAmountToSendShadow.text = amountToSend - if (amountToSend.length > 10) { - // post is needed to wait for text size changes - fg.tvAmountToSendShadow.post { - fg.etAmountToSend.setTextSize(TypedValue.COMPLEX_UNIT_PX, fg.tvAmountToSendShadow.textSize - 2) - fg.etAmountToSend.update(amountToSend) - if (!state.cursorAtTheSamePosition) fg.etAmountToSend.setSelection(amountToSend.length) - } - } else { - val textSize = fg.resources.getDimension(R.dimen.text_size_amount_to_send) - fg.tvAmountToSendShadow.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize) - fg.etAmountToSend.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize) - fg.etAmountToSend.update(amountToSend) - if (!state.cursorAtTheSamePosition) fg.etAmountToSend.setSelection(amountToSend.length) - } + fg.etAmountToSend.update(amountToSend) + +// fg.tvAmountToSendShadow.text = amountToSend +// if (amountToSend.length > 10) { +// post is needed to wait for text size changes +// fg.tvAmountToSendShadow.post { +// fg.etAmountToSend.setTextSize(TypedValue.COMPLEX_UNIT_PX, fg.tvAmountToSendShadow.textSize - 2) +// fg.etAmountToSend.update(amountToSend) +// if (!state.cursorAtTheSamePosition) fg.etAmountToSend.setSelection(amountToSend.length) +// } +// } else { +// val textSize = fg.resources.getDimension(R.dimen.text_size_amount_to_send) +// fg.tvAmountToSendShadow.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize) +// fg.etAmountToSend.setTextSize(TypedValue.COMPLEX_UNIT_PX, textSize) +// fg.etAmountToSend.update(amountToSend) +// if (!state.cursorAtTheSamePosition) fg.etAmountToSend.setSelection(amountToSend.length) +// } fg.tvAmountCurrency.update(state.mainCurrency.displayedValue) (fg as? SendFragment)?.saveMainCurrency(state.mainCurrency.value) @@ -159,22 +182,21 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber val totalTokenLayout = fg.flTotalTokenCrypto as ViewGroup fun getString(id: Int): String = mainLayout.context.getString(id) - mainLayout.show(state.mainLayoutIsVisible) when (state.visibleTypeOfReceipt) { ReceiptLayoutType.FIAT -> { val receipt = state.fiat ?: return totalLayout.show(true) totalTokenLayout.show(false) - fg.tvReceiptAmountValue.update("${receipt.amountFiat.toPlainString()} ${receipt.symbols.fiat}") - fg.tvReceiptFeeValue.update("${receipt.feeFiat.toPlainString()} ${receipt.symbols.fiat}") - totalLayout.tvTotalValue.update("${receipt.totalFiat.toPlainString()} ${receipt.symbols.fiat}") + fg.tvReceiptAmountValue.update("${receipt.amountFiat} ${receipt.symbols.fiat}") + fg.tvReceiptFeeValue.update("${receipt.feeFiat} ${receipt.symbols.fiat}") + totalLayout.tvTotalValue.update("${receipt.totalFiat} ${receipt.symbols.fiat}") val willSent = SpannableStringBuilder() - .bold { append(receipt.willSentCrypto.toPlainString()) }.append(" ") + .bold { append(receipt.willSentCrypto) }.append(" ") .append(receipt.symbols.crypto).append(" ") .append(getString(R.string.send_total_will_be_sent)) - totalLayout.tvWillBeSentValue.update(willSent) + totalLayout.tvWillBeSentValue.update(willSent.toString()) } ReceiptLayoutType.CRYPTO -> { @@ -182,41 +204,41 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber totalLayout.show(true) totalTokenLayout.show(false) - fg.tvReceiptAmountValue.update("${receipt.amountCrypto.toPlainString()} ${receipt.symbols.crypto}") - fg.tvReceiptFeeValue.update("${receipt.feeCrypto.toPlainString()} ${receipt.symbols.crypto}") - totalLayout.tvTotalValue.update("${receipt.totalCrypto.toPlainString()} ${receipt.symbols.crypto}") + fg.tvReceiptAmountValue.update("${receipt.amountCrypto} ${receipt.symbols.crypto}") + fg.tvReceiptFeeValue.update("${receipt.feeCrypto} ${receipt.symbols.crypto}") + totalLayout.tvTotalValue.update("${receipt.totalCrypto} ${receipt.symbols.crypto}") val willSent = SpannableStringBuilder() .bold { - append(getString(R.string.sign_rough)) - append(" ") - append(receipt.willSentFiat.toPlainString()) - append(" ") + append(getString(R.string.sign_rough)).append(" ") + append(receipt.willSentFiat).append(" ") append(receipt.symbols.fiat) + append(" (fee: ${receipt.feeFiat} ") + append(receipt.symbols.fiat).append(")") } - totalLayout.tvWillBeSentValue.update(willSent) + totalLayout.tvWillBeSentValue.update(willSent.toString()) } ReceiptLayoutType.TOKEN_FIAT -> { val receipt = state.tokenFiat ?: return totalLayout.show(true) totalTokenLayout.show(false) - fg.tvReceiptAmountValue.update("${receipt.amountFiat.toPlainString()} ${receipt.symbols.fiat}") - fg.tvReceiptFeeValue.update("${receipt.feeFiat.toPlainString()} ${receipt.symbols.fiat}") - totalLayout.tvTotalValue.update("${receipt.totalFiat.toPlainString()} ${receipt.symbols.fiat}") + fg.tvReceiptAmountValue.update("${receipt.amountFiat} ${receipt.symbols.fiat}") + fg.tvReceiptFeeValue.update("${receipt.feeFiat} ${receipt.symbols.fiat}") + totalLayout.tvTotalValue.update("${receipt.totalFiat} ${receipt.symbols.fiat}") val willSent = SpannableStringBuilder() .bold { append(receipt.symbols.token) append(" ") - append(receipt.willSentFeeCrypto.toPlainString()) + append(receipt.willSentFeeCrypto) }.append(" ").append(getString(R.string.generic_and)).append(" ") .bold { append(receipt.symbols.crypto).append(" ") - append(receipt.willSentFeeCrypto.toPlainString()).append(" ") + append(receipt.willSentFeeCrypto).append(" ") } .append(mainLayout.context.getString(R.string.send_total_will_be_sent)) - totalLayout.tvWillBeSentValue.update(willSent) + totalLayout.tvWillBeSentValue.update(willSent.toString()) } ReceiptLayoutType.TOKEN_CRYPTO -> { val receipt = state.tokenCrypto ?: return @@ -224,18 +246,16 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber totalLayout.show(false) totalTokenLayout.show(true) - fg.tvReceiptAmountValue.update("${receipt.amountToken.toPlainString()} ${receipt.symbols.token}") - fg.tvReceiptFeeValue.update("${receipt.feeCrypto.toPlainString()} ${receipt.symbols.crypto}") + fg.tvReceiptAmountValue.update("${receipt.amountToken} ${receipt.symbols.token}") + fg.tvReceiptFeeValue.update("${receipt.feeCrypto} ${receipt.symbols.crypto}") val willSent = SpannableStringBuilder() .bold { - append(getString(R.string.sign_rough)) - append(" ") - append(receipt.totalFiat.toPlainString()) - append(" ") + append(getString(R.string.sign_rough)).append(" ") + append(receipt.totalFiat).append(" ") append(receipt.symbols.fiat) } - totalTokenLayout.tvTotalTokenCryptoValue.update(willSent) + totalTokenLayout.tvTotalTokenCryptoValue.update(willSent.toString()) } } } diff --git a/app/src/main/res/drawable/ic_paste.xml b/app/src/main/res/drawable/ic_paste.xml index 5697018aec..5bc825fa49 100644 --- a/app/src/main/res/drawable/ic_paste.xml +++ b/app/src/main/res/drawable/ic_paste.xml @@ -1,9 +1,9 @@ + android:width="16dp" + android:height="19dp" + android:viewportWidth="16" + android:viewportHeight="19"> + android:pathData="M13.8333,1.6667H10.35C10,0.7 9.0833,0 8,0C6.9167,0 6,0.7 5.65,1.6667H2.1667C1.25,1.6667 0.5,2.4167 0.5,3.3333V16.6667C0.5,17.5833 1.25,18.3333 2.1667,18.3333H13.8333C14.75,18.3333 15.5,17.5833 15.5,16.6667V3.3333C15.5,2.4167 14.75,1.6667 13.8333,1.6667ZM8,1.6667C8.4583,1.6667 8.8333,2.0417 8.8333,2.5C8.8333,2.9583 8.4583,3.3333 8,3.3333C7.5417,3.3333 7.1667,2.9583 7.1667,2.5C7.1667,2.0417 7.5417,1.6667 8,1.6667ZM13.8333,16.6667H2.1667V3.3333H3.8333V5.8333H12.1667V3.3333H13.8333V16.6667Z" + android:fillColor="#1C1C1E"/> diff --git a/app/src/main/res/drawable/ic_paste_disabled.xml b/app/src/main/res/drawable/ic_paste_disabled.xml new file mode 100644 index 0000000000..7666c5a52d --- /dev/null +++ b/app/src/main/res/drawable/ic_paste_disabled.xml @@ -0,0 +1,11 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/paste_selector.xml b/app/src/main/res/drawable/paste_selector.xml new file mode 100644 index 0000000000..7d83495828 --- /dev/null +++ b/app/src/main/res/drawable/paste_selector.xml @@ -0,0 +1,9 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/btn_arrow_up_down.xml b/app/src/main/res/layout/btn_arrow_up_down.xml deleted file mode 100644 index 6f9c5f9bc0..0000000000 --- a/app/src/main/res/layout/btn_arrow_up_down.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/btn_paste.xml b/app/src/main/res/layout/btn_paste.xml deleted file mode 100644 index e561f4477f..0000000000 --- a/app/src/main/res/layout/btn_paste.xml +++ /dev/null @@ -1,22 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/btn_qr_code.xml b/app/src/main/res/layout/btn_qr_code.xml deleted file mode 100644 index ec3f704035..0000000000 --- a/app/src/main/res/layout/btn_qr_code.xml +++ /dev/null @@ -1,21 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_send.xml b/app/src/main/res/layout/fragment_send.xml index 3817a01615..8fedf3fed1 100644 --- a/app/src/main/res/layout/fragment_send.xml +++ b/app/src/main/res/layout/fragment_send.xml @@ -1,25 +1,36 @@ - - + android:layout_height="wrap_content" + android:background="@color/backgroundLightGray" + android:fitsSystemWindows="true" + app:liftOnScroll="true"> + + + + + android:fillViewport="true" + app:layout_behavior="@string/appbar_scrolling_view_behavior"> + tools:visibility="gone" /> - + - + + + + + + + @@ -94,4 +121,4 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_address.xml b/app/src/main/res/layout/layout_address.xml index 3fafd3e5cb..d54f709499 100644 --- a/app/src/main/res/layout/layout_address.xml +++ b/app/src/main/res/layout/layout_address.xml @@ -60,8 +60,8 @@ - - - - - - + android:textAllCaps="true" + android:textSize="20sp" + android:textStyle="bold" + tools:text="usd" /> - - - - - - - + android:layout_marginTop="4dp" + android:text="@string/send_total_will_be_sent" + android:textColor="@color/darkGray1" /> @@ -101,36 +67,16 @@ android:textSize="14sp" android:textStyle="bold" /> - - - - - - - - + android:textAllCaps="true" + android:textColor="@color/darkGray1" + android:textSize="14sp" + android:textStyle="bold" + tools:text="usd" /> diff --git a/app/src/main/res/layout/layout_send_address_payid.xml b/app/src/main/res/layout/layout_send_address_payid.xml index 16cd313308..238933794b 100644 --- a/app/src/main/res/layout/layout_send_address_payid.xml +++ b/app/src/main/res/layout/layout_send_address_payid.xml @@ -12,7 +12,7 @@ android:layout_width="0dp" android:layout_height="wrap_content" android:hint="@string/send_address_or_payid" - app:boxBackgroundColor="@android:color/transparent" + app:boxBackgroundColor="@color/backgroundLightGray" app:errorIconDrawable="@null" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" @@ -31,23 +31,45 @@ - + app:layout_constraintTop_toTopOf="@+id/flQrCode"> - + + + + + app:layout_constraintTop_toTopOf="@+id/tilAddressOrPayId"> + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/layout_send_amount.xml b/app/src/main/res/layout/layout_send_amount.xml index 4a6c5066e6..e2b5441fa2 100644 --- a/app/src/main/res/layout/layout_send_amount.xml +++ b/app/src/main/res/layout/layout_send_amount.xml @@ -44,7 +44,7 @@ android:layout_height="wrap_content" android:layout_marginStart="16dp" android:layout_marginEnd="16dp" - app:boxBackgroundColor="@android:color/transparent" + app:boxBackgroundColor="@color/backgroundLightGray" app:errorIconDrawable="@null"> - + app:layout_constraintTop_toTopOf="parent" + tools:text="USD" /> - - - - - - - - - - - - - + app:layout_constraintTop_toBottomOf="@+id/flAmountToSend" /> \ 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 index 4b68177593..ed8665c225 100644 --- a/app/src/main/res/layout/layout_send_receipt.xml +++ b/app/src/main/res/layout/layout_send_receipt.xml @@ -17,32 +17,16 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> - - - - - - - + app:layout_constraintTop_toTopOf="parent" + android:textAllCaps="true" + android:textStyle="bold" + tools:text="75.00 usd" /> - - - - - - - + app:layout_constraintTop_toTopOf="@+id/tvReceiptFee" + android:textAllCaps="true" + android:textColor="@color/darkGray1" + android:textStyle="bold" + tools:text="0.03 usd" /> 135dp 4dp + 44dp 32sp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e6fbc9e5af..074dfae669 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -44,6 +44,7 @@ This PayID already exists. Try a different one. Error response while creating PayID. + Unknown error PayID verification failed PayID unsupported by blockchain PayID not registered @@ -71,6 +72,7 @@ will be sent Balance: %1s %2s Maximum amount + Transaction was signed and sent to the blockchain Details