parent
80f833111c
commit
a52d78c3b3
9 changed files with 159 additions and 16 deletions
1
changelog.d/1591.feature
Normal file
1
changelog.d/1591.feature
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
Add intentional mentions to messages.
|
||||||
|
|
@ -47,6 +47,7 @@ import io.element.android.libraries.featureflag.api.FeatureFlags
|
||||||
import io.element.android.libraries.matrix.api.core.ProgressCallback
|
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.permalink.PermalinkBuilder
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
|
import io.element.android.libraries.matrix.api.room.Mention
|
||||||
import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder
|
import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder
|
||||||
import io.element.android.libraries.mediapickers.api.PickerProvider
|
import io.element.android.libraries.mediapickers.api.PickerProvider
|
||||||
import io.element.android.libraries.mediaupload.api.MediaSender
|
import io.element.android.libraries.mediaupload.api.MediaSender
|
||||||
|
|
@ -327,15 +328,25 @@ class MessageComposerPresenter @Inject constructor(
|
||||||
richTextEditorState: RichTextEditorState,
|
richTextEditorState: RichTextEditorState,
|
||||||
) = launch {
|
) = launch {
|
||||||
val capturedMode = messageComposerContext.composerMode
|
val capturedMode = messageComposerContext.composerMode
|
||||||
|
val mentions = richTextEditorState.mentionsState?.let { state ->
|
||||||
|
buildList {
|
||||||
|
if (state.hasAtRoomMention) {
|
||||||
|
add(Mention.AtRoom)
|
||||||
|
}
|
||||||
|
for (userId in state.userIds) {
|
||||||
|
add(Mention.User(userId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.orEmpty()
|
||||||
// Reset composer right away
|
// Reset composer right away
|
||||||
richTextEditorState.setHtml("")
|
richTextEditorState.setHtml("")
|
||||||
updateComposerMode(MessageComposerMode.Normal)
|
updateComposerMode(MessageComposerMode.Normal)
|
||||||
when (capturedMode) {
|
when (capturedMode) {
|
||||||
is MessageComposerMode.Normal -> room.sendMessage(body = message.markdown, htmlBody = message.html)
|
is MessageComposerMode.Normal -> room.sendMessage(body = message.markdown, htmlBody = message.html, mentions = mentions)
|
||||||
is MessageComposerMode.Edit -> {
|
is MessageComposerMode.Edit -> {
|
||||||
val eventId = capturedMode.eventId
|
val eventId = capturedMode.eventId
|
||||||
val transactionId = capturedMode.transactionId
|
val transactionId = capturedMode.transactionId
|
||||||
room.editMessage(eventId, transactionId, message.markdown, message.html)
|
room.editMessage(eventId, transactionId, message.markdown, message.html, mentions)
|
||||||
}
|
}
|
||||||
|
|
||||||
is MessageComposerMode.Quote -> TODO()
|
is MessageComposerMode.Quote -> TODO()
|
||||||
|
|
@ -343,6 +354,7 @@ class MessageComposerPresenter @Inject constructor(
|
||||||
capturedMode.eventId,
|
capturedMode.eventId,
|
||||||
message.markdown,
|
message.markdown,
|
||||||
message.html,
|
message.html,
|
||||||
|
mentions
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
analyticsService.capture(
|
analyticsService.capture(
|
||||||
|
|
|
||||||
|
|
@ -26,12 +26,12 @@ import app.cash.turbine.ReceiveTurbine
|
||||||
import app.cash.turbine.test
|
import app.cash.turbine.test
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import im.vector.app.features.analytics.plan.Composer
|
import im.vector.app.features.analytics.plan.Composer
|
||||||
|
import io.element.android.features.messages.impl.mentions.MentionSuggestion
|
||||||
import io.element.android.features.messages.impl.messagecomposer.AttachmentsState
|
import io.element.android.features.messages.impl.messagecomposer.AttachmentsState
|
||||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerContextImpl
|
import io.element.android.features.messages.impl.messagecomposer.MessageComposerContextImpl
|
||||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents
|
import io.element.android.features.messages.impl.messagecomposer.MessageComposerEvents
|
||||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter
|
import io.element.android.features.messages.impl.messagecomposer.MessageComposerPresenter
|
||||||
import io.element.android.features.messages.impl.messagecomposer.MessageComposerState
|
import io.element.android.features.messages.impl.messagecomposer.MessageComposerState
|
||||||
import io.element.android.features.messages.impl.mentions.MentionSuggestion
|
|
||||||
import io.element.android.features.messages.media.FakeLocalMediaFactory
|
import io.element.android.features.messages.media.FakeLocalMediaFactory
|
||||||
import io.element.android.libraries.core.mimetype.MimeTypes
|
import io.element.android.libraries.core.mimetype.MimeTypes
|
||||||
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
import io.element.android.libraries.designsystem.utils.snackbar.SnackbarDispatcher
|
||||||
|
|
@ -44,6 +44,7 @@ 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.media.VideoInfo
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||||
|
import io.element.android.libraries.matrix.api.room.Mention
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
import io.element.android.libraries.matrix.api.room.RoomMembershipState
|
||||||
import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder
|
import io.element.android.libraries.matrix.api.user.CurrentSessionIdHolder
|
||||||
import io.element.android.libraries.matrix.test.ANOTHER_MESSAGE
|
import io.element.android.libraries.matrix.test.ANOTHER_MESSAGE
|
||||||
|
|
@ -79,10 +80,12 @@ import io.element.android.tests.testutils.waitForPredicate
|
||||||
import io.mockk.mockk
|
import io.mockk.mockk
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.advanceUntilIdle
|
||||||
import kotlinx.coroutines.test.runTest
|
import kotlinx.coroutines.test.runTest
|
||||||
import okhttp3.internal.immutableListOf
|
import okhttp3.internal.immutableListOf
|
||||||
import org.junit.Rule
|
import org.junit.Rule
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
import uniffi.wysiwyg_composer.MentionsState
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@Suppress("LargeClass")
|
@Suppress("LargeClass")
|
||||||
|
|
@ -835,6 +838,67 @@ class MessageComposerPresenterTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@Test
|
||||||
|
fun `present - send messages with intentional mentions`() = runTest {
|
||||||
|
val room = FakeMatrixRoom()
|
||||||
|
val presenter = createPresenter(room = room, coroutineScope = this)
|
||||||
|
moleculeFlow(RecompositionMode.Immediate) {
|
||||||
|
presenter.present()
|
||||||
|
}.test {
|
||||||
|
skipItems(1)
|
||||||
|
val initialState = awaitItem()
|
||||||
|
|
||||||
|
// Check intentional mentions on message sent
|
||||||
|
val mentionUser1 = listOf(A_USER_ID.value)
|
||||||
|
initialState.richTextEditorState.mentionsState = MentionsState(
|
||||||
|
userIds = mentionUser1,
|
||||||
|
roomIds = emptyList(),
|
||||||
|
roomAliases = emptyList(),
|
||||||
|
hasAtRoomMention = false
|
||||||
|
)
|
||||||
|
initialState.richTextEditorState.setHtml(A_MESSAGE)
|
||||||
|
initialState.eventSink(MessageComposerEvents.SendMessage(A_MESSAGE.toMessage()))
|
||||||
|
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
assertThat(room.sendMessageMentions).isEqualTo(listOf(Mention.User(A_USER_ID.value)))
|
||||||
|
|
||||||
|
// Check intentional mentions on reply sent
|
||||||
|
initialState.eventSink(MessageComposerEvents.SetMode(aReplyMode()))
|
||||||
|
val mentionUser2 = listOf(A_USER_ID_2.value)
|
||||||
|
awaitItem().richTextEditorState.mentionsState = MentionsState(
|
||||||
|
userIds = mentionUser2,
|
||||||
|
roomIds = emptyList(),
|
||||||
|
roomAliases = emptyList(),
|
||||||
|
hasAtRoomMention = false
|
||||||
|
)
|
||||||
|
|
||||||
|
initialState.eventSink(MessageComposerEvents.SendMessage(A_MESSAGE.toMessage()))
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
assertThat(room.sendMessageMentions).isEqualTo(listOf(Mention.User(A_USER_ID_2.value)))
|
||||||
|
|
||||||
|
// Check intentional mentions on edit message
|
||||||
|
skipItems(1)
|
||||||
|
initialState.eventSink(MessageComposerEvents.SetMode(anEditMode()))
|
||||||
|
val mentionUser3 = listOf(A_USER_ID_3.value)
|
||||||
|
awaitItem().richTextEditorState.mentionsState = MentionsState(
|
||||||
|
userIds = mentionUser3,
|
||||||
|
roomIds = emptyList(),
|
||||||
|
roomAliases = emptyList(),
|
||||||
|
hasAtRoomMention = false
|
||||||
|
)
|
||||||
|
|
||||||
|
initialState.eventSink(MessageComposerEvents.SendMessage(A_MESSAGE.toMessage()))
|
||||||
|
advanceUntilIdle()
|
||||||
|
|
||||||
|
assertThat(room.sendMessageMentions).isEqualTo(listOf(Mention.User(A_USER_ID_3.value)))
|
||||||
|
|
||||||
|
skipItems(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun ReceiveTurbine<MessageComposerState>.backToNormalMode(state: MessageComposerState, skipCount: Int = 0): MessageComposerState {
|
private suspend fun ReceiveTurbine<MessageComposerState>.backToNormalMode(state: MessageComposerState, skipCount: Int = 0): MessageComposerState {
|
||||||
state.eventSink.invoke(MessageComposerEvents.CloseSpecialMode)
|
state.eventSink.invoke(MessageComposerEvents.CloseSpecialMode)
|
||||||
skipItems(skipCount)
|
skipItems(skipCount)
|
||||||
|
|
|
||||||
|
|
@ -90,13 +90,13 @@ interface MatrixRoom : Closeable {
|
||||||
|
|
||||||
suspend fun userAvatarUrl(userId: UserId): Result<String?>
|
suspend fun userAvatarUrl(userId: UserId): Result<String?>
|
||||||
|
|
||||||
suspend fun sendMessage(body: String, htmlBody: String?): Result<Unit>
|
suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit>
|
||||||
|
|
||||||
suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?): Result<Unit>
|
suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit>
|
||||||
|
|
||||||
suspend fun enterSpecialMode(eventId: EventId?): Result<Unit>
|
suspend fun enterSpecialMode(eventId: EventId?): Result<Unit>
|
||||||
|
|
||||||
suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?): Result<Unit>
|
suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit>
|
||||||
|
|
||||||
suspend fun redactEvent(eventId: EventId, reason: String? = null): Result<Unit>
|
suspend fun redactEvent(eventId: EventId, reason: String? = null): Result<Unit>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.matrix.api.room
|
||||||
|
|
||||||
|
sealed interface Mention {
|
||||||
|
data class User(val userId: String): Mention
|
||||||
|
data object AtRoom: Mention
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.libraries.matrix.impl.room
|
||||||
|
|
||||||
|
import io.element.android.libraries.matrix.api.room.Mention
|
||||||
|
import org.matrix.rustcomponents.sdk.Mentions
|
||||||
|
|
||||||
|
fun List<Mention>.map(): Mentions {
|
||||||
|
val hasAtRoom = any { it is Mention.AtRoom }
|
||||||
|
val userIds = filterIsInstance<Mention.User>().map { it.userId }
|
||||||
|
return Mentions(userIds, hasAtRoom)
|
||||||
|
}
|
||||||
|
|
@ -35,6 +35,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoomNotificationSettingsState
|
import io.element.android.libraries.matrix.api.room.MatrixRoomNotificationSettingsState
|
||||||
|
import io.element.android.libraries.matrix.api.room.Mention
|
||||||
import io.element.android.libraries.matrix.api.room.MessageEventType
|
import io.element.android.libraries.matrix.api.room.MessageEventType
|
||||||
import io.element.android.libraries.matrix.api.room.StateEventType
|
import io.element.android.libraries.matrix.api.room.StateEventType
|
||||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||||
|
|
@ -250,22 +251,28 @@ class RustMatrixRoom(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun sendMessage(body: String, htmlBody: String?): Result<Unit> = withContext(roomDispatcher) {
|
override suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> = withContext(roomDispatcher) {
|
||||||
messageEventContentFromParts(body, htmlBody).use { content ->
|
messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()).use { content ->
|
||||||
runCatching {
|
runCatching {
|
||||||
innerRoom.send(content)
|
innerRoom.send(content)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?): Result<Unit> =
|
override suspend fun editMessage(
|
||||||
|
originalEventId: EventId?,
|
||||||
|
transactionId: TransactionId?,
|
||||||
|
body: String,
|
||||||
|
htmlBody: String?,
|
||||||
|
mentions: List<Mention>,
|
||||||
|
): Result<Unit> =
|
||||||
withContext(roomDispatcher) {
|
withContext(roomDispatcher) {
|
||||||
if (originalEventId != null) {
|
if (originalEventId != null) {
|
||||||
runCatching {
|
runCatching {
|
||||||
val editedEvent = specialModeEventTimelineItem ?: innerRoom.getEventTimelineItemByEventId(originalEventId.value)
|
val editedEvent = specialModeEventTimelineItem ?: innerRoom.getEventTimelineItemByEventId(originalEventId.value)
|
||||||
editedEvent.use {
|
editedEvent.use {
|
||||||
innerRoom.edit(
|
innerRoom.edit(
|
||||||
newContent = messageEventContentFromParts(body, htmlBody),
|
newContent = messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()),
|
||||||
editItem = it,
|
editItem = it,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -289,11 +296,11 @@ class RustMatrixRoom(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?): Result<Unit> = withContext(roomDispatcher) {
|
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> = withContext(roomDispatcher) {
|
||||||
runCatching {
|
runCatching {
|
||||||
val inReplyTo = specialModeEventTimelineItem ?: innerRoom.getEventTimelineItemByEventId(eventId.value)
|
val inReplyTo = specialModeEventTimelineItem ?: innerRoom.getEventTimelineItemByEventId(eventId.value)
|
||||||
inReplyTo.use { eventTimelineItem ->
|
inReplyTo.use { eventTimelineItem ->
|
||||||
innerRoom.sendReply(messageEventContentFromParts(body, htmlBody), eventTimelineItem)
|
innerRoom.sendReply(messageEventContentFromParts(body, htmlBody).withMentions(mentions.map()), eventTimelineItem)
|
||||||
}
|
}
|
||||||
specialModeEventTimelineItem = null
|
specialModeEventTimelineItem = null
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
import io.element.android.libraries.matrix.api.room.MatrixRoomInfo
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
|
||||||
import io.element.android.libraries.matrix.api.room.MatrixRoomNotificationSettingsState
|
import io.element.android.libraries.matrix.api.room.MatrixRoomNotificationSettingsState
|
||||||
|
import io.element.android.libraries.matrix.api.room.Mention
|
||||||
import io.element.android.libraries.matrix.api.room.MessageEventType
|
import io.element.android.libraries.matrix.api.room.MessageEventType
|
||||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||||
|
|
@ -108,6 +109,7 @@ class FakeMatrixRoom(
|
||||||
private var generateWidgetWebViewUrlResult = Result.success("https://call.element.io")
|
private var generateWidgetWebViewUrlResult = Result.success("https://call.element.io")
|
||||||
private var getWidgetDriverResult: Result<MatrixWidgetDriver> = Result.success(FakeWidgetDriver())
|
private var getWidgetDriverResult: Result<MatrixWidgetDriver> = Result.success(FakeWidgetDriver())
|
||||||
private var canUserTriggerRoomNotificationResult: Result<Boolean> = Result.success(true)
|
private var canUserTriggerRoomNotificationResult: Result<Boolean> = Result.success(true)
|
||||||
|
var sendMessageMentions = emptyList<Mention>()
|
||||||
val editMessageCalls = mutableListOf<Pair<String, String?>>()
|
val editMessageCalls = mutableListOf<Pair<String, String?>>()
|
||||||
|
|
||||||
var sendMediaCount = 0
|
var sendMediaCount = 0
|
||||||
|
|
@ -190,7 +192,8 @@ class FakeMatrixRoom(
|
||||||
userAvatarUrlResult
|
userAvatarUrlResult
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun sendMessage(body: String, htmlBody: String?) = simulateLongTask {
|
override suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>) = simulateLongTask {
|
||||||
|
sendMessageMentions = mentions
|
||||||
Result.success(Unit)
|
Result.success(Unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -219,7 +222,14 @@ class FakeMatrixRoom(
|
||||||
return cancelSendResult
|
return cancelSendResult
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?): Result<Unit> {
|
override suspend fun editMessage(
|
||||||
|
originalEventId: EventId?,
|
||||||
|
transactionId: TransactionId?,
|
||||||
|
body: String,
|
||||||
|
htmlBody: String?,
|
||||||
|
mentions: List<Mention>
|
||||||
|
): Result<Unit> {
|
||||||
|
sendMessageMentions = mentions
|
||||||
editMessageCalls += body to htmlBody
|
editMessageCalls += body to htmlBody
|
||||||
return Result.success(Unit)
|
return Result.success(Unit)
|
||||||
}
|
}
|
||||||
|
|
@ -231,7 +241,8 @@ class FakeMatrixRoom(
|
||||||
return Result.success(Unit)
|
return Result.success(Unit)
|
||||||
}
|
}
|
||||||
|
|
||||||
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?): Result<Unit> {
|
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> {
|
||||||
|
sendMessageMentions = mentions
|
||||||
replyMessageParameter = body to htmlBody
|
replyMessageParameter = body to htmlBody
|
||||||
return Result.success(Unit)
|
return Result.success(Unit)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ dependencies {
|
||||||
implementation(projects.libraries.testtags)
|
implementation(projects.libraries.testtags)
|
||||||
implementation(projects.libraries.uiUtils)
|
implementation(projects.libraries.uiUtils)
|
||||||
|
|
||||||
implementation(libs.matrix.richtexteditor)
|
api(libs.matrix.richtexteditor)
|
||||||
api(libs.matrix.richtexteditor.compose)
|
api(libs.matrix.richtexteditor.compose)
|
||||||
|
|
||||||
ksp(libs.showkase.processor)
|
ksp(libs.showkase.processor)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue