Updated on 2026-08-14

This commit is contained in:
Tangem 2026-03-04 18:31:47 +03:00
parent d06051845b
commit 5c74278e3d
30 changed files with 83 additions and 666 deletions

View file

@ -66,7 +66,6 @@ internal class WcPairComponent(
else -> model.stackNavigation.pop()
}
is WcAppInfoRoutes.SelectNetworks,
is WcAppInfoRoutes.SelectWallet,
is WcAppInfoRoutes.PortfolioSelector,
-> model.stackNavigation.pop()
}
@ -102,14 +101,6 @@ internal class WcPairComponent(
callback = model,
),
)
is WcAppInfoRoutes.SelectWallet -> WcSelectWalletComponent(
appComponentContext = appComponentContext,
params = WcSelectWalletComponent.WcSelectWalletParams(
selectedWalletId = config.selectedWalletId,
onDismiss = ::dismiss,
callback = model,
),
)
WcAppInfoRoutes.PortfolioSelector -> portfolioSelectorComponentFactory.create(
context = appComponentContext,
params = PortfolioSelectorComponent.Params(

View file

@ -1,216 +0,0 @@
package com.tangem.features.walletconnect.connections.components
import android.content.res.Configuration
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Devices
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.util.fastForEach
import androidx.lifecycle.compose.collectAsStateWithLifecycle
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.decompose.model.getOrCreateModel
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.ComposableBottomSheetComponent
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.res.TangemTheme
import com.tangem.core.ui.res.TangemThemePreview
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.walletconnect.connections.model.WcSelectWalletModel
import com.tangem.features.walletconnect.impl.R
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
internal class WcSelectWalletComponent(
appComponentContext: AppComponentContext,
private val params: WcSelectWalletParams,
) : AppComponentContext by appComponentContext, ComposableBottomSheetComponent {
private val model: WcSelectWalletModel = getOrCreateModel(params = params)
override fun dismiss() {
params.onDismiss()
}
@Composable
override fun BottomSheet() {
val state by model.state.collectAsStateWithLifecycle()
WcSelectWalletModalBS(
wallets = state.wallets,
selectedWalletId = state.selectedUserWalletId,
onBack = router::pop,
onDismiss = ::dismiss,
)
}
interface ModelCallback {
fun onWalletSelected(userWalletId: UserWalletId)
}
data class WcSelectWalletParams(
val selectedWalletId: UserWalletId,
val callback: ModelCallback,
val onDismiss: () -> Unit,
)
}
@Composable
private fun WcSelectWalletModalBS(
wallets: ImmutableList<UserWalletItemUM>,
selectedWalletId: UserWalletId,
onBack: () -> Unit,
onDismiss: () -> Unit,
modifier: Modifier = Modifier,
) {
if (wallets.isEmpty()) return
TangemModalBottomSheet<TangemBottomSheetConfigContent.Empty>(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = onDismiss,
content = TangemBottomSheetConfigContent.Empty,
),
onBack = onBack,
containerColor = TangemTheme.colors.background.primary,
title = {
TangemModalBottomSheetTitle(
title = resourceReference(R.string.common_choose_wallet),
startIconRes = R.drawable.ic_back_24,
onStartClick = onBack,
)
},
content = {
WcSelectWalletContent(
modifier = modifier.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
wallets = wallets,
selectedWalletId = selectedWalletId,
)
},
)
}
@Composable
private fun WcSelectWalletContent(
wallets: ImmutableList<UserWalletItemUM>,
selectedWalletId: UserWalletId,
modifier: Modifier = Modifier,
) {
Column(modifier = modifier) {
wallets.fastForEach { state ->
key(state.id) {
val baseModifier = Modifier
.clip(RoundedCornerShape(14.dp))
.clickable(onClick = state.onClick)
val itemModifier = if (state.id == selectedWalletId.stringValue) {
baseModifier.border(
width = 1.dp,
color = TangemTheme.colors.text.accent,
shape = RoundedCornerShape(14.dp),
)
} else {
baseModifier
}
UserWalletItem(
modifier = itemModifier,
state = state,
blockColors = TangemBlockCardColors.copy(
containerColor = Color.Unspecified,
disabledContainerColor = Color.Unspecified,
),
)
}
}
}
}
@Suppress("LongMethod")
@Composable
@Preview(showBackground = true, device = Devices.PIXEL_7_PRO)
@Preview(showBackground = true, device = Devices.PIXEL_7_PRO, uiMode = Configuration.UI_MODE_NIGHT_YES)
private fun WcSelectWalletContent_Preview() {
val wallets = persistentListOf(
UserWalletItemUM(
id = "user_wallet_1",
name = stringReference("Tangem 2.0"),
information = getInformation(42),
balance = UserWalletItemUM.Balance.Loaded("1 496,34 $", isFlickering = false),
isEnabled = true,
onClick = {},
),
UserWalletItemUM(
id = "user_wallet_2",
name = stringReference("Tangem White"),
information = getInformation(24),
balance = UserWalletItemUM.Balance.Loaded("1 496,34 $", isFlickering = false),
isEnabled = true,
onClick = {},
),
UserWalletItemUM(
id = "user_wallet_3",
name = stringReference("Bitcoin"),
information = getInformation(1),
balance = UserWalletItemUM.Balance.Loaded("1 496,34 $", isFlickering = false),
isEnabled = true,
onClick = {},
),
UserWalletItemUM(
id = "user_wallet_4",
name = stringReference("Tangem 1.0"),
information = getInformation(21),
balance = UserWalletItemUM.Balance.Loaded("1 496,34 $", isFlickering = false),
isEnabled = true,
onClick = {},
),
UserWalletItemUM(
id = "user_wallet_4",
name = stringReference("Tangem 1.0"),
information = UserWalletItemUM.Information.Loading,
balance = UserWalletItemUM.Balance.Loaded("1 496,34 $", isFlickering = false),
isEnabled = true,
onClick = {},
),
UserWalletItemUM(
id = "user_wallet_4",
name = stringReference("Tangem 1.0"),
information = UserWalletItemUM.Information.Failed,
balance = UserWalletItemUM.Balance.Loaded("1 496,34 $", isFlickering = false),
isEnabled = true,
onClick = {},
),
)
TangemThemePreview {
WcSelectWalletModalBS(
wallets = wallets,
selectedWalletId = UserWalletId(wallets.first().id.encodeToByteArray()),
onBack = {},
onDismiss = {},
)
}
}
private fun getInformation(tokenCount: Int): UserWalletItemUM.Information.Loaded {
val text = TextReference.PluralRes(
id = R.plurals.card_label_token_count,
count = tokenCount,
formatArgs = wrappedList(tokenCount),
)
return UserWalletItemUM.Information.Loaded(text)
}

View file

@ -27,9 +27,7 @@ internal sealed class WcAppInfoUM : TangemBottomSheetConfigContent {
val verifiedDAppState: VerifiedDAppState,
val appSubtitle: String,
val notification: WcAppInfoSecurityNotification?,
val portfolioSelectRow: PortfolioSelectUM?,
val walletName: String,
val onWalletClick: (() -> Unit)?,
val portfolioSelectRow: PortfolioSelectUM,
val networksInfo: WcNetworksInfo,
val onNetworksClick: () -> Unit,
override val connectButtonConfig: WcPrimaryButtonConfig,

View file

@ -21,16 +21,11 @@ import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.core.ui.message.SnackbarMessage
import com.tangem.core.ui.message.ToastMessage
import com.tangem.domain.account.producer.SingleAccountListProducer
import com.tangem.domain.account.supplier.SingleAccountListSupplier
import com.tangem.domain.account.usecase.IsAccountsModeEnabledUseCase
import com.tangem.domain.models.account.Account
import com.tangem.domain.models.account.AccountStatus
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.domain.models.wallet.isLocked
import com.tangem.domain.models.wallet.isMultiCurrency
import com.tangem.domain.walletconnect.WcAnalyticEvents
import com.tangem.domain.walletconnect.model.WcPairError
import com.tangem.domain.walletconnect.model.WcPairError.Unknown
@ -40,16 +35,17 @@ import com.tangem.domain.walletconnect.model.WcSessionProposal
import com.tangem.domain.walletconnect.model.sdkcopy.WcAppMetaData
import com.tangem.domain.walletconnect.usecase.pair.WcPairState
import com.tangem.domain.walletconnect.usecase.pair.WcPairUseCase
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.account.PortfolioFetcher
import com.tangem.features.account.PortfolioSelectorComponent
import com.tangem.features.account.PortfolioSelectorController
import com.tangem.features.walletconnect.connections.components.WcPairComponent
import com.tangem.features.walletconnect.connections.components.WcSelectNetworksComponent
import com.tangem.features.walletconnect.connections.components.WcSelectWalletComponent
import com.tangem.features.walletconnect.connections.entity.WcAppInfoUM
import com.tangem.features.walletconnect.connections.entity.WcPrimaryButtonConfig
import com.tangem.features.walletconnect.connections.model.transformers.*
import com.tangem.features.walletconnect.connections.model.transformers.WcAppInfoTransformer
import com.tangem.features.walletconnect.connections.model.transformers.WcConnectButtonProgressTransformer
import com.tangem.features.walletconnect.connections.model.transformers.WcDAppVerifiedStateConverter
import com.tangem.features.walletconnect.connections.model.transformers.WcNetworksSelectedTransformer
import com.tangem.features.walletconnect.connections.routes.WcAppInfoRoutes
import com.tangem.features.walletconnect.impl.R
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
@ -61,11 +57,8 @@ import kotlin.properties.Delegates
import com.tangem.utils.transformer.update as transformerUpdate
internal interface WcPairComponentCallback :
WcSelectWalletComponent.ModelCallback,
WcSelectNetworksComponent.ModelCallback
private const val WC_WALLETS_SELECTOR_MIN_COUNT = 2
@Stable
@ModelScoped
@Suppress("LongParameterList", "LargeClass")
@ -75,11 +68,9 @@ internal class WcPairModel @Inject constructor(
override val dispatchers: CoroutineDispatcherProvider,
private val analytics: AnalyticsEventHandler,
val selectorController: PortfolioSelectorController,
private val singleAccountListSupplier: SingleAccountListSupplier,
private val isAccountsModeEnabledUseCase: IsAccountsModeEnabledUseCase,
portfolioFetcherFactory: PortfolioFetcher.Factory,
wcPairUseCaseFactory: WcPairUseCase.Factory,
getWalletsUseCase: GetWalletsUseCase,
paramsContainer: ParamsContainer,
) : Model(), WcPairComponentCallback {
@ -102,9 +93,6 @@ internal class WcPairModel @Inject constructor(
override val onBack: () -> Unit = { stackNavigation.pop() }
}
private val selectedUserWalletFlow: MutableStateFlow<UserWallet> by lazy {
MutableStateFlow(getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId })
}
private val selectedPortfolio = MutableSharedFlow<Pair<UserWallet, AccountStatus.CryptoPortfolio>>(
replay = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST,
@ -119,14 +107,15 @@ internal class WcPairModel @Inject constructor(
init {
modelScope.launch {
val params = SingleAccountListProducer.Params(params.userWalletId)
val accountList = singleAccountListSupplier.getSyncOrNull(params)
if (accountList == null) {
val portfolioBalance = portfolioFetcher.data.first().balances
.firstNotNullOfOrNull { (walletId, balance) ->
if (params.userWalletId == walletId) balance else null
}
if (portfolioBalance == null) {
router.pop()
return@launch
}
val firstAccount = accountList.accounts.first()
selectorController.selectAccount(firstAccount.accountId)
selectorController.selectAccount(portfolioBalance.accountsBalance.mainAccount.accountId)
combineFlows(portfolioFetcher)
}
}
@ -135,10 +124,12 @@ internal class WcPairModel @Inject constructor(
combine(
flow = portfolioFetcher.data,
flow2 = selectorController.selectedAccountWithData(portfolioFetcher)
.distinctUntilChanged()
.filterNotNull()
.onEach { selectedPortfolio.tryEmit(it) }
.onEach { stackNavigation.pop() },
.runningReduce { _, new ->
stackNavigation.pop()
new
},
flow3 = wcPairUseCase(),
flow4 = isAccountsModeEnabledUseCase(),
transform = { portfolios, selected, pairState, isAccountMode ->
@ -155,9 +146,9 @@ internal class WcPairModel @Inject constructor(
private suspend fun handlePairState(
pairState: WcPairState,
portfolios: PortfolioFetcher.Data? = null,
selected: Pair<UserWallet, AccountStatus.CryptoPortfolio>? = null,
isAccountMode: Boolean? = null,
portfolios: PortfolioFetcher.Data,
selected: Pair<UserWallet, AccountStatus.CryptoPortfolio>,
isAccountMode: Boolean,
) {
when (pairState) {
is WcPairState.Approving.Loading -> appInfoUiState.transformerUpdate(
@ -184,36 +175,32 @@ internal class WcPairModel @Inject constructor(
pairState = pairState,
portfolios = portfolios,
selected = selected,
isAccountMode = isAccountMode,
)
}
}
private suspend fun handleProposalState(
pairState: WcPairState.Proposal,
portfolios: PortfolioFetcher.Data? = null,
selected: Pair<UserWallet, AccountStatus.CryptoPortfolio>? = null,
portfolios: PortfolioFetcher.Data,
selected: Pair<UserWallet, AccountStatus.CryptoPortfolio>,
isAccountMode: Boolean,
) {
val availableWallets = pairState.dAppSession.proposalNetwork.keys
.filter { !it.isLocked && it.isMultiCurrency }
sessionProposal = pairState.dAppSession
val selectedUserWalletFlow = this.selectedUserWalletFlow
val portfolioWallet = selected?.first
val portfolioAccount = selected?.second
val portfolioAccountId = portfolioAccount?.account?.accountId
val portfolioAccount = selected.second
val portfolioAccountId = portfolioAccount.account.accountId
val proposalAccountNetwork = sessionProposal.proposalAccountNetwork
val foundNetwork = if (portfolioAccountId != null) {
requireNotNull(proposalAccountNetwork)[portfolioAccountId]
} else {
sessionProposal.proposalNetwork[selectedUserWalletFlow.value]
}
val foundNetwork = proposalAccountNetwork[portfolioAccountId]
if (foundNetwork == null) {
processError(Unknown("Selected wallet not found"))
} else {
val portfolioSelectRow = tryToCreatePortfolioSelectRow(selected, portfolios)
if (proposalAccountNetwork != null) {
selectorController.isEnabled.value = { _, account ->
proposalAccountNetwork.contains(account.account.accountId)
}
val portfolioSelectRow = createPortfolioSelectRow(
selectedPortfolio = selected,
portfolios = portfolios,
isAccountMode = isAccountMode,
)
selectorController.isEnabled.value = { _, account ->
proposalAccountNetwork.contains(account.account.accountId)
}
proposalNetwork = foundNetwork
additionallyEnabledNetworks = proposalNetwork.available
@ -224,13 +211,6 @@ internal class WcPairModel @Inject constructor(
onDismiss = ::rejectPairing,
onConnect = ::onConnect,
portfolioSelectRow = portfolioSelectRow,
onWalletClick = {
stackNavigation.pushNew(
WcAppInfoRoutes.SelectWallet(selectedUserWalletFlow.value.walletId),
)
}.takeIf {
portfolioSelectRow == null && availableWallets.size >= WC_WALLETS_SELECTOR_MIN_COUNT
},
onNetworksClick = {
stackNavigation.pushNew(
WcAppInfoRoutes.SelectNetworks(
@ -242,7 +222,6 @@ internal class WcPairModel @Inject constructor(
),
)
},
userWallet = portfolioWallet ?: selectedUserWalletFlow.value,
proposalNetwork = proposalNetwork,
additionallyEnabledNetworks = additionallyEnabledNetworks,
),
@ -250,17 +229,15 @@ internal class WcPairModel @Inject constructor(
}
}
private suspend fun tryToCreatePortfolioSelectRow(
selectedPortfolio: Pair<UserWallet, AccountStatus.CryptoPortfolio>?,
portfolios: PortfolioFetcher.Data?,
): PortfolioSelectUM? {
selectedPortfolio ?: return null
portfolios ?: return null
private suspend fun createPortfolioSelectRow(
selectedPortfolio: Pair<UserWallet, AccountStatus.CryptoPortfolio>,
portfolios: PortfolioFetcher.Data,
isAccountMode: Boolean,
): PortfolioSelectUM {
val (wallet, portfolioAccount) = selectedPortfolio
val account = when (val account = portfolioAccount.account) {
is Account.CryptoPortfolio -> account
}
val isAccountMode = selectorController.isAccountMode.first()
val icon: AccountIconUM.CryptoPortfolio?
val name: TextReference
if (isAccountMode) {
@ -303,15 +280,16 @@ internal class WcPairModel @Inject constructor(
private fun connect() {
val enabledAvailableNetworks =
proposalNetwork.available.filter { network -> network in additionallyEnabledNetworks }
val selectedPortfolio = selectedPortfolio.replayCache.firstOrNull()
val wallet = selectedPortfolio?.first ?: selectedUserWalletFlow.value
val account = selectedPortfolio?.second?.account
val selectedPortfolio = selectedPortfolio.replayCache
.firstOrNull()
?: return
val (wallet, account) = selectedPortfolio
modelScope.launch {
analytics.send(
WcAnalyticEvents.PairButtonConnect(
dAppName = sessionProposal.dAppMetaData.name,
accountDerivation = account?.derivationIndex?.value,
accountDerivation = account.account.derivationIndex.value,
),
)
}
@ -319,7 +297,7 @@ internal class WcPairModel @Inject constructor(
WcSessionApprove(
wallet = wallet,
network = enabledAvailableNetworks + proposalNetwork.required,
account = account,
account = account.account,
),
)
}
@ -375,20 +353,6 @@ internal class WcPairModel @Inject constructor(
alert?.let { stackNavigation.pushNew(it) }
}
override fun onWalletSelected(userWalletId: UserWalletId) {
val selectedUserWallet = sessionProposal.proposalNetwork.keys.first { it.walletId == userWalletId }
proposalNetwork = sessionProposal.proposalNetwork[selectedUserWallet] ?: return
selectedUserWalletFlow.update { selectedUserWallet }
additionallyEnabledNetworks = proposalNetwork.available
appInfoUiState.transformerUpdate(
WcAppInfoWalletChangedTransformer(
selectedUserWallet = selectedUserWallet,
proposalNetwork = proposalNetwork,
additionallyEnabledNetworks = additionallyEnabledNetworks,
),
)
}
override fun onNetworksSelected(selectedNetworks: Set<Network>) {
additionallyEnabledNetworks = selectedNetworks
appInfoUiState.transformerUpdate(

View file

@ -1,60 +0,0 @@
package com.tangem.features.walletconnect.connections.model
import androidx.compose.runtime.Stable
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.model.Model
import com.tangem.core.decompose.model.ParamsContainer
import com.tangem.core.decompose.navigation.Router
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.wallet.utils.UserWalletsFetcher
import com.tangem.features.walletconnect.connections.components.WcSelectWalletComponent.WcSelectWalletParams
import com.tangem.features.walletconnect.connections.entity.WcAppInfoWalletUM
import com.tangem.features.walletconnect.connections.utils.WcUserWalletsFetcher
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.*
import javax.inject.Inject
@Suppress("LongParameterList")
@Stable
@ModelScoped
internal class WcSelectWalletModel @Inject constructor(
paramsContainer: ParamsContainer,
messageSender: UiMessageSender,
userWalletsFetcherFactory: UserWalletsFetcher.Factory,
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val router: Router,
override val dispatchers: CoroutineDispatcherProvider,
) : Model() {
private val params = paramsContainer.require<WcSelectWalletParams>()
internal val state: StateFlow<WcAppInfoWalletUM>
field = MutableStateFlow<WcAppInfoWalletUM>(
WcAppInfoWalletUM(
wallets = persistentListOf(),
selectedUserWalletId = params.selectedWalletId,
),
)
private val userWalletsFetcher = WcUserWalletsFetcher(
userWalletsFetcherFactory = userWalletsFetcherFactory,
singleAccountStatusListSupplier = singleAccountStatusListSupplier,
messageSender = messageSender,
onWalletSelected = ::onWalletSelected,
)
init {
userWalletsFetcher
.userWallets
.onEach { state.update { state -> state.copy(wallets = it) } }
.launchIn(modelScope)
}
private fun onWalletSelected(userWalletId: UserWalletId) {
params.callback.onWalletSelected(userWalletId)
router.pop()
}
}

View file

@ -3,7 +3,6 @@ package com.tangem.features.walletconnect.connections.model.transformers
import com.domain.blockaid.models.dapp.CheckDAppResult
import com.tangem.common.ui.account.PortfolioSelectUM
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.walletconnect.model.WcSessionProposal
import com.tangem.features.walletconnect.connections.entity.WcAppInfoSecurityNotification
import com.tangem.features.walletconnect.connections.entity.WcAppInfoUM
@ -16,10 +15,8 @@ internal class WcAppInfoTransformer(
private val dAppVerifiedStateConverter: WcDAppVerifiedStateConverter,
private val onDismiss: () -> Unit,
private val onConnect: (securityStatus: CheckDAppResult) -> Unit,
private val portfolioSelectRow: PortfolioSelectUM?,
private val onWalletClick: (() -> Unit)?,
private val portfolioSelectRow: PortfolioSelectUM,
private val onNetworksClick: () -> Unit,
private val userWallet: UserWallet,
private val proposalNetwork: WcSessionProposal.ProposalNetwork,
private val additionallyEnabledNetworks: Set<Network>,
) : Transformer<WcAppInfoUM> {
@ -33,8 +30,6 @@ internal class WcAppInfoTransformer(
),
appSubtitle = WcAppSubtitleConverter.convert(dAppSession.dAppMetaData),
notification = createNotification(dAppSession.securityStatus),
walletName = userWallet.name,
onWalletClick = onWalletClick,
portfolioSelectRow = portfolioSelectRow,
networksInfo = WcNetworksInfoConverter.convert(
value = WcNetworksInfoConverter.Input(

View file

@ -1,39 +0,0 @@
package com.tangem.features.walletconnect.connections.model.transformers
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWallet
import com.tangem.domain.walletconnect.model.WcSessionProposal
import com.tangem.features.walletconnect.connections.entity.WcAppInfoUM
import com.tangem.utils.transformer.Transformer
internal class WcAppInfoWalletChangedTransformer(
private val selectedUserWallet: UserWallet,
private val proposalNetwork: WcSessionProposal.ProposalNetwork,
private val additionallyEnabledNetworks: Set<Network>,
) : Transformer<WcAppInfoUM> {
override fun transform(prevState: WcAppInfoUM): WcAppInfoUM {
val contentState = prevState as? WcAppInfoUM.Content ?: return prevState
return contentState.copy(
walletName = selectedUserWallet.name,
networksInfo = WcNetworksInfoConverter.convert(
WcNetworksInfoConverter.Input(
missingNetworks = proposalNetwork.missingRequired,
requiredNetworks = proposalNetwork.required,
availableNetworks = proposalNetwork.available,
notAddedNetworks = proposalNetwork.notAdded,
additionallyEnabledNetworks = additionallyEnabledNetworks,
),
),
connectButtonConfig = prevState.connectButtonConfig.copy(
enabled = WcConnectButtonAvailabilityConverter.convert(
WcConnectButtonAvailabilityConverter.Input(
missingNetworks = proposalNetwork.missingRequired,
requiredNetworks = proposalNetwork.required,
availableNetworks = proposalNetwork.available,
selectedNetworks = additionallyEnabledNetworks,
),
),
),
)
}
}

View file

@ -49,7 +49,7 @@ internal class WcSessionsAccountModeTransformer(
items.add(walletHeader)
accountList.accounts.filterIsInstance<Account.CryptoPortfolio>().forEach accountsForEach@{ account ->
val accountSessions = sessions.filter { it.account?.accountId == account.accountId }
val accountSessions = sessions.filter { it.account.accountId == account.accountId }
if (accountSessions.isEmpty()) return@accountsForEach
val connectedApps = accountSessions.map { dappSession ->
with(dappSession.sdkModel) {

View file

@ -3,7 +3,6 @@ package com.tangem.features.walletconnect.connections.routes
import androidx.compose.runtime.Immutable
import com.tangem.core.decompose.navigation.Route
import com.tangem.domain.models.network.Network
import com.tangem.domain.models.wallet.UserWalletId
import kotlinx.serialization.Serializable
@Serializable
@ -15,9 +14,6 @@ internal sealed class WcAppInfoRoutes : Route {
@Serializable
data object PortfolioSelector : WcAppInfoRoutes()
@Serializable
data class SelectWallet(val selectedWalletId: UserWalletId) : WcAppInfoRoutes()
@Serializable
data class SelectNetworks(
val missingRequiredNetworks: Set<Network>,

View file

@ -22,8 +22,6 @@ import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
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
@ -273,19 +271,7 @@ private fun WcAppInfoSecondBlock(state: WcAppInfoUM.Content, modifier: Modifier
val itemsModifier = Modifier
.fillMaxWidth()
.padding(TangemTheme.dimens.spacing12)
if (state.portfolioSelectRow != null) {
PortfolioRowItem(portfolioSelectRow = state.portfolioSelectRow)
} else {
WalletRowItem(
modifier = if (state.onWalletClick != null) {
Modifier.clickableSingle(onClick = state.onWalletClick)
} else {
Modifier
}.then(itemsModifier),
walletName = state.walletName,
showEndIcon = state.onWalletClick != null,
)
}
PortfolioRowItem(portfolioSelectRow = state.portfolioSelectRow)
HorizontalDivider(thickness = 1.dp, color = TangemTheme.colors.stroke.primary)
SelectNetworksBlock(
modifier = Modifier
@ -341,56 +327,6 @@ private fun PortfolioRowItem(portfolioSelectRow: PortfolioSelectUM, modifier: Mo
}
}
@Composable
private fun WalletRowItem(walletName: String, showEndIcon: Boolean, modifier: Modifier = Modifier) {
Row(modifier = modifier, verticalAlignment = Alignment.CenterVertically) {
Icon(
modifier = Modifier
.size(24.dp)
.testTag(WalletConnectBottomSheetTestTags.WALLET_ICON),
painter = painterResource(R.drawable.ic_wallet_new_24),
contentDescription = null,
tint = TangemTheme.colors.icon.accent,
)
Row(
modifier = Modifier.weight(1f),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
modifier = Modifier
.padding(start = TangemTheme.dimens.spacing4)
.testTag(WalletConnectBottomSheetTestTags.WALLET_NAME_TITLE),
text = stringResourceSafe(R.string.manage_tokens_network_selector_wallet),
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.primary1,
maxLines = 1,
)
Text(
modifier = Modifier
.padding(start = TangemTheme.dimens.spacing16)
.testTag(WalletConnectBottomSheetTestTags.WALLET_NAME),
text = walletName,
textAlign = TextAlign.End,
style = TangemTheme.typography.body1,
color = TangemTheme.colors.text.tertiary,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
}
if (showEndIcon) {
Icon(
modifier = Modifier
.padding(start = TangemTheme.dimens.spacing12)
.size(width = 18.dp, height = 24.dp),
painter = painterResource(R.drawable.ic_select_18_24),
contentDescription = null,
tint = TangemTheme.colors.icon.informative,
)
}
}
}
@Composable
private fun SelectNetworksBlock(networksInfo: WcNetworksInfo, modifier: Modifier = Modifier) {
Row(
@ -673,9 +609,7 @@ private class WcAppInfoStateProvider : CollectionPreviewParameterProvider<WcAppI
verifiedDAppState = VerifiedDAppState.Verified {},
appSubtitle = "react-app.walletconnect.com",
notification = WcAppInfoSecurityNotification.SecurityRisk,
walletName = "Tangem 2.0 Tangem 2.0 Tangem 2.0 Tangem 2.0",
onWalletClick = null,
portfolioSelectRow = null,
portfolioSelectRow = PortfolioSelectRowPreviewData.account,
networksInfo = WcNetworksInfo.ContainsAllRequiredNetworks(
items = persistentListOf(
WcNetworkInfoItem.Required(
@ -720,9 +654,7 @@ private class WcAppInfoStateProvider : CollectionPreviewParameterProvider<WcAppI
verifiedDAppState = VerifiedDAppState.Unknown,
appSubtitle = "react-app.walletconnect.com",
notification = WcAppInfoSecurityNotification.UnknownDomain,
portfolioSelectRow = null,
walletName = "Tangem 2.0 Tangem 2.0 Tangem 2.0 Tangem 2.0",
onWalletClick = {},
portfolioSelectRow = PortfolioSelectRowPreviewData.wallet,
networksInfo = WcNetworksInfo.MissingRequiredNetworkInfo(networks = "Solana"),
onNetworksClick = {},
onDismiss = {},
@ -735,8 +667,6 @@ private class WcAppInfoStateProvider : CollectionPreviewParameterProvider<WcAppI
appSubtitle = "react-app.walletconnect.com",
notification = WcAppInfoSecurityNotification.UnknownDomain,
portfolioSelectRow = PortfolioSelectRowPreviewData.account,
walletName = "Tangem 2.0 Tangem 2.0 Tangem 2.0 Tangem 2.0",
onWalletClick = {},
networksInfo = WcNetworksInfo.MissingRequiredNetworkInfo(networks = "Solana"),
onNetworksClick = {},
onDismiss = {},

View file

@ -1,64 +0,0 @@
package com.tangem.features.walletconnect.connections.utils
import com.tangem.common.ui.R
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.core.decompose.di.ModelScoped
import com.tangem.core.decompose.ui.UiMessageSender
import com.tangem.core.ui.extensions.TextReference
import com.tangem.core.ui.extensions.wrappedList
import com.tangem.domain.account.status.supplier.SingleAccountStatusListSupplier
import com.tangem.domain.models.TotalFiatBalance
import com.tangem.domain.models.wallet.UserWalletId
import com.tangem.features.wallet.utils.UserWalletsFetcher
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map
@ModelScoped
internal class WcUserWalletsFetcher(
userWalletsFetcherFactory: UserWalletsFetcher.Factory,
messageSender: UiMessageSender,
private val singleAccountStatusListSupplier: SingleAccountStatusListSupplier,
private val onWalletSelected: (UserWalletId) -> Unit,
) {
private val userWalletsFetcher = userWalletsFetcherFactory.create(
messageSender = messageSender,
onlyMultiCurrency = true,
isAuthMode = false,
isClickableIfLocked = false,
onWalletClick = { onWalletSelected(it) },
)
@OptIn(ExperimentalCoroutinesApi::class)
val userWallets: Flow<ImmutableList<UserWalletItemUM>> = userWalletsFetcher.userWallets
.flatMapLatest { listOfWalletItem ->
val flows = listOfWalletItem.map(::getTokenListFlow)
combine(flows) { it.toList().toImmutableList() }
}
private fun getTokenListFlow(walletItem: UserWalletItemUM): Flow<UserWalletItemUM> {
return singleAccountStatusListSupplier(userWalletId = UserWalletId(walletItem.id)).map { accountStatusList ->
val information = when (accountStatusList.totalFiatBalance) {
is TotalFiatBalance.Failed -> UserWalletItemUM.Information.Failed
is TotalFiatBalance.Loading -> UserWalletItemUM.Information.Loading
is TotalFiatBalance.Loaded -> tokenCountInfo(accountStatusList.flattenCurrencies().size)
}
walletItem.copy(information = information)
}
}
private fun tokenCountInfo(count: Int): UserWalletItemUM.Information.Loaded {
val text = TextReference.PluralRes(
id = R.plurals.card_label_token_count,
count = count,
formatArgs = wrappedList(count),
)
return UserWalletItemUM.Information.Loaded(text)
}
}

View file

@ -28,11 +28,6 @@ internal interface WalletConnectModelModule {
@ClassKey(WcPairModel::class)
fun bindWcPairModel(model: WcPairModel): Model
@Binds
@IntoMap
@ClassKey(WcSelectWalletModel::class)
fun bindWcSelectWalletModel(model: WcSelectWalletModel): Model
@Binds
@IntoMap
@ClassKey(WcSelectNetworksModel::class)

View file

@ -137,7 +137,7 @@ internal class WcAddNetworkModel @Inject constructor(
network = useCase.network,
emulationStatus = null,
securityStatus = CheckDAppResult.FAILED_TO_VERIFY,
accountDerivation = useCase.session.account?.derivationIndex?.value,
accountDerivation = useCase.session.account.derivationIndex.value,
),
)

View file

@ -426,7 +426,7 @@ internal class WcSendTransactionModel @Inject constructor(
rawRequest = useCase.rawSdkRequest,
network = useCase.network,
securityStatus = securityStatusState.value.toCheckDAppResult(),
accountDerivation = useCase.session.account?.derivationIndex?.value,
accountDerivation = useCase.session.account.derivationIndex.value,
)
analytics.send(event)
showSuccessSignMessage()
@ -466,7 +466,7 @@ internal class WcSendTransactionModel @Inject constructor(
network = useCase.network,
emulationStatus = emulationStatus,
securityStatus = securityCheck.toCheckDAppResult(),
accountDerivation = useCase.session.account?.derivationIndex?.value,
accountDerivation = useCase.session.account.derivationIndex.value,
),
)

View file

@ -143,7 +143,7 @@ internal class WcSignTransactionModel @Inject constructor(
rawRequest = useCase.rawSdkRequest,
network = useCase.network,
securityStatus = CheckDAppResult.FAILED_TO_VERIFY,
accountDerivation = useCase.session.account?.derivationIndex?.value,
accountDerivation = useCase.session.account.derivationIndex.value,
)
analytics.send(event)
showSuccessSignMessage()
@ -172,7 +172,7 @@ internal class WcSignTransactionModel @Inject constructor(
network = useCase.network,
emulationStatus = null,
securityStatus = CheckDAppResult.FAILED_TO_VERIFY,
accountDerivation = useCase.session.account?.derivationIndex?.value,
accountDerivation = useCase.session.account.derivationIndex.value,
),
)