diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcAppInfoContainerComponent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcAppInfoContainerComponent.kt index be95b71cd3..ef47353704 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcAppInfoContainerComponent.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcAppInfoContainerComponent.kt @@ -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, diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcSelectNetworksComponent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcSelectNetworksComponent.kt new file mode 100644 index 0000000000..8c12698940 --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcSelectNetworksComponent.kt @@ -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, 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, + available: ImmutableList, + 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, 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( + 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( + 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 = {}, + ), + ), +) \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcSelectWalletComponent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcSelectWalletComponent.kt index d6c7e19964..c810210b3e 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcSelectWalletComponent.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcSelectWalletComponent.kt @@ -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( + 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 = {}, + ), + ), + ) + }, ) } } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcAppInfoUM.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcAppInfoUM.kt index 92e385a3fa..6cdcb1729e 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcAppInfoUM.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcAppInfoUM.kt @@ -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() diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcConnectedAppInfoUM.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcConnectedAppInfoUM.kt index 5fe46765ca..a632be44c8 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcConnectedAppInfoUM.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcConnectedAppInfoUM.kt @@ -9,7 +9,7 @@ data class WcConnectedAppInfoUM( val isVerified: Boolean, val appSubtitle: String, val walletName: String, - val networks: ImmutableList, + val networks: ImmutableList, val disconnectButtonConfig: WcPrimaryButtonConfig, val onDismiss: () -> Unit, ) : TangemBottomSheetConfigContent \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcNetworkInfoItem.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcNetworkInfoItem.kt index 498b1b759c..5138a902a0 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcNetworkInfoItem.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcNetworkInfoItem.kt @@ -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, -) \ No newline at end of file +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() +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcSelectNetworksUM.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcSelectNetworksUM.kt new file mode 100644 index 0000000000..a5daa0be5a --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcSelectNetworksUM.kt @@ -0,0 +1,11 @@ +package com.tangem.features.walletconnect.connections.entity + +import kotlinx.collections.immutable.ImmutableList + +internal data class WcSelectNetworksUM( + val missing: ImmutableList, + val required: ImmutableList, + val available: ImmutableList, + val notAdded: ImmutableList, + val onDone: () -> Unit, +) \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcAppInfoModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcAppInfoModel.kt index fe9348ef4d..18581326b1 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcAppInfoModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcAppInfoModel.kt @@ -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() internal var sessionProposal by Delegates.notNull() @@ -85,6 +90,18 @@ internal class WcAppInfoModel @Inject constructor( field = MutableStateFlow(createLoadingState()) val walletsUiState: StateFlow field = MutableStateFlow(WcAppInfoWalletUM(persistentListOf(), selectedUserWalletFlow.value)) + val networksState: StateFlow + field = MutableStateFlow( + WcSelectNetworksUM( + missing = persistentListOf(), + required = persistentListOf(), + available = persistentListOf(), + notAdded = persistentListOf(), + onDone = ::onNetworksDone, + ), + ) + // TODO: [REDACTED_JIRA] Change it to UiManager + private val tempAvailableNetworksState = MutableStateFlow>(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, diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectedAppInfoModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectedAppInfoModel.kt index a751963826..0c49bc8dd9 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectedAppInfoModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectedAppInfoModel.kt @@ -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, diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectionsModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectionsModel.kt index 96d5df7e28..0c7c5da768 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectionsModel.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcConnectionsModel.kt @@ -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, diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcAppInfoTransformer.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcAppInfoTransformer.kt index 2249504df5..e83438a16b 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcAppInfoTransformer.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcAppInfoTransformer.kt @@ -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 { @@ -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(), diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcNetworksInfoConverter.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcNetworksInfoConverter.kt index 8bf63c01cf..3cca2f81ee 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcNetworksInfoConverter.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcNetworksInfoConverter.kt @@ -18,7 +18,7 @@ internal object WcNetworksInfoConverter : Converter { + 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(), + ) + } +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcSelectNetworksClearCheckedTransformer.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcSelectNetworksClearCheckedTransformer.kt new file mode 100644 index 0000000000..01f61e377b --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcSelectNetworksClearCheckedTransformer.kt @@ -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 { + override fun transform(prevState: WcSelectNetworksUM): WcSelectNetworksUM { + return prevState.copy( + available = prevState.available.map { it.copy(checked = false) }.toImmutableList(), + ) + } +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcSelectNetworksTransformer.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcSelectNetworksTransformer.kt new file mode 100644 index 0000000000..bb40167fcc --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcSelectNetworksTransformer.kt @@ -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 { + 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(), + ) + } +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoContent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoContent.kt index 136d5ac26b..e9cbbe442e 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoContent.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoContent.kt @@ -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( 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, modifier: Modifier = Modifier) { +private fun NetworksBlock(networks: ImmutableList, 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",