Merge pull request #5436 from element-hq/feature/fga/space_list_destroy
Misc : destroy SpaceRoomList
This commit is contained in:
commit
6b6cf93167
24 changed files with 199 additions and 170 deletions
|
|
@ -9,15 +9,15 @@ package io.element.android.x.di
|
||||||
|
|
||||||
import dev.zacsweers.metro.ContributesBinding
|
import dev.zacsweers.metro.ContributesBinding
|
||||||
import dev.zacsweers.metro.Inject
|
import dev.zacsweers.metro.Inject
|
||||||
import io.element.android.appnav.di.RoomComponentFactory
|
import io.element.android.appnav.di.RoomGraphFactory
|
||||||
import io.element.android.libraries.di.SessionScope
|
import io.element.android.libraries.di.SessionScope
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
|
|
||||||
@ContributesBinding(SessionScope::class)
|
@ContributesBinding(SessionScope::class)
|
||||||
@Inject
|
@Inject
|
||||||
class DefaultRoomComponentFactory(
|
class DefaultRoomGraphFactory(
|
||||||
private val sessionGraph: SessionGraph,
|
private val sessionGraph: SessionGraph,
|
||||||
) : RoomComponentFactory {
|
) : RoomGraphFactory {
|
||||||
override fun create(room: JoinedRoom): Any {
|
override fun create(room: JoinedRoom): Any {
|
||||||
return sessionGraph.roomGraphFactory
|
return sessionGraph.roomGraphFactory
|
||||||
.create(room, room)
|
.create(room, room)
|
||||||
|
|
@ -7,18 +7,15 @@
|
||||||
|
|
||||||
package io.element.android.x.di
|
package io.element.android.x.di
|
||||||
|
|
||||||
import dev.zacsweers.metro.ContributesTo
|
|
||||||
import dev.zacsweers.metro.GraphExtension
|
import dev.zacsweers.metro.GraphExtension
|
||||||
import dev.zacsweers.metro.Provides
|
import dev.zacsweers.metro.Provides
|
||||||
import io.element.android.libraries.architecture.NodeFactoriesBindings
|
import io.element.android.libraries.architecture.NodeFactoriesBindings
|
||||||
import io.element.android.libraries.di.RoomScope
|
import io.element.android.libraries.di.RoomScope
|
||||||
import io.element.android.libraries.di.SessionScope
|
|
||||||
import io.element.android.libraries.matrix.api.room.BaseRoom
|
import io.element.android.libraries.matrix.api.room.BaseRoom
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
|
|
||||||
@GraphExtension(RoomScope::class)
|
@GraphExtension(RoomScope::class)
|
||||||
interface RoomGraph : NodeFactoriesBindings {
|
interface RoomGraph : NodeFactoriesBindings {
|
||||||
@ContributesTo(SessionScope::class)
|
|
||||||
@GraphExtension.Factory
|
@GraphExtension.Factory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
fun create(
|
fun create(
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,6 @@
|
||||||
|
|
||||||
package io.element.android.x.di
|
package io.element.android.x.di
|
||||||
|
|
||||||
import dev.zacsweers.metro.AppScope
|
|
||||||
import dev.zacsweers.metro.ContributesTo
|
|
||||||
import dev.zacsweers.metro.GraphExtension
|
import dev.zacsweers.metro.GraphExtension
|
||||||
import dev.zacsweers.metro.Provides
|
import dev.zacsweers.metro.Provides
|
||||||
import io.element.android.libraries.architecture.NodeFactoriesBindings
|
import io.element.android.libraries.architecture.NodeFactoriesBindings
|
||||||
|
|
@ -19,7 +17,6 @@ import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
interface SessionGraph : NodeFactoriesBindings {
|
interface SessionGraph : NodeFactoriesBindings {
|
||||||
val roomGraphFactory: RoomGraph.Factory
|
val roomGraphFactory: RoomGraph.Factory
|
||||||
|
|
||||||
@ContributesTo(AppScope::class)
|
|
||||||
@GraphExtension.Factory
|
@GraphExtension.Factory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
fun create(@Provides matrixClient: MatrixClient): SessionGraph
|
fun create(@Provides matrixClient: MatrixClient): SessionGraph
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,6 @@ package io.element.android.appnav.di
|
||||||
|
|
||||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||||
|
|
||||||
fun interface RoomComponentFactory {
|
fun interface RoomGraphFactory {
|
||||||
fun create(room: JoinedRoom): Any
|
fun create(room: JoinedRoom): Any
|
||||||
}
|
}
|
||||||
|
|
@ -20,7 +20,7 @@ import com.bumble.appyx.navmodel.backstack.operation.push
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedInject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.appnav.di.RoomComponentFactory
|
import io.element.android.appnav.di.RoomGraphFactory
|
||||||
import io.element.android.appnav.room.RoomNavigationTarget
|
import io.element.android.appnav.room.RoomNavigationTarget
|
||||||
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
|
||||||
|
|
@ -56,7 +56,7 @@ class JoinedRoomLoadedFlowNode(
|
||||||
private val sessionCoroutineScope: CoroutineScope,
|
private val sessionCoroutineScope: CoroutineScope,
|
||||||
private val matrixClient: MatrixClient,
|
private val matrixClient: MatrixClient,
|
||||||
private val activeRoomsHolder: ActiveRoomsHolder,
|
private val activeRoomsHolder: ActiveRoomsHolder,
|
||||||
roomComponentFactory: RoomComponentFactory,
|
roomGraphFactory: RoomGraphFactory,
|
||||||
) : BaseFlowNode<JoinedRoomLoadedFlowNode.NavTarget>(
|
) : BaseFlowNode<JoinedRoomLoadedFlowNode.NavTarget>(
|
||||||
backstack = BackStack(
|
backstack = BackStack(
|
||||||
initialElement = when (val input = plugins.filterIsInstance<Inputs>().first().initialElement) {
|
initialElement = when (val input = plugins.filterIsInstance<Inputs>().first().initialElement) {
|
||||||
|
|
@ -83,7 +83,7 @@ class JoinedRoomLoadedFlowNode(
|
||||||
|
|
||||||
private val inputs: Inputs = inputs()
|
private val inputs: Inputs = inputs()
|
||||||
private val callbacks = plugins.filterIsInstance<Callback>()
|
private val callbacks = plugins.filterIsInstance<Callback>()
|
||||||
override val graph = roomComponentFactory.create(inputs.room)
|
override val graph = roomGraphFactory.create(inputs.room)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
lifecycle.subscribe(
|
lifecycle.subscribe(
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ 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.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.appnav.di.RoomComponentFactory
|
import io.element.android.appnav.di.RoomGraphFactory
|
||||||
import io.element.android.appnav.room.RoomNavigationTarget
|
import io.element.android.appnav.room.RoomNavigationTarget
|
||||||
import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode
|
import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode
|
||||||
import io.element.android.features.messages.api.MessagesEntryPoint
|
import io.element.android.features.messages.api.MessagesEntryPoint
|
||||||
|
|
@ -70,7 +70,7 @@ class JoinedRoomLoadedFlowNodeTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FakeRoomComponentFactory : RoomComponentFactory {
|
private class FakeRoomGraphFactory : RoomGraphFactory {
|
||||||
override fun create(room: JoinedRoom): Any {
|
override fun create(room: JoinedRoom): Any {
|
||||||
return Unit
|
return Unit
|
||||||
}
|
}
|
||||||
|
|
@ -110,7 +110,7 @@ class JoinedRoomLoadedFlowNodeTest {
|
||||||
roomDetailsEntryPoint = roomDetailsEntryPoint,
|
roomDetailsEntryPoint = roomDetailsEntryPoint,
|
||||||
appNavigationStateService = FakeAppNavigationStateService(),
|
appNavigationStateService = FakeAppNavigationStateService(),
|
||||||
sessionCoroutineScope = this,
|
sessionCoroutineScope = this,
|
||||||
roomComponentFactory = FakeRoomComponentFactory(),
|
roomGraphFactory = FakeRoomGraphFactory(),
|
||||||
matrixClient = FakeMatrixClient(),
|
matrixClient = FakeMatrixClient(),
|
||||||
activeRoomsHolder = activeRoomsHolder,
|
activeRoomsHolder = activeRoomsHolder,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import com.bumble.appyx.core.plugin.Plugin
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedInject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.appnav.di.RoomComponentFactory
|
import io.element.android.appnav.di.RoomGraphFactory
|
||||||
import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesEntryPoint
|
import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesEntryPoint
|
||||||
import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType
|
import io.element.android.features.changeroommemberroes.api.ChangeRoomMemberRolesListType
|
||||||
import io.element.android.libraries.architecture.NodeInputs
|
import io.element.android.libraries.architecture.NodeInputs
|
||||||
|
|
@ -36,7 +36,7 @@ import kotlinx.parcelize.Parcelize
|
||||||
class ChangeRoomMemberRolesRootNode(
|
class ChangeRoomMemberRolesRootNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
roomComponentFactory: RoomComponentFactory,
|
roomGraphFactory: RoomGraphFactory,
|
||||||
) : ParentNode<ChangeRoomMemberRolesRootNode.NavTarget>(
|
) : ParentNode<ChangeRoomMemberRolesRootNode.NavTarget>(
|
||||||
navModel = PermanentNavModel(
|
navModel = PermanentNavModel(
|
||||||
navTargets = setOf(NavTarget),
|
navTargets = setOf(NavTarget),
|
||||||
|
|
@ -54,7 +54,7 @@ class ChangeRoomMemberRolesRootNode(
|
||||||
|
|
||||||
private val inputs = inputs<Inputs>()
|
private val inputs = inputs<Inputs>()
|
||||||
|
|
||||||
override val graph = roomComponentFactory.create(inputs.joinedRoom)
|
override val graph = roomGraphFactory.create(inputs.joinedRoom)
|
||||||
|
|
||||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||||
return createNode<ChangeRolesNode>(
|
return createNode<ChangeRolesNode>(
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ class DefaultChangeRoomMemberRolesEntyPointTest {
|
||||||
ChangeRoomMemberRolesRootNode(
|
ChangeRoomMemberRolesRootNode(
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
plugins = plugins,
|
plugins = plugins,
|
||||||
roomComponentFactory = { },
|
roomGraphFactory = { },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val room = FakeJoinedRoom()
|
val room = FakeJoinedRoom()
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import android.os.Parcelable
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
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
|
||||||
|
|
@ -22,13 +23,16 @@ import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedInject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.space.api.SpaceEntryPoint
|
import io.element.android.features.space.api.SpaceEntryPoint
|
||||||
|
import io.element.android.features.space.impl.di.SpaceFlowGraph
|
||||||
import io.element.android.features.space.impl.leave.LeaveSpaceNode
|
import io.element.android.features.space.impl.leave.LeaveSpaceNode
|
||||||
import io.element.android.features.space.impl.root.SpaceNode
|
import io.element.android.features.space.impl.root.SpaceNode
|
||||||
import io.element.android.libraries.architecture.BackstackView
|
import io.element.android.libraries.architecture.BackstackView
|
||||||
import io.element.android.libraries.architecture.BaseFlowNode
|
import io.element.android.libraries.architecture.BaseFlowNode
|
||||||
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.di.DependencyInjectionGraphOwner
|
||||||
import io.element.android.libraries.di.SessionScope
|
import io.element.android.libraries.di.SessionScope
|
||||||
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
|
|
@ -37,6 +41,8 @@ import kotlinx.parcelize.Parcelize
|
||||||
class SpaceFlowNode(
|
class SpaceFlowNode(
|
||||||
@Assisted val buildContext: BuildContext,
|
@Assisted val buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
|
matrixClient: MatrixClient,
|
||||||
|
graphFactory: SpaceFlowGraph.Factory,
|
||||||
) : BaseFlowNode<SpaceFlowNode.NavTarget>(
|
) : BaseFlowNode<SpaceFlowNode.NavTarget>(
|
||||||
backstack = BackStack(
|
backstack = BackStack(
|
||||||
initialElement = NavTarget.Root,
|
initialElement = NavTarget.Root,
|
||||||
|
|
@ -44,9 +50,11 @@ class SpaceFlowNode(
|
||||||
),
|
),
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
plugins = plugins,
|
plugins = plugins,
|
||||||
) {
|
), DependencyInjectionGraphOwner {
|
||||||
private val inputs: SpaceEntryPoint.Inputs = inputs()
|
private val inputs: SpaceEntryPoint.Inputs = inputs()
|
||||||
private val callback = plugins.filterIsInstance<SpaceEntryPoint.Callback>().single()
|
private val callback = plugins.filterIsInstance<SpaceEntryPoint.Callback>().single()
|
||||||
|
private val spaceRoomList = matrixClient.spaceService.spaceRoomList(inputs.roomId)
|
||||||
|
override val graph = graphFactory.create(spaceRoomList)
|
||||||
|
|
||||||
sealed interface NavTarget : Parcelable {
|
sealed interface NavTarget : Parcelable {
|
||||||
@Parcelize
|
@Parcelize
|
||||||
|
|
@ -56,6 +64,15 @@ class SpaceFlowNode(
|
||||||
data object Leave : NavTarget
|
data object Leave : NavTarget
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onBuilt() {
|
||||||
|
super.onBuilt()
|
||||||
|
lifecycle.subscribe(
|
||||||
|
onDestroy = {
|
||||||
|
spaceRoomList.destroy()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||||
return when (navTarget) {
|
return when (navTarget) {
|
||||||
NavTarget.Leave -> {
|
NavTarget.Leave -> {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.space.impl.di
|
||||||
|
|
||||||
|
import dev.zacsweers.metro.ContributesTo
|
||||||
|
import dev.zacsweers.metro.GraphExtension
|
||||||
|
import dev.zacsweers.metro.Provides
|
||||||
|
import io.element.android.libraries.architecture.NodeFactoriesBindings
|
||||||
|
import io.element.android.libraries.di.SessionScope
|
||||||
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
||||||
|
|
||||||
|
@GraphExtension(SpaceFlowScope::class)
|
||||||
|
interface SpaceFlowGraph : NodeFactoriesBindings {
|
||||||
|
@ContributesTo(SessionScope::class)
|
||||||
|
@GraphExtension.Factory
|
||||||
|
interface Factory {
|
||||||
|
fun create(@Provides spaceRoomList: SpaceRoomList): SpaceFlowGraph
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.space.impl.di
|
||||||
|
|
||||||
|
abstract class SpaceFlowScope private constructor()
|
||||||
|
|
@ -15,20 +15,15 @@ import com.bumble.appyx.core.plugin.Plugin
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedInject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.space.api.SpaceEntryPoint
|
import io.element.android.features.space.impl.di.SpaceFlowScope
|
||||||
import io.element.android.libraries.architecture.inputs
|
|
||||||
import io.element.android.libraries.di.SessionScope
|
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SpaceFlowScope::class)
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
class LeaveSpaceNode(
|
class LeaveSpaceNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
presenterFactory: LeaveSpacePresenter.Factory,
|
private val presenter: LeaveSpacePresenter,
|
||||||
) : Node(buildContext, plugins = plugins) {
|
) : Node(buildContext, plugins = plugins) {
|
||||||
private val inputs: SpaceEntryPoint.Inputs = inputs()
|
|
||||||
private val presenter = presenterFactory.create(inputs)
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun View(modifier: Modifier) {
|
override fun View(modifier: Modifier) {
|
||||||
val state = presenter.present()
|
val state = presenter.present()
|
||||||
|
|
|
||||||
|
|
@ -15,17 +15,14 @@ import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.produceState
|
import androidx.compose.runtime.produceState
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Inject
|
||||||
import dev.zacsweers.metro.AssistedFactory
|
|
||||||
import dev.zacsweers.metro.AssistedInject
|
|
||||||
import io.element.android.features.space.api.SpaceEntryPoint
|
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.architecture.runUpdatingState
|
import io.element.android.libraries.architecture.runUpdatingState
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
|
||||||
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.spaces.SpaceRoom
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||||
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.ImmutableSet
|
import kotlinx.collections.immutable.ImmutableSet
|
||||||
import kotlinx.collections.immutable.persistentSetOf
|
import kotlinx.collections.immutable.persistentSetOf
|
||||||
|
|
@ -35,18 +32,10 @@ import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.jvm.optionals.getOrNull
|
import kotlin.jvm.optionals.getOrNull
|
||||||
|
|
||||||
@AssistedInject
|
@Inject
|
||||||
class LeaveSpacePresenter(
|
class LeaveSpacePresenter(
|
||||||
@Assisted private val inputs: SpaceEntryPoint.Inputs,
|
private val spaceRoomList: SpaceRoomList,
|
||||||
matrixClient: MatrixClient,
|
|
||||||
) : Presenter<LeaveSpaceState> {
|
) : Presenter<LeaveSpaceState> {
|
||||||
@AssistedFactory
|
|
||||||
fun interface Factory {
|
|
||||||
fun create(inputs: SpaceEntryPoint.Inputs): LeaveSpacePresenter
|
|
||||||
}
|
|
||||||
|
|
||||||
private val spaceRoomList = matrixClient.spaceService.spaceRoomList(inputs.roomId)
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): LeaveSpaceState {
|
override fun present(): LeaveSpaceState {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
|
|
|
||||||
|
|
@ -18,36 +18,33 @@ import com.bumble.appyx.core.plugin.Plugin
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Assisted
|
||||||
import dev.zacsweers.metro.AssistedInject
|
import dev.zacsweers.metro.AssistedInject
|
||||||
import io.element.android.annotations.ContributesNode
|
import io.element.android.annotations.ContributesNode
|
||||||
import io.element.android.features.space.api.SpaceEntryPoint
|
import io.element.android.features.space.impl.di.SpaceFlowScope
|
||||||
import io.element.android.libraries.androidutils.R
|
import io.element.android.libraries.androidutils.R
|
||||||
import io.element.android.libraries.androidutils.system.startSharePlainTextIntent
|
import io.element.android.libraries.androidutils.system.startSharePlainTextIntent
|
||||||
import io.element.android.libraries.architecture.inputs
|
|
||||||
import io.element.android.libraries.di.SessionScope
|
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
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.spaces.SpaceRoomList
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
@ContributesNode(SessionScope::class)
|
@ContributesNode(SpaceFlowScope::class)
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
class SpaceNode(
|
class SpaceNode(
|
||||||
@Assisted buildContext: BuildContext,
|
@Assisted buildContext: BuildContext,
|
||||||
@Assisted plugins: List<Plugin>,
|
@Assisted plugins: List<Plugin>,
|
||||||
presenterFactory: SpacePresenter.Factory,
|
private val presenter: SpacePresenter,
|
||||||
private val matrixClient: MatrixClient,
|
private val matrixClient: MatrixClient,
|
||||||
|
private val spaceRoomList: SpaceRoomList,
|
||||||
) : Node(buildContext, plugins = plugins) {
|
) : Node(buildContext, plugins = plugins) {
|
||||||
interface Callback : Plugin {
|
interface Callback : Plugin {
|
||||||
fun onOpenRoom(roomId: RoomId, viaParameters: List<String>)
|
fun onOpenRoom(roomId: RoomId, viaParameters: List<String>)
|
||||||
fun onLeaveSpace()
|
fun onLeaveSpace()
|
||||||
}
|
}
|
||||||
|
|
||||||
private val inputs: SpaceEntryPoint.Inputs = inputs()
|
|
||||||
private val callback = plugins.filterIsInstance<Callback>().single()
|
private val callback = plugins.filterIsInstance<Callback>().single()
|
||||||
private val presenter = presenterFactory.create(inputs)
|
|
||||||
|
|
||||||
private fun onShareRoom(context: Context) = lifecycleScope.launch {
|
private fun onShareRoom(context: Context) = lifecycleScope.launch {
|
||||||
matrixClient.getRoom(inputs.roomId)?.use { room ->
|
matrixClient.getRoom(spaceRoomList.roomId)?.use { room ->
|
||||||
room.getPermalink()
|
room.getPermalink()
|
||||||
.onSuccess { permalink ->
|
.onSuccess { permalink ->
|
||||||
context.startSharePlainTextIntent(
|
context.startSharePlainTextIntent(
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,8 @@ import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import dev.zacsweers.metro.Assisted
|
import dev.zacsweers.metro.Inject
|
||||||
import dev.zacsweers.metro.AssistedFactory
|
|
||||||
import dev.zacsweers.metro.AssistedInject
|
|
||||||
import io.element.android.features.invite.api.SeenInvitesStore
|
import io.element.android.features.invite.api.SeenInvitesStore
|
||||||
import io.element.android.features.space.api.SpaceEntryPoint
|
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.core.coroutine.mapState
|
import io.element.android.libraries.core.coroutine.mapState
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
|
|
@ -31,19 +28,12 @@ import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.jvm.optionals.getOrNull
|
import kotlin.jvm.optionals.getOrNull
|
||||||
|
|
||||||
@AssistedInject
|
@Inject
|
||||||
class SpacePresenter(
|
class SpacePresenter(
|
||||||
@Assisted private val inputs: SpaceEntryPoint.Inputs,
|
private val spaceRoomList: SpaceRoomList,
|
||||||
private val client: MatrixClient,
|
private val client: MatrixClient,
|
||||||
private val seenInvitesStore: SeenInvitesStore,
|
private val seenInvitesStore: SeenInvitesStore,
|
||||||
) : Presenter<SpaceState> {
|
) : Presenter<SpaceState> {
|
||||||
@AssistedFactory
|
|
||||||
fun interface Factory {
|
|
||||||
fun create(inputs: SpaceEntryPoint.Inputs): SpacePresenter
|
|
||||||
}
|
|
||||||
|
|
||||||
private val spaceRoomList = client.spaceService.spaceRoomList(inputs.roomId)
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun present(): SpaceState {
|
override fun present(): SpaceState {
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,12 @@ import com.bumble.appyx.core.modality.BuildContext
|
||||||
import com.bumble.appyx.testing.junit4.util.MainDispatcherRule
|
import com.bumble.appyx.testing.junit4.util.MainDispatcherRule
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.space.api.SpaceEntryPoint
|
import io.element.android.features.space.api.SpaceEntryPoint
|
||||||
|
import io.element.android.features.space.impl.di.FakeSpaceFlowGraph
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||||
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
|
import io.element.android.libraries.matrix.test.spaces.FakeSpaceRoomList
|
||||||
|
import io.element.android.libraries.matrix.test.spaces.FakeSpaceService
|
||||||
import io.element.android.tests.testutils.lambda.lambdaError
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
import io.element.android.tests.testutils.node.TestParentNode
|
import io.element.android.tests.testutils.node.TestParentNode
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
|
|
@ -34,6 +38,12 @@ class DefaultSpaceEntryPointTest {
|
||||||
SpaceFlowNode(
|
SpaceFlowNode(
|
||||||
buildContext = buildContext,
|
buildContext = buildContext,
|
||||||
plugins = plugins,
|
plugins = plugins,
|
||||||
|
matrixClient = FakeMatrixClient(
|
||||||
|
spaceService = FakeSpaceService(
|
||||||
|
spaceRoomListResult = { _: RoomId -> FakeSpaceRoomList(A_ROOM_ID) }
|
||||||
|
)
|
||||||
|
),
|
||||||
|
graphFactory = FakeSpaceFlowGraph.Factory
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val callback = object : SpaceEntryPoint.Callback {
|
val callback = object : SpaceEntryPoint.Callback {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2025 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
||||||
|
* Please see LICENSE files in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.space.impl.di
|
||||||
|
|
||||||
|
import com.bumble.appyx.core.node.Node
|
||||||
|
import io.element.android.libraries.architecture.AssistedNodeFactory
|
||||||
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
|
class FakeSpaceFlowGraph : SpaceFlowGraph {
|
||||||
|
object Factory : SpaceFlowGraph.Factory {
|
||||||
|
override fun create(spaceRoomList: SpaceRoomList): SpaceFlowGraph {
|
||||||
|
return FakeSpaceFlowGraph()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun nodeFactories(): Map<KClass<out Node>, AssistedNodeFactory<*>> {
|
||||||
|
return emptyMap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -10,15 +10,11 @@
|
||||||
package io.element.android.features.space.impl.leave
|
package io.element.android.features.space.impl.leave
|
||||||
|
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.space.api.SpaceEntryPoint
|
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
|
||||||
import io.element.android.libraries.matrix.test.A_SPACE_NAME
|
import io.element.android.libraries.matrix.test.A_SPACE_NAME
|
||||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
|
||||||
import io.element.android.libraries.matrix.test.spaces.FakeSpaceRoomList
|
import io.element.android.libraries.matrix.test.spaces.FakeSpaceRoomList
|
||||||
import io.element.android.libraries.matrix.test.spaces.FakeSpaceService
|
|
||||||
import io.element.android.libraries.previewutils.room.aSpaceRoom
|
import io.element.android.libraries.previewutils.room.aSpaceRoom
|
||||||
import io.element.android.tests.testutils.test
|
import io.element.android.tests.testutils.test
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
|
@ -29,15 +25,7 @@ import org.junit.Test
|
||||||
class LeaveSpacePresenterTest {
|
class LeaveSpacePresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - initial state`() = runTest {
|
fun `present - initial state`() = runTest {
|
||||||
val presenter = createLeaveSpacePresenter(
|
val presenter = createLeaveSpacePresenter()
|
||||||
matrixClient = FakeMatrixClient(
|
|
||||||
spaceService = FakeSpaceService(
|
|
||||||
spaceRoomListResult = {
|
|
||||||
FakeSpaceRoomList()
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val state = awaitItem()
|
val state = awaitItem()
|
||||||
assertThat(state.spaceName).isNull()
|
assertThat(state.spaceName).isNull()
|
||||||
|
|
@ -51,11 +39,7 @@ class LeaveSpacePresenterTest {
|
||||||
fun `present - current space name`() = runTest {
|
fun `present - current space name`() = runTest {
|
||||||
val fakeSpaceRoomList = FakeSpaceRoomList()
|
val fakeSpaceRoomList = FakeSpaceRoomList()
|
||||||
val presenter = createLeaveSpacePresenter(
|
val presenter = createLeaveSpacePresenter(
|
||||||
matrixClient = FakeMatrixClient(
|
spaceRoomList = fakeSpaceRoomList,
|
||||||
spaceService = FakeSpaceService(
|
|
||||||
spaceRoomListResult = { fakeSpaceRoomList },
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val state = awaitItem()
|
val state = awaitItem()
|
||||||
|
|
@ -71,12 +55,10 @@ class LeaveSpacePresenterTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createLeaveSpacePresenter(
|
private fun createLeaveSpacePresenter(
|
||||||
inputs: SpaceEntryPoint.Inputs = SpaceEntryPoint.Inputs(A_ROOM_ID),
|
spaceRoomList: SpaceRoomList = FakeSpaceRoomList(),
|
||||||
matrixClient: MatrixClient = FakeMatrixClient(),
|
|
||||||
): LeaveSpacePresenter {
|
): LeaveSpacePresenter {
|
||||||
return LeaveSpacePresenter(
|
return LeaveSpacePresenter(
|
||||||
inputs = inputs,
|
spaceRoomList = spaceRoomList,
|
||||||
matrixClient = matrixClient,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -12,13 +12,10 @@ package io.element.android.features.space.impl.root
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.invite.api.SeenInvitesStore
|
import io.element.android.features.invite.api.SeenInvitesStore
|
||||||
import io.element.android.features.invite.test.InMemorySeenInvitesStore
|
import io.element.android.features.invite.test.InMemorySeenInvitesStore
|
||||||
import io.element.android.features.space.api.SpaceEntryPoint
|
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
|
||||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
import io.element.android.libraries.matrix.test.spaces.FakeSpaceRoomList
|
import io.element.android.libraries.matrix.test.spaces.FakeSpaceRoomList
|
||||||
import io.element.android.libraries.matrix.test.spaces.FakeSpaceService
|
|
||||||
import io.element.android.libraries.previewutils.room.aSpaceRoom
|
import io.element.android.libraries.previewutils.room.aSpaceRoom
|
||||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
import io.element.android.tests.testutils.test
|
import io.element.android.tests.testutils.test
|
||||||
|
|
@ -33,17 +30,8 @@ class SpacePresenterTest {
|
||||||
val paginateResult = lambdaRecorder<Result<Unit>> {
|
val paginateResult = lambdaRecorder<Result<Unit>> {
|
||||||
Result.success(Unit)
|
Result.success(Unit)
|
||||||
}
|
}
|
||||||
val presenter = createSpacePresenter(
|
val spaceRoomList = FakeSpaceRoomList(paginateResult = paginateResult)
|
||||||
client = FakeMatrixClient(
|
val presenter = createSpacePresenter(spaceRoomList = spaceRoomList)
|
||||||
spaceService = FakeSpaceService(
|
|
||||||
spaceRoomListResult = {
|
|
||||||
FakeSpaceRoomList(
|
|
||||||
paginateResult = paginateResult,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val state = awaitItem()
|
val state = awaitItem()
|
||||||
assertThat(state.currentSpace).isNull()
|
assertThat(state.currentSpace).isNull()
|
||||||
|
|
@ -61,17 +49,8 @@ class SpacePresenterTest {
|
||||||
val paginateResult = lambdaRecorder<Result<Unit>> {
|
val paginateResult = lambdaRecorder<Result<Unit>> {
|
||||||
Result.success(Unit)
|
Result.success(Unit)
|
||||||
}
|
}
|
||||||
val presenter = createSpacePresenter(
|
val spaceRoomList = FakeSpaceRoomList(paginateResult = paginateResult)
|
||||||
client = FakeMatrixClient(
|
val presenter = createSpacePresenter(spaceRoomList = spaceRoomList)
|
||||||
spaceService = FakeSpaceService(
|
|
||||||
spaceRoomListResult = {
|
|
||||||
FakeSpaceRoomList(
|
|
||||||
paginateResult = paginateResult,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val state = awaitItem()
|
val state = awaitItem()
|
||||||
advanceUntilIdle()
|
advanceUntilIdle()
|
||||||
|
|
@ -84,25 +63,20 @@ class SpacePresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - has more to load value`() = runTest {
|
fun `present - has more to load value`() = runTest {
|
||||||
val fakeSpaceRoomList = FakeSpaceRoomList(
|
val paginateResult = lambdaRecorder<Result<Unit>> {
|
||||||
paginateResult = { Result.success(Unit) },
|
Result.success(Unit)
|
||||||
)
|
}
|
||||||
val presenter = createSpacePresenter(
|
val spaceRoomList = FakeSpaceRoomList(paginateResult = paginateResult)
|
||||||
client = FakeMatrixClient(
|
val presenter = createSpacePresenter(spaceRoomList = spaceRoomList)
|
||||||
spaceService = FakeSpaceService(
|
|
||||||
spaceRoomListResult = { fakeSpaceRoomList },
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val state = awaitItem()
|
val state = awaitItem()
|
||||||
advanceUntilIdle()
|
advanceUntilIdle()
|
||||||
assertThat(state.hasMoreToLoad).isTrue()
|
assertThat(state.hasMoreToLoad).isTrue()
|
||||||
fakeSpaceRoomList.emitPaginationStatus(
|
spaceRoomList.emitPaginationStatus(
|
||||||
SpaceRoomList.PaginationStatus.Idle(hasMoreToLoad = false)
|
SpaceRoomList.PaginationStatus.Idle(hasMoreToLoad = false)
|
||||||
)
|
)
|
||||||
assertThat(awaitItem().hasMoreToLoad).isFalse()
|
assertThat(awaitItem().hasMoreToLoad).isFalse()
|
||||||
fakeSpaceRoomList.emitPaginationStatus(
|
spaceRoomList.emitPaginationStatus(
|
||||||
SpaceRoomList.PaginationStatus.Idle(hasMoreToLoad = true)
|
SpaceRoomList.PaginationStatus.Idle(hasMoreToLoad = true)
|
||||||
)
|
)
|
||||||
assertThat(awaitItem().hasMoreToLoad).isTrue()
|
assertThat(awaitItem().hasMoreToLoad).isTrue()
|
||||||
|
|
@ -111,56 +85,46 @@ class SpacePresenterTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - current space value`() = runTest {
|
fun `present - current space value`() = runTest {
|
||||||
val fakeSpaceRoomList = FakeSpaceRoomList(
|
val paginateResult = lambdaRecorder<Result<Unit>> {
|
||||||
paginateResult = { Result.success(Unit) },
|
Result.success(Unit)
|
||||||
)
|
}
|
||||||
val presenter = createSpacePresenter(
|
val spaceRoomList = FakeSpaceRoomList(paginateResult = paginateResult)
|
||||||
client = FakeMatrixClient(
|
val presenter = createSpacePresenter(spaceRoomList = spaceRoomList)
|
||||||
spaceService = FakeSpaceService(
|
|
||||||
spaceRoomListResult = { fakeSpaceRoomList },
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val state = awaitItem()
|
val state = awaitItem()
|
||||||
advanceUntilIdle()
|
advanceUntilIdle()
|
||||||
assertThat(state.currentSpace).isNull()
|
assertThat(state.currentSpace).isNull()
|
||||||
val aSpace = aSpaceRoom()
|
val aSpace = aSpaceRoom()
|
||||||
fakeSpaceRoomList.emitCurrentSpace(aSpace)
|
spaceRoomList.emitCurrentSpace(aSpace)
|
||||||
assertThat(awaitItem().currentSpace).isEqualTo(aSpace)
|
assertThat(awaitItem().currentSpace).isEqualTo(aSpace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - children value`() = runTest {
|
fun `present - children value`() = runTest {
|
||||||
val fakeSpaceRoomList = FakeSpaceRoomList(
|
val paginateResult = lambdaRecorder<Result<Unit>> {
|
||||||
paginateResult = { Result.success(Unit) },
|
Result.success(Unit)
|
||||||
)
|
}
|
||||||
val presenter = createSpacePresenter(
|
val spaceRoomList = FakeSpaceRoomList(paginateResult = paginateResult)
|
||||||
client = FakeMatrixClient(
|
val presenter = createSpacePresenter(spaceRoomList = spaceRoomList)
|
||||||
spaceService = FakeSpaceService(
|
|
||||||
spaceRoomListResult = { fakeSpaceRoomList },
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
presenter.test {
|
presenter.test {
|
||||||
val state = awaitItem()
|
val state = awaitItem()
|
||||||
advanceUntilIdle()
|
advanceUntilIdle()
|
||||||
assertThat(state.children).isEmpty()
|
assertThat(state.children).isEmpty()
|
||||||
val aSpace = aSpaceRoom()
|
val aSpace = aSpaceRoom()
|
||||||
fakeSpaceRoomList.emitSpaceRooms(listOf(aSpace))
|
spaceRoomList.emitSpaceRooms(listOf(aSpace))
|
||||||
assertThat(awaitItem().children).containsExactly(aSpace)
|
assertThat(awaitItem().children).containsExactly(aSpace)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createSpacePresenter(
|
private fun createSpacePresenter(
|
||||||
inputs: SpaceEntryPoint.Inputs = SpaceEntryPoint.Inputs(A_ROOM_ID),
|
|
||||||
client: MatrixClient = FakeMatrixClient(),
|
client: MatrixClient = FakeMatrixClient(),
|
||||||
|
spaceRoomList: SpaceRoomList = FakeSpaceRoomList(),
|
||||||
seenInvitesStore: SeenInvitesStore = InMemorySeenInvitesStore(),
|
seenInvitesStore: SeenInvitesStore = InMemorySeenInvitesStore(),
|
||||||
): SpacePresenter {
|
): SpacePresenter {
|
||||||
return SpacePresenter(
|
return SpacePresenter(
|
||||||
inputs = inputs,
|
|
||||||
client = client,
|
client = client,
|
||||||
|
spaceRoomList = spaceRoomList,
|
||||||
seenInvitesStore = seenInvitesStore,
|
seenInvitesStore = seenInvitesStore,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.api.spaces
|
package io.element.android.libraries.matrix.api.spaces
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
|
|
@ -17,9 +18,13 @@ interface SpaceRoomList {
|
||||||
data class Idle(val hasMoreToLoad: Boolean) : PaginationStatus
|
data class Idle(val hasMoreToLoad: Boolean) : PaginationStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val roomId: RoomId
|
||||||
|
|
||||||
val currentSpaceFlow: StateFlow<Optional<SpaceRoom>>
|
val currentSpaceFlow: StateFlow<Optional<SpaceRoom>>
|
||||||
|
|
||||||
val spaceRoomsFlow: Flow<List<SpaceRoom>>
|
val spaceRoomsFlow: Flow<List<SpaceRoom>>
|
||||||
val paginationStatusFlow: StateFlow<PaginationStatus>
|
val paginationStatusFlow: StateFlow<PaginationStatus>
|
||||||
suspend fun paginate(): Result<Unit>
|
suspend fun paginate(): Result<Unit>
|
||||||
|
|
||||||
|
fun destroy()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,26 +8,31 @@
|
||||||
package io.element.android.libraries.matrix.impl.spaces
|
package io.element.android.libraries.matrix.impl.spaces
|
||||||
|
|
||||||
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
||||||
import kotlinx.coroutines.CompletableDeferred
|
import kotlinx.coroutines.CompletableDeferred
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.cancel
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import timber.log.Timber
|
||||||
import uniffi.matrix_sdk_ui.SpaceRoomListPaginationState
|
import uniffi.matrix_sdk_ui.SpaceRoomListPaginationState
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
import org.matrix.rustcomponents.sdk.SpaceRoomList as InnerSpaceRoomList
|
import org.matrix.rustcomponents.sdk.SpaceRoomList as InnerSpaceRoomList
|
||||||
|
|
||||||
class RustSpaceRoomList(
|
class RustSpaceRoomList(
|
||||||
|
override val roomId: RoomId,
|
||||||
private val innerProvider: suspend () -> InnerSpaceRoomList,
|
private val innerProvider: suspend () -> InnerSpaceRoomList,
|
||||||
sessionCoroutineScope: CoroutineScope,
|
private val coroutineScope: CoroutineScope,
|
||||||
spaceRoomMapper: SpaceRoomMapper,
|
spaceRoomMapper: SpaceRoomMapper,
|
||||||
) : SpaceRoomList {
|
) : SpaceRoomList {
|
||||||
private val inner = CompletableDeferred<InnerSpaceRoomList>()
|
private val innerCompletable = CompletableDeferred<InnerSpaceRoomList>()
|
||||||
|
|
||||||
override val currentSpaceFlow = MutableStateFlow<Optional<SpaceRoom>>(Optional.empty())
|
override val currentSpaceFlow = MutableStateFlow<Optional<SpaceRoom>>(Optional.empty())
|
||||||
|
|
||||||
|
|
@ -41,37 +46,45 @@ class RustSpaceRoomList(
|
||||||
)
|
)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
sessionCoroutineScope.launch {
|
coroutineScope.launch {
|
||||||
inner.complete(innerProvider())
|
val inner = innerProvider()
|
||||||
}
|
innerCompletable.complete(inner)
|
||||||
sessionCoroutineScope.launch {
|
|
||||||
inner.await().paginationStateFlow()
|
inner.paginationStateFlow()
|
||||||
.onEach { paginationStatus ->
|
.onEach { paginationStatus ->
|
||||||
paginationStatusFlow.emit(paginationStatus.into())
|
paginationStatusFlow.emit(paginationStatus.into())
|
||||||
}
|
}
|
||||||
.collect()
|
.launchIn(this)
|
||||||
}
|
|
||||||
|
|
||||||
sessionCoroutineScope.launch {
|
inner.spaceListUpdateFlow()
|
||||||
inner.await().spaceListUpdateFlow()
|
|
||||||
.onEach { updates ->
|
.onEach { updates ->
|
||||||
spaceListUpdateProcessor.postUpdates(updates)
|
spaceListUpdateProcessor.postUpdates(updates)
|
||||||
}
|
}
|
||||||
.collect()
|
.launchIn(this)
|
||||||
}
|
|
||||||
sessionCoroutineScope.launch {
|
inner.spaceUpdateFlow()
|
||||||
inner.await().spaceUpdateFlow()
|
|
||||||
.map { space -> space.map(spaceRoomMapper::map) }
|
.map { space -> space.map(spaceRoomMapper::map) }
|
||||||
.onEach { space ->
|
.onEach { space ->
|
||||||
currentSpaceFlow.emit(space)
|
currentSpaceFlow.emit(space)
|
||||||
}
|
}
|
||||||
.collect()
|
.launchIn(this)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun paginate(): Result<Unit> {
|
override suspend fun paginate(): Result<Unit> {
|
||||||
return runCatchingExceptions {
|
return runCatchingExceptions {
|
||||||
inner.await().paginate()
|
innerCompletable.await().paginate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
override fun destroy() {
|
||||||
|
Timber.d("Destroying SpaceRoomList $roomId")
|
||||||
|
coroutineScope.cancel()
|
||||||
|
try {
|
||||||
|
innerCompletable.getCompleted().destroy()
|
||||||
|
} catch (_: Exception) {
|
||||||
|
// Ignore, we just want to make sure it's completed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.impl.spaces
|
package io.element.android.libraries.matrix.impl.spaces
|
||||||
|
|
||||||
|
import io.element.android.libraries.core.coroutine.childScope
|
||||||
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
import io.element.android.libraries.core.extensions.runCatchingExceptions
|
||||||
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.spaces.SpaceRoom
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||||
|
|
@ -54,9 +55,11 @@ class RustSpaceService(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun spaceRoomList(id: RoomId): SpaceRoomList {
|
override fun spaceRoomList(id: RoomId): SpaceRoomList {
|
||||||
|
val childCoroutineScope = sessionCoroutineScope.childScope(sessionDispatcher, "SpaceRoomListScope-$this")
|
||||||
return RustSpaceRoomList(
|
return RustSpaceRoomList(
|
||||||
|
roomId = id,
|
||||||
innerProvider = { innerSpaceService.spaceRoomList(id.value) },
|
innerProvider = { innerSpaceService.spaceRoomList(id.value) },
|
||||||
sessionCoroutineScope = sessionCoroutineScope,
|
coroutineScope = childCoroutineScope,
|
||||||
spaceRoomMapper = spaceRoomMapper,
|
spaceRoomMapper = spaceRoomMapper,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,9 +11,11 @@ package io.element.android.libraries.matrix.impl.spaces
|
||||||
|
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
||||||
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustSpaceRoom
|
import io.element.android.libraries.matrix.impl.fixtures.factories.aRustSpaceRoom
|
||||||
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiSpaceRoomList
|
import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiSpaceRoomList
|
||||||
|
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
import io.element.android.libraries.matrix.test.A_ROOM_ID_2
|
||||||
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
import io.element.android.tests.testutils.lambda.lambdaRecorder
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
|
@ -84,13 +86,15 @@ class RustSpaceRoomListTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TestScope.createRustSpaceRoomList(
|
private fun TestScope.createRustSpaceRoomList(
|
||||||
|
roomId: RoomId = A_ROOM_ID,
|
||||||
innerSpaceRoomList: InnerSpaceRoomList = FakeFfiSpaceRoomList(),
|
innerSpaceRoomList: InnerSpaceRoomList = FakeFfiSpaceRoomList(),
|
||||||
innerProvider: suspend () -> InnerSpaceRoomList = { innerSpaceRoomList },
|
innerProvider: suspend () -> InnerSpaceRoomList = { innerSpaceRoomList },
|
||||||
spaceRoomMapper: SpaceRoomMapper = SpaceRoomMapper(),
|
spaceRoomMapper: SpaceRoomMapper = SpaceRoomMapper(),
|
||||||
): RustSpaceRoomList {
|
): RustSpaceRoomList {
|
||||||
return RustSpaceRoomList(
|
return RustSpaceRoomList(
|
||||||
|
roomId = roomId,
|
||||||
innerProvider = innerProvider,
|
innerProvider = innerProvider,
|
||||||
sessionCoroutineScope = backgroundScope,
|
coroutineScope = backgroundScope,
|
||||||
spaceRoomMapper = spaceRoomMapper,
|
spaceRoomMapper = spaceRoomMapper,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,10 @@
|
||||||
|
|
||||||
package io.element.android.libraries.matrix.test.spaces
|
package io.element.android.libraries.matrix.test.spaces
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoom
|
||||||
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
import io.element.android.libraries.matrix.api.spaces.SpaceRoomList
|
||||||
|
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||||
import io.element.android.tests.testutils.lambda.lambdaError
|
import io.element.android.tests.testutils.lambda.lambdaError
|
||||||
import io.element.android.tests.testutils.simulateLongTask
|
import io.element.android.tests.testutils.simulateLongTask
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
@ -18,6 +20,7 @@ import kotlinx.coroutines.flow.asStateFlow
|
||||||
import java.util.Optional
|
import java.util.Optional
|
||||||
|
|
||||||
class FakeSpaceRoomList(
|
class FakeSpaceRoomList(
|
||||||
|
override val roomId: RoomId = A_ROOM_ID,
|
||||||
initialSpaceFlowValue: SpaceRoom? = null,
|
initialSpaceFlowValue: SpaceRoom? = null,
|
||||||
initialSpaceRoomsValue: List<SpaceRoom> = emptyList(),
|
initialSpaceRoomsValue: List<SpaceRoom> = emptyList(),
|
||||||
initialSpaceRoomList: SpaceRoomList.PaginationStatus = SpaceRoomList.PaginationStatus.Loading,
|
initialSpaceRoomList: SpaceRoomList.PaginationStatus = SpaceRoomList.PaginationStatus.Loading,
|
||||||
|
|
@ -47,4 +50,8 @@ class FakeSpaceRoomList(
|
||||||
override suspend fun paginate(): Result<Unit> = simulateLongTask {
|
override suspend fun paginate(): Result<Unit> = simulateLongTask {
|
||||||
paginateResult()
|
paginateResult()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun destroy() {
|
||||||
|
// No op
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue