Updated on 2026-08-14
This commit is contained in:
parent
2f2a8b04b5
commit
09b489b175
6 changed files with 100 additions and 69 deletions
|
|
@ -13,8 +13,8 @@ import androidx.core.view.postDelayed
|
|||
import androidx.core.widget.addTextChangedListener
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.flowWithLifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import arrow.core.getOrElse
|
||||
|
|
@ -54,14 +54,13 @@ import com.tangem.tap.features.send.redux.states.FeeType
|
|||
import com.tangem.tap.features.send.redux.states.MainCurrencyType
|
||||
import com.tangem.tap.features.send.ui.adapters.WarningMessagesAdapter
|
||||
import com.tangem.tap.features.send.ui.stateSubscribers.SendStateSubscriber
|
||||
import com.tangem.tap.mainScope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
import com.tangem.wallet.databinding.FragmentSendBinding
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.FlowPreview
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import timber.log.Timber
|
||||
|
|
@ -73,6 +72,7 @@ private const val EDIT_TEXT_INPUT_DEBOUNCE = 400L
|
|||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Suppress("LargeClass")
|
||||
@OptIn(FlowPreview::class)
|
||||
@AndroidEntryPoint
|
||||
class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
||||
|
|
@ -103,18 +103,25 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
lifecycle.addObserver(viewModel)
|
||||
sendSubscriber.initViewModel(viewModel)
|
||||
Analytics.send(Token.Send.ScreenOpened())
|
||||
listenToQrCode()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
subscribeToQrCodeScanner()
|
||||
subscribeToTransactionExtrasFields()
|
||||
subscribeToAddressField()
|
||||
subscribeToAmountField()
|
||||
}
|
||||
}
|
||||
|
||||
addBackPressHandler(this)
|
||||
|
||||
etAmountToSend = view.findViewById(R.id.etAmountToSend)
|
||||
|
||||
initSendButtonStates()
|
||||
setupAddressLayout()
|
||||
setupTransactionExtrasLayout()
|
||||
setupAmountLayout()
|
||||
setupFeeLayout()
|
||||
setupWarningMessages()
|
||||
|
|
@ -149,14 +156,6 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
setOnFocusChangeListener { _, hasFocus ->
|
||||
store.dispatch(TruncateOrRestore(!hasFocus))
|
||||
}
|
||||
|
||||
inputtedTextAsFlow()
|
||||
.debounce(EDIT_TEXT_INPUT_DEBOUNCE)
|
||||
.filter { store.state.sendState.addressState.viewFieldValue.value != it }
|
||||
.onEach {
|
||||
store.dispatch(AddressActionUi.HandleUserInput(it))
|
||||
}
|
||||
.launchIn(mainScope)
|
||||
}
|
||||
|
||||
imvPaste.setOnClickListener {
|
||||
|
|
@ -183,31 +182,41 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun listenToQrCode() {
|
||||
lifecycleScope.launch {
|
||||
listenToQrScanningUseCase(SourceType.SEND)
|
||||
.getOrElse { emptyFlow() }
|
||||
.flowWithLifecycle(this@SendFragment.lifecycle, minActiveState = Lifecycle.State.CREATED)
|
||||
.collect { rawQr ->
|
||||
delay(200)
|
||||
|
||||
// Delayed launch is needed in order for the UI to be drawn and to process the sent events.
|
||||
// If do not use the delay, then etAmount error field is not displayed when
|
||||
// inserting an incorrect amount by shareUri
|
||||
cryptoCurrency?.let { cryptoCurrency ->
|
||||
parseQrCodeUseCase(rawQr, cryptoCurrency = cryptoCurrency).fold(
|
||||
ifLeft = {
|
||||
onCodeScanned(QrResult(address = rawQr))
|
||||
Timber.w(it)
|
||||
},
|
||||
ifRight = { onCodeScanned(it) },
|
||||
)
|
||||
} ?: onCodeScanned(QrResult(address = rawQr))
|
||||
}
|
||||
}
|
||||
private fun CoroutineScope.subscribeToAddressField() = with(binding.lSendAddress) {
|
||||
etAddress.inputtedTextAsFlow()
|
||||
.debounce(EDIT_TEXT_INPUT_DEBOUNCE)
|
||||
.filter { store.state.sendState.addressState.viewFieldValue.value != it }
|
||||
.onEach {
|
||||
store.dispatch(AddressActionUi.HandleUserInput(it))
|
||||
}
|
||||
.launchIn(this@subscribeToAddressField)
|
||||
}
|
||||
|
||||
private fun setupTransactionExtrasLayout() = with(binding.lSendAddress) {
|
||||
private fun CoroutineScope.subscribeToAmountField() {
|
||||
etAmountToSend.inputtedTextAsFlow()
|
||||
.debounce(EDIT_TEXT_INPUT_DEBOUNCE)
|
||||
.filter { store.state.sendState.amountState.viewAmountValue.value != it && it.isNotEmpty() }
|
||||
.onEach { store.dispatch(AmountActionUi.HandleUserInput(it)) }
|
||||
.launchIn(this)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.subscribeToQrCodeScanner() {
|
||||
listenToQrScanningUseCase(SourceType.SEND)
|
||||
.getOrElse { emptyFlow() }
|
||||
.onEach { rawQr ->
|
||||
cryptoCurrency?.let { cryptoCurrency ->
|
||||
parseQrCodeUseCase(rawQr, cryptoCurrency = cryptoCurrency).fold(
|
||||
ifLeft = {
|
||||
onCodeScanned(QrResult(address = rawQr))
|
||||
Timber.w(it)
|
||||
},
|
||||
ifRight = { onCodeScanned(it) },
|
||||
)
|
||||
} ?: onCodeScanned(QrResult(address = rawQr))
|
||||
}.launchIn(this)
|
||||
}
|
||||
|
||||
private fun CoroutineScope.subscribeToTransactionExtrasFields() = with(binding.lSendAddress) {
|
||||
// TODO: [REDACTED_TASK_KEY]
|
||||
etXlmMemo.inputtedTextAsFlow()
|
||||
.debounce(EDIT_TEXT_INPUT_DEBOUNCE)
|
||||
|
|
@ -216,7 +225,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
info.xlmMemo?.viewFieldValue?.value != it
|
||||
}
|
||||
.onEach { store.dispatch(TransactionExtrasAction.XlmMemo.HandleUserInput(it)) }
|
||||
.launchIn(mainScope)
|
||||
.launchIn(this@subscribeToTransactionExtrasFields)
|
||||
|
||||
etDestinationTag.inputtedTextAsFlow()
|
||||
.debounce(EDIT_TEXT_INPUT_DEBOUNCE)
|
||||
|
|
@ -225,7 +234,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
info.xrpDestinationTag?.viewFieldValue?.value != it
|
||||
}
|
||||
.onEach { store.dispatch(TransactionExtrasAction.XrpDestinationTag.HandleUserInput(it)) }
|
||||
.launchIn(mainScope)
|
||||
.launchIn(this@subscribeToTransactionExtrasFields)
|
||||
|
||||
etBinanceMemo.inputtedTextAsFlow()
|
||||
.debounce(EDIT_TEXT_INPUT_DEBOUNCE)
|
||||
|
|
@ -234,7 +243,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
info.binanceMemo?.viewFieldValue?.value != it
|
||||
}
|
||||
.onEach { store.dispatch(TransactionExtrasAction.BinanceMemo.HandleUserInput(it)) }
|
||||
.launchIn(mainScope)
|
||||
.launchIn(this@subscribeToTransactionExtrasFields)
|
||||
|
||||
etTonMemo.inputtedTextAsFlow()
|
||||
.debounce(EDIT_TEXT_INPUT_DEBOUNCE)
|
||||
|
|
@ -243,7 +252,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
info.tonMemoState?.viewFieldValue?.value != it
|
||||
}
|
||||
.onEach { store.dispatch(TransactionExtrasAction.TonMemo.HandleUserInput(it)) }
|
||||
.launchIn(mainScope)
|
||||
.launchIn(this@subscribeToTransactionExtrasFields)
|
||||
|
||||
etCosmosMemo.inputtedTextAsFlow()
|
||||
.debounce(EDIT_TEXT_INPUT_DEBOUNCE)
|
||||
|
|
@ -252,7 +261,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
info.cosmosMemoState?.viewFieldValue?.value != it
|
||||
}
|
||||
.onEach { store.dispatch(TransactionExtrasAction.CosmosMemo.HandleUserInput(it)) }
|
||||
.launchIn(mainScope)
|
||||
.launchIn(this@subscribeToTransactionExtrasFields)
|
||||
|
||||
etHederaMemo.inputtedTextAsFlow()
|
||||
.debounce(EDIT_TEXT_INPUT_DEBOUNCE)
|
||||
|
|
@ -261,7 +270,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
info.hederaMemoState?.viewFieldValue?.value != it
|
||||
}
|
||||
.onEach { store.dispatch(TransactionExtrasAction.HederaMemo.HandleUserInput(it)) }
|
||||
.launchIn(mainScope)
|
||||
.launchIn(this@subscribeToTransactionExtrasFields)
|
||||
|
||||
etAlgorandMemo.inputtedTextAsFlow()
|
||||
.debounce(EDIT_TEXT_INPUT_DEBOUNCE)
|
||||
|
|
@ -270,7 +279,7 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
info.algorandMemoState?.viewFieldValue?.value != it
|
||||
}
|
||||
.onEach { store.dispatch(TransactionExtrasAction.AlgorandMemo.HandleUserInput(it)) }
|
||||
.launchIn(mainScope)
|
||||
.launchIn(this@subscribeToTransactionExtrasFields)
|
||||
}
|
||||
|
||||
private fun onCodeScanned(parsedQr: QrResult) {
|
||||
|
|
@ -343,12 +352,6 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
if (!hasFocus && etAmountToSend.text?.toString() == "") etAmountToSend.setText("0")
|
||||
}
|
||||
|
||||
etAmountToSend.inputtedTextAsFlow()
|
||||
.debounce(EDIT_TEXT_INPUT_DEBOUNCE)
|
||||
.filter { store.state.sendState.amountState.viewAmountValue.value != it && it.isNotEmpty() }
|
||||
.onEach { store.dispatch(AmountActionUi.HandleUserInput(it)) }
|
||||
.launchIn(mainScope)
|
||||
|
||||
etAmountToSend.setOnImeActionListener(EditorInfo.IME_ACTION_DONE) {
|
||||
it.hideSoftKeyboard()
|
||||
it.clearFocus()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue