Updated on 2026-08-14

This commit is contained in:
Tangem 2025-05-13 11:51:55 +05:00
parent 1ccffe29f5
commit 4e69a47c32
17 changed files with 652 additions and 75 deletions

View file

@ -74,7 +74,7 @@ internal class WcAppInfoContainerComponent(
title = when (route) {
is WcAppInfoRoutes.Alert -> null
WcAppInfoRoutes.AppInfo -> resourceReference(R.string.wc_wallet_connect)
WcAppInfoRoutes.SelectNetworks -> TODO("[REDACTED_TASK_KEY]")
WcAppInfoRoutes.SelectNetworks -> stringReference("Choose networks")
WcAppInfoRoutes.SelectWallet -> stringReference("Choose wallet")
},
startIconRes = when (route) {
@ -100,10 +100,13 @@ internal class WcAppInfoContainerComponent(
}
private fun contentBack() {
if (contentStack.value.active.configuration == WcAppInfoRoutes.AppInfo) {
dismiss()
} else {
model.contentNavigation.pop()
when (contentStack.value.active.configuration) {
WcAppInfoRoutes.AppInfo -> dismiss()
WcAppInfoRoutes.SelectNetworks -> {
model.clearAvailableNetworks()
model.contentNavigation.pop()
}
else -> model.contentNavigation.pop()
}
}
@ -112,7 +115,10 @@ internal class WcAppInfoContainerComponent(
return when (config) {
is WcAppInfoRoutes.AppInfo -> WcAppInfoComponent(appComponentContext = appComponentContext, model = model)
is WcAppInfoRoutes.Alert -> TODO()
WcAppInfoRoutes.SelectNetworks -> TODO()
WcAppInfoRoutes.SelectNetworks -> WcSelectNetworksComponent(
appComponentContext = appComponentContext,
model = model,
)
WcAppInfoRoutes.SelectWallet -> WcSelectWalletComponent(
appComponentContext = appComponentContext,
model = model,

View file

@ -0,0 +1,367 @@
package com.tangem.features.walletconnect.connections.components
import android.content.res.Configuration
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.common.ui.notifications.NotificationUM
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.components.PrimaryButton
import com.tangem.core.ui.components.TangemSwitch
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.features.walletconnect.connections.entity.WcNetworkInfoItem
import com.tangem.features.walletconnect.connections.entity.WcSelectNetworksUM
import com.tangem.features.walletconnect.connections.model.WcAppInfoModel
import com.tangem.features.walletconnect.impl.R
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
internal class WcSelectNetworksComponent(
appComponentContext: AppComponentContext,
private val model: WcAppInfoModel,
) : AppComponentContext by appComponentContext, ComposableContentComponent {
@Composable
override fun Content(modifier: Modifier) {
val state by model.networksState.collectAsStateWithLifecycle()
WcSelectNetworksContent(
modifier = modifier.padding(horizontal = 16.dp),
state = state,
)
}
}
@Composable
private fun WcSelectNetworksContent(state: WcSelectNetworksUM, modifier: Modifier = Modifier) {
val blocksModifier = Modifier
.clip(RoundedCornerShape(TangemTheme.dimens.radius14))
.background(color = TangemTheme.colors.background.action)
.fillMaxWidth()
Column(modifier = modifier, verticalArrangement = Arrangement.spacedBy(16.dp)) {
if (state.missing.isNotEmpty()) {
MissingRequiredBlock(modifier = blocksModifier, missingNetworks = state.missing)
}
if (state.required.isNotEmpty() || state.available.isNotEmpty()) {
AvailableNetworksBlock(modifier = blocksModifier, required = state.required, available = state.available)
}
if (state.notAdded.isNotEmpty()) {
NotAddedBlock(modifier = blocksModifier, notAdded = state.notAdded)
}
PrimaryButton(
modifier = Modifier
.padding(bottom = 16.dp)
.fillMaxWidth(),
text = "Done",
onClick = state.onDone,
enabled = state.missing.isEmpty(),
)
}
}
@Composable
private fun MissingRequiredBlock(missingNetworks: ImmutableList<WcNetworkInfoItem>, modifier: Modifier = Modifier) {
val missingNetworksName = missingNetworks.joinToString { it.name }
val notificationUM = remember(missingNetworks) {
NotificationUM.Info(
title = stringReference("The wallet has no required networks"),
subtitle = stringReference("Add the $missingNetworksName network to your portfolio for this wallet."),
)
}
Column(modifier = modifier) {
Notification(
config = notificationUM.config,
containerColor = TangemTheme.colors.background.action,
iconTint = TangemTheme.colors.icon.attention,
)
HorizontalDivider(
modifier = modifier.fillMaxWidth(),
thickness = 1.dp,
color = TangemTheme.colors.stroke.primary,
)
missingNetworks.fastForEach { network ->
key(network.id) {
NetworkItems(modifier = Modifier.padding(vertical = 14.dp, horizontal = 12.dp), networkItem = network)
}
}
}
}
@Composable
private fun AvailableNetworksBlock(
required: ImmutableList<WcNetworkInfoItem>,
available: ImmutableList<WcNetworkInfoItem>,
modifier: Modifier = Modifier,
) {
Column(modifier = modifier) {
Text(
modifier = Modifier.padding(top = 12.dp, bottom = 4.dp, start = 12.dp, end = 12.dp),
text = "Available networks",
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.tertiary,
)
required.fastForEach { network ->
key(network.id) {
NetworkItems(modifier = Modifier.padding(vertical = 14.dp, horizontal = 12.dp), networkItem = network)
}
}
available.fastForEach { network ->
key(network.id) {
NetworkItems(modifier = Modifier.padding(vertical = 14.dp, horizontal = 12.dp), networkItem = network)
}
}
}
}
@Composable
private fun NotAddedBlock(notAdded: ImmutableList<WcNetworkInfoItem>, modifier: Modifier = Modifier) {
Column(modifier = modifier) {
Text(
modifier = Modifier.padding(top = 12.dp, bottom = 4.dp, start = 12.dp, end = 12.dp),
text = "Not Added",
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.tertiary,
)
notAdded.fastForEach { network ->
key(network.id) {
NetworkItems(modifier = Modifier.padding(vertical = 14.dp, horizontal = 12.dp), networkItem = network)
}
}
}
}
@Composable
private fun NetworkItems(networkItem: WcNetworkInfoItem, modifier: Modifier = Modifier) {
Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) {
when (networkItem) {
is WcNetworkInfoItem.Checkable,
is WcNetworkInfoItem.Checked,
is WcNetworkInfoItem.Required,
-> Image(
modifier = Modifier.size(24.dp),
painter = painterResource(networkItem.icon),
contentDescription = null,
)
is WcNetworkInfoItem.ReadOnly -> Icon(
modifier = Modifier
.size(24.dp)
.clip(CircleShape)
.background(TangemTheme.colors.button.secondary),
painter = painterResource(networkItem.icon),
contentDescription = null,
tint = TangemTheme.colors.icon.informative,
)
}
NetworkNameAndSymbol(
modifier = Modifier
.weight(1f)
.padding(horizontal = 12.dp),
name = networkItem.name,
symbol = networkItem.symbol,
)
when (networkItem) {
is WcNetworkInfoItem.Checkable -> TangemSwitch(
onCheckedChange = networkItem.onCheckedChange,
checked = networkItem.checked,
)
is WcNetworkInfoItem.Required -> Text(
text = "Required",
style = TangemTheme.typography.body2,
color = TangemTheme.colors.text.tertiary,
)
is WcNetworkInfoItem.Checked -> TangemSwitch(
onCheckedChange = {},
enabled = false,
checked = true,
)
is WcNetworkInfoItem.ReadOnly -> Unit
}
}
}
@Composable
private fun NetworkNameAndSymbol(name: String, symbol: String, modifier: Modifier = Modifier) {
Row(
modifier = modifier,
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
modifier = Modifier.weight(1f, fill = false),
text = name,
style = TangemTheme.typography.subtitle2,
color = TangemTheme.colors.text.primary1,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
Text(text = symbol, style = TangemTheme.typography.caption1, color = TangemTheme.colors.text.tertiary)
}
}
@Preview(showBackground = true, device = Devices.PIXEL_7_PRO)
@Preview(showBackground = true, device = Devices.PIXEL_7_PRO, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun WcSelectNetworksContent_Preview(
@PreviewParameter(WcSelectNetworksProvider::class)
state: WcSelectNetworksUM,
) {
TangemThemePreview {
TangemModalBottomSheet<TangemBottomSheetConfigContent.Empty>(
containerColor = TangemTheme.colors.background.tertiary,
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = {},
content = TangemBottomSheetConfigContent.Empty,
),
title = {
TangemModalBottomSheetTitle(
title = stringReference("Choose wallet"),
onEndClick = {},
endIconRes = R.drawable.ic_close_24,
)
},
content = {
WcSelectNetworksContent(
modifier = Modifier
.background(TangemTheme.colors.background.tertiary)
.padding(horizontal = TangemTheme.dimens.spacing16),
state = state,
)
},
)
}
}
private class WcSelectNetworksProvider : CollectionPreviewParameterProvider<WcSelectNetworksUM>(
collection = listOf(
WcSelectNetworksUM(
missing = persistentListOf(
WcNetworkInfoItem.Required(
id = "ethereum",
name = "EthereumEthereumEthereumEthereumEthereumEthereumEthereumEthereum",
symbol = "ETH",
icon = R.drawable.img_eth_22,
),
WcNetworkInfoItem.Required(
id = "bitcoin",
name = "Bitcoin",
symbol = "BTC",
icon = R.drawable.img_btc_22,
),
),
required = persistentListOf(
WcNetworkInfoItem.Checked(
id = "optimism",
name = "Optimism",
symbol = "OP",
icon = R.drawable.img_optimism_22,
),
),
available = persistentListOf(
WcNetworkInfoItem.Checkable(
id = "cardano",
name = "Cardano",
symbol = "ADA",
icon = R.drawable.img_cardano_22,
checked = false,
onCheckedChange = {},
),
WcNetworkInfoItem.Checkable(
id = "polygon",
name = "Polygon",
symbol = "MATIC",
icon = R.drawable.img_polygon_22,
checked = true,
onCheckedChange = {},
),
),
notAdded = persistentListOf(
WcNetworkInfoItem.ReadOnly(
id = "solana",
name = "SOLANA",
symbol = "SOL",
icon = R.drawable.ic_solana_16,
),
WcNetworkInfoItem.ReadOnly(
id = "avalanche",
name = "Avalanche",
symbol = "AVAX",
icon = R.drawable.ic_avalanche_22,
),
),
onDone = {},
),
WcSelectNetworksUM(
missing = persistentListOf(),
required = persistentListOf(
WcNetworkInfoItem.Checked(
id = "optimism",
name = "Optimism",
symbol = "OP",
icon = R.drawable.img_optimism_22,
),
),
available = persistentListOf(
WcNetworkInfoItem.Checkable(
id = "cardano",
name = "Cardano",
symbol = "ADA",
icon = R.drawable.img_cardano_22,
checked = false,
onCheckedChange = {},
),
WcNetworkInfoItem.Checkable(
id = "polygon",
name = "Polygon",
symbol = "MATIC",
icon = R.drawable.img_polygon_22,
checked = true,
onCheckedChange = {},
),
),
notAdded = persistentListOf(
WcNetworkInfoItem.ReadOnly(
id = "solana",
name = "SOLANA",
symbol = "SOL",
icon = R.drawable.ic_solana_16,
),
WcNetworkInfoItem.ReadOnly(
id = "avalanche",
name = "Avalanche",
symbol = "AVAX",
icon = R.drawable.ic_avalanche_22,
),
),
onDone = {},
),
),
)

View file

@ -1,7 +1,6 @@
package com.tangem.features.walletconnect.connections.components
import android.content.res.Configuration
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
@ -22,12 +21,17 @@ import com.tangem.common.ui.userwallet.UserWalletItem
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.components.block.TangemBlockCardColors
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheet
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.walletconnect.connections.model.WcAppInfoModel
import com.tangem.features.walletconnect.impl.R
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@ -86,45 +90,60 @@ private fun WcSelectWalletContent(
@Preview(showBackground = true, device = Devices.PIXEL_7_PRO, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun WcSelectWalletContent_Preview() {
TangemThemePreview {
WcSelectWalletContent(
modifier = Modifier
.background(TangemTheme.colors.background.tertiary)
.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
selectedWalletId = UserWalletId("user_wallet_1".encodeToByteArray()),
wallets = persistentListOf(
UserWalletItemUM(
id = UserWalletId("user_wallet_1".encodeToByteArray()),
name = stringReference("Tangem 2.0"),
information = stringReference("42 tokens"),
balance = UserWalletItemUM.Balance.Loaded("1 496,34 \$", isFlickering = false),
isEnabled = true,
onClick = {},
),
UserWalletItemUM(
id = UserWalletId("user_wallet_2".encodeToByteArray()),
name = stringReference("Tangem White"),
information = stringReference("24 tokens"),
balance = UserWalletItemUM.Balance.Loaded("1 496,34 \$", isFlickering = false),
isEnabled = true,
onClick = {},
),
UserWalletItemUM(
id = UserWalletId("user_wallet_3".encodeToByteArray()),
name = stringReference("Bitcoin"),
information = stringReference("1 token"),
balance = UserWalletItemUM.Balance.Loaded("1 496,34 \$", isFlickering = false),
isEnabled = true,
onClick = {},
),
UserWalletItemUM(
id = UserWalletId("user_wallet_4".encodeToByteArray()),
name = stringReference("Tangem 1.0"),
information = stringReference("21 tokens"),
balance = UserWalletItemUM.Balance.Loaded("1 496,34 \$", isFlickering = false),
isEnabled = true,
onClick = {},
),
TangemModalBottomSheet<TangemBottomSheetConfigContent.Empty>(
containerColor = TangemTheme.colors.background.primary,
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = {},
content = TangemBottomSheetConfigContent.Empty,
),
title = {
TangemModalBottomSheetTitle(
title = stringReference("Choose wallet"),
onEndClick = {},
endIconRes = R.drawable.ic_close_24,
)
},
content = {
WcSelectWalletContent(
modifier = Modifier.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
selectedWalletId = UserWalletId("user_wallet_1".encodeToByteArray()),
wallets = persistentListOf(
UserWalletItemUM(
id = UserWalletId("user_wallet_1".encodeToByteArray()),
name = stringReference("Tangem 2.0"),
information = stringReference("42 tokens"),
balance = UserWalletItemUM.Balance.Loaded("1 496,34 \$", isFlickering = false),
isEnabled = true,
onClick = {},
),
UserWalletItemUM(
id = UserWalletId("user_wallet_2".encodeToByteArray()),
name = stringReference("Tangem White"),
information = stringReference("24 tokens"),
balance = UserWalletItemUM.Balance.Loaded("1 496,34 \$", isFlickering = false),
isEnabled = true,
onClick = {},
),
UserWalletItemUM(
id = UserWalletId("user_wallet_3".encodeToByteArray()),
name = stringReference("Bitcoin"),
information = stringReference("1 token"),
balance = UserWalletItemUM.Balance.Loaded("1 496,34 \$", isFlickering = false),
isEnabled = true,
onClick = {},
),
UserWalletItemUM(
id = UserWalletId("user_wallet_4".encodeToByteArray()),
name = stringReference("Tangem 1.0"),
information = stringReference("21 tokens"),
balance = UserWalletItemUM.Balance.Loaded("1 496,34 \$", isFlickering = false),
isEnabled = true,
onClick = {},
),
),
)
},
)
}
}

View file

@ -28,6 +28,7 @@ internal sealed class WcAppInfoUM : TangemBottomSheetConfigContent {
val walletName: String,
val onWalletClick: () -> Unit,
val networksInfo: WcNetworksInfo,
val onNetworksClick: () -> Unit,
override val connectButtonConfig: WcPrimaryButtonConfig,
override val onDismiss: () -> Unit,
) : WcAppInfoUM()

View file

@ -9,7 +9,7 @@ data class WcConnectedAppInfoUM(
val isVerified: Boolean,
val appSubtitle: String,
val walletName: String,
val networks: ImmutableList<WcNetworkInfoItem>,
val networks: ImmutableList<WcNetworkInfoItem.Required>,
val disconnectButtonConfig: WcPrimaryButtonConfig,
val onDismiss: () -> Unit,
) : TangemBottomSheetConfigContent

View file

@ -2,9 +2,41 @@ package com.tangem.features.walletconnect.connections.entity
import androidx.annotation.DrawableRes
data class WcNetworkInfoItem(
val id: String,
@DrawableRes val icon: Int,
val name: String,
val symbol: String,
)
sealed class WcNetworkInfoItem {
abstract val id: String
@get:DrawableRes
abstract val icon: Int
abstract val name: String
abstract val symbol: String
data class Required(
override val id: String,
override val icon: Int,
override val name: String,
override val symbol: String,
) : WcNetworkInfoItem()
data class Checked(
override val id: String,
override val icon: Int,
override val name: String,
override val symbol: String,
) : WcNetworkInfoItem()
data class Checkable(
override val id: String,
override val icon: Int,
override val name: String,
override val symbol: String,
val checked: Boolean,
val onCheckedChange: (Boolean) -> Unit,
) : WcNetworkInfoItem()
data class ReadOnly(
override val id: String,
override val icon: Int,
override val name: String,
override val symbol: String,
) : WcNetworkInfoItem()
}

View file

@ -0,0 +1,11 @@
package com.tangem.features.walletconnect.connections.entity
import kotlinx.collections.immutable.ImmutableList
internal data class WcSelectNetworksUM(
val missing: ImmutableList<WcNetworkInfoItem.Required>,
val required: ImmutableList<WcNetworkInfoItem.Checked>,
val available: ImmutableList<WcNetworkInfoItem.Checkable>,
val notAdded: ImmutableList<WcNetworkInfoItem.ReadOnly>,
val onDone: () -> Unit,
)

View file

@ -13,6 +13,7 @@ import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.domain.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.tokens.GetWalletTotalBalanceUseCase
import com.tangem.domain.tokens.model.Network
import com.tangem.domain.walletconnect.model.WcPairRequest
import com.tangem.domain.walletconnect.model.WcSessionApprove
import com.tangem.domain.walletconnect.model.WcSessionProposal
@ -26,15 +27,20 @@ import com.tangem.features.walletconnect.connections.components.WcAppInfoContain
import com.tangem.features.walletconnect.connections.entity.WcAppInfoUM
import com.tangem.features.walletconnect.connections.entity.WcAppInfoWalletUM
import com.tangem.features.walletconnect.connections.entity.WcPrimaryButtonConfig
import com.tangem.features.walletconnect.connections.entity.WcSelectNetworksUM
import com.tangem.features.walletconnect.connections.model.transformers.WcAppInfoTransformer
import com.tangem.features.walletconnect.connections.model.transformers.WcAppInfoWalletChangedTransformer
import com.tangem.features.walletconnect.connections.model.transformers.WcConnectButtonProgressTransformer
import com.tangem.features.walletconnect.connections.model.transformers.WcSelectNetworksClearCheckedTransformer
import com.tangem.features.walletconnect.connections.model.transformers.WcSelectNetworksCheckedTransformer
import com.tangem.features.walletconnect.connections.model.transformers.WcSelectNetworksTransformer
import com.tangem.features.walletconnect.connections.routes.WcAppInfoRoutes
import com.tangem.features.walletconnect.connections.utils.WcUserWalletsFetcher
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.*
import timber.log.Timber
import javax.inject.Inject
import kotlin.properties.Delegates
import com.tangem.utils.transformer.update as transformerUpdate
@ -75,7 +81,6 @@ internal class WcAppInfoModel @Inject constructor(
private val selectedUserWalletFlow =
MutableStateFlow(getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId })
// TODO(wc) Doston: Temp solution, will be fixed in next PR`s
private var proposalNetwork by Delegates.notNull<WcSessionProposal.ProposalNetwork>()
internal var sessionProposal by Delegates.notNull<WcSessionProposal>()
@ -85,6 +90,18 @@ internal class WcAppInfoModel @Inject constructor(
field = MutableStateFlow<WcAppInfoUM>(createLoadingState())
val walletsUiState: StateFlow<WcAppInfoWalletUM>
field = MutableStateFlow(WcAppInfoWalletUM(persistentListOf(), selectedUserWalletFlow.value))
val networksState: StateFlow<WcSelectNetworksUM>
field = MutableStateFlow(
WcSelectNetworksUM(
missing = persistentListOf(),
required = persistentListOf(),
available = persistentListOf(),
notAdded = persistentListOf(),
onDone = ::onNetworksDone,
),
)
// TODO: [REDACTED_JIRA] Change it to UiManager
private val tempAvailableNetworksState = MutableStateFlow<Set<Network.ID>>(setOf())
init {
loadDAppInfo()
@ -109,22 +126,24 @@ internal class WcAppInfoModel @Inject constructor(
router.pop()
}
is WcPairState.Error -> {
// TODO: wc show toast/snackbar/alert?
Timber.e(state.error)
}
is WcPairState.Loading -> appInfoUiState.update { createLoadingState() }
is WcPairState.Proposal -> {
sessionProposal = state.dAppSession
proposalNetwork = sessionProposal.proposalNetwork.getValue(selectedUserWalletFlow.value)
val proposalNetwork = sessionProposal.proposalNetwork.getValue(selectedUserWalletFlow.value)
appInfoUiState.transformerUpdate(
WcAppInfoTransformer(
dAppSession = state.dAppSession,
onDismiss = ::dismiss,
onConnect = ::onConnect,
onWalletClick = { contentNavigation.pushNew(WcAppInfoRoutes.SelectWallet) },
onNetworksClick = { contentNavigation.pushNew(WcAppInfoRoutes.SelectNetworks) },
userWallet = selectedUserWalletFlow.value,
proposalNetwork = proposalNetwork,
),
)
updateNetworksState(proposalNetwork = proposalNetwork)
}
}
}
@ -136,19 +155,27 @@ internal class WcAppInfoModel @Inject constructor(
router.pop()
}
fun clearAvailableNetworks() {
networksState.transformerUpdate(WcSelectNetworksClearCheckedTransformer)
tempAvailableNetworksState.update { setOf() }
}
private fun onConnect() {
val enabledAvailableNetworks =
proposalNetwork.available.filter { network -> network.id in tempAvailableNetworksState.value }
wcPairUseCase.approve(
WcSessionApprove(
wallet = selectedUserWalletFlow.value,
network = proposalNetwork.required.plus(proposalNetwork.available).toList(),
network = enabledAvailableNetworks + proposalNetwork.required,
),
)
}
private fun onWalletSelected(userWalletId: UserWalletId) {
val selectedUserWallet = sessionProposal.proposalNetwork.keys.first { it.walletId == userWalletId }
val proposalNetwork = sessionProposal.proposalNetwork.getValue(selectedUserWallet)
selectedUserWalletFlow.update { selectedUserWallet }
proposalNetwork = sessionProposal.proposalNetwork.getValue(selectedUserWallet)
updateNetworksState(proposalNetwork)
appInfoUiState.transformerUpdate(WcAppInfoWalletChangedTransformer(selectedUserWallet, proposalNetwork))
contentNavigation.pop()
}
@ -157,6 +184,23 @@ internal class WcAppInfoModel @Inject constructor(
walletsUiState.update { state -> state.copy(wallets = items, selectedUserWallet = userWallet) }
}
private fun onNetworksDone() {
contentNavigation.pop()
}
private fun updateNetworksState(proposalNetwork: WcSessionProposal.ProposalNetwork) {
this.proposalNetwork = proposalNetwork
networksState.transformerUpdate(WcSelectNetworksTransformer(proposalNetwork, ::onCheckedChange))
tempAvailableNetworksState.update { setOf() }
}
private fun onCheckedChange(isChecked: Boolean, networkId: String) {
tempAvailableNetworksState.update {
if (isChecked) it.plus(Network.ID(networkId)) else it.minus(Network.ID(networkId))
}
networksState.transformerUpdate(WcSelectNetworksCheckedTransformer(networkId, isChecked))
}
private fun createLoadingState(): WcAppInfoUM.Loading {
return WcAppInfoUM.Loading(
onDismiss = ::dismiss,

View file

@ -60,7 +60,7 @@ internal class WcConnectedAppInfoModel @Inject constructor(
walletName = session.wallet.name,
networks = session.networks
.map {
WcNetworkInfoItem(
WcNetworkInfoItem.Required(
id = it.id.value,
icon = it.iconResId,
name = it.name,

View file

@ -33,7 +33,6 @@ import com.tangem.utils.transformer.update
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import timber.log.Timber
import javax.inject.Inject
@Suppress("LongParameterList")
@ -77,10 +76,8 @@ internal class WcConnectionsModel @Inject constructor(
private fun listenWcSessions() {
wcSessionsUseCase.invoke()
.conflate()
.onEach { Timber.tag("ddk9499").d("listenWcSessions before: ${it.size}") }
.distinctUntilChanged()
.onEach { sessionsMap ->
Timber.tag("ddk9499").d("listenWcSessions after: ${sessionsMap.size}")
uiState.update(
WcSessionsTransformer(
sessionsMap = sessionsMap,

View file

@ -6,11 +6,13 @@ import com.tangem.domain.wallets.models.UserWallet
import com.tangem.features.walletconnect.connections.entity.*
import com.tangem.utils.transformer.Transformer
@Suppress("LongParameterList")
internal class WcAppInfoTransformer(
private val dAppSession: WcSessionProposal,
private val onDismiss: () -> Unit,
private val onConnect: () -> Unit,
private val onWalletClick: () -> Unit,
private val onNetworksClick: () -> Unit,
private val userWallet: UserWallet,
private val proposalNetwork: WcSessionProposal.ProposalNetwork,
) : Transformer<WcAppInfoUM> {
@ -25,6 +27,7 @@ internal class WcAppInfoTransformer(
walletName = userWallet.name,
onWalletClick = onWalletClick,
networksInfo = WcNetworksInfoConverter.convert(proposalNetwork),
onNetworksClick = onNetworksClick,
connectButtonConfig = WcPrimaryButtonConfig(
showProgress = false,
enabled = proposalNetwork.missingRequired.isEmpty(),

View file

@ -18,7 +18,7 @@ internal object WcNetworksInfoConverter : Converter<WcSessionProposal.ProposalNe
WcNetworksInfo.ContainsAllRequiredNetworks(
items = (value.required + value.available)
.map {
WcNetworkInfoItem(
WcNetworkInfoItem.Required(
id = it.id.value,
icon = it.iconResId,
name = it.name,

View file

@ -0,0 +1,18 @@
package com.tangem.features.walletconnect.connections.model.transformers
import com.tangem.features.walletconnect.connections.entity.WcSelectNetworksUM
import com.tangem.utils.transformer.Transformer
import kotlinx.collections.immutable.toImmutableList
internal class WcSelectNetworksCheckedTransformer(
private val networkId: String,
private val isChecked: Boolean,
) : Transformer<WcSelectNetworksUM> {
override fun transform(prevState: WcSelectNetworksUM): WcSelectNetworksUM {
return prevState.copy(
available = prevState.available
.map { item -> if (item.id == networkId) item.copy(checked = isChecked) else item }
.toImmutableList(),
)
}
}

View file

@ -0,0 +1,13 @@
package com.tangem.features.walletconnect.connections.model.transformers
import com.tangem.features.walletconnect.connections.entity.WcSelectNetworksUM
import com.tangem.utils.transformer.Transformer
import kotlinx.collections.immutable.toImmutableList
internal object WcSelectNetworksClearCheckedTransformer : Transformer<WcSelectNetworksUM> {
override fun transform(prevState: WcSelectNetworksUM): WcSelectNetworksUM {
return prevState.copy(
available = prevState.available.map { it.copy(checked = false) }.toImmutableList(),
)
}
}

View file

@ -0,0 +1,53 @@
package com.tangem.features.walletconnect.connections.model.transformers
import com.tangem.core.ui.extensions.getGreyedOutIconRes
import com.tangem.core.ui.extensions.iconResId
import com.tangem.domain.walletconnect.model.WcSessionProposal
import com.tangem.features.walletconnect.connections.entity.WcNetworkInfoItem
import com.tangem.features.walletconnect.connections.entity.WcSelectNetworksUM
import com.tangem.utils.transformer.Transformer
import kotlinx.collections.immutable.toImmutableList
internal class WcSelectNetworksTransformer(
private val proposalNetwork: WcSessionProposal.ProposalNetwork,
private val onCheckedChange: (Boolean, String) -> Unit,
) : Transformer<WcSelectNetworksUM> {
override fun transform(prevState: WcSelectNetworksUM): WcSelectNetworksUM {
return prevState.copy(
missing = proposalNetwork.missingRequired.map { network ->
WcNetworkInfoItem.Required(
id = network.id.value,
icon = network.iconResId,
name = network.name,
symbol = network.currencySymbol,
)
}.toImmutableList(),
required = proposalNetwork.required.map { network ->
WcNetworkInfoItem.Checked(
id = network.id.value,
icon = network.iconResId,
name = network.name,
symbol = network.currencySymbol,
)
}.toImmutableList(),
available = proposalNetwork.available.map { network ->
WcNetworkInfoItem.Checkable(
id = network.id.value,
icon = network.iconResId,
name = network.name,
symbol = network.currencySymbol,
checked = false,
onCheckedChange = { onCheckedChange(it, network.id.value) },
)
}.toImmutableList(),
notAdded = proposalNetwork.notAdded.map { network ->
WcNetworkInfoItem.ReadOnly(
id = network.id.value,
icon = getGreyedOutIconRes(network.id.value),
name = network.name,
symbol = network.currencySymbol,
)
}.toImmutableList(),
)
}
}

View file

@ -1,7 +1,6 @@
package com.tangem.features.walletconnect.connections.ui
import android.content.res.Configuration
import androidx.compose.animation.animateContentSize
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
@ -95,7 +94,7 @@ private fun WcAppInfoModalBottomSheetContent(state: WcAppInfoUM.Content, modifie
@Composable
private fun WcAppInfoFirstBlock(state: WcAppInfoUM.Content, modifier: Modifier = Modifier) {
var connectionRequestExpanded by remember { mutableStateOf(false) }
Column(modifier = modifier.animateContentSize()) {
Column(modifier = modifier) {
WcAppInfoItem(
iconUrl = state.appIcon,
title = state.appName,
@ -228,8 +227,10 @@ private fun WcAppInfoSecondBlock(state: WcAppInfoUM.Content, modifier: Modifier
)
HorizontalDivider(thickness = 1.dp, color = TangemTheme.colors.stroke.primary)
SelectNetworksBlock(
modifier = Modifier
.clickable(onClick = state.onNetworksClick)
.then(itemsModifier),
networksInfo = state.networksInfo,
modifier = itemsModifier,
)
when (state.networksInfo) {
is WcNetworksInfo.ContainsAllRequiredNetworks -> Unit
@ -562,7 +563,7 @@ private fun WcAppInfoBottomSheetPreview(@PreviewParameter(WcAppInfoStateProvider
private class WcAppInfoStateProvider : CollectionPreviewParameterProvider<WcAppInfoUM>(
collection = listOf(
// WcAppInfoUM.Loading(onDismiss = {}, WcPrimaryButtonConfig(showProgress = false, enabled = false, onClick = {})),
WcAppInfoUM.Loading(onDismiss = {}, WcPrimaryButtonConfig(showProgress = false, enabled = false, onClick = {})),
WcAppInfoUM.Content(
appName = "React App",
appIcon = "",
@ -573,26 +574,31 @@ private class WcAppInfoStateProvider : CollectionPreviewParameterProvider<WcAppI
onWalletClick = {},
networksInfo = WcNetworksInfo.ContainsAllRequiredNetworks(
items = persistentListOf(
WcNetworkInfoItem(
WcNetworkInfoItem.Required(
id = "1",
icon = R.drawable.img_optimism_22,
name = "img_optimism_22",
symbol = "optimism",
),
WcNetworkInfoItem(id = "2", icon = R.drawable.img_bsc_22, name = "img_bsc_22", symbol = "bsc"),
WcNetworkInfoItem(
WcNetworkInfoItem.Required(
id = "2",
icon = R.drawable.img_bsc_22,
name = "img_bsc_22",
symbol = "bsc",
),
WcNetworkInfoItem.Required(
id = "3",
icon = R.drawable.img_avalanche_22,
name = "img_avalanche_22",
symbol = "avalanche",
),
WcNetworkInfoItem(
WcNetworkInfoItem.Required(
id = "4",
icon = R.drawable.img_solana_22,
name = "img_solana_22",
symbol = "solana",
),
WcNetworkInfoItem(
WcNetworkInfoItem.Required(
id = "5",
icon = R.drawable.img_avalanche_22,
name = "img_avalanche_22",
@ -600,6 +606,7 @@ private class WcAppInfoStateProvider : CollectionPreviewParameterProvider<WcAppI
),
),
),
onNetworksClick = {},
onDismiss = {},
connectButtonConfig = WcPrimaryButtonConfig(showProgress = false, enabled = true, onClick = {}),
),
@ -612,6 +619,7 @@ private class WcAppInfoStateProvider : CollectionPreviewParameterProvider<WcAppI
walletName = "Tangem 2.0",
onWalletClick = {},
networksInfo = WcNetworksInfo.MissingRequiredNetworkInfo(networks = "Solana"),
onNetworksClick = {},
onDismiss = {},
connectButtonConfig = WcPrimaryButtonConfig(showProgress = false, enabled = true, onClick = {}),
),

View file

@ -134,7 +134,7 @@ private fun AppInfoFirstBlock(state: WcConnectedAppInfoUM, modifier: Modifier =
}
@Composable
private fun NetworksBlock(networks: ImmutableList<WcNetworkInfoItem>, modifier: Modifier = Modifier) {
private fun NetworksBlock(networks: ImmutableList<WcNetworkInfoItem.Required>, modifier: Modifier = Modifier) {
Column(modifier = modifier) {
Text(
modifier = Modifier
@ -163,14 +163,19 @@ private fun WcConnectedAppInfoBS_Preview() {
appSubtitle = "react-app.walletconnect.com",
walletName = "Tangem 2.0",
networks = persistentListOf(
WcNetworkInfoItem(
WcNetworkInfoItem.Required(
id = "1",
icon = R.drawable.img_optimism_22,
name = "img_optimism_22img_optimism_22",
symbol = "optimism",
),
WcNetworkInfoItem(id = "2", icon = R.drawable.img_bsc_22, name = "img_bsc_22", symbol = "bsc"),
WcNetworkInfoItem(
WcNetworkInfoItem.Required(
id = "2",
icon = R.drawable.img_bsc_22,
name = "img_bsc_22",
symbol = "bsc",
),
WcNetworkInfoItem.Required(
id = "3",
icon = R.drawable.img_solana_22,
name = "img_solana_22",