Click on avatar in moderation bottom sheet opens avatar preview (#6991)

* Make the avatar in the room member moderation bottom sheet open the avatar in the media viewer (#6962)

* Make the avatar in the room member moderation bottom sheet open the avatar in the media viewer.

* Fix issue with avatar overlay not dismissing the bottom sheet: the bottom sheet would eat all the touch events until the first click, which would somehow cancel this behaviour

* Fix lint issues

* Update screenshots

---------

Co-authored-by: bxdxnn <267911624+bxdxnn@users.noreply.github.com>
Co-authored-by: ElementBot <android@element.io>
This commit is contained in:
Jorge Martin Espinosa
2026-06-08 16:32:13 +02:00
committed by GitHub
parent 786c53ac06
commit bea2903093
22 changed files with 111 additions and 39 deletions
@@ -190,6 +190,9 @@ class MessagesFlowNode(
@Parcelize
data object ThreadsList : NavTarget
@Parcelize
data class AvatarPreview(val name: String, val avatarUrl: String) : NavTarget
}
private val callback: MessagesEntryPoint.Callback = callback()
@@ -327,6 +330,10 @@ class MessagesFlowNode(
override fun navigateToDeveloperSettings() {
callback.navigateToDeveloperSettings()
}
override fun navigateToAvatarPreview(username: String, avatarUrl: String) {
overlay.show(NavTarget.AvatarPreview(username, avatarUrl))
}
}
val inputs = MessagesNode.Inputs(focusedEventId = navTarget.focusedEventId)
createNode<MessagesNode>(buildContext, listOf(callback, inputs))
@@ -562,6 +569,10 @@ class MessagesFlowNode(
override fun navigateToDeveloperSettings() {
callback.navigateToDeveloperSettings()
}
override fun navigateToAvatarPreview(username: String, avatarUrl: String) {
overlay.show(NavTarget.AvatarPreview(username, avatarUrl))
}
}
createNode<ThreadedMessagesNode>(buildContext, listOf(inputs, callback))
}
@@ -573,6 +584,31 @@ class MessagesFlowNode(
}
createNode<ThreadsListNode>(buildContext, listOf(callback))
}
is NavTarget.AvatarPreview -> {
val callback = object : MediaViewerEntryPoint.Callback {
override fun onDone() {
overlay.hide()
}
override fun viewInTimeline(eventId: EventId) {
// Cannot happen
}
override fun forwardEvent(eventId: EventId, fromPinnedEvents: Boolean) {
// Cannot happen
}
}
val params = mediaViewerEntryPoint.createParamsForAvatar(
filename = navTarget.name,
avatarUrl = navTarget.avatarUrl,
)
mediaViewerEntryPoint.createNode(
parentNode = this,
buildContext = buildContext,
params = params,
callback = callback,
)
}
}
}
@@ -66,6 +66,7 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.room.alias.matches
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
import io.element.android.libraries.matrix.ui.model.getBestName
import io.element.android.libraries.mediaplayer.api.MediaPlayer
import io.element.android.libraries.ui.strings.CommonStrings
import io.element.android.libraries.ui.utils.a11y.hasExternalKeyboard
@@ -136,6 +137,8 @@ class MessagesNode(
fun navigateToDeveloperSettings()
fun navigateToThreadsList()
fun navigateToAvatarPreview(username: String, avatarUrl: String)
}
override fun onBuilt() {
@@ -319,6 +322,11 @@ class MessagesNode(
else -> state.roomMemberModerationState.eventSink(RoomMemberModerationEvents.ProcessAction(action, target))
}
},
onAvatarClick = { user ->
user.avatarUrl?.let { url ->
callback.navigateToAvatarPreview(user.getBestName(), url)
}
},
modifier = Modifier,
)
@@ -67,6 +67,7 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.room.alias.matches
import io.element.android.libraries.matrix.api.timeline.Timeline
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
import io.element.android.libraries.matrix.ui.model.getBestName
import io.element.android.libraries.ui.utils.a11y.hasExternalKeyboard
import io.element.android.libraries.ui.utils.a11y.isTalkbackActive
import io.element.android.services.analytics.api.AnalyticsService
@@ -138,6 +139,8 @@ class ThreadedMessagesNode(
fun navigateToRoomCall(roomId: RoomId, isAudioCall: Boolean)
fun navigateToThread(threadRootId: ThreadId, focusedEventId: EventId?)
fun navigateToDeveloperSettings()
fun navigateToAvatarPreview(username: String, avatarUrl: String)
}
override fun onBuilt() {
@@ -315,6 +318,11 @@ class ThreadedMessagesNode(
else -> state.roomMemberModerationState.eventSink(RoomMemberModerationEvents.ProcessAction(action, target))
}
},
onAvatarClick = { user ->
user.avatarUrl?.let { url ->
callback.navigateToAvatarPreview(user.getBestName(), url)
}
},
modifier = Modifier,
)