Updated on 2026-08-14
This commit is contained in:
parent
94cee57a18
commit
3992cbf386
8 changed files with 98 additions and 27 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue