misc(navigation) : use JoinedRoomLoadedFlowNode as parent of SpaceFlowNode

This commit is contained in:
ganfra
2025-10-24 12:49:05 +02:00
parent cec4e105ec
commit d87c484dd8
6 changed files with 52 additions and 36 deletions

View File

@@ -62,7 +62,6 @@ import io.element.android.features.roomdirectory.api.RoomDescription
import io.element.android.features.roomdirectory.api.RoomDirectoryEntryPoint
import io.element.android.features.securebackup.api.SecureBackupEntryPoint
import io.element.android.features.share.api.ShareEntryPoint
import io.element.android.features.space.api.SpaceEntryPoint
import io.element.android.features.startchat.api.StartChatEntryPoint
import io.element.android.features.userprofile.api.UserProfileEntryPoint
import io.element.android.features.verifysession.api.IncomingVerificationEntryPoint
@@ -256,7 +255,7 @@ class LoggedInFlowNode(
val serverNames: List<String> = emptyList(),
val trigger: JoinedRoom.Trigger? = null,
val roomDescription: RoomDescription? = null,
val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages(),
val initialElement: RoomNavigationTarget = RoomNavigationTarget.Root(),
val targetId: UUID = UUID.randomUUID(),
) : NavTarget
@@ -358,7 +357,7 @@ class LoggedInFlowNode(
roomIdOrAlias = data.roomIdOrAlias,
serverNames = data.viaParameters,
trigger = JoinedRoom.Trigger.Timeline,
initialElement = RoomNavigationTarget.Messages(data.eventId),
initialElement = RoomNavigationTarget.Root(data.eventId),
)
if (pushToBackstack) {
backstack.push(target)
@@ -377,11 +376,6 @@ class LoggedInFlowNode(
backstack.push(NavTarget.Settings(PreferencesEntryPoint.InitialTarget.NotificationSettings))
}
}
val spaceCallback = object : SpaceEntryPoint.Callback {
override fun onOpenRoom(roomId: RoomId, viaParameters: List<String>) {
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), serverNames = viaParameters))
}
}
val inputs = RoomFlowNode.Inputs(
roomIdOrAlias = navTarget.roomIdOrAlias,
roomDescription = Optional.ofNullable(navTarget.roomDescription),
@@ -389,7 +383,7 @@ class LoggedInFlowNode(
trigger = Optional.ofNullable(navTarget.trigger),
initialElement = navTarget.initialElement
)
createNode<RoomFlowNode>(buildContext, plugins = listOf(inputs, joinedRoomCallback, spaceCallback))
createNode<RoomFlowNode>(buildContext, plugins = listOf(inputs, joinedRoomCallback))
}
is NavTarget.UserProfile -> {
val callback = object : UserProfileEntryPoint.Callback {
@@ -421,7 +415,7 @@ class LoggedInFlowNode(
}
override fun navigateTo(roomId: RoomId, eventId: EventId) {
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.Messages(eventId)))
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), initialElement = RoomNavigationTarget.Root(eventId)))
}
}
val inputs = PreferencesEntryPoint.Params(navTarget.initialElement)
@@ -516,9 +510,7 @@ class LoggedInFlowNode(
roomIdOrAlias = roomIdOrAlias,
serverNames = serverNames,
trigger = trigger,
initialElement = RoomNavigationTarget.Messages(
focusedEventId = eventId
)
initialElement = RoomNavigationTarget.Root(eventId = eventId)
)
backstack.accept(AttachRoomOperation(roomNavTarget, clearBackstack))
}

View File

