From 69aeaf5776f12b94a6f8f023bc37a249acd8b7ab Mon Sep 17 00:00:00 2001 From: Tangem Date: Tue, 11 Jun 2024 15:29:17 +0400 Subject: [PATCH] Updated on 2026-08-14 --- .../java/com/tangem/tap/DecomposeFragment.kt | 61 +++++++++++++++++++ .../tap/di/RootAppComponentContextModule.kt | 14 +---- .../com/tangem/tap/di/UiDependenciesModule.kt | 4 ++ ...vider.kt => ComposableContentComponent.kt} | 2 +- .../java/com/tangem/core/ui/UiDependencies.kt | 8 +++ .../core/ui/message/EventMessageHandler.kt | 2 + .../com/tangem/core/ui/res/TangemTheme.kt | 10 ++- .../tangem/core/ui/screen/ComposeScreen.kt | 1 + .../details/component/DetailsComponent.kt | 4 +- .../component/UserWalletListComponent.kt | 4 +- .../features/details/ui/DetailsScreen.kt | 8 +-- features/onboarding/build.gradle.kts | 1 + 12 files changed, 96 insertions(+), 23 deletions(-) create mode 100644 app/src/main/java/com/tangem/tap/DecomposeFragment.kt rename core/ui/src/main/java/com/tangem/core/ui/{ComposableContentProvider.kt => ComposableContentComponent.kt} (86%) diff --git a/app/src/main/java/com/tangem/tap/DecomposeFragment.kt b/app/src/main/java/com/tangem/tap/DecomposeFragment.kt new file mode 100644 index 0000000000..d76bd6cd67 --- /dev/null +++ b/app/src/main/java/com/tangem/tap/DecomposeFragment.kt @@ -0,0 +1,61 @@ +package com.tangem.tap + +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.fragment.app.Fragment +import com.tangem.core.decompose.context.AppComponentContext +import com.tangem.core.decompose.factory.ComponentFactory +import com.tangem.core.ui.ComposableContentComponent +import com.tangem.core.ui.UiDependencies +import com.tangem.core.ui.message.EventMessageEffect +import com.tangem.core.ui.screen.ComposeFragment +import com.tangem.utils.Provider +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject + +@AndroidEntryPoint +internal class DecomposeFragment : ComposeFragment() { + + @Inject + override lateinit var uiDependencies: UiDependencies + + private val component by lazy(mode = LazyThreadSafetyMode.NONE) { + requireNotNull(componentBuilder?.build()) { + "Component builder is not set, call newInstance() for DecomposeFragment creation first." + } + } + + @Composable + override fun ScreenContent(modifier: Modifier) { + component.Content(modifier) + + EventMessageEffect( + messageHandler = uiDependencies.eventMessageHandler, + snackbarHostState = uiDependencies.globalSnackbarHostState, + ) + } + + private class ComponentBuilder>( + private val contextProvider: Provider, + private val params: P, + private val componentFactory: F, + ) { + + fun build(): C = componentFactory.create(contextProvider(), params) + } + + companion object { + + private var componentBuilder: ComponentBuilder<*, *, *>? = null + + fun > newInstance( + contextProvider: Provider, + params: P, + componentFactory: F, + ): Fragment { + this@Companion.componentBuilder = ComponentBuilder(contextProvider, params, componentFactory) + + return DecomposeFragment() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/tangem/tap/di/RootAppComponentContextModule.kt b/app/src/main/java/com/tangem/tap/di/RootAppComponentContextModule.kt index b23a41b8a8..1128b32708 100644 --- a/app/src/main/java/com/tangem/tap/di/RootAppComponentContextModule.kt +++ b/app/src/main/java/com/tangem/tap/di/RootAppComponentContextModule.kt @@ -7,8 +7,7 @@ import com.tangem.core.decompose.context.AppComponentContext import com.tangem.core.decompose.context.DefaultAppComponentContext import com.tangem.core.decompose.di.DecomposeComponent import com.tangem.core.decompose.di.RootAppComponentContext -import com.tangem.core.decompose.ui.UiMessage -import com.tangem.core.decompose.ui.UiMessageHandler +import com.tangem.core.ui.UiDependencies import com.tangem.utils.coroutines.CoroutineDispatcherProvider import dagger.Module import dagger.Provides @@ -16,7 +15,6 @@ import dagger.hilt.InstallIn import dagger.hilt.android.components.ActivityComponent import dagger.hilt.android.qualifiers.ActivityContext import dagger.hilt.android.scopes.ActivityScoped -import timber.log.Timber @Module @InstallIn(ActivityComponent::class) @@ -29,17 +27,11 @@ internal object RootAppComponentContextModule { @ActivityContext context: Context, dispatchers: CoroutineDispatcherProvider, componentBuilder: DecomposeComponent.Builder, + uiDependencies: UiDependencies, ): AppComponentContext { - // TODO: Implement message handler - val dummyMessageHandler = object : UiMessageHandler { - override fun handleMessage(message: UiMessage) { - Timber.w("Unable to handle message: $message") - } - } - return DefaultAppComponentContext( componentContext = (context as AppCompatActivity).defaultComponentContext(), - messageHandler = dummyMessageHandler, + messageHandler = uiDependencies.eventMessageHandler, dispatchers = dispatchers, hiltComponentBuilder = componentBuilder, ) diff --git a/app/src/main/java/com/tangem/tap/di/UiDependenciesModule.kt b/app/src/main/java/com/tangem/tap/di/UiDependenciesModule.kt index 97b0f48bee..22a522e553 100644 --- a/app/src/main/java/com/tangem/tap/di/UiDependenciesModule.kt +++ b/app/src/main/java/com/tangem/tap/di/UiDependenciesModule.kt @@ -1,7 +1,9 @@ package com.tangem.tap.di +import androidx.compose.material3.SnackbarHostState import com.tangem.core.ui.UiDependencies import com.tangem.core.ui.haptic.HapticManager +import com.tangem.core.ui.message.EventMessageHandler import com.tangem.core.ui.theme.AppThemeModeHolder import dagger.Module import dagger.Provides @@ -19,6 +21,8 @@ internal object UiDependenciesModule { return object : UiDependencies { override val hapticManager = hapticManager override val appThemeModeHolder = appThemeModeHolder + override val globalSnackbarHostState: SnackbarHostState = SnackbarHostState() + override val eventMessageHandler: EventMessageHandler = EventMessageHandler() } } } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/ComposableContentProvider.kt b/core/ui/src/main/java/com/tangem/core/ui/ComposableContentComponent.kt similarity index 86% rename from core/ui/src/main/java/com/tangem/core/ui/ComposableContentProvider.kt rename to core/ui/src/main/java/com/tangem/core/ui/ComposableContentComponent.kt index 28868a7c18..933ae2fa9d 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/ComposableContentProvider.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/ComposableContentComponent.kt @@ -5,7 +5,7 @@ import androidx.compose.runtime.Stable import androidx.compose.ui.Modifier @Stable -fun interface ComposableContentProvider { +fun interface ComposableContentComponent { @Composable @Suppress("TopLevelComposableFunctions") // TODO: Remove this check diff --git a/core/ui/src/main/java/com/tangem/core/ui/UiDependencies.kt b/core/ui/src/main/java/com/tangem/core/ui/UiDependencies.kt index 62e19d4af6..49a90c5af6 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/UiDependencies.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/UiDependencies.kt @@ -1,11 +1,19 @@ package com.tangem.core.ui +import androidx.compose.material3.SnackbarHostState +import androidx.compose.runtime.Stable import com.tangem.core.ui.haptic.HapticManager +import com.tangem.core.ui.message.EventMessageHandler import com.tangem.core.ui.theme.AppThemeModeHolder +@Stable interface UiDependencies { val hapticManager: HapticManager val appThemeModeHolder: AppThemeModeHolder + + val globalSnackbarHostState: SnackbarHostState + + val eventMessageHandler: EventMessageHandler } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/message/EventMessageHandler.kt b/core/ui/src/main/java/com/tangem/core/ui/message/EventMessageHandler.kt index 0f4d117dab..782cd2a84f 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/message/EventMessageHandler.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/message/EventMessageHandler.kt @@ -1,5 +1,6 @@ package com.tangem.core.ui.message +import androidx.compose.runtime.Stable import com.tangem.core.decompose.ui.UiMessage import com.tangem.core.decompose.ui.UiMessageHandler import com.tangem.core.ui.event.StateEvent @@ -11,6 +12,7 @@ import kotlinx.coroutines.flow.StateFlow /** * Message handler that is used to show or remove an [EventMessage] in the UI. */ +@Stable class EventMessageHandler( private val events: MutableStateFlow> = MutableStateFlow(consumedEvent()), ) : UiMessageHandler, StateFlow> by events { diff --git a/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt b/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt index 4f3287a86d..e5628c9bf5 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/res/TangemTheme.kt @@ -3,19 +3,18 @@ package com.tangem.core.ui.res import androidx.compose.material.Colors import androidx.compose.material.MaterialTheme import androidx.compose.material.ProvideTextStyle +import androidx.compose.material3.SnackbarHostState import androidx.compose.runtime.* import com.tangem.core.ui.haptic.HapticManager import com.tangem.core.ui.haptic.MockHapticManager -// TODO: use isSystemInDarkTheme() for automatic color detection -internal const val IS_SYSTEM_IN_DARK_THEME: Boolean = false - @Composable fun TangemTheme( isDark: Boolean = false, typography: TangemTypography = TangemTheme.typography, dimens: TangemDimens = TangemTheme.dimens, hapticManager: HapticManager = MockHapticManager, + snackbarHostState: SnackbarHostState = remember { SnackbarHostState() }, content: @Composable () -> Unit, ) { val themeColors = if (isDark) darkThemeColors() else lightThemeColors() @@ -33,6 +32,7 @@ fun TangemTheme( LocalTangemShapes provides shapes, LocalIsInDarkTheme provides isDark, LocalHapticManager provides hapticManager, + LocalSnackbarHostState provides snackbarHostState, ) { ProvideTextStyle( value = TangemTheme.typography.body1, @@ -204,4 +204,8 @@ val LocalIsInDarkTheme = staticCompositionLocalOf { false } val LocalHapticManager = staticCompositionLocalOf { error("No HapticManager provided") +} + +val LocalSnackbarHostState = staticCompositionLocalOf { + error("No SnackbarHostState provided") } \ No newline at end of file diff --git a/core/ui/src/main/java/com/tangem/core/ui/screen/ComposeScreen.kt b/core/ui/src/main/java/com/tangem/core/ui/screen/ComposeScreen.kt index 4c2faae280..740eac38e1 100644 --- a/core/ui/src/main/java/com/tangem/core/ui/screen/ComposeScreen.kt +++ b/core/ui/src/main/java/com/tangem/core/ui/screen/ComposeScreen.kt @@ -58,6 +58,7 @@ internal fun ComposeScreen.createComposeView(context: Context): ComposeView { TangemTheme( isDark = shouldUseDarkTheme(appThemeMode), hapticManager = uiDependencies.hapticManager, + snackbarHostState = uiDependencies.globalSnackbarHostState, ) { ScreenContent(modifier = screenModifier) } diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/component/DetailsComponent.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/component/DetailsComponent.kt index 1f4026d32a..2aa9daebfe 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/component/DetailsComponent.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/component/DetailsComponent.kt @@ -2,10 +2,10 @@ package com.tangem.features.details.component import androidx.compose.material3.SnackbarHostState import com.tangem.core.decompose.context.AppComponentContext -import com.tangem.core.ui.ComposableContentProvider +import com.tangem.core.ui.ComposableContentComponent import com.tangem.domain.wallets.models.UserWalletId -interface DetailsComponent : ComposableContentProvider { +interface DetailsComponent : ComposableContentComponent { val snackbarHostState: SnackbarHostState diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/component/UserWalletListComponent.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/component/UserWalletListComponent.kt index 8e7ffc063a..37791d71d0 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/component/UserWalletListComponent.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/component/UserWalletListComponent.kt @@ -1,9 +1,9 @@ package com.tangem.features.details.component import com.tangem.core.decompose.context.AppComponentContext -import com.tangem.core.ui.ComposableContentProvider +import com.tangem.core.ui.ComposableContentComponent -interface UserWalletListComponent : ComposableContentProvider { +interface UserWalletListComponent : ComposableContentComponent { interface Factory { fun create(context: AppComponentContext): UserWalletListComponent diff --git a/features/details/impl/src/main/kotlin/com/tangem/features/details/ui/DetailsScreen.kt b/features/details/impl/src/main/kotlin/com/tangem/features/details/ui/DetailsScreen.kt index c0f66280bc..95413f3fc5 100644 --- a/features/details/impl/src/main/kotlin/com/tangem/features/details/ui/DetailsScreen.kt +++ b/features/details/impl/src/main/kotlin/com/tangem/features/details/ui/DetailsScreen.kt @@ -18,7 +18,7 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview -import com.tangem.core.ui.ComposableContentProvider +import com.tangem.core.ui.ComposableContentComponent import com.tangem.core.ui.components.SystemBarsEffect import com.tangem.core.ui.components.snackbar.TangemSnackbarHost import com.tangem.core.ui.res.TangemTheme @@ -36,7 +36,7 @@ private const val COLLAPSED_APP_BAR_THRESHOLD = 0.4f internal fun DetailsScreen( state: DetailsUM, snackbarHostState: SnackbarHostState, - userWalletListBlockContent: ComposableContentProvider, + userWalletListBlockContent: ComposableContentComponent, modifier: Modifier = Modifier, ) { val backgroundColor = TangemTheme.colors.background.secondary @@ -118,7 +118,7 @@ private fun TopBar(state: DetailsUM, scrollBehavior: TopAppBarScrollBehavior, mo @Composable private fun Content( state: DetailsUM, - userWalletListBlockContent: ComposableContentProvider, + userWalletListBlockContent: ComposableContentComponent, modifier: Modifier = Modifier, ) { LazyColumn( @@ -152,7 +152,7 @@ private fun Content( @Composable private fun Block( model: DetailsItemUM, - userWalletListBlockContent: ComposableContentProvider, + userWalletListBlockContent: ComposableContentComponent, modifier: Modifier = Modifier, ) { Column( diff --git a/features/onboarding/build.gradle.kts b/features/onboarding/build.gradle.kts index 6629611e63..9f081a463f 100644 --- a/features/onboarding/build.gradle.kts +++ b/features/onboarding/build.gradle.kts @@ -42,6 +42,7 @@ dependencies { /** Compose libraries */ implementation(deps.compose.material) + implementation(deps.compose.material3) implementation(deps.compose.animation) implementation(deps.compose.foundation) implementation(deps.compose.ui)