diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index ae3f30ae18..8d81632f83 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt
index e40d0bbb9b..07eeda2efa 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt
@@ -84,7 +84,8 @@ import io.element.android.libraries.matrix.api.room.MessageEventType
import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
-import io.element.android.libraries.matrix.ui.room.canRedactAsState
+import io.element.android.libraries.matrix.ui.room.canRedactOtherAsState
+import io.element.android.libraries.matrix.ui.room.canRedactOwnAsState
import io.element.android.libraries.matrix.ui.room.canSendMessageAsState
import io.element.android.libraries.textcomposer.model.MessageComposerMode
import kotlinx.coroutines.CoroutineScope
@@ -138,7 +139,8 @@ class MessagesPresenter @AssistedInject constructor(
val syncUpdateFlow = room.syncUpdateFlow.collectAsState()
val userHasPermissionToSendMessage by room.canSendMessageAsState(type = MessageEventType.ROOM_MESSAGE, updateKey = syncUpdateFlow.value)
- val userHasPermissionToRedact by room.canRedactAsState(updateKey = syncUpdateFlow.value)
+ val userHasPermissionToRedactOwn by room.canRedactOwnAsState(updateKey = syncUpdateFlow.value)
+ val userHasPermissionToRedactOther by room.canRedactOtherAsState(updateKey = syncUpdateFlow.value)
val userHasPermissionToSendReaction by room.canSendMessageAsState(type = MessageEventType.REACTION_SENT, updateKey = syncUpdateFlow.value)
val roomName: AsyncData by remember {
derivedStateOf { roomInfo?.name?.let { AsyncData.Success(it) } ?: AsyncData.Uninitialized }
@@ -219,7 +221,8 @@ class MessagesPresenter @AssistedInject constructor(
roomName = roomName,
roomAvatar = roomAvatar,
userHasPermissionToSendMessage = userHasPermissionToSendMessage,
- userHasPermissionToRedact = userHasPermissionToRedact,
+ userHasPermissionToRedactOwn = userHasPermissionToRedactOwn,
+ userHasPermissionToRedactOther = userHasPermissionToRedactOther,
userHasPermissionToSendReaction = userHasPermissionToSendReaction,
composerState = composerState,
voiceMessageComposerState = voiceMessageComposerState,
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt
index c196535607..8e4d1c484b 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt
@@ -36,7 +36,8 @@ data class MessagesState(
val roomName: AsyncData,
val roomAvatar: AsyncData,
val userHasPermissionToSendMessage: Boolean,
- val userHasPermissionToRedact: Boolean,
+ val userHasPermissionToRedactOwn: Boolean,
+ val userHasPermissionToRedactOther: Boolean,
val userHasPermissionToSendReaction: Boolean,
val composerState: MessageComposerState,
val voiceMessageComposerState: VoiceMessageComposerState,
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt
index 8fc7466f95..1bb500e08f 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt
@@ -86,7 +86,8 @@ fun aMessagesState() = MessagesState(
roomName = AsyncData.Success("Room name"),
roomAvatar = AsyncData.Success(AvatarData("!id:domain", "Room name", size = AvatarSize.TimelineRoom)),
userHasPermissionToSendMessage = true,
- userHasPermissionToRedact = false,
+ userHasPermissionToRedactOwn = false,
+ userHasPermissionToRedactOther = false,
userHasPermissionToSendReaction = true,
composerState = aMessageComposerState().copy(
richTextEditorState = RichTextEditorState("Hello", initialFocus = true),
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt
index 96af343a5a..394f1f3556 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt
@@ -160,7 +160,8 @@ fun MessagesView(
state.actionListState.eventSink(
ActionListEvents.ComputeForMessage(
event = event,
- canRedact = state.userHasPermissionToRedact,
+ canRedactOwn = state.userHasPermissionToRedactOwn,
+ canRedactOther = state.userHasPermissionToRedactOther,
canSendMessage = state.userHasPermissionToSendMessage,
canSendReaction = state.userHasPermissionToSendReaction,
)
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListEvents.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListEvents.kt
index 6339716ccf..e486c1ae2b 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListEvents.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListEvents.kt
@@ -22,7 +22,8 @@ sealed interface ActionListEvents {
data object Clear : ActionListEvents
data class ComputeForMessage(
val event: TimelineItem.Event,
- val canRedact: Boolean,
+ val canRedactOwn: Boolean,
+ val canRedactOther: Boolean,
val canSendMessage: Boolean,
val canSendReaction: Boolean,
) : ActionListEvents
diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt
index 474a0cf022..f753266062 100644
--- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt
+++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenter.kt
@@ -56,7 +56,8 @@ class ActionListPresenter @Inject constructor(
ActionListEvents.Clear -> target.value = ActionListState.Target.None
is ActionListEvents.ComputeForMessage -> localCoroutineScope.computeForMessage(
timelineItem = event.event,
- userCanRedact = event.canRedact,
+ userCanRedactOwn = event.canRedactOwn,
+ userCanRedactOther = event.canRedactOther,
userCanSendMessage = event.canSendMessage,
userCanSendReaction = event.canSendReaction,
isDeveloperModeEnabled = isDeveloperModeEnabled,
@@ -73,13 +74,15 @@ class ActionListPresenter @Inject constructor(
private fun CoroutineScope.computeForMessage(
timelineItem: TimelineItem.Event,
- userCanRedact: Boolean,
+ userCanRedactOwn: Boolean,
+ userCanRedactOther: Boolean,
userCanSendMessage: Boolean,
userCanSendReaction: Boolean,
isDeveloperModeEnabled: Boolean,
target: MutableState
) = launch {
target.value = ActionListState.Target.Loading(timelineItem)
+ val canRedact = timelineItem.isMine && userCanRedactOwn || !timelineItem.isMine && userCanRedactOther
val actions =
when (timelineItem.content) {
is TimelineItemRedactedContent -> {
@@ -98,8 +101,10 @@ class ActionListPresenter @Inject constructor(
}
}
is TimelineItemPollContent -> {
+ val canEndPoll = timelineItem.isRemote &&
+ !timelineItem.content.isEnded &&
+ (timelineItem.isMine || canRedact)
buildList {
- val isMineOrCanRedact = timelineItem.isMine || userCanRedact
if (timelineItem.isRemote) {
// Can only reply or forward messages already uploaded to the server
add(TimelineItemAction.Reply)
@@ -107,7 +112,7 @@ class ActionListPresenter @Inject constructor(
if (timelineItem.isRemote && timelineItem.isEditable) {
add(TimelineItemAction.Edit)
}
- if (timelineItem.isRemote && !timelineItem.content.isEnded && isMineOrCanRedact) {
+ if (canEndPoll) {
add(TimelineItemAction.EndPoll)
}
if (timelineItem.content.canBeCopied()) {
@@ -119,7 +124,7 @@ class ActionListPresenter @Inject constructor(
if (!timelineItem.isMine) {
add(TimelineItemAction.ReportContent)
}
- if (isMineOrCanRedact) {
+ if (canRedact) {
add(TimelineItemAction.Redact)
}
}
@@ -136,7 +141,7 @@ class ActionListPresenter @Inject constructor(
if (!timelineItem.isMine) {
add(TimelineItemAction.ReportContent)
}
- if (timelineItem.isMine || userCanRedact) {
+ if (canRedact) {
add(TimelineItemAction.Redact)
}
}
@@ -169,7 +174,7 @@ class ActionListPresenter @Inject constructor(
if (!timelineItem.isMine) {
add(TimelineItemAction.ReportContent)
}
- if (timelineItem.isMine || userCanRedact) {
+ if (canRedact) {
add(TimelineItemAction.Redact)
}
}
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt
index 1ad6a693ab..67bbb98e73 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesPresenterTest.kt
@@ -120,7 +120,7 @@ class MessagesPresenterTest {
assertThat(initialState.roomAvatar)
.isEqualTo(AsyncData.Success(AvatarData(id = A_ROOM_ID.value, name = "", url = AN_AVATAR_URL, size = AvatarSize.TimelineRoom)))
assertThat(initialState.userHasPermissionToSendMessage).isTrue()
- assertThat(initialState.userHasPermissionToRedact).isFalse()
+ assertThat(initialState.userHasPermissionToRedactOwn).isFalse()
assertThat(initialState.hasNetworkConnection).isTrue()
assertThat(initialState.snackbarMessage).isNull()
assertThat(initialState.inviteProgress).isEqualTo(AsyncData.Uninitialized)
@@ -601,14 +601,29 @@ class MessagesPresenterTest {
}
@Test
- fun `present - permission to redact`() = runTest {
- val matrixRoom = FakeMatrixRoom(canRedact = true)
+ fun `present - permission to redact own`() = runTest {
+ val matrixRoom = FakeMatrixRoom(canRedactOwn = true)
val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
- val initialState = consumeItemsUntilPredicate { it.userHasPermissionToRedact }.last()
- assertThat(initialState.userHasPermissionToRedact).isTrue()
+ val initialState = consumeItemsUntilPredicate { it.userHasPermissionToRedactOwn }.last()
+ assertThat(initialState.userHasPermissionToRedactOwn).isTrue()
+ assertThat(initialState.userHasPermissionToRedactOther).isFalse()
+ cancelAndIgnoreRemainingEvents()
+ }
+ }
+
+ @Test
+ fun `present - permission to redact other`() = runTest {
+ val matrixRoom = FakeMatrixRoom(canRedactOther = true)
+ val presenter = createMessagesPresenter(matrixRoom = matrixRoom)
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ val initialState = consumeItemsUntilPredicate { it.userHasPermissionToRedactOther }.last()
+ assertThat(initialState.userHasPermissionToRedactOwn).isFalse()
+ assertThat(initialState.userHasPermissionToRedactOther).isTrue()
cancelAndIgnoreRemainingEvents()
}
}
diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt
index 0dbdc7194b..0132b449df 100644
--- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt
+++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/actionlist/ActionListPresenterTest.kt
@@ -38,6 +38,7 @@ import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
+@Suppress("LargeClass")
class ActionListPresenterTest {
@get:Rule
val warmUpRule = WarmUpRule()
@@ -61,7 +62,15 @@ class ActionListPresenterTest {
}.test {
val initialState = awaitItem()
val messageEvent = aMessageEvent(isMine = true, content = TimelineItemRedactedContent)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true, canSendReaction = true))
+ initialState.eventSink.invoke(
+ ActionListEvents.ComputeForMessage(
+ event = messageEvent,
+ canRedactOwn = false,
+ canRedactOther = false,
+ canSendMessage = true,
+ canSendReaction = true,
+ )
+ )
// val loadingState = awaitItem()
// assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
val successState = awaitItem()
@@ -87,7 +96,15 @@ class ActionListPresenterTest {
}.test {
val initialState = awaitItem()
val messageEvent = aMessageEvent(isMine = false, content = TimelineItemRedactedContent)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true, canSendReaction = true))
+ initialState.eventSink.invoke(
+ ActionListEvents.ComputeForMessage(
+ event = messageEvent,
+ canRedactOwn = false,
+ canRedactOther = false,
+ canSendMessage = true,
+ canSendReaction = true,
+ )
+ )
// val loadingState = awaitItem()
// assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
val successState = awaitItem()
@@ -116,7 +133,15 @@ class ActionListPresenterTest {
isMine = false,
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null)
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true, canSendReaction = true))
+ initialState.eventSink.invoke(
+ ActionListEvents.ComputeForMessage(
+ event = messageEvent,
+ canRedactOwn = false,
+ canRedactOther = false,
+ canSendMessage = true,
+ canSendReaction = true,
+ )
+ )
// val loadingState = awaitItem()
// assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
val successState = awaitItem()
@@ -149,7 +174,15 @@ class ActionListPresenterTest {
isMine = false,
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null)
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = false, canSendReaction = true))
+ initialState.eventSink.invoke(
+ ActionListEvents.ComputeForMessage(
+ event = messageEvent,
+ canRedactOwn = true,
+ canRedactOther = false,
+ canSendMessage = false,
+ canSendReaction = true
+ )
+ )
// val loadingState = awaitItem()
// assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
val successState = awaitItem()
@@ -181,7 +214,15 @@ class ActionListPresenterTest {
isMine = false,
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null)
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = true, canSendMessage = true, canSendReaction = true))
+ initialState.eventSink.invoke(
+ ActionListEvents.ComputeForMessage(
+ event = messageEvent,
+ canRedactOwn = false,
+ canRedactOther = true,
+ canSendMessage = true,
+ canSendReaction = true,
+ )
+ )
val successState = awaitItem()
assertThat(successState.target).isEqualTo(
ActionListState.Target.Success(
@@ -213,7 +254,15 @@ class ActionListPresenterTest {
isMine = false,
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null)
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = true, canSendMessage = true, canSendReaction = false))
+ initialState.eventSink.invoke(
+ ActionListEvents.ComputeForMessage(
+ event = messageEvent,
+ canRedactOwn = false,
+ canRedactOther = true,
+ canSendMessage = true,
+ canSendReaction = false
+ )
+ )
val successState = awaitItem()
assertThat(successState.target).isEqualTo(
ActionListState.Target.Success(
@@ -245,7 +294,15 @@ class ActionListPresenterTest {
isMine = true,
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null)
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true, canSendReaction = true))
+ initialState.eventSink.invoke(
+ ActionListEvents.ComputeForMessage(
+ event = messageEvent,
+ canRedactOwn = true,
+ canRedactOther = false,
+ canSendMessage = true,
+ canSendReaction = true,
+ )
+ )
// val loadingState = awaitItem()
// assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
val successState = awaitItem()
@@ -268,6 +325,47 @@ class ActionListPresenterTest {
}
}
+ @Test
+ fun `present - compute for my message cannot redact`() = runTest {
+ val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
+ moleculeFlow(RecompositionMode.Immediate) {
+ presenter.present()
+ }.test {
+ val initialState = awaitItem()
+ val messageEvent = aMessageEvent(
+ isMine = true,
+ content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null)
+ )
+ initialState.eventSink.invoke(
+ ActionListEvents.ComputeForMessage(
+ event = messageEvent,
+ canRedactOwn = false,
+ canRedactOther = false,
+ canSendMessage = true,
+ canSendReaction = true,
+ )
+ )
+ // val loadingState = awaitItem()
+ // assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
+ val successState = awaitItem()
+ assertThat(successState.target).isEqualTo(
+ ActionListState.Target.Success(
+ event = messageEvent,
+ displayEmojiReactions = true,
+ actions = persistentListOf(
+ TimelineItemAction.Reply,
+ TimelineItemAction.Forward,
+ TimelineItemAction.Edit,
+ TimelineItemAction.Copy,
+ TimelineItemAction.ViewSource,
+ )
+ )
+ )
+ initialState.eventSink.invoke(ActionListEvents.Clear)
+ assertThat(awaitItem().target).isEqualTo(ActionListState.Target.None)
+ }
+ }
+
@Test
fun `present - compute for a media item`() = runTest {
val presenter = createActionListPresenter(isDeveloperModeEnabled = true)
@@ -279,7 +377,15 @@ class ActionListPresenterTest {
isMine = true,
content = aTimelineItemImageContent(),
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true, canSendReaction = true))
+ initialState.eventSink.invoke(
+ ActionListEvents.ComputeForMessage(
+ event = messageEvent,
+ canRedactOwn = true,
+ canRedactOther = false,
+ canSendMessage = true,
+ canSendReaction = true,
+ )
+ )
// val loadingState = awaitItem()
// assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
val successState = awaitItem()
@@ -311,7 +417,15 @@ class ActionListPresenterTest {
isMine = true,
content = aTimelineItemStateEventContent(),
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(stateEvent, canRedact = false, canSendMessage = true, canSendReaction = true))
+ initialState.eventSink.invoke(
+ ActionListEvents.ComputeForMessage(
+ event = stateEvent,
+ canRedactOwn = false,
+ canRedactOther = false,
+ canSendMessage = true,
+ canSendReaction = true,
+ )
+ )
// val loadingState = awaitItem()
// assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
val successState = awaitItem()
@@ -341,7 +455,15 @@ class ActionListPresenterTest {
isMine = true,
content = aTimelineItemStateEventContent(),
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(stateEvent, canRedact = false, canSendMessage = true, canSendReaction = true))
+ initialState.eventSink.invoke(
+ ActionListEvents.ComputeForMessage(
+ event = stateEvent,
+ canRedactOwn = false,
+ canRedactOther = false,
+ canSendMessage = true,
+ canSendReaction = true,
+ )
+ )
// val loadingState = awaitItem()
// assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
val successState = awaitItem()
@@ -370,7 +492,15 @@ class ActionListPresenterTest {
isMine = true,
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null)
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true, canSendReaction = true))
+ initialState.eventSink.invoke(
+ ActionListEvents.ComputeForMessage(
+ event = messageEvent,
+ canRedactOwn = true,
+ canRedactOther = false,
+ canSendMessage = true,
+ canSendReaction = true,
+ )
+ )
// val loadingState = awaitItem()
// assertThat(loadingState.target).isEqualTo(ActionListState.Target.Loading(messageEvent))
val successState = awaitItem()
@@ -408,10 +538,26 @@ class ActionListPresenterTest {
content = TimelineItemRedactedContent,
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true, canSendReaction = true))
+ initialState.eventSink.invoke(
+ ActionListEvents.ComputeForMessage(
+ event = messageEvent,
+ canRedactOwn = false,
+ canRedactOther = false,
+ canSendMessage = true,
+ canSendReaction = true,
+ )
+ )
assertThat(awaitItem().target).isInstanceOf(ActionListState.Target.Success::class.java)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(redactedEvent, canRedact = false, canSendMessage = true, canSendReaction = true))
+ initialState.eventSink.invoke(
+ ActionListEvents.ComputeForMessage(
+ event = redactedEvent,
+ canRedactOwn = false,
+ canRedactOther = false,
+ canSendMessage = true,
+ canSendReaction = true,
+ )
+ )
awaitItem().run {
assertThat(target).isEqualTo(ActionListState.Target.None)
}
@@ -432,7 +578,15 @@ class ActionListPresenterTest {
content = TimelineItemTextContent(body = A_MESSAGE, htmlDocument = null, isEdited = false, formattedBody = null),
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true, canSendReaction = true))
+ initialState.eventSink.invoke(
+ ActionListEvents.ComputeForMessage(
+ event = messageEvent,
+ canRedactOwn = true,
+ canRedactOther = false,
+ canSendMessage = true,
+ canSendReaction = true,
+ )
+ )
val successState = awaitItem()
assertThat(successState.target).isEqualTo(
ActionListState.Target.Success(
@@ -460,7 +614,15 @@ class ActionListPresenterTest {
isEditable = true,
content = aTimelineItemPollContent(answerItems = aPollAnswerItemList(hasVotes = false)),
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true, canSendReaction = true))
+ initialState.eventSink.invoke(
+ ActionListEvents.ComputeForMessage(
+ event = messageEvent,
+ canRedactOwn = true,
+ canRedactOther = false,
+ canSendMessage = true,
+ canSendReaction = true,
+ )
+ )
val successState = awaitItem()
assertThat(successState.target).isEqualTo(
ActionListState.Target.Success(
@@ -489,7 +651,15 @@ class ActionListPresenterTest {
isEditable = false,
content = aTimelineItemPollContent(answerItems = aPollAnswerItemList(hasVotes = true)),
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true, canSendReaction = true))
+ initialState.eventSink.invoke(
+ ActionListEvents.ComputeForMessage(
+ event = messageEvent,
+ canRedactOwn = true,
+ canRedactOther = false,
+ canSendMessage = true,
+ canSendReaction = true,
+ )
+ )
val successState = awaitItem()
assertThat(successState.target).isEqualTo(
ActionListState.Target.Success(
@@ -517,7 +687,15 @@ class ActionListPresenterTest {
isEditable = false,
content = aTimelineItemPollContent(isEnded = true),
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true, canSendReaction = true))
+ initialState.eventSink.invoke(
+ ActionListEvents.ComputeForMessage(
+ event = messageEvent,
+ canRedactOwn = true,
+ canRedactOther = false,
+ canSendMessage = true,
+ canSendReaction = true,
+ )
+ )
val successState = awaitItem()
assertThat(successState.target).isEqualTo(
ActionListState.Target.Success(
@@ -543,7 +721,15 @@ class ActionListPresenterTest {
isMine = true,
content = aTimelineItemVoiceContent(),
)
- initialState.eventSink.invoke(ActionListEvents.ComputeForMessage(messageEvent, canRedact = false, canSendMessage = true, canSendReaction = true))
+ initialState.eventSink.invoke(
+ ActionListEvents.ComputeForMessage(
+ event = messageEvent,
+ canRedactOwn = true,
+ canRedactOther = false,
+ canSendMessage = true,
+ canSendReaction = true,
+ )
+ )
val successState = awaitItem()
assertThat(successState.target).isEqualTo(
ActionListState.Target.Success(
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index eaa3da11b2..71d8e10d0b 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -153,7 +153,7 @@ jsoup = "org.jsoup:jsoup:1.17.2"
appyx_core = { module = "com.bumble.appyx:core", version.ref = "appyx" }
molecule-runtime = "app.cash.molecule:molecule-runtime:1.3.2"
timber = "com.jakewharton.timber:timber:5.0.1"
-matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.84"
+matrix_sdk = "org.matrix.rustcomponents:sdk-android:0.1.92"
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/room/MatrixRoom.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt
index 03c143f078..af6cd81fb2 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt
@@ -127,7 +127,9 @@ interface MatrixRoom : Closeable {
suspend fun canUserInvite(userId: UserId): Result
- suspend fun canUserRedact(userId: UserId): Result
+ suspend fun canUserRedactOwn(userId: UserId): Result
+
+ suspend fun canUserRedactOther(userId: UserId): Result
suspend fun canUserSendState(userId: UserId, type: StateEventType): Result
diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomPowerLevels.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomPowerLevels.kt
index 3a89f61d9d..c228a9cb2f 100644
--- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomPowerLevels.kt
+++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/powerlevels/MatrixRoomPowerLevels.kt
@@ -36,6 +36,11 @@ suspend fun MatrixRoom.canSendState(type: StateEventType): Result = can
suspend fun MatrixRoom.canSendMessage(type: MessageEventType): Result = canUserSendMessage(sessionId, type)
/**
- * Shortcut for calling [MatrixRoom.canUserRedact] with our own user.
+ * Shortcut for calling [MatrixRoom.canUserRedactOwn] with our own user.
*/
-suspend fun MatrixRoom.canRedact(): Result = canUserRedact(sessionId)
+suspend fun MatrixRoom.canRedactOwn(): Result = canUserRedactOwn(sessionId)
+
+/**
+ * Shortcut for calling [MatrixRoom.canRedactOther] with our own user.
+ */
+suspend fun MatrixRoom.canRedactOther(): Result = canUserRedactOther(sessionId)
diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt
index 84ccacb75f..c9a89e558e 100644
--- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt
+++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt
@@ -335,9 +335,15 @@ class RustMatrixRoom(
}
}
- override suspend fun canUserRedact(userId: UserId): Result {
+ override suspend fun canUserRedactOwn(userId: UserId): Result {
return runCatching {
- innerRoom.canUserRedact(userId.value)
+ innerRoom.canUserRedactOwn(userId.value)
+ }
+ }
+
+ override suspend fun canUserRedactOther(userId: UserId): Result {
+ return runCatching {
+ innerRoom.canUserRedactOther(userId.value)
}
}
diff --git a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt
index b4e0001e84..fbddf28bbe 100644
--- a/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt
+++ b/libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt
@@ -78,7 +78,8 @@ class FakeMatrixRoom(
override val activeMemberCount: Long = 234L,
val notificationSettingsService: NotificationSettingsService = FakeNotificationSettingsService(),
private val matrixTimeline: MatrixTimeline = FakeMatrixTimeline(),
- canRedact: Boolean = false,
+ canRedactOwn: Boolean = false,
+ canRedactOther: Boolean = false,
) : MatrixRoom {
private var ignoreResult: Result = Result.success(Unit)
private var unignoreResult: Result = Result.success(Unit)
@@ -88,7 +89,8 @@ class FakeMatrixRoom(
private var joinRoomResult = Result.success(Unit)
private var inviteUserResult = Result.success(Unit)
private var canInviteResult = Result.success(true)
- private var canRedactResult = Result.success(canRedact)
+ private var canRedactOwnResult = Result.success(canRedactOwn)
+ private var canRedactOtherResult = Result.success(canRedactOther)
private val canSendStateResults = mutableMapOf>()
private val canSendEventResults = mutableMapOf>()
private var sendMediaResult = Result.success(FakeMediaUploadHandler())
@@ -276,8 +278,12 @@ class FakeMatrixRoom(
return canInviteResult
}
- override suspend fun canUserRedact(userId: UserId): Result {
- return canRedactResult
+ override suspend fun canUserRedactOwn(userId: UserId): Result {
+ return canRedactOwnResult
+ }
+
+ override suspend fun canUserRedactOther(userId: UserId): Result {
+ return canRedactOtherResult
}
override suspend fun canUserSendState(userId: UserId, type: StateEventType): Result {
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 dea6195ba8..fbe35742e5 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
@@ -21,7 +21,8 @@ import androidx.compose.runtime.State
import androidx.compose.runtime.produceState
import io.element.android.libraries.matrix.api.room.MatrixRoom
import io.element.android.libraries.matrix.api.room.MessageEventType
-import io.element.android.libraries.matrix.api.room.powerlevels.canRedact
+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
@Composable
@@ -32,8 +33,15 @@ fun MatrixRoom.canSendMessageAsState(type: MessageEventType, updateKey: Long): S
}
@Composable
-fun MatrixRoom.canRedactAsState(updateKey: Long): State {
+fun MatrixRoom.canRedactOwnAsState(updateKey: Long): State {
return produceState(initialValue = false, key1 = updateKey) {
- value = canRedact().getOrElse { false }
+ value = canRedactOwn().getOrElse { false }
+ }
+}
+
+@Composable
+fun MatrixRoom.canRedactOtherAsState(updateKey: Long): State {
+ return produceState(initialValue = false, key1 = updateKey) {
+ value = canRedactOther().getOrElse { false }
}
}