Merge pull request #6045 from element-hq/feature/fga/invite_people_suggestions
Add suggestions section to InvitePeopleView
This commit is contained in:
@@ -11,6 +11,7 @@ package io.element.android.features.invitepeople.impl
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.produceState
|
||||
@@ -39,6 +40,7 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
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.filterMembers
|
||||
import io.element.android.libraries.matrix.api.room.recent.getRecentDirectRooms
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import io.element.android.libraries.ui.strings.CommonStrings
|
||||
import io.element.android.libraries.usersearch.api.UserRepository
|
||||
@@ -47,11 +49,16 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.persistentListOf
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.filterNot
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
private const val MAX_SUGGESTIONS_COUNT = 5
|
||||
|
||||
@AssistedInject
|
||||
class DefaultInvitePeoplePresenter(
|
||||
@Assisted private val joinedRoom: JoinedRoom?,
|
||||
@@ -78,6 +85,34 @@ class DefaultInvitePeoplePresenter(
|
||||
val showSearchLoader = rememberSaveable { mutableStateOf(false) }
|
||||
val sendInvitesAction = remember { mutableStateOf<AsyncAction<Unit>>(AsyncAction.Uninitialized) }
|
||||
|
||||
val recentDirectRooms by produceState(emptyList(), roomMembers.value) {
|
||||
if (roomMembers.value.isSuccess()) {
|
||||
val activeMemberIds = roomMembers.value.dataOrNull().orEmpty()
|
||||
.filter { it.membership.isActive() }
|
||||
.mapTo(mutableSetOf()) { it.userId }
|
||||
|
||||
value = matrixClient.getRecentDirectRooms()
|
||||
.filterNot { it.matrixUser.userId in activeMemberIds }
|
||||
.take(MAX_SUGGESTIONS_COUNT)
|
||||
.toList()
|
||||
}
|
||||
}
|
||||
|
||||
// Convert recent direct rooms to InvitableUser for display
|
||||
val suggestions by remember {
|
||||
derivedStateOf {
|
||||
recentDirectRooms.map { recentDirectRoom ->
|
||||
InvitableUser(
|
||||
matrixUser = recentDirectRoom.matrixUser,
|
||||
isSelected = recentDirectRoom.matrixUser in selectedUsers.value,
|
||||
isAlreadyJoined = false,
|
||||
isAlreadyInvited = false,
|
||||
isUnresolved = false,
|
||||
)
|
||||
}.toImmutableList()
|
||||
}
|
||||
}
|
||||
|
||||
val room by produceState(if (joinedRoom != null) AsyncData.Success(joinedRoom) else AsyncData.Loading()) {
|
||||
if (joinedRoom == null) {
|
||||
val result = matrixClient.getJoinedRoom(roomId)
|
||||
@@ -118,6 +153,7 @@ class DefaultInvitePeoplePresenter(
|
||||
is DefaultInvitePeopleEvents.ToggleUser -> {
|
||||
selectedUsers.toggleUser(event.user)
|
||||
searchResults.toggleUser(event.user)
|
||||
// suggestions will automatically update via derivedStateOf when selectedUsers changes
|
||||
}
|
||||
is InvitePeopleEvents.SendInvites -> {
|
||||
room.dataOrNull()?.let {
|
||||
@@ -140,6 +176,7 @@ class DefaultInvitePeoplePresenter(
|
||||
searchResults = searchResults.value,
|
||||
showSearchLoader = showSearchLoader.value,
|
||||
sendInvitesAction = sendInvitesAction.value,
|
||||
suggestions = suggestions,
|
||||
eventSink = ::handleEvent,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -25,5 +25,6 @@ data class DefaultInvitePeopleState(
|
||||
val selectedUsers: ImmutableList<MatrixUser>,
|
||||
override val isSearchActive: Boolean,
|
||||
override val sendInvitesAction: AsyncAction<Unit>,
|
||||
val suggestions: ImmutableList<InvitableUser>,
|
||||
override val eventSink: (InvitePeopleEvents) -> Unit
|
||||
) : InvitePeopleState
|
||||
|
||||
@@ -101,6 +101,9 @@ private fun aDefaultInvitePeopleState(
|
||||
isSearchActive: Boolean = false,
|
||||
showSearchLoader: Boolean = false,
|
||||
sendInvitesAction: AsyncAction<Unit> = AsyncAction.Uninitialized,
|
||||
suggestions: List<InvitableUser> = aMatrixUserList()
|
||||
.take(5)
|
||||
.map { user -> anInvitableUser(matrixUser = user, isSelected = user in selectedUsers) },
|
||||
): DefaultInvitePeopleState {
|
||||
return DefaultInvitePeopleState(
|
||||
room = room,
|
||||
@@ -111,6 +114,7 @@ private fun aDefaultInvitePeopleState(
|
||||
isSearchActive = isSearchActive,
|
||||
showSearchLoader = showSearchLoader,
|
||||
sendInvitesAction = sendInvitesAction,
|
||||
suggestions = suggestions.toImmutableList(),
|
||||
eventSink = {},
|
||||
)
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ import io.element.android.libraries.designsystem.components.async.AsyncLoading
|
||||
import io.element.android.libraries.designsystem.components.avatar.AvatarSize
|
||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||
import io.element.android.libraries.designsystem.theme.components.HorizontalDivider
|
||||
import io.element.android.libraries.designsystem.theme.components.ListSectionHeader
|
||||
import io.element.android.libraries.designsystem.theme.components.SearchBar
|
||||
import io.element.android.libraries.designsystem.theme.components.SearchBarResultState
|
||||
import io.element.android.libraries.designsystem.theme.components.Text
|
||||
@@ -82,6 +84,10 @@ private fun InvitePeopleContentView(
|
||||
modifier = modifier.fillMaxSize(),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
fun toggleUser(user: MatrixUser) {
|
||||
state.eventSink(DefaultInvitePeopleEvents.ToggleUser(user))
|
||||
}
|
||||
|
||||
InvitePeopleSearchBar(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
query = state.searchQuery,
|
||||
@@ -97,17 +103,45 @@ private fun InvitePeopleContentView(
|
||||
)
|
||||
},
|
||||
onTextChange = { state.eventSink(DefaultInvitePeopleEvents.UpdateSearchQuery(it)) },
|
||||
onToggleUser = { state.eventSink(DefaultInvitePeopleEvents.ToggleUser(it)) },
|
||||
onToggleUser = ::toggleUser,
|
||||
)
|
||||
|
||||
if (!state.isSearchActive) {
|
||||
SelectedUsersRowList(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
selectedUsers = state.selectedUsers,
|
||||
autoScroll = true,
|
||||
onUserRemove = { state.eventSink(DefaultInvitePeopleEvents.ToggleUser(it)) },
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
)
|
||||
if (state.selectedUsers.isNotEmpty()) {
|
||||
SelectedUsersRowList(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
selectedUsers = state.selectedUsers,
|
||||
autoScroll = true,
|
||||
onUserRemove = ::toggleUser,
|
||||
contentPadding = PaddingValues(all = 16.dp),
|
||||
)
|
||||
}
|
||||
if (state.suggestions.isNotEmpty()) {
|
||||
LazyColumn {
|
||||
item {
|
||||
ListSectionHeader(
|
||||
title = stringResource(id = CommonStrings.common_suggestions),
|
||||
hasDivider = false,
|
||||
)
|
||||
}
|
||||
itemsIndexed(state.suggestions) { index, invitableUser ->
|
||||
CheckableUserRow(
|
||||
checked = invitableUser.isSelected,
|
||||
onCheckedChange = {
|
||||
state.eventSink(DefaultInvitePeopleEvents.ToggleUser(invitableUser.matrixUser))
|
||||
},
|
||||
data = CheckableUserRowData.Resolved(
|
||||
avatarData = invitableUser.matrixUser.getAvatarData(AvatarSize.UserListItem),
|
||||
name = invitableUser.matrixUser.getBestName(),
|
||||
subtext = invitableUser.matrixUser.userId.value,
|
||||
),
|
||||
)
|
||||
if (index < state.suggestions.lastIndex) {
|
||||
HorizontalDivider()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,7 +174,7 @@ private fun InvitePeopleSearchBar(
|
||||
selectedUsers = selectedUsers,
|
||||
autoScroll = true,
|
||||
onUserRemove = onToggleUser,
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
contentPadding = PaddingValues(all = 16.dp),
|
||||
)
|
||||
}
|
||||
},
|
||||
|
||||
@@ -17,6 +17,7 @@ import io.element.android.libraries.designsystem.theme.components.SearchBarResul
|
||||
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.UserId
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.JoinedRoom
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembersState
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
||||
@@ -26,7 +27,9 @@ import io.element.android.libraries.matrix.test.A_ROOM_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID_2
|
||||
import io.element.android.libraries.matrix.test.FakeMatrixClient
|
||||
import io.element.android.libraries.matrix.test.room.FakeBaseRoom
|
||||
import io.element.android.libraries.matrix.test.room.FakeJoinedRoom
|
||||
import io.element.android.libraries.matrix.test.room.aRoomInfo
|
||||
import io.element.android.libraries.matrix.test.room.aRoomMember
|
||||
import io.element.android.libraries.matrix.test.room.aRoomMemberList
|
||||
import io.element.android.libraries.matrix.ui.components.aMatrixUser
|
||||
@@ -67,13 +70,15 @@ internal class DefaultInvitePeoplePresenterTest {
|
||||
assertThat(initialState.canInvite).isFalse()
|
||||
assertThat(initialState.searchQuery).isEmpty()
|
||||
|
||||
skipItems(1)
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - updates search active state`() = runTest {
|
||||
val presenter = createDefaultInvitePeoplePresenter()
|
||||
val presenter = createDefaultInvitePeoplePresenter(
|
||||
coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
skipItems(1)
|
||||
@@ -85,11 +90,12 @@ internal class DefaultInvitePeoplePresenterTest {
|
||||
resultState.eventSink(DefaultInvitePeopleEvents.UpdateSearchQuery("some query"))
|
||||
assertThat(awaitItemAsDefault().searchQuery).isEqualTo("some query")
|
||||
resultState.eventSink(InvitePeopleEvents.CloseSearch)
|
||||
skipItems(1)
|
||||
skipItems(2)
|
||||
awaitItemAsDefault().also {
|
||||
assertThat(it.isSearchActive).isFalse()
|
||||
assertThat(it.searchQuery).isEmpty()
|
||||
}
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -275,7 +281,7 @@ internal class DefaultInvitePeoplePresenterTest {
|
||||
val repository = FakeUserRepository()
|
||||
val presenter = createDefaultInvitePeoplePresenter(
|
||||
userRepository = repository,
|
||||
coroutineDispatchers = testCoroutineDispatchers()
|
||||
coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true)
|
||||
)
|
||||
presenter.test {
|
||||
val initialState = awaitItem()
|
||||
@@ -519,6 +525,85 @@ internal class DefaultInvitePeoplePresenterTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - suggestions are loaded from recent direct rooms`() = runTest {
|
||||
val dmRoomId = RoomId("!dm_room:server.org")
|
||||
val otherUserId = UserId("@frank:server.org")
|
||||
val matrixClient = FakeMatrixClient(sessionId = A_USER_ID).apply {
|
||||
// Track the DM room as recently visited
|
||||
trackRecentlyVisitedRoom(dmRoomId)
|
||||
// Set up a DM room with the other user
|
||||
givenGetRoomResult(
|
||||
dmRoomId,
|
||||
FakeBaseRoom(
|
||||
sessionId = A_USER_ID,
|
||||
roomId = dmRoomId,
|
||||
initialRoomInfo = aRoomInfo(
|
||||
id = dmRoomId,
|
||||
isDirect = true,
|
||||
activeMembersCount = 2,
|
||||
currentUserMembership = CurrentUserMembership.JOINED,
|
||||
),
|
||||
getDirectRoomMemberResult = { aRoomMember(userId = otherUserId, displayName = "Frank") }
|
||||
)
|
||||
)
|
||||
}
|
||||
val presenter = createDefaultInvitePeoplePresenter(
|
||||
matrixClient = matrixClient,
|
||||
// Use empty room members so the suggestion doesn't get filtered
|
||||
roomMembersState = RoomMembersState.Ready(persistentListOf()),
|
||||
coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(2)
|
||||
val state = awaitItemAsDefault()
|
||||
assertThat(state.suggestions).hasSize(1)
|
||||
assertThat(state.suggestions.first().matrixUser.userId).isEqualTo(otherUserId)
|
||||
assertThat(state.suggestions.first().isSelected).isFalse()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `present - suggestions filters out existing room members`() = runTest {
|
||||
val dmRoomId = RoomId("!dm_room:server.org")
|
||||
val alreadyJoinedUserId = UserId("@frank:server.org")
|
||||
val matrixClient = FakeMatrixClient(sessionId = A_USER_ID).apply {
|
||||
trackRecentlyVisitedRoom(dmRoomId)
|
||||
givenGetRoomResult(
|
||||
dmRoomId,
|
||||
FakeBaseRoom(
|
||||
sessionId = A_USER_ID,
|
||||
roomId = dmRoomId,
|
||||
initialRoomInfo = aRoomInfo(
|
||||
id = dmRoomId,
|
||||
isDirect = true,
|
||||
activeMembersCount = 2,
|
||||
currentUserMembership = CurrentUserMembership.JOINED,
|
||||
),
|
||||
getDirectRoomMemberResult = { aRoomMember(userId = alreadyJoinedUserId, displayName = "Frank") }
|
||||
)
|
||||
)
|
||||
}
|
||||
// The user in the suggestion is already a member of the target room
|
||||
val presenter = createDefaultInvitePeoplePresenter(
|
||||
matrixClient = matrixClient,
|
||||
roomMembersState = RoomMembersState.Ready(
|
||||
persistentListOf(
|
||||
aRoomMember(userId = alreadyJoinedUserId, membership = RoomMembershipState.JOIN)
|
||||
)
|
||||
),
|
||||
coroutineDispatchers = testCoroutineDispatchers(useUnconfinedTestDispatcher = true),
|
||||
)
|
||||
presenter.test {
|
||||
skipItems(1)
|
||||
// The suggestion should be filtered out because the user is already a room member
|
||||
val state = awaitItemAsDefault()
|
||||
assertThat(state.suggestions).isEmpty()
|
||||
cancelAndIgnoreRemainingEvents()
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun FakeUserRepository.emitStateWithUsers(
|
||||
users: List<MatrixUser>,
|
||||
isSearching: Boolean = false
|
||||
|
||||
@@ -31,6 +31,10 @@ import kotlinx.collections.immutable.ImmutableList
|
||||
import kotlinx.collections.immutable.toImmutableList
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.flow.toList
|
||||
|
||||
private const val MAX_SUGGESTIONS_COUNT = 5
|
||||
|
||||
@AssistedInject
|
||||
class DefaultUserListPresenter(
|
||||
@@ -53,7 +57,10 @@ class DefaultUserListPresenter(
|
||||
override fun present(): UserListState {
|
||||
var recentDirectRooms by remember { mutableStateOf(emptyList<RecentDirectRoom>()) }
|
||||
LaunchedEffect(Unit) {
|
||||
recentDirectRooms = matrixClient.getRecentDirectRooms()
|
||||
recentDirectRooms = matrixClient
|
||||
.getRecentDirectRooms()
|
||||
.take(MAX_SUGGESTIONS_COUNT)
|
||||
.toList()
|
||||
}
|
||||
var isSearchActive by rememberSaveable { mutableStateOf(false) }
|
||||
val selectedUsers by userListDataStore.selectedUsers.collectAsState(emptyList())
|
||||
|
||||
@@ -85,6 +85,13 @@ interface BaseRoom : Closeable {
|
||||
*/
|
||||
suspend fun getUpdatedMember(userId: UserId): Result<RoomMember>
|
||||
|
||||
/**
|
||||
* Gets the direct room member, if any.
|
||||
* This is a convenience method for getting the other member in a direct message room.
|
||||
* Returns null if the room is not a dm or if the member cannot be found.
|
||||
*/
|
||||
suspend fun getDirectRoomMember(): RoomMember?
|
||||
|
||||
/**
|
||||
* Adds the room to the sync subscription list.
|
||||
*/
|
||||
|
||||
@@ -17,10 +17,7 @@ import kotlin.coroutines.CoroutineContext
|
||||
* It does filter through the already known members, it doesn't perform additional requests.
|
||||
*/
|
||||
suspend fun BaseRoom.filterMembers(query: String, coroutineContext: CoroutineContext): List<RoomMember> = withContext(coroutineContext) {
|
||||
val roomMembersState = membersStateFlow.value
|
||||
val activeRoomMembers = roomMembersState.roomMembers()
|
||||
?.filter { it.membership.isActive() }
|
||||
.orEmpty()
|
||||
val activeRoomMembers = membersStateFlow.value.activeRoomMembers()
|
||||
val filteredMembers = if (query.isBlank()) {
|
||||
activeRoomMembers
|
||||
} else {
|
||||
|
||||
@@ -11,48 +11,35 @@ package io.element.android.libraries.matrix.api.room.recent
|
||||
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.UserId
|
||||
import io.element.android.libraries.matrix.api.room.BaseRoom
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.isDm
|
||||
import io.element.android.libraries.matrix.api.room.toMatrixUser
|
||||
import io.element.android.libraries.matrix.api.user.MatrixUser
|
||||
import kotlinx.coroutines.flow.first
|
||||
|
||||
private const val MAX_RECENT_DIRECT_ROOMS_TO_RETURN = 5
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
|
||||
data class RecentDirectRoom(
|
||||
val roomId: RoomId,
|
||||
val matrixUser: MatrixUser,
|
||||
)
|
||||
|
||||
suspend fun MatrixClient.getRecentDirectRooms(
|
||||
maxNumberOfResults: Int = MAX_RECENT_DIRECT_ROOMS_TO_RETURN,
|
||||
): List<RecentDirectRoom> {
|
||||
val result = mutableListOf<RecentDirectRoom>()
|
||||
/**
|
||||
* Returns a [Flow] of [RecentDirectRoom] from recently visited DM rooms.
|
||||
* The flow emits items lazily, allowing callers to filter and take only what they need.
|
||||
* Use [kotlinx.coroutines.flow.take] to limit results and stop iteration early.
|
||||
*/
|
||||
fun MatrixClient.getRecentDirectRooms(): Flow<RecentDirectRoom> = flow {
|
||||
val foundUserIds = mutableSetOf<UserId>()
|
||||
getRecentlyVisitedRooms().getOrNull()?.let { roomIds ->
|
||||
roomIds
|
||||
.mapNotNull { roomId -> getRoom(roomId) }
|
||||
.filter { it.isDm() && it.isJoined() }
|
||||
.map { room ->
|
||||
val otherUser = room.getMembers().getOrNull()
|
||||
?.firstOrNull { it.userId != sessionId }
|
||||
?.takeIf { foundUserIds.add(it.userId) }
|
||||
?.toMatrixUser()
|
||||
if (otherUser != null) {
|
||||
result.add(
|
||||
RecentDirectRoom(room.roomId, otherUser)
|
||||
)
|
||||
// Return early to avoid useless computation
|
||||
if (result.size >= maxNumberOfResults) {
|
||||
return@map
|
||||
}
|
||||
val recentlyVisitedRooms = getRecentlyVisitedRooms().getOrDefault(emptyList())
|
||||
for (roomId in recentlyVisitedRooms) {
|
||||
getRoom(roomId)?.use { room ->
|
||||
val info = room.info()
|
||||
if (info.isDm && info.currentUserMembership == CurrentUserMembership.JOINED) {
|
||||
val otherUser = room.getDirectRoomMember()?.toMatrixUser()
|
||||
if (otherUser != null && foundUserIds.add(otherUser.userId)) {
|
||||
emit(RecentDirectRoom(room.roomId, otherUser))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
suspend fun BaseRoom.isJoined(): Boolean {
|
||||
return roomInfoFlow.first().currentUserMembership == CurrentUserMembership.JOINED
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembersState
|
||||
import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
|
||||
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
|
||||
import io.element.android.libraries.matrix.api.room.isDm
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPermissions
|
||||
import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues
|
||||
import io.element.android.libraries.matrix.api.room.tombstone.PredecessorRoom
|
||||
@@ -112,6 +113,20 @@ class RustBaseRoom(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getDirectRoomMember(): RoomMember? = withContext(roomDispatcher) {
|
||||
runCatchingExceptions {
|
||||
if (info().isDm) {
|
||||
innerRoom.membersNoSync().use { members ->
|
||||
members.nextChunk(members.len())
|
||||
?.map(RoomMemberMapper::map)
|
||||
?.firstOrNull { roomMember -> roomMember.userId != sessionId && roomMember.membership.isActive() }
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
override suspend fun getUpdatedMember(userId: UserId): Result<RoomMember> = withContext(roomDispatcher) {
|
||||
runCatchingExceptions {
|
||||
RoomMemberMapper.map(innerRoom.member(userId.value))
|
||||
|
||||
@@ -55,6 +55,7 @@ class FakeBaseRoom(
|
||||
private val leaveRoomLambda: () -> Result<Unit> = { lambdaError() },
|
||||
private var updateMembersResult: () -> Unit = { lambdaError() },
|
||||
private val getMembersResult: (Int) -> Result<List<RoomMember>> = { lambdaError() },
|
||||
private val getDirectRoomMemberResult: () -> RoomMember? = { null },
|
||||
private val saveComposerDraftLambda: (ComposerDraft) -> Result<Unit> = { _: ComposerDraft -> Result.success(Unit) },
|
||||
private val loadComposerDraftLambda: () -> Result<ComposerDraft?> = { Result.success<ComposerDraft?>(null) },
|
||||
private val clearComposerDraftLambda: () -> Result<Unit> = { Result.success(Unit) },
|
||||
@@ -90,6 +91,10 @@ class FakeBaseRoom(
|
||||
return getMembersResult(limit)
|
||||
}
|
||||
|
||||
override suspend fun getDirectRoomMember(): RoomMember? {
|
||||
return getDirectRoomMemberResult()
|
||||
}
|
||||
|
||||
override suspend fun subscribeToSync() {
|
||||
subscribeToSyncLambda()
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user