Provide calculated server names when opening a room from another (#5155)
* Provide calculated server names when opening a room from another, based on the most frequently used domain names in the user ids for the users in the room. This helps when following permalinks or navigating to the successor room of a tombstoned one. Previously, the `/summary` endpoint was failing because no server names were used in the `via` parameters.
This commit is contained in:
parent
1a31e49f1e
commit
516c3cfda3
16 changed files with 54 additions and 27 deletions
|
|
@ -335,8 +335,8 @@ class LoggedInFlowNode @AssistedInject constructor(
|
|||
}
|
||||
is NavTarget.Room -> {
|
||||
val callback = object : JoinedRoomLoadedFlowNode.Callback {
|
||||
override fun onOpenRoom(roomId: RoomId) {
|
||||
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias()))
|
||||
override fun onOpenRoom(roomId: RoomId, serverNames: List<String>) {
|
||||
backstack.push(NavTarget.Room(roomId.toRoomIdOrAlias(), serverNames))
|
||||
}
|
||||
|
||||
override fun onForwardedToSingleRoom(roomId: RoomId) {
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor(
|
|||
plugins = plugins,
|
||||
), DaggerComponentOwner {
|
||||
interface Callback : Plugin {
|
||||
fun onOpenRoom(roomId: RoomId)
|
||||
fun onOpenRoom(roomId: RoomId, serverNames: List<String>)
|
||||
fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean)
|
||||
fun onForwardedToSingleRoom(roomId: RoomId)
|
||||
fun onOpenGlobalNotificationSettings()
|
||||
|
|
@ -121,8 +121,8 @@ class JoinedRoomLoadedFlowNode @AssistedInject constructor(
|
|||
callbacks.forEach { it.onOpenGlobalNotificationSettings() }
|
||||
}
|
||||
|
||||
override fun onOpenRoom(roomId: RoomId) {
|
||||
callbacks.forEach { it.onOpenRoom(roomId) }
|
||||
override fun onOpenRoom(roomId: RoomId, serverNames: List<String>) {
|
||||
callbacks.forEach { it.onOpenRoom(roomId, serverNames) }
|
||||
}
|
||||
|
||||
override fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean) {
|
||||
|
|
|
|||
|
|
@ -20,5 +20,5 @@ interface MessagesNavigator {
|
|||
fun onReportContentClick(eventId: EventId, senderId: UserId)
|
||||
fun onEditPollClick(eventId: EventId)
|
||||
fun onPreviewAttachment(attachments: ImmutableList<Attachment>)
|
||||
fun onNavigateToRoom(roomId: RoomId)
|
||||
fun onNavigateToRoom(roomId: RoomId, serverNames: List<String>)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ import io.element.android.libraries.mediaplayer.api.MediaPlayer
|
|||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
|
@ -213,11 +214,11 @@ class MessagesNode @AssistedInject constructor(
|
|||
callbacks.forEach { it.onPreviewAttachments(attachments) }
|
||||
}
|
||||
|
||||
override fun onNavigateToRoom(roomId: RoomId) {
|
||||
override fun onNavigateToRoom(roomId: RoomId, serverNames: List<String>) {
|
||||
if (roomId == room.roomId) {
|
||||
displaySameRoomToast()
|
||||
} else {
|
||||
val permalinkData = PermalinkData.RoomLink(roomId.toRoomIdOrAlias())
|
||||
val permalinkData = PermalinkData.RoomLink(roomId.toRoomIdOrAlias(), viaParameters = serverNames.toImmutableList())
|
||||
callbacks.forEach { it.onPermalinkClick(permalinkData) }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -278,7 +278,7 @@ fun MessagesView(
|
|||
state = state,
|
||||
onLinkClick = { url, customTab -> onLinkClick(url, customTab) },
|
||||
onRoomSuccessorClick = { roomId ->
|
||||
state.timelineState.eventSink(TimelineEvents.NavigateToRoom(roomId = roomId))
|
||||
state.timelineState.eventSink(TimelineEvents.NavigateToPredecessorOrSuccessorRoom(roomId = roomId))
|
||||
},
|
||||
)
|
||||
},
|
||||
|
|
|
|||
|
|
@ -31,7 +31,11 @@ sealed interface TimelineEvents {
|
|||
data class ComputeVerifiedUserSendFailure(val event: TimelineItem.Event) : EventFromTimelineItem
|
||||
data class ShowShieldDialog(val messageShield: MessageShield) : EventFromTimelineItem
|
||||
data class LoadMore(val direction: Timeline.PaginationDirection) : EventFromTimelineItem
|
||||
data class NavigateToRoom(val roomId: RoomId) : EventFromTimelineItem
|
||||
|
||||
/**
|
||||
* Navigate to the predecessor or successor room of the current room.
|
||||
*/
|
||||
data class NavigateToPredecessorOrSuccessorRoom(val roomId: RoomId) : EventFromTimelineItem
|
||||
|
||||
/**
|
||||
* Events coming from a poll item.
|
||||
|
|
|
|||
|
|
@ -178,8 +178,10 @@ class TimelinePresenter @AssistedInject constructor(
|
|||
is TimelineEvents.ComputeVerifiedUserSendFailure -> {
|
||||
resolveVerifiedUserSendFailureState.eventSink(ResolveVerifiedUserSendFailureEvents.ComputeForMessage(event.event))
|
||||
}
|
||||
is TimelineEvents.NavigateToRoom -> {
|
||||
navigator.onNavigateToRoom(event.roomId)
|
||||
is TimelineEvents.NavigateToPredecessorOrSuccessorRoom -> {
|
||||
// Navigate to the predecessor or successor room
|
||||
val serverNames = calculateServerNamesForRoom(room)
|
||||
navigator.onNavigateToRoom(event.roomId, serverNames)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -353,3 +355,19 @@ private fun FocusRequestState.onFocusEventRender(): FocusRequestState {
|
|||
else -> this
|
||||
}
|
||||
}
|
||||
|
||||
// Workaround for not having the server names available, get possible server names from the user ids of the room members
|
||||
private fun calculateServerNamesForRoom(room: JoinedRoom): List<String> {
|
||||
// If we have no room members, return right ahead
|
||||
val serverNames = room.membersStateFlow.value.roomMembers() ?: return emptyList()
|
||||
|
||||
// Otherwise get the three most common server names from the user ids of the room members
|
||||
return serverNames
|
||||
.mapNotNull { it.userId.domainName }
|
||||
.groupingBy { it }
|
||||
.eachCount()
|
||||
.let { map ->
|
||||
map.keys.sortedByDescending { map[it] }
|
||||
}
|
||||
.take(3)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ fun TimelineItemVirtualRow(
|
|||
roomName = timelineRoomInfo.name,
|
||||
isDm = timelineRoomInfo.isDm,
|
||||
onPredecessorRoomClick = { roomId ->
|
||||
eventSink(TimelineEvents.NavigateToRoom(roomId))
|
||||
eventSink(TimelineEvents.NavigateToPredecessorOrSuccessorRoom(roomId))
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ class FakeMessagesNavigator(
|
|||
private val onReportContentClickLambda: (eventId: EventId, senderId: UserId) -> Unit = { _, _ -> lambdaError() },
|
||||
private val onEditPollClickLambda: (eventId: EventId) -> Unit = { _ -> lambdaError() },
|
||||
private val onPreviewAttachmentLambda: (attachments: ImmutableList<Attachment>) -> Unit = { _ -> lambdaError() },
|
||||
private val onNavigateToRoomLambda: (roomId: RoomId) -> Unit = { _ -> lambdaError() }
|
||||
private val onNavigateToRoomLambda: (roomId: RoomId, serverNames: List<String>) -> Unit = { _, _ -> lambdaError() }
|
||||
) : MessagesNavigator {
|
||||
override fun onShowEventDebugInfoClick(eventId: EventId?, debugInfo: TimelineItemDebugInfo) {
|
||||
onShowEventDebugInfoClickLambda(eventId, debugInfo)
|
||||
|
|
@ -43,7 +43,7 @@ class FakeMessagesNavigator(
|
|||
onPreviewAttachmentLambda(attachments)
|
||||
}
|
||||
|
||||
override fun onNavigateToRoom(roomId: RoomId) {
|
||||
onNavigateToRoomLambda(roomId)
|
||||
override fun onNavigateToRoom(roomId: RoomId, serverNames: List<String>) {
|
||||
onNavigateToRoomLambda(roomId, serverNames)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -579,7 +579,7 @@ class MessagesViewTest {
|
|||
val text = rule.activity.getString(R.string.screen_room_timeline_tombstoned_room_action)
|
||||
// The bottomsheet subcompose seems to make the node to appear twice
|
||||
rule.onAllNodesWithText(text).onFirst().performClick()
|
||||
eventsRecorder.assertSingle(TimelineEvents.NavigateToRoom(successorRoomId))
|
||||
eventsRecorder.assertSingle(TimelineEvents.NavigateToPredecessorOrSuccessorRoom(successorRoomId))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -753,18 +753,19 @@ class TimelinePresenterTest {
|
|||
canUserSendMessageResult = { _, _ -> Result.success(true) },
|
||||
),
|
||||
)
|
||||
val onNavigateToRoomLambda = lambdaRecorder<RoomId, Unit> {}
|
||||
val onNavigateToRoomLambda = lambdaRecorder<RoomId, List<String>, Unit> { _, _ -> }
|
||||
val navigator = FakeMessagesNavigator(
|
||||
onNavigateToRoomLambda = onNavigateToRoomLambda
|
||||
)
|
||||
val presenter = createTimelinePresenter(room = room, messagesNavigator = navigator)
|
||||
presenter.test {
|
||||
val initialState = awaitFirstItem()
|
||||
initialState.eventSink(TimelineEvents.NavigateToRoom(A_ROOM_ID))
|
||||
initialState.eventSink(TimelineEvents.NavigateToPredecessorOrSuccessorRoom(A_ROOM_ID))
|
||||
assert(onNavigateToRoomLambda)
|
||||
.isCalledOnce()
|
||||
.with(
|
||||
value(A_ROOM_ID)
|
||||
value(A_ROOM_ID),
|
||||
value(emptyList<String>())
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ interface RoomDetailsEntryPoint : FeatureEntryPoint {
|
|||
|
||||
interface Callback : Plugin {
|
||||
fun onOpenGlobalNotificationSettings()
|
||||
fun onOpenRoom(roomId: RoomId)
|
||||
fun onOpenRoom(roomId: RoomId, serverNames: List<String>)
|
||||
fun onPermalinkClick(data: PermalinkData, pushToBackstack: Boolean)
|
||||
fun onForwardedToSingleRoom(roomId: RoomId)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -270,7 +270,7 @@ class RoomDetailsFlowNode @AssistedInject constructor(
|
|||
}
|
||||
|
||||
override fun onStartDM(roomId: RoomId) {
|
||||
plugins<RoomDetailsEntryPoint.Callback>().forEach { it.onOpenRoom(roomId) }
|
||||
plugins<RoomDetailsEntryPoint.Callback>().forEach { it.onOpenRoom(roomId, emptyList()) }
|
||||
}
|
||||
|
||||
override fun onStartCall(dmRoomId: RoomId) {
|
||||
|
|
|
|||
|
|
@ -29,4 +29,7 @@ value class UserId(val value: String) : Serializable {
|
|||
get() = value
|
||||
.removePrefix("@")
|
||||
.substringBefore(":")
|
||||
|
||||
val domainName: String?
|
||||
get() = value.substringAfter(":").takeIf { it.isNotEmpty() }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -283,7 +283,7 @@ class RustMatrixClient(
|
|||
}
|
||||
|
||||
override suspend fun getJoinedRoom(roomId: RoomId): JoinedRoom? = withContext(sessionDispatcher) {
|
||||
(roomFactory.getJoinedRoomOrPreview(roomId) as? GetRoomResult.Joined)?.joinedRoom
|
||||
(roomFactory.getJoinedRoomOrPreview(roomId, emptyList()) as? GetRoomResult.Joined)?.joinedRoom
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -481,7 +481,7 @@ class RustMatrixClient(
|
|||
is RoomIdOrAlias.Alias -> {
|
||||
val roomId = innerClient.resolveRoomAlias(roomIdOrAlias.roomAlias.value)?.roomId?.let { RoomId(it) }
|
||||
|
||||
var room = (roomId?.let { roomFactory.getJoinedRoomOrPreview(it) } as? GetRoomResult.NotJoined)?.notJoinedRoom
|
||||
var room = (roomId?.let { roomFactory.getJoinedRoomOrPreview(it, serverNames) } as? GetRoomResult.NotJoined)?.notJoinedRoom
|
||||
if (room == null) {
|
||||
val preview = innerClient.getRoomPreviewFromRoomAlias(roomIdOrAlias.roomAlias.value)
|
||||
room = NotJoinedRustRoom(sessionId, null, RoomPreviewInfoMapper.map(preview.info()))
|
||||
|
|
@ -489,7 +489,7 @@ class RustMatrixClient(
|
|||
room
|
||||
}
|
||||
is RoomIdOrAlias.Id -> {
|
||||
var room = (roomFactory.getJoinedRoomOrPreview(roomIdOrAlias.roomId) as? GetRoomResult.NotJoined)?.notJoinedRoom
|
||||
var room = (roomFactory.getJoinedRoomOrPreview(roomIdOrAlias.roomId, serverNames) as? GetRoomResult.NotJoined)?.notJoinedRoom
|
||||
|
||||
if (room == null) {
|
||||
val preview = innerClient.getRoomPreviewFromRoomId(roomIdOrAlias.roomId.value, serverNames)
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ class RustRoomFactory(
|
|||
)
|
||||
}
|
||||
|
||||
suspend fun getJoinedRoomOrPreview(roomId: RoomId): GetRoomResult? = withContext(dispatcher) {
|
||||
suspend fun getJoinedRoomOrPreview(roomId: RoomId, serverNames: List<String>): GetRoomResult? = withContext(dispatcher) {
|
||||
mutex.withLock {
|
||||
if (isDestroyed.get()) {
|
||||
Timber.d("Room factory is destroyed, returning null for $roomId")
|
||||
|
|
@ -132,7 +132,7 @@ class RustRoomFactory(
|
|||
)
|
||||
} else {
|
||||
val preview = try {
|
||||
sdkRoom.previewRoom(via = emptyList())
|
||||
sdkRoom.previewRoom(via = serverNames)
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Failed to get room preview for $roomId")
|
||||
return@withContext null
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue