diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/DefaultWalletConnectComponent.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/DefaultWalletConnectComponent.kt new file mode 100644 index 0000000000..5fadd6a1e8 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/DefaultWalletConnectComponent.kt @@ -0,0 +1,71 @@ +package com.tangem.tap.features.details.ui.walletconnect + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.ui.Modifier +import com.arkivanov.essenty.lifecycle.subscribe +import com.tangem.common.routing.AppRouter +import com.tangem.core.analytics.Analytics +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.tap.common.analytics.events.WalletConnect +import com.tangem.tap.common.extensions.dispatchNavigationAction +import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState +import com.tangem.tap.features.details.ui.walletconnect.api.WalletConnectComponent +import com.tangem.tap.store +import dagger.assisted.Assisted +import dagger.assisted.AssistedFactory +import dagger.assisted.AssistedInject +import org.rekotlin.StoreSubscriber + +@Suppress("UnusedPrivateMember") +internal class DefaultWalletConnectComponent @AssistedInject constructor( + @Assisted appComponentContext: AppComponentContext, + @Assisted params: Unit, +) : WalletConnectComponent, AppComponentContext by appComponentContext, StoreSubscriber { + + private val model: WalletConnectModel = getOrCreateModel() + + private var screenState: MutableState? = null + + init { + lifecycle.subscribe( + onCreate = { + Analytics.send(WalletConnect.ScreenOpened()) + screenState = mutableStateOf(model.updateState(store.state.walletConnectState)) + }, + onStart = { + store.subscribe(this) { state -> + state.skipRepeats { oldState, newState -> + oldState.walletConnectState == newState.walletConnectState + }.select { it.walletConnectState } + } + }, + onStop = { + store.unsubscribe(this) + }, + ) + } + + override fun newState(state: WalletConnectState) { + screenState?.value = model.updateState(state) + } + + @Composable + override fun Content(modifier: Modifier) { + val state = screenState?.value ?: return + WalletConnectScreen( + modifier = modifier, + state = state, + onBackClick = { + store.dispatchNavigationAction(AppRouter::pop) + }, + ) + } + + @AssistedFactory + interface Factory : WalletConnectComponent.Factory { + override fun create(context: AppComponentContext, params: Unit): DefaultWalletConnectComponent + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectFragment.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectFragment.kt deleted file mode 100644 index d720c10ec7..0000000000 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectFragment.kt +++ /dev/null @@ -1,68 +0,0 @@ -package com.tangem.tap.features.details.ui.walletconnect - -import android.os.Bundle -import androidx.compose.runtime.Composable -import androidx.compose.runtime.MutableState -import androidx.compose.runtime.mutableStateOf -import androidx.compose.ui.Modifier -import androidx.fragment.app.viewModels -import com.tangem.common.routing.AppRouter -import com.tangem.core.analytics.Analytics -import com.tangem.core.ui.UiDependencies -import com.tangem.core.ui.screen.ComposeFragment -import com.tangem.tap.common.analytics.events.WalletConnect -import com.tangem.tap.common.extensions.dispatchNavigationAction -import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState -import com.tangem.tap.store -import dagger.hilt.android.AndroidEntryPoint -import org.rekotlin.StoreSubscriber -import javax.inject.Inject - -@AndroidEntryPoint -internal class WalletConnectFragment : ComposeFragment(), StoreSubscriber { - - @Inject - override lateinit var uiDependencies: UiDependencies - - private val viewModel: WalletConnectViewModel by viewModels() - - private var screenState: MutableState? = null - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - Analytics.send(WalletConnect.ScreenOpened()) - lifecycle.addObserver(viewModel) - screenState = mutableStateOf(viewModel.updateState(store.state.walletConnectState)) - } - - @Composable - override fun ScreenContent(modifier: Modifier) { - val state = screenState?.value ?: return - WalletConnectScreen( - modifier = modifier, - state = state, - onBackClick = { - store.dispatchNavigationAction(AppRouter::pop) - }, - ) - } - - override fun onStart() { - super.onStart() - store.subscribe(this) { state -> - state.skipRepeats { oldState, newState -> - oldState.walletConnectState == newState.walletConnectState - }.select { it.walletConnectState } - } - } - - override fun onStop() { - super.onStop() - store.unsubscribe(this) - } - - override fun newState(state: WalletConnectState) { - if (activity == null || view == null) return - screenState?.value = viewModel.updateState(state) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectViewModel.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectModel.kt similarity index 82% rename from app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectViewModel.kt rename to app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectModel.kt index 2ee11076ee..6372c2f648 100644 --- a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/WalletConnectModel.kt @@ -1,31 +1,34 @@ package com.tangem.tap.features.details.ui.walletconnect -import androidx.lifecycle.* +import androidx.compose.runtime.Stable import arrow.core.getOrElse +import com.tangem.core.decompose.di.ComponentScoped +import com.tangem.core.decompose.model.Model import com.tangem.core.ui.clipboard.ClipboardManager import com.tangem.domain.qrscanning.models.SourceType import com.tangem.domain.qrscanning.usecases.ListenToQrScanningUseCase import com.tangem.tap.features.details.redux.walletconnect.WalletConnectAction import com.tangem.tap.features.details.redux.walletconnect.WalletConnectState import com.tangem.tap.store -import dagger.hilt.android.lifecycle.HiltViewModel +import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.collections.immutable.toImmutableList import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject -@HiltViewModel -internal class WalletConnectViewModel @Inject constructor( +@Stable +@ComponentScoped +internal class WalletConnectModel @Inject constructor( + override val dispatchers: CoroutineDispatcherProvider, private val listenToQrScanningUseCase: ListenToQrScanningUseCase, private val clipboardManager: ClipboardManager, -) : ViewModel(), DefaultLifecycleObserver { +) : Model() { - override fun onCreate(owner: LifecycleOwner) { - viewModelScope.launch { + init { + modelScope.launch { listenToQrScanningUseCase(SourceType.WALLET_CONNECT) .getOrElse { emptyFlow() } - .flowWithLifecycle(owner.lifecycle, minActiveState = Lifecycle.State.CREATED) .collect { store.dispatch(WalletConnectAction.OpenSession(it)) } } } diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/api/WalletConnectComponent.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/api/WalletConnectComponent.kt new file mode 100644 index 0000000000..4e1f4ab3fb --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/api/WalletConnectComponent.kt @@ -0,0 +1,8 @@ +package com.tangem.tap.features.details.ui.walletconnect.api + +import com.tangem.core.decompose.factory.ComponentFactory +import com.tangem.core.ui.decompose.ComposableContentComponent + +interface WalletConnectComponent : ComposableContentComponent { + interface Factory : ComponentFactory +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/di/WalletConnectFeatureModule.kt b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/di/WalletConnectFeatureModule.kt new file mode 100644 index 0000000000..20a85d18be --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/details/ui/walletconnect/di/WalletConnectFeatureModule.kt @@ -0,0 +1,25 @@ +package com.tangem.tap.features.details.ui.walletconnect.di + +import com.tangem.core.decompose.model.Model +import com.tangem.tap.features.details.ui.walletconnect.DefaultWalletConnectComponent +import com.tangem.tap.features.details.ui.walletconnect.WalletConnectModel +import com.tangem.tap.features.details.ui.walletconnect.api.WalletConnectComponent +import dagger.Binds +import dagger.Module +import dagger.hilt.InstallIn +import dagger.hilt.components.SingletonComponent +import dagger.multibindings.ClassKey +import dagger.multibindings.IntoMap + +@Module +@InstallIn(SingletonComponent::class) +internal interface WalletConnectFeatureModule { + + @Binds + fun bindFactory(impl: DefaultWalletConnectComponent.Factory): WalletConnectComponent.Factory + + @Binds + @IntoMap + @ClassKey(WalletConnectModel::class) + fun bindModel(model: WalletConnectModel): Model +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt index 09e825ea06..970abaae15 100644 --- a/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt +++ b/app/src/main/java/com/tangem/tap/routing/utils/ChildFactory.kt @@ -26,7 +26,7 @@ import com.tangem.tap.features.details.ui.cardsettings.CardSettingsFragment import com.tangem.tap.features.details.ui.cardsettings.coderecovery.AccessCodeRecoveryFragment import com.tangem.tap.features.details.ui.resetcard.ResetCardFragment import com.tangem.tap.features.details.ui.securitymode.SecurityModeFragment -import com.tangem.tap.features.details.ui.walletconnect.WalletConnectFragment +import com.tangem.tap.features.details.ui.walletconnect.api.WalletConnectComponent import com.tangem.tap.features.home.api.HomeComponent import com.tangem.tap.features.onboarding.products.note.OnboardingNoteFragment import com.tangem.tap.features.onboarding.products.otherCards.OnboardingOtherCardsFragment @@ -63,6 +63,7 @@ internal class ChildFactory @Inject constructor( private val swapComponentFactory: SwapComponent.Factory, private val homeComponentFactory: HomeComponent.Factory, private val tokenDetailsComponentFactory: TokenDetailsComponent.Factory, + private val walletConnectComponentFactory: WalletConnectComponent.Factory, private val walletRouter: WalletRouter, private val qrScanningRouter: QrScanningRouter, private val testerRouter: TesterRouter, @@ -267,6 +268,13 @@ internal class ChildFactory @Inject constructor( componentFactory = homeComponentFactory, ) } + is AppRoute.WalletConnectSessions -> { + createComponentChild( + contextProvider = contextProvider(route, contextFactory), + params = Unit, + componentFactory = walletConnectComponentFactory, + ) + } is AppRoute.AccessCodeRecovery, is AppRoute.AppCurrencySelector, is AppRoute.SaveWallet, @@ -281,7 +289,6 @@ internal class ChildFactory @Inject constructor( is AppRoute.ReferralProgram, is AppRoute.ResetToFactory, is AppRoute.Wallet, - is AppRoute.WalletConnectSessions, is AppRoute.PushNotification, -> error("Unsupported route: $route") } @@ -400,7 +407,11 @@ internal class ChildFactory @Inject constructor( route.asFragmentChild(Provider { walletRouter.getEntryFragment() }) } is AppRoute.WalletConnectSessions -> { - route.asFragmentChild(Provider { WalletConnectFragment() }) + route.asComponentChild( + contextProvider = contextProvider(route, contextFactory), + params = Unit, + componentFactory = walletConnectComponentFactory, + ) } is AppRoute.CurrencyDetails -> { route.asComponentChild(