Update dependency org.matrix.rustcomponents:sdk-android to v25.7.23 (#5073)
* Update dependency org.matrix.rustcomponents:sdk-android to v25.7.23
* Adapt to SDK changes:
- Add 'creator' role, adapt existing logic to it.
- Remove `ReplyParameters`, replace with `EventId` where possible.
- Fix changes in OIDC auth methods.
- Add more join rules.
* Make sure both creators and users with power level >= 150 are displayed as 'owners' in the room member list.
* Don't close the roles and permissions screen if the user is a creator
* Use `MediaPreviewValue.DEFAULT` for `MediaPreviewConfig.DEFAULT` too
* Improve APIs around checking roles and power levels:
- Ensure `RoomInfo.RoomPowerLevels.users` can't be directly used to check power levels since it can't check the power levels for creators.
- Add a few helper functions to handle actions that relied on the previous `users` property, and docs to explain their usages.
---------
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
Co-authored-by: Jorge Martín <jorgem@element.io>
This commit is contained in:
parent
33aa7a914f
commit
040fde7f22
57 changed files with 264 additions and 227 deletions
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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<String, String, String?, Int?, AssetType?, ReplyParameters?, Result<Unit>> { _, _, _, _, _, _ ->
|
||||
val sendLocationResult = lambdaRecorder<String, String, String?, Int?, AssetType?, EventId?, Result<Unit>> { _, _, _, _, _, _ ->
|
||||
Result.success(Unit)
|
||||
}
|
||||
val joinedRoom = FakeJoinedRoom(
|
||||
|
|
@ -328,7 +328,7 @@ class SendLocationPresenterTest {
|
|||
|
||||
@Test
|
||||
fun `share pin location`() = runTest {
|
||||
val sendLocationResult = lambdaRecorder<String, String, String?, Int?, AssetType?, ReplyParameters?, Result<Unit>> { _, _, _, _, _, _ ->
|
||||
val sendLocationResult = lambdaRecorder<String, String, String?, Int?, AssetType?, EventId?, Result<Unit>> { _, _, _, _, _, _ ->
|
||||
Result.success(Unit)
|
||||
}
|
||||
val joinedRoom = FakeJoinedRoom(
|
||||
|
|
@ -392,7 +392,7 @@ class SendLocationPresenterTest {
|
|||
|
||||
@Test
|
||||
fun `composer context passes through analytics`() = runTest {
|
||||
val sendLocationResult = lambdaRecorder<String, String, String?, Int?, AssetType?, ReplyParameters?, Result<Unit>> { _, _, _, _, _, _ ->
|
||||
val sendLocationResult = lambdaRecorder<String, String, String?, Int?, AssetType?, EventId?, Result<Unit>> { _, _, _, _, _, _ ->
|
||||
Result.success(Unit)
|
||||
}
|
||||
val joinedRoom = FakeJoinedRoom(
|
||||
|
|
|
|||
|
|
@ -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<SendActionState>,
|
||||
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 = {
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 = {},
|
||||
|
|
|
|||
|
|
@ -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<File, FileInfo, String?, String?, ProgressCallback?, ReplyParameters?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ ->
|
||||
lambdaRecorder<File, FileInfo, String?, String?, ProgressCallback?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ ->
|
||||
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<File, FileInfo, String?, String?, ProgressCallback?, ReplyParameters?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ ->
|
||||
lambdaRecorder<File, FileInfo, String?, String?, ProgressCallback?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ ->
|
||||
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<File, FileInfo, String?, String?, ProgressCallback?, ReplyParameters?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ ->
|
||||
lambdaRecorder<File, FileInfo, String?, String?, ProgressCallback?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ ->
|
||||
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<File, AudioInfo, String?, String?, ProgressCallback?, ReplyParameters?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ ->
|
||||
lambdaRecorder<File, AudioInfo, String?, String?, ProgressCallback?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ ->
|
||||
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<File, FileInfo, String?, String?, ProgressCallback?, ReplyParameters?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ ->
|
||||
lambdaRecorder<File, FileInfo, String?, String?, ProgressCallback?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ ->
|
||||
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<File, FileInfo, String?, String?, ProgressCallback?, ReplyParameters?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ ->
|
||||
lambdaRecorder<File, FileInfo, String?, String?, ProgressCallback?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ ->
|
||||
Result.failure(failure)
|
||||
}
|
||||
val onDoneListenerResult = lambdaRecorder<Unit> {}
|
||||
|
|
|
|||
|
|
@ -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<IntentionalMention>, _: Boolean ->
|
||||
val replyMessageLambda = lambdaRecorder { _: EventId?, _: String, _: String?, _: List<IntentionalMention>, _: 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<IntentionalMention>, _: Boolean ->
|
||||
val replyMessageLambda = lambdaRecorder { _: EventId?, _: String, _: String?, _: List<IntentionalMention>, _: Boolean ->
|
||||
Result.success(Unit)
|
||||
}
|
||||
val editMessageLambda = lambdaRecorder { _: EventOrTransactionId, _: String, _: String?, _: List<IntentionalMention> ->
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<File, AudioInfo, List<Float>, ProgressCallback?, ReplyParameters?, Result<FakeMediaUploadHandler>> { _, _, _, _, _ ->
|
||||
lambdaRecorder<File, AudioInfo, List<Float>, ProgressCallback?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _ ->
|
||||
Result.success(FakeMediaUploadHandler())
|
||||
}
|
||||
private val joinedRoom = FakeJoinedRoom(
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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() }
|
||||
|
|
|
|||
|
|
@ -110,8 +110,6 @@ class RolesAndPermissionsPresenter @Inject constructor(
|
|||
}
|
||||
|
||||
private fun RoomInfo.userCountWithRole(userIds: List<UserId>, 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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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) })
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -81,6 +81,7 @@
|
|||
<string name="screen_room_member_list_pending_header_title">"Pending"</string>
|
||||
<string name="screen_room_member_list_role_administrator">"Admin"</string>
|
||||
<string name="screen_room_member_list_role_moderator">"Moderator"</string>
|
||||
<string name="screen_room_member_list_role_owner">"Owner"</string>
|
||||
<string name="screen_room_member_list_room_members_header_title">"Room members"</string>
|
||||
<string name="screen_room_member_list_unbanning_user">"Unbanning %1$s"</string>
|
||||
<string name="screen_room_notification_settings_allow_custom">"Allow custom setting"</string>
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ data class RoomDescription(
|
|||
enum class JoinRule {
|
||||
PUBLIC,
|
||||
KNOCK,
|
||||
RESTRICTED,
|
||||
KNOCK_RESTRICTED,
|
||||
INVITE,
|
||||
UNKNOWN
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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<File, FileInfo, String?, String?, ProgressCallback?, ReplyParameters?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ ->
|
||||
lambdaRecorder<File, FileInfo, String?, String?, ProgressCallback?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ ->
|
||||
Result.success(FakeMediaUploadHandler())
|
||||
}
|
||||
val joinedRoom = FakeJoinedRoom(
|
||||
|
|
|
|||
|
|
@ -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" }
|
||||
|
|
|
|||
|
|
@ -142,7 +142,7 @@ interface MatrixClient {
|
|||
/**
|
||||
* Execute generic GET requests through the SDKs internal HTTP client.
|
||||
*/
|
||||
suspend fun getUrl(url: String): Result<String>
|
||||
suspend fun getUrl(url: String): Result<ByteArray>
|
||||
|
||||
/**
|
||||
* 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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -72,10 +72,21 @@ data class RoomInfo(
|
|||
val numUnreadMentions: Long,
|
||||
val heroes: ImmutableList<MatrixUser>,
|
||||
val pinnedEventIds: ImmutableList<EventId>,
|
||||
val creator: UserId?,
|
||||
val creators: ImmutableList<UserId>,
|
||||
val historyVisibility: RoomHistoryVisibility,
|
||||
val successorRoom: SuccessorRoom?,
|
||||
) {
|
||||
val aliases: List<RoomAlias>
|
||||
get() = listOfNotNull(canonicalAlias) + alternativeAliases
|
||||
|
||||
/**
|
||||
* Returns the list of users with the given [role] in this room.
|
||||
*/
|
||||
fun usersWithRole(role: RoomMember.Role): List<UserId> {
|
||||
return if (role == RoomMember.Role.CREATOR) {
|
||||
this.creators
|
||||
} else {
|
||||
this.roomPowerLevels?.usersWithRole(role).orEmpty().toList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
@ -22,10 +22,10 @@ import kotlinx.coroutines.flow.map
|
|||
*/
|
||||
fun BaseRoom.usersWithRole(role: RoomMember.Role): Flow<ImmutableList<RoomMember>> {
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -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<UserId, Long>,
|
||||
)
|
||||
private val users: ImmutableMap<UserId, Long>,
|
||||
) {
|
||||
/**
|
||||
* 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<UserId> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ data class RoomDescription(
|
|||
enum class JoinRule {
|
||||
PUBLIC,
|
||||
KNOCK,
|
||||
RESTRICTED,
|
||||
KNOCK_RESTRICTED,
|
||||
INVITE,
|
||||
UNKNOWN
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Unit>
|
||||
|
||||
suspend fun replyMessage(
|
||||
replyParameters: ReplyParameters,
|
||||
repliedToEventId: EventId,
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
|
|
@ -90,7 +89,7 @@ interface Timeline : AutoCloseable {
|
|||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
inReplyToEventId: EventId?,
|
||||
): Result<MediaUploadHandler>
|
||||
|
||||
suspend fun sendVideo(
|
||||
|
|
@ -100,7 +99,7 @@ interface Timeline : AutoCloseable {
|
|||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
inReplyToEventId: EventId?,
|
||||
): Result<MediaUploadHandler>
|
||||
|
||||
suspend fun sendAudio(
|
||||
|
|
@ -109,7 +108,7 @@ interface Timeline : AutoCloseable {
|
|||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
inReplyToEventId: EventId?,
|
||||
): Result<MediaUploadHandler>
|
||||
|
||||
suspend fun sendFile(
|
||||
|
|
@ -118,7 +117,7 @@ interface Timeline : AutoCloseable {
|
|||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
inReplyToEventId: EventId?,
|
||||
): Result<MediaUploadHandler>
|
||||
|
||||
/**
|
||||
|
|
@ -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<Unit>
|
||||
|
||||
suspend fun sendVoiceMessage(
|
||||
|
|
@ -147,7 +146,7 @@ interface Timeline : AutoCloseable {
|
|||
audioInfo: AudioInfo,
|
||||
waveform: List<Float>,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
inReplyToEventId: EventId?,
|
||||
): Result<MediaUploadHandler>
|
||||
|
||||
suspend fun redactEvent(eventOrTransactionId: EventOrTransactionId, reason: String?): Result<Unit>
|
||||
|
|
|
|||
|
|
@ -272,7 +272,7 @@ class RustMatrixClient(
|
|||
?: sessionId.value.substringAfter(":")
|
||||
}
|
||||
|
||||
override suspend fun getUrl(url: String): Result<String> = withContext(sessionDispatcher) {
|
||||
override suspend fun getUrl(url: String): Result<ByteArray> = withContext(sessionDispatcher) {
|
||||
runCatchingExceptions {
|
||||
innerClient.getUrl(url)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ class RustBaseRoom(
|
|||
override suspend fun reportRoom(reason: String?): Result<Unit> = withContext(roomDispatcher) {
|
||||
runCatchingExceptions {
|
||||
Timber.d("reportRoom $roomId")
|
||||
innerRoom.reportRoom(reason)
|
||||
innerRoom.reportRoom(reason.orEmpty())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
)
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<IntentionalMention>,
|
||||
|
|
@ -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<MediaUploadHandler> {
|
||||
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<MediaUploadHandler> {
|
||||
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<MediaUploadHandler> {
|
||||
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<MediaUploadHandler> {
|
||||
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<Unit> = 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<Float>,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
inReplyToEventId: EventId?,
|
||||
): Result<MediaUploadHandler> {
|
||||
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(),
|
||||
|
|
|
|||
|
|
@ -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<String> = listOf(),
|
||||
roomCreator: UserId? = null,
|
||||
roomCreators: List<String>? = 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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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<UserId>().toImmutableList(),
|
||||
heroes = emptyList<MatrixUser>().toImmutableList(),
|
||||
pinnedEventIds = emptyList<EventId>().toPersistentList(),
|
||||
creator = null,
|
||||
creators = persistentListOf(),
|
||||
isMarkedUnread = true,
|
||||
numUnreadMessages = 12L,
|
||||
numUnreadNotifications = 13L,
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ class FakeMatrixClient(
|
|||
private val getNotJoinedRoomResult: (RoomIdOrAlias, List<String>) -> Result<NotJoinedRoom> = { _, _ -> lambdaError() },
|
||||
private val clearCacheLambda: () -> Unit = { lambdaError() },
|
||||
private val userIdServerNameLambda: () -> String = { lambdaError() },
|
||||
private val getUrlLambda: (String) -> Result<String> = { lambdaError() },
|
||||
private val getUrlLambda: (String) -> Result<ByteArray> = { lambdaError() },
|
||||
private val canDeactivateAccountResult: () -> Boolean = { lambdaError() },
|
||||
private val deactivateAccountResult: (String, Boolean) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
private val currentSlidingSyncVersionLambda: () -> Result<SlidingSyncVersion> = { lambdaError() },
|
||||
|
|
@ -324,7 +324,7 @@ class FakeMatrixClient(
|
|||
return userIdServerNameLambda()
|
||||
}
|
||||
|
||||
override suspend fun getUrl(url: String): Result<String> {
|
||||
override suspend fun getUrl(url: String): Result<ByteArray> {
|
||||
return getUrlLambda(url)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ fun aRoomInfo(
|
|||
activeRoomCallParticipants: List<UserId> = emptyList(),
|
||||
heroes: List<MatrixUser> = emptyList(),
|
||||
pinnedEventIds: List<EventId> = emptyList(),
|
||||
roomCreator: UserId? = null,
|
||||
roomCreators: List<UserId> = 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,
|
||||
|
|
|
|||
|
|
@ -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<UserId> = emptyList(),
|
||||
heroes: List<MatrixUser> = emptyList(),
|
||||
pinnedEventIds: List<EventId> = emptyList(),
|
||||
roomCreator: UserId? = null,
|
||||
roomCreators: List<UserId> = 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,
|
||||
|
|
|
|||
|
|
@ -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<IntentionalMention>,
|
||||
|
|
@ -132,13 +131,13 @@ class FakeTimeline(
|
|||
}
|
||||
|
||||
override suspend fun replyMessage(
|
||||
replyParameters: ReplyParameters,
|
||||
repliedToEventId: EventId,
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
fromNotification: Boolean,
|
||||
): Result<Unit> = replyMessageLambda(
|
||||
replyParameters,
|
||||
repliedToEventId,
|
||||
body,
|
||||
htmlBody,
|
||||
intentionalMentions,
|
||||
|
|
@ -152,7 +151,7 @@ class FakeTimeline(
|
|||
body: String?,
|
||||
formattedBody: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
inReplyToEventId: EventId??,
|
||||
) -> Result<MediaUploadHandler> = { _, _, _, _, _, _, _ ->
|
||||
Result.success(FakeMediaUploadHandler())
|
||||
}
|
||||
|
|
@ -164,7 +163,7 @@ class FakeTimeline(
|
|||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
inReplyToEventId: EventId??,
|
||||
): Result<MediaUploadHandler> = 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<MediaUploadHandler> = { _, _, _, _, _, _, _ ->
|
||||
Result.success(FakeMediaUploadHandler())
|
||||
}
|
||||
|
|
@ -197,7 +196,7 @@ class FakeTimeline(
|
|||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
inReplyToEventId: EventId??,
|
||||
): Result<MediaUploadHandler> = 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<MediaUploadHandler> = { _, _, _, _, _, _ ->
|
||||
Result.success(FakeMediaUploadHandler())
|
||||
}
|
||||
|
|
@ -228,7 +227,7 @@ class FakeTimeline(
|
|||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
inReplyToEventId: EventId??,
|
||||
): Result<MediaUploadHandler> = 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<MediaUploadHandler> = { _, _, _, _, _, _ ->
|
||||
Result.success(FakeMediaUploadHandler())
|
||||
}
|
||||
|
|
@ -258,7 +257,7 @@ class FakeTimeline(
|
|||
caption: String?,
|
||||
formattedCaption: String?,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
inReplyToEventId: EventId??,
|
||||
): Result<MediaUploadHandler> = 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<Float>,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
inReplyToEventId: EventId??,
|
||||
) -> Result<MediaUploadHandler> = { _, _, _, _, _ ->
|
||||
Result.success(FakeMediaUploadHandler())
|
||||
}
|
||||
|
|
@ -286,7 +285,7 @@ class FakeTimeline(
|
|||
audioInfo: AudioInfo,
|
||||
waveform: List<Float>,
|
||||
progressCallback: ProgressCallback?,
|
||||
replyParameters: ReplyParameters?,
|
||||
inReplyToEventId: EventId??,
|
||||
): Result<MediaUploadHandler> = 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<Unit> = { _, _, _, _, _, _ ->
|
||||
lambdaError()
|
||||
}
|
||||
|
|
@ -315,7 +314,7 @@ class FakeTimeline(
|
|||
description: String?,
|
||||
zoomLevel: Int?,
|
||||
assetType: AssetType?,
|
||||
replyParameters: ReplyParameters?,
|
||||
inReplyToEventId: EventId??,
|
||||
): Result<Unit> = simulateLongTask {
|
||||
sendLocationLambda(
|
||||
body,
|
||||
|
|
@ -323,7 +322,7 @@ class FakeTimeline(
|
|||
description,
|
||||
zoomLevel,
|
||||
assetType,
|
||||
replyParameters,
|
||||
inReplyToEventId,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Boolean> {
|
||||
|
|
@ -106,8 +107,8 @@ fun BaseRoom.userPowerLevelAsState(updateKey: Long): State<Long> {
|
|||
@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
|
||||
|
|
|
|||
|
|
@ -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<Unit> {
|
||||
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<Unit> {
|
||||
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<Float>,
|
||||
progressCallback: ProgressCallback? = null,
|
||||
replyParameters: ReplyParameters? = null,
|
||||
inReplyToEventId: EventId? = null,
|
||||
): Result<Unit> {
|
||||
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<Unit> {
|
||||
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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<FakeMediaUploadHandler>,
|
||||
> { _, _, _, _, _, _ ->
|
||||
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<FakeMediaUploadHandler>(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<File, FileInfo, String?, String?, ProgressCallback?, ReplyParameters?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ ->
|
||||
lambdaRecorder<File, FileInfo, String?, String?, ProgressCallback?, EventId?, Result<FakeMediaUploadHandler>> { _, _, _, _, _, _ ->
|
||||
Result.success(FakeMediaUploadHandler())
|
||||
}
|
||||
val room = FakeJoinedRoom(
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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<String, String?, List<IntentionalMention>, Result<Unit>> { _, _, _ -> Result.success(Unit) }
|
||||
val replyMessage =
|
||||
lambdaRecorder<ReplyParameters, String, String?, List<IntentionalMention>, Boolean, Result<Unit>> { _, _, _, _, _ -> Result.success(Unit) }
|
||||
lambdaRecorder<EventId?, String, String?, List<IntentionalMention>, Boolean, Result<Unit>> { _, _, _, _, _ -> 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<String, String?, List<IntentionalMention>, Result<Unit>> { _, _, _ -> Result.success(Unit) }
|
||||
val replyMessage =
|
||||
lambdaRecorder<ReplyParameters, String, String?, List<IntentionalMention>, Boolean, Result<Unit>> { _, _, _, _, _ -> Result.success(Unit) }
|
||||
lambdaRecorder<EventId?, String, String?, List<IntentionalMention>, Boolean, Result<Unit>> { _, _, _, _, _ -> 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<IntentionalMention>()),
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
]
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue