From 44334364168545cdef5e145294efa3bb5034b323 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 28 Oct 2020 18:09:16 +0100 Subject: [PATCH 1/4] Room member profile: Add action to create (or open) a DM (#2310) --- CHANGES.md | 1 + .../android/sdk/common/CryptoTestHelper.kt | 9 +-------- .../android/sdk/api/session/room/RoomService.kt | 16 ++++++++++++++++ .../VerificationBottomSheetViewModel.kt | 10 +--------- .../home/room/detail/RoomDetailAction.kt | 1 + .../home/room/detail/RoomDetailFragment.kt | 7 +++++++ .../home/room/detail/RoomDetailPendingAction.kt | 1 + .../home/room/detail/RoomDetailViewEvents.kt | 2 ++ .../home/room/detail/RoomDetailViewModel.kt | 16 ++++++++++++++++ .../RoomMemberProfileController.kt | 9 +++++++++ .../RoomMemberProfileFragment.kt | 5 +++++ vector/src/main/res/values/strings.xml | 1 + 12 files changed, 61 insertions(+), 17 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 8261412b4c..5cf2e7f53b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -12,6 +12,7 @@ Improvements 🙌: - Use Hardware keyboard enter to send message (use shift-enter for new line) (#1881, #1440) - Edit and remove icons are now visible on image attachment preview screen (#2294) - Room profile: BigImageViewerActivity now only display the image. Use the room setting to change or delete the room Avatar + - Room member profile: Add action to create (or open) a DM (#2310) Bugfix 🐛: - Messages encrypted with no way to decrypt after SDK update from 0.18 to 1.0.0 (#2252) diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt index 370b416f54..ec2b28a4da 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/common/CryptoTestHelper.kt @@ -285,14 +285,7 @@ class CryptoTestHelper(private val mTestHelper: CommonTestHelper) { fun createDM(alice: Session, bob: Session): String { val roomId = mTestHelper.doSync { - alice.createRoom( - CreateRoomParams().apply { - invitedUserIds.add(bob.myUserId) - setDirectMessage() - enableEncryptionIfInvitedUsersSupportIt = true - }, - it - ) + alice.createDirectRoom(bob.myUserId, it) } mTestHelper.waitWithLatch { latch -> diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt index 965e7e23bb..9bd7738af4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt @@ -35,6 +35,22 @@ interface RoomService { fun createRoom(createRoomParams: CreateRoomParams, callback: MatrixCallback): Cancelable + /** + * Create a direct room asynchronously. This is a facility method to create a direct room with the necessary parameters + */ + fun createDirectRoom(otherUserId: String, + callback: MatrixCallback): Cancelable { + return createRoom( + CreateRoomParams() + .apply { + invitedUserIds.add(otherUserId) + setDirectMessage() + enableEncryptionIfInvitedUsersSupportIt = true + }, + callback + ) + } + /** * Join a room by id * @param roomIdOrAlias the roomId or the room alias of the room to join diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt index 2720c20fb0..22711108f8 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt @@ -49,7 +49,6 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationServic import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState import org.matrix.android.sdk.api.session.events.model.LocalEcho -import org.matrix.android.sdk.api.session.room.model.create.CreateRoomParams import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.internal.crypto.crosssigning.fromBase64 @@ -245,14 +244,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( pendingRequest = Loading() ) } - val roomParams = CreateRoomParams() - .apply { - invitedUserIds.add(otherUserId) - setDirectMessage() - enableEncryptionIfInvitedUsersSupportIt = true - } - - session.createRoom(roomParams, object : MatrixCallback { + session.createDirectRoom(otherUserId, object : MatrixCallback { override fun onSuccess(data: String) { setState { copy( diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt index 912967138b..99adc0bf83 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailAction.kt @@ -88,5 +88,6 @@ sealed class RoomDetailAction : VectorViewModelAction { val userJustAccepted: Boolean, val grantedEvents: RoomDetailViewEvents) : RoomDetailAction() + data class OpenOrCreateDm(val userId: String) : RoomDetailAction() data class JumpToReadReceipt(val userId: String) : RoomDetailAction() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index 24f3cabe12..9c6c473a7f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -363,6 +363,7 @@ class RoomDetailFragment @Inject constructor( RoomDetailViewEvents.ShowWaitingView -> vectorBaseActivity.showWaitingView() RoomDetailViewEvents.HideWaitingView -> vectorBaseActivity.hideWaitingView() is RoomDetailViewEvents.RequestNativeWidgetPermission -> requestNativeWidgetPermission(it) + is RoomDetailViewEvents.OpenRoom -> handleOpenRoom(it) }.exhaustive } @@ -371,6 +372,10 @@ class RoomDetailFragment @Inject constructor( } } + private fun handleOpenRoom(openRoom: RoomDetailViewEvents.OpenRoom) { + navigator.openRoom(requireContext(), openRoom.roomId, null) + } + private fun requestNativeWidgetPermission(it: RoomDetailViewEvents.RequestNativeWidgetPermission) { val tag = RoomWidgetPermissionBottomSheet::class.java.name val dFrag = childFragmentManager.findFragmentByTag(tag) as? RoomWidgetPermissionBottomSheet @@ -886,6 +891,8 @@ class RoomDetailFragment @Inject constructor( roomDetailViewModel.handle(RoomDetailAction.JumpToReadReceipt(roomDetailPendingAction.userId)) is RoomDetailPendingAction.MentionUser -> insertUserDisplayNameInTextEditor(roomDetailPendingAction.userId) + is RoomDetailPendingAction.OpenOrCreateDm -> + roomDetailViewModel.handle(RoomDetailAction.OpenOrCreateDm(roomDetailPendingAction.userId)) }.exhaustive } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailPendingAction.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailPendingAction.kt index 394d46ef8d..598ab9d056 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailPendingAction.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailPendingAction.kt @@ -17,6 +17,7 @@ package im.vector.app.features.home.room.detail sealed class RoomDetailPendingAction { + data class OpenOrCreateDm(val userId: String) : RoomDetailPendingAction() data class JumpToReadReceipt(val userId: String) : RoomDetailPendingAction() data class MentionUser(val userId: String) : RoomDetailPendingAction() } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt index ee2d193473..b9e3e6b31d 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewEvents.kt @@ -38,6 +38,8 @@ sealed class RoomDetailViewEvents : VectorViewEvents { data class ShowInfoOkDialog(val message: String) : RoomDetailViewEvents() data class ShowE2EErrorMessage(val withHeldCode: WithHeldCode?) : RoomDetailViewEvents() + data class OpenRoom(val roomId: String) : RoomDetailViewEvents() + data class NavigateToEvent(val eventId: String) : RoomDetailViewEvents() data class JoinJitsiConference(val widget: Widget, val withVideo: Boolean) : RoomDetailViewEvents() diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index e721e0948d..ca387db1f6 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -273,10 +273,26 @@ class RoomDetailViewModel @AssistedInject constructor( is RoomDetailAction.RemoveWidget -> handleDeleteWidget(action.widgetId) is RoomDetailAction.EnsureNativeWidgetAllowed -> handleCheckWidgetAllowed(action) is RoomDetailAction.CancelSend -> handleCancel(action) + is RoomDetailAction.OpenOrCreateDm -> handleOpenOrCreateDm(action) is RoomDetailAction.JumpToReadReceipt -> handleJumpToReadReceipt(action) }.exhaustive } + private fun handleOpenOrCreateDm(action: RoomDetailAction.OpenOrCreateDm) { + val existingDm = session.getExistingDirectRoomWithUser(action.userId) + if (existingDm == null) { + // First create a direct room + viewModelScope.launch(Dispatchers.IO) { + val roomId = awaitCallback { + session.createDirectRoom(action.userId, it) + } + _viewEvents.post(RoomDetailViewEvents.OpenRoom(roomId)) + } + } else { + _viewEvents.post(RoomDetailViewEvents.OpenRoom(existingDm.roomId)) + } + } + private fun handleJumpToReadReceipt(action: RoomDetailAction.JumpToReadReceipt) { room.getUserReadReceipt(action.userId) ?.let { handleNavigateToEvent(RoomDetailAction.NavigateToEvent(it, true)) } diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt index a3ffd80ade..2e91091443 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileController.kt @@ -45,6 +45,7 @@ class RoomMemberProfileController @Inject constructor( fun onTapVerify() fun onShowDeviceList() fun onShowDeviceListNoCrossSigning() + fun onOpenDmClicked() fun onJumpToReadReceiptClicked() fun onMentionClicked() fun onEditPowerLevel(currentRole: Role) @@ -173,6 +174,14 @@ class RoomMemberProfileController @Inject constructor( buildProfileSection(stringProvider.getString(R.string.room_profile_section_more)) + buildProfileAction( + id = "direct", + editable = false, + title = stringProvider.getString(R.string.room_member_open_or_create_dm), + dividerColor = dividerColor, + action = { callback?.onOpenDmClicked() } + ) + if (state.hasReadReceipt) { buildProfileAction( id = "read_receipt", diff --git a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt index 2f5b2d5387..d60b5580fa 100644 --- a/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt +++ b/vector/src/main/java/im/vector/app/features/roommemberprofile/RoomMemberProfileFragment.kt @@ -278,6 +278,11 @@ class RoomMemberProfileFragment @Inject constructor( DeviceListBottomSheet.newInstance(it.userId).show(parentFragmentManager, "DEV_LIST") } + override fun onOpenDmClicked() { + roomDetailPendingActionStore.data = RoomDetailPendingAction.OpenOrCreateDm(fragmentArgs.userId) + vectorBaseActivity.finish() + } + override fun onJumpToReadReceiptClicked() { roomDetailPendingActionStore.data = RoomDetailPendingAction.JumpToReadReceipt(fragmentArgs.userId) vectorBaseActivity.finish() diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml index dd461123cc..16ebca5f20 100644 --- a/vector/src/main/res/values/strings.xml +++ b/vector/src/main/res/values/strings.xml @@ -2167,6 +2167,7 @@ Default in %1$s Custom (%1$d) in %2$s + Direct message Jump to read receipt "Element does not handle events of type '%1$s'" From d9723387ebb7be282aeb9fb0749bd39b3340fa2c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Oct 2020 11:00:01 +0100 Subject: [PATCH 2/4] We only need the roomId in many cases, so update the API --- .../org/matrix/android/sdk/api/session/room/RoomService.kt | 5 ++++- .../android/sdk/internal/session/room/DefaultRoomService.kt | 2 +- .../matrix/android/sdk/internal/session/room/RoomGetter.kt | 5 ++--- .../crypto/verification/VerificationBottomSheetViewModel.kt | 2 +- .../app/features/home/room/detail/RoomDetailViewModel.kt | 6 +++--- 5 files changed, 11 insertions(+), 9 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt index 9bd7738af4..0e42e36d86 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt @@ -129,5 +129,8 @@ interface RoomService { */ fun getChangeMembershipsLive(): LiveData> - fun getExistingDirectRoomWithUser(otherUserId: String): Room? + /** + * Return the roomId of an existing DM with the other user, or null if such room does not exist + */ + fun getExistingDirectRoomWithUser(otherUserId: String): String? } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt index 171e90703c..d49c2f120c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/DefaultRoomService.kt @@ -61,7 +61,7 @@ internal class DefaultRoomService @Inject constructor( return roomGetter.getRoom(roomId) } - override fun getExistingDirectRoomWithUser(otherUserId: String): Room? { + override fun getExistingDirectRoomWithUser(otherUserId: String): String? { return roomGetter.getDirectRoomWith(otherUserId) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomGetter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomGetter.kt index 2947518605..ebf2b402a4 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomGetter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomGetter.kt @@ -31,7 +31,7 @@ import javax.inject.Inject internal interface RoomGetter { fun getRoom(roomId: String): Room? - fun getDirectRoomWith(otherUserId: String): Room? + fun getDirectRoomWith(otherUserId: String): String? } @SessionScope @@ -46,7 +46,7 @@ internal class DefaultRoomGetter @Inject constructor( } } - override fun getDirectRoomWith(otherUserId: String): Room? { + override fun getDirectRoomWith(otherUserId: String): String? { return realmSessionProvider.withRealm { realm -> RoomSummaryEntity.where(realm) .equalTo(RoomSummaryEntityFields.IS_DIRECT, true) @@ -55,7 +55,6 @@ internal class DefaultRoomGetter @Inject constructor( .filter { dm -> dm.otherMemberIds.contains(otherUserId) } .map { it.roomId } .firstOrNull { roomId -> otherUserId in RoomMemberHelper(realm, roomId).getActiveRoomMemberIds() } - ?.let { roomId -> createRoom(realm, roomId) } } } diff --git a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt index 22711108f8..2d09974687 100644 --- a/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/crypto/verification/VerificationBottomSheetViewModel.kt @@ -232,7 +232,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor( override fun handle(action: VerificationAction) = withState { state -> val otherUserId = state.otherUserMxItem?.id ?: return@withState val roomId = state.roomId - ?: session.getExistingDirectRoomWithUser(otherUserId)?.roomId + ?: session.getExistingDirectRoomWithUser(otherUserId) when (action) { is VerificationAction.RequestVerificationByDM -> { diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index ca387db1f6..5d978ed6a4 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -279,8 +279,8 @@ class RoomDetailViewModel @AssistedInject constructor( } private fun handleOpenOrCreateDm(action: RoomDetailAction.OpenOrCreateDm) { - val existingDm = session.getExistingDirectRoomWithUser(action.userId) - if (existingDm == null) { + val existingDmRoomId = session.getExistingDirectRoomWithUser(action.userId) + if (existingDmRoomId == null) { // First create a direct room viewModelScope.launch(Dispatchers.IO) { val roomId = awaitCallback { @@ -289,7 +289,7 @@ class RoomDetailViewModel @AssistedInject constructor( _viewEvents.post(RoomDetailViewEvents.OpenRoom(roomId)) } } else { - _viewEvents.post(RoomDetailViewEvents.OpenRoom(existingDm.roomId)) + _viewEvents.post(RoomDetailViewEvents.OpenRoom(existingDmRoomId)) } } From a393c4dfae7c4b83386fa156a5d2cfa7b0d5194c Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Oct 2020 11:05:09 +0100 Subject: [PATCH 3/4] Do not open again the DM if the user is already seeing it --- .../app/features/home/room/detail/RoomDetailViewModel.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index 5d978ed6a4..9efad1081f 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -289,7 +289,9 @@ class RoomDetailViewModel @AssistedInject constructor( _viewEvents.post(RoomDetailViewEvents.OpenRoom(roomId)) } } else { - _viewEvents.post(RoomDetailViewEvents.OpenRoom(existingDmRoomId)) + if (existingDmRoomId != initialState.roomId) { + _viewEvents.post(RoomDetailViewEvents.OpenRoom(existingDmRoomId)) + } } } From 1bc726abffd3be9eb69cd9be9fcf623d772f34ba Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 29 Oct 2020 13:25:21 +0100 Subject: [PATCH 4/4] Update the algorithm to find an existing DM: simplify and make sure there is only 2 people in the room --- .../matrix/android/sdk/api/session/room/RoomService.kt | 8 ++++++++ .../android/sdk/internal/session/room/RoomGetter.kt | 6 ++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt index 0e42e36d86..b772225f51 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/RoomService.kt @@ -131,6 +131,14 @@ interface RoomService { /** * Return the roomId of an existing DM with the other user, or null if such room does not exist + * A room is a DM if: + * - it is listed in the `m.direct` account data + * - the current user has joined the room + * - the other user is invited or has joined the room + * - it has exactly 2 members + * Note: + * - the returning room can be encrypted or not + * - the power level of the users are not taken into account. Normally in a DM, the 2 members are admins of the room */ fun getExistingDirectRoomWithUser(otherUserId: String): String? } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomGetter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomGetter.kt index ebf2b402a4..eb9cd9fcba 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomGetter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/RoomGetter.kt @@ -25,7 +25,6 @@ import org.matrix.android.sdk.internal.database.model.RoomSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomSummaryEntityFields import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.session.SessionScope -import org.matrix.android.sdk.internal.session.room.membership.RoomMemberHelper import javax.inject.Inject internal interface RoomGetter { @@ -52,9 +51,8 @@ internal class DefaultRoomGetter @Inject constructor( .equalTo(RoomSummaryEntityFields.IS_DIRECT, true) .equalTo(RoomSummaryEntityFields.MEMBERSHIP_STR, Membership.JOIN.name) .findAll() - .filter { dm -> dm.otherMemberIds.contains(otherUserId) } - .map { it.roomId } - .firstOrNull { roomId -> otherUserId in RoomMemberHelper(realm, roomId).getActiveRoomMemberIds() } + .firstOrNull { dm -> dm.otherMemberIds.size == 1 && dm.otherMemberIds.first() == otherUserId } + ?.roomId } }