diff --git a/app/src/main/java/com/tangem/tap/features/home/DefaultHomeComponent.kt b/app/src/main/java/com/tangem/tap/features/home/DefaultHomeComponent.kt new file mode 100644 index 0000000000..a7a853a86a --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/home/DefaultHomeComponent.kt @@ -0,0 +1,74 @@ +package com.tangem.tap.features.home + +import androidx.activity.compose.BackHandler +import androidx.compose.runtime.Composable +import androidx.compose.runtime.MutableState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import com.arkivanov.essenty.lifecycle.subscribe +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.model.getOrCreateModel +import com.tangem.core.ui.components.SystemBarsIconsDisposable +import com.tangem.core.ui.utils.findActivity +import com.tangem.tap.common.redux.AppState +import com.tangem.tap.features.home.api.HomeComponent +import com.tangem.tap.features.home.compose.StoriesScreen +import com.tangem.tap.features.home.redux.HomeAction +import com.tangem.tap.features.home.redux.HomeState +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 DefaultHomeComponent @AssistedInject constructor( + @Assisted appComponentContext: AppComponentContext, + @Assisted params: Unit, +) : HomeComponent, AppComponentContext by appComponentContext, StoreSubscriber { + + private val model: HomeModel = getOrCreateModel() + + private var homeState: MutableState = mutableStateOf(store.state.homeState) + + init { + lifecycle.subscribe( + onCreate = { + store.dispatch(HomeAction.OnCreate) + }, + onStart = { + store.subscribe(subscriber = this) { state -> + state + .skipRepeats { oldState, newState -> oldState.homeState == newState.homeState } + .select(AppState::homeState) + } + }, + onStop = { + store.unsubscribe(this) + }, + ) + } + + @Composable + override fun Content(modifier: Modifier) { + val activity = LocalContext.current.findActivity() + BackHandler(onBack = activity::finish) + SystemBarsIconsDisposable(darkIcons = false) + StoriesScreen( + homeState = homeState, + onScanButtonClick = model::onScanClick, + onShopButtonClick = model::onShopClick, + onSearchTokensClick = model::onSearchClick, + ) + } + + override fun newState(state: HomeState) { + homeState.value = state + } + + @AssistedFactory + interface Factory : HomeComponent.Factory { + override fun create(context: AppComponentContext, params: Unit): DefaultHomeComponent + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt b/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt deleted file mode 100644 index 2379b399a9..0000000000 --- a/app/src/main/java/com/tangem/tap/features/home/HomeFragment.kt +++ /dev/null @@ -1,75 +0,0 @@ -package com.tangem.tap.features.home - -import android.os.Bundle -import androidx.activity.compose.BackHandler -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.core.ui.UiDependencies -import com.tangem.core.ui.components.SystemBarsIconsDisposable -import com.tangem.core.ui.screen.ComposeFragment -import com.tangem.tap.common.redux.AppState -import com.tangem.tap.features.home.compose.StoriesScreen -import com.tangem.tap.features.home.redux.HomeAction -import com.tangem.tap.features.home.redux.HomeState -import com.tangem.tap.store -import dagger.hilt.android.AndroidEntryPoint -import org.rekotlin.StoreSubscriber -import javax.inject.Inject - -@AndroidEntryPoint -internal class HomeFragment : ComposeFragment(), StoreSubscriber { - - @Inject - override lateinit var uiDependencies: UiDependencies - - private var homeState: MutableState = mutableStateOf(store.state.homeState) - - private val viewModel by viewModels() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - store.dispatch(HomeAction.OnCreate) - } - - @Composable - override fun ScreenContent(modifier: Modifier) { - BackHandler(onBack = requireActivity()::finish) - SystemBarsIconsDisposable(darkIcons = false) - ScreenContent() - } - - override fun onStart() { - super.onStart() - - store.subscribe(subscriber = this) { state -> - state - .skipRepeats { oldState, newState -> oldState.homeState == newState.homeState } - .select(AppState::homeState) - } - } - - override fun onStop() { - super.onStop() - store.unsubscribe(this) - } - - override fun newState(state: HomeState) { - if (activity == null || view == null) return - - homeState.value = state - } - - @Suppress("TopLevelComposableFunctions") - @Composable - private fun ScreenContent() { - StoriesScreen( - homeState = homeState, - onScanButtonClick = viewModel::onScanClick, - onShopButtonClick = viewModel::onShopClick, - onSearchTokensClick = viewModel::onSearchClick, - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/HomeViewModel.kt b/app/src/main/java/com/tangem/tap/features/home/HomeModel.kt similarity index 93% rename from app/src/main/java/com/tangem/tap/features/home/HomeViewModel.kt rename to app/src/main/java/com/tangem/tap/features/home/HomeModel.kt index f0b0d760e6..91a72ef7ed 100644 --- a/app/src/main/java/com/tangem/tap/features/home/HomeViewModel.kt +++ b/app/src/main/java/com/tangem/tap/features/home/HomeModel.kt @@ -1,7 +1,6 @@ package com.tangem.tap.features.home -import androidx.lifecycle.ViewModel -import androidx.lifecycle.viewModelScope +import androidx.compose.runtime.Stable import com.google.firebase.analytics.ktx.analytics import com.google.firebase.ktx.Firebase import com.tangem.common.routing.AppRoute @@ -10,6 +9,8 @@ import com.tangem.core.analytics.Analytics import com.tangem.core.analytics.api.AnalyticsEventHandler import com.tangem.core.analytics.models.AnalyticsParam import com.tangem.core.analytics.models.Basic +import com.tangem.core.decompose.di.ComponentScoped +import com.tangem.core.decompose.model.Model import com.tangem.core.navigation.url.UrlOpener import com.tangem.domain.card.ScanCardProcessor import com.tangem.domain.card.repository.CardSdkConfigRepository @@ -30,7 +31,7 @@ import com.tangem.tap.features.home.redux.HIDE_PROGRESS_DELAY import com.tangem.tap.features.home.redux.HomeAction import com.tangem.tap.features.home.redux.HomeMiddleware.NEW_BUY_WALLET_URL import com.tangem.tap.store -import dagger.hilt.android.lifecycle.HiltViewModel +import com.tangem.utils.coroutines.CoroutineDispatcherProvider import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.launch @@ -38,8 +39,10 @@ import timber.log.Timber import javax.inject.Inject @Suppress("LongParameterList") -@HiltViewModel -internal class HomeViewModel @Inject constructor( +@Stable +@ComponentScoped +internal class HomeModel @Inject constructor( + override val dispatchers: CoroutineDispatcherProvider, private val scanCardProcessor: ScanCardProcessor, private val generateWalletNameUseCase: GenerateWalletNameUseCase, private val saveWalletUseCase: SaveWalletUseCase, @@ -47,7 +50,7 @@ internal class HomeViewModel @Inject constructor( private val settingsRepository: SettingsRepository, private val urlOpener: UrlOpener, private val analyticsEventHandler: AnalyticsEventHandler, -) : ViewModel() { +) : Model() { private val tangemErrorHandler = TangemTangemErrorsHandler(store) @@ -73,7 +76,7 @@ internal class HomeViewModel @Inject constructor( } private fun scanCard() { - viewModelScope.launch { + modelScope.launch { cardSdkConfigRepository.isBiometricsRequestPolicy = settingsRepository.shouldSaveAccessCodes() scanCardProcessor.scan( diff --git a/app/src/main/java/com/tangem/tap/features/home/api/HomeComponent.kt b/app/src/main/java/com/tangem/tap/features/home/api/HomeComponent.kt new file mode 100644 index 0000000000..0c63e10a1c --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/home/api/HomeComponent.kt @@ -0,0 +1,9 @@ +package com.tangem.tap.features.home.api + +import com.tangem.core.decompose.factory.ComponentFactory +import com.tangem.core.ui.decompose.ComposableContentComponent + +interface HomeComponent : ComposableContentComponent { + + interface Factory : ComponentFactory +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/features/home/di/HomeFeatureModule.kt b/app/src/main/java/com/tangem/tap/features/home/di/HomeFeatureModule.kt new file mode 100644 index 0000000000..9f92cc5583 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/features/home/di/HomeFeatureModule.kt @@ -0,0 +1,25 @@ +package com.tangem.tap.features.home.di + +import com.tangem.core.decompose.model.Model +import com.tangem.tap.features.home.DefaultHomeComponent +import com.tangem.tap.features.home.HomeModel +import com.tangem.tap.features.home.api.HomeComponent +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 HomeFeatureModule { + + @Binds + fun bindFactory(impl: DefaultHomeComponent.Factory): HomeComponent.Factory + + @Binds + @IntoMap + @ClassKey(HomeModel::class) + fun bindModel(model: HomeModel): 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 10fc7890b2..d8483ca2db 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 @@ -27,7 +27,7 @@ import com.tangem.tap.features.details.ui.cardsettings.coderecovery.AccessCodeRe 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.home.HomeFragment +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 import com.tangem.tap.features.onboarding.products.twins.ui.OnboardingTwinsFragment @@ -61,6 +61,7 @@ internal class ChildFactory @Inject constructor( private val sendComponentFactory: SendComponent.Factory, private val stakingComponentFactory: StakingComponent.Factory, private val swapComponentFactory: SwapComponent.Factory, + private val homeComponentFactory: HomeComponent.Factory, private val tokenDetailsRouter: TokenDetailsRouter, private val walletRouter: WalletRouter, private val qrScanningRouter: QrScanningRouter, @@ -249,13 +250,20 @@ internal class ChildFactory @Inject constructor( componentFactory = sendComponentFactory, ) } + is AppRoute.Home -> { + createComponentChild( + contextProvider = contextProvider(route, contextFactory), + params = Unit, + componentFactory = homeComponentFactory, + ) + } is AppRoute.AccessCodeRecovery, is AppRoute.AppCurrencySelector, is AppRoute.SaveWallet, is AppRoute.AppSettings, is AppRoute.CardSettings, is AppRoute.DetailsSecurity, - is AppRoute.Home, + is AppRoute.OnboardingNote, is AppRoute.OnboardingOther, is AppRoute.OnboardingTwins, @@ -328,7 +336,11 @@ internal class ChildFactory @Inject constructor( ) } is AppRoute.Home -> { - route.asFragmentChild(Provider { HomeFragment() }) + route.asComponentChild( + contextProvider = contextProvider(route, contextFactory), + params = Unit, + componentFactory = homeComponentFactory, + ) } is AppRoute.ManageTokens -> { val source = when (route.source) {