Updated on 2026-08-14

This commit is contained in:
Tangem 2025-05-22 14:13:05 +05:00
parent 1f629433a3
commit 4b6a093ba9
17 changed files with 633 additions and 419 deletions

View file

@ -48,7 +48,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> TangemModalBottomSheetWi
noinline onBack: (() -> Unit)? = null,
crossinline title: @Composable BoxScope.(T) -> Unit = {},
crossinline content: @Composable (T) -> Unit,
noinline footer: @Composable (BoxScope.(T) -> Unit),
crossinline footer: @Composable (BoxScope.(T) -> Unit),
) {
val isAlwaysVisible = LocalBottomSheetAlwaysVisible.current
@ -83,7 +83,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> DefaultModalBottomSheetW
noinline onBack: (() -> Unit)? = null,
crossinline title: @Composable BoxScope.(T) -> Unit,
crossinline content: @Composable (T) -> Unit,
noinline footer: @Composable (BoxScope.(T) -> Unit),
crossinline footer: @Composable (BoxScope.(T) -> Unit),
) {
var isVisible by remember { mutableStateOf(value = config.isShown) }
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = skipPartiallyExpanded)
@ -117,7 +117,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> PreviewModalBottomSheetW
skipPartiallyExpanded: Boolean = true,
crossinline title: @Composable BoxScope.(T) -> Unit,
crossinline content: @Composable (T) -> Unit,
noinline footer: @Composable BoxScope.(T) -> Unit,
crossinline footer: @Composable BoxScope.(T) -> Unit,
) {
BasicModalBottomSheetWithFooter<T>(
config = config,
@ -144,7 +144,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicModalBottomSheetWit
noinline onBack: (() -> Unit)? = null,
crossinline title: @Composable BoxScope.(T) -> Unit,
crossinline content: @Composable (T) -> Unit,
noinline footer: @Composable (BoxScope.(T) -> Unit),
crossinline footer: @Composable (BoxScope.(T) -> Unit),
modifier: Modifier = Modifier,
) {
val model = config.content as? T ?: return
@ -183,13 +183,10 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicModalBottomSheetWit
)
}
Box(modifier = Modifier.weight(1f, fill = false)) {
val bottomBarHeight = with(LocalDensity.current) {
WindowInsets.systemBars.getBottom(density = this).toDp()
}
Column(
modifier = Modifier
.verticalScroll(state = scrollState)
.padding(bottom = TangemTheme.dimens.spacing76 + bottomBarHeight),
.padding(bottom = TangemTheme.dimens.spacing80),
) {
content(model)
}
@ -202,6 +199,7 @@ inline fun <reified T : TangemBottomSheetConfigContent> BasicModalBottomSheetWit
Box(
modifier = Modifier
.fillMaxWidth()
.height(80.dp)
.align(Alignment.BottomCenter),
) {
footer(model)

View file

@ -130,7 +130,7 @@ sealed class WcAnalyticEvents(
class ButtonCancel(
type: Type,
) : WcAnalyticEvents(
event = "Button - Sign",
event = "Button - Cancel",
params = mapOf(
AnalyticsParam.Key.TYPE to type.type,
),

View file

@ -1,27 +1,27 @@
package com.tangem.features.walletconnect.connections.components
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.res.TangemTheme
import com.tangem.features.walletconnect.connections.model.WcAppInfoModel
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.features.walletconnect.connections.model.WcPairModel
import com.tangem.features.walletconnect.connections.ui.WcAppInfoModalBottomSheet
internal class WcAppInfoComponent(
private val appComponentContext: AppComponentContext,
private val model: WcAppInfoModel,
) : AppComponentContext by appComponentContext, ComposableContentComponent {
private val model: WcPairModel,
private val onDismiss: () -> Unit,
) : AppComponentContext by appComponentContext, ComposableBottomSheetComponent {
override fun dismiss() {
onDismiss()
}
@Composable
override fun Content(modifier: Modifier) {
override fun BottomSheet() {
val state by model.appInfoUiState.collectAsStateWithLifecycle()
WcAppInfoModalBottomSheet(
modifier = modifier.padding(horizontal = TangemTheme.dimens.spacing16),
state = state,
)
WcAppInfoModalBottomSheet(state = state, onBack = router::pop, onDismiss = ::dismiss)
}
}

View file

@ -1,130 +0,0 @@
package com.tangem.features.walletconnect.connections.components
import androidx.compose.animation.animateContentSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.extensions.compose.stack.Children
import com.arkivanov.decompose.extensions.compose.subscribeAsState
import com.arkivanov.decompose.router.stack.childStack
import com.arkivanov.decompose.router.stack.pop
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
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.resourceReference
import com.tangem.core.ui.extensions.stringReference
import com.tangem.core.ui.res.TangemTheme
import com.tangem.domain.walletconnect.model.WcPairRequest
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.walletconnect.connections.model.WcAppInfoModel
import com.tangem.features.walletconnect.connections.routes.WcAppInfoRoutes
import com.tangem.features.walletconnect.impl.R
internal class WcAppInfoContainerComponent(
private val appComponentContext: AppComponentContext,
params: Params,
) : AppComponentContext by appComponentContext, ComposableContentComponent {
private val model: WcAppInfoModel = getOrCreateModel(params = params)
private val contentStack = childStack(
source = model.contentNavigation,
serializer = WcAppInfoRoutes.serializer(),
initialConfiguration = WcAppInfoRoutes.AppInfo,
childFactory = ::screenChild,
)
private fun dismiss() {
model.dismiss()
}
@Composable
override fun Content(modifier: Modifier) {
val content by contentStack.subscribeAsState()
TangemModalBottomSheet<WcAppInfoRoutes>(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = ::dismiss,
content = content.active.configuration,
),
onBack = ::contentBack,
containerColor = when (content.active.configuration) {
is WcAppInfoRoutes.Alert,
is WcAppInfoRoutes.SelectNetworks,
is WcAppInfoRoutes.AppInfo,
-> TangemTheme.colors.background.tertiary
is WcAppInfoRoutes.SelectWallet -> TangemTheme.colors.background.primary
},
title = { config -> Title(route = config) },
content = {
Children(modifier = Modifier.animateContentSize(), stack = content) { child ->
child.instance.Content(modifier = Modifier)
}
},
)
}
@Composable
private fun Title(route: WcAppInfoRoutes) {
TangemModalBottomSheetTitle(
title = when (route) {
is WcAppInfoRoutes.Alert -> null
WcAppInfoRoutes.AppInfo -> resourceReference(R.string.wc_wallet_connect)
WcAppInfoRoutes.SelectNetworks -> stringReference("Choose networks")
WcAppInfoRoutes.SelectWallet -> stringReference("Choose wallet")
},
startIconRes = when (route) {
is WcAppInfoRoutes.AppInfo, is WcAppInfoRoutes.Alert -> null
is WcAppInfoRoutes.SelectNetworks, is WcAppInfoRoutes.SelectWallet -> R.drawable.ic_back_24
},
onStartClick = when (route) {
is WcAppInfoRoutes.AppInfo, is WcAppInfoRoutes.Alert -> null
is WcAppInfoRoutes.SelectNetworks, is WcAppInfoRoutes.SelectWallet -> ::contentBack
},
endIconRes = when (route) {
is WcAppInfoRoutes.AppInfo, is WcAppInfoRoutes.Alert -> R.drawable.ic_close_24
is WcAppInfoRoutes.SelectNetworks, is WcAppInfoRoutes.SelectWallet -> null
},
onEndClick = when (route) {
is WcAppInfoRoutes.AppInfo -> ::dismiss
is WcAppInfoRoutes.Alert -> ::contentBack
is WcAppInfoRoutes.SelectNetworks,
is WcAppInfoRoutes.SelectWallet,
-> null
},
)
}
private fun contentBack() {
when (contentStack.value.active.configuration) {
WcAppInfoRoutes.AppInfo -> dismiss()
WcAppInfoRoutes.SelectNetworks -> {
model.clearAvailableNetworks()
model.contentNavigation.pop()
}
else -> model.contentNavigation.pop()
}
}
private fun screenChild(config: WcAppInfoRoutes, componentContext: ComponentContext): ComposableContentComponent {
val appComponentContext = childByContext(componentContext)
return when (config) {
is WcAppInfoRoutes.AppInfo -> WcAppInfoComponent(appComponentContext = appComponentContext, model = model)
is WcAppInfoRoutes.Alert -> TODO()
WcAppInfoRoutes.SelectNetworks -> WcSelectNetworksComponent(
appComponentContext = appComponentContext,
model = model,
)
WcAppInfoRoutes.SelectWallet -> WcSelectWalletComponent(
appComponentContext = appComponentContext,
model = model,
)
}
}
data class Params(val userWalletId: UserWalletId, val wcUrl: String, val source: WcPairRequest.Source)
}

View file

@ -0,0 +1,105 @@
package com.tangem.features.walletconnect.connections.components
import androidx.activity.compose.BackHandler
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import com.arkivanov.decompose.ComponentContext
import com.arkivanov.decompose.extensions.compose.subscribeAsState
import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.childStack
import com.arkivanov.decompose.router.stack.pop
import com.tangem.core.decompose.context.AppComponentContext
import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.decompose.navigation.inner.InnerRouter
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.domain.walletconnect.model.WcPairRequest
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.walletconnect.connections.model.WcPairModel
import com.tangem.features.walletconnect.connections.routes.WcAppInfoRoutes
internal class WcPairComponent(
private val appComponentContext: AppComponentContext,
params: Params,
) : AppComponentContext by appComponentContext, ComposableContentComponent {
private val stackNavigation = StackNavigation<WcAppInfoRoutes>()
private val innerRouter = InnerRouter<WcAppInfoRoutes>(
stackNavigation = stackNavigation,
popCallback = { onChildBack() },
)
private val model: WcPairModel = getOrCreateModel(params = params, router = innerRouter)
private val contentStack = childStack(
key = "WcConnectionFlowStack",
source = stackNavigation,
serializer = WcAppInfoRoutes.serializer(),
initialConfiguration = WcAppInfoRoutes.AppInfo,
handleBackButton = true,
childFactory = ::screenChild,
)
private fun dismiss() {
model.rejectPairing()
router.pop()
}
@Composable
override fun Content(modifier: Modifier) {
val content by contentStack.subscribeAsState()
BackHandler(onBack = ::onChildBack)
content.active.instance.BottomSheet()
}
private fun onChildBack() {
when (contentStack.value.active.configuration) {
WcAppInfoRoutes.AppInfo -> dismiss()
is WcAppInfoRoutes.Alert,
is WcAppInfoRoutes.SelectNetworks,
is WcAppInfoRoutes.SelectWallet,
-> stackNavigation.pop()
}
}
private fun screenChild(
config: WcAppInfoRoutes,
componentContext: ComponentContext,
): ComposableBottomSheetComponent {
val appComponentContext = childByContext(
componentContext = componentContext,
router = innerRouter,
)
return when (config) {
is WcAppInfoRoutes.AppInfo -> WcAppInfoComponent(
appComponentContext = appComponentContext,
onDismiss = ::dismiss,
model = model,
)
is WcAppInfoRoutes.Alert -> TODO()
is WcAppInfoRoutes.SelectNetworks -> WcSelectNetworksComponent(
appComponentContext = appComponentContext,
params = WcSelectNetworksComponent.WcSelectNetworksParams(
missingRequiredNetworks = config.missingRequiredNetworks,
requiredNetworks = config.requiredNetworks,
availableNetworks = config.availableNetworks,
notAddedNetworks = config.notAddedNetworks,
enabledAvailableNetworks = config.enabledAvailableNetworks,
onDismiss = ::dismiss,
callback = model,
),
)
is WcAppInfoRoutes.SelectWallet -> WcSelectWalletComponent(
appComponentContext = appComponentContext,
params = WcSelectWalletComponent.WcSelectWalletParams(
selectedWalletId = config.selectedWalletId,
onDismiss = ::dismiss,
callback = model,
),
)
}
}
data class Params(val userWalletId: UserWalletId, val wcUrl: String, val source: WcPairRequest.Source)
}

View file

@ -27,37 +27,100 @@ 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.decompose.model.getOrCreateModel
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.bottomsheets.modal.TangemModalBottomSheetWithFooter
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
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.models.network.Network
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.connections.model.WcSelectNetworksModel
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 {
private val params: WcSelectNetworksParams,
) : AppComponentContext by appComponentContext, ComposableBottomSheetComponent {
private val model: WcSelectNetworksModel = getOrCreateModel(params = params)
override fun dismiss() {
params.onDismiss()
}
@Composable
override fun Content(modifier: Modifier) {
val state by model.networksState.collectAsStateWithLifecycle()
WcSelectNetworksContent(
modifier = modifier.padding(horizontal = 16.dp),
state = state,
)
override fun BottomSheet() {
val state by model.state.collectAsStateWithLifecycle()
WcSelectNetworksBS(state = state, onBack = router::pop, onDismiss = ::dismiss)
}
interface ModelCallback {
fun onNetworksSelected(selectedNetworks: Set<Network.RawID>)
}
data class WcSelectNetworksParams(
val missingRequiredNetworks: Set<Network>,
val requiredNetworks: Set<Network>,
val availableNetworks: Set<Network>,
val notAddedNetworks: Set<Network>,
val enabledAvailableNetworks: Set<Network.RawID>,
val onDismiss: () -> Unit,
val callback: ModelCallback,
)
}
@Composable
private fun WcSelectNetworksBS(
state: WcSelectNetworksUM,
onBack: () -> Unit,
onDismiss: () -> Unit,
modifier: Modifier = Modifier,
) {
if (state.required.isEmpty()) return // required can/must not be empty
TangemModalBottomSheetWithFooter<TangemBottomSheetConfigContent.Empty>(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = onDismiss,
content = TangemBottomSheetConfigContent.Empty,
),
onBack = onBack,
containerColor = TangemTheme.colors.background.tertiary,
title = {
TangemModalBottomSheetTitle(
title = stringReference("Choose networks"),
startIconRes = R.drawable.ic_back_24,
onStartClick = onBack,
)
},
content = {
WcSelectNetworksContent(
modifier = modifier.padding(horizontal = 16.dp),
state = state,
)
},
footer = {
PrimaryButton(
modifier = Modifier
.padding(16.dp)
.fillMaxWidth(),
text = "Done",
onClick = state.onDone,
enabled = state.missing.isEmpty(),
)
},
)
}
@Composable
@ -76,14 +139,6 @@ private fun WcSelectNetworksContent(state: WcSelectNetworksUM, modifier: Modifie
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(),
)
}
}

View file

@ -20,35 +20,88 @@ 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.ComposableContentComponent
import com.tangem.core.ui.decompose.ComposableBottomSheetComponent
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.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 model: WcAppInfoModel,
) : AppComponentContext by appComponentContext, ComposableContentComponent {
private val params: WcSelectWalletParams,
) : AppComponentContext by appComponentContext, ComposableBottomSheetComponent {
private val model: WcSelectWalletModel = getOrCreateModel(params = params)
override fun dismiss() {
params.onDismiss()
}
@Composable
override fun Content(modifier: Modifier) {
val walletsState by model.walletsUiState.collectAsStateWithLifecycle()
WcSelectWalletContent(
modifier = modifier.padding(start = 16.dp, end = 16.dp, bottom = 16.dp),
wallets = walletsState.wallets,
selectedWalletId = walletsState.selectedUserWallet.walletId,
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 = stringReference("Choose networks"),
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

View file

@ -1,10 +1,10 @@
package com.tangem.features.walletconnect.connections.entity
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import kotlinx.collections.immutable.ImmutableList
internal data class WcAppInfoWalletUM(
val wallets: ImmutableList<UserWalletItemUM>,
val selectedUserWallet: UserWallet,
val selectedUserWalletId: UserWalletId,
)

View file

@ -1,205 +0,0 @@
package com.tangem.features.walletconnect.connections.model
import androidx.compose.runtime.Stable
import com.arkivanov.decompose.router.stack.StackNavigation
import com.arkivanov.decompose.router.stack.pop
import com.arkivanov.decompose.router.stack.pushNew
import com.tangem.common.ui.userwallet.state.UserWalletItemUM
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.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.models.network.Network
import com.tangem.domain.tokens.GetWalletTotalBalanceUseCase
import com.tangem.domain.walletconnect.model.WcPairRequest
import com.tangem.domain.walletconnect.model.WcSessionApprove
import com.tangem.domain.walletconnect.model.WcSessionProposal
import com.tangem.domain.walletconnect.usecase.pair.WcPairState
import com.tangem.domain.walletconnect.usecase.pair.WcPairUseCase
import com.tangem.domain.wallets.models.UserWallet
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.GetCardImageUseCase
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
import com.tangem.features.walletconnect.connections.components.WcAppInfoContainerComponent
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.*
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
@Suppress("LongParameterList")
@Stable
@ModelScoped
internal class WcAppInfoModel @Inject constructor(
private val router: Router,
override val dispatchers: CoroutineDispatcherProvider,
wcPairUseCaseFactory: WcPairUseCase.Factory,
getWalletsUseCase: GetWalletsUseCase,
getWalletTotalBalanceUseCase: GetWalletTotalBalanceUseCase,
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
messageSender: UiMessageSender,
getCardImageUseCase: GetCardImageUseCase,
paramsContainer: ParamsContainer,
) : Model() {
private val params: WcAppInfoContainerComponent.Params = paramsContainer.require()
private val wcPairUseCase = wcPairUseCaseFactory.create(
WcPairRequest(
userWalletId = params.userWalletId,
uri = params.wcUrl,
source = params.source,
),
)
private val userWalletsFetcher = WcUserWalletsFetcher(
getWalletsUseCase = getWalletsUseCase,
getWalletTotalBalanceUseCase = getWalletTotalBalanceUseCase,
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
getBalanceHidingSettingsUseCase = getBalanceHidingSettingsUseCase,
messageSender = messageSender,
getCardImageUseCase = getCardImageUseCase,
onWalletSelected = ::onWalletSelected,
)
private val selectedUserWalletFlow =
MutableStateFlow(getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId })
private var proposalNetwork by Delegates.notNull<WcSessionProposal.ProposalNetwork>()
internal var sessionProposal by Delegates.notNull<WcSessionProposal>()
// UI states
internal val contentNavigation = StackNavigation<WcAppInfoRoutes>()
val appInfoUiState: StateFlow<WcAppInfoUM>
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.RawID>>(setOf())
init {
loadDAppInfo()
combine(
flow = userWalletsFetcher.userWallets,
flow2 = selectedUserWalletFlow,
transform = ::updateWalletsState,
).launchIn(modelScope)
}
private fun loadDAppInfo() {
wcPairUseCase()
.onEach { state ->
when (state) {
is WcPairState.Approving.Loading -> appInfoUiState.transformerUpdate(
WcConnectButtonProgressTransformer(showProgress = true),
)
is WcPairState.Approving.Result -> {
appInfoUiState.transformerUpdate(
WcConnectButtonProgressTransformer(showProgress = false),
)
router.pop()
}
is WcPairState.Error -> {
Timber.e(state.error)
}
is WcPairState.Loading -> appInfoUiState.update { createLoadingState() }
is WcPairState.Proposal -> {
sessionProposal = state.dAppSession
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)
}
}
}
.launchIn(modelScope)
}
fun dismiss() {
wcPairUseCase.reject()
router.pop()
}
fun clearAvailableNetworks() {
networksState.transformerUpdate(WcSelectNetworksClearCheckedTransformer)
tempAvailableNetworksState.update { setOf() }
}
private fun onConnect() {
val enabledAvailableNetworks =
proposalNetwork.available.filter { network -> network.id.rawId in tempAvailableNetworksState.value }
wcPairUseCase.approve(
WcSessionApprove(
wallet = selectedUserWalletFlow.value,
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 }
updateNetworksState(proposalNetwork)
appInfoUiState.transformerUpdate(WcAppInfoWalletChangedTransformer(selectedUserWallet, proposalNetwork))
contentNavigation.pop()
}
private fun updateWalletsState(items: ImmutableList<UserWalletItemUM>, userWallet: UserWallet) {
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.RawID(networkId)) else it.minus(Network.RawID(networkId))
}
networksState.transformerUpdate(WcSelectNetworksCheckedTransformer(networkId, isChecked))
}
private fun createLoadingState(): WcAppInfoUM.Loading {
return WcAppInfoUM.Loading(
onDismiss = ::dismiss,
connectButtonConfig = WcPrimaryButtonConfig(showProgress = false, enabled = false, onClick = ::onConnect),
)
}
}

View file

@ -0,0 +1,153 @@
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.domain.models.network.Network
import com.tangem.domain.walletconnect.model.WcPairRequest
import com.tangem.domain.walletconnect.model.WcSessionApprove
import com.tangem.domain.walletconnect.model.WcSessionProposal
import com.tangem.domain.walletconnect.usecase.pair.WcPairState
import com.tangem.domain.walletconnect.usecase.pair.WcPairUseCase
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
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.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.routes.WcAppInfoRoutes
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import kotlinx.coroutines.flow.*
import timber.log.Timber
import javax.inject.Inject
import kotlin.properties.Delegates
import com.tangem.utils.transformer.update as transformerUpdate
internal interface WcPairComponentCallback :
WcSelectWalletComponent.ModelCallback,
WcSelectNetworksComponent.ModelCallback
@Stable
@ModelScoped
internal class WcPairModel @Inject constructor(
private val router: Router,
override val dispatchers: CoroutineDispatcherProvider,
wcPairUseCaseFactory: WcPairUseCase.Factory,
getWalletsUseCase: GetWalletsUseCase,
paramsContainer: ParamsContainer,
) : Model(), WcPairComponentCallback {
private val params: WcPairComponent.Params = paramsContainer.require()
private val wcPairUseCase = wcPairUseCaseFactory.create(
WcPairRequest(
userWalletId = params.userWalletId,
uri = params.wcUrl,
source = params.source,
),
)
private val selectedUserWalletFlow =
MutableStateFlow(getWalletsUseCase.invokeSync().first { it.walletId == params.userWalletId })
private var proposalNetwork by Delegates.notNull<WcSessionProposal.ProposalNetwork>()
internal var sessionProposal by Delegates.notNull<WcSessionProposal>()
private val additionallyEnabledNetworks = mutableSetOf<Network.RawID>()
val appInfoUiState: StateFlow<WcAppInfoUM>
field = MutableStateFlow<WcAppInfoUM>(createLoadingState())
init {
loadDAppInfo()
}
private fun loadDAppInfo() {
wcPairUseCase()
.onEach { pairState ->
when (pairState) {
is WcPairState.Approving.Loading -> appInfoUiState.transformerUpdate(
WcConnectButtonProgressTransformer(showProgress = true),
)
is WcPairState.Approving.Result -> {
appInfoUiState.transformerUpdate(
WcConnectButtonProgressTransformer(showProgress = false),
)
router.pop()
}
is WcPairState.Error -> {
Timber.e(pairState.error)
}
is WcPairState.Loading -> appInfoUiState.update { createLoadingState() }
is WcPairState.Proposal -> {
sessionProposal = pairState.dAppSession
proposalNetwork = sessionProposal.proposalNetwork.getValue(selectedUserWalletFlow.value)
appInfoUiState.transformerUpdate(
WcAppInfoTransformer(
dAppSession = pairState.dAppSession,
onDismiss = ::rejectPairing,
onConnect = ::onConnect,
onWalletClick = {
router.push(
WcAppInfoRoutes.SelectWallet(selectedUserWalletFlow.value.walletId),
)
},
onNetworksClick = {
router.push(
WcAppInfoRoutes.SelectNetworks(
missingRequiredNetworks = proposalNetwork.missingRequired,
requiredNetworks = proposalNetwork.required,
availableNetworks = proposalNetwork.available,
enabledAvailableNetworks = additionallyEnabledNetworks,
notAddedNetworks = proposalNetwork.notAdded,
),
)
},
userWallet = selectedUserWalletFlow.value,
proposalNetwork = proposalNetwork,
),
)
}
}
}
.launchIn(modelScope)
}
fun rejectPairing() {
wcPairUseCase.reject()
}
private fun onConnect() {
val enabledAvailableNetworks =
proposalNetwork.available.filter { network -> network.id.rawId in additionallyEnabledNetworks }
wcPairUseCase.approve(
WcSessionApprove(
wallet = selectedUserWalletFlow.value,
network = enabledAvailableNetworks + proposalNetwork.required,
),
)
}
override fun onWalletSelected(userWalletId: UserWalletId) {
val selectedUserWallet = sessionProposal.proposalNetwork.keys.first { it.walletId == userWalletId }
proposalNetwork = sessionProposal.proposalNetwork.getValue(selectedUserWallet)
selectedUserWalletFlow.update { selectedUserWallet }
appInfoUiState.transformerUpdate(WcAppInfoWalletChangedTransformer(selectedUserWallet, proposalNetwork))
additionallyEnabledNetworks.clear()
}
override fun onNetworksSelected(selectedNetworks: Set<Network.RawID>) {
additionallyEnabledNetworks.clear()
additionallyEnabledNetworks.addAll(selectedNetworks)
}
private fun createLoadingState(): WcAppInfoUM.Loading {
return WcAppInfoUM.Loading(
onDismiss = ::rejectPairing,
connectButtonConfig = WcPrimaryButtonConfig(showProgress = false, enabled = false, onClick = ::onConnect),
)
}
}

View file

@ -0,0 +1,68 @@
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.domain.models.network.Network
import com.tangem.features.walletconnect.connections.components.WcSelectNetworksComponent.WcSelectNetworksParams
import com.tangem.features.walletconnect.connections.entity.WcSelectNetworksUM
import com.tangem.features.walletconnect.connections.model.transformers.WcSelectNetworksCheckedTransformer
import com.tangem.features.walletconnect.connections.model.transformers.WcSelectNetworksTransformer
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
import com.tangem.utils.transformer.update
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import javax.inject.Inject
@Stable
@ModelScoped
internal class WcSelectNetworksModel @Inject constructor(
paramsContainer: ParamsContainer,
private val router: Router,
override val dispatchers: CoroutineDispatcherProvider,
) : Model() {
private val params = paramsContainer.require<WcSelectNetworksParams>()
val state: StateFlow<WcSelectNetworksUM>
field = MutableStateFlow(
WcSelectNetworksUM(
missing = persistentListOf(),
required = persistentListOf(),
available = persistentListOf(),
notAdded = persistentListOf(),
onDone = ::onDone,
),
)
private val additionallyEnabledNetworks = MutableStateFlow<Set<Network.RawID>>(setOf())
init {
state.update(
WcSelectNetworksTransformer(
missingRequiredNetworks = params.missingRequiredNetworks,
requiredNetworks = params.requiredNetworks,
availableNetworks = params.availableNetworks,
notAddedNetworks = params.notAddedNetworks,
enabledNetworks = params.enabledAvailableNetworks,
onCheckedChange = ::onCheckedChange,
),
)
}
private fun onCheckedChange(isChecked: Boolean, networkId: String) {
additionallyEnabledNetworks.update {
if (isChecked) it.plus(Network.RawID(networkId)) else it.minus(Network.RawID(networkId))
}
state.update(WcSelectNetworksCheckedTransformer(networkId, isChecked))
}
private fun onDone() {
params.callback.onNetworksSelected(additionallyEnabledNetworks.value)
router.pop()
}
}

View file

@ -0,0 +1,69 @@
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.appcurrency.GetSelectedAppCurrencyUseCase
import com.tangem.domain.balancehiding.GetBalanceHidingSettingsUseCase
import com.tangem.domain.tokens.GetWalletTotalBalanceUseCase
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.domain.wallets.usecase.GetCardImageUseCase
import com.tangem.domain.wallets.usecase.GetWalletsUseCase
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,
getWalletsUseCase: GetWalletsUseCase,
getWalletTotalBalanceUseCase: GetWalletTotalBalanceUseCase,
getSelectedAppCurrencyUseCase: GetSelectedAppCurrencyUseCase,
getBalanceHidingSettingsUseCase: GetBalanceHidingSettingsUseCase,
messageSender: UiMessageSender,
getCardImageUseCase: GetCardImageUseCase,
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(
getWalletsUseCase = getWalletsUseCase,
getWalletTotalBalanceUseCase = getWalletTotalBalanceUseCase,
getSelectedAppCurrencyUseCase = getSelectedAppCurrencyUseCase,
getBalanceHidingSettingsUseCase = getBalanceHidingSettingsUseCase,
messageSender = messageSender,
getCardImageUseCase = getCardImageUseCase,
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

@ -2,19 +2,23 @@ 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.domain.models.network.Network
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 missingRequiredNetworks: Set<Network>,
private val requiredNetworks: Set<Network>,
private val availableNetworks: Set<Network>,
private val notAddedNetworks: Set<Network>,
private val enabledNetworks: Set<Network.RawID>,
private val onCheckedChange: (Boolean, String) -> Unit,
) : Transformer<WcSelectNetworksUM> {
override fun transform(prevState: WcSelectNetworksUM): WcSelectNetworksUM {
return prevState.copy(
missing = proposalNetwork.missingRequired.map { network ->
missing = missingRequiredNetworks.map { network ->
WcNetworkInfoItem.Required(
id = network.rawId,
icon = network.iconResId,
@ -22,7 +26,7 @@ internal class WcSelectNetworksTransformer(
symbol = network.currencySymbol,
)
}.toImmutableList(),
required = proposalNetwork.required.map { network ->
required = requiredNetworks.map { network ->
WcNetworkInfoItem.Checked(
id = network.rawId,
icon = network.iconResId,
@ -30,17 +34,17 @@ internal class WcSelectNetworksTransformer(
symbol = network.currencySymbol,
)
}.toImmutableList(),
available = proposalNetwork.available.map { network ->
available = availableNetworks.map { network ->
WcNetworkInfoItem.Checkable(
id = network.rawId,
icon = network.iconResId,
name = network.name,
symbol = network.currencySymbol,
checked = false,
checked = network.id.rawId in enabledNetworks,
onCheckedChange = { onCheckedChange(it, network.rawId) },
)
}.toImmutableList(),
notAdded = proposalNetwork.notAdded.map { network ->
notAdded = notAddedNetworks.map { network ->
WcNetworkInfoItem.ReadOnly(
id = network.rawId,
icon = getGreyedOutIconRes(network.rawId),

View file

@ -1,21 +1,30 @@
package com.tangem.features.walletconnect.connections.routes
import androidx.compose.runtime.Immutable
import com.tangem.core.decompose.navigation.Route
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.domain.models.network.Network
import com.tangem.domain.wallets.models.UserWalletId
import com.tangem.features.walletconnect.connections.components.AlertsComponent
import kotlinx.serialization.Serializable
@Serializable
@Immutable
internal sealed class WcAppInfoRoutes : TangemBottomSheetConfigContent {
internal sealed class WcAppInfoRoutes : TangemBottomSheetConfigContent, Route {
@Serializable
data object AppInfo : WcAppInfoRoutes()
@Serializable
data object SelectWallet : WcAppInfoRoutes()
data class SelectWallet(val selectedWalletId: UserWalletId) : WcAppInfoRoutes()
@Serializable
data object SelectNetworks : WcAppInfoRoutes()
data class SelectNetworks(
val missingRequiredNetworks: Set<Network>,
val requiredNetworks: Set<Network>,
val availableNetworks: Set<Network>,
val enabledAvailableNetworks: Set<Network.RawID>,
val notAddedNetworks: Set<Network>,
) : WcAppInfoRoutes()
@Serializable
data class Alert(val alertType: AlertsComponent.AlertType) : WcAppInfoRoutes()

View file

@ -15,8 +15,8 @@ import com.tangem.core.decompose.context.childByContext
import com.tangem.core.decompose.model.getOrCreateModel
import com.tangem.core.ui.decompose.ComposableContentComponent
import com.tangem.features.walletconnect.components.WcRoutingComponent
import com.tangem.features.walletconnect.connections.components.WcPairComponent
import com.tangem.features.walletconnect.connections.components.AlertsComponent
import com.tangem.features.walletconnect.connections.components.WcAppInfoContainerComponent
import com.tangem.features.walletconnect.transaction.components.WcSignTransactionContainerComponent
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@ -57,12 +57,12 @@ internal class DefaultWcRoutingComponent @AssistedInject constructor(
)
return when (config) {
is WcInnerRoute.SignMessage -> WcSignTransactionContainerComponent(
childContext,
appComponentContext = childContext,
params = WcSignTransactionContainerComponent.Params(config.rawRequest),
)
is WcInnerRoute.Pair -> WcAppInfoContainerComponent(
childContext,
WcAppInfoContainerComponent.Params(
is WcInnerRoute.Pair -> WcPairComponent(
appComponentContext = childContext,
params = WcPairComponent.Params(
userWalletId = config.request.userWalletId,
wcUrl = config.request.uri,
source = config.request.source,

View file

@ -28,6 +28,10 @@ import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.datasource.CollectionPreviewParameterProvider
import androidx.compose.ui.unit.dp
import com.tangem.core.ui.components.*
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfig
import com.tangem.core.ui.components.bottomsheets.TangemBottomSheetConfigContent
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetTitle
import com.tangem.core.ui.components.bottomsheets.modal.TangemModalBottomSheetWithFooter
import com.tangem.core.ui.components.notifications.Notification
import com.tangem.core.ui.components.notifications.NotificationConfig
import com.tangem.core.ui.extensions.resourceReference
@ -42,10 +46,43 @@ import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
@Composable
internal fun WcAppInfoModalBottomSheet(state: WcAppInfoUM, modifier: Modifier = Modifier) {
internal fun WcAppInfoModalBottomSheet(state: WcAppInfoUM, onBack: () -> Unit, onDismiss: () -> Unit) {
TangemModalBottomSheetWithFooter<TangemBottomSheetConfigContent.Empty>(
config = TangemBottomSheetConfig(
isShown = true,
onDismissRequest = onDismiss,
content = TangemBottomSheetConfigContent.Empty,
),
containerColor = TangemTheme.colors.background.tertiary,
onBack = onBack,
title = {
TangemModalBottomSheetTitle(
title = resourceReference(R.string.wc_wallet_connect),
endIconRes = R.drawable.ic_close_24,
onEndClick = onDismiss,
)
},
content = {
WcAppInfoModalBottomSheet(
modifier = Modifier.padding(horizontal = TangemTheme.dimens.spacing16),
state = state,
)
},
footer = {
WcAppInfoButtons(
modifier = Modifier.padding(16.dp),
onDismiss = onDismiss,
connectButtonConfig = state.connectButtonConfig,
)
},
)
}
@Composable
private fun WcAppInfoModalBottomSheet(state: WcAppInfoUM, modifier: Modifier = Modifier) {
when (state) {
is WcAppInfoUM.Content -> WcAppInfoModalBottomSheetContent(state, modifier)
is WcAppInfoUM.Loading -> WcAppInfoModalBottomSheetLoading(state, modifier)
is WcAppInfoUM.Loading -> WcAppInfoModalBottomSheetLoading(modifier)
}
}
@ -83,11 +120,6 @@ private fun WcAppInfoModalBottomSheetContent(state: WcAppInfoUM.Content, modifie
.then(blocksModifier),
state = state,
)
WcAppInfoButtons(
modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing16),
onDismiss = state.onDismiss,
connectButtonConfig = state.connectButtonConfig,
)
}
}
@ -410,7 +442,7 @@ private fun WcAppInfoButtons(
// region Loading state
@Composable
private fun WcAppInfoModalBottomSheetLoading(state: WcAppInfoUM.Loading, modifier: Modifier = Modifier) {
private fun WcAppInfoModalBottomSheetLoading(modifier: Modifier = Modifier) {
Column(modifier = modifier) {
val blocksModifier = Modifier
.clip(RoundedCornerShape(TangemTheme.dimens.radius14))
@ -422,11 +454,6 @@ private fun WcAppInfoModalBottomSheetLoading(state: WcAppInfoUM.Loading, modifie
.padding(top = 14.dp)
.then(blocksModifier),
)
WcAppInfoButtons(
modifier = Modifier.padding(vertical = TangemTheme.dimens.spacing16),
onDismiss = state.onDismiss,
connectButtonConfig = state.connectButtonConfig,
)
}
}

View file

@ -2,9 +2,7 @@ package com.tangem.features.walletconnect.di
import com.tangem.core.decompose.di.ModelComponent
import com.tangem.core.decompose.model.Model
import com.tangem.features.walletconnect.connections.model.WcAppInfoModel
import com.tangem.features.walletconnect.connections.model.WcConnectedAppInfoModel
import com.tangem.features.walletconnect.connections.model.WcConnectionsModel
import com.tangem.features.walletconnect.connections.model.*
import com.tangem.features.walletconnect.connections.routing.WcRoutingModel
import com.tangem.features.walletconnect.transaction.model.WcSignTransactionModel
import dagger.Binds
@ -24,8 +22,18 @@ internal interface WalletConnectModelModule {
@Binds
@IntoMap
@ClassKey(WcAppInfoModel::class)
fun bindWcAppInfoModel(model: WcAppInfoModel): Model
@ClassKey(WcPairModel::class)
fun bindWcPairModel(model: WcPairModel): Model
@Binds
@IntoMap
@ClassKey(WcSelectWalletModel::class)
fun bindWcSelectWalletModel(model: WcSelectWalletModel): Model
@Binds
@IntoMap
@ClassKey(WcSelectNetworksModel::class)
fun bindWcSelectNetworksModel(model: WcSelectNetworksModel): Model
@Binds
@IntoMap