Merge pull request #319 from vector-im/feature/fga/clean_up
Feature/fga/clean up
This commit is contained in:
@@ -68,4 +68,7 @@ dependencies {
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.features.rageshake.test)
|
||||
testImplementation(projects.features.rageshake.impl)
|
||||
testImplementation(projects.services.appnavstate.test)
|
||||
testImplementation(libs.test.appyx.junit)
|
||||
testImplementation(libs.test.arch.core)
|
||||
}
|
||||
|
||||
@@ -108,17 +108,17 @@ class LoggedInFlowNode @AssistedInject constructor(
|
||||
val imageLoaderFactory = bindings<MatrixUIBindings>().loggedInImageLoaderFactory()
|
||||
Coil.setImageLoader(imageLoaderFactory)
|
||||
inputs.matrixClient.startSync()
|
||||
appNavigationStateService.onNavigateToSession(inputs.matrixClient.sessionId)
|
||||
appNavigationStateService.onNavigateToSession(id, inputs.matrixClient.sessionId)
|
||||
// TODO We do not support Space yet, so directly navigate to main space
|
||||
appNavigationStateService.onNavigateToSpace(MAIN_SPACE)
|
||||
appNavigationStateService.onNavigateToSpace(id, MAIN_SPACE)
|
||||
loggedInFlowProcessor.observeEvents(coroutineScope)
|
||||
},
|
||||
onDestroy = {
|
||||
val imageLoaderFactory = bindings<MatrixUIBindings>().notLoggedInImageLoaderFactory()
|
||||
Coil.setImageLoader(imageLoaderFactory)
|
||||
plugins<LifecycleCallback>().forEach { it.onFlowReleased(inputs.matrixClient) }
|
||||
appNavigationStateService.onLeavingSpace()
|
||||
appNavigationStateService.onLeavingSession()
|
||||
appNavigationStateService.onLeavingSpace(id)
|
||||
appNavigationStateService.onLeavingSession(id)
|
||||
loggedInFlowProcessor.stopObserving()
|
||||
}
|
||||
)
|
||||
|
||||
@@ -19,6 +19,7 @@ package io.element.android.appnav
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.bumble.appyx.core.composable.Children
|
||||
import com.bumble.appyx.core.lifecycle.subscribe
|
||||
import com.bumble.appyx.core.modality.BuildContext
|
||||
@@ -44,6 +45,7 @@ import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import timber.log.Timber
|
||||
|
||||
@@ -55,7 +57,6 @@ class RoomFlowNode @AssistedInject constructor(
|
||||
private val roomDetailsEntryPoint: RoomDetailsEntryPoint,
|
||||
private val appNavigationStateService: AppNavigationStateService,
|
||||
roomMembershipObserver: RoomMembershipObserver,
|
||||
coroutineScope: CoroutineScope,
|
||||
) : BackstackNode<RoomFlowNode.NavTarget>(
|
||||
backstack = BackStack(
|
||||
initialElement = NavTarget.Messages,
|
||||
@@ -75,41 +76,49 @@ class RoomFlowNode @AssistedInject constructor(
|
||||
) : NodeInputs
|
||||
|
||||
private val inputs: Inputs = inputs()
|
||||
private val timeline = inputs.room.timeline()
|
||||
|
||||
private val roomFlowPresenter = RoomFlowPresenter(inputs.room)
|
||||
|
||||
init {
|
||||
lifecycle.subscribe(
|
||||
onCreate = {
|
||||
Timber.v("OnCreate")
|
||||
plugins<LifecycleCallback>().forEach { it.onFlowCreated(inputs.room) }
|
||||
appNavigationStateService.onNavigateToRoom(inputs.room.roomId)
|
||||
appNavigationStateService.onNavigateToRoom(id, inputs.room.roomId)
|
||||
fetchRoomMembers()
|
||||
},
|
||||
onDestroy = {
|
||||
Timber.v("OnDestroy")
|
||||
inputs.room.close()
|
||||
plugins<LifecycleCallback>().forEach { it.onFlowReleased(inputs.room) }
|
||||
appNavigationStateService.onLeavingRoom()
|
||||
appNavigationStateService.onLeavingRoom(id)
|
||||
}
|
||||
)
|
||||
|
||||
roomMembershipObserver.updates
|
||||
.filter { update -> update.roomId == inputs.room.roomId && !update.isUserInRoom }
|
||||
.onEach {
|
||||
navigateUp()
|
||||
}
|
||||
.launchIn(coroutineScope)
|
||||
.launchIn(lifecycleScope)
|
||||
}
|
||||
|
||||
private fun fetchRoomMembers() = lifecycleScope.launch {
|
||||
val room = inputs.room
|
||||
room.fetchMembers()
|
||||
.onFailure {
|
||||
Timber.e(it, "Fail to fetch members for room ${room.roomId}")
|
||||
}.onSuccess {
|
||||
Timber.v("Success fetching members for room ${room.roomId}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||
return when (navTarget) {
|
||||
NavTarget.Messages -> {
|
||||
messagesEntryPoint.createNode(this, buildContext, object : MessagesEntryPoint.Callback {
|
||||
val callback = object : MessagesEntryPoint.Callback {
|
||||
override fun onRoomDetailsClicked() {
|
||||
backstack.push(NavTarget.RoomDetails)
|
||||
}
|
||||
})
|
||||
}
|
||||
messagesEntryPoint.createNode(this, buildContext, callback)
|
||||
}
|
||||
NavTarget.RoomDetails -> {
|
||||
roomDetailsEntryPoint.createNode(this, buildContext, emptyList())
|
||||
@@ -127,7 +136,6 @@ class RoomFlowNode @AssistedInject constructor(
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
roomFlowPresenter.present()
|
||||
Children(
|
||||
navModel = backstack,
|
||||
modifier = modifier,
|
||||
|
||||
@@ -1,46 +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.appnav
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import io.element.android.libraries.architecture.Presenter
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import timber.log.Timber
|
||||
|
||||
class RoomFlowPresenter(
|
||||
private val room: MatrixRoom,
|
||||
) : Presenter<RoomFlowState> {
|
||||
|
||||
@Composable
|
||||
override fun present(): RoomFlowState {
|
||||
// Preload room members so we can quickly detect if the room is a DM room
|
||||
LaunchedEffect(Unit) {
|
||||
room.fetchMembers()
|
||||
.onFailure {
|
||||
Timber.e(it, "Fail to fetch members for room ${room.roomId}")
|
||||
}.onSuccess {
|
||||
Timber.v("Success fetching members for room ${room.roomId}")
|
||||
}
|
||||
}
|
||||
|
||||
return RoomFlowState
|
||||
}
|
||||
}
|
||||
|
||||
// At first the return type was Unit, but detekt complained about it
|
||||
object RoomFlowState
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import androidx.lifecycle.Lifecycle
|
||||
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.navmodel.backstack.activeElement
|
||||
import com.bumble.appyx.testing.junit4.util.MainDispatcherRule
|
||||
import com.bumble.appyx.testing.unit.common.helper.nodeTestHelper
|
||||
import com.bumble.appyx.testing.unit.common.helper.parentNodeTestHelper
|
||||
import com.google.common.truth.Truth
|
||||
import io.element.android.features.messages.api.MessagesEntryPoint
|
||||
import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint
|
||||
import io.element.android.libraries.architecture.childNode
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.services.appnavstate.test.NoopAppNavigationStateService
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
|
||||
class RoomFlowNodeTest {
|
||||
|
||||
@get:Rule
|
||||
val instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
@get:Rule
|
||||
val mainDispatcherRule = MainDispatcherRule()
|
||||
|
||||
private class FakeMessagesEntryPoint : MessagesEntryPoint {
|
||||
|
||||
var nodeId: String? = null
|
||||
var callback: MessagesEntryPoint.Callback? = null
|
||||
|
||||
override fun createNode(parentNode: Node, buildContext: BuildContext, callback: MessagesEntryPoint.Callback): Node {
|
||||
return node(buildContext) {}.also {
|
||||
nodeId = it.id
|
||||
this.callback = callback
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class FakeRoomDetailsEntryPoint : RoomDetailsEntryPoint {
|
||||
|
||||
var nodeId: String? = null
|
||||
|
||||
override fun createNode(parentNode: Node, buildContext: BuildContext, plugins: List<Plugin>): Node {
|
||||
return node(buildContext) {}.also {
|
||||
nodeId = it.id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun aRoomFlowNode(
|
||||
plugins: List<Plugin>,
|
||||
messagesEntryPoint: MessagesEntryPoint = FakeMessagesEntryPoint(),
|
||||
roomDetailsEntryPoint: RoomDetailsEntryPoint = FakeRoomDetailsEntryPoint(),
|
||||
) = RoomFlowNode(
|
||||
buildContext = BuildContext.root(savedStateMap = null),
|
||||
plugins = plugins,
|
||||
messagesEntryPoint = messagesEntryPoint,
|
||||
roomDetailsEntryPoint = roomDetailsEntryPoint,
|
||||
appNavigationStateService = NoopAppNavigationStateService(),
|
||||
roomMembershipObserver = RoomMembershipObserver()
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `given a room flow node when initialized then it fetches room members`() {
|
||||
// GIVEN
|
||||
val room = FakeMatrixRoom()
|
||||
val inputs = RoomFlowNode.Inputs(room)
|
||||
val roomFlowNode = aRoomFlowNode(listOf(inputs))
|
||||
Truth.assertThat(room.areMembersFetched).isFalse()
|
||||
// WHEN
|
||||
roomFlowNode.nodeTestHelper()
|
||||
// THEN
|
||||
Truth.assertThat(room.areMembersFetched).isTrue()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a room flow node when initialized then it loads messages entry point`() {
|
||||
// GIVEN
|
||||
val room = FakeMatrixRoom()
|
||||
val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
|
||||
val inputs = RoomFlowNode.Inputs(room)
|
||||
val roomFlowNode = aRoomFlowNode(listOf(inputs), fakeMessagesEntryPoint)
|
||||
// WHEN
|
||||
val roomFlowNodeTestHelper = roomFlowNode.parentNodeTestHelper()
|
||||
|
||||
// THEN
|
||||
Truth.assertThat(roomFlowNode.backstack.activeElement).isEqualTo(RoomFlowNode.NavTarget.Messages)
|
||||
roomFlowNodeTestHelper.assertChildHasLifecycle(RoomFlowNode.NavTarget.Messages, Lifecycle.State.CREATED)
|
||||
val messagesNode = roomFlowNode.childNode(RoomFlowNode.NavTarget.Messages)!!
|
||||
Truth.assertThat(messagesNode.id).isEqualTo(fakeMessagesEntryPoint.nodeId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `given a room flow node when callback on room details is triggered then it loads room details entry point`() {
|
||||
// GIVEN
|
||||
val room = FakeMatrixRoom()
|
||||
val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
|
||||
val fakeRoomDetailsEntryPoint = FakeRoomDetailsEntryPoint()
|
||||
val inputs = RoomFlowNode.Inputs(room)
|
||||
val roomFlowNode = aRoomFlowNode(listOf(inputs), fakeMessagesEntryPoint, fakeRoomDetailsEntryPoint)
|
||||
val roomFlowNodeTestHelper = roomFlowNode.parentNodeTestHelper()
|
||||
// WHEN
|
||||
fakeMessagesEntryPoint.callback?.onRoomDetailsClicked()
|
||||
// THEN
|
||||
roomFlowNodeTestHelper.assertChildHasLifecycle(RoomFlowNode.NavTarget.RoomDetails, Lifecycle.State.CREATED)
|
||||
val roomDetailsNode = roomFlowNode.childNode(RoomFlowNode.NavTarget.RoomDetails)!!
|
||||
Truth.assertThat(roomDetailsNode.id).isEqualTo(fakeRoomDetailsEntryPoint.nodeId)
|
||||
}
|
||||
}
|
||||
@@ -1,62 +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.appnav
|
||||
|
||||
import app.cash.molecule.RecompositionClock
|
||||
import app.cash.molecule.moleculeFlow
|
||||
import app.cash.turbine.test
|
||||
import com.google.common.truth.Truth
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.libraries.matrix.test.timeline.FakeMatrixTimeline
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Test
|
||||
import java.lang.IllegalStateException
|
||||
|
||||
class RoomFlowPresenterTest {
|
||||
|
||||
@Test
|
||||
fun `present - fetches room members`() = runTest {
|
||||
val fakeTimeline = FakeMatrixTimeline()
|
||||
val room = FakeMatrixRoom(matrixTimeline = fakeTimeline)
|
||||
val presenter = RoomFlowPresenter(room)
|
||||
|
||||
Truth.assertThat(room.areMembersFetched).isFalse()
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
Truth.assertThat(room.areMembersFetched).isTrue()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - recovers from error while fetching room members`() = runTest {
|
||||
val fakeTimeline = FakeMatrixTimeline()
|
||||
val room = FakeMatrixRoom(matrixTimeline = fakeTimeline).apply {
|
||||
givenFetchMemberResult(Result.failure(IllegalStateException("Some error")))
|
||||
}
|
||||
val presenter = RoomFlowPresenter(room)
|
||||
|
||||
Truth.assertThat(room.areMembersFetched).isFalse()
|
||||
moleculeFlow(RecompositionClock.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
Truth.assertThat(room.areMembersFetched).isFalse()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,6 @@ import io.element.android.libraries.architecture.BackstackNode
|
||||
import io.element.android.libraries.architecture.animation.rememberDefaultTransitionHandler
|
||||
import io.element.android.libraries.architecture.createNode
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@@ -62,19 +61,16 @@ class RoomDetailsFlowNode @AssistedInject constructor(
|
||||
data class RoomMemberDetails(val roomMember: RoomMember) : NavTarget
|
||||
}
|
||||
|
||||
interface Callback : Plugin {
|
||||
fun openRoomMemberList()
|
||||
}
|
||||
|
||||
val callback = object : Callback {
|
||||
override fun openRoomMemberList() {
|
||||
backstack.push(NavTarget.RoomMemberList)
|
||||
}
|
||||
}
|
||||
|
||||
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
|
||||
return when (navTarget) {
|
||||
NavTarget.RoomDetails -> createNode<RoomDetailsNode>(buildContext, listOf(callback))
|
||||
NavTarget.RoomDetails -> {
|
||||
val callback = object : RoomDetailsNode.Callback {
|
||||
override fun openRoomMemberList() {
|
||||
backstack.push(NavTarget.RoomMemberList)
|
||||
}
|
||||
}
|
||||
createNode<RoomDetailsNode>(buildContext, listOf(callback))
|
||||
}
|
||||
NavTarget.RoomMemberList -> {
|
||||
val callback = object : RoomMemberListNode.Callback {
|
||||
override fun openRoomMemberDetails(roomMember: RoomMember) {
|
||||
@@ -84,7 +80,8 @@ class RoomDetailsFlowNode @AssistedInject constructor(
|
||||
createNode<RoomMemberListNode>(buildContext, listOf(callback))
|
||||
}
|
||||
is NavTarget.RoomMemberDetails -> {
|
||||
createNode<RoomMemberDetailsNode>(buildContext, listOf(RoomMemberDetailsNode.Inputs(navTarget.roomMember)))
|
||||
val inputs = RoomMemberDetailsNode.Inputs(navTarget.roomMember)
|
||||
createNode<RoomMemberDetailsNode>(buildContext, listOf(inputs))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,11 @@ class RoomDetailsNode @AssistedInject constructor(
|
||||
private val room: MatrixRoom,
|
||||
) : Node(buildContext, plugins = plugins) {
|
||||
|
||||
private val callback = plugins<RoomDetailsFlowNode.Callback>().firstOrNull()
|
||||
interface Callback : Plugin {
|
||||
fun openRoomMemberList()
|
||||
}
|
||||
|
||||
private val callback = plugins<Callback>().firstOrNull()
|
||||
|
||||
private fun openRoomMemberList() {
|
||||
callback?.openRoomMemberList()
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
|
||||
package io.element.android.features.roomdetails.impl.members.details
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
@@ -51,30 +50,32 @@ class RoomMemberDetailsNode @AssistedInject constructor(
|
||||
private val inputs = inputs<Inputs>()
|
||||
private val presenter = presenterFactory.create(inputs.member)
|
||||
|
||||
private fun onShareUser(context: Context) {
|
||||
val permalinkResult = PermalinkBuilder.permalinkForUser(UserId(inputs.member.userId))
|
||||
permalinkResult.onSuccess { permalink ->
|
||||
startSharePlainTextIntent(
|
||||
context = context,
|
||||
activityResultLauncher = null,
|
||||
chooserTitle = context.getString(R.string.screen_room_details_share_room_title),
|
||||
text = permalink,
|
||||
noActivityFoundMessage = context.getString(AndroidUtilsR.string.error_no_compatible_app_found)
|
||||
)
|
||||
}.onFailure {
|
||||
Timber.e(it)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun View(modifier: Modifier) {
|
||||
|
||||
val context = LocalContext.current
|
||||
|
||||
fun onShareUser() {
|
||||
val permalinkResult = PermalinkBuilder.permalinkForUser(UserId(inputs.member.userId))
|
||||
permalinkResult.onSuccess { permalink ->
|
||||
startSharePlainTextIntent(
|
||||
context = context,
|
||||
activityResultLauncher = null,
|
||||
chooserTitle = context.getString(R.string.screen_room_details_share_room_title),
|
||||
text = permalink,
|
||||
noActivityFoundMessage = context.getString(AndroidUtilsR.string.error_no_compatible_app_found)
|
||||
)
|
||||
}.onFailure {
|
||||
Timber.e(it)
|
||||
}
|
||||
}
|
||||
|
||||
val state = presenter.present()
|
||||
RoomMemberDetailsView(
|
||||
state = state,
|
||||
modifier = modifier,
|
||||
goBack = { navigateUp() },
|
||||
onShareUser = { onShareUser(context) }
|
||||
goBack = this::navigateUp,
|
||||
onShareUser = ::onShareUser
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,6 @@ import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_NAME
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -45,7 +44,7 @@ import org.junit.Test
|
||||
@ExperimentalCoroutinesApi
|
||||
class RoomDetailsPresenterTests {
|
||||
|
||||
private val roomMembershipObserver = RoomMembershipObserver(A_SESSION_ID)
|
||||
private val roomMembershipObserver = RoomMembershipObserver()
|
||||
|
||||
@Test
|
||||
fun `present - initial state is created from room info`() = runTest {
|
||||
|
||||
@@ -38,7 +38,6 @@ import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
|
||||
import io.element.android.libraries.designsystem.utils.handleSnackbarMessage
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.room.RoomSummary
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
||||
@@ -61,11 +60,9 @@ class RoomListPresenter @Inject constructor(
|
||||
private val snackbarDispatcher: SnackbarDispatcher,
|
||||
) : Presenter<RoomListState> {
|
||||
|
||||
private val roomMembershipObserver: RoomMembershipObserver = client.roomMembershipObserver()
|
||||
|
||||
@Composable
|
||||
override fun present(): RoomListState {
|
||||
val matrixUser: MutableState<MatrixUser?> = remember {
|
||||
val matrixUser: MutableState<MatrixUser?> = rememberSaveable {
|
||||
mutableStateOf(null)
|
||||
}
|
||||
var filter by rememberSaveable { mutableStateOf("") }
|
||||
|
||||
@@ -103,6 +103,7 @@ network_retrofit_converter_serialization = "com.jakewharton.retrofit:retrofit2-k
|
||||
# Test
|
||||
test_core = { module = "androidx.test:core", version.ref = "test_core" }
|
||||
test_corektx = { module = "androidx.test:core-ktx", version.ref = "test_core" }
|
||||
test_arch_core = "androidx.arch.core:core-testing:2.2.0"
|
||||
test_junit = "junit:junit:4.13.2"
|
||||
test_runner = "androidx.test:runner:1.5.2"
|
||||
test_uiautomator = "androidx.test.uiautomator:uiautomator:2.2.0"
|
||||
@@ -115,6 +116,7 @@ test_turbine = "app.cash.turbine:turbine:0.12.1"
|
||||
test_truth = "com.google.truth:truth:1.1.3"
|
||||
test_parameter_injector = "com.google.testparameterinjector:test-parameter-injector:1.11"
|
||||
test_robolectric = "org.robolectric:robolectric:4.9.2"
|
||||
test_appyx_junit = { module = "com.bumble.appyx:testing-junit4", version.ref = "appyx" }
|
||||
|
||||
# Others
|
||||
coil = { module = "io.coil-kt:coil", version.ref = "coil" }
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.libraries.architecture
|
||||
|
||||
import com.bumble.appyx.core.children.nodeOrNull
|
||||
import com.bumble.appyx.core.node.Node
|
||||
import com.bumble.appyx.core.node.ParentNode
|
||||
|
||||
fun <NavTarget : Any> ParentNode<NavTarget>.childNode(navTarget: NavTarget): Node? {
|
||||
val childMap = children.value
|
||||
val key = childMap.keys.find { it.navTarget == navTarget }
|
||||
return childMap[key]?.nodeOrNull
|
||||
}
|
||||
@@ -59,5 +59,5 @@ interface MatrixRoom: Closeable {
|
||||
|
||||
suspend fun redactEvent(eventId: EventId, reason: String? = null): Result<Unit>
|
||||
|
||||
fun leave(): Result<Unit>
|
||||
suspend fun leave(): Result<Unit>
|
||||
}
|
||||
|
||||
@@ -31,7 +31,6 @@ data class RoomMember(
|
||||
val isIgnored: Boolean,
|
||||
) : Parcelable
|
||||
|
||||
@Parcelize
|
||||
enum class RoomMembershipState : Parcelable {
|
||||
enum class RoomMembershipState {
|
||||
BAN, INVITE, JOIN, KNOCK, LEAVE
|
||||
}
|
||||
|
||||
@@ -17,14 +17,11 @@
|
||||
package io.element.android.libraries.matrix.api.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
|
||||
class RoomMembershipObserver(
|
||||
private val sessionId: SessionId,
|
||||
) {
|
||||
class RoomMembershipObserver {
|
||||
data class RoomMembershipUpdate(
|
||||
val roomId: RoomId,
|
||||
val isUserInRoom: Boolean,
|
||||
|
||||
@@ -145,7 +145,7 @@ class RustMatrixClient constructor(
|
||||
private val mediaResolver = RustMediaResolver(this)
|
||||
private val isSyncing = AtomicBoolean(false)
|
||||
|
||||
private val roomMembershipObserver = RoomMembershipObserver(sessionId)
|
||||
private val roomMembershipObserver = RoomMembershipObserver()
|
||||
|
||||
init {
|
||||
client.setDelegate(clientDelegate)
|
||||
|
||||
@@ -93,6 +93,7 @@ class RustMatrixRoom(
|
||||
coroutineDispatchers = coroutineDispatchers
|
||||
)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
innerRoom.destroy()
|
||||
slidingSyncRoom.destroy()
|
||||
@@ -188,7 +189,9 @@ class RustMatrixRoom(
|
||||
}
|
||||
}
|
||||
|
||||
override fun leave(): Result<Unit> {
|
||||
return runCatching { innerRoom.leave() }
|
||||
override suspend fun leave(): Result<Unit> = withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
innerRoom.leave()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ class FakeMatrixClient(
|
||||
override fun onSlidingSyncUpdate() {}
|
||||
|
||||
override fun roomMembershipObserver(): RoomMembershipObserver {
|
||||
return RoomMembershipObserver(A_SESSION_ID)
|
||||
return RoomMembershipObserver()
|
||||
}
|
||||
|
||||
// Mocks
|
||||
|
||||
@@ -122,7 +122,7 @@ class FakeMatrixRoom(
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override fun leave(): Result<Unit> = leaveRoomError?.let { Result.failure(it) } ?: Result.success(Unit)
|
||||
override suspend fun leave(): Result<Unit> = leaveRoomError?.let { Result.failure(it) } ?: Result.success(Unit)
|
||||
|
||||
override fun close() = Unit
|
||||
|
||||
|
||||
@@ -21,26 +21,38 @@ import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.core.SpaceId
|
||||
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||
|
||||
sealed interface AppNavigationState {
|
||||
object Root : AppNavigationState
|
||||
/**
|
||||
* Can represent the current global app navigation state.
|
||||
* @param owner mostly a Node identifier associated with the state.
|
||||
* We are using the owner parameter to check when calling onLeaving methods is still using the same owner than his companion onNavigate.
|
||||
* Why this is needed : for now we rely on lifecycle methods of the node, which are async.
|
||||
* If you navigate quickly between nodes, onCreate of the new node is called before onDestroy of the previous node.
|
||||
* So we assume if we don't get the same owner, we can skip the onLeaving action as we already replaced it.
|
||||
*/
|
||||
sealed class AppNavigationState(open val owner: String) {
|
||||
object Root : AppNavigationState("ROOT")
|
||||
|
||||
data class Session(
|
||||
override val owner: String,
|
||||
val sessionId: SessionId,
|
||||
) : AppNavigationState
|
||||
) : AppNavigationState(owner)
|
||||
|
||||
data class Space(
|
||||
override val owner: String,
|
||||
// Can be fake value, if no space is selected
|
||||
val spaceId: SpaceId,
|
||||
val parentSession: Session,
|
||||
) : AppNavigationState
|
||||
) : AppNavigationState(owner)
|
||||
|
||||
data class Room(
|
||||
override val owner: String,
|
||||
val roomId: RoomId,
|
||||
val parentSpace: Space,
|
||||
) : AppNavigationState
|
||||
) : AppNavigationState(owner)
|
||||
|
||||
data class Thread(
|
||||
override val owner: String,
|
||||
val threadId: ThreadId,
|
||||
val parentRoom: Room,
|
||||
) : AppNavigationState
|
||||
) : AppNavigationState(owner)
|
||||
}
|
||||
|
||||
@@ -25,15 +25,15 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
interface AppNavigationStateService {
|
||||
val appNavigationStateFlow: StateFlow<AppNavigationState>
|
||||
|
||||
fun onNavigateToSession(sessionId: SessionId)
|
||||
fun onLeavingSession()
|
||||
fun onNavigateToSession(owner: String, sessionId: SessionId)
|
||||
fun onLeavingSession(owner: String)
|
||||
|
||||
fun onNavigateToSpace(spaceId: SpaceId)
|
||||
fun onLeavingSpace()
|
||||
fun onNavigateToSpace(owner: String, spaceId: SpaceId)
|
||||
fun onLeavingSpace(owner: String)
|
||||
|
||||
fun onNavigateToRoom(roomId: RoomId)
|
||||
fun onLeavingRoom()
|
||||
fun onNavigateToRoom(owner: String, roomId: RoomId)
|
||||
fun onLeavingRoom(owner: String)
|
||||
|
||||
fun onNavigateToThread(threadId: ThreadId)
|
||||
fun onLeavingThread()
|
||||
fun onNavigateToThread(owner: String, threadId: ThreadId)
|
||||
fun onLeavingThread(owner: String)
|
||||
}
|
||||
|
||||
@@ -47,4 +47,5 @@ dependencies {
|
||||
testImplementation(libs.coroutines.test)
|
||||
testImplementation(libs.test.truth)
|
||||
testImplementation(projects.libraries.matrix.test)
|
||||
testImplementation(projects.services.appnavstate.test)
|
||||
}
|
||||
|
||||
@@ -40,11 +40,10 @@ private val loggerTag = LoggerTag("Navigation")
|
||||
@SingleIn(AppScope::class)
|
||||
class DefaultAppNavigationStateService @Inject constructor() : AppNavigationStateService {
|
||||
|
||||
private val currentAppNavigationState = MutableStateFlow<AppNavigationState>(AppNavigationState.Root)
|
||||
|
||||
private val currentAppNavigationState: MutableStateFlow<AppNavigationState> = MutableStateFlow(AppNavigationState.Root)
|
||||
override val appNavigationStateFlow: StateFlow<AppNavigationState> = currentAppNavigationState
|
||||
|
||||
override fun onNavigateToSession(sessionId: SessionId) {
|
||||
override fun onNavigateToSession(owner: String, sessionId: SessionId) {
|
||||
val currentValue = currentAppNavigationState.value
|
||||
Timber.tag(loggerTag.value).d("Navigating to session $sessionId. Current state: $currentValue")
|
||||
val newValue: AppNavigationState.Session = when (currentValue) {
|
||||
@@ -52,53 +51,54 @@ class DefaultAppNavigationStateService @Inject constructor() : AppNavigationStat
|
||||
is AppNavigationState.Space,
|
||||
is AppNavigationState.Room,
|
||||
is AppNavigationState.Thread,
|
||||
AppNavigationState.Root -> AppNavigationState.Session(sessionId)
|
||||
is AppNavigationState.Root -> AppNavigationState.Session(owner, sessionId)
|
||||
}
|
||||
currentAppNavigationState.value = newValue
|
||||
}
|
||||
|
||||
override fun onNavigateToSpace(spaceId: SpaceId) {
|
||||
override fun onNavigateToSpace(owner: String, spaceId: SpaceId) {
|
||||
val currentValue = currentAppNavigationState.value
|
||||
Timber.tag(loggerTag.value).d("Navigating to space $spaceId. Current state: $currentValue")
|
||||
val newValue: AppNavigationState.Space = when (currentValue) {
|
||||
AppNavigationState.Root -> error("onNavigateToSession() must be called first")
|
||||
is AppNavigationState.Session -> AppNavigationState.Space(spaceId, currentValue)
|
||||
is AppNavigationState.Space -> AppNavigationState.Space(spaceId, currentValue.parentSession)
|
||||
is AppNavigationState.Room -> AppNavigationState.Space(spaceId, currentValue.parentSpace.parentSession)
|
||||
is AppNavigationState.Thread -> AppNavigationState.Space(spaceId, currentValue.parentRoom.parentSpace.parentSession)
|
||||
is AppNavigationState.Session -> AppNavigationState.Space(owner, spaceId, currentValue)
|
||||
is AppNavigationState.Space -> AppNavigationState.Space(owner, spaceId, currentValue.parentSession)
|
||||
is AppNavigationState.Room -> AppNavigationState.Space(owner, spaceId, currentValue.parentSpace.parentSession)
|
||||
is AppNavigationState.Thread -> AppNavigationState.Space(owner, spaceId, currentValue.parentRoom.parentSpace.parentSession)
|
||||
}
|
||||
currentAppNavigationState.value = newValue
|
||||
}
|
||||
|
||||
override fun onNavigateToRoom(roomId: RoomId) {
|
||||
override fun onNavigateToRoom(owner: String, roomId: RoomId) {
|
||||
val currentValue = currentAppNavigationState.value
|
||||
Timber.tag(loggerTag.value).d("Navigating to room $roomId. Current state: $currentValue")
|
||||
val newValue: AppNavigationState.Room = when (currentValue) {
|
||||
AppNavigationState.Root -> error("onNavigateToSession() must be called first")
|
||||
is AppNavigationState.Session -> error("onNavigateToSpace() must be called first")
|
||||
is AppNavigationState.Space -> AppNavigationState.Room(roomId, currentValue)
|
||||
is AppNavigationState.Room -> AppNavigationState.Room(roomId, currentValue.parentSpace)
|
||||
is AppNavigationState.Thread -> AppNavigationState.Room(roomId, currentValue.parentRoom.parentSpace)
|
||||
is AppNavigationState.Space -> AppNavigationState.Room(owner, roomId, currentValue)
|
||||
is AppNavigationState.Room -> AppNavigationState.Room(owner, roomId, currentValue.parentSpace)
|
||||
is AppNavigationState.Thread -> AppNavigationState.Room(owner, roomId, currentValue.parentRoom.parentSpace)
|
||||
}
|
||||
currentAppNavigationState.value = newValue
|
||||
}
|
||||
|
||||
override fun onNavigateToThread(threadId: ThreadId) {
|
||||
override fun onNavigateToThread(owner: String, threadId: ThreadId) {
|
||||
val currentValue = currentAppNavigationState.value
|
||||
Timber.tag(loggerTag.value).d("Navigating to thread $threadId. Current state: $currentValue")
|
||||
val newValue: AppNavigationState.Thread = when (currentValue) {
|
||||
AppNavigationState.Root -> error("onNavigateToSession() must be called first")
|
||||
is AppNavigationState.Session -> error("onNavigateToSpace() must be called first")
|
||||
is AppNavigationState.Space -> error("onNavigateToRoom() must be called first")
|
||||
is AppNavigationState.Room -> AppNavigationState.Thread(threadId, currentValue)
|
||||
is AppNavigationState.Thread -> AppNavigationState.Thread(threadId, currentValue.parentRoom)
|
||||
is AppNavigationState.Room -> AppNavigationState.Thread(owner, threadId, currentValue)
|
||||
is AppNavigationState.Thread -> AppNavigationState.Thread(owner, threadId, currentValue.parentRoom)
|
||||
}
|
||||
currentAppNavigationState.value = newValue
|
||||
}
|
||||
|
||||
override fun onLeavingThread() {
|
||||
override fun onLeavingThread(owner: String) {
|
||||
val currentValue = currentAppNavigationState.value
|
||||
Timber.tag(loggerTag.value).d("Leaving thread. Current state: $currentValue")
|
||||
if (!currentValue.assertOwner(owner)) return
|
||||
val newValue: AppNavigationState.Room = when (currentValue) {
|
||||
AppNavigationState.Root -> error("onNavigateToSession() must be called first")
|
||||
is AppNavigationState.Session -> error("onNavigateToSpace() must be called first")
|
||||
@@ -109,9 +109,10 @@ class DefaultAppNavigationStateService @Inject constructor() : AppNavigationStat
|
||||
currentAppNavigationState.value = newValue
|
||||
}
|
||||
|
||||
override fun onLeavingRoom() {
|
||||
override fun onLeavingRoom(owner: String) {
|
||||
val currentValue = currentAppNavigationState.value
|
||||
Timber.tag(loggerTag.value).d("Leaving room. Current state: $currentValue")
|
||||
if (!currentValue.assertOwner(owner)) return
|
||||
val newValue: AppNavigationState.Space = when (currentValue) {
|
||||
AppNavigationState.Root -> error("onNavigateToSession() must be called first")
|
||||
is AppNavigationState.Session -> error("onNavigateToSpace() must be called first")
|
||||
@@ -122,9 +123,10 @@ class DefaultAppNavigationStateService @Inject constructor() : AppNavigationStat
|
||||
currentAppNavigationState.value = newValue
|
||||
}
|
||||
|
||||
override fun onLeavingSpace() {
|
||||
override fun onLeavingSpace(owner: String) {
|
||||
val currentValue = currentAppNavigationState.value
|
||||
Timber.tag(loggerTag.value).d("Leaving space. Current state: $currentValue")
|
||||
if (!currentValue.assertOwner(owner)) return
|
||||
val newValue: AppNavigationState.Session = when (currentValue) {
|
||||
AppNavigationState.Root -> error("onNavigateToSession() must be called first")
|
||||
is AppNavigationState.Session -> error("onNavigateToSpace() must be called first")
|
||||
@@ -135,9 +137,18 @@ class DefaultAppNavigationStateService @Inject constructor() : AppNavigationStat
|
||||
currentAppNavigationState.value = newValue
|
||||
}
|
||||
|
||||
override fun onLeavingSession() {
|
||||
override fun onLeavingSession(owner: String) {
|
||||
val currentValue = currentAppNavigationState.value
|
||||
Timber.tag(loggerTag.value).d("Leaving session. Current state: $currentValue")
|
||||
if (!currentValue.assertOwner(owner)) return
|
||||
currentAppNavigationState.value = AppNavigationState.Root
|
||||
}
|
||||
|
||||
private fun AppNavigationState.assertOwner(owner: String): Boolean {
|
||||
if (this.owner != owner) {
|
||||
Timber.tag(loggerTag.value).d("Can't leave current state as the owner is not the same (current = ${this.owner}, new = $owner)")
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,29 +24,35 @@ import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.A_SPACE_ID
|
||||
import io.element.android.libraries.matrix.test.A_THREAD_ID
|
||||
import io.element.android.services.appnavstate.api.AppNavigationState
|
||||
import io.element.android.services.appnavstate.test.A_ROOM_OWNER
|
||||
import io.element.android.services.appnavstate.test.A_SESSION_OWNER
|
||||
import io.element.android.services.appnavstate.test.A_SPACE_OWNER
|
||||
import io.element.android.services.appnavstate.test.A_THREAD_OWNER
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Assert.assertThrows
|
||||
import org.junit.Test
|
||||
|
||||
class DefaultAppNavigationStateServiceTest {
|
||||
|
||||
@Test
|
||||
fun testNavigation() = runTest {
|
||||
val service = DefaultAppNavigationStateService()
|
||||
service.onNavigateToSession(A_SESSION_ID)
|
||||
service.onNavigateToSpace(A_SPACE_ID)
|
||||
service.onNavigateToRoom(A_ROOM_ID)
|
||||
service.onNavigateToThread(A_THREAD_ID)
|
||||
service.onNavigateToSession(A_SESSION_OWNER, A_SESSION_ID)
|
||||
service.onNavigateToSpace(A_SPACE_OWNER, A_SPACE_ID)
|
||||
service.onNavigateToRoom(A_ROOM_OWNER, A_ROOM_ID)
|
||||
service.onNavigateToThread(A_THREAD_OWNER, A_THREAD_ID)
|
||||
assertThat(service.appNavigationStateFlow.first()).isEqualTo(
|
||||
AppNavigationState.Thread(
|
||||
A_THREAD_ID,
|
||||
A_THREAD_OWNER, A_THREAD_ID,
|
||||
AppNavigationState.Room(
|
||||
A_ROOM_OWNER,
|
||||
A_ROOM_ID,
|
||||
AppNavigationState.Space(
|
||||
A_SPACE_OWNER,
|
||||
A_SPACE_ID,
|
||||
AppNavigationState.Session(
|
||||
A_SESSION_OWNER,
|
||||
A_SESSION_ID
|
||||
)
|
||||
)
|
||||
@@ -58,6 +64,6 @@ class DefaultAppNavigationStateServiceTest {
|
||||
@Test
|
||||
fun testFailure() = runTest {
|
||||
val service = DefaultAppNavigationStateService()
|
||||
assertThrows(IllegalStateException::class.java) { service.onNavigateToSpace(A_SPACE_ID) }
|
||||
assertThrows(IllegalStateException::class.java) { service.onNavigateToSpace(A_SPACE_OWNER, A_SPACE_ID) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,4 +27,5 @@ android {
|
||||
dependencies {
|
||||
api(projects.libraries.matrix.api)
|
||||
api(projects.services.appnavstate.api)
|
||||
implementation(libs.coroutines.core)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,11 @@ import io.element.android.libraries.matrix.api.core.SpaceId
|
||||
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||
import io.element.android.services.appnavstate.api.AppNavigationState
|
||||
|
||||
const val A_SESSION_OWNER = "aSessionOwner"
|
||||
const val A_SPACE_OWNER = "aSpaceOwner"
|
||||
const val A_ROOM_OWNER = "aRoomOwner"
|
||||
const val A_THREAD_OWNER = "aThreadOwner"
|
||||
|
||||
fun anAppNavigationState(
|
||||
sessionId: SessionId? = null,
|
||||
spaceId: SpaceId? = MAIN_SPACE,
|
||||
@@ -32,17 +37,17 @@ fun anAppNavigationState(
|
||||
if (sessionId == null) {
|
||||
return AppNavigationState.Root
|
||||
}
|
||||
val session = AppNavigationState.Session(sessionId)
|
||||
val session = AppNavigationState.Session(A_SESSION_OWNER, sessionId)
|
||||
if (spaceId == null) {
|
||||
return session
|
||||
}
|
||||
val space = AppNavigationState.Space(spaceId, session)
|
||||
val space = AppNavigationState.Space(A_SPACE_OWNER, spaceId, session)
|
||||
if (roomId == null) {
|
||||
return space
|
||||
}
|
||||
val room = AppNavigationState.Room(roomId, space)
|
||||
val room = AppNavigationState.Room(A_ROOM_OWNER, roomId, space)
|
||||
if (threadId == null) {
|
||||
return room
|
||||
}
|
||||
return AppNavigationState.Thread(threadId, room)
|
||||
return AppNavigationState.Thread(A_THREAD_OWNER, threadId, room)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* 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.services.appnavstate.test
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.core.SpaceId
|
||||
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||
import io.element.android.services.appnavstate.api.AppNavigationState
|
||||
import io.element.android.services.appnavstate.api.AppNavigationStateService
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
|
||||
class NoopAppNavigationStateService : AppNavigationStateService {
|
||||
|
||||
private val currentAppNavigationState: MutableStateFlow<AppNavigationState> =
|
||||
MutableStateFlow(AppNavigationState.Root)
|
||||
override val appNavigationStateFlow: StateFlow<AppNavigationState> = currentAppNavigationState
|
||||
|
||||
override fun onNavigateToSession(owner: String, sessionId: SessionId) = Unit
|
||||
override fun onLeavingSession(owner: String) = Unit
|
||||
|
||||
override fun onNavigateToSpace(owner: String, spaceId: SpaceId) = Unit
|
||||
|
||||
override fun onLeavingSpace(owner: String) = Unit
|
||||
|
||||
override fun onNavigateToRoom(owner: String, roomId: RoomId) = Unit
|
||||
|
||||
override fun onLeavingRoom(owner: String) = Unit
|
||||
|
||||
override fun onNavigateToThread(owner: String, threadId: ThreadId) = Unit
|
||||
|
||||
override fun onLeavingThread(owner: String) = Unit
|
||||
}
|
||||
Reference in New Issue
Block a user