Updated on 2026-08-14
This commit is contained in:
parent
a19f530fd8
commit
1d1c641e6b
13 changed files with 295 additions and 108 deletions
|
|
@ -61,7 +61,7 @@ dependencies {
|
|||
implementation 'androidx.core:core-ktx:1.3.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
|
||||
implementation 'com.google.android.material:material:1.2.0'
|
||||
implementation 'com.google.android.material:material:1.2.1'
|
||||
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.0.10'
|
||||
|
||||
implementation 'com.tangem:blockchain:1.27.0'
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
package com.tangem.tap.common.extensions
|
||||
|
||||
fun Int.isEven() = this and 1 == 0
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
package com.tangem.tap.common.qrCodeScan
|
||||
|
||||
import android.Manifest
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
|
|
@ -9,14 +10,17 @@ import androidx.core.app.ActivityCompat
|
|||
import androidx.core.content.ContextCompat
|
||||
import com.google.zxing.Result
|
||||
import com.otaliastudios.cameraview.CameraView.PERMISSION_REQUEST_CODE
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdActionUI
|
||||
import com.tangem.tap.store
|
||||
import me.dm7.barcodescanner.zxing.ZXingScannerView
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class ScanQrCodeActivity : AppCompatActivity(), ZXingScannerView.ResultHandler {
|
||||
companion object {
|
||||
val SCAN_QR_REQUEST_CODE = 1001
|
||||
val SCAN_RESULT = "scanResult"
|
||||
}
|
||||
|
||||
private lateinit var mScannerView: ZXingScannerView
|
||||
|
||||
override fun onCreate(state: Bundle?) {
|
||||
|
|
@ -40,7 +44,7 @@ class ScanQrCodeActivity : AppCompatActivity(), ZXingScannerView.ResultHandler {
|
|||
}
|
||||
|
||||
override fun handleResult(result: Result) {
|
||||
store.dispatch(AddressPayIdActionUI.SetAddressOrPayId(result.text))
|
||||
setResult(SCAN_QR_REQUEST_CODE, Intent().apply { putExtra(SCAN_RESULT, result.text) })
|
||||
finish()
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import timber.log.Timber
|
|||
val logMiddleware: Middleware<AppState> = { dispatch, appState ->
|
||||
{ nextDispatch ->
|
||||
{ action ->
|
||||
Timber.d("$action")
|
||||
Timber.d("Dispatch action: $action")
|
||||
nextDispatch(action)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
136
app/src/main/java/com/tangem/tap/common/text/Truncate.kt
Normal file
136
app/src/main/java/com/tangem/tap/common/text/Truncate.kt
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
package com.tangem.tap.common.text
|
||||
|
||||
import android.widget.TextView
|
||||
import com.tangem.tap.common.extensions.isEven
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
enum class TruncateType {
|
||||
START, MIDDLE, END
|
||||
}
|
||||
|
||||
interface Truncate {
|
||||
fun apply(tv: TextView, text: String, with: String): String
|
||||
|
||||
companion object {
|
||||
fun create(type: TruncateType): Truncate {
|
||||
return when (type) {
|
||||
TruncateType.START -> TruncateStart()
|
||||
TruncateType.MIDDLE -> TruncateMiddle()
|
||||
TruncateType.END -> TruncateEnd()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class BaseTruncate : Truncate {
|
||||
protected var hasBeenTruncated = false
|
||||
|
||||
override fun apply(tv: TextView, text: String, with: String): String {
|
||||
val roughLength = getRoughFitLength(tv, text, with)
|
||||
val fittedText = preciseFitting(tv, roughTruncate(text, roughLength), with)
|
||||
return if (hasBeenTruncated) attachWith(fittedText, with) else fittedText
|
||||
}
|
||||
|
||||
protected fun getRoughFitLength(tv: TextView, text: String, with: String): Int {
|
||||
val existingSpace = tv.measuredWidth - (tv.paddingStart + tv.paddingEnd)
|
||||
val textWillTakeSpace = tv.paint.measureText(text)
|
||||
val overSizeRatio: Float = textWillTakeSpace / existingSpace
|
||||
|
||||
val maxLengthOfText = text.length / overSizeRatio
|
||||
if (text.length <= maxLengthOfText) return text.length
|
||||
|
||||
return maxLengthOfText.toInt()
|
||||
}
|
||||
|
||||
protected fun preciseFitting(tv: TextView, text: String, with: String): String {
|
||||
if (!hasBeenTruncated) return text
|
||||
|
||||
val spaceForText = tv.measuredWidth - (tv.paddingStart + tv.paddingEnd)
|
||||
|
||||
var fittedText = text
|
||||
while (tv.paint.measureText(fittedText + with) > spaceForText) {
|
||||
fittedText = preciseTruncate(fittedText)
|
||||
}
|
||||
return fittedText
|
||||
}
|
||||
|
||||
protected abstract fun roughTruncate(text: String, residualLength: Int): String
|
||||
protected abstract fun preciseTruncate(text: String): String
|
||||
protected abstract fun attachWith(text: String, with: String): String
|
||||
}
|
||||
|
||||
class TruncateStart : BaseTruncate() {
|
||||
override fun roughTruncate(text: String, residualLength: Int): String {
|
||||
if (text.length <= residualLength) return text
|
||||
|
||||
hasBeenTruncated = true
|
||||
return text.substring(residualLength, text.length)
|
||||
}
|
||||
|
||||
override fun preciseTruncate(text: String): String = text.substring(1, text.length)
|
||||
|
||||
override fun attachWith(text: String, with: String): String = with + text
|
||||
}
|
||||
|
||||
class TruncateMiddle : BaseTruncate() {
|
||||
|
||||
override fun roughTruncate(text: String, residualLength: Int): String {
|
||||
if (text.length <= residualLength) return text
|
||||
|
||||
hasBeenTruncated = true
|
||||
val halfOfResidualLength = residualLength / 2
|
||||
val leftSide = text.substring(0, halfOfResidualLength)
|
||||
val rightSide = text.substring(text.length - halfOfResidualLength, text.length)
|
||||
|
||||
return leftSide + rightSide
|
||||
}
|
||||
|
||||
override fun preciseTruncate(text: String): String {
|
||||
val middlePosition = text.length / 2
|
||||
return if (text.length.isEven()) {
|
||||
val leftSide = text.substring(0, middlePosition - 1)
|
||||
val rightSide = text.substring(middlePosition, text.length)
|
||||
leftSide + rightSide
|
||||
} else {
|
||||
val leftSide = text.substring(0, middlePosition)
|
||||
val rightSide = text.substring(middlePosition + 1, text.length)
|
||||
leftSide + rightSide
|
||||
}
|
||||
}
|
||||
|
||||
override fun attachWith(text: String, with: String): String {
|
||||
val cuttingPosition = text.length / 2
|
||||
val leftSide = text.substring(0, cuttingPosition)
|
||||
val rightSide = text.substring(cuttingPosition, text.length)
|
||||
return leftSide + with + rightSide
|
||||
}
|
||||
}
|
||||
|
||||
class TruncateEnd : BaseTruncate() {
|
||||
override fun roughTruncate(text: String, residualLength: Int): String {
|
||||
if (text.length <= residualLength) return text
|
||||
|
||||
hasBeenTruncated = true
|
||||
return text.substring(0, residualLength)
|
||||
}
|
||||
|
||||
override fun preciseTruncate(text: String): String = text.substring(0, text.length - 1)
|
||||
|
||||
override fun attachWith(text: String, with: String): String = text + with
|
||||
}
|
||||
|
||||
fun TextView.truncateWith(text: String, type: TruncateType, with: String = "..."): String {
|
||||
val truncate = Truncate.create(type)
|
||||
return truncate.apply(this, text, with)
|
||||
}
|
||||
|
||||
fun TextView.truncateStartWith(text: String, with: String = "..."): String =
|
||||
this.truncateWith(text, TruncateType.START, with)
|
||||
|
||||
fun TextView.truncateMiddleWith(text: String, with: String = "..."): String =
|
||||
this.truncateWith(text, TruncateType.MIDDLE, with)
|
||||
|
||||
fun TextView.truncateEndWith(text: String, with: String = "..."): String =
|
||||
this.truncateWith(text, TruncateType.END, with)
|
||||
|
|
@ -44,14 +44,13 @@ private fun handleSendAction(action: Action) {
|
|||
internal class AddressPayIdHandler {
|
||||
fun handle(data: String) {
|
||||
val walletManager = store.state.globalState.walletManager ?: return
|
||||
if (data == store.state.sendState.addressPayIDState.etFieldValue) return
|
||||
|
||||
if (isPayId(data)) {
|
||||
verifyPayId(data, walletManager)
|
||||
store.dispatch(ProcessedButNotVerifiedAddressPayId(data))
|
||||
} else {
|
||||
val supposedAddress = extractAddressFromShareUri(data)
|
||||
store.dispatch(PayIdVerification.Failed(supposedAddress, FailReason.IS_NOT_PAY_ID))
|
||||
store.dispatch(ProcessedButNotVerifiedAddressPayId(supposedAddress))
|
||||
verifyAddress(walletManager, supposedAddress)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
package com.tangem.tap.features.send.redux
|
||||
|
||||
import android.view.View
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdActionUI.*
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.AddressVerification
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdVerifyAction.PayIdVerification
|
||||
import com.tangem.tap.features.send.redux.FeeActionUI.*
|
||||
import org.rekotlin.Action
|
||||
import org.rekotlin.StateType
|
||||
|
|
@ -11,7 +14,12 @@ import timber.log.Timber
|
|||
*/
|
||||
class SendReducer {
|
||||
companion object {
|
||||
fun reduce(action: Action, sendState: SendState): SendState = internalReduce(action, sendState)
|
||||
fun reduce(action: Action, sendState: SendState): SendState {
|
||||
val newState = internalReduce(action, sendState)
|
||||
if (newState == sendState) Timber.i("state didn't modified.")
|
||||
else Timber.i("state was updated to: $newState.")
|
||||
return newState
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -20,66 +28,53 @@ private fun internalReduce(incomingAction: Action, sendState: SendState): SendSt
|
|||
val action = incomingAction as? SendScreenAction ?: return sendState
|
||||
|
||||
return when (action) {
|
||||
is FeeActionUI -> handleFeeLayoutAction(action, sendState, sendState.feeLayoutState)
|
||||
// is SetAddressOrPayId -> handleAddressPayIdUIAction(action, sendState, sendState.addressPayIDState)
|
||||
is AddressPayIdActionUI -> handleAddressPayIdActionUI(action, sendState, sendState.addressPayIDState)
|
||||
is AddressPayIdVerifyAction -> handleAddressPayIdAction(action, sendState, sendState.addressPayIDState)
|
||||
is FeeActionUI -> handleFeeActionUI(action, sendState, sendState.feeLayoutState)
|
||||
else -> sendState
|
||||
}
|
||||
}
|
||||
|
||||
fun handleAddressPayIdActionUI(
|
||||
action: AddressPayIdActionUI,
|
||||
sendState: SendState,
|
||||
state: AddressPayIDState
|
||||
): SendState {
|
||||
val result = when (action) {
|
||||
is SetAddressOrPayId -> state
|
||||
is SetTruncateHandler -> state.copy(truncateHandler = action.handler)
|
||||
is TruncateOrRestore -> {
|
||||
if(action.truncate) state.copy(etFieldValue = state.truncatedFieldValue)
|
||||
else state.copy(etFieldValue = state.normalFieldValue)
|
||||
}
|
||||
}
|
||||
return updateLastState(sendState.copy(addressPayIDState = result), result)
|
||||
}
|
||||
|
||||
private fun handleAddressPayIdAction(
|
||||
action: AddressPayIdVerifyAction,
|
||||
sendState: SendState,
|
||||
state: AddressPayIDState
|
||||
): SendState {
|
||||
val result: AddressPayIDState? = when (action) {
|
||||
is AddressPayIdVerifyAction.PayIdVerification.Success -> {
|
||||
state.copyPayIdWalletAddress(action.payId, action.payIdWalletAddress)
|
||||
}
|
||||
is AddressPayIdVerifyAction.PayIdVerification.Failed -> {
|
||||
state.copyPaiIdError(action.payId, action.reason)
|
||||
}
|
||||
is AddressPayIdVerifyAction.AddressVerification.Success -> {
|
||||
state.copyWalletAddress(action.address)
|
||||
}
|
||||
is AddressPayIdVerifyAction.AddressVerification.Failed -> {
|
||||
state.copyError(action.address, action.reason)
|
||||
}
|
||||
is AddressPayIdVerifyAction.ProcessedButNotVerifiedAddressPayId -> {
|
||||
state.copy(etFieldValue = action.data)
|
||||
}
|
||||
val result = when (action) {
|
||||
is PayIdVerification.Success -> state.copyPayIdWalletAddress(action.payId, action.payIdWalletAddress)
|
||||
is PayIdVerification.Failed -> state.copyPaiIdError(action.payId, action.reason)
|
||||
is AddressVerification.Success -> state.copyWalletAddress(action.address)
|
||||
is AddressVerification.Failed -> state.copyError(action.address, action.reason)
|
||||
}
|
||||
return if (result == null) {
|
||||
Timber.e("AddressPayIDState didn't modified.")
|
||||
sendState
|
||||
} else {
|
||||
updateLastState(sendState.copy(addressPayIDState = result), result)
|
||||
}
|
||||
|
||||
return updateLastState(sendState.copy(addressPayIDState = result), result)
|
||||
}
|
||||
|
||||
private fun handleFeeLayoutAction(action: FeeActionUI, sendState: SendState, state: FeeLayoutState): SendState {
|
||||
return when (action) {
|
||||
private fun handleFeeActionUI(action: FeeActionUI, sendState: SendState, state: FeeLayoutState): SendState {
|
||||
val result = when (action) {
|
||||
is ToggleFeeLayoutVisibility -> {
|
||||
val visibility = if (state.visibility == View.VISIBLE) View.GONE
|
||||
else View.VISIBLE
|
||||
|
||||
val result = state.copy(visibility = visibility)
|
||||
updateLastState(sendState.copy(feeLayoutState = result), result)
|
||||
}
|
||||
is ChangeSelectedFee -> {
|
||||
val result = state.copy(selectedFeeId = action.id)
|
||||
updateLastState(sendState.copy(feeLayoutState = result), result)
|
||||
}
|
||||
is ChangeIncludeFee -> {
|
||||
val result = state.copy(includeFeeIsChecked = action.isChecked)
|
||||
updateLastState(sendState.copy(feeLayoutState = result), result)
|
||||
state.copy(visibility = if (state.visibility == View.VISIBLE) View.GONE else View.VISIBLE)
|
||||
}
|
||||
is ChangeSelectedFee -> state.copy(selectedFeeId = action.id)
|
||||
is ChangeIncludeFee -> state.copy(includeFeeIsChecked = action.isChecked)
|
||||
}
|
||||
return updateLastState(sendState.copy(feeLayoutState = result), result)
|
||||
}
|
||||
|
||||
private fun updateLastState(sendState: SendState, lastChangedState: StateType): SendState {
|
||||
val sendState = sendState.copy(lastChangedStateType = lastChangedState)
|
||||
Timber.d("$sendState")
|
||||
return sendState
|
||||
}
|
||||
private fun updateLastState(sendState: SendState, lastChangedState: StateType): SendState =
|
||||
sendState.copy(lastChangedStateType = lastChangedState)
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ sealed class FeeActionUI : SendScreenActionUI {
|
|||
// shortness AddressOrPayId = APid
|
||||
sealed class AddressPayIdActionUI : SendScreenActionUI {
|
||||
data class SetAddressOrPayId(val data: String) : AddressPayIdActionUI()
|
||||
data class SetTruncateHandler(val handler: (String) -> String) : AddressPayIdActionUI()
|
||||
data class TruncateOrRestore(val truncate: Boolean) : AddressPayIdActionUI()
|
||||
}
|
||||
|
||||
sealed class AddressPayIdVerifyAction : SendScreenAction {
|
||||
|
|
@ -32,8 +34,6 @@ sealed class AddressPayIdVerifyAction : SendScreenAction {
|
|||
ADDRESS_SAME_AS_WALLET
|
||||
}
|
||||
|
||||
data class ProcessedButNotVerifiedAddressPayId(val data: String) : AddressPayIdVerifyAction()
|
||||
|
||||
sealed class PayIdVerification : AddressPayIdVerifyAction() {
|
||||
data class Failed(val payId: String, val reason: FailReason) : PayIdVerification()
|
||||
data class Success(val payId: String, val payIdWalletAddress: String) : PayIdVerification()
|
||||
|
|
|
|||
|
|
@ -19,25 +19,57 @@ class NoneState : StateType
|
|||
|
||||
data class AddressPayIDState(
|
||||
val etFieldValue: String? = null,
|
||||
val normalFieldValue: String? = null,
|
||||
val truncatedFieldValue: String? = null,
|
||||
val walletAddress: String? = null,
|
||||
val error: AddressPayIdVerifyAction.FailReason? = null,
|
||||
val truncateHandler: ((String) -> String)? = null
|
||||
) : StateType {
|
||||
|
||||
fun isPayIdState():Boolean = walletAddress != null && walletAddress != etFieldValue
|
||||
fun isPayIdState(): Boolean = walletAddress != null && walletAddress != normalFieldValue
|
||||
|
||||
fun copyWalletAddress(address: String): AddressPayIDState {
|
||||
return this.copy(etFieldValue = address, walletAddress = address, error = null)
|
||||
val truncated = truncateHandler?.invoke(address) ?: address
|
||||
return this.copy(
|
||||
etFieldValue = address,
|
||||
normalFieldValue = address,
|
||||
truncatedFieldValue = truncated,
|
||||
walletAddress = address,
|
||||
error = null
|
||||
)
|
||||
}
|
||||
|
||||
fun copyError(address: String, error: AddressPayIdVerifyAction.FailReason): AddressPayIDState {
|
||||
return this.copy(etFieldValue = address, error = error, walletAddress = null)
|
||||
val truncated = truncateHandler?.invoke(address) ?: address
|
||||
return this.copy(
|
||||
etFieldValue = address,
|
||||
normalFieldValue = address,
|
||||
truncatedFieldValue = truncated,
|
||||
error = error,
|
||||
walletAddress = null
|
||||
)
|
||||
}
|
||||
|
||||
fun copyPayIdWalletAddress(payId: String, address: String): AddressPayIDState {
|
||||
return this.copy(etFieldValue = payId, walletAddress = address, error = null)
|
||||
val truncated = truncateHandler?.invoke(address) ?: address
|
||||
return this.copy(
|
||||
etFieldValue = payId,
|
||||
normalFieldValue = payId,
|
||||
truncatedFieldValue = truncated,
|
||||
walletAddress = address,
|
||||
error = null
|
||||
)
|
||||
}
|
||||
|
||||
fun copyPaiIdError(payId: String, error: AddressPayIdVerifyAction.FailReason): AddressPayIDState {
|
||||
return this.copy(etFieldValue = payId, error = error, walletAddress = null)
|
||||
val truncated = truncateHandler?.invoke(payId) ?: payId
|
||||
return this.copy(
|
||||
etFieldValue = payId,
|
||||
normalFieldValue = payId,
|
||||
truncatedFieldValue = truncated,
|
||||
error = error,
|
||||
walletAddress = null
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,12 +7,12 @@ import android.widget.EditText
|
|||
import androidx.core.widget.addTextChangedListener
|
||||
import com.tangem.tap.common.extensions.getFromClipboard
|
||||
import com.tangem.tap.common.qrCodeScan.ScanQrCodeActivity
|
||||
import com.tangem.tap.common.text.truncateMiddleWith
|
||||
import com.tangem.tap.features.send.BaseStoreFragment
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdActionUI.SetAddressOrPayId
|
||||
import com.tangem.tap.features.send.redux.AddressPayIdActionUI.*
|
||||
import com.tangem.tap.features.send.redux.FeeActionUI.*
|
||||
import com.tangem.tap.features.send.redux.ReleaseSendState
|
||||
import com.tangem.tap.features.send.ui.stateSubscribers.SendStateSubscriber
|
||||
import com.tangem.tap.features.send.ui.stateSubscribers.WalletStateSubscriber
|
||||
import com.tangem.tap.mainScope
|
||||
import com.tangem.tap.store
|
||||
import com.tangem.wallet.R
|
||||
|
|
@ -24,17 +24,53 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.*
|
||||
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
||||
|
||||
private val sendSubscriber = SendStateSubscriber(this)
|
||||
private val walletSubscriber = WalletStateSubscriber(this)
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
setupAddressOrPayIdLayout()
|
||||
setupFeeLayout()
|
||||
}
|
||||
|
||||
private fun setupAddressOrPayIdLayout() {
|
||||
store.dispatch(SetTruncateHandler { etAddressOrPayId.truncateMiddleWith(it, " *** ") })
|
||||
|
||||
etAddressOrPayId.setOnFocusChangeListener { v, hasFocus ->
|
||||
store.dispatch(TruncateOrRestore(!hasFocus))
|
||||
}
|
||||
etAddressOrPayId.inputedTextAsFlow()
|
||||
.debounce(400)
|
||||
.filter { store.state.sendState.addressPayIDState.etFieldValue != it }
|
||||
.onEach { store.dispatch(SetAddressOrPayId(it)) }
|
||||
.launchIn(mainScope)
|
||||
|
||||
imvPaste.setOnClickListener {
|
||||
store.dispatch(SetAddressOrPayId(requireContext().getFromClipboard()?.toString() ?: ""))
|
||||
store.dispatch(TruncateOrRestore(!etAddressOrPayId.isFocused))
|
||||
}
|
||||
imvQrCode.setOnClickListener {
|
||||
startActivityForResult(
|
||||
Intent(requireContext(), ScanQrCodeActivity::class.java),
|
||||
ScanQrCodeActivity.SCAN_QR_REQUEST_CODE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (requestCode != ScanQrCodeActivity.SCAN_QR_REQUEST_CODE) return
|
||||
|
||||
val scannedCode = data?.getStringExtra(ScanQrCodeActivity.SCAN_RESULT) ?: ""
|
||||
store.dispatch(SetAddressOrPayId(scannedCode))
|
||||
store.dispatch(TruncateOrRestore(!etAddressOrPayId.isFocused))
|
||||
}
|
||||
|
||||
private fun setupFeeLayout() {
|
||||
flExpandCollapse.setOnClickListener {
|
||||
store.dispatch(ToggleFeeLayoutVisibility)
|
||||
}
|
||||
|
|
@ -44,30 +80,13 @@ class SendFragment : BaseStoreFragment(R.layout.fragment_send) {
|
|||
swIncludeFee.setOnCheckedChangeListener { btn, isChecked ->
|
||||
store.dispatch(ChangeIncludeFee(isChecked))
|
||||
}
|
||||
|
||||
etAddressOrPayId.inputedTextAsFlow()
|
||||
.debounce(400)
|
||||
.filter { store.state.sendState.addressPayIDState.etFieldValue != it }
|
||||
.onEach { store.dispatch(SetAddressOrPayId(it)) }
|
||||
.launchIn(mainScope)
|
||||
|
||||
imvPaste.setOnClickListener {
|
||||
store.dispatch(SetAddressOrPayId(requireContext().getFromClipboard()?.toString() ?: ""))
|
||||
}
|
||||
imvQrCode.setOnClickListener {
|
||||
requireActivity().startActivity(Intent(requireContext(), ScanQrCodeActivity::class.java))
|
||||
}
|
||||
}
|
||||
|
||||
override fun subscribeToStore() {
|
||||
store.subscribe(walletSubscriber) { appState ->
|
||||
appState.skipRepeats { oldState, newState -> false }.select { it.walletState }
|
||||
}
|
||||
store.subscribe(sendSubscriber) { appState ->
|
||||
appState.skipRepeats { oldState, newState -> false }.select { it.sendState }
|
||||
appState.skipRepeats { oldState, newState -> oldState == newState }.select { it.sendState }
|
||||
}
|
||||
|
||||
storeSubscribersList.add(walletSubscriber)
|
||||
storeSubscribersList.add(sendSubscriber)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,14 +40,25 @@ class SendStateSubscriber(fragment: Fragment) : FragmentStateSubscriber<SendStat
|
|||
else context.getString(resId)
|
||||
}
|
||||
|
||||
val parsedError = parseError(fg.tilAddressOrPayId.context, state.error)
|
||||
fg.tilAddressOrPayId.error = parsedError
|
||||
fg.tilAddressOrPayId.isErrorEnabled = parsedError != null
|
||||
fg.tilAddressOrPayId.helperText = state.walletAddress
|
||||
fg.tilAddressOrPayId.isHelperTextEnabled = state.isPayIdState() && parsedError == null
|
||||
if (fg.etAddressOrPayId.text?.toString() != state.etFieldValue) {
|
||||
fg.etAddressOrPayId.setText(state.etFieldValue)
|
||||
if (fg.etAddressOrPayId.isFocused) fg.etAddressOrPayId.setSelection(state.etFieldValue?.length ?: 0)
|
||||
val et = fg.etAddressOrPayId
|
||||
val til = fg.tilAddressOrPayId
|
||||
val parsedError = parseError(til.context, state.error)
|
||||
|
||||
til.error = parsedError
|
||||
til.isErrorEnabled = parsedError != null
|
||||
til.helperText = state.walletAddress
|
||||
til.isHelperTextEnabled = state.isPayIdState() && parsedError == null
|
||||
|
||||
// prevent cycling
|
||||
if (state.etFieldValue == null || et.text?.toString() == state.etFieldValue) return
|
||||
|
||||
// prevent cursor jumping while editing
|
||||
if (et.isFocused) {
|
||||
val prevSelection = et.selectionStart
|
||||
et.setText(state.etFieldValue)
|
||||
et.setSelection(prevSelection)
|
||||
} else {
|
||||
et.setText(state.etFieldValue)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
package com.tangem.tap.features.send.ui.stateSubscribers
|
||||
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.tangem.tap.features.wallet.redux.WalletState
|
||||
|
||||
/**
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
class WalletStateSubscriber(fragment: Fragment) : FragmentStateSubscriber<WalletState>(fragment) {
|
||||
override fun updateWithNewState(fg: Fragment, state: WalletState) {
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -2,17 +2,18 @@
|
|||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp">
|
||||
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/tilAddressOrPayId"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/send_address_or_payid"
|
||||
app:boxBackgroundColor="@android:color/transparent"
|
||||
app:errorIconDrawable="@null"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
|
@ -23,7 +24,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:ellipsize="middle"
|
||||
android:paddingStart="0dp"
|
||||
android:paddingEnd="96dp"
|
||||
android:paddingEnd="98dp"
|
||||
android:singleLine="true"
|
||||
android:textSize="16sp"
|
||||
tools:text="139mrsJgyWnJ **** y9BV" />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue