Updated on 2026-08-14
This commit is contained in:
parent
d9d3ba2cda
commit
3a6668eee0
10 changed files with 97 additions and 28 deletions
|
|
@ -35,7 +35,7 @@ internal class DecomposeFragment : ComposeFragment() {
|
|||
)
|
||||
}
|
||||
|
||||
private class ComponentBuilder<C : ComposableContentComponent, P : Any, F : ComponentFactory<C, P>>(
|
||||
private class ComponentBuilder<C : ComposableContentComponent, P : Any, F : ComponentFactory<P, C>>(
|
||||
private val contextProvider: Provider<AppComponentContext>,
|
||||
private val params: P,
|
||||
private val componentFactory: F,
|
||||
|
|
@ -48,7 +48,7 @@ internal class DecomposeFragment : ComposeFragment() {
|
|||
|
||||
private var componentBuilder: ComponentBuilder<*, *, *>? = null
|
||||
|
||||
fun <C : ComposableContentComponent, P : Any, F : ComponentFactory<C, P>> newInstance(
|
||||
fun <C : ComposableContentComponent, P : Any, F : ComponentFactory<P, C>> newInstance(
|
||||
contextProvider: Provider<AppComponentContext>,
|
||||
params: P,
|
||||
componentFactory: F,
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ internal fun AppRoute.asFragmentChild(fragmentProvider: Provider<Fragment>): Chi
|
|||
return Child.LegacyFragment(path, provider)
|
||||
}
|
||||
|
||||
internal fun <C : ComposableContentComponent, P : Any, F : ComponentFactory<C, P>> AppRoute.asComponentChild(
|
||||
internal fun <C : ComposableContentComponent, P : Any, F : ComponentFactory<P, C>> AppRoute.asComponentChild(
|
||||
contextProvider: Provider<AppComponentContext>,
|
||||
params: P,
|
||||
componentFactory: F,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.tangem.core.decompose.di
|
||||
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.core.decompose.ui.UiMessageSender
|
||||
import dagger.BindsInstance
|
||||
|
|
@ -37,6 +38,14 @@ interface DecomposeComponent {
|
|||
*/
|
||||
fun uiMessageSender(@BindsInstance uiMessageSender: UiMessageSender): Builder
|
||||
|
||||
/**
|
||||
* Sets the parameters container for the component.
|
||||
*
|
||||
* @param container The parameters container to set.
|
||||
* @return The builder instance.
|
||||
*/
|
||||
fun paramsContainer(@BindsInstance container: ParamsContainer): Builder
|
||||
|
||||
/**
|
||||
* Builds the Decompose component.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package com.tangem.core.decompose.factory
|
|||
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
|
||||
interface ComponentFactory<Component : Any, Params : Any> {
|
||||
interface ComponentFactory<P : Any, C : Any> {
|
||||
|
||||
fun create(context: AppComponentContext, params: Params): Component
|
||||
fun create(context: AppComponentContext, params: P): C
|
||||
}
|
||||
|
|
@ -22,24 +22,43 @@ interface ModelsEntryPoint {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets or creates a component's [Model].
|
||||
* Gets or creates a component's [Model] with no parameters.
|
||||
*/
|
||||
inline fun <reified M : Model> AppComponentContext.getOrCreateModel(): M {
|
||||
val modelKey = "model_${M::class.simpleName}"
|
||||
inline fun <reified M : Model> AppComponentContext.getOrCreateModel(): M = getOrCreateModel(params = null)
|
||||
|
||||
/**
|
||||
* Gets or creates a component's [Model].
|
||||
*
|
||||
* Be careful with objects you pass in parameters as they will be used in [Model] lifecycle.
|
||||
* If you pass a object with different lifecycle than the [Model] then you can face memory leaks.
|
||||
*
|
||||
* @param params The parameters to store in the [ParamsContainer],
|
||||
|
||||
*/
|
||||
inline fun <reified M : Model, reified P : Any> AppComponentContext.getOrCreateModel(params: P?): M {
|
||||
val entryPoint = instanceKeeper.getOrCreateSimple(key = "modelsEntryPoint") {
|
||||
val hiltComponent = hiltComponentBuilder
|
||||
.router(router)
|
||||
.uiMessageSender(messageSender)
|
||||
.let { builder ->
|
||||
if (params != null) {
|
||||
val container = MutableParamsContainer(params)
|
||||
|
||||
builder.paramsContainer(container)
|
||||
} else {
|
||||
builder
|
||||
}
|
||||
}
|
||||
.build()
|
||||
|
||||
EntryPoints.get(hiltComponent, ModelsEntryPoint::class.java)
|
||||
}
|
||||
|
||||
val modelKey = "model_${M::class.simpleName}"
|
||||
val model = instanceKeeper.getOrCreate(modelKey) {
|
||||
requireNotNull(entryPoint.models()[M::class.java]?.get()) {
|
||||
"Model ${M::class.simpleName} is not provided"
|
||||
}
|
||||
} as M
|
||||
}
|
||||
|
||||
val isModelExist = tags.getOrElse(modelKey) { false } as Boolean
|
||||
|
|
@ -47,5 +66,5 @@ inline fun <reified M : Model> AppComponentContext.getOrCreateModel(): M {
|
|||
tags[modelKey] = true
|
||||
}
|
||||
|
||||
return model as M
|
||||
return model
|
||||
}
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package com.tangem.core.decompose.model
|
||||
|
||||
/**
|
||||
* Lazy container for [Model] params.
|
||||
*
|
||||
* This contrainer can be accessed by DI because it's provided via [com.tangem.core.decompose.di.DecomposeComponent].
|
||||
* */
|
||||
interface ParamsContainer {
|
||||
|
||||
/** Returns stored value if it is of type [T], otherwise returns null. */
|
||||
fun <T> get(): T?
|
||||
|
||||
/** Returns stored value if it is of type [T], otherwise throws an exception. */
|
||||
fun <T> require(): T
|
||||
}
|
||||
|
||||
/**
|
||||
* Mutable implementation of [ParamsContainer].
|
||||
*
|
||||
* ***You should not use this class directly in other modules, use immutable [ParamsContainer] instead.***
|
||||
* */
|
||||
class MutableParamsContainer private constructor() : ParamsContainer {
|
||||
|
||||
private var value: Any? = null
|
||||
|
||||
/** Stores [value] inside a container, replaces any previous stored value. */
|
||||
fun set(value: Any) {
|
||||
this.value = value
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
override fun <T> get(): T? = value as? T
|
||||
|
||||
override fun <T> require(): T = get() ?: error("Contrainer is empty or contains a value of a different type.")
|
||||
|
||||
companion object {
|
||||
|
||||
/** Creates a new [MutableParamsContainer] and stores [value] inside it. */
|
||||
operator fun <T : Any> invoke(value: T): MutableParamsContainer {
|
||||
return MutableParamsContainer().apply {
|
||||
set(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -16,7 +16,4 @@ dependencies {
|
|||
/* Project - Core */
|
||||
implementation(projects.core.decompose)
|
||||
implementation(projects.core.ui)
|
||||
|
||||
/* AndroidX */
|
||||
implementation(deps.androidx.fragment.ktx)
|
||||
}
|
||||
|
|
@ -1,18 +1,14 @@
|
|||
package com.tangem.features.details.component
|
||||
|
||||
import com.tangem.core.decompose.context.AppComponentContext
|
||||
import com.tangem.core.decompose.factory.ComponentFactory
|
||||
import com.tangem.core.ui.ComposableContentComponent
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
|
||||
interface DetailsComponent : ComposableContentComponent {
|
||||
|
||||
interface Factory : ComponentFactory<DetailsComponent, Params> {
|
||||
|
||||
override fun create(context: AppComponentContext, params: Params): DetailsComponent
|
||||
}
|
||||
interface Factory : ComponentFactory<Params, DetailsComponent>
|
||||
|
||||
data class Params(
|
||||
val selectedUserWalletId: UserWalletId,
|
||||
val userWalletId: UserWalletId,
|
||||
)
|
||||
}
|
||||
|
|
@ -17,20 +17,16 @@ import dagger.assisted.AssistedInject
|
|||
|
||||
internal class DefaultDetailsComponent @AssistedInject constructor(
|
||||
@Assisted context: AppComponentContext,
|
||||
@Assisted private val params: DetailsComponent.Params,
|
||||
@Assisted params: DetailsComponent.Params,
|
||||
userWalletListComponentFactory: UserWalletListComponent.Factory,
|
||||
) : DetailsComponent, AppComponentContext by context {
|
||||
|
||||
private val model: DetailsModel = getOrCreateModel()
|
||||
private val model: DetailsModel = getOrCreateModel(params)
|
||||
|
||||
private val userWalletListComponent = userWalletListComponentFactory.create(
|
||||
context = child(key = "user_wallet_list"),
|
||||
)
|
||||
|
||||
init {
|
||||
model.provideUserWalletId(params.selectedUserWalletId)
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun Content(modifier: Modifier) {
|
||||
val state by model.state.collectAsStateWithLifecycle()
|
||||
|
|
|
|||
|
|
@ -3,9 +3,10 @@ package com.tangem.features.details.model
|
|||
import arrow.core.getOrElse
|
||||
import com.tangem.core.decompose.di.ComponentScoped
|
||||
import com.tangem.core.decompose.model.Model
|
||||
import com.tangem.core.decompose.model.ParamsContainer
|
||||
import com.tangem.core.decompose.navigation.Router
|
||||
import com.tangem.domain.walletconnect.CheckIsWalletConnectAvailableUseCase
|
||||
import com.tangem.domain.wallets.models.UserWalletId
|
||||
import com.tangem.features.details.component.DetailsComponent
|
||||
import com.tangem.features.details.entity.DetailsFooterUM
|
||||
import com.tangem.features.details.entity.DetailsItemUM
|
||||
import com.tangem.features.details.entity.DetailsUM
|
||||
|
|
@ -24,15 +25,19 @@ import timber.log.Timber
|
|||
import javax.inject.Inject
|
||||
|
||||
@ComponentScoped
|
||||
@Suppress("LongParameterList")
|
||||
internal class DetailsModel @Inject constructor(
|
||||
private val socialsBuilder: SocialsBuilder,
|
||||
private val itemsBuilder: ItemsBuilder,
|
||||
private val appVersionProvider: AppVersionProvider,
|
||||
private val checkIsWalletConnectAvailableUseCase: CheckIsWalletConnectAvailableUseCase,
|
||||
private val router: Router,
|
||||
private val paramsContainer: ParamsContainer,
|
||||
override val dispatchers: CoroutineDispatcherProvider,
|
||||
) : Model() {
|
||||
|
||||
private val params: DetailsComponent.Params = paramsContainer.require()
|
||||
|
||||
private val items: MutableStateFlow<ImmutableList<DetailsItemUM>> = MutableStateFlow(value = persistentListOf())
|
||||
|
||||
val state: MutableStateFlow<DetailsUM> = MutableStateFlow(
|
||||
|
|
@ -50,10 +55,12 @@ internal class DetailsModel @Inject constructor(
|
|||
items
|
||||
.onEach(::updateState)
|
||||
.launchIn(modelScope)
|
||||
|
||||
checkWalletConnectAvailability()
|
||||
}
|
||||
|
||||
fun provideUserWalletId(userWalletId: UserWalletId) = modelScope.launch {
|
||||
val isWalletConnectAvailable = checkIsWalletConnectAvailableUseCase(userWalletId).getOrElse {
|
||||
private fun checkWalletConnectAvailability() = modelScope.launch {
|
||||
val isWalletConnectAvailable = checkIsWalletConnectAvailableUseCase(params.userWalletId).getOrElse {
|
||||
Timber.w("Unable to check WalletConnect availability: $it")
|
||||
|
||||
false
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue