Other cleanup

This commit is contained in:
Benoit Marty 2025-09-05 14:58:31 +02:00 committed by Benoit Marty
parent ad8ad3d443
commit 8d533e8a20
11 changed files with 14 additions and 20 deletions

View file

@ -36,7 +36,7 @@ import io.element.android.libraries.matrix.ui.media.ImageLoaderHolder
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
/** /**
* `LoggedInAppScopeFlowNode` is a Node responsible to set up the Dagger * `LoggedInAppScopeFlowNode` is a Node responsible to set up the Session graph.
* [io.element.android.libraries.di.SessionScope]. It has only one child: [LoggedInFlowNode]. * [io.element.android.libraries.di.SessionScope]. It has only one child: [LoggedInFlowNode].
* This allow to inject objects with SessionScope in the constructor of [LoggedInFlowNode]. * This allow to inject objects with SessionScope in the constructor of [LoggedInFlowNode].
*/ */

View file

@ -260,7 +260,7 @@ Here are the main points:
2. Views are compose first 2. Views are compose first
3. Presenters are also compose first, and have a single `present(): State` method. It's using the power of compose-runtime/compiler. 3. Presenters are also compose first, and have a single `present(): State` method. It's using the power of compose-runtime/compiler.
4. The point of connection between a `View` and a `Presenter` is a `Node`. 4. The point of connection between a `View` and a `Presenter` is a `Node`.
5. A `Node` is also responsible for managing Dagger components if any. 5. A `Node` is also responsible for managing DI graph if any, see for instance `LoggedInAppScopeFlowNode`.
6. A `ParentNode` has some children `Node` and only know about them. 6. A `ParentNode` has some children `Node` and only know about them.
7. This is a single activity full compose application. The `MainActivity` is responsible for holding and configuring the `RootNode`. 7. This is a single activity full compose application. The `MainActivity` is responsible for holding and configuring the `RootNode`.
8. There is no more needs for Android Architecture Component ViewModel as configuration change should be handled by Composable if needed. 8. There is no more needs for Android Architecture Component ViewModel as configuration change should be handled by Composable if needed.
@ -422,7 +422,7 @@ Rageshake can be very useful to get logs from a release version of the applicati
- When this is possible, prefer using `sealed interface` instead of `sealed class`; - When this is possible, prefer using `sealed interface` instead of `sealed class`;
- When writing temporary code, using the string "DO NOT COMMIT" in a comment can help to avoid committing things by mistake. If committed and pushed, the CI - When writing temporary code, using the string "DO NOT COMMIT" in a comment can help to avoid committing things by mistake. If committed and pushed, the CI
will detect this String and will warn the user about it. (TODO Not supported yet!) will detect this String and will warn the user about it. (TODO Not supported yet!)
- Very occasionally the gradle cache misbehaves and causes problems with Dagger. Try building with `--no-build-cache` if Dagger isn't behaving how you expect. - Very occasionally the gradle cache misbehaves and causes problems with code generation. Adding `--no-build-cache` to the `gradlew` command line can help to fix compilation issue.
## Happy coding! ## Happy coding!

View file

@ -13,7 +13,7 @@ import kotlin.reflect.KClass
/** /**
* Annotation to add a factory of type [TimelineItemPresenterFactory] to a * Annotation to add a factory of type [TimelineItemPresenterFactory] to a
* Dagger map multi binding keyed with a subclass of [TimelineItemEventContent]. * dependency injection map multi binding keyed with a subclass of [TimelineItemEventContent].
*/ */
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
@MapKey @MapKey

View file

@ -20,7 +20,7 @@ import io.element.android.libraries.di.RoomScope
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
* Dagger module that declares the [TimelineItemPresenterFactory] map multi binding. * Container that declares the [TimelineItemPresenterFactory] map multi binding.
* *
* Its sole purpose is to support the case of an empty map multibinding. * Its sole purpose is to support the case of an empty map multibinding.
*/ */

View file

