Adapt to new DM definition changes in the SDK (#6748)

* Set `DmRoomDefinition.TwoPeople` in `ClientBuilder`. This applies the 'direct and with at most 2 non-service members' rule to what the SDK should consider a DM.

* Map `RoomInfo.isDm` from the SDK

* Map `NotificationData.isDm` from `NotificationInfo.roomInfo.isDm`

* Remove `RoomIsDmCheck` file as its extension functions are now redundant. Move `Room.isDm` helper function to `BaseRoom`.

* Map `isDm` in `SpaceRoom` from the SDK too

* Replace `isDirect` with `isDm` where possible

* Map `RoomMember.isServiceMember` from the SDK and use it to tell apart normal members of a room from service members (i.e. `RoomMembersState.getDirectRoomMember`)
This commit is contained in:
Jorge Martin Espinosa 2026-05-11 17:22:16 +02:00 committed by GitHub
parent 5e5e0bbc6e
commit 11476c73cf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
66 changed files with 115 additions and 232 deletions

View file

@ -19,7 +19,7 @@ fun BaseRoom.toAnalyticsViewRoom(
val activeSpace = selectedSpace?.toActiveSpace() ?: ViewRoom.ActiveSpace.Home
return ViewRoom(
isDM = info().isDirect,
isDM = info().isDm,
isSpace = info().isSpace,
trigger = trigger,
activeSpace = activeSpace,

View file

@ -21,7 +21,7 @@ interface NotificationSettingsService {
val notificationSettingsChangeFlow: SharedFlow<Unit>
suspend fun getRoomNotificationSettings(roomId: RoomId, isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationSettings>
suspend fun getDefaultRoomNotificationMode(isEncrypted: Boolean, isOneToOne: Boolean): Result<RoomNotificationMode>
suspend fun setDefaultRoomNotificationMode(isEncrypted: Boolean, mode: RoomNotificationMode, isOneToOne: Boolean): Result<Unit>
suspend fun setDefaultRoomNotificationMode(isEncrypted: Boolean, mode: RoomNotificationMode, isDM: Boolean): Result<Unit>
suspend fun setRoomNotificationMode(roomId: RoomId, mode: RoomNotificationMode): Result<Unit>
suspend fun restoreDefaultRoomNotificationMode(roomId: RoomId): Result<Unit>
suspend fun muteRoom(roomId: RoomId): Result<Unit>

View file

@ -61,13 +61,12 @@ interface BaseRoom : Closeable {
*/
fun info(): RoomInfo = roomInfoFlow.value
fun predecessorRoom(): PredecessorRoom?
/**
* A one-to-one is a room with exactly 2 members.
* See [the Matrix spec](https://spec.matrix.org/latest/client-server-api/#default-underride-rules).
* Returns whether the [BaseRoom] is a DM, with an updated state from the latest [RoomInfo].
*/
val isOneToOne: Boolean get() = info().activeMembersCount == 2L
fun isDm() = roomInfoFlow.value.isDm
fun predecessorRoom(): PredecessorRoom?
/**
* Try to load the room members and update the membersFlow.

View file

@ -29,6 +29,7 @@ data class RoomInfo(
val avatarUrl: String?,
val isPublic: Boolean?,
val isDirect: Boolean,
val isDm: Boolean,
val isEncrypted: Boolean?,
val joinRule: JoinRule?,
val isSpace: Boolean,

View file

@ -1,32 +0,0 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.api.room
import kotlinx.coroutines.flow.first
/**
* Returns whether the room with the provided info is a DM.
* A DM is a room with at most 2 active members (one of them may have left).
*
* @param isDirect true if the room is direct
* @param activeMembersCount the number of active members in the room (joined or invited)
*/
fun isDm(isDirect: Boolean, activeMembersCount: Int): Boolean {
return isDirect && activeMembersCount <= 2
}
/**
* Returns whether the [BaseRoom] is a DM, with an updated state from the latest [RoomInfo].
*/
suspend fun BaseRoom.isDm() = roomInfoFlow.first().isDm
/**
* Returns whether the [RoomInfo] is from a DM.
*/
val RoomInfo.isDm get() = isDm(isDirect, activeMembersCount.toInt())

View file

@ -22,6 +22,7 @@ data class RoomMember(
val isIgnored: Boolean,
val role: Role,
val membershipChangeReason: String?,
val isServiceMember: Boolean,
) {
/**
* Role of the RoomMember, based on its [powerLevel].

View file

@ -40,5 +40,5 @@ fun RoomMembersState.activeRoomMembers(): List<RoomMember> {
fun RoomMembersState.getDirectRoomMember(roomInfo: RoomInfo, sessionId: SessionId): RoomMember? {
return roomMembers()
?.takeIf { roomInfo.isDm }
?.find { it.userId != sessionId && it.membership.isActive() }
?.find { !it.isServiceMember && it.userId != sessionId && it.membership.isActive() }
}

View file

@ -12,7 +12,6 @@ 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.isDm
import io.element.android.libraries.matrix.api.room.toMatrixUser
import io.element.android.libraries.matrix.api.user.MatrixUser
import kotlinx.coroutines.flow.Flow

View file

@ -21,5 +21,5 @@ data class RoomSummary(
is LatestEventValue.Remote -> latestEvent.timestamp
is LatestEventValue.RoomInvite -> latestEvent.timestamp
}
val isOneToOne get() = info.activeMembersCount == 2L
val isDm = info.isDm
}

View file

@ -38,6 +38,7 @@ data class SpaceRoom(
*/
val via: ImmutableList<String>,
val isDirect: Boolean?,
val isDm: Boolean?,
) {
val isSpace = roomType == RoomType.Space

View file

@ -1,54 +0,0 @@
/*
* Copyright (c) 2025 Element Creations Ltd.
* Copyright 2024, 2025 New Vector Ltd.
*
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
* Please see LICENSE files in the repository root for full details.
*/
package io.element.android.libraries.matrix.api.room
import com.google.common.truth.Truth.assertThat
import org.junit.Test
class RoomIsDmCheckTest {
@Test
fun `a room is a DM only if it has at most 2 members and is direct`() {
val isDirect = true
val activeMembersCount = 2
val isDm = isDm(isDirect, activeMembersCount)
assertThat(isDm).isTrue()
}
@Test
fun `a room can be a DM if it has also a single active user`() {
val isDirect = true
val activeMembersCount = 1
val isDm = isDm(isDirect, activeMembersCount)
assertThat(isDm).isTrue()
}
@Test
fun `a room is not a DM if it's not direct`() {
val isDirect = false
val activeMembersCount = 2
val isDm = isDm(isDirect, activeMembersCount)
assertThat(isDm).isFalse()
}
@Test
fun `a room is not a DM if it has more than 2 active users`() {
val isDirect = true
val activeMembersCount = 3
val isDm = isDm(isDirect, activeMembersCount)
assertThat(isDm).isFalse()
}
}