@@ -9,7 +9,6 @@ package io.element.android.appnav.room
import android.os.Parcelable
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.lifecycle.lifecycleScope
import com.bumble.appyx.core.modality.BuildContext
@@ -37,7 +36,6 @@ import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.NodeInputs
import io.element.android.libraries.architecture.createNode
import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.core.bool.orFalse
import io.element.android.libraries.core.coroutine.withPreviousValue
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.MatrixClient
@@ -49,11 +47,11 @@ import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
import io.element.android.libraries.matrix.api.room.alias.ResolvedRoomAlias
import io.element.android.libraries.matrix.ui.room.LoadingRoomState
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
@@ -130,7 +128,6 @@ class RoomFlowNode(
private fun subscribeToRoomInfoFlow(roomId: RoomId, serverNames: List<String>) {
val roomInfoFlow = client.getRoomInfoFlow(roomId)
val isSpaceFlow = roomInfoFlow.map { it.getOrNull()?.isSpace.orFalse() }.distinctUntilChanged()
// This observes the local membership changes for the room
val membershipUpdateFlow = membershipObserver.updates
@@ -143,14 +140,10 @@ class RoomFlowNode(
.map { it.getOrNull()?.currentUserMembership }
.distinctUntilChanged()
.withPreviousValue()
combine(currentMembershipFlow, isSpaceFlow) { (previousMembership, membership), isSpace ->
currentMembershipFlow.onEach { (previousMembership, membership) ->
Timber.d("Room membership: $membership")
if (membership == CurrentUserMembership.JOINED) {
if (isSpace) {
backstack.newRoot(NavTarget.JoinedSpace(spaceId = roomId))
} else {
backstack.newRoot(NavTarget.JoinedRoom(roomId))
}
backstack.newRoot(NavTarget.JoinedRoom(roomId))
} else {
val leavingFromCurrentDevice =
membership == CurrentUserMembership.LEFT &&

View File

@@ -13,7 +13,7 @@ import kotlinx.parcelize.Parcelize
sealed interface RoomNavigationTarget : Parcelable {
@Parcelize
data class Messages(val focusedEventId: EventId? = null) : RoomNavigationTarget
data class Root(val eventId: EventId? = null) : RoomNavigationTarget
@Parcelize
data object Details : RoomNavigationTarget

View File

@@ -22,8 +22,11 @@ import dev.zacsweers.metro.AssistedInject
import io.element.android.annotations.ContributesNode
import io.element.android.appnav.di.RoomGraphFactory
import io.element.android.appnav.room.RoomNavigationTarget
import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode.Inputs
import io.element.android.appnav.room.joined.JoinedRoomLoadedFlowNode.NavTarget
import io.element.android.features.messages.api.MessagesEntryPoint
import io.element.android.features.roomdetails.api.RoomDetailsEntryPoint
import io.element.android.features.space.api.SpaceEntryPoint
import io.element.android.libraries.architecture.BackstackView
import io.element.android.libraries.architecture.BaseFlowNode
import io.element.android.libraries.architecture.NodeInputs
@@ -51,6 +54,7 @@ class JoinedRoomLoadedFlowNode(
@Assisted plugins: List<Plugin>,
private val messagesEntryPoint: MessagesEntryPoint,
private val roomDetailsEntryPoint: RoomDetailsEntryPoint,
private val spaceEntryPoint: SpaceEntryPoint,
private val appNavigationStateService: AppNavigationStateService,
@SessionCoroutineScope
private val sessionCoroutineScope: CoroutineScope,
@@ -59,11 +63,7 @@ class JoinedRoomLoadedFlowNode(
roomGraphFactory: RoomGraphFactory,
) : BaseFlowNode<JoinedRoomLoadedFlowNode.NavTarget>(
backstack = BackStack(
initialElement = when (val input = plugins.filterIsInstance<Inputs>().first().initialElement) {
is RoomNavigationTarget.Messages -> NavTarget.Messages(input.focusedEventId)
RoomNavigationTarget.Details -> NavTarget.RoomDetails
RoomNavigationTarget.NotificationSettings -> NavTarget.RoomNotificationSettings
},
initialElement = initialElement(plugins),
savedStateMap = buildContext.savedStateMap,
),
buildContext = buildContext,
@@ -154,9 +154,25 @@ class JoinedRoomLoadedFlowNode(
NavTarget.RoomNotificationSettings -> {
createRoomDetailsNode(buildContext, RoomDetailsEntryPoint.InitialTarget.RoomNotificationSettings)
}
NavTarget.Space -> {
createSpaceNode(buildContext)
}
}
}
private fun createSpaceNode(buildContext: BuildContext): Node {
val callback = object : SpaceEntryPoint.Callback {
override fun onOpenRoom(roomId: RoomId, viaParameters: List<String>) {
callbacks.forEach { it.onOpenRoom(roomId, viaParameters) }
}
}
return spaceEntryPoint.nodeBuilder(this, buildContext)
.inputs(SpaceEntryPoint.Inputs(roomId = inputs.room.roomId))
.callback(callback)
.build()
}
private fun createMessagesNode(
buildContext: BuildContext,
navTarget: NavTarget.Messages,
@@ -188,6 +204,9 @@ class JoinedRoomLoadedFlowNode(
}
sealed interface NavTarget : Parcelable {
@Parcelize
data object Space : NavTarget
@Parcelize
data class Messages(val focusedEventId: EventId? = null) : NavTarget
@@ -206,3 +225,18 @@ class JoinedRoomLoadedFlowNode(
BackstackView()
}
}
private fun initialElement(plugins: List<Plugin>): NavTarget {
val input = plugins.filterIsInstance<Inputs>().single()
return when (input.initialElement) {
is RoomNavigationTarget.Root -> {
if (input.room.roomInfoFlow.value.isSpace) {
NavTarget.Space
} else {
NavTarget.Messages(input.initialElement.eventId)
}
}
RoomNavigationTarget.Details -> NavTarget.RoomDetails
RoomNavigationTarget.NotificationSettings -> NavTarget.RoomNotificationSettings
}
}

View File

@@ -19,8 +19,6 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.atomic.molecules.IconTitlePlaceholdersRowMolecule
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.designsystem.components.button.BackButton
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
@@ -76,7 +74,6 @@ private fun LoadingRoomTopBar(
BackButton(onClick = onBackClick)
},
title = {
IconTitlePlaceholdersRowMolecule(iconSize = AvatarSize.TimelineRoom.dp)
},
)
}

View File

@@ -120,7 +120,7 @@ class JoinedRoomLoadedFlowNodeTest {
// GIVEN
val room = FakeJoinedRoom(baseRoom = FakeBaseRoom(updateMembersResult = {}))
val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Messages())
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Root())
val roomFlowNode = createJoinedRoomLoadedFlowNode(
plugins = listOf(inputs),
messagesEntryPoint = fakeMessagesEntryPoint,
@@ -141,7 +141,7 @@ class JoinedRoomLoadedFlowNodeTest {
val room = FakeJoinedRoom(baseRoom = FakeBaseRoom(updateMembersResult = {}))
val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
val fakeRoomDetailsEntryPoint = FakeRoomDetailsEntryPoint()
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Messages())
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Root())
val roomFlowNode = createJoinedRoomLoadedFlowNode(
plugins = listOf(inputs),
messagesEntryPoint = fakeMessagesEntryPoint,
@@ -162,7 +162,7 @@ class JoinedRoomLoadedFlowNodeTest {
val room = FakeJoinedRoom(baseRoom = FakeBaseRoom(updateMembersResult = {}))
val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
val fakeRoomDetailsEntryPoint = FakeRoomDetailsEntryPoint()
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Messages())
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Root())
val activeRoomsHolder = ActiveRoomsHolder()
val roomFlowNode = createJoinedRoomLoadedFlowNode(
plugins = listOf(inputs),
@@ -185,7 +185,7 @@ class JoinedRoomLoadedFlowNodeTest {
val room = FakeJoinedRoom(baseRoom = FakeBaseRoom(updateMembersResult = {}))
val fakeMessagesEntryPoint = FakeMessagesEntryPoint()
val fakeRoomDetailsEntryPoint = FakeRoomDetailsEntryPoint()
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Messages())
val inputs = JoinedRoomLoadedFlowNode.Inputs(room, RoomNavigationTarget.Root())
val activeRoomsHolder = ActiveRoomsHolder().apply {
addRoom(room)
}