@ -13,7 +13,7 @@ import io.element.android.libraries.architecture.Presenter
/** /**
* A factory for a [Presenter] associated with a timeline item. * A factory for a [Presenter] associated with a timeline item.
* *
* Implementations should be annotated with [AssistedFactory] to be created by Dagger. * Implementations should be annotated with [dev.zacsweers.metro.AssistedFactory] to be created by the dependency injection library.
* *
* @param C The timeline item's [TimelineItemEventContent] subtype. * @param C The timeline item's [TimelineItemEventContent] subtype.
* @param S The [Presenter]'s state class. * @param S The [Presenter]'s state class.

View file

@ -12,12 +12,11 @@ import android.content.ContextWrapper
import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.Node
import io.element.android.libraries.di.DependencyInjectionGraphOwner import io.element.android.libraries.di.DependencyInjectionGraphOwner
inline fun <reified T : Any> Node.optionalBindings() = optionalBindings(T::class.java)
inline fun <reified T : Any> Node.bindings() = bindings(T::class.java) inline fun <reified T : Any> Node.bindings() = bindings(T::class.java)
inline fun <reified T : Any> Context.bindings() = bindings(T::class.java) inline fun <reified T : Any> Context.bindings() = bindings(T::class.java)
fun <T : Any> Context.bindings(klass: Class<T>): T { fun <T : Any> Context.bindings(klass: Class<T>): T {
// search dagger components in the context hierarchy // search the components in the dependency injection graph
return generateSequence(this) { (it as? ContextWrapper)?.baseContext } return generateSequence(this) { (it as? ContextWrapper)?.baseContext }
.plus(applicationContext) .plus(applicationContext)
.filterIsInstance<DependencyInjectionGraphOwner>() .filterIsInstance<DependencyInjectionGraphOwner>()
@ -28,16 +27,13 @@ fun <T : Any> Context.bindings(klass: Class<T>): T {
?: error("Unable to find bindings for ${klass.name}") ?: error("Unable to find bindings for ${klass.name}")
} }
fun <T : Any> Node.optionalBindings(klass: Class<T>): T? { fun <T : Any> Node.bindings(klass: Class<T>): T {
// search dagger components in node hierarchy // search the components in the node hierarchy
return generateSequence(this, Node::parent) return generateSequence(this, Node::parent)
.filterIsInstance<DependencyInjectionGraphOwner>() .filterIsInstance<DependencyInjectionGraphOwner>()
.map { it.graph } .map { it.graph }
.flatMap { it as? Collection<*> ?: listOf(it) } .flatMap { it as? Collection<*> ?: listOf(it) }
.filterIsInstance(klass) .filterIsInstance(klass)
.firstOrNull() .firstOrNull()
} ?: error("Unable to find bindings for ${klass.name}")
fun <T : Any> Node.bindings(klass: Class<T>): T {
return optionalBindings(klass) ?: error("Unable to find bindings for ${klass.name}")
} }

View file

@ -37,7 +37,7 @@ inline fun <reified N : Node> NodeFactoriesBindings.createNode(
val nodeClass = N::class val nodeClass = N::class
val nodeFactoryMap = nodeFactories() val nodeFactoryMap = nodeFactories()
// Note to developers: If you got the error below, make sure to build again after // Note to developers: If you got the error below, make sure to build again after
// clearing the cache (sometimes several times) to let Dagger generate the NodeFactory. // clearing the cache (sometimes several times) to let codegen generate the NodeFactory.
val nodeFactory = nodeFactoryMap[nodeClass] ?: error("Cannot find NodeFactory for ${nodeClass.java.name}.") val nodeFactory = nodeFactoryMap[nodeClass] ?: error("Cannot find NodeFactory for ${nodeClass.java.name}.")
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")

View file

@ -13,7 +13,7 @@ import kotlin.reflect.KClass
/** /**
* Annotation to add a factory of type [MediaItemPresenterFactory] to a * Annotation to add a factory of type [MediaItemPresenterFactory] to a
* Dagger map multi binding keyed with a subclass of [MediaItem.Event]. * DI map multi binding keyed with a subclass of [MediaItem.Event].
*/ */
@Retention(AnnotationRetention.RUNTIME) @Retention(AnnotationRetention.RUNTIME)
@MapKey @MapKey

View file

@ -20,7 +20,7 @@ import io.element.android.libraries.mediaviewer.impl.model.MediaItem
import kotlin.reflect.KClass import kotlin.reflect.KClass
/** /**
* Dagger module that declares the [MediaItemPresenterFactory] map multi binding. * Container that declares the [MediaItemPresenterFactory] map multi binding.
* *
* Its sole purpose is to support the case of an empty map multibinding. * Its sole purpose is to support the case of an empty map multibinding.
*/ */

View file

@ -13,7 +13,7 @@ import io.element.android.libraries.mediaviewer.impl.model.MediaItem
/** /**
* A factory for a [Presenter] associated with a timeline item. * A factory for a [Presenter] associated with a timeline item.
* *
* Implementations should be annotated with [AssistedFactory] to be created by Dagger. * Implementations should be annotated with [dev.zacsweers.metro.AssistedFactory] to be created.
* *
* @param C The timeline item's [MediaItem.Event] subtype. * @param C The timeline item's [MediaItem.Event] subtype.
* @param S The [Presenter]'s state class. * @param S The [Presenter]'s state class.

View file

@ -67,14 +67,12 @@ fun Project.setupKover() {
"*_ModuleKt", "*_ModuleKt",
"com.airbnb.android.showkase*", "com.airbnb.android.showkase*",
"io.element.android.libraries.designsystem.showkase.*", "io.element.android.libraries.designsystem.showkase.*",
"io.element.android.x.di.DaggerAppComponent*",
"*_Factory", "*_Factory",
"*_Factory_Impl", "*_Factory_Impl",
"*_Factory$*", "*_Factory$*",
"*_Module", "*_Module",
"*_Module$*", "*_Module$*",
"*Module_Provides*", "*Module_Provides*",
"Dagger*Component*",
"*ComposableSingletons$*", "*ComposableSingletons$*",
"*_AssistedFactory_Impl*", "*_AssistedFactory_Impl*",
"*BuildConfig", "*BuildConfig",