Updated on 2026-08-14
This commit is contained in:
parent
0b8d6e48ba
commit
05da18fa9c
76 changed files with 1748 additions and 248 deletions
|
|
@ -60,6 +60,7 @@ dependencies {
|
|||
implementation(Compose.foundation)
|
||||
implementation(Compose.material)
|
||||
implementation(Compose.uiTooling)
|
||||
implementation(Compose.coil)
|
||||
|
||||
/** Domain */
|
||||
implementation(project(":features:swap:domain"))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,22 @@
|
|||
package com.tangem.feature.swap.models
|
||||
|
||||
data class SwapPermissionStateHolder(
|
||||
val currency: String,
|
||||
val amount: String,
|
||||
val walletAddress: String,
|
||||
val spenderAddress: String,
|
||||
val fee: String,
|
||||
val approveButton: ApprovePermissionButton,
|
||||
val cancelButton: CancelPermissionButton,
|
||||
)
|
||||
|
||||
data class ApprovePermissionButton(
|
||||
val enabled: Boolean,
|
||||
val loading: Boolean = false,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
|
||||
data class CancelPermissionButton(
|
||||
val enabled: Boolean,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
package com.tangem.feature.swap.models
|
||||
|
||||
data class SwapSelectTokenStateHolder(
|
||||
val tokens: List<TokenToSelect>,
|
||||
val onSearchEntered: (String) -> Unit,
|
||||
val onTokenSelected: (String) -> Unit,
|
||||
)
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
package com.tangem.feature.swap.models
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
|
||||
data class SwapStateHolder(
|
||||
val sendCardData: SwapCardData,
|
||||
val receiveCardData: SwapCardData,
|
||||
val networkCurrency: String,
|
||||
val fee: FeeState = FeeState.Loaded(),
|
||||
val warnings: List<SwapWarning> = emptyList(),
|
||||
|
||||
val permissionState: SwapPermissionStateHolder? = null,
|
||||
val successState: SwapSuccessStateHolder? = null,
|
||||
val selectTokenState: SwapSelectTokenStateHolder? = null,
|
||||
|
||||
val swapButton: SwapButton,
|
||||
|
||||
val onRefresh: () -> Unit,
|
||||
val onBackClicked: () -> Unit,
|
||||
val onChangeCardsClicked: () -> Unit,
|
||||
val onSelectTokenClick: (() -> Unit)? = null,
|
||||
val onSuccess: (() -> Unit)? = null,
|
||||
)
|
||||
|
||||
data class TokenToSelect(
|
||||
val id: String,
|
||||
val name: String,
|
||||
val symbol: String,
|
||||
val addedTokenBalanceData: TokenBalanceData? = null,
|
||||
)
|
||||
|
||||
data class TokenBalanceData(
|
||||
val amount: String?,
|
||||
val amountEquivalent: String?,
|
||||
)
|
||||
|
||||
data class SwapCardData(
|
||||
val type: TransactionCardType,
|
||||
val amount: String?,
|
||||
val amountEquivalent: String?,
|
||||
val tokenIconUrl: String,
|
||||
val tokenCurrency: String,
|
||||
@DrawableRes val networkIconRes: Int? = null,
|
||||
val canSelectAnotherToken: Boolean = false,
|
||||
)
|
||||
|
||||
data class SwapButton(
|
||||
val enabled: Boolean,
|
||||
val loading: Boolean = false,
|
||||
val onClick: () -> Unit,
|
||||
)
|
||||
|
||||
sealed interface FeeState {
|
||||
data class Loaded(
|
||||
val fee: String = "",
|
||||
) : FeeState
|
||||
|
||||
object Loading : FeeState
|
||||
|
||||
data class NotEnoughFundsWarning(val fee: String) : FeeState
|
||||
}
|
||||
|
||||
sealed interface TransactionCardType {
|
||||
|
||||
data class SendCard(
|
||||
val balance: String,
|
||||
val permissionIsGiven: Boolean,
|
||||
) : TransactionCardType
|
||||
|
||||
data class ReceiveCard(
|
||||
val highPriceImpact: String? = null,
|
||||
) : TransactionCardType
|
||||
}
|
||||
|
||||
sealed interface SwapWarning {
|
||||
data class PermissionNeeded(val tokenCurrency: String) : SwapWarning
|
||||
// data class RateExpired(val onClick: () -> Unit) : SwapWarning
|
||||
// object HighPriceImpact : SwapWarning
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package com.tangem.feature.swap.models
|
||||
|
||||
data class SwapSuccessStateHolder(
|
||||
val message: String,
|
||||
)
|
||||
|
|
@ -1,16 +1,21 @@
|
|||
package com.tangem.feature.swap.presentation
|
||||
|
||||
import android.os.Bundle
|
||||
import android.transition.TransitionInflater
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.tangem.feature.swap.router.SwapRouter
|
||||
import com.tangem.feature.swap.router.SwapScreen
|
||||
import com.tangem.feature.swap.ui.SwapScreen
|
||||
import com.tangem.feature.swap.ui.SwapSuccessScreen
|
||||
import com.tangem.feature.swap.viewmodels.SwapViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
@AndroidEntryPoint
|
||||
class SwapFragment : Fragment() {
|
||||
|
|
@ -19,10 +24,24 @@ class SwapFragment : Fragment() {
|
|||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
activity?.window?.let { WindowCompat.setDecorFitsSystemWindows(it, true) }
|
||||
viewModel
|
||||
viewModel.setRouter(SwapRouter(fragmentManager = WeakReference(parentFragmentManager)))
|
||||
return ComposeView(inflater.context).apply {
|
||||
setContent {
|
||||
//todo add compose screen here
|
||||
Crossfade(targetState = viewModel.currentScreen) { screen ->
|
||||
when (screen) {
|
||||
SwapScreen.Main -> SwapScreen(stateHolder = viewModel.uiState)
|
||||
SwapScreen.Success -> {
|
||||
val successState = viewModel.uiState.successState
|
||||
if (successState != null) {
|
||||
SwapSuccessScreen(state = successState, viewModel.uiState.onBackClicked)
|
||||
} else {
|
||||
SwapScreen(stateHolder = viewModel.uiState)
|
||||
}
|
||||
}
|
||||
SwapScreen.SelectToken -> SwapScreen(stateHolder = viewModel.uiState)// TODO: use proper screen
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.feature.swap.router
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
internal class SwapRouter(
|
||||
private val fragmentManager: WeakReference<FragmentManager>,
|
||||
) {
|
||||
|
||||
var currentScreen by mutableStateOf(SwapScreen.Main)
|
||||
private set
|
||||
|
||||
fun openScreen(screen: SwapScreen) {
|
||||
currentScreen = screen
|
||||
}
|
||||
|
||||
fun back() {
|
||||
if (currentScreen == SwapScreen.SelectToken) {
|
||||
currentScreen = SwapScreen.Main
|
||||
} else {
|
||||
fragmentManager.get()?.popBackStack()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class SwapScreen {
|
||||
Main, Success, SelectToken
|
||||
}
|
||||
|
|
@ -0,0 +1,219 @@
|
|||
package com.tangem.feature.swap.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Divider
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.PrimaryButtonIconRight
|
||||
import com.tangem.core.ui.components.SecondaryButton
|
||||
import com.tangem.core.ui.components.SpacerH10
|
||||
import com.tangem.core.ui.components.SpacerH12
|
||||
import com.tangem.core.ui.components.SpacerH16
|
||||
import com.tangem.core.ui.components.SpacerH28
|
||||
import com.tangem.core.ui.components.SpacerH32
|
||||
import com.tangem.core.ui.components.atoms.Hand
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.swap.models.ApprovePermissionButton
|
||||
import com.tangem.feature.swap.models.CancelPermissionButton
|
||||
import com.tangem.feature.swap.models.SwapPermissionStateHolder
|
||||
import com.tangem.feature.swap.presentation.R
|
||||
|
||||
@Composable
|
||||
fun SwapPermissionBottomSheetContent(
|
||||
data: SwapPermissionStateHolder,
|
||||
onCancel: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(color = TangemTheme.colors.background.primary)
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = TangemTheme.dimens.spacing16),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
Hand()
|
||||
|
||||
SpacerH10()
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.swapping_permission_header),
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
)
|
||||
|
||||
SpacerH10()
|
||||
|
||||
Text(
|
||||
text = stringResource(id = R.string.swapping_permission_subheader, data.currency),
|
||||
color = TangemTheme.colors.text.secondary,
|
||||
style = TangemTheme.typography.body2,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing8),
|
||||
)
|
||||
|
||||
SpacerH16()
|
||||
|
||||
ApprovalBottomSheetInfo(
|
||||
currency = data.currency,
|
||||
amount = data.amount,
|
||||
walletAddress = data.walletAddress,
|
||||
spenderAddress = data.spenderAddress,
|
||||
fee = data.fee,
|
||||
)
|
||||
|
||||
SpacerH28()
|
||||
|
||||
PrimaryButtonIconRight(
|
||||
text = stringResource(id = R.string.swapping_permission_buttons_approve),
|
||||
icon = painterResource(id = R.drawable.ic_tangem_24),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
onClick = data.approveButton.onClick,
|
||||
)
|
||||
|
||||
SpacerH12()
|
||||
|
||||
SecondaryButton(
|
||||
text = stringResource(id = R.string.common_cancel),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
data.cancelButton.onClick()
|
||||
onCancel()
|
||||
}
|
||||
|
||||
SpacerH32()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ApprovalBottomSheetInfo(
|
||||
currency: String, amount: String, walletAddress: String, spenderAddress: String, fee: String,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(color = TangemTheme.colors.background.primary)
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing12,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
|
||||
AmountItem(currency = currency, amount = amount)
|
||||
DividerBottomSheet()
|
||||
WalletAddressItem(walletAddress = walletAddress)
|
||||
DividerBottomSheet()
|
||||
SpenderItem(spenderAddress = spenderAddress)
|
||||
DividerBottomSheet()
|
||||
FeeItem(fee = fee)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun DividerBottomSheet() {
|
||||
Divider(
|
||||
color = TangemTheme.colors.stroke.primary,
|
||||
thickness = TangemTheme.dimens.size0_5,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InformationItem(subtitle: String, value: String) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
|
||||
Text(
|
||||
text = subtitle,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.subtitle1,
|
||||
maxLines = 1,
|
||||
)
|
||||
|
||||
SpacerH16()
|
||||
|
||||
Text(
|
||||
text = value,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
style = TangemTheme.typography.body2,
|
||||
maxLines = 1,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AmountItem(currency: String, amount: String) {
|
||||
InformationItem(
|
||||
subtitle = stringResource(id = R.string.swapping_permission_rows_amount, currency),
|
||||
value = amount,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun WalletAddressItem(walletAddress: String) {
|
||||
InformationItem(
|
||||
subtitle = stringResource(id = R.string.swapping_permission_rows_your_wallet),
|
||||
value = walletAddress,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SpenderItem(spenderAddress: String) {
|
||||
InformationItem(
|
||||
subtitle = stringResource(id = R.string.swapping_permission_rows_spender),
|
||||
value = spenderAddress,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FeeItem(fee: String) {
|
||||
InformationItem(
|
||||
subtitle = stringResource(id = R.string.send_fee_label),
|
||||
value = fee,
|
||||
)
|
||||
}
|
||||
|
||||
// region preview
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun Preview_AgreementBottomSheet_InLightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
SwapPermissionBottomSheetContent(data = previewData) {}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun Preview_AgreementBottomSheet_InDarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
SwapPermissionBottomSheetContent(data = previewData) {}
|
||||
}
|
||||
}
|
||||
|
||||
private val previewData = SwapPermissionStateHolder(
|
||||
currency = "DAI",
|
||||
amount = "∞",
|
||||
walletAddress = "",
|
||||
spenderAddress = "",
|
||||
fee = "2,14$",
|
||||
approveButton = ApprovePermissionButton(true) {},
|
||||
cancelButton = CancelPermissionButton(true) {},
|
||||
)
|
||||
|
||||
//endregion preview
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
package com.tangem.feature.swap.ui
|
||||
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.BottomSheetScaffold
|
||||
import androidx.compose.material.BottomSheetState
|
||||
import androidx.compose.material.BottomSheetValue
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.rememberBottomSheetScaffoldState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.swap.models.SwapStateHolder
|
||||
import com.tangem.feature.swap.presentation.R
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
internal fun SwapScreen(stateHolder: SwapStateHolder) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val bottomSheetScaffoldState = rememberBottomSheetScaffoldState(
|
||||
bottomSheetState = BottomSheetState(BottomSheetValue.Collapsed),
|
||||
)
|
||||
|
||||
TangemTheme {
|
||||
BottomSheetScaffold(
|
||||
topBar = {
|
||||
AppBarWithBackButton(
|
||||
text = stringResource(R.string.swapping_swap),
|
||||
onBackClick = stateHolder.onBackClicked,
|
||||
iconRes = R.drawable.ic_close_24,
|
||||
)
|
||||
},
|
||||
sheetContent = {
|
||||
if (stateHolder.permissionState != null) {
|
||||
SwapPermissionBottomSheetContent(
|
||||
data = stateHolder.permissionState,
|
||||
onCancel = { coroutineScope.launch { bottomSheetScaffoldState.bottomSheetState.collapse() } },
|
||||
)
|
||||
}
|
||||
},
|
||||
scaffoldState = bottomSheetScaffoldState,
|
||||
sheetShape = RoundedCornerShape(
|
||||
topStart = TangemTheme.dimens.radius16,
|
||||
topEnd = TangemTheme.dimens.radius16,
|
||||
),
|
||||
sheetElevation = TangemTheme.dimens.elevation24,
|
||||
sheetPeekHeight = TangemTheme.dimens.size0,
|
||||
content = {
|
||||
SwapScreenContent(
|
||||
state = stateHolder,
|
||||
onPermissionWarningClick = {
|
||||
coroutineScope.launch {
|
||||
if (bottomSheetScaffoldState.bottomSheetState.isCollapsed) {
|
||||
bottomSheetScaffoldState.bottomSheetState.expand()
|
||||
} else {
|
||||
bottomSheetScaffoldState.bottomSheetState.collapse()
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,226 @@
|
|||
package com.tangem.feature.swap.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.tangem.core.ui.components.ClickableWarningCard
|
||||
import com.tangem.core.ui.components.PrimaryButtonIconRight
|
||||
import com.tangem.core.ui.components.SmallInfoCard
|
||||
import com.tangem.core.ui.components.SmallInfoCardWithWarning
|
||||
import com.tangem.core.ui.components.SpacerH16
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.swap.models.FeeState
|
||||
import com.tangem.feature.swap.models.SwapButton
|
||||
import com.tangem.feature.swap.models.SwapCardData
|
||||
import com.tangem.feature.swap.models.SwapStateHolder
|
||||
import com.tangem.feature.swap.models.SwapWarning
|
||||
import com.tangem.feature.swap.models.TransactionCardType
|
||||
import com.tangem.feature.swap.presentation.R
|
||||
|
||||
@Composable
|
||||
internal fun SwapScreenContent(
|
||||
modifier: Modifier = Modifier,
|
||||
state: SwapStateHolder,
|
||||
onPermissionWarningClick: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.background(color = TangemTheme.colors.background.primary)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
top = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing32,
|
||||
),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(TangemTheme.dimens.spacing16),
|
||||
) {
|
||||
|
||||
MainInfo(state)
|
||||
|
||||
FeeItem(feeState = state.fee, currency = state.networkCurrency)
|
||||
|
||||
if (state.warnings.isNotEmpty()) {
|
||||
SwapWarnings(
|
||||
warnings = state.warnings,
|
||||
onApproveWarningClick = onPermissionWarningClick,
|
||||
)
|
||||
}
|
||||
|
||||
PrimaryButtonIconRight(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = stringResource(id = R.string.swapping_swap),
|
||||
icon = painterResource(id = R.drawable.ic_tangem_24),
|
||||
enabled = state.swapButton.enabled,
|
||||
showProgress = state.swapButton.loading,
|
||||
onClick = state.swapButton.onClick,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
private fun MainInfo(state: SwapStateHolder) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Column {
|
||||
TransactionCard(
|
||||
type = state.sendCardData.type,
|
||||
amount = state.sendCardData.amount,
|
||||
amountEquivalent = state.sendCardData.amountEquivalent,
|
||||
tokenIconUrl = state.sendCardData.tokenIconUrl,
|
||||
tokenCurrency = state.sendCardData.tokenCurrency,
|
||||
networkIconRes = state.sendCardData.networkIconRes,
|
||||
onChangeTokenClick = if (state.sendCardData.canSelectAnotherToken) state.onSelectTokenClick else null,
|
||||
)
|
||||
SpacerH16()
|
||||
TransactionCard(
|
||||
type = state.receiveCardData.type,
|
||||
amount = state.receiveCardData.amount,
|
||||
amountEquivalent = state.receiveCardData.amountEquivalent,
|
||||
tokenIconUrl = state.receiveCardData.tokenIconUrl,
|
||||
tokenCurrency = state.receiveCardData.tokenCurrency,
|
||||
networkIconRes = state.receiveCardData.networkIconRes,
|
||||
onChangeTokenClick = if (state.receiveCardData.canSelectAnotherToken) state.onSelectTokenClick else null,
|
||||
)
|
||||
}
|
||||
Card(
|
||||
elevation = TangemTheme.dimens.elevation3,
|
||||
shape = CircleShape,
|
||||
backgroundColor = TangemTheme.colors.background.plain,
|
||||
contentColor = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size48),
|
||||
onClick = state.onChangeCardsClicked,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = com.tangem.core.ui.R.drawable.ic_exchange_vertical_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.text.primary1,
|
||||
modifier = Modifier
|
||||
.padding(TangemTheme.dimens.spacing12),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FeeItem(feeState: FeeState, currency: String) {
|
||||
|
||||
val titleString = stringResource(id = R.string.send_fee_label)
|
||||
when (feeState) {
|
||||
is FeeState.Loaded -> {
|
||||
SmallInfoCard(startText = titleString, endText = feeState.fee, isLoading = false)
|
||||
}
|
||||
FeeState.Loading -> {
|
||||
SmallInfoCard(startText = titleString, endText = "", isLoading = true)
|
||||
}
|
||||
is FeeState.NotEnoughFundsWarning -> {
|
||||
SmallInfoCardWithWarning(
|
||||
startText = titleString,
|
||||
endText = feeState.fee,
|
||||
warningText = stringResource(id = R.string.token_details_send_blocked_fee_format, currency),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SwapWarnings(
|
||||
warnings: List<SwapWarning>, onApproveWarningClick: () -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(color = MaterialTheme.colors.primary)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
warnings.forEach { warning ->
|
||||
when (warning) {
|
||||
// SwapWarning.HighPriceImpact -> {
|
||||
// WarningCard(title = stringResource(id = R.string.), description = stringResource(id = R.string.))
|
||||
// }
|
||||
is SwapWarning.PermissionNeeded -> {
|
||||
ClickableWarningCard(
|
||||
title = stringResource(id = R.string.swapping_permission_header),
|
||||
description = stringResource(
|
||||
id = R.string.swapping_permission_subheader,
|
||||
warning.tokenCurrency,
|
||||
),
|
||||
onClick = onApproveWarningClick,
|
||||
)
|
||||
}
|
||||
// is SwapWarning.RateExpired -> {
|
||||
// RefreshableWaringCard(
|
||||
// title = stringResource(id = R.string.),
|
||||
// description = stringResource(id = R.string.),
|
||||
// onClick = warning.onClick,
|
||||
// )
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// region preview
|
||||
|
||||
private val sendCard = SwapCardData(
|
||||
type = TransactionCardType.SendCard("123", false),
|
||||
amount = "1 000 000",
|
||||
amountEquivalent = "1 000 000",
|
||||
tokenIconUrl = "",
|
||||
tokenCurrency = "DAI",
|
||||
networkIconRes = R.drawable.img_polygon_22,
|
||||
canSelectAnotherToken = false,
|
||||
)
|
||||
|
||||
private val receiveCard = SwapCardData(
|
||||
type = TransactionCardType.ReceiveCard(),
|
||||
amount = "1 000 000",
|
||||
amountEquivalent = "1 000 000",
|
||||
tokenIconUrl = "",
|
||||
tokenCurrency = "DAI",
|
||||
networkIconRes = R.drawable.img_polygon_22,
|
||||
canSelectAnotherToken = true,
|
||||
)
|
||||
|
||||
private val state = SwapStateHolder(
|
||||
sendCardData = sendCard,
|
||||
receiveCardData = receiveCard,
|
||||
fee = FeeState.Loaded(fee = "0.155 MATIC (0.14 $)"),
|
||||
warnings = emptyList(),
|
||||
networkCurrency = "MATIC",
|
||||
swapButton = SwapButton(enabled = true, loading = false, onClick = {}),
|
||||
onRefresh = {}, onBackClicked = {}, onChangeCardsClicked = {},
|
||||
)
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun SwapScreenContentPreview() {
|
||||
TangemTheme(isDark = false) {
|
||||
SwapScreenContent(state = state) {}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion preview
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.tangem.feature.swap.ui
|
||||
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.tangem.core.ui.components.SuccessScreenContent
|
||||
import com.tangem.core.ui.components.appbar.AppBarWithBackButton
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.swap.models.SwapSuccessStateHolder
|
||||
import com.tangem.feature.swap.presentation.R
|
||||
|
||||
@Composable
|
||||
fun SwapSuccessScreen(state: SwapSuccessStateHolder, onBack: () -> Unit) {
|
||||
TangemTheme {
|
||||
Scaffold(
|
||||
content = {
|
||||
SuccessScreenContent(
|
||||
successMessage = state.message,
|
||||
onButtonClick = onBack,
|
||||
)
|
||||
},
|
||||
topBar = {
|
||||
AppBarWithBackButton(
|
||||
text = stringResource(R.string.swapping_swap),
|
||||
onBackClick = onBack,
|
||||
iconRes = R.drawable.ic_close_24,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,378 @@
|
|||
package com.tangem.feature.swap.ui
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.Card
|
||||
import androidx.compose.material.ExperimentalMaterialApi
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import coil.compose.SubcomposeAsyncImage
|
||||
import coil.request.ImageRequest
|
||||
import com.tangem.core.ui.R
|
||||
import com.tangem.core.ui.components.CurrencyPlaceholderIcon
|
||||
import com.tangem.core.ui.components.FontSizeRange
|
||||
import com.tangem.core.ui.components.ResizableText
|
||||
import com.tangem.core.ui.components.SpacerH4
|
||||
import com.tangem.core.ui.components.SpacerW12
|
||||
import com.tangem.core.ui.res.TangemTheme
|
||||
import com.tangem.feature.swap.models.TransactionCardType
|
||||
import com.valentinilk.shimmer.shimmer
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun TransactionCard(
|
||||
type: TransactionCardType,
|
||||
amount: String?,
|
||||
amountEquivalent: String?,
|
||||
tokenIconUrl: String,
|
||||
tokenCurrency: String,
|
||||
@DrawableRes networkIconRes: Int? = null,
|
||||
onChangeTokenClick: (() -> Unit)? = null,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Card(
|
||||
modifier = modifier,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius12),
|
||||
backgroundColor = TangemTheme.colors.background.primary,
|
||||
elevation = TangemTheme.dimens.elevation2,
|
||||
onClick = onChangeTokenClick ?: {},
|
||||
enabled = onChangeTokenClick != null,
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(TangemTheme.dimens.size116),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
|
||||
Header(type = type)
|
||||
|
||||
Content(
|
||||
type = type,
|
||||
amount = amount,
|
||||
amountEquivalent = amountEquivalent,
|
||||
)
|
||||
}
|
||||
|
||||
Box(modifier = Modifier.align(Alignment.BottomEnd)) {
|
||||
Token(
|
||||
tokenIconUrl = tokenIconUrl,
|
||||
tokenCurrency = tokenCurrency,
|
||||
networkIconRes = networkIconRes,
|
||||
)
|
||||
}
|
||||
|
||||
if (onChangeTokenClick != null) {
|
||||
Box(modifier = Modifier.align(Alignment.CenterEnd)) {
|
||||
ChangeTokenSelector()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Header(
|
||||
type: TransactionCardType,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(
|
||||
bottom = dimensionResource(id = R.dimen.spacing8),
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
val title = when (type) {
|
||||
is TransactionCardType.ReceiveCard -> R.string.exchange_receive_view_header
|
||||
is TransactionCardType.SendCard -> R.string.exchange_send_view_header
|
||||
}
|
||||
Text(
|
||||
text = stringResource(id = title),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
style = MaterialTheme.typography.subtitle2,
|
||||
modifier = Modifier.padding(top = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
if (type is TransactionCardType.SendCard) {
|
||||
Text(
|
||||
text = stringResource(R.string.common_balance, type.balance),
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
style = MaterialTheme.typography.body2,
|
||||
modifier = Modifier.padding(top = TangemTheme.dimens.spacing12),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Content(
|
||||
type: TransactionCardType,
|
||||
amount: String?,
|
||||
amountEquivalent: String?,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
top = TangemTheme.dimens.spacing8,
|
||||
start = TangemTheme.dimens.spacing16,
|
||||
bottom = TangemTheme.dimens.spacing16,
|
||||
),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
) {
|
||||
|
||||
if (type is TransactionCardType.SendCard && !type.permissionIsGiven) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size56)
|
||||
.background(
|
||||
color = TangemTheme.colors.button.secondary,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.size12),
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_locked_24),
|
||||
contentDescription = null,
|
||||
tint = TangemTheme.colors.icon.primary1,
|
||||
)
|
||||
}
|
||||
|
||||
SpacerW12()
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
end = TangemTheme.dimens.spacing92,
|
||||
),
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
|
||||
if (amount != null) {
|
||||
ResizableText(
|
||||
text = amount,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
style = TangemTheme.typography.h2,
|
||||
fontSizeRange =
|
||||
FontSizeRange(min = 16.sp, max = TangemTheme.typography.h2.fontSize),
|
||||
)
|
||||
} else {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(width = TangemTheme.dimens.size102, height = TangemTheme.dimens.size24)
|
||||
.shimmer()
|
||||
.background(
|
||||
color = TangemTheme.colors.button.secondary,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius6),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.size(TangemTheme.dimens.size4)
|
||||
.weight(1f),
|
||||
)
|
||||
|
||||
if (amountEquivalent != null) {
|
||||
Text(
|
||||
text = amountEquivalent,
|
||||
color = TangemTheme.colors.text.tertiary,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.body2,
|
||||
)
|
||||
} else {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(width = TangemTheme.dimens.size40, height = TangemTheme.dimens.size12)
|
||||
.shimmer()
|
||||
.background(
|
||||
color = TangemTheme.colors.button.secondary,
|
||||
shape = RoundedCornerShape(TangemTheme.dimens.radius3),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Token(
|
||||
tokenIconUrl: String,
|
||||
tokenCurrency: String,
|
||||
@DrawableRes networkIconRes: Int? = null,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
end = TangemTheme.dimens.spacing12,
|
||||
bottom = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
verticalArrangement = Arrangement.Bottom,
|
||||
horizontalAlignment = Alignment.End,
|
||||
) {
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
end = TangemTheme.dimens.spacing16,
|
||||
)
|
||||
.size(TangemTheme.dimens.size42),
|
||||
) {
|
||||
val tokenImageModifier = Modifier
|
||||
.align(Alignment.BottomStart)
|
||||
.size(TangemTheme.dimens.size36)
|
||||
|
||||
SubcomposeAsyncImage(
|
||||
modifier = tokenImageModifier,
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
.data(tokenIconUrl)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
loading = { TokenImageShimmer(modifier = tokenImageModifier) },
|
||||
error = { CurrencyPlaceholderIcon(modifier = tokenImageModifier, tokenCurrency) },
|
||||
contentDescription = null,
|
||||
)
|
||||
|
||||
if (networkIconRes != null) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.size(TangemTheme.dimens.size18)
|
||||
.background(
|
||||
color = Color.White,
|
||||
shape = CircleShape,
|
||||
),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
Image(
|
||||
modifier = Modifier.padding(all = (0.5).dp),
|
||||
painter = painterResource(id = networkIconRes),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SpacerH4()
|
||||
|
||||
Text(
|
||||
text = tokenCurrency,
|
||||
color = TangemTheme.colors.text.primary1,
|
||||
maxLines = 1,
|
||||
style = TangemTheme.typography.subtitle2,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier
|
||||
.defaultMinSize(minWidth = TangemTheme.dimens.size80),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChangeTokenSelector() {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.padding(
|
||||
top = TangemTheme.dimens.spacing12,
|
||||
start = TangemTheme.dimens.spacing24,
|
||||
end = TangemTheme.dimens.spacing12,
|
||||
),
|
||||
contentAlignment = Alignment.CenterEnd,
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier.size(TangemTheme.dimens.size20),
|
||||
painter = painterResource(id = R.drawable.ic_chevron_24),
|
||||
contentDescription = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TokenImageShimmer(
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(modifier = modifier.shimmer()) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.background(
|
||||
color = TangemTheme.colors.button.secondary,
|
||||
shape = CircleShape,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// region preview
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 116, showBackground = true)
|
||||
@Composable
|
||||
fun Preview_SwapMainCard_InLightTheme() {
|
||||
TangemTheme(isDark = false) {
|
||||
TransactionCardPreview()
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(widthDp = 328, heightDp = 116, showBackground = true)
|
||||
@Composable
|
||||
fun Preview_SwapMainCard_InDarkTheme() {
|
||||
TangemTheme(isDark = true) {
|
||||
TransactionCardPreview()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun TransactionCardPreview() {
|
||||
TransactionCard(
|
||||
type = TransactionCardType.SendCard("123", false),
|
||||
amount = "1 000 000",
|
||||
amountEquivalent = "1 000 000",
|
||||
tokenIconUrl = "",
|
||||
tokenCurrency = "DAI",
|
||||
networkIconRes = R.drawable.img_polygon_22,
|
||||
onChangeTokenClick = {},
|
||||
)
|
||||
}
|
||||
|
||||
// endregion preview
|
||||
|
|
@ -1,14 +1,31 @@
|
|||
package com.tangem.feature.swap.viewmodels
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.tangem.feature.swap.domain.SwapInteractor
|
||||
import com.tangem.feature.swap.domain.models.data.Currency
|
||||
import com.tangem.feature.swap.models.ApprovePermissionButton
|
||||
import com.tangem.feature.swap.models.CancelPermissionButton
|
||||
import com.tangem.feature.swap.models.FeeState
|
||||
import com.tangem.feature.swap.models.SwapButton
|
||||
import com.tangem.feature.swap.models.SwapCardData
|
||||
import com.tangem.feature.swap.models.SwapPermissionStateHolder
|
||||
import com.tangem.feature.swap.models.SwapStateHolder
|
||||
import com.tangem.feature.swap.models.SwapSuccessStateHolder
|
||||
import com.tangem.feature.swap.models.SwapWarning
|
||||
import com.tangem.feature.swap.models.TransactionCardType
|
||||
import com.tangem.feature.swap.presentation.R
|
||||
import com.tangem.feature.swap.router.SwapRouter
|
||||
import com.tangem.feature.swap.router.SwapScreen
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
@HiltViewModel
|
||||
internal class SwapViewModel @Inject constructor(
|
||||
|
|
@ -16,6 +33,27 @@ internal class SwapViewModel @Inject constructor(
|
|||
private val dispatchers: CoroutineDispatcherProvider,
|
||||
) : ViewModel() {
|
||||
|
||||
var uiState: SwapStateHolder by mutableStateOf(createInitialUiState())
|
||||
private set
|
||||
|
||||
private var swapRouter: SwapRouter by Delegates.notNull()
|
||||
|
||||
var currentScreen = SwapScreen.Main
|
||||
get() = swapRouter.currentScreen
|
||||
|
||||
fun setRouter(router: SwapRouter) {
|
||||
swapRouter = router
|
||||
uiState = uiState.copy(
|
||||
onBackClicked = router::back,
|
||||
onSelectTokenClick = { router.openScreen(SwapScreen.Success) },
|
||||
onSuccess = { router.openScreen(SwapScreen.Success) },
|
||||
)
|
||||
}
|
||||
|
||||
private fun createInitialUiState(): SwapStateHolder { // TODO: create using real state
|
||||
return createTestUiState()
|
||||
}
|
||||
|
||||
// region temp for test
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
|
|
@ -39,5 +77,48 @@ internal class SwapViewModel @Inject constructor(
|
|||
"1",
|
||||
)
|
||||
}
|
||||
|
||||
private fun createTestUiState(): SwapStateHolder {
|
||||
val sendCard = SwapCardData(
|
||||
type = TransactionCardType.SendCard("123", false),
|
||||
amount = "1 000 000 000 000 000 000",
|
||||
amountEquivalent = "1 000 000 000 000 000 000",
|
||||
tokenIconUrl = "",
|
||||
tokenCurrency = "DAI",
|
||||
networkIconRes = R.drawable.img_polygon_22,
|
||||
)
|
||||
|
||||
val receiveCard = SwapCardData(
|
||||
type = TransactionCardType.ReceiveCard(),
|
||||
amount = "1 000 000",
|
||||
amountEquivalent = "1 000 000",
|
||||
tokenIconUrl = "",
|
||||
tokenCurrency = "DAI",
|
||||
networkIconRes = R.drawable.img_polygon_22,
|
||||
canSelectAnotherToken = true,
|
||||
)
|
||||
|
||||
val permissionState = SwapPermissionStateHolder(
|
||||
currency = "DAI",
|
||||
amount = "∞",
|
||||
walletAddress = "",
|
||||
spenderAddress = "",
|
||||
fee = "2,14$",
|
||||
approveButton = ApprovePermissionButton(true) {},
|
||||
cancelButton = CancelPermissionButton(true) {},
|
||||
)
|
||||
|
||||
return SwapStateHolder(
|
||||
sendCardData = sendCard,
|
||||
receiveCardData = receiveCard,
|
||||
fee = FeeState.Loaded(fee = "0.155 MATIC (0.14 $)"),
|
||||
warnings = listOf(SwapWarning.PermissionNeeded("DAI")),
|
||||
networkCurrency = "MATIC",
|
||||
swapButton = SwapButton(enabled = true, loading = false, onClick = {}),
|
||||
onRefresh = {}, onBackClicked = {}, onChangeCardsClicked = {},
|
||||
permissionState = permissionState,
|
||||
successState = SwapSuccessStateHolder("Success"),
|
||||
)
|
||||
}
|
||||
// endregion temp for test
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue