diff --git a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationPresenter.kt b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationPresenter.kt index 4710e4bd50..e4859da08d 100644 --- a/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationPresenter.kt +++ b/features/location/impl/src/main/kotlin/io/element/android/features/location/impl/send/SendLocationPresenter.kt @@ -26,9 +26,6 @@ import io.element.android.libraries.architecture.Presenter import io.element.android.libraries.core.meta.BuildMeta import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.location.AssetType -import io.element.android.libraries.matrix.api.room.message.ReplyParameters -import io.element.android.libraries.matrix.api.room.message.replyInThread -import io.element.android.libraries.matrix.ui.messages.reply.eventId import io.element.android.libraries.textcomposer.model.MessageComposerMode import io.element.android.services.analytics.api.AnalyticsService import kotlinx.coroutines.launch @@ -103,17 +100,7 @@ class SendLocationPresenter @Inject constructor( mode: SendLocationState.Mode, ) { val replyMode = messageComposerContext.composerMode as? MessageComposerMode.Reply - val replyParams = replyMode?.replyToDetails?.let { details -> - if (replyMode.inThread) { - replyInThread(details.eventId()) - } else { - ReplyParameters( - inReplyToEventId = details.eventId(), - enforceThreadReply = false, - replyWithinThread = false - ) - } - } + val inReplyToEventId = replyMode?.eventId when (mode) { SendLocationState.Mode.PinLocation -> { val geoUri = event.cameraPosition.toGeoUri() @@ -123,7 +110,7 @@ class SendLocationPresenter @Inject constructor( description = null, zoomLevel = MapDefaults.DEFAULT_ZOOM.toInt(), assetType = AssetType.PIN, - replyParameters = replyParams, + inReplyToEventId = inReplyToEventId, ) analyticsService.capture( Composer( @@ -142,7 +129,7 @@ class SendLocationPresenter @Inject constructor( description = null, zoomLevel = MapDefaults.DEFAULT_ZOOM.toInt(), assetType = AssetType.SENDER, - replyParameters = replyParams, + inReplyToEventId = inReplyToEventId, ) analyticsService.capture( Composer( diff --git a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt index 7addee38e4..543d71eae1 100644 --- a/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt +++ b/features/location/impl/src/test/kotlin/io/element/android/features/location/impl/send/SendLocationPresenterTest.kt @@ -20,9 +20,9 @@ import io.element.android.features.location.impl.common.permissions.PermissionsE import io.element.android.features.location.impl.common.permissions.PermissionsPresenter import io.element.android.features.location.impl.common.permissions.PermissionsState import io.element.android.features.messages.test.FakeMessageComposerContext +import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.room.JoinedRoom import io.element.android.libraries.matrix.api.room.location.AssetType -import io.element.android.libraries.matrix.api.room.message.ReplyParameters import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.core.aBuildMeta @@ -264,7 +264,7 @@ class SendLocationPresenterTest { @Test fun `share sender location`() = runTest { - val sendLocationResult = lambdaRecorder> { _, _, _, _, _, _ -> + val sendLocationResult = lambdaRecorder> { _, _, _, _, _, _ -> Result.success(Unit) } val joinedRoom = FakeJoinedRoom( @@ -328,7 +328,7 @@ class SendLocationPresenterTest { @Test fun `share pin location`() = runTest { - val sendLocationResult = lambdaRecorder> { _, _, _, _, _, _ -> + val sendLocationResult = lambdaRecorder> { _, _, _, _, _, _ -> Result.success(Unit) } val joinedRoom = FakeJoinedRoom( @@ -392,7 +392,7 @@ class SendLocationPresenterTest { @Test fun `composer context passes through analytics`() = runTest { - val sendLocationResult = lambdaRecorder> { _, _, _, _, _, _ -> + val sendLocationResult = lambdaRecorder> { _, _, _, _, _, _ -> Result.success(Unit) } val joinedRoom = FakeJoinedRoom( diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt index 35ef6d5d8d..bfae4c20d1 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt @@ -31,9 +31,9 @@ import io.element.android.libraries.core.extensions.runCatchingExceptions import io.element.android.libraries.di.annotations.SessionCoroutineScope import io.element.android.libraries.featureflag.api.FeatureFlagService import io.element.android.libraries.featureflag.api.FeatureFlags +import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder -import io.element.android.libraries.matrix.api.room.message.ReplyParameters import io.element.android.libraries.mediaupload.api.MediaSender import io.element.android.libraries.mediaupload.api.MediaUploadInfo import io.element.android.libraries.mediaupload.api.allFiles @@ -129,7 +129,7 @@ class AttachmentsPreviewPresenter @AssistedInject constructor( caption = caption, sendActionState = sendActionState, dismissAfterSend = !useSendQueue, - replyParameters = null, + inReplyToEventId = null, ) // Clean up the pre-processed media after it's been sent @@ -245,7 +245,7 @@ class AttachmentsPreviewPresenter @AssistedInject constructor( caption: String?, sendActionState: MutableState, dismissAfterSend: Boolean, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId?, ) = runCatchingExceptions { val context = coroutineContext val progressCallback = object : ProgressCallback { @@ -261,7 +261,7 @@ class AttachmentsPreviewPresenter @AssistedInject constructor( caption = caption, formattedCaption = null, progressCallback = progressCallback, - replyParameters = replyParameters, + inReplyToEventId = inReplyToEventId, ).getOrThrow() }.fold( onSuccess = { diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt index f7187c5021..c998090b76 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenter.kt @@ -55,7 +55,6 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom 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.isDm -import io.element.android.libraries.matrix.api.room.message.ReplyParameters import io.element.android.libraries.matrix.api.timeline.TimelineException import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId import io.element.android.libraries.matrix.ui.messages.reply.InReplyToDetails @@ -466,12 +465,7 @@ class MessageComposerPresenter @AssistedInject constructor( body = message.markdown, htmlBody = message.html, intentionalMentions = message.intentionalMentions, - replyParameters = ReplyParameters( - inReplyToEventId = eventId, - enforceThreadReply = inThread, - // This should be false until we add a way to make a reply in a thread an explicit reply to the provided eventId - replyWithinThread = false, - ), + repliedToEventId = eventId, ) } } diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt index 72035cdc3d..a3cab8610f 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/components/virtual/TimelineItemRoomBeginningView.kt @@ -26,7 +26,6 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.designsystem.text.toAnnotatedString import io.element.android.libraries.designsystem.theme.components.Text import io.element.android.libraries.designsystem.utils.allBooleans -import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.tombstone.PredecessorRoom @@ -92,7 +91,7 @@ internal fun TimelineItemRoomBeginningViewPreview() = ElementPreview { onPredecessorRoomClick = {}, ) TimelineItemRoomBeginningView( - predecessorRoom = PredecessorRoom(RoomId("!roomId:matrix.org"), EventId("\$eventId:matrix.org")), + predecessorRoom = PredecessorRoom(RoomId("!roomId:matrix.org")), roomName = "Room Name", isDm = isDm, onPredecessorRoomClick = {}, diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt index 1c1ffd45b4..292bc89654 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt @@ -23,6 +23,7 @@ import io.element.android.libraries.androidutils.file.TemporaryUriDeleter import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.featureflag.api.FeatureFlags import io.element.android.libraries.featureflag.test.FakeFeatureFlagService +import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.media.AudioInfo import io.element.android.libraries.matrix.api.media.FileInfo @@ -30,7 +31,6 @@ import io.element.android.libraries.matrix.api.media.ImageInfo import io.element.android.libraries.matrix.api.media.VideoInfo import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder import io.element.android.libraries.matrix.api.room.JoinedRoom -import io.element.android.libraries.matrix.api.room.message.ReplyParameters import io.element.android.libraries.matrix.test.A_CAPTION import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler import io.element.android.libraries.matrix.test.permalink.FakePermalinkBuilder @@ -108,7 +108,7 @@ class AttachmentsPreviewPresenterTest { @Test fun `present - send media success scenario`() = runTest { val sendFileResult = - lambdaRecorder> { _, _, _, _, _, _ -> + lambdaRecorder> { _, _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) } val room = FakeJoinedRoom( @@ -152,7 +152,7 @@ class AttachmentsPreviewPresenterTest { @Test fun `present - send media after pre-processing success scenario`() = runTest { val sendFileResult = - lambdaRecorder> { _, _, _, _, _, _ -> + lambdaRecorder> { _, _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) } val room = FakeJoinedRoom( @@ -190,7 +190,7 @@ class AttachmentsPreviewPresenterTest { @Test fun `present - send media before pre-processing success scenario`() = runTest { val sendFileResult = - lambdaRecorder> { _, _, _, _, _, _ -> + lambdaRecorder> { _, _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) } val room = FakeJoinedRoom( @@ -305,7 +305,7 @@ class AttachmentsPreviewPresenterTest { @Test fun `present - send image with caption success scenario`() = runTest { val sendImageResult = - lambdaRecorder { _: File, _: File?, _: ImageInfo, _: String?, _: String?, _: ProgressCallback?, _: ReplyParameters? -> + lambdaRecorder { _: File, _: File?, _: ImageInfo, _: String?, _: String?, _: ProgressCallback?, _: EventId? -> Result.success(FakeMediaUploadHandler()) } val mediaPreProcessor = FakeMediaPreProcessor().apply { @@ -349,7 +349,7 @@ class AttachmentsPreviewPresenterTest { @Test fun `present - send video with caption success scenario`() = runTest { val sendVideoResult = - lambdaRecorder { _: File, _: File?, _: VideoInfo, _: String?, _: String?, _: ProgressCallback?, _: ReplyParameters? -> + lambdaRecorder { _: File, _: File?, _: VideoInfo, _: String?, _: String?, _: ProgressCallback?, _: EventId? -> Result.success(FakeMediaUploadHandler()) } val mediaPreProcessor = FakeMediaPreProcessor().apply { @@ -393,7 +393,7 @@ class AttachmentsPreviewPresenterTest { @Test fun `present - send audio with caption success scenario`() = runTest { val sendAudioResult = - lambdaRecorder> { _, _, _, _, _, _ -> + lambdaRecorder> { _, _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) } val mediaPreProcessor = FakeMediaPreProcessor().apply { @@ -435,7 +435,7 @@ class AttachmentsPreviewPresenterTest { fun `present - send media failure scenario without media queue`() = runTest { val failure = MediaPreProcessor.Failure(null) val sendFileResult = - lambdaRecorder> { _, _, _, _, _, _ -> + lambdaRecorder> { _, _, _, _, _, _ -> Result.failure(failure) } val room = FakeJoinedRoom( @@ -466,7 +466,7 @@ class AttachmentsPreviewPresenterTest { fun `present - send media failure scenario with media queue`() = runTest { val failure = MediaPreProcessor.Failure(null) val sendFileResult = - lambdaRecorder> { _, _, _, _, _, _ -> + lambdaRecorder> { _, _, _, _, _, _ -> Result.failure(failure) } val onDoneListenerResult = lambdaRecorder {} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt index 1a7a1d930e..1d5318f94f 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerPresenterTest.kt @@ -48,7 +48,6 @@ import io.element.android.libraries.matrix.api.room.RoomMembersState import io.element.android.libraries.matrix.api.room.RoomMembershipState 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.message.ReplyParameters import io.element.android.libraries.matrix.api.timeline.TimelineException import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo @@ -610,7 +609,7 @@ class MessageComposerPresenterTest { @Test fun `present - reply message`() = runTest { - val replyMessageLambda = lambdaRecorder { _: ReplyParameters, _: String, _: String?, _: List, _: Boolean -> + val replyMessageLambda = lambdaRecorder { _: EventId?, _: String, _: String?, _: List, _: Boolean -> Result.success(Unit) } val timeline = FakeTimeline().apply { @@ -1100,7 +1099,7 @@ class MessageComposerPresenterTest { @OptIn(ExperimentalCoroutinesApi::class) @Test fun `present - send messages with intentional mentions`() = runTest { - val replyMessageLambda = lambdaRecorder { _: ReplyParameters, _: String, _: String?, _: List, _: Boolean -> + val replyMessageLambda = lambdaRecorder { _: EventId?, _: String, _: String?, _: List, _: Boolean -> Result.success(Unit) } val editMessageLambda = lambdaRecorder { _: EventOrTransactionId, _: String, _: String?, _: List -> diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt index 2cc7d9764a..f202fd7e6e 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelinePresenterTest.kt @@ -710,11 +710,7 @@ class TimelinePresenterTest { @Test fun `present - timeline room info includes predecessor room when room has predecessor`() = runTest { val predecessorRoomId = RoomId("!predecessor:server.org") - val predecessorEventId = EventId("\$predecessorEvent:server.org") - val predecessorRoom = PredecessorRoom( - roomId = predecessorRoomId, - lastEventId = predecessorEventId - ) + val predecessorRoom = PredecessorRoom(roomId = predecessorRoomId) val room = FakeJoinedRoom( baseRoom = FakeBaseRoom( @@ -730,7 +726,6 @@ class TimelinePresenterTest { val initialState = awaitFirstItem() assertThat(initialState.timelineRoomInfo.predecessorRoom).isNotNull() assertThat(initialState.timelineRoomInfo.predecessorRoom?.roomId).isEqualTo(predecessorRoomId) - assertThat(initialState.timelineRoomInfo.predecessorRoom?.lastEventId).isEqualTo(predecessorEventId) } } diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenterTest.kt index 7d0ceb5527..3eb3210a63 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/VoiceMessageComposerPresenterTest.kt @@ -19,9 +19,9 @@ import com.google.common.truth.Truth.assertThat import im.vector.app.features.analytics.plan.Composer import io.element.android.features.messages.impl.messagecomposer.aReplyMode import io.element.android.features.messages.test.FakeMessageComposerContext +import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.media.AudioInfo -import io.element.android.libraries.matrix.api.room.message.ReplyParameters import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler import io.element.android.libraries.matrix.test.room.FakeJoinedRoom import io.element.android.libraries.matrix.test.timeline.FakeTimeline @@ -63,7 +63,7 @@ class VoiceMessageComposerPresenterTest { ) private val analyticsService = FakeAnalyticsService() private val sendVoiceMessageResult = - lambdaRecorder, ProgressCallback?, ReplyParameters?, Result> { _, _, _, _, _ -> + lambdaRecorder, ProgressCallback?, EventId?, Result> { _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) } private val joinedRoom = FakeJoinedRoom( diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/analytics/AnalyticUtils.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/analytics/AnalyticUtils.kt index 7e4ae1d7c2..d28e87b58c 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/analytics/AnalyticUtils.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/analytics/AnalyticUtils.kt @@ -13,6 +13,7 @@ import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsV import io.element.android.services.analytics.api.AnalyticsService internal fun RoomMember.Role.toAnalyticsMemberRole(): RoomModeration.Role = when (this) { + RoomMember.Role.CREATOR -> RoomModeration.Role.Administrator // TODO - distinguish creator from admin RoomMember.Role.ADMIN -> RoomModeration.Role.Administrator RoomMember.Role.MODERATOR -> RoomModeration.Role.Moderator RoomMember.Role.USER -> RoomModeration.Role.User diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt index 443a40c6a2..81dc638d91 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/members/RoomMemberListView.kt @@ -61,6 +61,7 @@ import io.element.android.libraries.designsystem.theme.components.TopAppBar import io.element.android.libraries.matrix.api.encryption.identity.IdentityState import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.getBestName +import io.element.android.libraries.matrix.api.room.isOwner import io.element.android.libraries.matrix.api.room.toMatrixUser import io.element.android.libraries.matrix.ui.components.MatrixUserRow import io.element.android.libraries.ui.strings.CommonStrings @@ -293,10 +294,15 @@ private fun RoomMemberListItem( onClick: () -> Unit, modifier: Modifier = Modifier, ) { - val roleText = when (roomMemberWithIdentity.roomMember.role) { - RoomMember.Role.ADMIN -> stringResource(R.string.screen_room_member_list_role_administrator) - RoomMember.Role.MODERATOR -> stringResource(R.string.screen_room_member_list_role_moderator) - RoomMember.Role.USER -> null + val member = roomMemberWithIdentity.roomMember + val roleText = if (member.isOwner()) { + stringResource(R.string.screen_room_member_list_role_owner) + } else { + when (member.role) { + RoomMember.Role.ADMIN -> stringResource(R.string.screen_room_member_list_role_administrator) + RoomMember.Role.MODERATOR -> stringResource(R.string.screen_room_member_list_role_moderator) + else -> null + } } MatrixUserRow( diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsNode.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsNode.kt index 8b96bcda27..a46ba8000e 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsNode.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsNode.kt @@ -21,6 +21,7 @@ import io.element.android.anvilannotations.ContributesNode import io.element.android.libraries.di.RoomScope import io.element.android.libraries.matrix.api.room.BaseRoom import io.element.android.libraries.matrix.api.room.RoomMember +import io.element.android.libraries.matrix.ui.model.roleOf import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.onEach @@ -59,7 +60,8 @@ class RolesAndPermissionsNode @AssistedInject constructor( lifecycleScope.launch { room.roomInfoFlow .filter { info -> - info.roomPowerLevels?.users?.get(room.sessionId) != RoomMember.Role.ADMIN.powerLevel + val role = info.roleOf(room.sessionId) + role != RoomMember.Role.ADMIN && role != RoomMember.Role.CREATOR } .take(1) .onEach { navigateUp() } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsPresenter.kt index f2de1d2817..be8b77a07e 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/RolesAndPermissionsPresenter.kt @@ -110,8 +110,6 @@ class RolesAndPermissionsPresenter @Inject constructor( } private fun RoomInfo.userCountWithRole(userIds: List, role: RoomMember.Role): Int { - return this.roomPowerLevels?.users?.count { (userId, level) -> - RoomMember.Role.forPowerLevel(level) == role && userId in userIds - } ?: 0 + return usersWithRole(role).filter { it in userIds }.size } } diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesPresenter.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesPresenter.kt index e3a173f2f2..20256cb2ba 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesPresenter.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesPresenter.kt @@ -36,6 +36,7 @@ import io.element.android.libraries.matrix.api.room.powerlevels.UserRoleChange import io.element.android.libraries.matrix.api.room.powerlevels.usersWithRole import io.element.android.libraries.matrix.api.room.toMatrixUser import io.element.android.libraries.matrix.api.user.MatrixUser +import io.element.android.libraries.matrix.ui.model.roleOf import io.element.android.services.analytics.api.AnalyticsService import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.PersistentList @@ -109,8 +110,8 @@ class ChangeRolesPresenter @AssistedInject constructor( val roomInfo by room.roomInfoFlow.collectAsState() fun canChangeMemberRole(userId: UserId): Boolean { // An admin can't remove or demote another admin - val powerLevel = roomInfo.roomPowerLevels?.users?.get(userId) ?: 0L - return RoomMember.Role.forPowerLevel(powerLevel) != RoomMember.Role.ADMIN + val role = roomInfo.roleOf(userId) + return role !in listOf(RoomMember.Role.ADMIN, RoomMember.Role.CREATOR) } fun handleEvent(event: ChangeRolesEvent) { diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesView.kt index a9e321a11f..7a993b6fc6 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/changeroles/ChangeRolesView.kt @@ -98,7 +98,7 @@ fun ChangeRolesView( titleStr = when (state.role) { RoomMember.Role.ADMIN -> stringResource(R.string.screen_room_change_role_administrators_title) RoomMember.Role.MODERATOR -> stringResource(R.string.screen_room_change_role_moderators_title) - RoomMember.Role.USER -> error("This should never be reached") + RoomMember.Role.CREATOR, RoomMember.Role.USER -> error("This should never be reached") }, navigationIcon = { BackButton(onClick = { state.eventSink(ChangeRolesEvent.Exit) }) diff --git a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsView.kt b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsView.kt index ba8cc7f978..48984b4ed7 100644 --- a/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsView.kt +++ b/features/roomdetails/impl/src/main/kotlin/io/element/android/features/roomdetails/impl/rolesandpermissions/permissions/ChangeRoomPermissionsView.kt @@ -135,7 +135,7 @@ private fun SelectRoleItem( onClick: (RoomPermissionType, RoomMember.Role) -> Unit ) { val title = when (role) { - RoomMember.Role.ADMIN -> stringResource(R.string.screen_room_change_permissions_administrators) + RoomMember.Role.ADMIN, RoomMember.Role.CREATOR -> stringResource(R.string.screen_room_change_permissions_administrators) RoomMember.Role.MODERATOR -> stringResource(R.string.screen_room_change_permissions_moderators) RoomMember.Role.USER -> stringResource(R.string.screen_room_change_permissions_everyone) } diff --git a/features/roomdetails/impl/src/main/res/values/localazy.xml b/features/roomdetails/impl/src/main/res/values/localazy.xml index 883f300bff..6cea0c26a9 100644 --- a/features/roomdetails/impl/src/main/res/values/localazy.xml +++ b/features/roomdetails/impl/src/main/res/values/localazy.xml @@ -81,6 +81,7 @@ "Pending" "Admin" "Moderator" + "Owner" "Room members" "Unbanning %1$s" "Allow custom setting" diff --git a/features/roomdirectory/api/src/main/kotlin/io/element/android/features/roomdirectory/api/RoomDescription.kt b/features/roomdirectory/api/src/main/kotlin/io/element/android/features/roomdirectory/api/RoomDescription.kt index 9914aa77b2..7d479cbad9 100644 --- a/features/roomdirectory/api/src/main/kotlin/io/element/android/features/roomdirectory/api/RoomDescription.kt +++ b/features/roomdirectory/api/src/main/kotlin/io/element/android/features/roomdirectory/api/RoomDescription.kt @@ -30,6 +30,9 @@ data class RoomDescription( enum class JoinRule { PUBLIC, KNOCK, + RESTRICTED, + KNOCK_RESTRICTED, + INVITE, UNKNOWN } diff --git a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/model/RoomDescription.kt b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/model/RoomDescription.kt index ed52e71e48..fba94c49f7 100644 --- a/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/model/RoomDescription.kt +++ b/features/roomdirectory/impl/src/main/kotlin/io/element/android/features/roomdirectory/impl/root/model/RoomDescription.kt @@ -21,6 +21,9 @@ fun MatrixRoomDescription.toFeatureModel(): RoomDescription { joinRule = when (joinRule) { MatrixRoomDescription.JoinRule.PUBLIC -> RoomDescription.JoinRule.PUBLIC MatrixRoomDescription.JoinRule.KNOCK -> RoomDescription.JoinRule.KNOCK + MatrixRoomDescription.JoinRule.RESTRICTED -> RoomDescription.JoinRule.RESTRICTED + MatrixRoomDescription.JoinRule.KNOCK_RESTRICTED -> RoomDescription.JoinRule.KNOCK_RESTRICTED + MatrixRoomDescription.JoinRule.INVITE -> RoomDescription.JoinRule.INVITE MatrixRoomDescription.JoinRule.UNKNOWN -> RoomDescription.JoinRule.UNKNOWN } ) diff --git a/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/SharePresenterTest.kt b/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/SharePresenterTest.kt index 979d6aa61c..203d5888d1 100644 --- a/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/SharePresenterTest.kt +++ b/features/share/impl/src/test/kotlin/io/element/android/features/share/impl/SharePresenterTest.kt @@ -16,9 +16,9 @@ import com.google.common.truth.Truth.assertThat import io.element.android.libraries.architecture.AsyncAction import io.element.android.libraries.core.mimetype.MimeTypes import io.element.android.libraries.matrix.api.MatrixClient +import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.media.FileInfo -import io.element.android.libraries.matrix.api.room.message.ReplyParameters import io.element.android.libraries.matrix.test.A_MESSAGE import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.FakeMatrixClient @@ -122,7 +122,7 @@ class SharePresenterTest { @Test fun `present - send media ok`() = runTest { val sendFileResult = - lambdaRecorder> { _, _, _, _, _, _ -> + lambdaRecorder> { _, _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) } val joinedRoom = FakeJoinedRoom( diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index bec5bb7c3f..5308e42bb2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -176,7 +176,7 @@ jsoup = "org.jsoup:jsoup:1.21.1" appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" } molecule-runtime = "app.cash.molecule:molecule-runtime:2.1.0" timber = "com.jakewharton.timber:timber:5.0.1" -matrix_sdk = "org.matrix.rustcomponents:sdk-android:25.7.15" +matrix_sdk = "org.matrix.rustcomponents:sdk-android:25.7.23" matrix_richtexteditor = { module = "io.element.android:wysiwyg", version.ref = "wysiwyg" } matrix_richtexteditor_compose = { module = "io.element.android:wysiwyg-compose", version.ref = "wysiwyg" } sqldelight-driver-android = { module = "app.cash.sqldelight:android-driver", version.ref = "sqldelight" } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index d968ae63de..a23ee2c14e 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -142,7 +142,7 @@ interface MatrixClient { /** * Execute generic GET requests through the SDKs internal HTTP client. */ - suspend fun getUrl(url: String): Result + suspend fun getUrl(url: String): Result /** * Get a room preview for a given room ID or alias. This is especially useful for rooms that the user is not a member of, or hasn't joined yet. diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaPreviewConfig.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaPreviewConfig.kt index 66a53b3ad3..f1adec8e89 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaPreviewConfig.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaPreviewConfig.kt @@ -19,7 +19,7 @@ data class MediaPreviewConfig( * The default config if unknown (no local nor server config). */ val DEFAULT = MediaPreviewConfig( - mediaPreviewValue = MediaPreviewValue.On, + mediaPreviewValue = MediaPreviewValue.DEFAULT, hideInviteAvatar = false ) } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaPreviewValue.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaPreviewValue.kt index 25d489a48c..60f7552fd9 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaPreviewValue.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/media/MediaPreviewValue.kt @@ -21,12 +21,19 @@ import io.element.android.libraries.matrix.api.room.join.JoinRule enum class MediaPreviewValue { On, Off, - Private + Private; + + companion object { + /** + * The default value if unknown (no local nor server config). + */ + val DEFAULT = On + } } -fun MediaPreviewValue.isPreviewEnabled(joinRule: JoinRule?): Boolean { +fun MediaPreviewValue?.isPreviewEnabled(joinRule: JoinRule?): Boolean { return when (this) { - On -> true + null, On -> true Off -> false Private -> when (joinRule) { is JoinRule.Private, diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomInfo.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomInfo.kt index 261a484fd6..812994af92 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomInfo.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomInfo.kt @@ -72,10 +72,21 @@ data class RoomInfo( val numUnreadMentions: Long, val heroes: ImmutableList, val pinnedEventIds: ImmutableList, - val creator: UserId?, + val creators: ImmutableList, val historyVisibility: RoomHistoryVisibility, val successorRoom: SuccessorRoom?, ) { val aliases: List get() = listOfNotNull(canonicalAlias) + alternativeAliases + + /** + * Returns the list of users with the given [role] in this room. + */ + fun usersWithRole(role: RoomMember.Role): List { + return if (role == RoomMember.Role.CREATOR) { + this.creators + } else { + this.roomPowerLevels?.usersWithRole(role).orEmpty().toList() + } + } } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt index 00da7e4137..74c5eef63d 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/RoomMember.kt @@ -26,13 +26,17 @@ data class RoomMember( * Role of the RoomMember, based on its [powerLevel]. */ enum class Role(val powerLevel: Long) { + CREATOR(Long.MAX_VALUE), ADMIN(100L), MODERATOR(50L), USER(0L); companion object { + const val SUPER_ADMIN_LEVEL = 150L + fun forPowerLevel(powerLevel: Long): Role { return when { + powerLevel > SUPER_ADMIN_LEVEL -> CREATOR powerLevel >= ADMIN.powerLevel -> ADMIN powerLevel >= MODERATOR.powerLevel -> MODERATOR else -> USER @@ -83,3 +87,11 @@ fun RoomMember.toMatrixUser() = MatrixUser( displayName = displayName, avatarUrl = avatarUrl, ) + +/** + * Returns `true` if the [RoomMember] is an owner of the room. + * Owners are defined as members with either the [RoomMember.Role.CREATOR] role or a power level greater than or equal to [RoomMember.Role.SUPER_ADMIN_LEVEL]. + */ +fun RoomMember.isOwner(): Boolean { + return role == RoomMember.Role.CREATOR || powerLevel >= RoomMember.Role.SUPER_ADMIN_LEVEL +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/message/ReplyParameters.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/message/ReplyParameters.kt deleted file mode 100644 index 6157989a43..0000000000 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/message/ReplyParameters.kt +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.matrix.api.room.message - -import io.element.android.libraries.matrix.api.core.EventId - -data class ReplyParameters( - val inReplyToEventId: EventId, - val enforceThreadReply: Boolean, - val replyWithinThread: Boolean, -) - -fun replyInThread(eventId: EventId, explicitReply: Boolean = false) = ReplyParameters( - inReplyToEventId = eventId, - enforceThreadReply = true, - replyWithinThread = explicitReply, -) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomMembersWithRole.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomMembersWithRole.kt index eb3afaf994..52a2dd691d 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomMembersWithRole.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomMembersWithRole.kt @@ -22,10 +22,10 @@ import kotlinx.coroutines.flow.map */ fun BaseRoom.usersWithRole(role: RoomMember.Role): Flow> { return roomInfoFlow - .map { it.roomPowerLevels?.users.orEmpty().filter { (_, powerLevel) -> RoomMember.Role.forPowerLevel(powerLevel) == role } } + .map { roomInfo -> roomInfo.usersWithRole(role) } .combine(membersStateFlow) { powerLevels, membersState -> membersState.activeRoomMembers() - .filter { powerLevels.containsKey(it.userId) } + .filter { powerLevels.contains(it.userId) } .toPersistentList() } .distinctUntilChanged() diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/RoomPowerLevels.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/RoomPowerLevels.kt index 08f2166494..2d8b415ae5 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/RoomPowerLevels.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/RoomPowerLevels.kt @@ -8,9 +8,43 @@ package io.element.android.libraries.matrix.api.room.powerlevels import io.element.android.libraries.matrix.api.core.UserId +import io.element.android.libraries.matrix.api.room.RoomMember import kotlinx.collections.immutable.ImmutableMap +/** + * Represents the power levels in a Matrix room, containing both the levels needed to perform actions and the custom power levels for users. + * + * **WARNING**: this won't contain the power level of the room creators, as it is not stored in the power levels event. The `users` property is private to + * enforce this restriction and try to avoid using this property directly to check if a user has a certain role. + * Use the [usersWithRole] or [roleOf] methods instead, and never for creators, that logic should be handled separately. + */ data class RoomPowerLevels( + /** + * The power levels required to perform various actions in the room. + */ val values: RoomPowerLevelsValues, - val users: ImmutableMap, -) + private val users: ImmutableMap, +) { + /** + * Returns the set of [UserId]s that have the given role in the room. + * + * **WARNING**: This method must not be used with the [RoomMember.Role.CREATOR] role. It'll result in a runtime error. + */ + fun usersWithRole(role: RoomMember.Role): Set { + return if (role == RoomMember.Role.CREATOR) { + error("RoomPowerLevels.usersWithRole should not be used with CREATOR role, use roomInfo.creators instead") + } else { + users.filterValues { RoomMember.Role.forPowerLevel(it) == role }.keys + } + } + + /** + * Returns the role of the user in the room based on their power level. + * If the user is not found, returns null. + * + * **WARNING**: This method must not be used with the [RoomMember.Role.CREATOR] role, as it won't return any results. + */ + fun roleOf(userId: UserId): RoomMember.Role? { + return users[userId]?.let(RoomMember.Role::forPowerLevel) + } +} diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/tombstone/PredecessorRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/tombstone/PredecessorRoom.kt index cd54b5a07b..4e6e8a7157 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/tombstone/PredecessorRoom.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/tombstone/PredecessorRoom.kt @@ -7,7 +7,6 @@ package io.element.android.libraries.matrix.api.room.tombstone -import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId /** @@ -17,15 +16,10 @@ import io.element.android.libraries.matrix.api.core.RoomId * about the predecessor room. * * A room is tombstoned if it has received a m.room.tombstone state event. - * */ data class PredecessorRoom( /** * The ID of the replaced room. */ val roomId: RoomId, - /** - * The event ID of the last known event in the predecessor room. - */ - val lastEventId: EventId, ) diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDescription.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDescription.kt index 2f0436dd2e..16aa58de28 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDescription.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/roomdirectory/RoomDescription.kt @@ -23,6 +23,9 @@ data class RoomDescription( enum class JoinRule { PUBLIC, KNOCK, + RESTRICTED, + KNOCK_RESTRICTED, + INVITE, UNKNOWN } } diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt index 00652a4fa9..9bcdafb913 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/timeline/Timeline.kt @@ -19,7 +19,6 @@ 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.room.IntentionalMention import io.element.android.libraries.matrix.api.room.location.AssetType -import io.element.android.libraries.matrix.api.room.message.ReplyParameters import io.element.android.libraries.matrix.api.timeline.item.event.EventOrTransactionId import io.element.android.libraries.matrix.api.timeline.item.event.InReplyTo import io.element.android.libraries.matrix.api.timeline.item.event.toEventOrTransactionId @@ -76,7 +75,7 @@ interface Timeline : AutoCloseable { ): Result suspend fun replyMessage( - replyParameters: ReplyParameters, + repliedToEventId: EventId, body: String, htmlBody: String?, intentionalMentions: List, @@ -90,7 +89,7 @@ interface Timeline : AutoCloseable { caption: String?, formattedCaption: String?, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId?, ): Result suspend fun sendVideo( @@ -100,7 +99,7 @@ interface Timeline : AutoCloseable { caption: String?, formattedCaption: String?, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId?, ): Result suspend fun sendAudio( @@ -109,7 +108,7 @@ interface Timeline : AutoCloseable { caption: String?, formattedCaption: String?, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId?, ): Result suspend fun sendFile( @@ -118,7 +117,7 @@ interface Timeline : AutoCloseable { caption: String?, formattedCaption: String?, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId?, ): Result /** @@ -131,7 +130,7 @@ interface Timeline : AutoCloseable { * @param zoomLevel Optional zoom level to display the map at. * @param assetType Optional type of the location asset. * Set to SENDER if sharing own location. Set to PIN if sharing any location. - * @param replyParameters Optional reply parameters to use when sending the location. + * @param inReplyToEventId Optional [EventId] for the event this message should reply to. */ suspend fun sendLocation( body: String, @@ -139,7 +138,7 @@ interface Timeline : AutoCloseable { description: String? = null, zoomLevel: Int? = null, assetType: AssetType? = null, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId?, ): Result suspend fun sendVoiceMessage( @@ -147,7 +146,7 @@ interface Timeline : AutoCloseable { audioInfo: AudioInfo, waveform: List, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId?, ): Result suspend fun redactEvent(eventOrTransactionId: EventOrTransactionId, reason: String?): Result diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index fca6439c05..50b71d92c1 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -272,7 +272,7 @@ class RustMatrixClient( ?: sessionId.value.substringAfter(":") } - override suspend fun getUrl(url: String): Result = withContext(sessionDispatcher) { + override suspend fun getUrl(url: String): Result = withContext(sessionDispatcher) { runCatchingExceptions { innerClient.getUrl(url) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt index a66c80c691..a1ffbc5da9 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/auth/RustMatrixAuthenticationService.kt @@ -200,6 +200,7 @@ class RustMatrixAuthenticationService @Inject constructor( loginHint = loginHint, // If we want to restore a previous session for which we have encryption keys, we can pass the deviceId here. At the moment, we don't deviceId = null, + additionalScopes = emptyList(), ) val url = oAuthAuthorizationData.loginUrl() pendingOAuthAuthorizationData = oAuthAuthorizationData diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaPreviewService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaPreviewService.kt index e4e9bc07bd..15e86ab983 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaPreviewService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/media/RustMediaPreviewService.kt @@ -79,8 +79,9 @@ private fun MediaPreviewValue.into(): MediaPreviews { } } -private fun MediaPreviews.into(): MediaPreviewValue { +private fun MediaPreviews?.into(): MediaPreviewValue { return when (this) { + null -> MediaPreviewValue.DEFAULT MediaPreviews.ON -> MediaPreviewValue.On MediaPreviews.OFF -> MediaPreviewValue.Off MediaPreviews.PRIVATE -> MediaPreviewValue.Private diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt index 5b5d1f69f0..dda5bf2f3a 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapper.kt @@ -35,7 +35,7 @@ class RoomInfoMapper { fun map(rustRoomInfo: RustRoomInfo): RoomInfo = rustRoomInfo.let { return RoomInfo( id = RoomId(it.id), - creator = it.creator?.let(::UserId), + creators = it.creators.orEmpty().map(::UserId).toImmutableList(), name = it.displayName, rawName = it.rawName, topic = it.topic, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoom.kt index 734531a7cc..45aad80350 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustBaseRoom.kt @@ -293,7 +293,7 @@ class RustBaseRoom( override suspend fun reportRoom(reason: String?): Result = withContext(roomDispatcher) { runCatchingExceptions { Timber.d("reportRoom $roomId") - innerRoom.reportRoom(reason) + innerRoom.reportRoom(reason.orEmpty()) } } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/member/RoomMemberMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/member/RoomMemberMapper.kt index 99fec55a02..fe49cdff83 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/member/RoomMemberMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/member/RoomMemberMapper.kt @@ -10,6 +10,7 @@ package io.element.android.libraries.matrix.impl.room.member import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.RoomMembershipState +import io.element.android.libraries.matrix.impl.room.powerlevels.into import uniffi.matrix_sdk.RoomMemberRole import org.matrix.rustcomponents.sdk.MembershipState as RustMembershipState import org.matrix.rustcomponents.sdk.RoomMember as RustRoomMember @@ -21,8 +22,8 @@ object RoomMemberMapper { avatarUrl = roomMember.avatarUrl, membership = mapMembership(roomMember.membership), isNameAmbiguous = roomMember.isNameAmbiguous, - powerLevel = roomMember.powerLevel, - normalizedPowerLevel = roomMember.normalizedPowerLevel, + powerLevel = roomMember.powerLevel.into(), + normalizedPowerLevel = roomMember.normalizedPowerLevel.into(), isIgnored = roomMember.isIgnored, role = mapRole(roomMember.suggestedRoleForPowerLevel), membershipChangeReason = roomMember.membershipChangeReason @@ -30,6 +31,7 @@ object RoomMemberMapper { fun mapRole(role: RoomMemberRole): RoomMember.Role = when (role) { + RoomMemberRole.CREATOR -> RoomMember.Role.CREATOR RoomMemberRole.ADMINISTRATOR -> RoomMember.Role.ADMIN RoomMemberRole.MODERATOR -> RoomMember.Role.MODERATOR RoomMemberRole.USER -> RoomMember.Role.USER diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/message/ReplyParameters.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/message/ReplyParameters.kt deleted file mode 100644 index 415d493e7d..0000000000 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/message/ReplyParameters.kt +++ /dev/null @@ -1,16 +0,0 @@ -/* - * Copyright 2025 New Vector Ltd. - * - * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial - * Please see LICENSE files in the repository root for full details. - */ - -package io.element.android.libraries.matrix.impl.room.message - -import io.element.android.libraries.matrix.api.room.message.ReplyParameters - -fun ReplyParameters.map() = org.matrix.rustcomponents.sdk.ReplyParameters( - eventId = inReplyToEventId.value, - enforceThread = enforceThreadReply, - replyWithinThread = replyWithinThread, -) diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/powerlevels/RoomPowerLevelsValuesMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/powerlevels/RoomPowerLevelsValuesMapper.kt index 840a5da428..f48c055db0 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/powerlevels/RoomPowerLevelsValuesMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/powerlevels/RoomPowerLevelsValuesMapper.kt @@ -7,7 +7,9 @@ package io.element.android.libraries.matrix.impl.room.powerlevels +import io.element.android.libraries.matrix.api.room.RoomMember import io.element.android.libraries.matrix.api.room.powerlevels.RoomPowerLevelsValues +import org.matrix.rustcomponents.sdk.PowerLevel import org.matrix.rustcomponents.sdk.RoomPowerLevelsValues as RustRoomPowerLevelsValues object RoomPowerLevelsValuesMapper { @@ -24,3 +26,8 @@ object RoomPowerLevelsValuesMapper { ) } } + +fun PowerLevel.into(): Long = when (this) { + PowerLevel.Infinite -> RoomMember.Role.CREATOR.powerLevel + is PowerLevel.Value -> this.value +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/tombstone/PredecessorRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/tombstone/PredecessorRoom.kt index 08daee4f19..a09867bf6c 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/tombstone/PredecessorRoom.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/tombstone/PredecessorRoom.kt @@ -7,14 +7,10 @@ package io.element.android.libraries.matrix.impl.room.tombstone -import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.RoomId import io.element.android.libraries.matrix.api.room.tombstone.PredecessorRoom import org.matrix.rustcomponents.sdk.PredecessorRoom as RustPredecessorRoom fun RustPredecessorRoom.map(): PredecessorRoom { - return PredecessorRoom( - roomId = RoomId(roomId), - lastEventId = EventId(lastEventId), - ) + return PredecessorRoom(roomId = RoomId(roomId)) } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RoomDescriptionMapper.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RoomDescriptionMapper.kt index 106c66c522..76a6394e94 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RoomDescriptionMapper.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/roomdirectory/RoomDescriptionMapper.kt @@ -32,6 +32,9 @@ internal fun PublicRoomJoinRule?.map(): RoomDescription.JoinRule { return when (this) { PublicRoomJoinRule.PUBLIC -> RoomDescription.JoinRule.PUBLIC PublicRoomJoinRule.KNOCK -> RoomDescription.JoinRule.KNOCK + PublicRoomJoinRule.RESTRICTED -> RoomDescription.JoinRule.RESTRICTED + PublicRoomJoinRule.KNOCK_RESTRICTED -> RoomDescription.JoinRule.KNOCK_RESTRICTED + PublicRoomJoinRule.INVITE -> RoomDescription.JoinRule.INVITE null -> RoomDescription.JoinRule.UNKNOWN } } diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt index 404de3a248..00f89e39a3 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt @@ -23,7 +23,6 @@ import io.element.android.libraries.matrix.api.room.IntentionalMention import io.element.android.libraries.matrix.api.room.JoinedRoom 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.message.ReplyParameters 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.Timeline @@ -37,7 +36,6 @@ import io.element.android.libraries.matrix.impl.media.toMSC3246range import io.element.android.libraries.matrix.impl.poll.toInner import io.element.android.libraries.matrix.impl.room.RoomContentForwarder import io.element.android.libraries.matrix.impl.room.location.toInner -import io.element.android.libraries.matrix.impl.room.message.map import io.element.android.libraries.matrix.impl.timeline.item.event.EventTimelineItemMapper import io.element.android.libraries.matrix.impl.timeline.item.event.TimelineEventContentMapper import io.element.android.libraries.matrix.impl.timeline.item.virtual.VirtualTimelineItemMapper @@ -206,18 +204,18 @@ class RustTimeline( _timelineItems, backwardPaginationStatus, forwardPaginationStatus, - joinedRoom.roomInfoFlow.map { it.creator to it.isDm }.distinctUntilChanged(), + joinedRoom.roomInfoFlow.map { it.creators to it.isDm }.distinctUntilChanged(), ) { timelineItems, backwardPaginationStatus, forwardPaginationStatus, - (roomCreator, isDm) -> + (roomCreators, isDm) -> withContext(dispatcher) { timelineItems .let { items -> roomBeginningPostProcessor.process( items = items, isDm = isDm, - roomCreator = roomCreator, + roomCreator = roomCreators.firstOrNull(), hasMoreToLoadBackwards = backwardPaginationStatus.hasMoreToLoad, ) } @@ -320,7 +318,7 @@ class RustTimeline( } override suspend fun replyMessage( - replyParameters: ReplyParameters, + repliedToEventId: EventId, body: String, htmlBody: String?, intentionalMentions: List, @@ -330,7 +328,7 @@ class RustTimeline( val msg = MessageEventContent.from(body, htmlBody, intentionalMentions) inner.sendReply( msg = msg, - replyParams = replyParameters.map(), + eventId = repliedToEventId.value, ) } } @@ -342,7 +340,7 @@ class RustTimeline( caption: String?, formattedCaption: String?, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId?, ): Result { val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue) return sendAttachment(listOfNotNull(file, thumbnailFile)) { @@ -355,7 +353,7 @@ class RustTimeline( }, useSendQueue = useSendQueue, mentions = null, - replyParams = replyParameters?.map(), + inReplyTo = inReplyToEventId?.value, ), thumbnailPath = thumbnailFile?.path, imageInfo = imageInfo.map(), @@ -371,7 +369,7 @@ class RustTimeline( caption: String?, formattedCaption: String?, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId?, ): Result { val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue) return sendAttachment(listOfNotNull(file, thumbnailFile)) { @@ -384,7 +382,7 @@ class RustTimeline( }, useSendQueue = useSendQueue, mentions = null, - replyParams = replyParameters?.map(), + inReplyTo = inReplyToEventId?.value, ), thumbnailPath = thumbnailFile?.path, videoInfo = videoInfo.map(), @@ -399,7 +397,7 @@ class RustTimeline( caption: String?, formattedCaption: String?, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId?, ): Result { val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue) return sendAttachment(listOf(file)) { @@ -412,7 +410,7 @@ class RustTimeline( }, useSendQueue = useSendQueue, mentions = null, - replyParams = replyParameters?.map(), + inReplyTo = inReplyToEventId?.value, ), audioInfo = audioInfo.map(), progressWatcher = progressCallback?.toProgressWatcher() @@ -426,7 +424,7 @@ class RustTimeline( caption: String?, formattedCaption: String?, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId?, ): Result { val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue) return sendAttachment(listOf(file)) { @@ -439,7 +437,7 @@ class RustTimeline( }, useSendQueue = useSendQueue, mentions = null, - replyParams = replyParameters?.map(), + inReplyTo = inReplyToEventId?.value, ), fileInfo = fileInfo.map(), progressWatcher = progressCallback?.toProgressWatcher(), @@ -470,7 +468,7 @@ class RustTimeline( description: String?, zoomLevel: Int?, assetType: AssetType?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId?, ): Result = withContext(dispatcher) { runCatchingExceptions { inner.sendLocation( @@ -479,7 +477,7 @@ class RustTimeline( description = description, zoomLevel = zoomLevel?.toUByte(), assetType = assetType?.toInner(), - replyParams = replyParameters?.map(), + repliedToEventId = inReplyToEventId?.value, ) } } @@ -489,7 +487,7 @@ class RustTimeline( audioInfo: AudioInfo, waveform: List, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId?, ): Result { val useSendQueue = featureFlagsService.isFeatureEnabled(FeatureFlags.MediaUploadOnSendQueue) return sendAttachment(listOf(file)) { @@ -501,7 +499,7 @@ class RustTimeline( formattedCaption = null, useSendQueue = useSendQueue, mentions = null, - replyParams = replyParameters?.map(), + inReplyTo = inReplyToEventId?.value, ), audioInfo = audioInfo.map(), waveform = waveform.toMSC3246range(), diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomInfo.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomInfo.kt index 9ba3c100a7..5596d44b48 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomInfo.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomInfo.kt @@ -7,7 +7,6 @@ package io.element.android.libraries.matrix.impl.fixtures.factories -import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.impl.fixtures.fakes.FakeFfiRoomPowerLevels import io.element.android.libraries.matrix.test.A_ROOM_ID import io.element.android.libraries.matrix.test.A_ROOM_NAME @@ -52,7 +51,7 @@ fun aRustRoomInfo( numUnreadNotifications: ULong = 0uL, numUnreadMentions: ULong = 0uL, pinnedEventIds: List = listOf(), - roomCreator: UserId? = null, + roomCreators: List? = emptyList(), joinRule: JoinRule? = null, historyVisibility: RoomHistoryVisibility = RoomHistoryVisibility.Joined, successorRoom: SuccessorRoom? = null, @@ -86,7 +85,7 @@ fun aRustRoomInfo( numUnreadNotifications = numUnreadNotifications, numUnreadMentions = numUnreadMentions, pinnedEventIds = pinnedEventIds, - creator = roomCreator?.value, + creators = roomCreators, joinRule = joinRule, historyVisibility = historyVisibility, successorRoom = successorRoom, diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomMember.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomMember.kt index a38496222c..2922762548 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomMember.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/fixtures/factories/RoomMember.kt @@ -9,6 +9,7 @@ package io.element.android.libraries.matrix.impl.fixtures.factories import io.element.android.libraries.matrix.api.core.UserId import org.matrix.rustcomponents.sdk.MembershipState +import org.matrix.rustcomponents.sdk.PowerLevel import org.matrix.rustcomponents.sdk.RoomMember import uniffi.matrix_sdk.RoomMemberRole @@ -18,7 +19,7 @@ fun aRustRoomMember( avatarUrl: String? = null, membership: MembershipState = MembershipState.Join, isNameAmbiguous: Boolean = false, - powerLevel: Long = 0L, + powerLevel: PowerLevel = PowerLevel.Value(0L), isIgnored: Boolean = false, role: RoomMemberRole = RoomMemberRole.USER, membershipChangeReason: String? = null, diff --git a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapperTest.kt b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapperTest.kt index 7140a90dc4..d170bff029 100644 --- a/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapperTest.kt +++ b/libraries/matrix/impl/src/test/kotlin/io/element/android/libraries/matrix/impl/room/RoomInfoMapperTest.kt @@ -31,6 +31,7 @@ import io.element.android.libraries.matrix.test.A_USER_ID_3 import io.element.android.libraries.matrix.test.A_USER_ID_6 import io.element.android.libraries.matrix.test.room.aRoomMember import io.element.android.libraries.matrix.test.room.defaultRoomPowerLevelValues +import kotlinx.collections.immutable.persistentListOf import kotlinx.collections.immutable.persistentMapOf import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toPersistentList @@ -78,7 +79,7 @@ class RoomInfoMapperTest { numUnreadNotifications = 13uL, numUnreadMentions = 14uL, pinnedEventIds = listOf(AN_EVENT_ID.value), - roomCreator = A_USER_ID, + roomCreators = listOf(A_USER_ID.value), historyVisibility = RustRoomHistoryVisibility.Joined, ) ) @@ -119,7 +120,7 @@ class RoomInfoMapperTest { ) ).toImmutableList(), pinnedEventIds = listOf(AN_EVENT_ID).toPersistentList(), - creator = A_USER_ID, + creators = persistentListOf(A_USER_ID), isMarkedUnread = false, numUnreadMessages = 12L, numUnreadNotifications = 13L, @@ -166,7 +167,7 @@ class RoomInfoMapperTest { numUnreadNotifications = 13uL, numUnreadMentions = 14uL, pinnedEventIds = emptyList(), - roomCreator = null, + roomCreators = null, ) ) ).isEqualTo( @@ -201,7 +202,7 @@ class RoomInfoMapperTest { activeRoomCallParticipants = emptyList().toImmutableList(), heroes = emptyList().toImmutableList(), pinnedEventIds = emptyList().toPersistentList(), - creator = null, + creators = persistentListOf(), isMarkedUnread = true, numUnreadMessages = 12L, numUnreadNotifications = 13L, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt index 9b609b39c7..a36704c342 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/FakeMatrixClient.kt @@ -84,7 +84,7 @@ class FakeMatrixClient( private val getNotJoinedRoomResult: (RoomIdOrAlias, List) -> Result = { _, _ -> lambdaError() }, private val clearCacheLambda: () -> Unit = { lambdaError() }, private val userIdServerNameLambda: () -> String = { lambdaError() }, - private val getUrlLambda: (String) -> Result = { lambdaError() }, + private val getUrlLambda: (String) -> Result = { lambdaError() }, private val canDeactivateAccountResult: () -> Boolean = { lambdaError() }, private val deactivateAccountResult: (String, Boolean) -> Result = { _, _ -> lambdaError() }, private val currentSlidingSyncVersionLambda: () -> Result = { lambdaError() }, @@ -324,7 +324,7 @@ class FakeMatrixClient( return userIdServerNameLambda() } - override suspend fun getUrl(url: String): Result { + override suspend fun getUrl(url: String): Result { return getUrlLambda(url) } diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt index d7f9ccd6be..c46173d86a 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomInfoFixture.kt @@ -59,7 +59,7 @@ fun aRoomInfo( activeRoomCallParticipants: List = emptyList(), heroes: List = emptyList(), pinnedEventIds: List = emptyList(), - roomCreator: UserId? = null, + roomCreators: List = emptyList(), isMarkedUnread: Boolean = false, numUnreadMessages: Long = 0, numUnreadNotifications: Long = 0, @@ -93,7 +93,7 @@ fun aRoomInfo( activeRoomCallParticipants = activeRoomCallParticipants.toImmutableList(), heroes = heroes.toImmutableList(), pinnedEventIds = pinnedEventIds.toImmutableList(), - creator = roomCreator, + creators = roomCreators.toImmutableList(), isMarkedUnread = isMarkedUnread, numUnreadMessages = numUnreadMessages, numUnreadNotifications = numUnreadNotifications, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt index aab27e8a11..f415172cc2 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/RoomSummaryFixture.kt @@ -31,6 +31,7 @@ import io.element.android.libraries.matrix.test.A_ROOM_TOPIC import io.element.android.libraries.matrix.test.A_USER_ID import io.element.android.libraries.matrix.test.timeline.anEventTimelineItem import kotlinx.collections.immutable.persistentMapOf +import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toPersistentList fun aRoomSummary( @@ -72,7 +73,7 @@ fun aRoomSummary( activeRoomCallParticipants: List = emptyList(), heroes: List = emptyList(), pinnedEventIds: List = emptyList(), - roomCreator: UserId? = null, + roomCreators: List = emptyList(), isMarkedUnread: Boolean = false, numUnreadMessages: Long = 0, numUnreadNotifications: Long = 0, @@ -94,7 +95,7 @@ fun aRoomSummary( successorRoom = successorRoom, isFavorite = isFavorite, canonicalAlias = canonicalAlias, - alternativeAliases = alternativeAliases.toPersistentList(), + alternativeAliases = alternativeAliases.toImmutableList(), currentUserMembership = currentUserMembership, inviter = inviter, activeMembersCount = activeMembersCount, @@ -105,10 +106,10 @@ fun aRoomSummary( notificationCount = notificationCount, userDefinedNotificationMode = userDefinedNotificationMode, hasRoomCall = hasRoomCall, - activeRoomCallParticipants = activeRoomCallParticipants.toPersistentList(), + activeRoomCallParticipants = activeRoomCallParticipants.toImmutableList(), heroes = heroes.toPersistentList(), - pinnedEventIds = pinnedEventIds.toPersistentList(), - creator = roomCreator, + pinnedEventIds = pinnedEventIds.toImmutableList(), + creators = roomCreators.toImmutableList(), isMarkedUnread = isMarkedUnread, numUnreadMessages = numUnreadMessages, numUnreadNotifications = numUnreadNotifications, diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt index c4ab0160c4..dd0bf02d4d 100644 --- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt +++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/timeline/FakeTimeline.kt @@ -19,7 +19,6 @@ 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.room.IntentionalMention import io.element.android.libraries.matrix.api.room.location.AssetType -import io.element.android.libraries.matrix.api.room.message.ReplyParameters 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.Timeline @@ -122,7 +121,7 @@ class FakeTimeline( ) var replyMessageLambda: ( - replyParameters: ReplyParameters, + inReplyToEventId: EventId?, body: String, htmlBody: String?, intentionalMentions: List, @@ -132,13 +131,13 @@ class FakeTimeline( } override suspend fun replyMessage( - replyParameters: ReplyParameters, + repliedToEventId: EventId, body: String, htmlBody: String?, intentionalMentions: List, fromNotification: Boolean, ): Result = replyMessageLambda( - replyParameters, + repliedToEventId, body, htmlBody, intentionalMentions, @@ -152,7 +151,7 @@ class FakeTimeline( body: String?, formattedBody: String?, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId??, ) -> Result = { _, _, _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) } @@ -164,7 +163,7 @@ class FakeTimeline( caption: String?, formattedCaption: String?, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId??, ): Result = simulateLongTask { simulateSendMediaProgress(progressCallback) sendImageLambda( @@ -174,7 +173,7 @@ class FakeTimeline( caption, formattedCaption, progressCallback, - replyParameters, + inReplyToEventId, ) } @@ -185,7 +184,7 @@ class FakeTimeline( body: String?, formattedBody: String?, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId??, ) -> Result = { _, _, _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) } @@ -197,7 +196,7 @@ class FakeTimeline( caption: String?, formattedCaption: String?, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId??, ): Result = simulateLongTask { simulateSendMediaProgress(progressCallback) sendVideoLambda( @@ -207,7 +206,7 @@ class FakeTimeline( caption, formattedCaption, progressCallback, - replyParameters, + inReplyToEventId, ) } @@ -217,7 +216,7 @@ class FakeTimeline( caption: String?, formattedCaption: String?, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId??, ) -> Result = { _, _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) } @@ -228,7 +227,7 @@ class FakeTimeline( caption: String?, formattedCaption: String?, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId??, ): Result = simulateLongTask { simulateSendMediaProgress(progressCallback) sendAudioLambda( @@ -237,7 +236,7 @@ class FakeTimeline( caption, formattedCaption, progressCallback, - replyParameters, + inReplyToEventId, ) } @@ -247,7 +246,7 @@ class FakeTimeline( caption: String?, formattedCaption: String?, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId??, ) -> Result = { _, _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) } @@ -258,7 +257,7 @@ class FakeTimeline( caption: String?, formattedCaption: String?, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId??, ): Result = simulateLongTask { simulateSendMediaProgress(progressCallback) sendFileLambda( @@ -267,7 +266,7 @@ class FakeTimeline( caption, formattedCaption, progressCallback, - replyParameters, + inReplyToEventId, ) } @@ -276,7 +275,7 @@ class FakeTimeline( audioInfo: AudioInfo, waveform: List, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId??, ) -> Result = { _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) } @@ -286,7 +285,7 @@ class FakeTimeline( audioInfo: AudioInfo, waveform: List, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId??, ): Result = simulateLongTask { simulateSendMediaProgress(progressCallback) sendVoiceMessageLambda( @@ -294,7 +293,7 @@ class FakeTimeline( audioInfo, waveform, progressCallback, - replyParameters, + inReplyToEventId, ) } @@ -304,7 +303,7 @@ class FakeTimeline( description: String?, zoomLevel: Int?, assetType: AssetType?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId??, ) -> Result = { _, _, _, _, _, _ -> lambdaError() } @@ -315,7 +314,7 @@ class FakeTimeline( description: String?, zoomLevel: Int?, assetType: AssetType?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId??, ): Result = simulateLongTask { sendLocationLambda( body, @@ -323,7 +322,7 @@ class FakeTimeline( description, zoomLevel, assetType, - replyParameters, + inReplyToEventId, ) } diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/RoomInfoExtension.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/RoomInfoExtension.kt index 14e64c3557..d6d53a3321 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/RoomInfoExtension.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/model/RoomInfoExtension.kt @@ -9,7 +9,9 @@ package io.element.android.libraries.matrix.ui.model import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarSize +import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.room.RoomInfo +import io.element.android.libraries.matrix.api.room.RoomMember fun RoomInfo.getAvatarData(size: AvatarSize) = AvatarData( id = id.value, @@ -17,3 +19,17 @@ fun RoomInfo.getAvatarData(size: AvatarSize) = AvatarData( url = avatarUrl, size = size, ) + +/** + * Returns the role of the user in the room. + * If the user is a creator, returns [RoomMember.Role.CREATOR]. + * Otherwise, checks the power levels and returns the corresponding role. + * If no specific power level is set for the user, defaults to [RoomMember.Role.USER]. + */ +fun RoomInfo.roleOf(userId: UserId): RoomMember.Role { + return if (creators.contains(userId)) { + RoomMember.Role.CREATOR + } else { + roomPowerLevels?.roleOf(userId) ?: RoomMember.Role.USER + } +} diff --git a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt index 0080eea700..f4b372df5f 100644 --- a/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt +++ b/libraries/matrixui/src/main/kotlin/io/element/android/libraries/matrix/ui/room/MatrixRoomState.kt @@ -23,6 +23,7 @@ import io.element.android.libraries.matrix.api.room.powerlevels.canKick import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOther import io.element.android.libraries.matrix.api.room.powerlevels.canRedactOwn import io.element.android.libraries.matrix.api.room.powerlevels.canSendMessage +import io.element.android.libraries.matrix.ui.model.roleOf @Composable fun BaseRoom.canSendMessageAsState(type: MessageEventType, updateKey: Long): State { @@ -106,8 +107,8 @@ fun BaseRoom.userPowerLevelAsState(updateKey: Long): State { @Composable fun BaseRoom.isOwnUserAdmin(): Boolean { val roomInfo by roomInfoFlow.collectAsState() - val powerLevel = roomInfo.roomPowerLevels?.users?.get(sessionId) ?: 0L - return RoomMember.Role.forPowerLevel(powerLevel) == RoomMember.Role.ADMIN + val role = roomInfo.roleOf(sessionId) + return role == RoomMember.Role.ADMIN || role == RoomMember.Role.CREATOR } @Composable diff --git a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt index 80df147dc0..0039f87963 100644 --- a/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt +++ b/libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt @@ -9,10 +9,10 @@ package io.element.android.libraries.mediaupload.api import android.net.Uri import io.element.android.libraries.core.extensions.flatMapCatching +import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.media.MediaUploadHandler import io.element.android.libraries.matrix.api.room.JoinedRoom -import io.element.android.libraries.matrix.api.room.message.ReplyParameters import io.element.android.libraries.matrix.api.timeline.Timeline import io.element.android.libraries.preferences.api.store.SessionPreferencesStore import kotlinx.coroutines.CancellationException @@ -48,14 +48,14 @@ class MediaSender @Inject constructor( caption: String?, formattedCaption: String?, progressCallback: ProgressCallback?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId?, ): Result { return room.liveTimeline.sendMedia( uploadInfo = mediaUploadInfo, progressCallback = progressCallback, caption = caption, formattedCaption = formattedCaption, - replyParameters = replyParameters, + inReplyToEventId = inReplyToEventId, ) .handleSendResult() } @@ -66,7 +66,7 @@ class MediaSender @Inject constructor( caption: String? = null, formattedCaption: String? = null, progressCallback: ProgressCallback? = null, - replyParameters: ReplyParameters? = null, + inReplyToEventId: EventId? = null, ): Result { val compressIfPossible = sessionPreferencesStore.doesCompressMedia().first() return preProcessor @@ -82,7 +82,7 @@ class MediaSender @Inject constructor( progressCallback = progressCallback, caption = caption, formattedCaption = formattedCaption, - replyParameters = replyParameters, + inReplyToEventId = inReplyToEventId, ) } .handleSendResult() @@ -93,7 +93,7 @@ class MediaSender @Inject constructor( mimeType: String, waveForm: List, progressCallback: ProgressCallback? = null, - replyParameters: ReplyParameters? = null, + inReplyToEventId: EventId? = null, ): Result { return preProcessor .process( @@ -114,7 +114,7 @@ class MediaSender @Inject constructor( progressCallback = progressCallback, caption = null, formattedCaption = null, - replyParameters = replyParameters, + inReplyToEventId = inReplyToEventId, ) } .handleSendResult() @@ -136,7 +136,7 @@ class MediaSender @Inject constructor( progressCallback: ProgressCallback?, caption: String?, formattedCaption: String?, - replyParameters: ReplyParameters?, + inReplyToEventId: EventId?, ): Result { val handler = when (uploadInfo) { is MediaUploadInfo.Image -> { @@ -147,7 +147,7 @@ class MediaSender @Inject constructor( caption = caption, formattedCaption = formattedCaption, progressCallback = progressCallback, - replyParameters = replyParameters, + inReplyToEventId = inReplyToEventId, ) } is MediaUploadInfo.Video -> { @@ -158,7 +158,7 @@ class MediaSender @Inject constructor( caption = caption, formattedCaption = formattedCaption, progressCallback = progressCallback, - replyParameters = replyParameters, + inReplyToEventId = inReplyToEventId, ) } is MediaUploadInfo.Audio -> { @@ -168,7 +168,7 @@ class MediaSender @Inject constructor( caption = caption, formattedCaption = formattedCaption, progressCallback = progressCallback, - replyParameters = replyParameters, + inReplyToEventId = inReplyToEventId, ) } is MediaUploadInfo.VoiceMessage -> { @@ -177,7 +177,7 @@ class MediaSender @Inject constructor( audioInfo = uploadInfo.audioInfo, waveform = uploadInfo.waveform, progressCallback = progressCallback, - replyParameters = replyParameters, + inReplyToEventId = inReplyToEventId, ) } is MediaUploadInfo.AnyFile -> { @@ -187,7 +187,7 @@ class MediaSender @Inject constructor( caption = caption, formattedCaption = formattedCaption, progressCallback = progressCallback, - replyParameters = replyParameters, + inReplyToEventId = inReplyToEventId, ) } } diff --git a/libraries/mediaupload/api/src/test/kotlin/io/element/android/libraries/mediaupload/api/MediaSenderTest.kt b/libraries/mediaupload/api/src/test/kotlin/io/element/android/libraries/mediaupload/api/MediaSenderTest.kt index 7b10e84816..fd6110f700 100644 --- a/libraries/mediaupload/api/src/test/kotlin/io/element/android/libraries/mediaupload/api/MediaSenderTest.kt +++ b/libraries/mediaupload/api/src/test/kotlin/io/element/android/libraries/mediaupload/api/MediaSenderTest.kt @@ -10,11 +10,11 @@ package io.element.android.libraries.mediaupload.api import android.net.Uri import com.google.common.truth.Truth.assertThat import io.element.android.libraries.core.mimetype.MimeTypes +import io.element.android.libraries.matrix.api.core.EventId import io.element.android.libraries.matrix.api.core.ProgressCallback import io.element.android.libraries.matrix.api.media.FileInfo import io.element.android.libraries.matrix.api.media.ImageInfo import io.element.android.libraries.matrix.api.room.JoinedRoom -import io.element.android.libraries.matrix.api.room.message.ReplyParameters import io.element.android.libraries.matrix.test.media.FakeMediaUploadHandler import io.element.android.libraries.matrix.test.room.FakeJoinedRoom import io.element.android.libraries.matrix.test.timeline.FakeTimeline @@ -47,7 +47,7 @@ class MediaSenderTest { String?, String?, ProgressCallback?, - ReplyParameters?, + EventId?, Result, > { _, _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) @@ -65,7 +65,7 @@ class MediaSenderTest { @Test fun `given an attachment when sending it the Room will call sendMedia`() = runTest { val sendImageResult = - lambdaRecorder { _: File, _: File?, _: ImageInfo, _: String?, _: String?, _: ProgressCallback?, _: ReplyParameters? -> + lambdaRecorder { _: File, _: File?, _: ImageInfo, _: String?, _: String?, _: ProgressCallback?, _: EventId? -> Result.success(FakeMediaUploadHandler()) } val room = FakeJoinedRoom( @@ -98,7 +98,7 @@ class MediaSenderTest { givenImageResult() } val sendImageResult = - lambdaRecorder { _: File, _: File?, _: ImageInfo, _: String?, _: String?, _: ProgressCallback?, _: ReplyParameters? -> + lambdaRecorder { _: File, _: File?, _: ImageInfo, _: String?, _: String?, _: ProgressCallback?, _: EventId? -> Result.failure(Exception()) } val room = FakeJoinedRoom( @@ -121,7 +121,7 @@ class MediaSenderTest { @Test fun `given a cancellation in the media upload when sending the job is cancelled`() = runTest(StandardTestDispatcher()) { val sendFileResult = - lambdaRecorder> { _, _, _, _, _, _ -> + lambdaRecorder> { _, _, _, _, _, _ -> Result.success(FakeMediaUploadHandler()) } val room = FakeJoinedRoom( diff --git a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandler.kt b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandler.kt index fab6e3a53a..b664991dfb 100644 --- a/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandler.kt +++ b/libraries/push/impl/src/main/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandler.kt @@ -17,7 +17,6 @@ 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.room.JoinedRoom import io.element.android.libraries.matrix.api.room.isDm -import io.element.android.libraries.matrix.api.room.message.replyInThread import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.preferences.api.store.SessionPreferencesStoreFactory import io.element.android.libraries.push.api.notifications.NotificationCleaner @@ -173,7 +172,7 @@ class NotificationBroadcastReceiverHandler @Inject constructor( htmlBody = null, intentionalMentions = emptyList(), fromNotification = true, - replyParameters = replyInThread(replyToEventId), + repliedToEventId = replyToEventId, ) } else { room.liveTimeline.sendMessage( diff --git a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandlerTest.kt b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandlerTest.kt index 3fd94863a1..7a6f6b0118 100644 --- a/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandlerTest.kt +++ b/libraries/push/impl/src/test/kotlin/io/element/android/libraries/push/impl/notifications/NotificationBroadcastReceiverHandlerTest.kt @@ -15,8 +15,6 @@ 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.room.IntentionalMention import io.element.android.libraries.matrix.api.room.RoomInfo -import io.element.android.libraries.matrix.api.room.message.ReplyParameters -import io.element.android.libraries.matrix.api.room.message.replyInThread import io.element.android.libraries.matrix.api.timeline.ReceiptType import io.element.android.libraries.matrix.test.AN_EVENT_ID import io.element.android.libraries.matrix.test.A_MESSAGE @@ -342,7 +340,7 @@ class NotificationBroadcastReceiverHandlerTest { fun `Test send reply`() = runTest { val sendMessage = lambdaRecorder, Result> { _, _, _ -> Result.success(Unit) } val replyMessage = - lambdaRecorder, Boolean, Result> { _, _, _, _, _ -> Result.success(Unit) } + lambdaRecorder, Boolean, Result> { _, _, _, _, _ -> Result.success(Unit) } val liveTimeline = FakeTimeline().apply { sendMessageLambda = sendMessage replyMessageLambda = replyMessage @@ -409,7 +407,7 @@ class NotificationBroadcastReceiverHandlerTest { fun `Test send reply to thread`() = runTest { val sendMessage = lambdaRecorder, Result> { _, _, _ -> Result.success(Unit) } val replyMessage = - lambdaRecorder, Boolean, Result> { _, _, _, _, _ -> Result.success(Unit) } + lambdaRecorder, Boolean, Result> { _, _, _, _, _ -> Result.success(Unit) } val liveTimeline = FakeTimeline().apply { sendMessageLambda = sendMessage replyMessageLambda = replyMessage @@ -448,7 +446,7 @@ class NotificationBroadcastReceiverHandlerTest { replyMessage.assertions() .isCalledOnce() .with( - value(replyInThread(eventId = AN_EVENT_ID, explicitReply = false)), + value(AN_EVENT_ID), value(A_MESSAGE), value(null), value(emptyList()), diff --git a/tools/localazy/checkForbiddenTerms.py b/tools/localazy/checkForbiddenTerms.py index 2ebe582ba3..dc522ebf33 100755 --- a/tools/localazy/checkForbiddenTerms.py +++ b/tools/localazy/checkForbiddenTerms.py @@ -26,6 +26,9 @@ forbiddenTerms = { "call_invalid_audio_device_bluetooth_devices_disabled", # Contains "Element X" "screen_room_timeline_legacy_call", + # We explicitly want to mention Element Pro in these 2: + "screen_change_server_error_element_pro_required_title", + "screen_change_server_error_element_pro_required_message", ] }