Updated on 2026-08-14
This commit is contained in:
parent
13fcfb8728
commit
b723940318
16 changed files with 182 additions and 22 deletions
|
|
@ -7,6 +7,8 @@ import com.tangem.core.navigation.NavigationAction
|
|||
import com.tangem.core.navigation.ReduxNavController
|
||||
import com.tangem.domain.tokens.model.CryptoCurrency
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.feature.qrscanning.QrScanningRouter
|
||||
import com.tangem.feature.qrscanning.SourceType
|
||||
import com.tangem.features.send.impl.presentation.SendFragment
|
||||
import com.tangem.features.tokendetails.navigation.TokenDetailsRouter
|
||||
|
||||
|
|
@ -31,4 +33,16 @@ internal class DefaultSendRouter(
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
override fun openQrCodeScanner(network: String) {
|
||||
reduxNavController.navigate(
|
||||
action = NavigationAction.NavigateTo(
|
||||
screen = AppScreen.QrScanning,
|
||||
bundle = bundleOf(
|
||||
QrScanningRouter.SOURCE_KEY to SourceType.SEND,
|
||||
QrScanningRouter.NETWORK_KEY to network,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -11,4 +11,7 @@ interface InnerSendRouter : SendRouter {
|
|||
|
||||
/** Open token details screen by [userWalletId] and [currency] */
|
||||
fun openTokenDetails(userWalletId: UserWalletId, currency: CryptoCurrency)
|
||||
|
||||
/** Open QR code scanner screen */
|
||||
fun openQrCodeScanner(network: String)
|
||||
}
|
||||
|
|
@ -4,16 +4,25 @@ import android.os.Bundle
|
|||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.flowWithLifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import arrow.core.getOrElse
|
||||
import com.tangem.core.ui.components.SystemBarsEffect
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.core.ui.screen.ComposeFragment
|
||||
import com.tangem.core.ui.theme.AppThemeModeHolder
|
||||
import com.tangem.feature.qrscanning.SourceType
|
||||
import com.tangem.feature.qrscanning.usecase.ListenToQrScanningUseCase
|
||||
import com.tangem.features.send.api.navigation.SendRouter
|
||||
import com.tangem.features.send.impl.navigation.InnerSendRouter
|
||||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
import com.tangem.features.send.impl.presentation.ui.SendScreen
|
||||
import com.tangem.features.send.impl.presentation.viewmodel.SendViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import java.lang.ref.WeakReference
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -29,6 +38,9 @@ internal class SendFragment : ComposeFragment() {
|
|||
@Inject
|
||||
lateinit var router: SendRouter
|
||||
|
||||
@Inject
|
||||
lateinit var listenToQrScanningUseCase: ListenToQrScanningUseCase
|
||||
|
||||
private val viewModel by viewModels<SendViewModel>()
|
||||
private val innerSendRouter: InnerSendRouter
|
||||
get() = requireNotNull(router as? InnerSendRouter) {
|
||||
|
|
@ -44,6 +56,7 @@ internal class SendFragment : ComposeFragment() {
|
|||
fragmentManager = WeakReference(parentFragmentManager),
|
||||
),
|
||||
)
|
||||
listenToQrCode()
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -60,7 +73,24 @@ internal class SendFragment : ComposeFragment() {
|
|||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun listenToQrCode() {
|
||||
lifecycleScope.launch {
|
||||
listenToQrScanningUseCase(SourceType.SEND)
|
||||
.getOrElse { emptyFlow() }
|
||||
.flowWithLifecycle(this@SendFragment.lifecycle, minActiveState = Lifecycle.State.CREATED)
|
||||
.collect {
|
||||
delay(QR_SCAN_DELAY)
|
||||
|
||||
// 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
|
||||
viewModel.onRecipientAddressScanned(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val QR_SCAN_DELAY = 200L
|
||||
|
||||
/** Create send fragment instance */
|
||||
fun create(): SendFragment = SendFragment()
|
||||
|
|
|
|||
|
|
@ -94,11 +94,14 @@ internal class SendStateFactory(
|
|||
currentState = MutableStateFlow(SendUiStateType.Amount),
|
||||
)
|
||||
|
||||
fun getReadyState(): SendUiState = currentStateProvider().copy(
|
||||
amountState = amountStateConverter.convert(Unit),
|
||||
recipientState = recipientStateConverter.convert(Unit),
|
||||
feeState = feeStateConverter.convert(Unit),
|
||||
)
|
||||
fun getReadyState(): SendUiState {
|
||||
val state = currentStateProvider()
|
||||
return state.copy(
|
||||
amountState = state.amountState ?: amountStateConverter.convert(Unit),
|
||||
recipientState = state.recipientState ?: recipientStateConverter.convert(Unit),
|
||||
feeState = state.feeState ?: feeStateConverter.convert(Unit),
|
||||
)
|
||||
}
|
||||
//endregion
|
||||
|
||||
//region amount state clicks
|
||||
|
|
@ -160,9 +163,8 @@ internal class SendStateFactory(
|
|||
isValidating = false,
|
||||
addressTextField = recipientState.addressTextField.copy(
|
||||
error = when {
|
||||
!isValidAddress || isAddressInWallet -> resourceReference(
|
||||
R.string.send_recipient_address_error,
|
||||
)
|
||||
!isValidAddress -> resourceReference(R.string.send_recipient_address_error)
|
||||
isAddressInWallet -> resourceReference(R.string.send_error_address_same_as_wallet)
|
||||
else -> null
|
||||
},
|
||||
isError = value.isNotEmpty() && !isValidAddress || isAddressInWallet,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import com.tangem.features.send.impl.presentation.state.SendUiState
|
|||
import com.tangem.utils.Provider
|
||||
import com.tangem.utils.converter.Converter
|
||||
import java.math.BigDecimal
|
||||
import java.text.DecimalFormatSymbols
|
||||
import java.text.NumberFormat
|
||||
|
||||
internal class SendAmountFieldChangeConverter(
|
||||
|
|
@ -106,8 +105,7 @@ internal class SendAmountFieldChangeConverter(
|
|||
var trimmedValue = this
|
||||
if (length > 1 && firstOrNull() == '0' && get(1).isDigit()) trimmedValue = drop(1)
|
||||
|
||||
val separatorChar = DecimalFormatSymbols.getInstance().decimalSeparator.toString()
|
||||
return trimmedValue.replace(TRIM_REGEX.toRegex(), separatorChar)
|
||||
return trimmedValue.replace(TRIM_REGEX.toRegex(), ".")
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
|
|||
|
|
@ -26,14 +26,15 @@ import com.tangem.domain.txhistory.usecase.GetTxHistoryItemsUseCase
|
|||
import com.tangem.domain.walletmanager.WalletManagersFacade
|
||||
import com.tangem.domain.wallets.models.UserWallet
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.domain.wallets.usecase.GetUserWalletUseCase
|
||||
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
|
||||
import com.tangem.domain.wallets.usecase.ValidateWalletAddressUseCase
|
||||
import com.tangem.domain.wallets.usecase.ValidateWalletMemoUseCase
|
||||
import com.tangem.domain.wallets.usecase.*
|
||||
import com.tangem.features.send.api.navigation.SendRouter
|
||||
import com.tangem.features.send.impl.navigation.InnerSendRouter
|
||||
import com.tangem.features.send.impl.presentation.domain.AvailableWallet
|
||||
import com.tangem.features.send.impl.presentation.state.*
|
||||
import com.tangem.features.send.impl.presentation.state.SendNotificationFactory
|
||||
import com.tangem.features.send.impl.presentation.state.SendStateFactory
|
||||
import com.tangem.features.send.impl.presentation.state.SendUiState
|
||||
import com.tangem.features.send.impl.presentation.state.StateRouter
|
||||
import com.tangem.features.send.impl.presentation.state.fee.FeeSelectorState
|
||||
import com.tangem.features.send.impl.presentation.state.fee.FeeType
|
||||
import com.tangem.features.send.impl.presentation.state.fee.getFee
|
||||
|
|
@ -67,6 +68,7 @@ internal class SendViewModel @Inject constructor(
|
|||
private val sendTransactionUseCase: SendTransactionUseCase,
|
||||
private val createTransactionUseCase: CreateTransactionUseCase,
|
||||
private val validateWalletAddressUseCase: ValidateWalletAddressUseCase,
|
||||
private val parseSharedAddressUseCase: ParseSharedAddressUseCase,
|
||||
private val walletManagersFacade: WalletManagersFacade,
|
||||
getExplorerTransactionUrlUseCase: GetExplorerTransactionUrlUseCase,
|
||||
validateWalletMemoUseCase: ValidateWalletMemoUseCase,
|
||||
|
|
@ -116,6 +118,7 @@ internal class SendViewModel @Inject constructor(
|
|||
private var feeJobHolder = JobHolder()
|
||||
private var addressValidationJobHolder = JobHolder()
|
||||
private var sendNotificationsJobHolder = JobHolder()
|
||||
private var qrScannerJobHolder = JobHolder()
|
||||
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
subscribeOnCurrencyStatusUpdates(owner)
|
||||
|
|
@ -326,9 +329,7 @@ internal class SendViewModel @Inject constructor(
|
|||
override fun onNextClick() = stateRouter.onNextClick()
|
||||
override fun onPrevClick() = stateRouter.onPrevClick()
|
||||
|
||||
override fun onQrCodeScanClick() {
|
||||
// TODO Add QR code scanning
|
||||
}
|
||||
override fun onQrCodeScanClick() = innerRouter.openQrCodeScanner(cryptoCurrency.network.name)
|
||||
|
||||
override fun onTokenDetailsClick(userWalletId: UserWalletId, currency: CryptoCurrency) =
|
||||
innerRouter.openTokenDetails(userWalletId, currency)
|
||||
|
|
@ -355,6 +356,21 @@ internal class SendViewModel @Inject constructor(
|
|||
// endregion
|
||||
|
||||
// region recipient state clicks
|
||||
fun onRecipientAddressScanned(address: String) {
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
parseSharedAddressUseCase(address, cryptoCurrency.network).fold(
|
||||
ifRight = { parsedCode ->
|
||||
onRecipientAddressValueChange(parsedCode.address)
|
||||
parsedCode.amount?.let { onAmountValueChange(it.toPlainString()) }
|
||||
parsedCode.memo?.let { onRecipientMemoValueChange(it) }
|
||||
},
|
||||
ifLeft = {
|
||||
Timber.w(it)
|
||||
},
|
||||
)
|
||||
}.saveIn(qrScannerJobHolder)
|
||||
}
|
||||
|
||||
override fun onRecipientAddressValueChange(value: String) {
|
||||
uiState = stateFactory.onRecipientAddressValueChange(value)
|
||||
viewModelScope.launch(dispatchers.main) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue