Merge pull request #1378 from vector-im/feature/fga/fix_nav_restoration

DI: rework how components are created and provided
This commit is contained in:
ganfra 2023-09-19 20:58:50 +02:00 committed by GitHub
commit 04c1fa5e69
15 changed files with 131 additions and 129 deletions

View file

@ -34,8 +34,8 @@ import com.bumble.appyx.core.integrationpoint.NodeComponentActivity
import com.bumble.appyx.core.plugin.NodeReadyObserver import com.bumble.appyx.core.plugin.NodeReadyObserver
import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.core.log.logger.LoggerTag import io.element.android.libraries.core.log.logger.LoggerTag
import io.element.android.libraries.theme.ElementTheme
import io.element.android.libraries.designsystem.utils.LocalSnackbarDispatcher import io.element.android.libraries.designsystem.utils.LocalSnackbarDispatcher
import io.element.android.libraries.theme.ElementTheme
import io.element.android.x.di.AppBindings import io.element.android.x.di.AppBindings
import io.element.android.x.intent.SafeUriHandler import io.element.android.x.intent.SafeUriHandler
import timber.log.Timber import timber.log.Timber
@ -74,7 +74,6 @@ class MainActivity : NodeComponentActivity() {
NodeHost(integrationPoint = appyxIntegrationPoint) { NodeHost(integrationPoint = appyxIntegrationPoint) {
MainNode( MainNode(
it, it,
appBindings.mainDaggerComponentOwner(),
plugins = listOf( plugins = listOf(
object : NodeReadyObserver<MainNode> { object : NodeReadyObserver<MainNode> {
override fun init(node: MainNode) { override fun init(node: MainNode) {
@ -83,7 +82,8 @@ class MainActivity : NodeComponentActivity() {
mainNode.handleIntent(intent) mainNode.handleIntent(intent)
} }
} }
) ),
context = applicationContext
) )
} }
} }

View file

