Open user profile and room with event from permalink
This commit is contained in:
parent
682fd45e00
commit
0476bd95c8
93 changed files with 1499 additions and 370 deletions
|
|
@ -56,6 +56,7 @@ import io.element.android.features.roomdirectory.api.RoomDescription
|
|||
import io.element.android.features.roomdirectory.api.RoomDirectoryEntryPoint
|
||||
import io.element.android.features.roomlist.api.RoomListEntryPoint
|
||||
import io.element.android.features.securebackup.api.SecureBackupEntryPoint
|
||||
import io.element.android.features.userprofile.api.UserProfileEntryPoint
|
||||
import io.element.android.libraries.architecture.BackstackView
|
||||
import io.element.android.libraries.architecture.BaseFlowNode
|
||||
import io.element.android.libraries.architecture.createNode
|
||||
|
|
@ -64,9 +65,11 @@ import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatch
|
|||
import io.element.android.libraries.di.AppScope
|
||||
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.MAIN_SPACE
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||
import io.element.android.libraries.matrix.api.sync.SyncState
|
||||
|
|
@ -91,6 +94,7 @@ class LoggedInFlowNode @AssistedInject constructor(
|
|||
private val createRoomEntryPoint: CreateRoomEntryPoint,
|
||||
private val appNavigationStateService: AppNavigationStateService,
|
||||
private val secureBackupEntryPoint: SecureBackupEntryPoint,
|
||||
private val userProfileEntryPoint: UserProfileEntryPoint,
|
||||
private val ftueEntryPoint: FtueEntryPoint,
|
||||
private val coroutineScope: CoroutineScope,
|
||||
private val networkMonitor: NetworkMonitor,
|
||||
|
|
@ -197,6 +201,11 @@ class LoggedInFlowNode @AssistedInject constructor(
|
|||
val initialElement: RoomNavigationTarget = RoomNavigationTarget.Messages()
|
||||
) : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data class UserProfile(
|
||||
val userId: UserId,
|
||||
) : NavTarget
|
||||
|
||||
@Parcelize
|
||||
data class Settings(
|
||||
val initialElement: PreferencesEntryPoint.InitialTarget = PreferencesEntryPoint.InitialTarget.Root
|
||||
|
|
@ -270,14 +279,14 @@ class LoggedInFlowNode @AssistedInject constructor(
|
|||
}
|
||||
|
||||
override fun onForwardedToSingleRoom(roomId: RoomId) {
|
||||
coroutineScope.launch { attachRoom(roomId) }
|
||||
coroutineScope.launch { attachRoom(roomId.toRoomIdOrAlias()) }
|
||||
}
|
||||
|
||||
override fun onPermalinkClicked(data: PermalinkData) {
|
||||
when (data) {
|
||||
is PermalinkData.UserLink -> {
|
||||
// FIXME Add a user profile screen.
|
||||
Timber.e("User link clicked: ${data.userId}. TODO Add a user profile screen")
|
||||
// Should not happen (handled by MessagesNode)
|
||||
Timber.e("User link clicked: ${data.userId}.")
|
||||
}
|
||||
is PermalinkData.RoomLink -> {
|
||||
backstack.push(
|
||||
|
|
@ -306,6 +315,17 @@ class LoggedInFlowNode @AssistedInject constructor(
|
|||
)
|
||||
createNode<RoomFlowNode>(buildContext, plugins = listOf(inputs, callback))
|
||||
}
|
||||
is NavTarget.UserProfile -> {
|
||||
val callback = object : UserProfileEntryPoint.Callback {
|
||||
override fun onOpenRoom(roomId: RoomId) {
|
||||
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias()))
|
||||
}
|
||||
}
|
||||
userProfileEntryPoint.nodeBuilder(this, buildContext)
|
||||
.params(UserProfileEntryPoint.Params(userId = navTarget.userId))
|
||||
.callback(callback)
|
||||
.build()
|
||||
}
|
||||
is NavTarget.Settings -> {
|
||||
val callback = object : PreferencesEntryPoint.Callback {
|
||||
override fun onOpenBugReport() {
|
||||
|
|
@ -321,7 +341,7 @@ class LoggedInFlowNode @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
val inputs = PreferencesEntryPoint.Params(navTarget.initialElement)
|
||||
return preferencesEntryPoint.nodeBuilder(this, buildContext)
|
||||
preferencesEntryPoint.nodeBuilder(this, buildContext)
|
||||
.params(inputs)
|
||||
.callback(callback)
|
||||
.build()
|
||||
|
|
@ -363,12 +383,32 @@ class LoggedInFlowNode @AssistedInject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun attachRoom(roomId: RoomId) {
|
||||
suspend fun attachRoom(roomIdOrAlias: RoomIdOrAlias, eventId: EventId? = null) {
|
||||
waitForNavTargetAttached { navTarget ->
|
||||
navTarget is NavTarget.RoomList
|
||||
}
|
||||
attachChild<RoomFlowNode> {
|
||||
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias()))
|
||||
backstack.push(
|
||||
NavTarget.Room(
|
||||
roomIdOrAlias = roomIdOrAlias,
|
||||
initialElement = RoomNavigationTarget.Messages(
|
||||
focusedEventId = eventId
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun attachUser(userId: UserId) {
|
||||
waitForNavTargetAttached { navTarget ->
|
||||
navTarget is NavTarget.RoomList
|
||||
}
|
||||
attachChild<Node> {
|
||||
backstack.push(
|
||||
NavTarget.UserProfile(
|
||||
userId = userId,
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ import io.element.android.libraries.designsystem.theme.components.CircularProgre
|
|||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||
import io.element.android.libraries.sessionstorage.api.LoggedInState
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
|
|
@ -279,9 +281,31 @@ class RootFlowNode @AssistedInject constructor(
|
|||
when (resolvedIntent) {
|
||||
is ResolvedIntent.Navigation -> navigateTo(resolvedIntent.deeplinkData)
|
||||
is ResolvedIntent.Oidc -> onOidcAction(resolvedIntent.oidcAction)
|
||||
is ResolvedIntent.Permalink -> navigateTo(resolvedIntent.permalinkData)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun navigateTo(permalinkData: PermalinkData) {
|
||||
Timber.d("Navigating to $permalinkData")
|
||||
attachSession(null)
|
||||
.attachSession()
|
||||
.apply {
|
||||
when (permalinkData) {
|
||||
is PermalinkData.FallbackLink -> Unit
|
||||
is PermalinkData.RoomEmailInviteLink -> Unit
|
||||
is PermalinkData.RoomLink -> {
|
||||
attachRoom(
|
||||
roomIdOrAlias = permalinkData.roomIdOrAlias,
|
||||
eventId = permalinkData.eventId,
|
||||
)
|
||||
}
|
||||
is PermalinkData.UserLink -> {
|
||||
attachUser(permalinkData.userId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun navigateTo(deeplinkData: DeeplinkData) {
|
||||
Timber.d("Navigating to $deeplinkData")
|
||||
attachSession(deeplinkData.sessionId)
|
||||
|
|
@ -289,7 +313,7 @@ class RootFlowNode @AssistedInject constructor(
|
|||
.apply {
|
||||
when (deeplinkData) {
|
||||
is DeeplinkData.Root -> Unit // The room list will always be shown, observing FtueState
|
||||
is DeeplinkData.Room -> attachRoom(deeplinkData.roomId)
|
||||
is DeeplinkData.Room -> attachRoom(deeplinkData.roomId.toRoomIdOrAlias())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -298,10 +322,11 @@ class RootFlowNode @AssistedInject constructor(
|
|||
oidcActionFlow.post(oidcAction)
|
||||
}
|
||||
|
||||
private suspend fun attachSession(sessionId: SessionId): LoggedInAppScopeFlowNode {
|
||||
// [sessionId] will be null for permalink.
|
||||
private suspend fun attachSession(sessionId: SessionId?): LoggedInAppScopeFlowNode {
|
||||
// TODO handle multi-session
|
||||
return waitForChildAttached { navTarget ->
|
||||
navTarget is NavTarget.LoggedInFlow && navTarget.sessionId == sessionId
|
||||
navTarget is NavTarget.LoggedInFlow && (sessionId == null || navTarget.sessionId == sessionId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,27 +21,41 @@ import io.element.android.features.login.api.oidc.OidcAction
|
|||
import io.element.android.features.login.api.oidc.OidcIntentResolver
|
||||
import io.element.android.libraries.deeplink.DeeplinkData
|
||||
import io.element.android.libraries.deeplink.DeeplinkParser
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
|
||||
import timber.log.Timber
|
||||
import javax.inject.Inject
|
||||
|
||||
sealed interface ResolvedIntent {
|
||||
data class Navigation(val deeplinkData: DeeplinkData) : ResolvedIntent
|
||||
data class Oidc(val oidcAction: OidcAction) : ResolvedIntent
|
||||
data class Permalink(val permalinkData: PermalinkData) : ResolvedIntent
|
||||
}
|
||||
|
||||
class IntentResolver @Inject constructor(
|
||||
private val deeplinkParser: DeeplinkParser,
|
||||
private val oidcIntentResolver: OidcIntentResolver
|
||||
private val oidcIntentResolver: OidcIntentResolver,
|
||||
private val permalinkParser: PermalinkParser,
|
||||
) {
|
||||
fun resolve(intent: Intent): ResolvedIntent? {
|
||||
if (intent.canBeIgnored()) return null
|
||||
|
||||
// Coming from a notification?
|
||||
val deepLinkData = deeplinkParser.getFromIntent(intent)
|
||||
if (deepLinkData != null) return ResolvedIntent.Navigation(deepLinkData)
|
||||
|
||||
// Coming during login using Oidc?
|
||||
val oidcAction = oidcIntentResolver.resolve(intent)
|
||||
if (oidcAction != null) return ResolvedIntent.Oidc(oidcAction)
|
||||
|
||||
// External link clicked? (matrix.to, element.io, etc.)
|
||||
val permalinkData = intent
|
||||
.takeIf { it.action == Intent.ACTION_VIEW }
|
||||
?.dataString
|
||||
?.let { permalinkParser.parse(it) }
|
||||
?.takeIf { it !is PermalinkData.FallbackLink }
|
||||
if (permalinkData != null) return ResolvedIntent.Permalink(permalinkData)
|
||||
|
||||
// Unknown intent
|
||||
Timber.w("Unknown intent")
|
||||
return null
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package io.element.android.appnav.intent
|
|||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.core.net.toUri
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.features.login.api.oidc.OidcAction
|
||||
|
|
@ -26,9 +27,11 @@ import io.element.android.features.login.impl.oidc.OidcUrlParser
|
|||
import io.element.android.libraries.deeplink.DeepLinkCreator
|
||||
import io.element.android.libraries.deeplink.DeeplinkData
|
||||
import io.element.android.libraries.deeplink.DeeplinkParser
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_SESSION_ID
|
||||
import io.element.android.libraries.matrix.test.A_THREAD_ID
|
||||
import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser
|
||||
import org.junit.Assert.assertThrows
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
|
@ -179,6 +182,9 @@ class IntentResolverTest {
|
|||
oidcIntentResolver = DefaultOidcIntentResolver(
|
||||
oidcUrlParser = OidcUrlParser()
|
||||
),
|
||||
permalinkParser = FakePermalinkParser(
|
||||
result = { PermalinkData.FallbackLink(Uri.parse("https://matrix.org")) }
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue