diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/modal/TangemModalBottomSheetWithFooter.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/modal/TangemModalBottomSheetWithFooter.kt index 7b38499c71..479fe79469 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/modal/TangemModalBottomSheetWithFooter.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/modal/TangemModalBottomSheetWithFooter.kt @@ -48,7 +48,7 @@ inline fun 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 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 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( config = config, @@ -144,7 +144,7 @@ inline fun 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 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 BasicModalBottomSheetWit Box( modifier = Modifier .fillMaxWidth() + .height(80.dp) .align(Alignment.BottomCenter), ) { footer(model) diff --git a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/WcAnalyticEvents.kt b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/WcAnalyticEvents.kt index 2d41363ec8..37b4175437 100644 --- a/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/WcAnalyticEvents.kt +++ b/domain/wallet-connect/src/main/kotlin/com/tangem/domain/walletconnect/WcAnalyticEvents.kt @@ -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, ), diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcAppInfoComponent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcAppInfoComponent.kt index 87a06ece5b..decb85d31e 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcAppInfoComponent.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcAppInfoComponent.kt @@ -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) } } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcAppInfoContainerComponent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcAppInfoContainerComponent.kt deleted file mode 100644 index ef47353704..0000000000 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcAppInfoContainerComponent.kt +++ /dev/null @@ -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( - 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) -} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcPairComponent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcPairComponent.kt new file mode 100644 index 0000000000..7ad310da6e --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcPairComponent.kt @@ -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() + private val innerRouter = InnerRouter( + 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) +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcSelectNetworksComponent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcSelectNetworksComponent.kt index 8c12698940..86acac33bc 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcSelectNetworksComponent.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcSelectNetworksComponent.kt @@ -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) + } + + data class WcSelectNetworksParams( + val missingRequiredNetworks: Set, + val requiredNetworks: Set, + val availableNetworks: Set, + val notAddedNetworks: Set, + val enabledAvailableNetworks: Set, + 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( + 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(), - ) } } diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcSelectWalletComponent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcSelectWalletComponent.kt index c810210b3e..152effbda2 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcSelectWalletComponent.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/components/WcSelectWalletComponent.kt @@ -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, + selectedWalletId: UserWalletId, + onBack: () -> Unit, + onDismiss: () -> Unit, + modifier: Modifier = Modifier, +) { + if (wallets.isEmpty()) return + + TangemModalBottomSheet( + 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 diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcAppInfoWalletUM.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcAppInfoWalletUM.kt index 0c682702a0..23d62c4c79 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcAppInfoWalletUM.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/entity/WcAppInfoWalletUM.kt @@ -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, - val selectedUserWallet: UserWallet, + val selectedUserWalletId: UserWalletId, ) \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcAppInfoModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcAppInfoModel.kt deleted file mode 100644 index 5b4d1ce57c..0000000000 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcAppInfoModel.kt +++ /dev/null @@ -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() - internal var sessionProposal by Delegates.notNull() - - // UI states - internal val contentNavigation = StackNavigation() - val appInfoUiState: StateFlow - field = MutableStateFlow(createLoadingState()) - val walletsUiState: StateFlow - field = MutableStateFlow(WcAppInfoWalletUM(persistentListOf(), selectedUserWalletFlow.value)) - val networksState: StateFlow - field = MutableStateFlow( - WcSelectNetworksUM( - missing = persistentListOf(), - required = persistentListOf(), - available = persistentListOf(), - notAdded = persistentListOf(), - onDone = ::onNetworksDone, - ), - ) - // TODO: [REDACTED_JIRA] Change it to UiManager - private val tempAvailableNetworksState = MutableStateFlow>(setOf()) - - init { - loadDAppInfo() - 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, 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), - ) - } -} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt new file mode 100644 index 0000000000..95bfeffd96 --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcPairModel.kt @@ -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() + internal var sessionProposal by Delegates.notNull() + private val additionallyEnabledNetworks = mutableSetOf() + + val appInfoUiState: StateFlow + field = MutableStateFlow(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) { + additionallyEnabledNetworks.clear() + additionallyEnabledNetworks.addAll(selectedNetworks) + } + + private fun createLoadingState(): WcAppInfoUM.Loading { + return WcAppInfoUM.Loading( + onDismiss = ::rejectPairing, + connectButtonConfig = WcPrimaryButtonConfig(showProgress = false, enabled = false, onClick = ::onConnect), + ) + } +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcSelectNetworksModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcSelectNetworksModel.kt new file mode 100644 index 0000000000..fb3a664462 --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcSelectNetworksModel.kt @@ -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() + + val state: StateFlow + field = MutableStateFlow( + WcSelectNetworksUM( + missing = persistentListOf(), + required = persistentListOf(), + available = persistentListOf(), + notAdded = persistentListOf(), + onDone = ::onDone, + ), + ) + + private val additionallyEnabledNetworks = MutableStateFlow>(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() + } +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcSelectWalletModel.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcSelectWalletModel.kt new file mode 100644 index 0000000000..758d1a0054 --- /dev/null +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/WcSelectWalletModel.kt @@ -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() + + internal val state: StateFlow + field = MutableStateFlow( + 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() + } +} \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcSelectNetworksTransformer.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcSelectNetworksTransformer.kt index ed39ecae86..68e07039c3 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcSelectNetworksTransformer.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/model/transformers/WcSelectNetworksTransformer.kt @@ -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, + private val requiredNetworks: Set, + private val availableNetworks: Set, + private val notAddedNetworks: Set, + private val enabledNetworks: Set, private val onCheckedChange: (Boolean, String) -> Unit, ) : Transformer { 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), diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routes/WcAppInfoRoutes.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routes/WcAppInfoRoutes.kt index 74a5c349f2..dfebe98a93 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routes/WcAppInfoRoutes.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routes/WcAppInfoRoutes.kt @@ -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, + val requiredNetworks: Set, + val availableNetworks: Set, + val enabledAvailableNetworks: Set, + val notAddedNetworks: Set, + ) : WcAppInfoRoutes() @Serializable data class Alert(val alertType: AlertsComponent.AlertType) : WcAppInfoRoutes() diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routing/DefaultWcRoutingComponent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routing/DefaultWcRoutingComponent.kt index c64c2b892d..174119ad19 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routing/DefaultWcRoutingComponent.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/routing/DefaultWcRoutingComponent.kt @@ -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, diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoContent.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoBS.kt similarity index 93% rename from features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoContent.kt rename to features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoBS.kt index e9cbbe442e..2c2a3b4c51 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoContent.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/connections/ui/WcAppInfoBS.kt @@ -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( + 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, - ) } } diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/di/WalletConnectModelModule.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/di/WalletConnectModelModule.kt index cc9d17fe49..01394aa611 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/di/WalletConnectModelModule.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/di/WalletConnectModelModule.kt @@ -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