Skip to content

Commit 4539474

Browse files
authored
Merge pull request #4779 from element-hq/feature/fga/user_moderation_bottomsheet
Change : RoomMember moderation
2 parents 3ddea79 + 24c29c5 commit 4539474

File tree

144 files changed

+1944
-1671
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

144 files changed

+1944
-1671
lines changed

features/messages/impl/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ dependencies {
6767
implementation(libs.telephoto.zoomableimage)
6868
implementation(libs.matrix.emojibase.bindings)
6969
implementation(projects.features.knockrequests.api)
70+
implementation(projects.features.roommembermoderation.api)
7071

7172
testImplementation(libs.test.junit)
7273
testImplementation(libs.coroutines.test)

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesEvents.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,13 @@ package io.element.android.features.messages.impl
1010
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
1111
import io.element.android.features.messages.impl.timeline.model.TimelineItem
1212
import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId
13+
import io.element.android.libraries.matrix.api.user.MatrixUser
1314

1415
sealed interface MessagesEvents {
1516
data class HandleAction(val action: TimelineItemAction, val event: TimelineItem.Event) : MessagesEvents
1617
data class ToggleReaction(val emoji: String, val eventOrTransactionId: EventOrTransactionId) : MessagesEvents
1718
data class InviteDialogDismissed(val action: InviteDialogAction) : MessagesEvents
19+
data class OnUserClicked(val user: MatrixUser) : MessagesEvents
1820
data object Dismiss : MessagesEvents
1921
}
2022

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesNode.kt

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ import io.element.android.features.messages.impl.timeline.TimelinePresenter
3939
import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories
4040
import io.element.android.features.messages.impl.timeline.di.TimelineItemPresenterFactories
4141
import io.element.android.features.messages.impl.timeline.model.TimelineItem
42+
import io.element.android.features.roommembermoderation.api.ModerationAction
43+
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
44+
import io.element.android.features.roommembermoderation.api.RoomMemberModerationRenderer
4245
import io.element.android.libraries.androidutils.browser.openUrlInChromeCustomTab
4346
import io.element.android.libraries.androidutils.system.openUrlInExternalApp
4447
import io.element.android.libraries.androidutils.system.toast
@@ -76,7 +79,8 @@ class MessagesNode @AssistedInject constructor(
7679
private val timelineItemPresenterFactories: TimelineItemPresenterFactories,
7780
private val mediaPlayer: MediaPlayer,
7881
private val permalinkParser: PermalinkParser,
79-
private val knockRequestsBannerRenderer: KnockRequestsBannerRenderer
82+
private val knockRequestsBannerRenderer: KnockRequestsBannerRenderer,
83+
private val roomMemberModerationRenderer: RoomMemberModerationRenderer,
8084
) : Node(buildContext, plugins = plugins), MessagesNavigator {
8185
private val presenter = presenterFactory.create(
8286
navigator = this,
@@ -257,6 +261,16 @@ class MessagesNode @AssistedInject constructor(
257261
},
258262
modifier = modifier,
259263
)
264+
roomMemberModerationRenderer.Render(
265+
state = state.roomMemberModerationState,
266+
onSelectAction = { action, target ->
267+
when (action) {
268+
is ModerationAction.DisplayProfile -> onUserDataClick(target.userId)
269+
else -> state.roomMemberModerationState.eventSink(RoomMemberModerationEvents.ProcessAction(action, target))
270+
}
271+
},
272+
modifier = Modifier,
273+
)
260274

261275
var focusedEventId by rememberSaveable {
262276
mutableStateOf(inputs.focusedEventId)

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
5050
import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState
5151
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState
5252
import io.element.android.features.roomcall.api.RoomCallState
53+
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
54+
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
5355
import io.element.android.libraries.androidutils.clipboard.ClipboardHelper
5456
import io.element.android.libraries.architecture.AsyncData
5557
import io.element.android.libraries.architecture.Presenter
@@ -103,6 +105,7 @@ class MessagesPresenter @AssistedInject constructor(
103105
private val readReceiptBottomSheetPresenter: Presenter<ReadReceiptBottomSheetState>,
104106
private val pinnedMessagesBannerPresenter: Presenter<PinnedMessagesBannerState>,
105107
private val roomCallStatePresenter: Presenter<RoomCallState>,
108+
private val roomMemberModerationPresenter: Presenter<RoomMemberModerationState>,
106109
private val syncService: SyncService,
107110
private val snackbarDispatcher: SnackbarDispatcher,
108111
private val dispatchers: CoroutineDispatchers,
@@ -143,7 +146,7 @@ class MessagesPresenter @AssistedInject constructor(
143146
val readReceiptBottomSheetState = readReceiptBottomSheetPresenter.present()
144147
val pinnedMessagesBannerState = pinnedMessagesBannerPresenter.present()
145148
val roomCallState = roomCallStatePresenter.present()
146-
149+
val roomMemberModerationState = roomMemberModerationPresenter.present()
147150
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
148151

149152
val userEventPermissions by userEventPermissions(syncUpdateFlow.value)
@@ -233,6 +236,9 @@ class MessagesPresenter @AssistedInject constructor(
233236
}
234237
}
235238
is MessagesEvents.Dismiss -> actionListState.eventSink(ActionListEvents.Clear)
239+
is MessagesEvents.OnUserClicked -> {
240+
roomMemberModerationState.eventSink(RoomMemberModerationEvents.ShowActionsForUser(event.user))
241+
}
236242
}
237243
}
238244

@@ -262,6 +268,7 @@ class MessagesPresenter @AssistedInject constructor(
262268
roomCallState = roomCallState,
263269
pinnedMessagesBannerState = pinnedMessagesBannerState,
264270
dmUserVerificationState = dmUserVerificationState,
271+
roomMemberModerationState = roomMemberModerationState,
265272
eventSink = { handleEvents(it) }
266273
)
267274
}

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import io.element.android.features.messages.impl.timeline.components.receipt.bot
2020
import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState
2121
import io.element.android.features.messages.impl.voicemessages.composer.VoiceMessageComposerState
2222
import io.element.android.features.roomcall.api.RoomCallState
23+
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
2324
import io.element.android.libraries.architecture.AsyncData
2425
import io.element.android.libraries.designsystem.components.avatar.AvatarData
2526
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarMessage
@@ -54,5 +55,6 @@ data class MessagesState(
5455
val appName: String,
5556
val pinnedMessagesBannerState: PinnedMessagesBannerState,
5657
val dmUserVerificationState: IdentityState?,
58+
val roomMemberModerationState: RoomMemberModerationState,
5759
val eventSink: (MessagesEvents) -> Unit
5860
)

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ import io.element.android.features.messages.impl.voicemessages.composer.aVoiceMe
3737
import io.element.android.features.roomcall.api.RoomCallState
3838
import io.element.android.features.roomcall.api.aStandByCallState
3939
import io.element.android.features.roomcall.api.anOngoingCallState
40+
import io.element.android.features.roommembermoderation.api.RoomMemberModerationEvents
41+
import io.element.android.features.roommembermoderation.api.RoomMemberModerationState
4042
import io.element.android.libraries.architecture.AsyncData
4143
import io.element.android.libraries.designsystem.components.avatar.AvatarData
4244
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
@@ -116,6 +118,7 @@ fun aMessagesState(
116118
roomCallState: RoomCallState = aStandByCallState(),
117119
pinnedMessagesBannerState: PinnedMessagesBannerState = aLoadedPinnedMessagesBannerState(),
118120
dmUserVerificationState: IdentityState? = null,
121+
roomMemberModerationState: RoomMemberModerationState = aRoomMemberModerationState(),
119122
eventSink: (MessagesEvents) -> Unit = {},
120123
) = MessagesState(
121124
roomId = RoomId("!id:domain"),
@@ -143,9 +146,19 @@ fun aMessagesState(
143146
appName = "Element",
144147
pinnedMessagesBannerState = pinnedMessagesBannerState,
145148
dmUserVerificationState = dmUserVerificationState,
149+
roomMemberModerationState = roomMemberModerationState,
146150
eventSink = eventSink,
147151
)
148152

153+
fun aRoomMemberModerationState(
154+
canKick: Boolean = false,
155+
canBan: Boolean = false,
156+
) = object : RoomMemberModerationState {
157+
override val canKick: Boolean = canKick
158+
override val canBan: Boolean = canBan
159+
override val eventSink: (RoomMemberModerationEvents) -> Unit = {}
160+
}
161+
149162
fun aUserEventPermissions(
150163
canRedactOwn: Boolean = false,
151164
canRedactOther: Boolean = false,

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ import io.element.android.libraries.designsystem.utils.snackbar.rememberSnackbar
103103
import io.element.android.libraries.matrix.api.core.EventId
104104
import io.element.android.libraries.matrix.api.core.UserId
105105
import io.element.android.libraries.matrix.api.encryption.identity.IdentityState
106+
import io.element.android.libraries.matrix.api.user.MatrixUser
106107
import io.element.android.libraries.textcomposer.model.TextEditorState
107108
import io.element.android.libraries.ui.strings.CommonStrings
108109
import io.element.android.wysiwyg.link.Link
@@ -208,7 +209,11 @@ fun MessagesView(
208209
.consumeWindowInsets(padding),
209210
onContentClick = ::onContentClick,
210211
onMessageLongClick = ::onMessageLongClick,
211-
onUserDataClick = { hidingKeyboard { onUserDataClick(it) } },
212+
onUserDataClick = {
213+
hidingKeyboard {
214+
state.eventSink(MessagesEvents.OnUserClicked(it))
215+
}
216+
},
212217
onLinkClick = { link, customTab ->
213218
if (customTab) {
214219
onLinkClick(link.url, true)
@@ -293,7 +298,7 @@ private fun ReinviteDialog(state: MessagesState) {
293298
private fun MessagesViewContent(
294299
state: MessagesState,
295300
onContentClick: (TimelineItem.Event) -> Unit,
296-
onUserDataClick: (UserId) -> Unit,
301+
onUserDataClick: (MatrixUser) -> Unit,
297302
onLinkClick: (Link, Boolean) -> Unit,
298303
onReactionClick: (key: String, TimelineItem.Event) -> Unit,
299304
onReactionLongClick: (key: String, TimelineItem.Event) -> Unit,

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListNode.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import io.element.android.libraries.matrix.api.core.UserId
3333
import io.element.android.libraries.matrix.api.permalink.PermalinkData
3434
import io.element.android.libraries.matrix.api.permalink.PermalinkParser
3535
import io.element.android.libraries.matrix.api.timeline.item.TimelineItemDebugInfo
36+
import io.element.android.libraries.matrix.api.user.MatrixUser
3637
import io.element.android.libraries.ui.strings.CommonStrings
3738

3839
@ContributesNode(RoomScope::class)
@@ -63,8 +64,8 @@ class PinnedMessagesListNode @AssistedInject constructor(
6364
return callbacks.forEach { it.onEventClick(event) }
6465
}
6566

66-
private fun onUserDataClick(userId: UserId) {
67-
callbacks.forEach { it.onUserDataClick(userId) }
67+
private fun onUserDataClick(user: MatrixUser) {
68+
callbacks.forEach { it.onUserDataClick(user.userId) }
6869
}
6970

7071
private fun onLinkClick(context: Context, url: String) {

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/pinned/list/PinnedMessagesListView.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import io.element.android.libraries.designsystem.theme.components.CircularProgre
4848
import io.element.android.libraries.designsystem.theme.components.Scaffold
4949
import io.element.android.libraries.designsystem.theme.components.Text
5050
import io.element.android.libraries.designsystem.theme.components.TopAppBar
51-
import io.element.android.libraries.matrix.api.core.UserId
51+
import io.element.android.libraries.matrix.api.user.MatrixUser
5252
import io.element.android.libraries.ui.strings.CommonStrings
5353
import io.element.android.services.analytics.compose.LocalAnalyticsService
5454
import io.element.android.services.analyticsproviders.api.trackers.captureInteraction
@@ -59,7 +59,7 @@ fun PinnedMessagesListView(
5959
state: PinnedMessagesListState,
6060
onBackClick: () -> Unit,
6161
onEventClick: (event: TimelineItem.Event) -> Unit,
62-
onUserDataClick: (UserId) -> Unit,
62+
onUserDataClick: (MatrixUser) -> Unit,
6363
onLinkClick: (Link) -> Unit,
6464
onLinkLongClick: (Link) -> Unit,
6565
modifier: Modifier = Modifier,
@@ -115,7 +115,7 @@ private fun PinnedMessagesListTopBar(
115115
private fun PinnedMessagesListContent(
116116
state: PinnedMessagesListState,
117117
onEventClick: (event: TimelineItem.Event) -> Unit,
118-
onUserDataClick: (UserId) -> Unit,
118+
onUserDataClick: (MatrixUser) -> Unit,
119119
onLinkClick: (Link) -> Unit,
120120
onLinkLongClick: (Link) -> Unit,
121121
onErrorDismiss: () -> Unit,
@@ -171,7 +171,7 @@ private fun PinnedMessagesListEmpty(
171171
private fun PinnedMessagesListLoaded(
172172
state: PinnedMessagesListState.Filled,
173173
onEventClick: (event: TimelineItem.Event) -> Unit,
174-
onUserDataClick: (UserId) -> Unit,
174+
onUserDataClick: (MatrixUser) -> Unit,
175175
onLinkClick: (Link) -> Unit,
176176
onLinkLongClick: (Link) -> Unit,
177177
modifier: Modifier = Modifier,

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,8 @@ import io.element.android.libraries.designsystem.theme.components.FloatingAction
6969
import io.element.android.libraries.designsystem.theme.components.Icon
7070
import io.element.android.libraries.designsystem.utils.animateScrollToItemCenter
7171
import io.element.android.libraries.matrix.api.core.EventId
72-
import io.element.android.libraries.matrix.api.core.UserId
7372
import io.element.android.libraries.matrix.api.timeline.Timeline
73+
import io.element.android.libraries.matrix.api.user.MatrixUser
7474
import io.element.android.libraries.testtags.TestTags
7575
import io.element.android.libraries.testtags.testTag
7676
import io.element.android.libraries.ui.strings.CommonStrings
@@ -92,7 +92,7 @@ import kotlin.time.Duration.Companion.milliseconds
9292
fun TimelineView(
9393
state: TimelineState,
9494
timelineProtectionState: TimelineProtectionState,
95-
onUserDataClick: (UserId) -> Unit,
95+
onUserDataClick: (MatrixUser) -> Unit,
9696
onLinkClick: (Link) -> Unit,
9797
onContentClick: (TimelineItem.Event) -> Unit,
9898
onMessageLongClick: (TimelineItem.Event) -> Unit,

0 commit comments

Comments
 (0)