Updated on 2026-08-14

This commit is contained in:
Tangem 2023-05-24 16:16:34 +05:00
parent 94cee57a18
commit 3992cbf386
8 changed files with 98 additions and 27 deletions

View file

@ -69,6 +69,10 @@ sealed class TransactionExtrasAction : SendScreenActionUi {
sealed class TonMemo : TransactionExtrasAction() {
data class HandleUserInput(val data: String) : TonMemo()
}
sealed class CosmosMemo : TransactionExtrasAction() {
data class HandleUserInput(val data: String) : CosmosMemo()
}
}
sealed class AddressPayIdVerifyAction : SendScreenAction {

View file

@ -2,6 +2,7 @@ package com.tangem.tap.features.send.redux.middlewares
import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.tangem.blockchain.blockchains.binance.BinanceTransactionExtras
import com.tangem.blockchain.blockchains.cosmos.CosmosTransactionExtras
import com.tangem.blockchain.blockchains.polkadot.ExistentialDepositProvider
import com.tangem.blockchain.blockchains.stellar.StellarTransactionExtras
import com.tangem.blockchain.blockchains.ton.TonTransactionExtras
@ -171,6 +172,7 @@ private fun sendTransaction(
transactionExtras.xrpDestinationTag?.tag?.let {
txData = txData.copy(extras = XrpTransactionBuilder.XrpTransactionExtras(it))
}
transactionExtras.cosmosMemoState?.memo?.let { txData = txData.copy(extras = CosmosTransactionExtras(it)) }
transactionExtras.tonMemoState?.memo?.let { txData = txData.copy(extras = TonTransactionExtras(it)) }
scope.launch {

View file

@ -3,21 +3,8 @@ 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.BinanceMemo
import com.tangem.tap.features.send.redux.TransactionExtrasAction.Prepare
import com.tangem.tap.features.send.redux.TransactionExtrasAction.Release
import com.tangem.tap.features.send.redux.TransactionExtrasAction.TonMemo
import com.tangem.tap.features.send.redux.TransactionExtrasAction.XlmMemo
import com.tangem.tap.features.send.redux.TransactionExtrasAction.XrpDestinationTag
import com.tangem.tap.features.send.redux.states.BinanceMemoState
import com.tangem.tap.features.send.redux.states.InputViewValue
import com.tangem.tap.features.send.redux.states.SendState
import com.tangem.tap.features.send.redux.states.TonMemoState
import com.tangem.tap.features.send.redux.states.TransactionExtraError
import com.tangem.tap.features.send.redux.states.TransactionExtrasState
import com.tangem.tap.features.send.redux.states.XlmMemoState
import com.tangem.tap.features.send.redux.states.XlmMemoType
import com.tangem.tap.features.send.redux.states.XrpDestinationTagState
import com.tangem.tap.features.send.redux.TransactionExtrasAction.*
import com.tangem.tap.features.send.redux.states.*
/**
[REDACTED_AUTHOR]
@ -31,6 +18,7 @@ class TransactionExtrasReducer : SendInternalReducer {
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)
else -> sendState
}
}
@ -60,6 +48,7 @@ class TransactionExtrasReducer : SendInternalReducer {
Blockchain.Stellar -> TransactionExtrasState(xlmMemo = XlmMemoState())
Blockchain.Binance -> TransactionExtrasState(binanceMemo = BinanceMemoState())
Blockchain.TON, Blockchain.TONTestnet -> TransactionExtrasState(tonMemoState = TonMemoState())
Blockchain.Cosmos -> TransactionExtrasState(cosmosMemoState = CosmosMemoState())
else -> emptyResult
}
return updateLastState(sendState.copy(transactionExtrasState = result), result)
@ -155,4 +144,19 @@ class TransactionExtrasReducer : SendInternalReducer {
}
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)
}
}

View file

@ -31,6 +31,7 @@ data class TransactionExtrasState(
val binanceMemo: BinanceMemoState? = null,
val xrpDestinationTag: XrpDestinationTagState? = null,
val tonMemoState: TonMemoState? = null,
val cosmosMemoState: CosmosMemoState? = null,
) : IdStateHolder {
override val stateId: StateId = StateId.TRANSACTION_EXTRAS
}
@ -104,6 +105,11 @@ data class TonMemoState(
val memo: String? = null,
val error: TransactionExtraError? = null,
)
data class CosmosMemoState(
val viewFieldValue: InputViewValue = InputViewValue(""),
val memo: String? = null,
val error: TransactionExtraError? = null,
)
enum class TransactionExtraError {
INVALID_DESTINATION_TAG,

View file

@ -60,6 +60,7 @@ import com.tangem.tap.mainScope
import com.tangem.tap.store
import com.tangem.wallet.R
import com.tangem.wallet.databinding.FragmentSendBinding
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
@ -69,9 +70,12 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import java.text.DecimalFormatSymbols
private const val EDIT_TEXT_INPUT_DEBOUNCE = 400L
/**
[REDACTED_AUTHOR]
*/
@OptIn(FlowPreview::class)
class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
lateinit var sendBtn: ViewStateWidget
@ -115,16 +119,15 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
sendBtn = IndeterminateProgressButtonWidget(btnSend, progress)
}
@Suppress("MagicNumber")
private fun setupAddressOrPayIdLayout() = with(binding.lSendAddressPayid) {
store.dispatch(SetTruncateHandler { etAddressOrPayId.truncateMiddleWith(it, "...") })
store.dispatch(CheckClipboard(requireContext().getFromClipboard()?.toString()))
etAddressOrPayId.setOnFocusChangeListener { v, hasFocus ->
etAddressOrPayId.setOnFocusChangeListener { _, hasFocus ->
store.dispatch(TruncateOrRestore(!hasFocus))
}
etAddressOrPayId.inputtedTextAsFlow()
.debounce(400)
.debounce(EDIT_TEXT_INPUT_DEBOUNCE)
.filter { store.state.sendState.addressPayIdState.viewFieldValue.value != it }
.onEach {
store.dispatch(AddressPayIdActionUi.HandleUserInput(it))
@ -146,8 +149,9 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
}
private fun setupTransactionExtrasLayout() = with(binding.lSendAddressPayid) {
// TODO: [REDACTED_TASK_KEY]
etXlmMemo.inputtedTextAsFlow()
.debounce(400)
.debounce(EDIT_TEXT_INPUT_DEBOUNCE)
.filter {
val info = store.state.sendState.transactionExtrasState
info.xlmMemo?.viewFieldValue?.value != it
@ -156,7 +160,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
.launchIn(mainScope)
etDestinationTag.inputtedTextAsFlow()
.debounce(400)
.debounce(EDIT_TEXT_INPUT_DEBOUNCE)
.filter {
val info = store.state.sendState.transactionExtrasState
info.xrpDestinationTag?.viewFieldValue?.value != it
@ -165,7 +169,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
.launchIn(mainScope)
etBinanceMemo.inputtedTextAsFlow()
.debounce(400)
.debounce(EDIT_TEXT_INPUT_DEBOUNCE)
.filter {
val info = store.state.sendState.transactionExtrasState
info.binanceMemo?.viewFieldValue?.value != it
@ -174,13 +178,22 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
.launchIn(mainScope)
etTonMemo.inputtedTextAsFlow()
.debounce(400)
.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(mainScope)
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(mainScope)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
@ -234,7 +247,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
}
etAmountToSend.keyListener = DigitsKeyListener.getInstance("0123456789,.")
etAmountToSend.setOnFocusChangeListener { v, hasFocus ->
etAmountToSend.setOnFocusChangeListener { _, hasFocus ->
snackbarControlledByChangingFocus = true
if (hasFocus) {
etAmountToSend.postDelayed(200) {
@ -257,7 +270,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
}
etAmountToSend.inputtedTextAsFlow()
.debounce(400)
.debounce(EDIT_TEXT_INPUT_DEBOUNCE)
.filter { store.state.sendState.amountState.viewAmountValue.value != it && it.isNotEmpty() }
.onEach { store.dispatch(AmountActionUi.HandleUserInput(it)) }
.launchIn(mainScope)
@ -273,13 +286,13 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
store.dispatch(ToggleControlsVisibility)
}
chipGroup.check(FeeUiHelper.toId(FeeType.NORMAL))
chipGroup.setOnCheckedChangeListener { group, checkedId ->
chipGroup.setOnCheckedChangeListener { _, checkedId ->
if (checkedId == -1) return@setOnCheckedChangeListener
store.dispatch(ChangeSelectedFee(FeeUiHelper.toType(checkedId)))
store.dispatch(CheckAmountToSend)
}
swIncludeFee.setOnCheckedChangeListener { btn, isChecked ->
swIncludeFee.setOnCheckedChangeListener { _, isChecked ->
store.dispatch(ChangeIncludeFee(isChecked))
store.dispatch(CheckAmountToSend)
}

View file

@ -80,6 +80,7 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
showView(xrpDestinationTagContainer, infoState.xrpDestinationTag)
showView(binanceMemoContainer, infoState.binanceMemo)
showView(tonMemoContainer, infoState.tonMemoState)
showView(cosmosMemoContainer, infoState.cosmosMemoState)
infoState.xlmMemo?.let {
if (!it.viewFieldValue.isFromUserInput) etXlmMemo.setText(it.viewFieldValue.value)
@ -126,6 +127,16 @@ class SendStateSubscriber(fragment: BaseStoreFragment) : FragmentStateSubscriber
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)
}
}
}
private fun handleSendScreen(fg: SendFragment, state: SendState) = with(fg.binding) {

View file

@ -259,6 +259,37 @@
</FrameLayout>
<FrameLayout
android:id="@+id/cosmosMemoContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
tools:visibility="visible">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/tilCosmosMemo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/send_extras_hint_memo"
app:boxBackgroundColor="@color/backgroundLightGray"
app:errorIconDrawable="@null">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/etCosmosMemo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/backgroundLightGray"
android:ellipsize="end"
android:paddingStart="0dp"
android:paddingEnd="0dp"
android:singleLine="true"
android:textSize="16sp"
tools:text="Ton memo" />
</com.google.android.material.textfield.TextInputLayout>
</FrameLayout>
</LinearLayout>
</LinearLayout>

View file

@ -73,7 +73,7 @@ reactiveNetwork = "3.0.8"
# endregion Other libraries
# region Tangem
tangemBlockchainSdk = "develop-219"
tangemBlockchainSdk = "develop-221"
#tangemBlockchainSdk = "0.0.1" # Keep it! - used for local builds
tangemCardSdk = "develop-242"
#tangemCardSdk = "0.0.1" # Keep it! - used for local builds