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>
This commit is contained in:
parent
75a6af068c
commit
bb47ff8f49
47 changed files with 195 additions and 73 deletions
|
|
@ -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,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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(
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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>>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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(),
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:fd2b534a026cb2c96c90f20eb0154b20672d4eae294c558804898cd927e93585
|
||||||
|
size 106258
|
||||||
|
|
@ -0,0 +1,3 @@
|
||||||
|
version https://git-lfs.github.com/spec/v1
|
||||||
|
oid sha256:13ed33b9e0b040ac1052389728179014984dc765671384e2d7215377a19d8fcc
|
||||||
|
size 91575
|
||||||
Loading…
Add table
Add a link
Reference in a new issue