Browse Source

Unify the way we decide whether a room is a DM or a group room (#3100)

* Add centralised 'room is DM' check

Also add extension functions for `MatrixRoom` and `MatrixRoomInfo`.

* Use the centralised method and extension functions through the app, including:

- Room list.
- Room details screen.
- Invites.
- Notifications.

Replace most `isDirect` usages with `isDm`.

* Update screenshots

---------

Co-authored-by: ElementBot <benoitm+elementbot@element.io>
pull/3178/head
Jorge Martin Espinosa 2 months ago committed by GitHub
parent
commit
0be7058416
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 4
      features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteStateProvider.kt
  2. 2
      features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/InviteData.kt
  3. 4
      features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInviteView.kt
  4. 4
      features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt
  5. 9
      features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt
  6. 2
      features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt
  7. 11
      features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt
  8. 6
      features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt
  9. 1
      features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/DefaultLeaveRoomPresenter.kt
  10. 2
      features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/DefaultLeaveRoomPresenterTest.kt
  11. 3
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt
  12. 1
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt
  13. 1
      features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt
  14. 4
      features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenterTest.kt
  15. 1
      features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt
  16. 4
      features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/DefaultRoomMembersModerationPresenter.kt
  17. 6
      features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/DefaultRoomMembersModerationPresenterTest.kt
  18. 2
      features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt
  19. 8
      features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt
  20. 2
      features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt
  21. 2
      features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt
  22. 1
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt
  23. 3
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt
  24. 38
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomIsDmCheck.kt
  25. 1
      libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/recent/RecentDirectRoom.kt
  26. 62
      libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/room/RoomIsDmCheckTest.kt
  27. 8
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt
  28. 4
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilter.kt
  29. 3
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt
  30. 1
      libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt
  31. 10
      libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilterTest.kt
  32. 1
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/NotificationData.kt
  33. 1
      libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt
  34. 2
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/CallNotificationEventResolver.kt
  35. 10
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt
  36. 3
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandler.kt
  37. 12
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactory.kt
  38. 2
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomEventGroupInfo.kt
  39. 5
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt
  40. 2
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt
  41. 2
      libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt
  42. 5
      libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt
  43. 4
      libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultRoomGroupMessageCreatorTest.kt
  44. 2
      libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt
  45. 1
      libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt
  46. 3
      tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_10_en.png
  47. 3
      tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_10_en.png

4
features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/AcceptDeclineInviteStateProvider.kt

@ -27,13 +27,13 @@ open class AcceptDeclineInviteStateProvider : PreviewParameterProvider<AcceptDec
anAcceptDeclineInviteState(), anAcceptDeclineInviteState(),
anAcceptDeclineInviteState( anAcceptDeclineInviteState(
invite = Optional.of( invite = Optional.of(
InviteData(RoomId("!room:matrix.org"), isDirect = true, roomName = "Alice"), InviteData(RoomId("!room:matrix.org"), isDm = true, roomName = "Alice"),
), ),
declineAction = AsyncAction.Confirming, declineAction = AsyncAction.Confirming,
), ),
anAcceptDeclineInviteState( anAcceptDeclineInviteState(
invite = Optional.of( invite = Optional.of(
InviteData(RoomId("!room:matrix.org"), isDirect = false, roomName = "Some room"), InviteData(RoomId("!room:matrix.org"), isDm = false, roomName = "Some room"),
), ),
declineAction = AsyncAction.Confirming, declineAction = AsyncAction.Confirming,
), ),

2
features/invite/api/src/main/kotlin/io/element/android/features/invite/api/response/InviteData.kt

@ -21,5 +21,5 @@ import io.element.android.libraries.matrix.api.core.RoomId
data class InviteData( data class InviteData(
val roomId: RoomId, val roomId: RoomId,
val roomName: String, val roomName: String,
val isDirect: Boolean, val isDm: Boolean,
) )

4
features/invite/impl/src/main/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInviteView.kt

@ -79,13 +79,13 @@ private fun DeclineConfirmationDialog(
onDismissClick: () -> Unit, onDismissClick: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val contentResource = if (invite.isDirect) { val contentResource = if (invite.isDm) {
R.string.screen_invites_decline_direct_chat_message R.string.screen_invites_decline_direct_chat_message
} else { } else {
R.string.screen_invites_decline_chat_message R.string.screen_invites_decline_chat_message
} }
val titleResource = if (invite.isDirect) { val titleResource = if (invite.isDm) {
R.string.screen_invites_decline_direct_chat_title R.string.screen_invites_decline_direct_chat_title
} else { } else {
R.string.screen_invites_decline_chat_title R.string.screen_invites_decline_chat_title

4
features/invite/impl/src/test/kotlin/io/element/android/features/invite/impl/response/AcceptDeclineInvitePresenterTest.kt

@ -260,12 +260,12 @@ class AcceptDeclineInvitePresenterTest {
private fun anInviteData( private fun anInviteData(
roomId: RoomId = A_ROOM_ID, roomId: RoomId = A_ROOM_ID,
name: String = A_ROOM_NAME, name: String = A_ROOM_NAME,
isDirect: Boolean = false isDm: Boolean = false
): InviteData { ): InviteData {
return InviteData( return InviteData(
roomId = roomId, roomId = roomId,
roomName = name, roomName = name,
isDirect = isDirect isDm = isDm
) )
} }

9
features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenter.kt

@ -45,6 +45,7 @@ import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
import io.element.android.libraries.matrix.api.room.CurrentUserMembership import io.element.android.libraries.matrix.api.room.CurrentUserMembership
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
import io.element.android.libraries.matrix.api.room.RoomType import io.element.android.libraries.matrix.api.room.RoomType
import io.element.android.libraries.matrix.api.room.isDm
import io.element.android.libraries.matrix.api.room.join.JoinRoom import io.element.android.libraries.matrix.api.room.join.JoinRoom
import io.element.android.libraries.matrix.api.room.preview.RoomPreview import io.element.android.libraries.matrix.api.room.preview.RoomPreview
import io.element.android.libraries.matrix.ui.model.toInviteSender import io.element.android.libraries.matrix.ui.model.toInviteSender
@ -173,7 +174,7 @@ private fun RoomPreview.toContentState(): ContentState {
topic = topic, topic = topic,
alias = canonicalAlias, alias = canonicalAlias,
numberOfMembers = numberOfJoinedMembers, numberOfMembers = numberOfJoinedMembers,
isDirect = false, isDm = false,
roomType = roomType, roomType = roomType,
roomAvatarUrl = avatarUrl, roomAvatarUrl = avatarUrl,
joinAuthorisationStatus = when { joinAuthorisationStatus = when {
@ -194,7 +195,7 @@ internal fun RoomDescription.toContentState(): ContentState {
topic = topic, topic = topic,
alias = alias, alias = alias,
numberOfMembers = numberOfMembers, numberOfMembers = numberOfMembers,
isDirect = false, isDm = false,
roomType = RoomType.Room, roomType = RoomType.Room,
roomAvatarUrl = avatarUrl, roomAvatarUrl = avatarUrl,
joinAuthorisationStatus = when (joinRule) { joinAuthorisationStatus = when (joinRule) {
@ -213,7 +214,7 @@ internal fun MatrixRoomInfo.toContentState(): ContentState {
topic = topic, topic = topic,
alias = canonicalAlias, alias = canonicalAlias,
numberOfMembers = activeMembersCount, numberOfMembers = activeMembersCount,
isDirect = isDirect, isDm = isDm,
roomType = if (isSpace) RoomType.Space else RoomType.Room, roomType = if (isSpace) RoomType.Space else RoomType.Room,
roomAvatarUrl = avatarUrl, roomAvatarUrl = avatarUrl,
joinAuthorisationStatus = when { joinAuthorisationStatus = when {
@ -233,7 +234,7 @@ internal fun ContentState.toInviteData(): InviteData? {
roomId = roomId, roomId = roomId,
// Note: name should not be null at this point, but use Id just in case... // Note: name should not be null at this point, but use Id just in case...
roomName = name ?: roomId.value, roomName = name ?: roomId.value,
isDirect = isDirect isDm = isDm
) )
else -> null else -> null
} }

2
features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomState.kt

@ -53,7 +53,7 @@ sealed interface ContentState {
val topic: String?, val topic: String?,
val alias: RoomAlias?, val alias: RoomAlias?,
val numberOfMembers: Long?, val numberOfMembers: Long?,
val isDirect: Boolean, val isDm: Boolean,
val roomType: RoomType, val roomType: RoomType,
val roomAvatarUrl: String?, val roomAvatarUrl: String?,
val joinAuthorisationStatus: JoinAuthorisationStatus, val joinAuthorisationStatus: JoinAuthorisationStatus,

11
features/joinroom/impl/src/main/kotlin/io/element/android/features/joinroom/impl/JoinRoomStateProvider.kt

@ -28,6 +28,7 @@ import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
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.core.toRoomIdOrAlias import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
import io.element.android.libraries.matrix.api.room.RoomType import io.element.android.libraries.matrix.api.room.RoomType
import io.element.android.libraries.matrix.api.room.isDm
import io.element.android.libraries.matrix.ui.model.InviteSender import io.element.android.libraries.matrix.ui.model.InviteSender
open class JoinRoomStateProvider : PreviewParameterProvider<JoinRoomState> { open class JoinRoomStateProvider : PreviewParameterProvider<JoinRoomState> {
@ -84,6 +85,12 @@ open class JoinRoomStateProvider : PreviewParameterProvider<JoinRoomState> {
roomType = RoomType.Space, roomType = RoomType.Space,
) )
), ),
aJoinRoomState(
contentState = aLoadedContentState(
name = "A DM",
isDm = true,
)
),
) )
} }
@ -106,7 +113,7 @@ fun aLoadedContentState(
alias: RoomAlias? = RoomAlias("#exa:matrix.org"), alias: RoomAlias? = RoomAlias("#exa:matrix.org"),
topic: String? = "Element X is a secure, private and decentralized messenger.", topic: String? = "Element X is a secure, private and decentralized messenger.",
numberOfMembers: Long? = null, numberOfMembers: Long? = null,
isDirect: Boolean = false, isDm: Boolean = false,
roomType: RoomType = RoomType.Room, roomType: RoomType = RoomType.Room,
roomAvatarUrl: String? = null, roomAvatarUrl: String? = null,
joinAuthorisationStatus: JoinAuthorisationStatus = JoinAuthorisationStatus.Unknown joinAuthorisationStatus: JoinAuthorisationStatus = JoinAuthorisationStatus.Unknown
@ -116,7 +123,7 @@ fun aLoadedContentState(
alias = alias, alias = alias,
topic = topic, topic = topic,
numberOfMembers = numberOfMembers, numberOfMembers = numberOfMembers,
isDirect = isDirect, isDm = isDm,
roomType = roomType, roomType = roomType,
roomAvatarUrl = roomAvatarUrl, roomAvatarUrl = roomAvatarUrl,
joinAuthorisationStatus = joinAuthorisationStatus joinAuthorisationStatus = joinAuthorisationStatus

6
features/joinroom/impl/src/test/kotlin/io/element/android/features/joinroom/impl/JoinRoomPresenterTest.kt

@ -93,7 +93,7 @@ class JoinRoomPresenterTest {
assertThat(contentState.topic).isEqualTo(roomInfo.topic) assertThat(contentState.topic).isEqualTo(roomInfo.topic)
assertThat(contentState.alias).isEqualTo(roomInfo.canonicalAlias) assertThat(contentState.alias).isEqualTo(roomInfo.canonicalAlias)
assertThat(contentState.numberOfMembers).isEqualTo(roomInfo.activeMembersCount) assertThat(contentState.numberOfMembers).isEqualTo(roomInfo.activeMembersCount)
assertThat(contentState.isDirect).isEqualTo(roomInfo.isDirect) assertThat(contentState.isDm).isEqualTo(roomInfo.isDirect)
assertThat(contentState.roomAvatarUrl).isEqualTo(roomInfo.avatarUrl) assertThat(contentState.roomAvatarUrl).isEqualTo(roomInfo.avatarUrl)
} }
} }
@ -283,7 +283,7 @@ class JoinRoomPresenterTest {
assertThat(contentState.topic).isEqualTo(roomDescription.topic) assertThat(contentState.topic).isEqualTo(roomDescription.topic)
assertThat(contentState.alias).isEqualTo(roomDescription.alias) assertThat(contentState.alias).isEqualTo(roomDescription.alias)
assertThat(contentState.numberOfMembers).isEqualTo(roomDescription.numberOfMembers) assertThat(contentState.numberOfMembers).isEqualTo(roomDescription.numberOfMembers)
assertThat(contentState.isDirect).isFalse() assertThat(contentState.isDm).isFalse()
assertThat(contentState.roomAvatarUrl).isEqualTo(roomDescription.avatarUrl) assertThat(contentState.roomAvatarUrl).isEqualTo(roomDescription.avatarUrl)
} }
} }
@ -398,7 +398,7 @@ class JoinRoomPresenterTest {
topic = "Room topic", topic = "Room topic",
alias = RoomAlias("#alias:matrix.org"), alias = RoomAlias("#alias:matrix.org"),
numberOfMembers = 2, numberOfMembers = 2,
isDirect = false, isDm = false,
roomType = RoomType.Room, roomType = RoomType.Room,
roomAvatarUrl = "avatarUrl", roomAvatarUrl = "avatarUrl",
joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin joinAuthorisationStatus = JoinAuthorisationStatus.CanJoin

1
features/leaveroom/impl/src/main/kotlin/io/element/android/features/leaveroom/impl/DefaultLeaveRoomPresenter.kt

@ -34,6 +34,7 @@ import io.element.android.libraries.di.SessionScope
import io.element.android.libraries.matrix.api.MatrixClient 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.room.RoomMembershipObserver import io.element.android.libraries.matrix.api.room.RoomMembershipObserver
import io.element.android.libraries.matrix.api.room.isDm
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject

2
features/leaveroom/impl/src/test/kotlin/io/element/android/features/leaveroom/impl/DefaultLeaveRoomPresenterTest.kt

@ -119,7 +119,7 @@ class DefaultLeaveRoomPresenterTest {
client = FakeMatrixClient().apply { client = FakeMatrixClient().apply {
givenGetRoomResult( givenGetRoomResult(
roomId = A_ROOM_ID, roomId = A_ROOM_ID,
result = FakeMatrixRoom(activeMemberCount = 2, isDirect = true, isOneToOne = true), result = FakeMatrixRoom(activeMemberCount = 2, isDirect = true),
) )
} }
) )

3
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt

@ -72,6 +72,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
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.MessageEventType import io.element.android.libraries.matrix.api.room.MessageEventType
import io.element.android.libraries.matrix.api.room.isDm
import io.element.android.libraries.matrix.ui.messages.reply.map import io.element.android.libraries.matrix.ui.messages.reply.map
import io.element.android.libraries.matrix.ui.model.getAvatarData import io.element.android.libraries.matrix.ui.model.getAvatarData
import io.element.android.libraries.matrix.ui.room.canCall import io.element.android.libraries.matrix.ui.room.canCall
@ -162,7 +163,7 @@ class MessagesPresenter @AssistedInject constructor(
var showReinvitePrompt by remember { mutableStateOf(false) } var showReinvitePrompt by remember { mutableStateOf(false) }
LaunchedEffect(hasDismissedInviteDialog, composerState.textEditorState.hasFocus(), syncUpdateFlow.value) { LaunchedEffect(hasDismissedInviteDialog, composerState.textEditorState.hasFocus(), syncUpdateFlow.value) {
withContext(dispatchers.io) { withContext(dispatchers.io) {
showReinvitePrompt = !hasDismissedInviteDialog && composerState.textEditorState.hasFocus() && room.isDirect && room.activeMemberCount == 1L showReinvitePrompt = !hasDismissedInviteDialog && composerState.textEditorState.hasFocus() && room.isDm && room.activeMemberCount == 1L
} }
} }
val networkConnectionStatus by networkMonitor.connectivity.collectAsState() val networkConnectionStatus by networkMonitor.connectivity.collectAsState()

1
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt

@ -57,6 +57,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.Mention import io.element.android.libraries.matrix.api.room.Mention
import io.element.android.libraries.matrix.api.room.draft.ComposerDraft import io.element.android.libraries.matrix.api.room.draft.ComposerDraft
import io.element.android.libraries.matrix.api.room.draft.ComposerDraftType import io.element.android.libraries.matrix.api.room.draft.ComposerDraftType
import io.element.android.libraries.matrix.api.room.isDm
import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails
import io.element.android.libraries.matrix.ui.messages.reply.map import io.element.android.libraries.matrix.ui.messages.reply.map
import io.element.android.libraries.mediapickers.api.PickerProvider import io.element.android.libraries.mediapickers.api.PickerProvider

1
features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenter.kt

@ -42,6 +42,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers
import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.EventId
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.MessageEventType import io.element.android.libraries.matrix.api.room.MessageEventType
import io.element.android.libraries.matrix.api.room.isDm
import io.element.android.libraries.matrix.api.room.roomMembers import io.element.android.libraries.matrix.api.room.roomMembers
import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.timeline.ReceiptType
import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin import io.element.android.libraries.matrix.api.timeline.item.event.TimelineItemEventOrigin

4
features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/textcomposer/MessageComposerPresenterTest.kt

@ -839,7 +839,6 @@ class MessageComposerPresenterTest {
val david = aRoomMember(userId = A_USER_ID_4, displayName = "Dave", membership = RoomMembershipState.JOIN) val david = aRoomMember(userId = A_USER_ID_4, displayName = "Dave", membership = RoomMembershipState.JOIN)
val room = FakeMatrixRoom( val room = FakeMatrixRoom(
isDirect = false, isDirect = false,
isOneToOne = false,
).apply { ).apply {
givenRoomMembersState( givenRoomMembersState(
MatrixRoomMembersState.Ready( MatrixRoomMembersState.Ready(
@ -904,7 +903,8 @@ class MessageComposerPresenterTest {
val david = aRoomMember(userId = A_USER_ID_4, displayName = "Dave", membership = RoomMembershipState.JOIN) val david = aRoomMember(userId = A_USER_ID_4, displayName = "Dave", membership = RoomMembershipState.JOIN)
val room = FakeMatrixRoom( val room = FakeMatrixRoom(
isDirect = true, isDirect = true,
isOneToOne = true, activeMemberCount = 2,
isEncrypted = true,
).apply { ).apply {
givenRoomMembersState( givenRoomMembersState(
MatrixRoomMembersState.Ready( MatrixRoomMembersState.Ready(

1
features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/RoomDetailsPresenter.kt

@ -41,6 +41,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.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMember
import io.element.android.libraries.matrix.api.room.StateEventType import io.element.android.libraries.matrix.api.room.StateEventType
import io.element.android.libraries.matrix.api.room.isDm
import io.element.android.libraries.matrix.api.room.powerlevels.canInvite import io.element.android.libraries.matrix.api.room.powerlevels.canInvite
import io.element.android.libraries.matrix.api.room.powerlevels.canSendState import io.element.android.libraries.matrix.api.room.powerlevels.canSendState
import io.element.android.libraries.matrix.api.room.roomNotificationSettings import io.element.android.libraries.matrix.api.room.roomNotificationSettings

4
features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/moderation/DefaultRoomMembersModerationPresenter.kt

@ -35,6 +35,7 @@ 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.RoomMember 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.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.canBan
import io.element.android.libraries.matrix.api.room.powerlevels.canKick import io.element.android.libraries.matrix.api.room.powerlevels.canKick
import io.element.android.services.analytics.api.AnalyticsService import io.element.android.services.analytics.api.AnalyticsService
@ -58,8 +59,7 @@ class DefaultRoomMembersModerationPresenter @Inject constructor(
private suspend fun canKick() = room.canKick().getOrDefault(false) private suspend fun canKick() = room.canKick().getOrDefault(false)
override suspend fun canDisplayModerationActions(): Boolean { override suspend fun canDisplayModerationActions(): Boolean {
val isDm = room.isDm && room.isEncrypted return !room.isDm && (canBan() || canKick())
return !isDm && (canBan() || canKick())
} }
@Composable @Composable

6
features/roomdetails/impl/src/test/kotlin/io/element/android/features/roomdetails/members/moderation/DefaultRoomMembersModerationPresenterTest.kt

@ -45,7 +45,7 @@ import org.junit.Test
class DefaultRoomMembersModerationPresenterTest { class DefaultRoomMembersModerationPresenterTest {
@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, isOneToOne = true).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 = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
@ -54,7 +54,7 @@ class DefaultRoomMembersModerationPresenterTest {
@Test @Test
fun `canDisplayModerationActions - when user can kick other users, FF is enabled and room is not a DM returns true`() = runTest { fun `canDisplayModerationActions - when user can kick other users, FF is enabled and room is not a DM returns true`() = runTest {
val room = FakeMatrixRoom(isDirect = false, isOneToOne = false).apply { val room = FakeMatrixRoom(isDirect = false, activeMemberCount = 10).apply {
givenCanKickResult(Result.success(true)) givenCanKickResult(Result.success(true))
} }
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room) val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)
@ -63,7 +63,7 @@ class DefaultRoomMembersModerationPresenterTest {
@Test @Test
fun `canDisplayModerationActions - when user can ban other users, FF is enabled and room is not a DM returns true`() = runTest { fun `canDisplayModerationActions - when user can ban other users, FF is enabled and room is not a DM returns true`() = runTest {
val room = FakeMatrixRoom(isDirect = false, isOneToOne = false).apply { val room = FakeMatrixRoom(isDirect = false, activeMemberCount = 10).apply {
givenCanBanResult(Result.success(true)) givenCanBanResult(Result.success(true))
} }
val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room) val presenter = createDefaultRoomMembersModerationPresenter(matrixRoom = room)

2
features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/RoomListPresenter.kt

@ -290,5 +290,5 @@ internal fun RoomListRoomSummary.toInviteData() = InviteData(
roomId = roomId, roomId = roomId,
// Note: `name` should not be null at this point, but just in case, fallback to the roomId // Note: `name` should not be null at this point, but just in case, fallback to the roomId
roomName = name ?: roomId.value, roomName = name ?: roomId.value,
isDirect = isDirect, isDm = isDm,
) )

8
features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/components/RoomSummaryRow.kt

@ -95,8 +95,8 @@ internal fun RoomSummaryRow(
modifier = modifier modifier = modifier
) { ) {
InviteNameAndIndicatorRow(name = room.name) InviteNameAndIndicatorRow(name = room.name)
InviteSubtitle(isDirect = room.isDirect, inviteSender = room.inviteSender, canonicalAlias = room.canonicalAlias) InviteSubtitle(isDm = room.isDm, inviteSender = room.inviteSender, canonicalAlias = room.canonicalAlias)
if (!room.isDirect && room.inviteSender != null) { if (!room.isDm && room.inviteSender != null) {
Spacer(modifier = Modifier.height(4.dp)) Spacer(modifier = Modifier.height(4.dp))
InviteSenderView( InviteSenderView(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
@ -206,12 +206,12 @@ private fun NameAndTimestampRow(
@Composable @Composable
private fun InviteSubtitle( private fun InviteSubtitle(
isDirect: Boolean, isDm: Boolean,
inviteSender: InviteSender?, inviteSender: InviteSender?,
canonicalAlias: RoomAlias?, canonicalAlias: RoomAlias?,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val subtitle = if (isDirect) { val subtitle = if (isDm) {
inviteSender?.userId?.value inviteSender?.userId?.value
} else { } else {
canonicalAlias?.value canonicalAlias?.value

2
features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/datasource/RoomListRoomSummaryFactory.kt

@ -45,7 +45,7 @@ class RoomListRoomSummaryFactory @Inject constructor(
isMarkedUnread = details.isMarkedUnread, isMarkedUnread = details.isMarkedUnread,
timestamp = lastMessageTimestampFormatter.format(details.lastMessageTimestamp), timestamp = lastMessageTimestampFormatter.format(details.lastMessageTimestamp),
lastMessage = details.lastMessage?.let { message -> lastMessage = details.lastMessage?.let { message ->
roomLastMessageFormatter.format(message.event, details.isDirect) roomLastMessageFormatter.format(message.event, details.isDm)
}.orEmpty(), }.orEmpty(),
avatarData = avatarData, avatarData = avatarData,
userDefinedNotificationMode = details.userDefinedNotificationMode, userDefinedNotificationMode = details.userDefinedNotificationMode,

2
features/roomlist/impl/src/main/kotlin/io/element/android/features/roomlist/impl/model/RoomListRoomSummaryProvider.kt

@ -101,7 +101,7 @@ open class RoomListRoomSummaryProvider : PreviewParameterProvider<RoomListRoomSu
userId = UserId("@bob:matrix.org"), userId = UserId("@bob:matrix.org"),
displayName = "Bob", displayName = "Bob",
), ),
isDirect = true, isDm = true,
), ),
aRoomListRoomSummary( aRoomListRoomSummary(
name = null, name = null,

1
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/notification/NotificationData.kt

@ -33,6 +33,7 @@ data class NotificationData(
val roomAvatarUrl: String?, val roomAvatarUrl: String?,
val roomDisplayName: String?, val roomDisplayName: String?,
val isDirect: Boolean, val isDirect: Boolean,
val isDm: Boolean,
val isEncrypted: Boolean, val isEncrypted: Boolean,
val isNoisy: Boolean, val isNoisy: Boolean,
val timestamp: Long, val timestamp: Long,

3
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt

@ -57,9 +57,6 @@ interface MatrixRoom : Closeable {
val activeMemberCount: Long val activeMemberCount: Long
val joinedMemberCount: Long val joinedMemberCount: Long
/** Whether the room is a direct message. */
val isDm: Boolean get() = isDirect && isOneToOne
val roomInfoFlow: Flow<MatrixRoomInfo> val roomInfoFlow: Flow<MatrixRoomInfo>
val roomTypingMembersFlow: Flow<List<UserId>> val roomTypingMembersFlow: Flow<List<UserId>>

38
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomIsDmCheck.kt

@ -0,0 +1,38 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.element.android.libraries.matrix.api.room
/**
* 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 [MatrixRoom] is a DM.
*/
val MatrixRoom.isDm get() = isDm(isDirect, activeMemberCount.toInt())
/**
* Returns whether the [MatrixRoomInfo] is from a DM.
*/
val MatrixRoomInfo.isDm get() = isDm(isDirect, activeMembersCount.toInt())

1
libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/recent/RecentDirectRoom.kt

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

62
libraries/matrix/api/src/test/kotlin/io/element/android/libraries/matrix/api/room/RoomIsDmCheckTest.kt

@ -0,0 +1,62 @@
/*
* Copyright (c) 2024 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
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()
}
}

8
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/notification/NotificationMapper.kt

@ -23,6 +23,7 @@ import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.notification.NotificationContent import io.element.android.libraries.matrix.api.notification.NotificationContent
import io.element.android.libraries.matrix.api.notification.NotificationData import io.element.android.libraries.matrix.api.notification.NotificationData
import io.element.android.libraries.matrix.api.room.RoomMembershipState import io.element.android.libraries.matrix.api.room.RoomMembershipState
import io.element.android.libraries.matrix.api.room.isDm
import io.element.android.services.toolbox.api.systemclock.SystemClock import io.element.android.services.toolbox.api.systemclock.SystemClock
import org.matrix.rustcomponents.sdk.NotificationEvent import org.matrix.rustcomponents.sdk.NotificationEvent
import org.matrix.rustcomponents.sdk.NotificationItem import org.matrix.rustcomponents.sdk.NotificationItem
@ -40,15 +41,20 @@ class NotificationMapper(
notificationItem: NotificationItem notificationItem: NotificationItem
): NotificationData { ): NotificationData {
return notificationItem.use { item -> return notificationItem.use { item ->
val isDm = isDm(
isDirect = item.roomInfo.isDirect,
activeMembersCount = item.roomInfo.joinedMembersCount.toInt(),
)
NotificationData( NotificationData(
eventId = eventId, eventId = eventId,
roomId = roomId, roomId = roomId,
senderAvatarUrl = item.senderInfo.avatarUrl, senderAvatarUrl = item.senderInfo.avatarUrl,
senderDisplayName = item.senderInfo.displayName, senderDisplayName = item.senderInfo.displayName,
senderIsNameAmbiguous = item.senderInfo.isNameAmbiguous, senderIsNameAmbiguous = item.senderInfo.isNameAmbiguous,
roomAvatarUrl = item.roomInfo.avatarUrl ?: item.senderInfo.avatarUrl.takeIf { item.roomInfo.isDirect }, roomAvatarUrl = item.roomInfo.avatarUrl ?: item.senderInfo.avatarUrl.takeIf { isDm },
roomDisplayName = item.roomInfo.displayName, roomDisplayName = item.roomInfo.displayName,
isDirect = item.roomInfo.isDirect, isDirect = item.roomInfo.isDirect,
isDm = isDm,
isEncrypted = item.roomInfo.isEncrypted.orFalse(), isEncrypted = item.roomInfo.isEncrypted.orFalse(),
isNoisy = item.isNoisy.orFalse(), isNoisy = item.isNoisy.orFalse(),
timestamp = item.timestamp() ?: clock.epochMillis(), timestamp = item.timestamp() ?: clock.epochMillis(),

4
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilter.kt

@ -26,10 +26,10 @@ val RoomListFilter.predicate
is RoomListFilter.Any -> { _: RoomSummary -> true } is RoomListFilter.Any -> { _: RoomSummary -> true }
RoomListFilter.None -> { _: RoomSummary -> false } RoomListFilter.None -> { _: RoomSummary -> false }
RoomListFilter.Category.Group -> { roomSummary: RoomSummary -> RoomListFilter.Category.Group -> { roomSummary: RoomSummary ->
!roomSummary.isDirect && !roomSummary.isInvited() !roomSummary.isDm && !roomSummary.isInvited()
} }
RoomListFilter.Category.People -> { roomSummary: RoomSummary -> RoomListFilter.Category.People -> { roomSummary: RoomSummary ->
roomSummary.isDirect && !roomSummary.isInvited() roomSummary.isDm && !roomSummary.isInvited()
} }
RoomListFilter.Favorite -> { roomSummary: RoomSummary -> RoomListFilter.Favorite -> { roomSummary: RoomSummary ->
roomSummary.isFavorite && !roomSummary.isInvited() roomSummary.isFavorite && !roomSummary.isInvited()

3
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomSummaryDetailsFactory.kt

@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.impl.roomlist
import io.element.android.libraries.matrix.api.core.RoomAlias import io.element.android.libraries.matrix.api.core.RoomAlias
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.room.isDm
import io.element.android.libraries.matrix.api.roomlist.RoomSummary import io.element.android.libraries.matrix.api.roomlist.RoomSummary
import io.element.android.libraries.matrix.impl.notificationsettings.RoomNotificationSettingsMapper import io.element.android.libraries.matrix.impl.notificationsettings.RoomNotificationSettingsMapper
import io.element.android.libraries.matrix.impl.room.elementHeroes import io.element.android.libraries.matrix.impl.room.elementHeroes
@ -47,7 +48,7 @@ class RoomSummaryDetailsFactory(private val roomMessageFactory: RoomMessageFacto
inviter = roomInfo.inviter?.let(RoomMemberMapper::map), inviter = roomInfo.inviter?.let(RoomMemberMapper::map),
userDefinedNotificationMode = roomInfo.userDefinedNotificationMode?.let(RoomNotificationSettingsMapper::mapMode), userDefinedNotificationMode = roomInfo.userDefinedNotificationMode?.let(RoomNotificationSettingsMapper::mapMode),
hasRoomCall = roomInfo.hasRoomCall, hasRoomCall = roomInfo.hasRoomCall,
isDm = roomInfo.isDirect && roomInfo.activeMembersCount.toLong() == 2L, isDm = isDm(isDirect = roomInfo.isDirect, activeMembersCount = roomInfo.activeMembersCount.toInt()),
isFavorite = roomInfo.isFavourite, isFavorite = roomInfo.isFavourite,
currentUserMembership = roomInfo.membership.map(), currentUserMembership = roomInfo.membership.map(),
heroes = roomInfo.elementHeroes(), heroes = roomInfo.elementHeroes(),

1
libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt

@ -28,6 +28,7 @@ import io.element.android.libraries.matrix.api.media.VideoInfo
import io.element.android.libraries.matrix.api.poll.PollKind import io.element.android.libraries.matrix.api.poll.PollKind
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.Mention import io.element.android.libraries.matrix.api.room.Mention
import io.element.android.libraries.matrix.api.room.isDm
import io.element.android.libraries.matrix.api.room.location.AssetType import io.element.android.libraries.matrix.api.room.location.AssetType
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.timeline.ReceiptType

10
libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/roomlist/RoomListFilterTest.kt

@ -25,10 +25,10 @@ import org.junit.Test
class RoomListFilterTest { class RoomListFilterTest {
private val regularRoom = aRoomSummary( private val regularRoom = aRoomSummary(
isDirect = false isDm = false
) )
private val directRoom = aRoomSummary( private val dmRoom = aRoomSummary(
isDirect = true isDm = true
) )
private val favoriteRoom = aRoomSummary( private val favoriteRoom = aRoomSummary(
isFavorite = true isFavorite = true
@ -48,7 +48,7 @@ class RoomListFilterTest {
private val roomSummaries = listOf( private val roomSummaries = listOf(
regularRoom, regularRoom,
directRoom, dmRoom,
favoriteRoom, favoriteRoom,
markedAsUnreadRoom, markedAsUnreadRoom,
unreadNotificationRoom, unreadNotificationRoom,
@ -71,7 +71,7 @@ class RoomListFilterTest {
@Test @Test
fun `Room list filter people`() = runTest { fun `Room list filter people`() = runTest {
val filter = RoomListFilter.Category.People val filter = RoomListFilter.Category.People
assertThat(roomSummaries.filter(filter)).containsExactly(directRoom) assertThat(roomSummaries.filter(filter)).containsExactly(dmRoom)
} }
@Test @Test

1
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/notification/NotificationData.kt

@ -34,6 +34,7 @@ fun aNotificationData(
roomAvatarUrl = null, roomAvatarUrl = null,
roomDisplayName = null, roomDisplayName = null,
isDirect = false, isDirect = false,
isDm = false,
isEncrypted = false, isEncrypted = false,
isNoisy = false, isNoisy = false,
timestamp = 0L, timestamp = 0L,

1
libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt

@ -80,7 +80,6 @@ class FakeMatrixRoom(
override val isPublic: Boolean = true, override val isPublic: Boolean = true,
override val isSpace: Boolean = false, override val isSpace: Boolean = false,
override val isDirect: Boolean = false, override val isDirect: Boolean = false,
override val isOneToOne: Boolean = false,
override val joinedMemberCount: Long = 123L, override val joinedMemberCount: Long = 123L,
override val activeMemberCount: Long = 234L, override val activeMemberCount: Long = 234L,
val notificationSettingsService: NotificationSettingsService = FakeNotificationSettingsService(), val notificationSettingsService: NotificationSettingsService = FakeNotificationSettingsService(),

2
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/CallNotificationEventResolver.kt

@ -79,7 +79,7 @@ class DefaultCallNotificationEventResolver @Inject constructor(
senderDisambiguatedDisplayName = getDisambiguatedDisplayName(content.senderId), senderDisambiguatedDisplayName = getDisambiguatedDisplayName(content.senderId),
body = " ${stringProvider.getString(R.string.notification_incoming_call)}", body = " ${stringProvider.getString(R.string.notification_incoming_call)}",
roomName = roomDisplayName, roomName = roomDisplayName,
roomIsDirect = isDirect, roomIsDm = isDm,
roomAvatarPath = roomAvatarUrl, roomAvatarPath = roomAvatarUrl,
senderAvatarPath = senderAvatarUrl, senderAvatarPath = senderAvatarUrl,
type = EventType.CALL_NOTIFY, type = EventType.CALL_NOTIFY,

10
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolver.kt

@ -120,7 +120,7 @@ class DefaultNotifiableEventResolver @Inject constructor(
body = notificationBody, body = notificationBody,
imageUriString = fetchImageIfPresent(client)?.toString(), imageUriString = fetchImageIfPresent(client)?.toString(),
roomName = roomDisplayName, roomName = roomDisplayName,
roomIsDirect = isDirect, roomIsDm = isDm,
roomAvatarPath = roomAvatarUrl, roomAvatarPath = roomAvatarUrl,
senderAvatarPath = senderAvatarUrl, senderAvatarPath = senderAvatarUrl,
) )
@ -168,7 +168,7 @@ class DefaultNotifiableEventResolver @Inject constructor(
body = stringProvider.getString(CommonStrings.common_call_invite), body = stringProvider.getString(CommonStrings.common_call_invite),
imageUriString = fetchImageIfPresent(client)?.toString(), imageUriString = fetchImageIfPresent(client)?.toString(),
roomName = roomDisplayName, roomName = roomDisplayName,
roomIsDirect = isDirect, roomIsDm = isDm,
roomAvatarPath = roomAvatarUrl, roomAvatarPath = roomAvatarUrl,
senderAvatarPath = senderAvatarUrl, senderAvatarPath = senderAvatarUrl,
) )
@ -197,7 +197,7 @@ class DefaultNotifiableEventResolver @Inject constructor(
body = stringProvider.getString(CommonStrings.common_poll_summary, content.question), body = stringProvider.getString(CommonStrings.common_poll_summary, content.question),
imageUriString = null, imageUriString = null,
roomName = roomDisplayName, roomName = roomDisplayName,
roomIsDirect = isDirect, roomIsDm = isDm,
roomAvatarPath = roomAvatarUrl, roomAvatarPath = roomAvatarUrl,
senderAvatarPath = senderAvatarUrl, senderAvatarPath = senderAvatarUrl,
) )
@ -330,7 +330,7 @@ internal fun buildNotifiableMessageEvent(
imageUriString: String? = null, imageUriString: String? = null,
threadId: ThreadId? = null, threadId: ThreadId? = null,
roomName: String? = null, roomName: String? = null,
roomIsDirect: Boolean = false, roomIsDm: Boolean = false,
roomAvatarPath: String? = null, roomAvatarPath: String? = null,
senderAvatarPath: String? = null, senderAvatarPath: String? = null,
soundName: String? = null, soundName: String? = null,
@ -354,7 +354,7 @@ internal fun buildNotifiableMessageEvent(
imageUriString = imageUriString, imageUriString = imageUriString,
threadId = threadId, threadId = threadId,
roomName = roomName, roomName = roomName,
roomIsDirect = roomIsDirect, roomIsDm = roomIsDm,
roomAvatarPath = roomAvatarPath, roomAvatarPath = roomAvatarPath,
senderAvatarPath = senderAvatarPath, senderAvatarPath = senderAvatarPath,
soundName = soundName, soundName = soundName,

3
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandler.kt

@ -25,6 +25,7 @@ import io.element.android.libraries.matrix.api.core.SessionId
import io.element.android.libraries.matrix.api.core.ThreadId import io.element.android.libraries.matrix.api.core.ThreadId
import io.element.android.libraries.matrix.api.core.asEventId import io.element.android.libraries.matrix.api.core.asEventId
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.isDm
import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.api.timeline.ReceiptType
import io.element.android.libraries.preferences.api.store.SessionPreferencesStoreFactory import io.element.android.libraries.preferences.api.store.SessionPreferencesStoreFactory
import io.element.android.libraries.push.api.notifications.NotificationDrawerManager import io.element.android.libraries.push.api.notifications.NotificationDrawerManager
@ -160,7 +161,7 @@ class NotificationBroadcastReceiverHandler @Inject constructor(
imageUriString = null, imageUriString = null,
threadId = threadId, threadId = threadId,
roomName = room.displayName, roomName = room.displayName,
roomIsDirect = room.isDirect, roomIsDm = room.isDm,
outGoingMessage = true, outGoingMessage = true,
) )
onNotifiableEventReceived.onNotifiableEventReceived(notifiableMessageEvent) onNotifiableEventReceived.onNotifiableEventReceived(notifiableMessageEvent)

12
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationDataFactory.kt

@ -79,7 +79,7 @@ class DefaultNotificationDataFactory @Inject constructor(
.groupBy { it.roomId } .groupBy { it.roomId }
return messagesToDisplay.map { (roomId, events) -> return messagesToDisplay.map { (roomId, events) ->
val roomName = events.lastOrNull()?.roomName ?: roomId.value val roomName = events.lastOrNull()?.roomName ?: roomId.value
val isDirect = events.lastOrNull()?.roomIsDirect ?: false val isDm = events.lastOrNull()?.roomIsDm ?: false
val notification = roomGroupMessageCreator.createRoomMessage( val notification = roomGroupMessageCreator.createRoomMessage(
currentUser = currentUser, currentUser = currentUser,
events = events, events = events,
@ -90,7 +90,7 @@ class DefaultNotificationDataFactory @Inject constructor(
RoomNotification( RoomNotification(
notification = notification, notification = notification,
roomId = roomId, roomId = roomId,
summaryLine = createRoomMessagesGroupSummaryLine(events, roomName, isDirect), summaryLine = createRoomMessagesGroupSummaryLine(events, roomName, isDm),
messageCount = events.size, messageCount = events.size,
latestTimestamp = events.maxOf { it.timestamp }, latestTimestamp = events.maxOf { it.timestamp },
shouldBing = events.any { it.noisy } shouldBing = events.any { it.noisy }
@ -167,9 +167,9 @@ class DefaultNotificationDataFactory @Inject constructor(
} }
} }
private fun createRoomMessagesGroupSummaryLine(events: List<NotifiableMessageEvent>, roomName: String, roomIsDirect: Boolean): CharSequence { private fun createRoomMessagesGroupSummaryLine(events: List<NotifiableMessageEvent>, roomName: String, roomIsDm: Boolean): CharSequence {
return when (events.size) { return when (events.size) {
1 -> createFirstMessageSummaryLine(events.first(), roomName, roomIsDirect) 1 -> createFirstMessageSummaryLine(events.first(), roomName, roomIsDm)
else -> { else -> {
stringProvider.getQuantityString( stringProvider.getQuantityString(
R.plurals.notification_compat_summary_line_for_room, R.plurals.notification_compat_summary_line_for_room,
@ -181,8 +181,8 @@ class DefaultNotificationDataFactory @Inject constructor(
} }
} }
private fun createFirstMessageSummaryLine(event: NotifiableMessageEvent, roomName: String, roomIsDirect: Boolean): CharSequence { private fun createFirstMessageSummaryLine(event: NotifiableMessageEvent, roomName: String, roomIsDm: Boolean): CharSequence {
return if (roomIsDirect) { return if (roomIsDm) {
buildSpannedString { buildSpannedString {
event.senderDisambiguatedDisplayName?.let { event.senderDisambiguatedDisplayName?.let {
inSpans(StyleSpan(Typeface.BOLD)) { inSpans(StyleSpan(Typeface.BOLD)) {

2
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomEventGroupInfo.kt

@ -26,7 +26,7 @@ data class RoomEventGroupInfo(
val sessionId: SessionId, val sessionId: SessionId,
val roomId: RoomId, val roomId: RoomId,
val roomDisplayName: String, val roomDisplayName: String,
val isDirect: Boolean = false, val isDm: Boolean = false,
// An event in the list has not yet been display // An event in the list has not yet been display
val hasNewEvent: Boolean = false, val hasNewEvent: Boolean = false,
// true if at least one on the not yet displayed event is noisy // true if at least one on the not yet displayed event is noisy

5
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/RoomGroupMessageCreator.kt

@ -56,7 +56,7 @@ class DefaultRoomGroupMessageCreator @Inject constructor(
): Notification { ): Notification {
val lastKnownRoomEvent = events.last() val lastKnownRoomEvent = events.last()
val roomName = lastKnownRoomEvent.roomName ?: lastKnownRoomEvent.senderDisambiguatedDisplayName ?: "Room name (${roomId.value.take(8)}…)" val roomName = lastKnownRoomEvent.roomName ?: lastKnownRoomEvent.senderDisambiguatedDisplayName ?: "Room name (${roomId.value.take(8)}…)"
val roomIsGroup = !lastKnownRoomEvent.roomIsDirect val roomIsGroup = !lastKnownRoomEvent.roomIsDm
val tickerText = if (roomIsGroup) { val tickerText = if (roomIsGroup) {
stringProvider.getString(R.string.notification_ticker_text_group, roomName, events.last().senderDisambiguatedDisplayName, events.last().description) stringProvider.getString(R.string.notification_ticker_text_group, roomName, events.last().senderDisambiguatedDisplayName, events.last().description)
@ -68,12 +68,13 @@ class DefaultRoomGroupMessageCreator @Inject constructor(
val lastMessageTimestamp = events.last().timestamp val lastMessageTimestamp = events.last().timestamp
val smartReplyErrors = events.filter { it.isSmartReplyError() } val smartReplyErrors = events.filter { it.isSmartReplyError() }
val roomIsDm = !roomIsGroup
return notificationCreator.createMessagesListNotification( return notificationCreator.createMessagesListNotification(
RoomEventGroupInfo( RoomEventGroupInfo(
sessionId = currentUser.userId, sessionId = currentUser.userId,
roomId = roomId, roomId = roomId,
roomDisplayName = roomName, roomDisplayName = roomName,
isDirect = !roomIsGroup, isDm = roomIsDm,
hasSmartReplyError = smartReplyErrors.isNotEmpty(), hasSmartReplyError = smartReplyErrors.isNotEmpty(),
shouldBing = events.any { it.noisy }, shouldBing = events.any { it.noisy },
customSound = events.last().soundName, customSound = events.last().soundName,

2
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/factories/NotificationCreator.kt

@ -157,7 +157,7 @@ class DefaultNotificationCreator @Inject constructor(
val messagingStyle = existingNotification?.let { val messagingStyle = existingNotification?.let {
MessagingStyle.extractMessagingStyleFromNotification(it) MessagingStyle.extractMessagingStyleFromNotification(it)
} ?: messagingStyleFromCurrentUser(roomInfo.sessionId, currentUser, imageLoader, roomInfo.roomDisplayName, !roomInfo.isDirect) } ?: messagingStyleFromCurrentUser(roomInfo.sessionId, currentUser, imageLoader, roomInfo.roomDisplayName, !roomInfo.isDm)
messagingStyle.addMessagesFromEvents(events, imageLoader) messagingStyle.addMessagesFromEvents(events, imageLoader)

2
libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/model/NotifiableMessageEvent.kt

@ -43,7 +43,7 @@ data class NotifiableMessageEvent(
val imageUriString: String?, val imageUriString: String?,
val threadId: ThreadId?, val threadId: ThreadId?,
val roomName: String?, val roomName: String?,
val roomIsDirect: Boolean = false, val roomIsDm: Boolean = false,
val roomAvatarPath: String? = null, val roomAvatarPath: String? = null,
val senderAvatarPath: String? = null, val senderAvatarPath: String? = null,
val soundName: String? = null, val soundName: String? = null,

5
libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultNotifiableEventResolverTest.kt

@ -473,7 +473,6 @@ class DefaultNotifiableEventResolverTest {
imageUriString = null, imageUriString = null,
threadId = null, threadId = null,
roomName = null, roomName = null,
roomIsDirect = false,
roomAvatarPath = null, roomAvatarPath = null,
senderAvatarPath = null, senderAvatarPath = null,
soundName = null, soundName = null,
@ -544,7 +543,6 @@ class DefaultNotifiableEventResolverTest {
roomId = A_ROOM_ID, roomId = A_ROOM_ID,
threadId = null, threadId = null,
roomName = null, roomName = null,
roomIsDirect = false,
canBeReplaced = false, canBeReplaced = false,
isRedacted = false, isRedacted = false,
imageUriString = null, imageUriString = null,
@ -578,7 +576,6 @@ class DefaultNotifiableEventResolverTest {
roomId = A_ROOM_ID, roomId = A_ROOM_ID,
threadId = null, threadId = null,
roomName = null, roomName = null,
roomIsDirect = false,
canBeReplaced = false, canBeReplaced = false,
isRedacted = false, isRedacted = false,
imageUriString = null, imageUriString = null,
@ -686,6 +683,7 @@ class DefaultNotifiableEventResolverTest {
timestamp = timestamp, timestamp = timestamp,
content = content, content = content,
hasMention = hasMention, hasMention = hasMention,
isDm = false,
) )
} }
@ -704,7 +702,6 @@ class DefaultNotifiableEventResolverTest {
imageUriString = null, imageUriString = null,
threadId = null, threadId = null,
roomName = null, roomName = null,
roomIsDirect = false,
roomAvatarPath = null, roomAvatarPath = null,
senderAvatarPath = null, senderAvatarPath = null,
soundName = null, soundName = null,

4
libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/DefaultRoomGroupMessageCreatorTest.kt

@ -188,14 +188,14 @@ class DefaultRoomGroupMessageCreatorTest {
} }
@Test @Test
fun `test createRoomMessage for direct room`() = runTest { fun `test createRoomMessage for DM`() = runTest {
val sut = createRoomGroupMessageCreator() val sut = createRoomGroupMessageCreator()
val fakeImageLoader = FakeImageLoader() val fakeImageLoader = FakeImageLoader()
val result = sut.createRoomMessage( val result = sut.createRoomMessage(
currentUser = aMatrixUser(), currentUser = aMatrixUser(),
events = listOf( events = listOf(
aNotifiableMessageEvent(timestamp = A_TIMESTAMP).copy( aNotifiableMessageEvent(timestamp = A_TIMESTAMP).copy(
roomIsDirect = true, roomIsDm = true,
), ),
), ),
roomId = A_ROOM_ID, roomId = A_ROOM_ID,

2
libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/factories/DefaultNotificationCreatorTest.kt

@ -227,7 +227,6 @@ class DefaultNotificationCreatorTest {
sessionId = A_SESSION_ID, sessionId = A_SESSION_ID,
roomId = A_ROOM_ID, roomId = A_ROOM_ID,
roomDisplayName = "roomDisplayName", roomDisplayName = "roomDisplayName",
isDirect = false,
hasSmartReplyError = false, hasSmartReplyError = false,
shouldBing = false, shouldBing = false,
customSound = null, customSound = null,
@ -254,7 +253,6 @@ class DefaultNotificationCreatorTest {
sessionId = A_SESSION_ID, sessionId = A_SESSION_ID,
roomId = A_ROOM_ID, roomId = A_ROOM_ID,
roomDisplayName = "roomDisplayName", roomDisplayName = "roomDisplayName",
isDirect = false,
hasSmartReplyError = false, hasSmartReplyError = false,
shouldBing = true, shouldBing = true,
customSound = null, customSound = null,

1
libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/fixtures/NotifiableEventFixture.kt

@ -97,7 +97,6 @@ fun aNotifiableMessageEvent(
roomId = roomId, roomId = roomId,
threadId = threadId, threadId = threadId,
roomName = "room-name", roomName = "room-name",
roomIsDirect = false,
canBeReplaced = false, canBeReplaced = false,
isRedacted = isRedacted, isRedacted = isRedacted,
imageUriString = null, imageUriString = null,

3
tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Day_10_en.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fd2b534a026cb2c96c90f20eb0154b20672d4eae294c558804898cd927e93585
size 106258

3
tests/uitests/src/test/snapshots/images/features.joinroom.impl_JoinRoomView_Night_10_en.png

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:13ed33b9e0b040ac1052389728179014984dc765671384e2d7215377a19d8fcc
size 91575
Loading…
Cancel
Save