mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-02 12:07:28 +00:00
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:
committed by
GitHub
parent
786c53ac06
commit
bea2903093
+36
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+8
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
+8
@@ -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,
|
||||
)
|
||||
|
||||
|
||||
+4
@@ -258,6 +258,10 @@ class RoomDetailsFlowNode(
|
||||
override fun navigateToInviteMembers() {
|
||||
backstack.push(NavTarget.InviteMembers)
|
||||
}
|
||||
|
||||
override fun navigateToAvatarPreview(username: String, avatarUrl: String) {
|
||||
overlay.show(NavTarget.AvatarPreview(username, avatarUrl))
|
||||
}
|
||||
}
|
||||
createNode<RoomMemberListNode>(buildContext, listOf(roomMemberListCallback))
|
||||
}
|
||||
|
||||
+7
@@ -27,6 +27,7 @@ import io.element.android.libraries.architecture.appyx.launchMolecule
|
||||
import io.element.android.libraries.architecture.callback
|
||||
import io.element.android.libraries.di.RoomScope
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.ui.model.getBestName
|
||||
import io.element.android.services.analytics.api.AnalyticsService
|
||||
|
||||
@ContributesNode(RoomScope::class)
|
||||
@@ -41,6 +42,7 @@ class RoomMemberListNode(
|
||||
interface Callback : Plugin {
|
||||
fun navigateToRoomMemberDetails(roomMemberId: UserId)
|
||||
fun navigateToInviteMembers()
|
||||
fun navigateToAvatarPreview(username: String, avatarUrl: String)
|
||||
}
|
||||
|
||||
private val callback: Callback = callback()
|
||||
@@ -82,6 +84,11 @@ class RoomMemberListNode(
|
||||
else -> state.moderationState.eventSink(RoomMemberModerationEvents.ProcessAction(action, target))
|
||||
}
|
||||
},
|
||||
onAvatarClick = { user ->
|
||||
user.avatarUrl?.let { url ->
|
||||
callback.navigateToAvatarPreview(user.getBestName(), url)
|
||||
}
|
||||
},
|
||||
modifier = Modifier,
|
||||
)
|
||||
}
|
||||
|
||||
+1
@@ -17,6 +17,7 @@ interface RoomMemberModerationRenderer {
|
||||
fun Render(
|
||||
state: RoomMemberModerationState,
|
||||
onSelectAction: (ModerationAction, MatrixUser) -> Unit,
|
||||
onAvatarClick: ((MatrixUser) -> Unit)?,
|
||||
modifier: Modifier,
|
||||
)
|
||||
}
|
||||
|
||||
+2
-1
@@ -25,10 +25,11 @@ class DefaultRoomMemberModerationRenderer : RoomMemberModerationRenderer {
|
||||
override fun Render(
|
||||
state: RoomMemberModerationState,
|
||||
onSelectAction: (ModerationAction, MatrixUser) -> Unit,
|
||||
onAvatarClick: ((MatrixUser) -> Unit)?,
|
||||
modifier: Modifier
|
||||
) {
|
||||
if (state is InternalRoomMemberModerationState) {
|
||||
RoomMemberModerationView(state, onSelectAction, modifier)
|
||||
RoomMemberModerationView(modifier = modifier, state = state, onSelectAction = onSelectAction, onAvatarClick = onAvatarClick)
|
||||
} else {
|
||||
SideEffect {
|
||||
Timber.d("RoomMemberModerationRenderer: Render called with unsupported state: $state")
|
||||
|
||||
+17
-10
@@ -12,6 +12,7 @@ import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
@@ -64,6 +65,7 @@ import timber.log.Timber
|
||||
fun RoomMemberModerationView(
|
||||
state: InternalRoomMemberModerationState,
|
||||
onSelectAction: (ModerationAction, MatrixUser) -> Unit,
|
||||
onAvatarClick: ((MatrixUser) -> Unit)?,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Box(modifier = modifier) {
|
||||
@@ -73,6 +75,7 @@ fun RoomMemberModerationView(
|
||||
user = selectedUser,
|
||||
actions = state.actions,
|
||||
onSelectAction = onSelectAction,
|
||||
onAvatarClick = onAvatarClick,
|
||||
onDismiss = { state.eventSink(InternalRoomMemberModerationEvents.Reset) },
|
||||
)
|
||||
}
|
||||
@@ -214,6 +217,7 @@ private fun RoomMemberActionsBottomSheet(
|
||||
user: MatrixUser,
|
||||
actions: ImmutableList<ModerationActionState>,
|
||||
onSelectAction: (ModerationAction, MatrixUser) -> Unit,
|
||||
onAvatarClick: ((MatrixUser) -> Unit)? = null,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
@@ -240,10 +244,11 @@ private fun RoomMemberActionsBottomSheet(
|
||||
modifier = Modifier
|
||||
.padding(bottom = 24.dp)
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.clickable {
|
||||
.clickable(enabled = user.avatarUrl != null && onAvatarClick != null) {
|
||||
coroutineScope.launch {
|
||||
onSelectAction(ModerationAction.DisplayProfile, user)
|
||||
bottomSheetState.hide()
|
||||
onAvatarClick?.invoke(user)
|
||||
onDismiss()
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -282,8 +287,8 @@ private fun RoomMemberActionsBottomSheet(
|
||||
leadingContent = ListItemContent.Icon(IconSource.Vector(CompoundIcons.UserProfile())),
|
||||
onClick = {
|
||||
coroutineScope.launch {
|
||||
onSelectAction(action, user)
|
||||
bottomSheetState.hide()
|
||||
onSelectAction(action, user)
|
||||
}
|
||||
},
|
||||
enabled = actionState.isEnabled
|
||||
@@ -340,16 +345,18 @@ private fun RoomMemberActionsBottomSheet(
|
||||
@PreviewsDayNight
|
||||
@Composable
|
||||
internal fun RoomMemberModerationViewPreview(@PreviewParameter(InternalRoomMemberModerationStateProvider::class) state: InternalRoomMemberModerationState) {
|
||||
val isDoingAction = listOf(state.kickUserAsyncAction, state.banUserAsyncAction, state.unbanUserAsyncAction).any { it is AsyncAction.Loading }
|
||||
val modifier = if (isDoingAction) {
|
||||
Modifier.fillMaxWidth().heightIn(min = 64.dp)
|
||||
} else {
|
||||
Modifier.fillMaxSize()
|
||||
}
|
||||
ElementPreview {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = 64.dp)
|
||||
) {
|
||||
Box(modifier) {
|
||||
RoomMemberModerationView(
|
||||
state = state,
|
||||
onSelectAction = { _, _ ->
|
||||
},
|
||||
onSelectAction = { _, _ -> },
|
||||
onAvatarClick = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user