@ -16,6 +16,7 @@
package io.element.android.x package io.element.android.x
import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Parcelable import android.os.Parcelable
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@ -27,24 +28,17 @@ import com.bumble.appyx.core.navigation.model.permanent.PermanentNavModel
import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.node.ParentNode import com.bumble.appyx.core.node.ParentNode
import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.Plugin
import io.element.android.appnav.LoggedInAppScopeFlowNode
import io.element.android.appnav.RootFlowNode import io.element.android.appnav.RootFlowNode
import io.element.android.appnav.room.RoomLoadedFlowNode
import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.DaggerComponentOwner import io.element.android.libraries.di.DaggerComponentOwner
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.x.di.MainDaggerComponentsOwner
import io.element.android.x.di.RoomComponent
import io.element.android.x.di.SessionComponent
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
class MainNode( class MainNode(
buildContext: BuildContext, buildContext: BuildContext,
private val mainDaggerComponentOwner: MainDaggerComponentsOwner,
plugins: List<Plugin>, plugins: List<Plugin>,
@ApplicationContext context: Context,
) : ParentNode<MainNode.RootNavTarget>( ) : ParentNode<MainNode.RootNavTarget>(
navModel = PermanentNavModel( navModel = PermanentNavModel(
navTargets = setOf(RootNavTarget), navTargets = setOf(RootNavTarget),
@ -53,38 +47,12 @@ class MainNode(
buildContext = buildContext, buildContext = buildContext,
plugins = plugins, plugins = plugins,
), ),
DaggerComponentOwner by mainDaggerComponentOwner { DaggerComponentOwner {
private val loggedInFlowNodeCallback = object : LoggedInAppScopeFlowNode.LifecycleCallback { override val daggerComponent = (context as DaggerComponentOwner).daggerComponent
override fun onFlowCreated(identifier: String, client: MatrixClient) {
val component = bindings<SessionComponent.ParentBindings>().sessionComponentBuilder().client(client).build()
mainDaggerComponentOwner.addComponent(identifier, component)
}
override fun onFlowReleased(identifier: String, client: MatrixClient) {
mainDaggerComponentOwner.removeComponent(identifier)
}
}
private val roomFlowNodeCallback = object : RoomLoadedFlowNode.LifecycleCallback {
override fun onFlowCreated(identifier: String, room: MatrixRoom) {
val component = bindings<RoomComponent.ParentBindings>().roomComponentBuilder().room(room).build()
mainDaggerComponentOwner.addComponent(identifier, component)
}
override fun onFlowReleased(identifier: String, room: MatrixRoom) {
mainDaggerComponentOwner.removeComponent(identifier)
}
}
override fun resolve(navTarget: RootNavTarget, buildContext: BuildContext): Node { override fun resolve(navTarget: RootNavTarget, buildContext: BuildContext): Node {
return createNode<RootFlowNode>( return createNode<RootFlowNode>(context = buildContext)
context = buildContext,
plugins = listOf(
loggedInFlowNodeCallback,
roomFlowNodeCallback,
)
)
} }
@Composable @Composable
@ -100,4 +68,5 @@ class MainNode(
@Parcelize @Parcelize
object RootNavTarget : Parcelable object RootNavTarget : Parcelable
} }

View file

@ -24,7 +24,6 @@ import io.element.android.libraries.matrix.api.tracing.TracingService
@ContributesTo(AppScope::class) @ContributesTo(AppScope::class)
interface AppBindings { interface AppBindings {
fun mainDaggerComponentOwner(): MainDaggerComponentsOwner
fun snackbarDispatcher(): SnackbarDispatcher fun snackbarDispatcher(): SnackbarDispatcher
fun tracingService(): TracingService fun tracingService(): TracingService
fun bugReporter(): BugReporter fun bugReporter(): BugReporter

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.x.di
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.appnav.di.RoomComponentFactory
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.room.MatrixRoom
import javax.inject.Inject
@ContributesBinding(SessionScope::class)
class DefaultRoomComponentFactory @Inject constructor(
private val roomComponentBuilder: RoomComponent.Builder
) : RoomComponentFactory {
override fun create(room: MatrixRoom): Any {
return roomComponentBuilder.room(room).build()
}
}

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.x.di
import com.squareup.anvil.annotations.ContributesBinding
import io.element.android.appnav.di.SessionComponentFactory
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.matrix.api.MatrixClient
import javax.inject.Inject
@ContributesBinding(AppScope::class)
class DefaultSessionComponentFactory @Inject constructor(
private val sessionComponentBuilder: SessionComponent.Builder
) : SessionComponentFactory {
override fun create(client: MatrixClient): Any {
return sessionComponentBuilder.client(client).build()
}
}

View file

@ -1,47 +0,0 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.x.di
import android.content.Context
import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.ApplicationContext
import io.element.android.libraries.di.DaggerComponentOwner
import io.element.android.libraries.di.SingleIn
import javax.inject.Inject
@SingleIn(AppScope::class)
class MainDaggerComponentsOwner @Inject constructor(@ApplicationContext context: Context) : DaggerComponentOwner {
private val daggerComponents = LinkedHashMap<String, Any>().apply {
put("app", (context as DaggerComponentOwner).daggerComponent)
}
fun addComponent(identifier: String, component: Any) {
daggerComponents[identifier] = component
}
fun removeComponent(identifier: String) {
daggerComponents.remove(identifier)
}
/**
* We expose the dagger components in the opposite order they arrived.
* So we pick the most recent component when searching with the [io.element.android.libraries.architecture.bindings] methods.
*/
override val daggerComponent: Any
get() = daggerComponents.values.reversed()
}

View file

@ -31,12 +31,13 @@ import com.bumble.appyx.core.plugin.plugins
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode import io.element.android.anvilannotations.ContributesNode
import io.element.android.appnav.di.SessionComponentFactory
import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.bindings import io.element.android.libraries.architecture.bindings
import io.element.android.libraries.architecture.createNode import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.architecture.inputs import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.architecture.waitForChildAttached
import io.element.android.libraries.di.AppScope import io.element.android.libraries.di.AppScope
import io.element.android.libraries.di.DaggerComponentOwner
import io.element.android.libraries.matrix.api.MatrixClient import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.ui.di.MatrixUIBindings import io.element.android.libraries.matrix.ui.di.MatrixUIBindings
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@ -50,6 +51,7 @@ import kotlinx.parcelize.Parcelize
class LoggedInAppScopeFlowNode @AssistedInject constructor( class LoggedInAppScopeFlowNode @AssistedInject constructor(
@Assisted buildContext: BuildContext, @Assisted buildContext: BuildContext,
@Assisted plugins: List<Plugin>, @Assisted plugins: List<Plugin>,
sessionComponentFactory: SessionComponentFactory,
) : ParentNode<LoggedInAppScopeFlowNode.NavTarget>( ) : ParentNode<LoggedInAppScopeFlowNode.NavTarget>(
navModel = PermanentNavModel( navModel = PermanentNavModel(
navTargets = setOf(NavTarget), navTargets = setOf(NavTarget),
@ -57,7 +59,7 @@ class LoggedInAppScopeFlowNode @AssistedInject constructor(
), ),
buildContext = buildContext, buildContext = buildContext,
plugins = plugins plugins = plugins
) { ), DaggerComponentOwner {
interface Callback : Plugin { interface Callback : Plugin {
fun onOpenBugReport() fun onOpenBugReport()
} }
@ -65,29 +67,20 @@ class LoggedInAppScopeFlowNode @AssistedInject constructor(
@Parcelize @Parcelize
object NavTarget : Parcelable object NavTarget : Parcelable
interface LifecycleCallback : NodeLifecycleCallback {
fun onFlowCreated(identifier: String, client: MatrixClient)
fun onFlowReleased(identifier: String, client: MatrixClient)
}
data class Inputs( data class Inputs(
val matrixClient: MatrixClient val matrixClient: MatrixClient
) : NodeInputs ) : NodeInputs
private val inputs: Inputs = inputs() private val inputs: Inputs = inputs()
override val daggerComponent = sessionComponentFactory.create(inputs.matrixClient)
override fun onBuilt() { override fun onBuilt() {
super.onBuilt() super.onBuilt()
lifecycle.subscribe( lifecycle.subscribe(
onCreate = { onCreate = {
plugins<LifecycleCallback>().forEach { it.onFlowCreated(id, inputs.matrixClient) }
val imageLoaderFactory = bindings<MatrixUIBindings>().loggedInImageLoaderFactory() val imageLoaderFactory = bindings<MatrixUIBindings>().loggedInImageLoaderFactory()
Coil.setImageLoader(imageLoaderFactory) Coil.setImageLoader(imageLoaderFactory)
}, },
onDestroy = {
plugins<LifecycleCallback>().forEach { it.onFlowReleased(id, inputs.matrixClient) }
}
) )
} }
@ -97,13 +90,10 @@ class LoggedInAppScopeFlowNode @AssistedInject constructor(
plugins<Callback>().forEach { it.onOpenBugReport() } plugins<Callback>().forEach { it.onOpenBugReport() }
} }
} }
val nodeLifecycleCallbacks = plugins<NodeLifecycleCallback>() return createNode<LoggedInFlowNode>(buildContext, listOf(callback))
return createNode<LoggedInFlowNode>(buildContext, nodeLifecycleCallbacks + callback)
} }
suspend fun attachSession(): LoggedInFlowNode { suspend fun attachSession(): LoggedInFlowNode = waitForChildAttached()
return waitForChildAttached { _ -> true }
}
@Composable @Composable
override fun View(modifier: Modifier) { override fun View(modifier: Modifier) {

View file

@ -227,14 +227,13 @@ class LoggedInFlowNode @AssistedInject constructor(
.build() .build()
} }
is NavTarget.Room -> { is NavTarget.Room -> {
val nodeLifecycleCallbacks = plugins<NodeLifecycleCallback>()
val callback = object : RoomLoadedFlowNode.Callback { val callback = object : RoomLoadedFlowNode.Callback {
override fun onForwardedToSingleRoom(roomId: RoomId) { override fun onForwardedToSingleRoom(roomId: RoomId) {
coroutineScope.launch { attachRoom(roomId) } coroutineScope.launch { attachRoom(roomId) }
} }
} }
val inputs = RoomFlowNode.Inputs(roomId = navTarget.roomId, initialElement = navTarget.initialElement) val inputs = RoomFlowNode.Inputs(roomId = navTarget.roomId, initialElement = navTarget.initialElement)
createNode<RoomFlowNode>(buildContext, plugins = listOf(inputs, callback) + nodeLifecycleCallbacks) createNode<RoomFlowNode>(buildContext, plugins = listOf(inputs, callback))
} }
NavTarget.Settings -> { NavTarget.Settings -> {
val callback = object : PreferencesEntryPoint.Callback { val callback = object : PreferencesEntryPoint.Callback {

View file

@ -29,7 +29,6 @@ import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.node.node import com.bumble.appyx.core.node.node
import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.core.state.MutableSavedStateMap import com.bumble.appyx.core.state.MutableSavedStateMap
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.pop import com.bumble.appyx.navmodel.backstack.operation.pop
@ -196,8 +195,7 @@ class RootFlowNode @AssistedInject constructor(
backstack.push(NavTarget.BugReport) backstack.push(NavTarget.BugReport)
} }
} }
val nodeLifecycleCallbacks = plugins<NodeLifecycleCallback>() createNode<LoggedInAppScopeFlowNode>(buildContext, plugins = listOf(inputs, callback))
createNode<LoggedInAppScopeFlowNode>(buildContext, plugins = listOf(inputs, callback) + nodeLifecycleCallbacks)
} }
NavTarget.NotLoggedInFlow -> createNode<NotLoggedInFlowNode>(buildContext) NavTarget.NotLoggedInFlow -> createNode<NotLoggedInFlowNode>(buildContext)
NavTarget.SplashScreen -> splashNode(buildContext) NavTarget.SplashScreen -> splashNode(buildContext)

View file

@ -14,8 +14,10 @@
* limitations under the License. * limitations under the License.
*/ */
package io.element.android.appnav package io.element.android.appnav.di
import com.bumble.appyx.core.plugin.Plugin import io.element.android.libraries.matrix.api.room.MatrixRoom
interface NodeLifecycleCallback : Plugin interface RoomComponentFactory {
fun create(room: MatrixRoom): Any
}

View file

@ -0,0 +1,23 @@
/*
* Copyright (c) 2023 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.appnav.di
import io.element.android.libraries.matrix.api.MatrixClient
interface SessionComponentFactory {
fun create(client: MatrixClient): Any
}

View file

@ -36,7 +36,6 @@ import com.bumble.appyx.navmodel.backstack.operation.newRoot
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode import io.element.android.anvilannotations.ContributesNode
import io.element.android.appnav.NodeLifecycleCallback
import io.element.android.features.networkmonitor.api.NetworkMonitor import io.element.android.features.networkmonitor.api.NetworkMonitor
import io.element.android.features.networkmonitor.api.NetworkStatus import io.element.android.features.networkmonitor.api.NetworkStatus
import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.BackstackNode
@ -103,12 +102,11 @@ class RoomFlowNode @AssistedInject constructor(
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node { override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
return when (navTarget) { return when (navTarget) {
NavTarget.Loaded -> { NavTarget.Loaded -> {
val nodeLifecycleCallbacks = plugins<NodeLifecycleCallback>()
val roomFlowNodeCallback = plugins<RoomLoadedFlowNode.Callback>() val roomFlowNodeCallback = plugins<RoomLoadedFlowNode.Callback>()
val awaitRoomState = loadingRoomStateStateFlow.value val awaitRoomState = loadingRoomStateStateFlow.value
if (awaitRoomState is LoadingRoomState.Loaded) { if (awaitRoomState is LoadingRoomState.Loaded) {
val inputs = RoomLoadedFlowNode.Inputs(awaitRoomState.room, initialElement = inputs.initialElement) val inputs = RoomLoadedFlowNode.Inputs(awaitRoomState.room, initialElement = inputs.initialElement)
createNode<RoomLoadedFlowNode>(buildContext, plugins = listOf(inputs) + roomFlowNodeCallback + nodeLifecycleCallbacks) createNode<RoomLoadedFlowNode>(buildContext, plugins = listOf(inputs) + roomFlowNodeCallback)
} else { } else {
loadingNode(buildContext, this::navigateUp) loadingNode(buildContext, this::navigateUp)
} }

View file

@ -27,19 +27,19 @@ import com.bumble.appyx.core.lifecycle.subscribe
import com.bumble.appyx.core.modality.BuildContext import com.bumble.appyx.core.modality.BuildContext
import com.bumble.appyx.core.node.Node import com.bumble.appyx.core.node.Node
import com.bumble.appyx.core.plugin.Plugin import com.bumble.appyx.core.plugin.Plugin
import com.bumble.appyx.core.plugin.plugins
import com.bumble.appyx.navmodel.backstack.BackStack import com.bumble.appyx.navmodel.backstack.BackStack
import com.bumble.appyx.navmodel.backstack.operation.push import com.bumble.appyx.navmodel.backstack.operation.push
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import io.element.android.anvilannotations.ContributesNode import io.element.android.anvilannotations.ContributesNode
import io.element.android.appnav.NodeLifecycleCallback import io.element.android.appnav.di.RoomComponentFactory
import io.element.android.features.messages.api.MessagesEntryPoint import io.element.android.features.messages.api.MessagesEntryPoint
import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint
import io.element.android.libraries.architecture.BackstackNode import io.element.android.libraries.architecture.BackstackNode
import io.element.android.libraries.architecture.NodeInputs import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
import io.element.android.libraries.architecture.inputs import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.di.DaggerComponentOwner
import io.element.android.libraries.di.SessionScope import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.core.UserId
@ -60,6 +60,7 @@ class RoomLoadedFlowNode @AssistedInject constructor(
private val messagesEntryPoint: MessagesEntryPoint, private val messagesEntryPoint: MessagesEntryPoint,
private val roomDetailsEntryPoint: RoomDetailsEntryPoint, private val roomDetailsEntryPoint: RoomDetailsEntryPoint,
private val appNavigationStateService: AppNavigationStateService, private val appNavigationStateService: AppNavigationStateService,
roomComponentFactory: RoomComponentFactory,
roomMembershipObserver: RoomMembershipObserver, roomMembershipObserver: RoomMembershipObserver,
) : BackstackNode<RoomLoadedFlowNode.NavTarget>( ) : BackstackNode<RoomLoadedFlowNode.NavTarget>(
backstack = BackStack( backstack = BackStack(
@ -68,17 +69,12 @@ class RoomLoadedFlowNode @AssistedInject constructor(
), ),
buildContext = buildContext, buildContext = buildContext,
plugins = plugins, plugins = plugins,
) { ), DaggerComponentOwner {
interface Callback : Plugin { interface Callback : Plugin {
fun onForwardedToSingleRoom(roomId: RoomId) fun onForwardedToSingleRoom(roomId: RoomId)
} }
interface LifecycleCallback : NodeLifecycleCallback {
fun onFlowCreated(identifier: String, room: MatrixRoom)
fun onFlowReleased(identifier: String, room: MatrixRoom)
}
data class Inputs( data class Inputs(
val room: MatrixRoom, val room: MatrixRoom,
val initialElement: NavTarget = NavTarget.Messages, val initialElement: NavTarget = NavTarget.Messages,
@ -86,18 +82,17 @@ class RoomLoadedFlowNode @AssistedInject constructor(
private val inputs: Inputs = inputs() private val inputs: Inputs = inputs()
private val callbacks = plugins.filterIsInstance<Callback>() private val callbacks = plugins.filterIsInstance<Callback>()
override val daggerComponent = roomComponentFactory.create(inputs.room)
init { init {
lifecycle.subscribe( lifecycle.subscribe(
onCreate = { onCreate = {
Timber.v("OnCreate") Timber.v("OnCreate => ${inputs.room.roomId}")
plugins<LifecycleCallback>().forEach { it.onFlowCreated(id, inputs.room) }
appNavigationStateService.onNavigateToRoom(id, inputs.room.roomId) appNavigationStateService.onNavigateToRoom(id, inputs.room.roomId)
fetchRoomMembers() fetchRoomMembers()
}, },
onDestroy = { onDestroy = {
Timber.v("OnDestroy") Timber.v("OnDestroy")
plugins<LifecycleCallback>().forEach { it.onFlowReleased(id, inputs.room) }
appNavigationStateService.onLeavingRoom(id) appNavigationStateService.onLeavingRoom(id)
} }
) )

View file

@ -26,10 +26,12 @@ import com.bumble.appyx.navmodel.backstack.activeElement
import com.bumble.appyx.testing.junit4.util.MainDispatcherRule import com.bumble.appyx.testing.junit4.util.MainDispatcherRule
import com.bumble.appyx.testing.unit.common.helper.parentNodeTestHelper import com.bumble.appyx.testing.unit.common.helper.parentNodeTestHelper
import com.google.common.truth.Truth import com.google.common.truth.Truth
import io.element.android.appnav.di.RoomComponentFactory
import io.element.android.appnav.room.RoomLoadedFlowNode import io.element.android.appnav.room.RoomLoadedFlowNode
import io.element.android.features.messages.api.MessagesEntryPoint import io.element.android.features.messages.api.MessagesEntryPoint
import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint
import io.element.android.libraries.architecture.childNode import io.element.android.libraries.architecture.childNode
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
import io.element.android.services.appnavstate.test.FakeAppNavigationStateService import io.element.android.services.appnavstate.test.FakeAppNavigationStateService
@ -57,6 +59,12 @@ class RoomFlowNodeTest {
} }
} }
private class FakeRoomComponentFactory : RoomComponentFactory {
override fun create(room: MatrixRoom): Any {
return Unit
}
}
private class FakeRoomDetailsEntryPoint : RoomDetailsEntryPoint { private class FakeRoomDetailsEntryPoint : RoomDetailsEntryPoint {
var nodeId: String? = null var nodeId: String? = null
@ -83,7 +91,8 @@ class RoomFlowNodeTest {
messagesEntryPoint = messagesEntryPoint, messagesEntryPoint = messagesEntryPoint,
roomDetailsEntryPoint = roomDetailsEntryPoint, roomDetailsEntryPoint = roomDetailsEntryPoint,
appNavigationStateService = FakeAppNavigationStateService(), appNavigationStateService = FakeAppNavigationStateService(),
roomMembershipObserver = RoomMembershipObserver() roomMembershipObserver = RoomMembershipObserver(),
roomComponentFactory = FakeRoomComponentFactory(),
) )
@Test @Test

1
changelog.d/1378.misc Normal file
View file

@ -0,0 +1 @@
[DI] Rework how dagger components are created and provided.