Updated on 2026-08-14
This commit is contained in:
parent
3c59214c76
commit
4c903fc654
73 changed files with 886 additions and 190 deletions
|
|
@ -41,6 +41,7 @@ data class SwapCardData(
|
|||
val tokenIconUrl: String,
|
||||
val tokenCurrency: String,
|
||||
val balance: String,
|
||||
val isBalanceHidden: Boolean,
|
||||
val isNotNativeToken: Boolean,
|
||||
val canSelectAnotherToken: Boolean = false,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,11 @@ class SwapFragment : Fragment() {
|
|||
|
||||
private val viewModel by viewModels<SwapViewModel>()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
lifecycle.addObserver(viewModel)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
viewModel.setRouter(
|
||||
SwapRouter(
|
||||
|
|
@ -70,6 +75,11 @@ class SwapFragment : Fragment() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
lifecycle.removeObserver(viewModel)
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CURRENCY_BUNDLE_KEY = "swap_currency"
|
||||
const val DERIVATION_PATH = "DERIVATION_STYLE"
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package com.tangem.feature.swap.ui
|
|||
|
||||
import androidx.compose.ui.text.TextRange
|
||||
import androidx.compose.ui.text.input.TextFieldValue
|
||||
import com.tangem.common.Provider
|
||||
import com.tangem.core.ui.components.states.Item
|
||||
import com.tangem.core.ui.components.states.SelectableItemsState
|
||||
import com.tangem.core.ui.extensions.TextReference
|
||||
|
|
@ -21,7 +22,7 @@ import kotlinx.collections.immutable.toImmutableList
|
|||
* State builder creates a specific states for SwapScreen
|
||||
*/
|
||||
@Suppress("LargeClass")
|
||||
internal class StateBuilder(val actions: UiActions) {
|
||||
internal class StateBuilder(val actions: UiActions, val isBalanceHiddenProvider: Provider<Boolean>) {
|
||||
|
||||
private val tokensDataConverter = TokensDataConverter(actions.onSearchEntered, actions.onTokenSelected)
|
||||
|
||||
|
|
@ -39,6 +40,7 @@ internal class StateBuilder(val actions: UiActions) {
|
|||
canSelectAnotherToken = false,
|
||||
isNotNativeToken = initialCurrency.isNonNative(),
|
||||
balance = "",
|
||||
isBalanceHidden = true,
|
||||
),
|
||||
receiveCardData = SwapCardData(
|
||||
type = TransactionCardType.ReceiveCard(),
|
||||
|
|
@ -50,6 +52,7 @@ internal class StateBuilder(val actions: UiActions) {
|
|||
balance = "",
|
||||
isNotNativeToken = false,
|
||||
coinId = null,
|
||||
isBalanceHidden = true,
|
||||
),
|
||||
fee = FeeState.Loading,
|
||||
networkCurrency = networkInfo.blockchainCurrency,
|
||||
|
|
@ -84,6 +87,7 @@ internal class StateBuilder(val actions: UiActions) {
|
|||
isNotNativeToken = fromToken.isNonNative(),
|
||||
canSelectAnotherToken = canSelectSendToken,
|
||||
balance = if (!canSelectSendToken) uiStateHolder.sendCardData.balance else "",
|
||||
isBalanceHidden = isBalanceHiddenProvider(),
|
||||
),
|
||||
receiveCardData = SwapCardData(
|
||||
type = TransactionCardType.ReceiveCard(),
|
||||
|
|
@ -95,6 +99,7 @@ internal class StateBuilder(val actions: UiActions) {
|
|||
isNotNativeToken = toToken.isNonNative(),
|
||||
canSelectAnotherToken = canSelectReceiveToken,
|
||||
balance = if (!canSelectReceiveToken) uiStateHolder.receiveCardData.balance else "",
|
||||
isBalanceHidden = isBalanceHiddenProvider(),
|
||||
),
|
||||
fee = FeeState.Loading,
|
||||
swapButton = SwapButton(enabled = false, loading = true, onClick = {}),
|
||||
|
|
@ -144,6 +149,7 @@ internal class StateBuilder(val actions: UiActions) {
|
|||
tokenCurrency = uiStateHolder.sendCardData.tokenCurrency,
|
||||
canSelectAnotherToken = uiStateHolder.sendCardData.canSelectAnotherToken,
|
||||
balance = quoteModel.fromTokenInfo.tokenWalletBalance,
|
||||
isBalanceHidden = isBalanceHiddenProvider(),
|
||||
),
|
||||
receiveCardData = SwapCardData(
|
||||
type = TransactionCardType.ReceiveCard(),
|
||||
|
|
@ -155,6 +161,7 @@ internal class StateBuilder(val actions: UiActions) {
|
|||
tokenCurrency = uiStateHolder.receiveCardData.tokenCurrency,
|
||||
canSelectAnotherToken = uiStateHolder.receiveCardData.canSelectAnotherToken,
|
||||
balance = quoteModel.toTokenInfo.tokenWalletBalance,
|
||||
isBalanceHidden = isBalanceHiddenProvider(),
|
||||
),
|
||||
networkCurrency = quoteModel.networkCurrency,
|
||||
warnings = warnings,
|
||||
|
|
@ -192,6 +199,7 @@ internal class StateBuilder(val actions: UiActions) {
|
|||
tokenCurrency = uiStateHolder.sendCardData.tokenCurrency,
|
||||
canSelectAnotherToken = uiStateHolder.sendCardData.canSelectAnotherToken,
|
||||
balance = emptyAmountState.fromTokenWalletBalance,
|
||||
isBalanceHidden = isBalanceHiddenProvider(),
|
||||
),
|
||||
receiveCardData = SwapCardData(
|
||||
type = TransactionCardType.ReceiveCard(),
|
||||
|
|
@ -203,6 +211,7 @@ internal class StateBuilder(val actions: UiActions) {
|
|||
tokenCurrency = uiStateHolder.receiveCardData.tokenCurrency,
|
||||
canSelectAnotherToken = uiStateHolder.receiveCardData.canSelectAnotherToken,
|
||||
balance = emptyAmountState.toTokenWalletBalance,
|
||||
isBalanceHidden = isBalanceHiddenProvider(),
|
||||
),
|
||||
warnings = emptyList(),
|
||||
fee = FeeState.Empty,
|
||||
|
|
@ -254,6 +263,19 @@ internal class StateBuilder(val actions: UiActions) {
|
|||
)
|
||||
}
|
||||
|
||||
fun updateBalanceHiddenState(uiState: SwapStateHolder, isBalanceHidden: Boolean): SwapStateHolder {
|
||||
val patchedSendCardData = uiState.sendCardData.copy(
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
)
|
||||
val patchedReceiveCardData = uiState.receiveCardData.copy(
|
||||
isBalanceHidden = isBalanceHidden,
|
||||
)
|
||||
return uiState.copy(
|
||||
sendCardData = patchedSendCardData,
|
||||
receiveCardData = patchedReceiveCardData,
|
||||
)
|
||||
}
|
||||
|
||||
fun updateApproveType(uiState: SwapStateHolder, approveType: ApproveType): SwapStateHolder {
|
||||
return if (uiState.permissionState is SwapPermissionState.ReadyForRequest) {
|
||||
uiState.copy(
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import androidx.compose.ui.text.input.TextFieldValue
|
|||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.constraintlayout.compose.ConstraintLayout
|
||||
import com.tangem.common.Strings.STARS
|
||||
import com.tangem.core.ui.components.*
|
||||
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
|
||||
import com.tangem.core.ui.components.states.Item
|
||||
|
|
@ -143,7 +144,11 @@ private fun MainInfo(state: SwapStateHolder) {
|
|||
val priceImpactWarning = state.warnings.filterIsInstance<SwapWarning.HighPriceImpact>().firstOrNull()
|
||||
TransactionCard(
|
||||
type = state.sendCardData.type,
|
||||
balance = state.sendCardData.balance,
|
||||
balance = if (state.sendCardData.isBalanceHidden) {
|
||||
STARS
|
||||
} else {
|
||||
state.sendCardData.balance
|
||||
},
|
||||
textFieldValue = state.sendCardData.amountTextFieldValue,
|
||||
amountEquivalent = state.sendCardData.amountEquivalent,
|
||||
tokenIconUrl = state.sendCardData.tokenIconUrl,
|
||||
|
|
@ -161,7 +166,7 @@ private fun MainInfo(state: SwapStateHolder) {
|
|||
val marginCard = TangemTheme.dimens.spacing16
|
||||
TransactionCard(
|
||||
type = state.receiveCardData.type,
|
||||
balance = state.receiveCardData.balance,
|
||||
balance = if (state.receiveCardData.isBalanceHidden) STARS else state.receiveCardData.balance,
|
||||
textFieldValue = state.receiveCardData.amountTextFieldValue,
|
||||
amountEquivalent = state.receiveCardData.amountEquivalent,
|
||||
tokenIconUrl = state.receiveCardData.tokenIconUrl,
|
||||
|
|
@ -371,6 +376,7 @@ private val sendCard = SwapCardData(
|
|||
canSelectAnotherToken = false,
|
||||
balance = "123",
|
||||
coinId = "",
|
||||
isBalanceHidden = false,
|
||||
)
|
||||
|
||||
private val receiveCard = SwapCardData(
|
||||
|
|
@ -383,6 +389,7 @@ private val receiveCard = SwapCardData(
|
|||
canSelectAnotherToken = true,
|
||||
balance = "33333",
|
||||
coinId = "",
|
||||
isBalanceHidden = false,
|
||||
)
|
||||
|
||||
val stateSelectable = SelectableItemsState(
|
||||
|
|
|
|||
|
|
@ -3,11 +3,12 @@ package com.tangem.feature.swap.viewmodels
|
|||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.*
|
||||
import com.tangem.common.Provider
|
||||
import com.tangem.core.analytics.api.AnalyticsEventHandler
|
||||
import com.tangem.core.ui.utils.InputNumberFormatter
|
||||
import com.tangem.domain.balancehiding.IsBalanceHiddenUseCase
|
||||
import com.tangem.domain.balancehiding.ListenToFlipsUseCase
|
||||
import com.tangem.feature.swap.analytics.SwapEvents
|
||||
import com.tangem.feature.swap.domain.BlockchainInteractor
|
||||
import com.tangem.feature.swap.domain.SwapInteractor
|
||||
|
|
@ -27,8 +28,9 @@ import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
|||
import com.tangem.utils.coroutines.Debouncer
|
||||
import com.tangem.utils.coroutines.runCatching
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.*
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.Json
|
||||
import timber.log.Timber
|
||||
import java.text.DecimalFormat
|
||||
|
|
@ -37,15 +39,17 @@ import java.util.*
|
|||
import javax.inject.Inject
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
@Suppress("LargeClass")
|
||||
@Suppress("LargeClass", "LongParameterList")
|
||||
@HiltViewModel
|
||||
internal class SwapViewModel @Inject constructor(
|
||||
private val swapInteractor: SwapInteractor,
|
||||
private val blockchainInteractor: BlockchainInteractor,
|
||||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
private val analyticsEventHandler: AnalyticsEventHandler,
|
||||
private val isBalanceHiddenUseCase: IsBalanceHiddenUseCase,
|
||||
private val listenToFlipsUseCase: ListenToFlipsUseCase,
|
||||
savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel() {
|
||||
) : ViewModel(), DefaultLifecycleObserver {
|
||||
|
||||
private val currency = Json.decodeFromString<Currency>(
|
||||
savedStateHandle[SwapFragment.CURRENCY_BUNDLE_KEY]
|
||||
|
|
@ -53,15 +57,20 @@ internal class SwapViewModel @Inject constructor(
|
|||
)
|
||||
private val derivationPath = savedStateHandle.get<String>(SwapFragment.DERIVATION_PATH)
|
||||
|
||||
private var isBalanceHidden = true
|
||||
|
||||
private val stateBuilder = StateBuilder(
|
||||
actions = createUiActions(),
|
||||
isBalanceHiddenProvider = Provider { isBalanceHidden },
|
||||
)
|
||||
|
||||
private val inputNumberFormatter =
|
||||
InputNumberFormatter(NumberFormat.getInstance(Locale.getDefault()) as DecimalFormat)
|
||||
private val amountDebouncer = Debouncer()
|
||||
private val singleTaskScheduler = SingleTaskScheduler<SwapState>()
|
||||
|
||||
private var dataState by mutableStateOf(SwapProcessDataState(networkId = currency.networkId))
|
||||
|
||||
var uiState: SwapStateHolder by mutableStateOf(
|
||||
stateBuilder.createInitialLoadingState(
|
||||
initialCurrency = currency,
|
||||
|
|
@ -82,6 +91,24 @@ internal class SwapViewModel @Inject constructor(
|
|||
initTokens(currency)
|
||||
}
|
||||
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
isBalanceHiddenUseCase()
|
||||
.flowWithLifecycle(owner.lifecycle)
|
||||
.onEach { hidden ->
|
||||
isBalanceHidden = hidden
|
||||
withContext(dispatchers.main) {
|
||||
uiState = stateBuilder.updateBalanceHiddenState(uiState, isBalanceHidden)
|
||||
}
|
||||
}
|
||||
.launchIn(viewModelScope)
|
||||
|
||||
viewModelScope.launch {
|
||||
listenToFlipsUseCase()
|
||||
.flowWithLifecycle(owner.lifecycle)
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
singleTaskScheduler.cancelTask()
|
||||
super.onCleared()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue