Merge pull request #2721 from element-hq/feature/bma/roomMemberDetailsPresenterImprovement
Room member details presenter improvement
This commit is contained in:
commit
3d4528700d
4 changed files with 133 additions and 50 deletions
1
changelog.d/2721.misc
Normal file
1
changelog.d/2721.misc
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
RoomMember screen: fallback to userProfile data, if the member is not a user of the room.
|
||||||
|
|
@ -37,8 +37,13 @@ import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
import io.element.android.libraries.matrix.api.core.RoomId
|
import io.element.android.libraries.matrix.api.core.RoomId
|
||||||
import io.element.android.libraries.matrix.api.core.UserId
|
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.MatrixRoom
|
||||||
|
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||||
import io.element.android.libraries.matrix.ui.room.getRoomMemberAsState
|
import io.element.android.libraries.matrix.ui.room.getRoomMemberAsState
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class RoomMemberDetailsPresenter @AssistedInject constructor(
|
class RoomMemberDetailsPresenter @AssistedInject constructor(
|
||||||
|
|
@ -56,20 +61,24 @@ class RoomMemberDetailsPresenter @AssistedInject constructor(
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
var confirmationDialog by remember { mutableStateOf<ConfirmationDialog?>(null) }
|
var confirmationDialog by remember { mutableStateOf<ConfirmationDialog?>(null) }
|
||||||
val roomMember by room.getRoomMemberAsState(roomMemberId)
|
val roomMember by room.getRoomMemberAsState(roomMemberId)
|
||||||
|
var userProfile by remember { mutableStateOf<MatrixUser?>(null) }
|
||||||
val startDmActionState: MutableState<AsyncAction<RoomId>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
|
val startDmActionState: MutableState<AsyncAction<RoomId>> = remember { mutableStateOf(AsyncAction.Uninitialized) }
|
||||||
// the room member is not really live...
|
val isBlocked: MutableState<AsyncData<Boolean>> = remember { mutableStateOf(AsyncData.Uninitialized) }
|
||||||
val isBlocked: MutableState<AsyncData<Boolean>> = remember(roomMember) {
|
LaunchedEffect(Unit) {
|
||||||
val isIgnored = roomMember?.isIgnored
|
client.ignoredUsersFlow
|
||||||
if (isIgnored == null) {
|
.map { ignoredUsers -> roomMemberId in ignoredUsers }
|
||||||
mutableStateOf(AsyncData.Uninitialized)
|
.distinctUntilChanged()
|
||||||
} else {
|
.onEach { isBlocked.value = AsyncData.Success(it) }
|
||||||
mutableStateOf(AsyncData.Success(isIgnored))
|
.launchIn(this)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
// Update room member info when opening this screen
|
// Update room member info when opening this screen
|
||||||
// We don't need to assign the result as it will be automatically propagated by `room.getRoomMemberAsState`
|
// We don't need to assign the result as it will be automatically propagated by `room.getRoomMemberAsState`
|
||||||
room.getUpdatedMember(roomMemberId)
|
room.getUpdatedMember(roomMemberId)
|
||||||
|
.onFailure {
|
||||||
|
// Not a member of the room, try to get the user profile
|
||||||
|
userProfile = client.getProfile(roomMemberId).getOrNull()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleEvents(event: RoomMemberDetailsEvents) {
|
fun handleEvents(event: RoomMemberDetailsEvents) {
|
||||||
|
|
@ -105,16 +114,34 @@ class RoomMemberDetailsPresenter @AssistedInject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val userName by produceState(initialValue = roomMember?.displayName) {
|
val userName: String? by produceState(
|
||||||
room.userDisplayName(roomMemberId).onSuccess { displayName ->
|
initialValue = roomMember?.displayName ?: userProfile?.displayName,
|
||||||
if (displayName != null) value = displayName
|
key1 = roomMember,
|
||||||
}
|
key2 = userProfile,
|
||||||
|
) {
|
||||||
|
value = room.userDisplayName(roomMemberId)
|
||||||
|
.fold(
|
||||||
|
onSuccess = { it },
|
||||||
|
onFailure = {
|
||||||
|
// Fallback to user profile
|
||||||
|
userProfile?.displayName
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
val userAvatar by produceState(initialValue = roomMember?.avatarUrl) {
|
val userAvatar: String? by produceState(
|
||||||
room.userAvatarUrl(roomMemberId).onSuccess { avatarUrl ->
|
initialValue = roomMember?.avatarUrl ?: userProfile?.avatarUrl,
|
||||||
if (avatarUrl != null) value = avatarUrl
|
key1 = roomMember,
|
||||||
}
|
key2 = userProfile,
|
||||||
|
) {
|
||||||
|
value = room.userAvatarUrl(roomMemberId)
|
||||||
|
.fold(
|
||||||
|
onSuccess = { it },
|
||||||
|
onFailure = {
|
||||||
|
// Fallback to user profile
|
||||||
|
userProfile?.avatarUrl
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return RoomMemberDetailsState(
|
return RoomMemberDetailsState(
|
||||||
|
|
@ -124,7 +151,7 @@ class RoomMemberDetailsPresenter @AssistedInject constructor(
|
||||||
isBlocked = isBlocked.value,
|
isBlocked = isBlocked.value,
|
||||||
startDmActionState = startDmActionState.value,
|
startDmActionState = startDmActionState.value,
|
||||||
displayConfirmationDialog = confirmationDialog,
|
displayConfirmationDialog = confirmationDialog,
|
||||||
isCurrentUser = client.isMe(roomMember?.userId),
|
isCurrentUser = client.isMe(roomMemberId),
|
||||||
eventSink = ::handleEvents
|
eventSink = ::handleEvents
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -132,28 +159,18 @@ class RoomMemberDetailsPresenter @AssistedInject constructor(
|
||||||
private fun CoroutineScope.blockUser(userId: UserId, isBlockedState: MutableState<AsyncData<Boolean>>) = launch {
|
private fun CoroutineScope.blockUser(userId: UserId, isBlockedState: MutableState<AsyncData<Boolean>>) = launch {
|
||||||
isBlockedState.value = AsyncData.Loading(false)
|
isBlockedState.value = AsyncData.Loading(false)
|
||||||
client.ignoreUser(userId)
|
client.ignoreUser(userId)
|
||||||
.fold(
|
.onFailure {
|
||||||
onSuccess = {
|
isBlockedState.value = AsyncData.Failure(it, false)
|
||||||
isBlockedState.value = AsyncData.Success(true)
|
}
|
||||||
room.getUpdatedMember(userId)
|
// Note: on success, ignoredUserList will be updated.
|
||||||
},
|
|
||||||
onFailure = {
|
|
||||||
isBlockedState.value = AsyncData.Failure(it, false)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CoroutineScope.unblockUser(userId: UserId, isBlockedState: MutableState<AsyncData<Boolean>>) = launch {
|
private fun CoroutineScope.unblockUser(userId: UserId, isBlockedState: MutableState<AsyncData<Boolean>>) = launch {
|
||||||
isBlockedState.value = AsyncData.Loading(true)
|
isBlockedState.value = AsyncData.Loading(true)
|
||||||
client.unignoreUser(userId)
|
client.unignoreUser(userId)
|
||||||
.fold(
|
.onFailure {
|
||||||
onSuccess = {
|
isBlockedState.value = AsyncData.Failure(it, true)
|
||||||
isBlockedState.value = AsyncData.Success(false)
|
}
|
||||||
room.getUpdatedMember(userId)
|
// Note: on success, ignoredUserList will be updated.
|
||||||
},
|
|
||||||
onFailure = {
|
|
||||||
isBlockedState.value = AsyncData.Failure(it, true)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ package io.element.android.features.roomdetails.members.details
|
||||||
|
|
||||||
import app.cash.molecule.RecompositionMode
|
import app.cash.molecule.RecompositionMode
|
||||||
import app.cash.molecule.moleculeFlow
|
import app.cash.molecule.moleculeFlow
|
||||||
|
import app.cash.turbine.ReceiveTurbine
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import io.element.android.features.createroom.api.StartDMAction
|
import io.element.android.features.createroom.api.StartDMAction
|
||||||
|
|
@ -30,12 +31,13 @@ import io.element.android.features.roomdetails.impl.members.details.RoomMemberDe
|
||||||
import io.element.android.libraries.architecture.AsyncAction
|
import io.element.android.libraries.architecture.AsyncAction
|
||||||
import io.element.android.libraries.architecture.AsyncData
|
import io.element.android.libraries.architecture.AsyncData
|
||||||
import io.element.android.libraries.matrix.api.MatrixClient
|
import io.element.android.libraries.matrix.api.MatrixClient
|
||||||
|
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.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.api.room.RoomMember
|
|
||||||
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||||
import io.element.android.libraries.matrix.test.A_THROWABLE
|
import io.element.android.libraries.matrix.test.A_THROWABLE
|
||||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||||
|
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
||||||
import io.element.android.tests.testutils.WarmUpRule
|
import io.element.android.tests.testutils.WarmUpRule
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
|
@ -58,12 +60,12 @@ class RoomMemberDetailsPresenterTests {
|
||||||
}
|
}
|
||||||
val presenter = createRoomMemberDetailsPresenter(
|
val presenter = createRoomMemberDetailsPresenter(
|
||||||
room = room,
|
room = room,
|
||||||
roomMember = roomMember
|
roomMemberId = roomMember.userId
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitFirstItem()
|
||||||
assertThat(initialState.userId).isEqualTo(roomMember.userId.value)
|
assertThat(initialState.userId).isEqualTo(roomMember.userId.value)
|
||||||
assertThat(initialState.userName).isEqualTo(roomMember.displayName)
|
assertThat(initialState.userName).isEqualTo(roomMember.displayName)
|
||||||
assertThat(initialState.avatarUrl).isEqualTo(roomMember.avatarUrl)
|
assertThat(initialState.avatarUrl).isEqualTo(roomMember.avatarUrl)
|
||||||
|
|
@ -85,12 +87,12 @@ class RoomMemberDetailsPresenterTests {
|
||||||
}
|
}
|
||||||
val presenter = createRoomMemberDetailsPresenter(
|
val presenter = createRoomMemberDetailsPresenter(
|
||||||
room = room,
|
room = room,
|
||||||
roomMember = roomMember
|
roomMemberId = roomMember.userId
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitFirstItem()
|
||||||
assertThat(initialState.userName).isEqualTo(roomMember.displayName)
|
assertThat(initialState.userName).isEqualTo(roomMember.displayName)
|
||||||
assertThat(initialState.avatarUrl).isEqualTo(roomMember.avatarUrl)
|
assertThat(initialState.avatarUrl).isEqualTo(roomMember.avatarUrl)
|
||||||
|
|
||||||
|
|
@ -108,12 +110,12 @@ class RoomMemberDetailsPresenterTests {
|
||||||
}
|
}
|
||||||
val presenter = createRoomMemberDetailsPresenter(
|
val presenter = createRoomMemberDetailsPresenter(
|
||||||
room = room,
|
room = room,
|
||||||
roomMember = roomMember
|
roomMemberId = roomMember.userId
|
||||||
)
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitFirstItem()
|
||||||
assertThat(initialState.userName).isEqualTo(roomMember.displayName)
|
assertThat(initialState.userName).isEqualTo(roomMember.displayName)
|
||||||
assertThat(initialState.avatarUrl).isEqualTo(roomMember.avatarUrl)
|
assertThat(initialState.avatarUrl).isEqualTo(roomMember.avatarUrl)
|
||||||
|
|
||||||
|
|
@ -121,13 +123,40 @@ class RoomMemberDetailsPresenterTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - will fallback to user profile if user is not a member of the room`() = runTest {
|
||||||
|
val bobProfile = aMatrixUser("@bob:server.org", "Bob", avatarUrl = "anAvatarUrl")
|
||||||
|
val room = aMatrixRoom().apply {
|
||||||
|
givenUserDisplayNameResult(Result.failure(Exception("Not a member!")))
|
||||||
|
givenUserAvatarUrlResult(Result.failure(Exception("Not a member!")))
|
||||||
|
}
|
||||||
|
val client = FakeMatrixClient().apply {
|
||||||
|
givenGetProfileResult(bobProfile.userId, Result.success(bobProfile))
|
||||||
|
}
|
||||||
|
val presenter = createRoomMemberDetailsPresenter(
|
||||||
|
client = client,
|
||||||
|
room = room,
|
||||||
|
roomMemberId = UserId("@bob:server.org")
|
||||||
|
)
|
||||||
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
presenter.present()
|
||||||
|
}.test {
|
||||||
|
skipItems(2)
|
||||||
|
val initialState = awaitFirstItem()
|
||||||
|
assertThat(initialState.userName).isEqualTo("Bob")
|
||||||
|
assertThat(initialState.avatarUrl).isEqualTo("anAvatarUrl")
|
||||||
|
|
||||||
|
ensureAllEventsConsumed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - BlockUser needing confirmation displays confirmation dialog`() = runTest {
|
fun `present - BlockUser needing confirmation displays confirmation dialog`() = runTest {
|
||||||
val presenter = createRoomMemberDetailsPresenter()
|
val presenter = createRoomMemberDetailsPresenter()
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitFirstItem()
|
||||||
initialState.eventSink(RoomMemberDetailsEvents.BlockUser(needsConfirmation = true))
|
initialState.eventSink(RoomMemberDetailsEvents.BlockUser(needsConfirmation = true))
|
||||||
|
|
||||||
val dialogState = awaitItem()
|
val dialogState = awaitItem()
|
||||||
|
|
@ -142,17 +171,24 @@ class RoomMemberDetailsPresenterTests {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - BlockUser and UnblockUser without confirmation change the 'blocked' state`() = runTest {
|
fun `present - BlockUser and UnblockUser without confirmation change the 'blocked' state`() = runTest {
|
||||||
val presenter = createRoomMemberDetailsPresenter()
|
val client = FakeMatrixClient()
|
||||||
|
val roomMember = aRoomMember()
|
||||||
|
val presenter = createRoomMemberDetailsPresenter(
|
||||||
|
client = client,
|
||||||
|
roomMemberId = roomMember.userId
|
||||||
|
)
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitFirstItem()
|
||||||
initialState.eventSink(RoomMemberDetailsEvents.BlockUser(needsConfirmation = false))
|
initialState.eventSink(RoomMemberDetailsEvents.BlockUser(needsConfirmation = false))
|
||||||
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
|
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
|
||||||
|
client.emitIgnoreUserList(listOf(roomMember.userId))
|
||||||
assertThat(awaitItem().isBlocked.dataOrNull()).isTrue()
|
assertThat(awaitItem().isBlocked.dataOrNull()).isTrue()
|
||||||
|
|
||||||
initialState.eventSink(RoomMemberDetailsEvents.UnblockUser(needsConfirmation = false))
|
initialState.eventSink(RoomMemberDetailsEvents.UnblockUser(needsConfirmation = false))
|
||||||
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
|
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
|
||||||
|
client.emitIgnoreUserList(listOf())
|
||||||
assertThat(awaitItem().isBlocked.dataOrNull()).isFalse()
|
assertThat(awaitItem().isBlocked.dataOrNull()).isFalse()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -165,7 +201,7 @@ class RoomMemberDetailsPresenterTests {
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitFirstItem()
|
||||||
initialState.eventSink(RoomMemberDetailsEvents.BlockUser(needsConfirmation = false))
|
initialState.eventSink(RoomMemberDetailsEvents.BlockUser(needsConfirmation = false))
|
||||||
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
|
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
|
||||||
val errorState = awaitItem()
|
val errorState = awaitItem()
|
||||||
|
|
@ -176,13 +212,32 @@ class RoomMemberDetailsPresenterTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `present - UnblockUser with error`() = runTest {
|
||||||
|
val matrixClient = FakeMatrixClient()
|
||||||
|
matrixClient.givenUnignoreUserResult(Result.failure(A_THROWABLE))
|
||||||
|
val presenter = createRoomMemberDetailsPresenter(client = matrixClient)
|
||||||
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
presenter.present()
|
||||||
|
}.test {
|
||||||
|
val initialState = awaitFirstItem()
|
||||||
|
initialState.eventSink(RoomMemberDetailsEvents.UnblockUser(needsConfirmation = false))
|
||||||
|
assertThat(awaitItem().isBlocked.isLoading()).isTrue()
|
||||||
|
val errorState = awaitItem()
|
||||||
|
assertThat(errorState.isBlocked.errorOrNull()).isEqualTo(A_THROWABLE)
|
||||||
|
// Clear error
|
||||||
|
initialState.eventSink(RoomMemberDetailsEvents.ClearBlockUserError)
|
||||||
|
assertThat(awaitItem().isBlocked).isEqualTo(AsyncData.Success(true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `present - UnblockUser needing confirmation displays confirmation dialog`() = runTest {
|
fun `present - UnblockUser needing confirmation displays confirmation dialog`() = runTest {
|
||||||
val presenter = createRoomMemberDetailsPresenter()
|
val presenter = createRoomMemberDetailsPresenter()
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitFirstItem()
|
||||||
initialState.eventSink(RoomMemberDetailsEvents.UnblockUser(needsConfirmation = true))
|
initialState.eventSink(RoomMemberDetailsEvents.UnblockUser(needsConfirmation = true))
|
||||||
|
|
||||||
val dialogState = awaitItem()
|
val dialogState = awaitItem()
|
||||||
|
|
@ -202,7 +257,7 @@ class RoomMemberDetailsPresenterTests {
|
||||||
moleculeFlow(RecompositionMode.Immediate) {
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
presenter.present()
|
presenter.present()
|
||||||
}.test {
|
}.test {
|
||||||
val initialState = awaitItem()
|
val initialState = awaitFirstItem()
|
||||||
assertThat(initialState.startDmActionState).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
assertThat(initialState.startDmActionState).isInstanceOf(AsyncAction.Uninitialized::class.java)
|
||||||
val startDMSuccessResult = AsyncAction.Success(A_ROOM_ID)
|
val startDMSuccessResult = AsyncAction.Success(A_ROOM_ID)
|
||||||
val startDMFailureResult = AsyncAction.Failure(A_THROWABLE)
|
val startDMFailureResult = AsyncAction.Failure(A_THROWABLE)
|
||||||
|
|
@ -229,14 +284,19 @@ class RoomMemberDetailsPresenterTests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private suspend fun <T> ReceiveTurbine<T>.awaitFirstItem(): T {
|
||||||
|
skipItems(1)
|
||||||
|
return awaitItem()
|
||||||
|
}
|
||||||
|
|
||||||
private fun createRoomMemberDetailsPresenter(
|
private fun createRoomMemberDetailsPresenter(
|
||||||
client: MatrixClient = FakeMatrixClient(),
|
client: MatrixClient = FakeMatrixClient(),
|
||||||
room: MatrixRoom = aMatrixRoom(),
|
room: MatrixRoom = aMatrixRoom(),
|
||||||
roomMember: RoomMember = aRoomMember(),
|
roomMemberId: UserId = UserId("@alice:server.org"),
|
||||||
startDMAction: StartDMAction = FakeStartDMAction()
|
startDMAction: StartDMAction = FakeStartDMAction()
|
||||||
): RoomMemberDetailsPresenter {
|
): RoomMemberDetailsPresenter {
|
||||||
return RoomMemberDetailsPresenter(
|
return RoomMemberDetailsPresenter(
|
||||||
roomMemberId = roomMember.userId,
|
roomMemberId = roomMemberId,
|
||||||
client = client,
|
client = client,
|
||||||
room = room,
|
room = room,
|
||||||
startDMAction = startDMAction
|
startDMAction = startDMAction
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ import io.element.android.libraries.matrix.test.verification.FakeSessionVerifica
|
||||||
import io.element.android.tests.testutils.simulateLongTask
|
import io.element.android.tests.testutils.simulateLongTask
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
@ -205,6 +206,10 @@ class FakeMatrixClient(
|
||||||
return RoomMembershipObserver()
|
return RoomMembershipObserver()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun emitIgnoreUserList(users: List<UserId>) {
|
||||||
|
ignoredUsersFlow.emit(users.toImmutableList())
|
||||||
|
}
|
||||||
|
|
||||||
// Mocks
|
// Mocks
|
||||||
|
|
||||||
fun givenLogoutError(failure: Throwable?) {
|
fun givenLogoutError(failure: Throwable?) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue