Handle permalink navigation - WIP

- prepare navigating to an Event
- add NodeBuilder to MessagesEntryPoint
This commit is contained in:
Benoit Marty 2024-04-16 10:21:26 +02:00 committed by Benoit Marty
parent 68346dd782
commit 2a467bd49b
11 changed files with 186 additions and 43 deletions

View file

@ -66,6 +66,7 @@ import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.MAIN_SPACE
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.permalink.PermalinkData
import io.element.android.libraries.matrix.api.sync.SyncState
import io.element.android.services.appnavstate.api.AppNavigationStateService
import kotlinx.coroutines.CoroutineScope
@ -191,7 +192,7 @@ class LoggedInFlowNode @AssistedInject constructor(
data class Room(
val roomId: RoomId,
val roomDescription: RoomDescription? = null,
val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages
val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages(null)
) : NavTarget
@Parcelize
@ -270,6 +271,40 @@ class LoggedInFlowNode @AssistedInject constructor(
coroutineScope.launch { attachRoom(roomId) }
}
override fun onPermalinkClicked(data: PermalinkData) {
coroutineScope.launch {
when (data) {
is PermalinkData.UserLink -> {
// FIXME: Add a user profile screen.
Timber.e("User link clicked: ${data.userId}. TODO Add a user profile screen")
}
is PermalinkData.RoomIdLink -> {
backstack.push(NavTarget.Room(data.roomId))
}
is PermalinkData.RoomAliasLink -> {
// FIXME Implement room alias navigation
Timber.e("Room alias link clicked: ${data.roomAlias}. TODO Handle a room alias navigation")
}
is PermalinkData.EventIdAliasLink -> {
// FIXME Implement event alias navigation
Timber.e("Event with room alias link clicked: ${data.eventId}. TODO Handle an event with room alias navigation")
}
is PermalinkData.EventIdLink -> {
backstack.push(
NavTarget.Room(
data.roomId,
initialElement = RoomNavigationTarget.Messages(data.eventId)
)
)
}
is PermalinkData.FallbackLink,
is PermalinkData.RoomEmailInviteLink -> {
// Should not happen (handled by MessagesNode)
}
}
}
}
override fun onOpenGlobalNotificationSettings() {
backstack.push(NavTarget.Settings(PreferencesEntryPoint.InitialTarget.NotificationSettings))
}

View file

@ -75,7 +75,7 @@ class RoomFlowNode @AssistedInject constructor(
data class Inputs(
val roomId: RoomId,
val roomDescription: Optional<RoomDescription>,
val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages,
val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages(),
) : NodeInputs
private val inputs: Inputs = inputs()

View file

@ -16,8 +16,17 @@
package io.element.android.appnav.room
enum class RoomNavigationTarget {
Messages,
Details,
NotificationSettings,
import android.os.Parcelable
import io.element.android.libraries.matrix.api.core.EventId
import kotlinx.parcelize.Parcelize
sealed interface RoomNavigationTarget : Parcelable {
@Parcelize
data class Messages(val eventId: EventId? = null) : RoomNavigationTarget
@Parcelize
data object Details : RoomNavigationTarget
@Parcelize
data object NotificationSettings : RoomNavigationTarget
}

View file

@ -69,7 +69,7 @@ class JoinedRoomFlowNode @AssistedInject constructor(
) {
data class Inputs(
val roomId: RoomId,
val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages,
val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages(),
) : NodeInputs
private val inputs: Inputs = inputs()

View file

@ -42,8 +42,10 @@ import io.element.android.libraries.architecture.inputs
import io.element.android.libraries.di.DaggerComponentOwner
import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.core.RoomId
import io.element.android.libraries.matrix.api.core.UserId
import io.element.android.libraries.matrix.api.permalink.PermalinkData
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.services.appnavstate.api.AppNavigationStateService
import kotlinx.coroutines.CoroutineScope
@ -63,8 +65,8 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor(
roomComponentFactory: RoomComponentFactory,
) : BaseFlowNode<JoinedRoomLoadedFlowNode.NavTarget>(
backstack = BackStack(
initialElement = when (plugins.filterIsInstance(Inputs::class.java).first().initialElement) {
RoomNavigationTarget.Messages -> NavTarget.Messages
initialElement = when (val input = plugins.filterIsInstance(Inputs::class.java).first().initialElement) {
is RoomNavigationTarget.Messages -> NavTarget.Messages(input.eventId)
RoomNavigationTarget.Details -> NavTarget.RoomDetails
RoomNavigationTarget.NotificationSettings -> NavTarget.RoomNotificationSettings
},
@ -75,13 +77,14 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor(
), DaggerComponentOwner {
interface Callback : Plugin {
fun onOpenRoom(roomId: RoomId)
fun onPermalinkClicked(data: PermalinkData)
fun onForwardedToSingleRoom(roomId: RoomId)
fun onOpenGlobalNotificationSettings()
}
data class Inputs(
val room: MatrixRoom,
val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages,
val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages(),
) : NodeInputs
private val inputs: Inputs = inputs()
@ -139,7 +142,7 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor(
override fun resolve(navTarget: NavTarget, buildContext: BuildContext): Node {
return when (navTarget) {
NavTarget.Messages -> {
is NavTarget.Messages -> {
val callback = object : MessagesEntryPoint.Callback {
override fun onRoomDetailsClicked() {
backstack.push(NavTarget.RoomDetails)
@ -149,11 +152,18 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor(
backstack.push(NavTarget.RoomMemberDetails(userId))
}
override fun onPermalinkClicked(data: PermalinkData) {
callbacks.forEach { it.onPermalinkClicked(data) }
}
override fun onForwardedToSingleRoom(roomId: RoomId) {
callbacks.forEach { it.onForwardedToSingleRoom(roomId) }
}
}
messagesEntryPoint.createNode(this, buildContext, callback)
messagesEntryPoint.nodeBuilder(this, buildContext)
.params(MessagesEntryPoint.Params(navTarget.eventId))
.callback(callback)
.build()
}
NavTarget.RoomDetails -> {
createRoomDetailsNode(buildContext, RoomDetailsEntryPoint.InitialTarget.RoomDetails)
@ -169,7 +179,7 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor(
sealed interface NavTarget : Parcelable {
@Parcelize
data object Messages : NavTarget
data class Messages(val eventId: EventId? = null) : NavTarget
@Parcelize
data object RoomDetails : NavTarget

View file

@ -47,14 +47,30 @@ class JoinRoomLoadedFlowNodeTest {
@get:Rule
val mainDispatcherRule = MainDispatcherRule()
private class FakeMessagesEntryPoint : MessagesEntryPoint {
private class FakeMessagesEntryPoint : MessagesEntryPoint, MessagesEntryPoint.NodeBuilder {
var buildContext: BuildContext? = null
var nodeId: String? = null
var parameters: MessagesEntryPoint.Params? = null
var callback: MessagesEntryPoint.Callback? = null
override fun createNode(parentNode: Node, buildContext: BuildContext, callback: MessagesEntryPoint.Callback): Node {
return node(buildContext) {}.also {
override fun nodeBuilder(parentNode: Node, buildContext: BuildContext): MessagesEntryPoint.NodeBuilder {
this.buildContext = buildContext
return this
}
override fun params(params: MessagesEntryPoint.Params): MessagesEntryPoint.NodeBuilder {
parameters = params
return this
}
override fun callback(callback: MessagesEntryPoint.Callback): MessagesEntryPoint.NodeBuilder {
this.callback = callback
return this
}
override fun build(): Node {
return node(buildContext!!) {}.also {
nodeId = it.id
this.callback = callback
}
}
}
@ -118,9 +134,9 @@ class JoinRoomLoadedFlowNodeTest {
val roomFlowNodeTestHelper = roomFlowNode.parentNodeTestHelper()
// THEN
assertThat(roomFlowNode.backstack.activeElement).isEqualTo(JoinedRoomLoadedFlowNode.NavTarget.Messages)
roomFlowNodeTestHelper.assertChildHasLifecycle(JoinedRoomLoadedFlowNode.NavTarget.Messages, Lifecycle.State.CREATED)
val messagesNode = roomFlowNode.childNode(JoinedRoomLoadedFlowNode.NavTarget.Messages)!!
assertThat(roomFlowNode.backstack.activeElement).isEqualTo(JoinedRoomLoadedFlowNode.NavTarget.Messages())
roomFlowNodeTestHelper.assertChildHasLifecycle(JoinedRoomLoadedFlowNode.NavTarget.Messages(), Lifecycle.State.CREATED)
val messagesNode = roomFlowNode.childNode(JoinedRoomLoadedFlowNode.NavTarget.Messages())!!
assertThat(messagesNode.id).isEqualTo(fakeMessagesEntryPoint.nodeId)
}