Updated on 2026-08-14
This commit is contained in:
parent
ba4ceb2e8b
commit
69aeaf5776
12 changed files with 96 additions and 23 deletions
61
app/src/main/java/com/tangem/tap/DecomposeFragment.kt
Normal file
61
app/src/main/java/com/tangem/tap/DecomposeFragment.kt
Normal file
|
|
@ -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<C : ComposableContentComponent, P : Any, F : ComponentFactory<C, P>>(
|
||||
private val contextProvider: Provider<AppComponentContext>,
|
||||
private val params: P,
|
||||
private val componentFactory: F,
|
||||
) {
|
||||
|
||||
fun build(): C = componentFactory.create(contextProvider(), params)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private var componentBuilder: ComponentBuilder<*, *, *>? = null
|
||||
|
||||
fun <C : ComposableContentComponent, P : Any, F : ComponentFactory<C, P>> newInstance(
|
||||
contextProvider: Provider<AppComponentContext>,
|
||||
params: P,
|
||||
componentFactory: F,
|
||||
): Fragment {
|
||||
this@Companion.componentBuilder = ComponentBuilder(contextProvider, params, componentFactory)
|
||||
|
||||
return DecomposeFragment()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
@ -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<StateEvent<EventMessage>> = MutableStateFlow(consumedEvent()),
|
||||
) : UiMessageHandler, StateFlow<StateEvent<EventMessage>> by events {
|
||||
|
|
|
|||
|
|
@ -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<HapticManager> {
|
||||
error("No HapticManager provided")
|
||||
}
|
||||
|
||||
val LocalSnackbarHostState = staticCompositionLocalOf<SnackbarHostState> {
|
||||
error("No SnackbarHostState provided")
|
||||
}
|
||||
|
|
@ -58,6 +58,7 @@ internal fun ComposeScreen.createComposeView(context: Context): ComposeView {
|
|||
TangemTheme(
|
||||
isDark = shouldUseDarkTheme(appThemeMode),
|
||||
hapticManager = uiDependencies.hapticManager,
|
||||
snackbarHostState = uiDependencies.globalSnackbarHostState,
|
||||
) {
|
||||
ScreenContent(modifier = screenModifier)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue