Updated on 2026-08-14
This commit is contained in:
parent
1f629433a3
commit
4b6a093ba9
17 changed files with 633 additions and 419 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
|
|
@ -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(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue