diff --git a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetConfig.kt b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetConfig.kt index d99aefacb5..7fa19c76a2 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetConfig.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/components/bottomsheets/TangemBottomSheetConfig.kt @@ -10,7 +10,7 @@ package com.tangem.core.ui.components.bottomsheets data class TangemBottomSheetConfig( val isShown: Boolean, val onDismissRequest: () -> Unit, - val dismissOnClickOutside: () -> Boolean = { true }, + val dismissOnClickOutside: (() -> Boolean)? = null, val content: TangemBottomSheetConfigContent, ) { 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 8b2c012fc1..00646f4f2e 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 @@ -88,17 +88,22 @@ inline fun DefaultModalBottomSheetW noinline footer: @Composable (BoxScope.(T) -> Unit)?, ) { var isVisible by remember { mutableStateOf(value = config.isShown) } - val sheetState = rememberModalBottomSheetState( - skipPartiallyExpanded = skipPartiallyExpanded, - confirmValueChange = { sheetValue -> - if (config.dismissOnClickOutside().not()) { - // Ignore transitions to hidden (prevents dismiss on outside click/back press) - sheetValue != SheetValue.Hidden - } else { - true - } - }, - ) + + val sheetState = if (config.dismissOnClickOutside == null) { + rememberModalBottomSheetState(skipPartiallyExpanded = skipPartiallyExpanded) + } else { + rememberModalBottomSheetState( + skipPartiallyExpanded = skipPartiallyExpanded, + confirmValueChange = { sheetValue -> + if (config.dismissOnClickOutside().not()) { + // Ignore transitions to hidden (prevents dismiss on outside click/back press) + sheetValue != SheetValue.Hidden + } else { + true + } + }, + ) + } if (isVisible && config.content is T) { BasicModalBottomSheetWithFooter( diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt index 47a63b25ac..b0e9c01c6e 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/di/WalletConnectDataModule.kt @@ -124,7 +124,10 @@ internal object WalletConnectDataModule { @Provides @Singleton - fun defaultWcRequestService(diHelperBox: DiHelperBox, respondService: WcRespondService): DefaultWcRequestService { + fun defaultWcRequestService( + diHelperBox: DiHelperBox, + respondService: DefaultWcRespondService, + ): DefaultWcRequestService { return DefaultWcRequestService( requestConverters = diHelperBox.handlers, respondService = respondService, @@ -133,7 +136,11 @@ internal object WalletConnectDataModule { @Provides @Singleton - fun wcRespondService(): WcRespondService = DefaultWcRespondService() + fun wcDefaultWcRespondService(): DefaultWcRespondService = DefaultWcRespondService() + + @Provides + @Singleton + fun wcRespondService(default: DefaultWcRespondService): WcRespondService = default @Provides @Singleton diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/request/DefaultWcRequestService.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/request/DefaultWcRequestService.kt index e1cf03ff9d..b2393fe07f 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/request/DefaultWcRequestService.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/request/DefaultWcRequestService.kt @@ -1,9 +1,7 @@ package com.tangem.data.walletconnect.request import com.reown.walletkit.client.Wallet -import com.tangem.common.extensions.calculateSha256 -import com.tangem.common.extensions.toHexString -import com.tangem.data.walletconnect.respond.WcRespondService +import com.tangem.data.walletconnect.respond.DefaultWcRespondService import com.tangem.data.walletconnect.utils.WC_TAG import com.tangem.data.walletconnect.utils.WcSdkObserver import com.tangem.data.walletconnect.utils.WcSdkSessionRequestConverter @@ -14,16 +12,13 @@ import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.* import org.joda.time.DateTime -import org.joda.time.Duration import timber.log.Timber internal class DefaultWcRequestService( private val requestConverters: Set, - private val respondService: WcRespondService, + private val respondService: DefaultWcRespondService, ) : WcSdkObserver, WcRequestService { - private val expireDuration = Duration.standardSeconds(120) - private val cachedRequest = MutableStateFlow>>(setOf()) private val _wcRequest: Channel> = Channel(Channel.BUFFERED) override val wcRequest: Flow> = _wcRequest.receiveAsFlow() .filter { filterDuplicateRequest(request = it.second) } @@ -52,10 +47,10 @@ internal class DefaultWcRequestService( } private fun filterDuplicateRequest(request: WcSdkSessionRequest): Boolean { - val hash = request.request.params.calculateSha256().toHexString() + val hash = respondService.sessionRequestHash(request) val now = DateTime.now().millis - val expiredMillis = now - expireDuration.millis - val cachedRequest = cachedRequest.updateAndGet { + val expiredMillis = now - respondService.expireDuration.millis + val cachedRequest = respondService.cachedRequest.updateAndGet { it.filterTo(mutableSetOf()) { (millis, _) -> millis > expiredMillis } } @@ -67,8 +62,8 @@ internal class DefaultWcRequestService( } private fun saveRequest(request: WcSdkSessionRequest) { - val hash = request.request.params.calculateSha256().toHexString() + val hash = respondService.sessionRequestHash(request) val now = DateTime.now().millis - cachedRequest.update { it + (now to hash) } + respondService.cachedRequest.update { it + (now to hash) } } } \ No newline at end of file diff --git a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/respond/DefaultWcRespondService.kt b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/respond/DefaultWcRespondService.kt index e2d5ff2e60..ca0bc550dd 100644 --- a/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/respond/DefaultWcRespondService.kt +++ b/data/wallet-connect/src/main/kotlin/com/tangem/data/walletconnect/respond/DefaultWcRespondService.kt @@ -5,15 +5,27 @@ import arrow.core.left import arrow.core.right import com.reown.walletkit.client.Wallet import com.reown.walletkit.client.WalletKit +import com.tangem.common.extensions.calculateSha256 +import com.tangem.common.extensions.toHexString import com.tangem.data.walletconnect.utils.WC_TAG import com.tangem.domain.walletconnect.model.WcRequestError import com.tangem.domain.walletconnect.model.sdkcopy.WcSdkSessionRequest +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.update import kotlinx.coroutines.suspendCancellableCoroutine +import org.joda.time.Duration import timber.log.Timber import kotlin.coroutines.resume internal class DefaultWcRespondService : WcRespondService { + internal val expireDuration = Duration.standardSeconds(120) + internal val cachedRequest = MutableStateFlow>>(emptySet()) + + internal fun sessionRequestHash(request: WcSdkSessionRequest): String { + return request.request.params.calculateSha256().toHexString() + } + override suspend fun respond(request: WcSdkSessionRequest, response: String): Either = suspendCancellableCoroutine { continuation -> WalletKit.respondSessionRequest( @@ -56,6 +68,7 @@ internal class DefaultWcRespondService : WcRespondService { override fun rejectRequestNonBlock(request: WcSdkSessionRequest, message: String) { Timber.tag(WC_TAG).i("reject request $request") + removeCachedRequest(request) WalletKit.respondSessionRequest( params = Wallet.Params.SessionRequestResponse( sessionTopic = request.topic, @@ -69,4 +82,10 @@ internal class DefaultWcRespondService : WcRespondService { onError = {}, ) } + + private fun removeCachedRequest(request: WcSdkSessionRequest) { + cachedRequest.update { set -> + set.filterTo(mutableSetOf()) { (_, hash) -> hash != sessionRequestHash(request) } + } + } } \ No newline at end of file diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/components/common/WcNavigationUtils.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/components/common/WcNavigationUtils.kt index 7a34577605..eaf30bde46 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/components/common/WcNavigationUtils.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/components/common/WcNavigationUtils.kt @@ -27,7 +27,7 @@ internal fun getWcCommonScreen( is WcTransactionRoutes.Transaction -> transactionScreenConverter() is WcTransactionRoutes.Alert -> AlertsComponentV2( appComponentContext = appComponentContext, - messageUM = createCommonTransactionAppInfoAlertUM(config.type), + messageUM = createCommonTransactionAppInfoAlertUM(config.alertType), ) is WcTransactionRoutes.TransactionRequestInfo -> WcTransactionRequestInfoComponent( appComponentContext = appComponentContext, diff --git a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/routes/WcTransactionRoutes.kt b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/routes/WcTransactionRoutes.kt index 40b2a98e49..360e5e1ee7 100644 --- a/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/routes/WcTransactionRoutes.kt +++ b/features/walletconnect/impl/src/main/kotlin/com/tangem/features/walletconnect/transaction/routes/WcTransactionRoutes.kt @@ -22,7 +22,7 @@ internal sealed class WcTransactionRoutes : TangemBottomSheetConfigContent, Rout data object SelectFee : WcTransactionRoutes() @Serializable - data class Alert(val type: Type) : WcTransactionRoutes() { + data class Alert(val alertType: Type) : WcTransactionRoutes() { @Serializable sealed class Type { data class Verified(val appName: String) : Type()