Remove dependencies to other presenters to RoomMembersModerationPresenter.
Move canDisplayModerationActions from presenter API to the state it emits.
This commit is contained in:
parent
7dce60c756
commit
62cbff0ffe
9 changed files with 242 additions and 256 deletions
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 New Vector Ltd.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
* Please see LICENSE in the repository root for full details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.roomdetails.impl.di
|
||||||
|
|
||||||
|
import com.squareup.anvil.annotations.ContributesTo
|
||||||
|
import dagger.Binds
|
||||||
|
import dagger.Module
|
||||||
|
import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationPresenter
|
||||||
|
import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationState
|
||||||
|
import io.element.android.libraries.architecture.Presenter
|
||||||
|
import io.element.android.libraries.di.RoomScope
|
||||||
|
|
||||||
|
@Module
|
||||||
|
@ContributesTo(RoomScope::class)
|
||||||
|
interface RoomDetailsModule {
|
||||||
|
@Binds
|
||||||
|
fun bindRoomMembersModerationPresenter(presenter: RoomMembersModerationPresenter): Presenter<RoomMembersModerationState>
|
||||||
|
}
|
||||||
|
|
@ -21,7 +21,7 @@ import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationEvents
|
import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationEvents
|
||||||
import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationPresenter
|
import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationState
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
|
|
@ -40,7 +40,7 @@ class RoomMemberListPresenter @AssistedInject constructor(
|
||||||
private val room: MatrixRoom,
|
private val room: MatrixRoom,
|
||||||
private val roomMemberListDataSource: RoomMemberListDataSource,
|
private val roomMemberListDataSource: RoomMemberListDataSource,
|
||||||
private val coroutineDispatchers: CoroutineDispatchers,
|
private val coroutineDispatchers: CoroutineDispatchers,
|
||||||
private val roomMembersModerationPresenter: RoomMembersModerationPresenter,
|
private val roomMembersModerationPresenter: Presenter<RoomMembersModerationState>,
|
||||||
@Assisted private val navigator: RoomMemberListNavigator,
|
@Assisted private val navigator: RoomMemberListNavigator,
|
||||||
) : Presenter<RoomMemberListState> {
|
) : Presenter<RoomMemberListState> {
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
|
|
@ -136,7 +136,7 @@ class RoomMemberListPresenter @AssistedInject constructor(
|
||||||
is RoomMemberListEvents.OnSearchActiveChanged -> isSearchActive = event.active
|
is RoomMemberListEvents.OnSearchActiveChanged -> isSearchActive = event.active
|
||||||
is RoomMemberListEvents.UpdateSearchQuery -> searchQuery = event.query
|
is RoomMemberListEvents.UpdateSearchQuery -> searchQuery = event.query
|
||||||
is RoomMemberListEvents.RoomMemberSelected -> coroutineScope.launch {
|
is RoomMemberListEvents.RoomMemberSelected -> coroutineScope.launch {
|
||||||
if (roomMembersModerationPresenter.canDisplayModerationActions()) {
|
if (roomModerationState.canDisplayModerationActions) {
|
||||||
roomModerationState.eventSink(RoomMembersModerationEvents.SelectRoomMember(event.roomMember))
|
roomModerationState.eventSink(RoomMembersModerationEvents.SelectRoomMember(event.roomMember))
|
||||||
} else {
|
} else {
|
||||||
navigator.openRoomMemberDetails(event.roomMember.userId)
|
navigator.openRoomMemberDetails(event.roomMember.userId)
|
||||||
|
|
|
||||||
|
|
@ -1,174 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2024 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
* Please see LICENSE in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.features.roomdetails.impl.members.moderation
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.MutableState
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.produceState
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import com.squareup.anvil.annotations.ContributesBinding
|
|
||||||
import im.vector.app.features.analytics.plan.RoomModeration
|
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
|
||||||
import io.element.android.libraries.architecture.runUpdatingState
|
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
|
||||||
import io.element.android.libraries.core.extensions.finally
|
|
||||||
import io.element.android.libraries.di.RoomScope
|
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
|
||||||
import io.element.android.libraries.matrix.api.room.isDm
|
|
||||||
import io.element.android.libraries.matrix.api.room.powerlevels.canBan
|
|
||||||
import io.element.android.libraries.matrix.api.room.powerlevels.canKick
|
|
||||||
import io.element.android.services.analytics.api.AnalyticsService
|
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.flow.drop
|
|
||||||
import kotlinx.coroutines.flow.take
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@ContributesBinding(RoomScope::class)
|
|
||||||
class DefaultRoomMembersModerationPresenter @Inject constructor(
|
|
||||||
private val room: MatrixRoom,
|
|
||||||
private val dispatchers: CoroutineDispatchers,
|
|
||||||
private val analyticsService: AnalyticsService,
|
|
||||||
) : RoomMembersModerationPresenter {
|
|
||||||
private var selectedMember by mutableStateOf<RoomMember?>(null)
|
|
||||||
|
|
||||||
private suspend fun canBan() = room.canBan().getOrDefault(false)
|
|
||||||
private suspend fun canKick() = room.canKick().getOrDefault(false)
|
|
||||||
|
|
||||||
override suspend fun canDisplayModerationActions(): Boolean {
|
|
||||||
return !room.isDm && (canBan() || canKick())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
override fun present(): RoomMembersModerationState {
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
var moderationActions by remember { mutableStateOf(persistentListOf<ModerationAction>()) }
|
|
||||||
|
|
||||||
val kickUserAsyncAction = remember { mutableStateOf(AsyncAction.Uninitialized as AsyncAction<Unit>) }
|
|
||||||
val banUserAsyncAction = remember { mutableStateOf(AsyncAction.Uninitialized as AsyncAction<Unit>) }
|
|
||||||
val unbanUserAsyncAction = remember { mutableStateOf(AsyncAction.Uninitialized as AsyncAction<Unit>) }
|
|
||||||
|
|
||||||
val canDisplayBannedUsers by produceState(initialValue = false) {
|
|
||||||
value = !room.isDm && canBan()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun handleEvent(event: RoomMembersModerationEvents) {
|
|
||||||
when (event) {
|
|
||||||
is RoomMembersModerationEvents.SelectRoomMember -> {
|
|
||||||
coroutineScope.launch {
|
|
||||||
selectedMember = event.roomMember
|
|
||||||
if (event.roomMember.membership == RoomMembershipState.BAN && canBan()) {
|
|
||||||
unbanUserAsyncAction.value = AsyncAction.Confirming
|
|
||||||
} else {
|
|
||||||
moderationActions = buildList {
|
|
||||||
add(ModerationAction.DisplayProfile(event.roomMember.userId))
|
|
||||||
val currentUserMemberPowerLevel = room.userRole(room.sessionId).getOrDefault(RoomMember.Role.USER).powerLevel
|
|
||||||
if (currentUserMemberPowerLevel > event.roomMember.powerLevel) {
|
|
||||||
if (canKick()) {
|
|
||||||
add(ModerationAction.KickUser(event.roomMember.userId))
|
|
||||||
}
|
|
||||||
if (canBan()) {
|
|
||||||
add(ModerationAction.BanUser(event.roomMember.userId))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.toPersistentList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is RoomMembersModerationEvents.KickUser -> {
|
|
||||||
moderationActions = persistentListOf()
|
|
||||||
selectedMember?.let {
|
|
||||||
coroutineScope.kickUser(it.userId, kickUserAsyncAction)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is RoomMembersModerationEvents.BanUser -> {
|
|
||||||
if (banUserAsyncAction.value.isConfirming()) {
|
|
||||||
moderationActions = persistentListOf()
|
|
||||||
selectedMember?.let {
|
|
||||||
coroutineScope.banUser(it.userId, banUserAsyncAction)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
banUserAsyncAction.value = AsyncAction.Confirming
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is RoomMembersModerationEvents.UnbanUser -> {
|
|
||||||
if (unbanUserAsyncAction.value.isConfirming()) {
|
|
||||||
moderationActions = persistentListOf()
|
|
||||||
selectedMember?.let {
|
|
||||||
coroutineScope.unbanUser(it.userId, unbanUserAsyncAction)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unbanUserAsyncAction.value = AsyncAction.Confirming
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is RoomMembersModerationEvents.Reset -> {
|
|
||||||
selectedMember = null
|
|
||||||
moderationActions = persistentListOf()
|
|
||||||
kickUserAsyncAction.value = AsyncAction.Uninitialized
|
|
||||||
banUserAsyncAction.value = AsyncAction.Uninitialized
|
|
||||||
unbanUserAsyncAction.value = AsyncAction.Uninitialized
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return RoomMembersModerationState(
|
|
||||||
selectedRoomMember = selectedMember,
|
|
||||||
actions = moderationActions,
|
|
||||||
kickUserAsyncAction = kickUserAsyncAction.value,
|
|
||||||
banUserAsyncAction = banUserAsyncAction.value,
|
|
||||||
unbanUserAsyncAction = unbanUserAsyncAction.value,
|
|
||||||
canDisplayBannedUsers = canDisplayBannedUsers,
|
|
||||||
eventSink = { handleEvent(it) },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun CoroutineScope.kickUser(
|
|
||||||
userId: UserId,
|
|
||||||
kickUserAction: MutableState<AsyncAction<Unit>>,
|
|
||||||
) = runActionAndWaitForMembershipChange(kickUserAction) {
|
|
||||||
analyticsService.capture(RoomModeration(RoomModeration.Action.KickMember))
|
|
||||||
room.kickUser(userId).finally { selectedMember = null }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun CoroutineScope.banUser(
|
|
||||||
userId: UserId,
|
|
||||||
banUserAction: MutableState<AsyncAction<Unit>>,
|
|
||||||
) = runActionAndWaitForMembershipChange(banUserAction) {
|
|
||||||
analyticsService.capture(RoomModeration(RoomModeration.Action.BanMember))
|
|
||||||
room.banUser(userId).finally { selectedMember = null }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun CoroutineScope.unbanUser(
|
|
||||||
userId: UserId,
|
|
||||||
unbanUserAction: MutableState<AsyncAction<Unit>>,
|
|
||||||
) = runActionAndWaitForMembershipChange(unbanUserAction) {
|
|
||||||
analyticsService.capture(RoomModeration(RoomModeration.Action.UnbanMember))
|
|
||||||
room.unbanUser(userId).finally { selectedMember = null }
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun <T> CoroutineScope.runActionAndWaitForMembershipChange(action: MutableState<AsyncAction<T>>, block: suspend () -> Result<T>) {
|
|
||||||
launch(dispatchers.io) {
|
|
||||||
action.runUpdatingState {
|
|
||||||
val result = block()
|
|
||||||
if (result.isSuccess) {
|
|
||||||
room.membersStateFlow.drop(1).take(1)
|
|
||||||
}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -7,20 +7,180 @@
|
||||||
|
|
||||||
package io.element.android.features.roomdetails.impl.members.moderation
|
package io.element.android.features.roomdetails.impl.members.moderation
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.produceState
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import com.squareup.anvil.annotations.ContributesBinding
|
||||||
|
import im.vector.app.features.analytics.plan.RoomModeration
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.architecture.Presenter
|
import io.element.android.libraries.architecture.Presenter
|
||||||
|
import io.element.android.libraries.architecture.runUpdatingState
|
||||||
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
|
import io.element.android.libraries.core.extensions.finally
|
||||||
|
import io.element.android.libraries.di.RoomScope
|
||||||
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||||
|
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
||||||
|
import io.element.android.libraries.matrix.api.room.isDm
|
||||||
|
import io.element.android.libraries.matrix.api.room.powerlevels.canBan
|
||||||
|
import io.element.android.libraries.matrix.api.room.powerlevels.canKick
|
||||||
|
import io.element.android.services.analytics.api.AnalyticsService
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.drop
|
||||||
|
import kotlinx.coroutines.flow.take
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
interface RoomMembersModerationPresenter : Presenter<RoomMembersModerationState> {
|
class RoomMembersModerationPresenter @Inject constructor(
|
||||||
suspend fun canDisplayModerationActions(): Boolean
|
private val room: MatrixRoom,
|
||||||
|
private val dispatchers: CoroutineDispatchers,
|
||||||
|
private val analyticsService: AnalyticsService,
|
||||||
|
) : Presenter<RoomMembersModerationState> {
|
||||||
|
private var selectedMember by mutableStateOf<RoomMember?>(null)
|
||||||
|
|
||||||
fun dummyState() = RoomMembersModerationState(
|
private suspend fun canBan() = room.canBan().getOrDefault(false)
|
||||||
selectedRoomMember = null,
|
private suspend fun canKick() = room.canKick().getOrDefault(false)
|
||||||
actions = persistentListOf(),
|
|
||||||
kickUserAsyncAction = AsyncAction.Uninitialized,
|
@Composable
|
||||||
banUserAsyncAction = AsyncAction.Uninitialized,
|
override fun present(): RoomMembersModerationState {
|
||||||
unbanUserAsyncAction = AsyncAction.Uninitialized,
|
val coroutineScope = rememberCoroutineScope()
|
||||||
canDisplayBannedUsers = false,
|
var moderationActions by remember { mutableStateOf(persistentListOf<ModerationAction>()) }
|
||||||
eventSink = {}
|
|
||||||
)
|
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
|
||||||
|
val canDisplayModerationActions by produceState(
|
||||||
|
initialValue = false,
|
||||||
|
key1 = syncUpdateFlow.value
|
||||||
|
) {
|
||||||
|
value = !room.isDm && (canBan() || canKick())
|
||||||
|
}
|
||||||
|
val kickUserAsyncAction =
|
||||||
|
remember { mutableStateOf(AsyncAction.Uninitialized as AsyncAction<Unit>) }
|
||||||
|
val banUserAsyncAction =
|
||||||
|
remember { mutableStateOf(AsyncAction.Uninitialized as AsyncAction<Unit>) }
|
||||||
|
val unbanUserAsyncAction =
|
||||||
|
remember { mutableStateOf(AsyncAction.Uninitialized as AsyncAction<Unit>) }
|
||||||
|
|
||||||
|
val canDisplayBannedUsers by produceState(initialValue = false) {
|
||||||
|
value = !room.isDm && canBan()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun handleEvent(event: RoomMembersModerationEvents) {
|
||||||
|
when (event) {
|
||||||
|
is RoomMembersModerationEvents.SelectRoomMember -> {
|
||||||
|
coroutineScope.launch {
|
||||||
|
selectedMember = event.roomMember
|
||||||
|
if (event.roomMember.membership == RoomMembershipState.BAN && canBan()) {
|
||||||
|
unbanUserAsyncAction.value = AsyncAction.Confirming
|
||||||
|
} else {
|
||||||
|
moderationActions = buildList {
|
||||||
|
add(ModerationAction.DisplayProfile(event.roomMember.userId))
|
||||||
|
val currentUserMemberPowerLevel = room.userRole(room.sessionId)
|
||||||
|
.getOrDefault(RoomMember.Role.USER).powerLevel
|
||||||
|
if (currentUserMemberPowerLevel > event.roomMember.powerLevel) {
|
||||||
|
if (canKick()) {
|
||||||
|
add(ModerationAction.KickUser(event.roomMember.userId))
|
||||||
|
}
|
||||||
|
if (canBan()) {
|
||||||
|
add(ModerationAction.BanUser(event.roomMember.userId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.toPersistentList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is RoomMembersModerationEvents.KickUser -> {
|
||||||
|
moderationActions = persistentListOf()
|
||||||
|
selectedMember?.let {
|
||||||
|
coroutineScope.kickUser(it.userId, kickUserAsyncAction)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is RoomMembersModerationEvents.BanUser -> {
|
||||||
|
if (banUserAsyncAction.value.isConfirming()) {
|
||||||
|
moderationActions = persistentListOf()
|
||||||
|
selectedMember?.let {
|
||||||
|
coroutineScope.banUser(it.userId, banUserAsyncAction)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
banUserAsyncAction.value = AsyncAction.Confirming
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is RoomMembersModerationEvents.UnbanUser -> {
|
||||||
|
if (unbanUserAsyncAction.value.isConfirming()) {
|
||||||
|
moderationActions = persistentListOf()
|
||||||
|
selectedMember?.let {
|
||||||
|
coroutineScope.unbanUser(it.userId, unbanUserAsyncAction)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unbanUserAsyncAction.value = AsyncAction.Confirming
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is RoomMembersModerationEvents.Reset -> {
|
||||||
|
selectedMember = null
|
||||||
|
moderationActions = persistentListOf()
|
||||||
|
kickUserAsyncAction.value = AsyncAction.Uninitialized
|
||||||
|
banUserAsyncAction.value = AsyncAction.Uninitialized
|
||||||
|
unbanUserAsyncAction.value = AsyncAction.Uninitialized
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return RoomMembersModerationState(
|
||||||
|
canDisplayModerationActions = canDisplayModerationActions,
|
||||||
|
selectedRoomMember = selectedMember,
|
||||||
|
actions = moderationActions,
|
||||||
|
kickUserAsyncAction = kickUserAsyncAction.value,
|
||||||
|
banUserAsyncAction = banUserAsyncAction.value,
|
||||||
|
unbanUserAsyncAction = unbanUserAsyncAction.value,
|
||||||
|
canDisplayBannedUsers = canDisplayBannedUsers,
|
||||||
|
eventSink = { handleEvent(it) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CoroutineScope.kickUser(
|
||||||
|
userId: UserId,
|
||||||
|
kickUserAction: MutableState<AsyncAction<Unit>>,
|
||||||
|
) = runActionAndWaitForMembershipChange(kickUserAction) {
|
||||||
|
analyticsService.capture(RoomModeration(RoomModeration.Action.KickMember))
|
||||||
|
room.kickUser(userId).finally { selectedMember = null }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CoroutineScope.banUser(
|
||||||
|
userId: UserId,
|
||||||
|
banUserAction: MutableState<AsyncAction<Unit>>,
|
||||||
|
) = runActionAndWaitForMembershipChange(banUserAction) {
|
||||||
|
analyticsService.capture(RoomModeration(RoomModeration.Action.BanMember))
|
||||||
|
room.banUser(userId).finally { selectedMember = null }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CoroutineScope.unbanUser(
|
||||||
|
userId: UserId,
|
||||||
|
unbanUserAction: MutableState<AsyncAction<Unit>>,
|
||||||
|
) = runActionAndWaitForMembershipChange(unbanUserAction) {
|
||||||
|
analyticsService.capture(RoomModeration(RoomModeration.Action.UnbanMember))
|
||||||
|
room.unbanUser(userId).finally { selectedMember = null }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> CoroutineScope.runActionAndWaitForMembershipChange(
|
||||||
|
action: MutableState<AsyncAction<T>>,
|
||||||
|
block: suspend () -> Result<T>
|
||||||
|
) {
|
||||||
|
launch(dispatchers.io) {
|
||||||
|
action.runUpdatingState {
|
||||||
|
val result = block()
|
||||||
|
if (result.isSuccess) {
|
||||||
|
room.membersStateFlow.drop(1).take(1)
|
||||||
|
}
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import io.element.android.libraries.matrix.api.room.RoomMember
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
data class RoomMembersModerationState(
|
data class RoomMembersModerationState(
|
||||||
|
val canDisplayModerationActions: Boolean,
|
||||||
val selectedRoomMember: RoomMember?,
|
val selectedRoomMember: RoomMember?,
|
||||||
val actions: ImmutableList<ModerationAction>,
|
val actions: ImmutableList<ModerationAction>,
|
||||||
val kickUserAsyncAction: AsyncAction<Unit>,
|
val kickUserAsyncAction: AsyncAction<Unit>,
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,7 @@ class RoomMembersModerationStatePreviewProvider : PreviewParameterProvider<RoomM
|
||||||
}
|
}
|
||||||
|
|
||||||
fun aRoomMembersModerationState(
|
fun aRoomMembersModerationState(
|
||||||
|
canDisplayModerationActions: Boolean = false,
|
||||||
selectedRoomMember: RoomMember? = null,
|
selectedRoomMember: RoomMember? = null,
|
||||||
actions: List<ModerationAction> = emptyList(),
|
actions: List<ModerationAction> = emptyList(),
|
||||||
kickUserAsyncAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
kickUserAsyncAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||||
|
|
@ -79,6 +80,7 @@ fun aRoomMembersModerationState(
|
||||||
canDisplayBannedUsers: Boolean = false,
|
canDisplayBannedUsers: Boolean = false,
|
||||||
eventSink: (RoomMembersModerationEvents) -> Unit = {},
|
eventSink: (RoomMembersModerationEvents) -> Unit = {},
|
||||||
) = RoomMembersModerationState(
|
) = RoomMembersModerationState(
|
||||||
|
canDisplayModerationActions = canDisplayModerationActions,
|
||||||
selectedRoomMember = selectedRoomMember,
|
selectedRoomMember = selectedRoomMember,
|
||||||
actions = actions.toPersistentList(),
|
actions = actions.toPersistentList(),
|
||||||
kickUserAsyncAction = kickUserAsyncAction,
|
kickUserAsyncAction = kickUserAsyncAction,
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ import io.element.android.features.roomdetails.impl.members.aRoomMemberList
|
||||||
import io.element.android.features.roomdetails.impl.members.aVictor
|
import io.element.android.features.roomdetails.impl.members.aVictor
|
||||||
import io.element.android.features.roomdetails.impl.members.aWalter
|
import io.element.android.features.roomdetails.impl.members.aWalter
|
||||||
import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationEvents
|
import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationEvents
|
||||||
|
import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationState
|
||||||
import io.element.android.features.roomdetails.impl.members.moderation.aRoomMembersModerationState
|
import io.element.android.features.roomdetails.impl.members.moderation.aRoomMembersModerationState
|
||||||
import io.element.android.features.roomdetails.members.moderation.FakeRoomMembersModerationPresenter
|
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
import io.element.android.libraries.matrix.api.core.UserId
|
||||||
|
|
@ -28,6 +28,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||||
|
import io.element.android.tests.testutils.EventsRecorder
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
|
@ -194,9 +195,9 @@ class RoomMemberListPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - RoomMemberSelected by default opens the room member details through the navigator`() = runTest {
|
fun `present - RoomMemberSelected by default opens the room member details through the navigator`() = runTest {
|
||||||
val navigator = FakeRoomMemberListNavigator()
|
val navigator = FakeRoomMemberListNavigator()
|
||||||
val moderationPresenter = FakeRoomMembersModerationPresenter(canDisplayModerationActions = false)
|
val roomMembersModerationStateLambda = { aRoomMembersModerationState(canDisplayModerationActions = false) }
|
||||||
val presenter = createPresenter(
|
val presenter = createPresenter(
|
||||||
moderationPresenter = moderationPresenter,
|
roomMembersModerationStateLambda = roomMembersModerationStateLambda,
|
||||||
navigator = navigator,
|
navigator = navigator,
|
||||||
matrixRoom = FakeMatrixRoom(
|
matrixRoom = FakeMatrixRoom(
|
||||||
updateMembersResult = { Result.success(Unit) },
|
updateMembersResult = { Result.success(Unit) },
|
||||||
|
|
@ -215,17 +216,15 @@ class RoomMemberListPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `present - RoomMemberSelected will open the moderation options if the current user can use them`() = runTest {
|
fun `present - RoomMemberSelected will open the moderation options if the current user can use them`() = runTest {
|
||||||
val navigator = FakeRoomMemberListNavigator()
|
val navigator = FakeRoomMemberListNavigator()
|
||||||
var selectRoomMemberCallCounts = 0
|
val eventsRecorder = EventsRecorder<RoomMembersModerationEvents>()
|
||||||
val capturingState = aRoomMembersModerationState(eventSink = { event ->
|
val roomMembersModerationStateLambda = {
|
||||||
if (event is RoomMembersModerationEvents.SelectRoomMember) {
|
aRoomMembersModerationState(
|
||||||
selectRoomMemberCallCounts++
|
canDisplayModerationActions = true,
|
||||||
}
|
eventSink = eventsRecorder,
|
||||||
})
|
)
|
||||||
val moderationPresenter = FakeRoomMembersModerationPresenter(canDisplayModerationActions = true).apply {
|
|
||||||
givenState(capturingState)
|
|
||||||
}
|
}
|
||||||
val presenter = createPresenter(
|
val presenter = createPresenter(
|
||||||
moderationPresenter = moderationPresenter,
|
roomMembersModerationStateLambda = roomMembersModerationStateLambda,
|
||||||
navigator = navigator,
|
navigator = navigator,
|
||||||
matrixRoom = FakeMatrixRoom(
|
matrixRoom = FakeMatrixRoom(
|
||||||
updateMembersResult = { Result.success(Unit) },
|
updateMembersResult = { Result.success(Unit) },
|
||||||
|
|
@ -237,7 +236,7 @@ class RoomMemberListPresenterTest {
|
||||||
}.test {
|
}.test {
|
||||||
skipItems(1)
|
skipItems(1)
|
||||||
awaitItem().eventSink(RoomMemberListEvents.RoomMemberSelected(aVictor()))
|
awaitItem().eventSink(RoomMemberListEvents.RoomMemberSelected(aVictor()))
|
||||||
assertThat(selectRoomMemberCallCounts).isEqualTo(1)
|
eventsRecorder.assertSingle(RoomMembersModerationEvents.SelectRoomMember(aVictor()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -266,12 +265,12 @@ private fun TestScope.createPresenter(
|
||||||
updateMembersResult = { Result.success(Unit) }
|
updateMembersResult = { Result.success(Unit) }
|
||||||
),
|
),
|
||||||
roomMemberListDataSource: RoomMemberListDataSource = createDataSource(coroutineDispatchers = coroutineDispatchers),
|
roomMemberListDataSource: RoomMemberListDataSource = createDataSource(coroutineDispatchers = coroutineDispatchers),
|
||||||
moderationPresenter: FakeRoomMembersModerationPresenter = FakeRoomMembersModerationPresenter(),
|
roomMembersModerationStateLambda: () -> RoomMembersModerationState = { aRoomMembersModerationState() },
|
||||||
navigator: RoomMemberListNavigator = object : RoomMemberListNavigator {}
|
navigator: RoomMemberListNavigator = object : RoomMemberListNavigator {}
|
||||||
) = RoomMemberListPresenter(
|
) = RoomMemberListPresenter(
|
||||||
room = matrixRoom,
|
room = matrixRoom,
|
||||||
roomMemberListDataSource = roomMemberListDataSource,
|
roomMemberListDataSource = roomMemberListDataSource,
|
||||||
coroutineDispatchers = coroutineDispatchers,
|
coroutineDispatchers = coroutineDispatchers,
|
||||||
roomMembersModerationPresenter = moderationPresenter,
|
roomMembersModerationPresenter = { roomMembersModerationStateLambda() },
|
||||||
navigator = navigator
|
navigator = navigator
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2024 New Vector Ltd.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
|
||||||
* Please see LICENSE in the repository root for full details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.features.roomdetails.members.moderation
|
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationPresenter
|
|
||||||
import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationState
|
|
||||||
|
|
||||||
class FakeRoomMembersModerationPresenter(
|
|
||||||
private val canDisplayModerationActions: Boolean = true,
|
|
||||||
) : RoomMembersModerationPresenter {
|
|
||||||
private var state by mutableStateOf(dummyState())
|
|
||||||
|
|
||||||
override suspend fun canDisplayModerationActions(): Boolean {
|
|
||||||
return canDisplayModerationActions
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
override fun present(): RoomMembersModerationState {
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
|
|
||||||
fun givenState(state: RoomMembersModerationState) {
|
|
||||||
this.state = state
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -14,9 +14,9 @@ import com.google.common.truth.Truth.assertThat
|
||||||
import im.vector.app.features.analytics.plan.RoomModeration
|
import im.vector.app.features.analytics.plan.RoomModeration
|
||||||
import io.element.android.features.roomdetails.impl.members.aRoomMember
|
import io.element.android.features.roomdetails.impl.members.aRoomMember
|
||||||
import io.element.android.features.roomdetails.impl.members.aVictor
|
import io.element.android.features.roomdetails.impl.members.aVictor
|
||||||
import io.element.android.features.roomdetails.impl.members.moderation.DefaultRoomMembersModerationPresenter
|
|
||||||
import io.element.android.features.roomdetails.impl.members.moderation.ModerationAction
|
import io.element.android.features.roomdetails.impl.members.moderation.ModerationAction
|
||||||
import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationEvents
|
import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationEvents
|
||||||
|
import io.element.android.features.roomdetails.impl.members.moderation.RoomMembersModerationPresenter
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||||
|
|
@ -27,20 +27,23 @@ import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||||
import io.element.android.services.analytics.test.FakeAnalyticsService
|
import io.element.android.services.analytics.test.FakeAnalyticsService
|
||||||
|
import io.element.android.tests.testutils.test
|
||||||
import io.element.android.tests.testutils.testCoroutineDispatchers
|
import io.element.android.tests.testutils.testCoroutineDispatchers
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.coroutines.test.TestScope
|
import kotlinx.coroutines.test.TestScope
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
|
||||||
class DefaultRoomMembersModerationPresenterTest {
|
class RoomMembersModerationPresenterTest {
|
||||||
@Test
|
@Test
|
||||||
fun `canDisplayModerationActions - when room is DM is false`() = runTest {
|
fun `canDisplayModerationActions - when room is DM is false`() = runTest {
|
||||||
val room = FakeMatrixRoom(isDirect = true, isPublic = true, activeMemberCount = 2).apply {
|
val room = FakeMatrixRoom(isDirect = true, isPublic = true, activeMemberCount = 2).apply {
|
||||||
givenRoomInfo(aRoomInfo(isDirect = true, isPublic = false, activeMembersCount = 2))
|
givenRoomInfo(aRoomInfo(isDirect = true, isPublic = false, activeMembersCount = 2))
|
||||||
}
|
}
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
val presenter = createRoomMembersModerationPresenter(matrixRoom = room)
|
||||||
assertThat(presenter.canDisplayModerationActions()).isFalse()
|
presenter.test {
|
||||||
|
assertThat(awaitItem().canDisplayModerationActions).isFalse()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -51,8 +54,11 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||||
canKickResult = { Result.success(true) },
|
canKickResult = { Result.success(true) },
|
||||||
canBanResult = { Result.success(true) },
|
canBanResult = { Result.success(true) },
|
||||||
)
|
)
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
val presenter = createRoomMembersModerationPresenter(matrixRoom = room)
|
||||||
assertThat(presenter.canDisplayModerationActions()).isTrue()
|
presenter.test {
|
||||||
|
skipItems(1)
|
||||||
|
assertThat(awaitItem().canDisplayModerationActions).isTrue()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -62,8 +68,11 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||||
activeMemberCount = 10,
|
activeMemberCount = 10,
|
||||||
canBanResult = { Result.success(true) },
|
canBanResult = { Result.success(true) },
|
||||||
)
|
)
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
val presenter = createRoomMembersModerationPresenter(matrixRoom = room)
|
||||||
assertThat(presenter.canDisplayModerationActions()).isTrue()
|
presenter.test {
|
||||||
|
skipItems(1)
|
||||||
|
assertThat(awaitItem().canDisplayModerationActions).isTrue()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
@ -74,7 +83,7 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||||
)
|
)
|
||||||
val selectedMember = aVictor()
|
val selectedMember = aVictor()
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
val presenter = createRoomMembersModerationPresenter(matrixRoom = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -101,7 +110,7 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||||
)
|
)
|
||||||
val selectedMember = aRoomMember(A_USER_ID_2, powerLevel = 100L)
|
val selectedMember = aRoomMember(A_USER_ID_2, powerLevel = 100L)
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
val presenter = createRoomMembersModerationPresenter(matrixRoom = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -125,7 +134,7 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||||
canBanResult = { Result.success(true) },
|
canBanResult = { Result.success(true) },
|
||||||
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
userRoleResult = { Result.success(RoomMember.Role.ADMIN) },
|
||||||
)
|
)
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
val presenter = createRoomMembersModerationPresenter(matrixRoom = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -148,7 +157,7 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||||
kickUserResult = { _, _ -> Result.success(Unit) },
|
kickUserResult = { _, _ -> Result.success(Unit) },
|
||||||
)
|
)
|
||||||
val selectedMember = aVictor()
|
val selectedMember = aVictor()
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService)
|
val presenter = createRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -176,7 +185,7 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||||
banUserResult = { _, _ -> Result.success(Unit) },
|
banUserResult = { _, _ -> Result.success(Unit) },
|
||||||
)
|
)
|
||||||
val selectedMember = aVictor()
|
val selectedMember = aVictor()
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService)
|
val presenter = createRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -211,7 +220,7 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||||
).apply {
|
).apply {
|
||||||
givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(selectedMember)))
|
givenRoomMembersState(MatrixRoomMembersState.Ready(persistentListOf(selectedMember)))
|
||||||
}
|
}
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService)
|
val presenter = createRoomMembersModerationPresenter(matrixRoom = room, analyticsService = analyticsService)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -237,7 +246,7 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||||
canBanResult = { Result.success(true) },
|
canBanResult = { Result.success(true) },
|
||||||
userRoleResult = { Result.success(RoomMember.Role.USER) },
|
userRoleResult = { Result.success(RoomMember.Role.USER) },
|
||||||
)
|
)
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
val presenter = createRoomMembersModerationPresenter(matrixRoom = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -261,7 +270,7 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||||
unBanUserResult = { _, _ -> Result.failure(Throwable("Eek")) },
|
unBanUserResult = { _, _ -> Result.failure(Throwable("Eek")) },
|
||||||
userRoleResult = { Result.success(RoomMember.Role.USER) },
|
userRoleResult = { Result.success(RoomMember.Role.USER) },
|
||||||
)
|
)
|
||||||
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
|
val presenter = createRoomMembersModerationPresenter(matrixRoom = room)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
|
|
@ -301,12 +310,12 @@ class DefaultRoomMembersModerationPresenterTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun TestScope.createDefaultRoomMembersModerationPresenter(
|
private fun TestScope.createRoomMembersModerationPresenter(
|
||||||
matrixRoom: FakeMatrixRoom = FakeMatrixRoom(),
|
matrixRoom: FakeMatrixRoom = FakeMatrixRoom(),
|
||||||
dispatchers: CoroutineDispatchers = testCoroutineDispatchers(),
|
dispatchers: CoroutineDispatchers = testCoroutineDispatchers(),
|
||||||
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
|
analyticsService: FakeAnalyticsService = FakeAnalyticsService(),
|
||||||
): DefaultRoomMembersModerationPresenter {
|
): RoomMembersModerationPresenter {
|
||||||
return DefaultRoomMembersModerationPresenter(
|
return RoomMembersModerationPresenter(
|
||||||
room = matrixRoom,
|
room = matrixRoom,
|
||||||
dispatchers = dispatchers,
|
dispatchers = dispatchers,
|
||||||
analyticsService = analyticsService,
|
analyticsService = analyticsService,
|
||||||
Loading…
Add table
Add a link
Reference in a new issue