Merge pull request #3322 from element-hq/feature/bma/roomAliasCompletion
Suggestion for room alias (disabled for now)
This commit is contained in:
commit
a942fd4a24
42 changed files with 814 additions and 274 deletions
|
|
@ -72,6 +72,13 @@ enum class FeatureFlags(
|
|||
defaultValue = { true },
|
||||
isFinished = false,
|
||||
),
|
||||
RoomAliasSuggestions(
|
||||
key = "feature.roomAliasSuggestions",
|
||||
title = "Room alias suggestions",
|
||||
description = "Type `#` to get room alias suggestions and insert them",
|
||||
defaultValue = { false },
|
||||
isFinished = false,
|
||||
),
|
||||
MarkAsUnread(
|
||||
key = "feature.markAsUnread",
|
||||
title = "Mark as unread",
|
||||
|
|
|
|||
|
|
@ -25,6 +25,5 @@ interface PermalinkBuilder {
|
|||
}
|
||||
|
||||
sealed class PermalinkBuilderError : Throwable() {
|
||||
data object InvalidUserId : PermalinkBuilderError()
|
||||
data object InvalidRoomAlias : PermalinkBuilderError()
|
||||
data object InvalidData : PermalinkBuilderError()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,12 +16,9 @@
|
|||
|
||||
package io.element.android.libraries.matrix.api.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.RoomId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
|
||||
sealed interface Mention {
|
||||
data class User(val userId: UserId) : Mention
|
||||
data object AtRoom : Mention
|
||||
data class Room(val roomId: RoomId) : Mention
|
||||
data class RoomAlias(val roomAlias: RoomAlias?) : Mention
|
||||
sealed interface IntentionalMention {
|
||||
data class User(val userId: UserId) : IntentionalMention
|
||||
data object Room : IntentionalMention
|
||||
}
|
||||
|
|
@ -129,9 +129,9 @@ interface MatrixRoom : Closeable {
|
|||
|
||||
suspend fun userAvatarUrl(userId: UserId): Result<String?>
|
||||
|
||||
suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit>
|
||||
suspend fun sendMessage(body: String, htmlBody: String?, intentionalMentions: List<IntentionalMention>): Result<Unit>
|
||||
|
||||
suspend fun editMessage(eventId: EventId, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit>
|
||||
suspend fun editMessage(eventId: EventId, body: String, htmlBody: String?, intentionalMentions: List<IntentionalMention>): Result<Unit>
|
||||
|
||||
suspend fun sendImage(
|
||||
file: File,
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import io.element.android.libraries.matrix.api.media.ImageInfo
|
|||
import io.element.android.libraries.matrix.api.media.MediaUploadHandler
|
||||
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.Mention
|
||||
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.timeline.item.event.InReplyTo
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -52,15 +52,24 @@ interface Timeline : AutoCloseable {
|
|||
fun paginationStatus(direction: PaginationDirection): StateFlow<PaginationStatus>
|
||||
val timelineItems: Flow<List<MatrixTimelineItem>>
|
||||
|
||||
suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit>
|
||||
suspend fun sendMessage(
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
): Result<Unit>
|
||||
|
||||
suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit>
|
||||
suspend fun editMessage(
|
||||
originalEventId: EventId?,
|
||||
transactionId: TransactionId?,
|
||||
body: String, htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
): Result<Unit>
|
||||
|
||||
suspend fun replyMessage(
|
||||
eventId: EventId,
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
mentions: List<Mention>,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
fromNotification: Boolean = false,
|
||||
): Result<Unit>
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import javax.inject.Inject
|
|||
class DefaultPermalinkBuilder @Inject constructor() : PermalinkBuilder {
|
||||
override fun permalinkForUser(userId: UserId): Result<String> {
|
||||
if (!MatrixPatterns.isUserId(userId.value)) {
|
||||
return Result.failure(PermalinkBuilderError.InvalidUserId)
|
||||
return Result.failure(PermalinkBuilderError.InvalidData)
|
||||
}
|
||||
return runCatching {
|
||||
matrixToUserPermalink(userId.value)
|
||||
|
|
@ -40,7 +40,7 @@ class DefaultPermalinkBuilder @Inject constructor() : PermalinkBuilder {
|
|||
|
||||
override fun permalinkForRoomAlias(roomAlias: RoomAlias): Result<String> {
|
||||
if (!MatrixPatterns.isRoomAlias(roomAlias.value)) {
|
||||
return Result.failure(PermalinkBuilderError.InvalidRoomAlias)
|
||||
return Result.failure(PermalinkBuilderError.InvalidData)
|
||||
}
|
||||
return runCatching {
|
||||
matrixToRoomAliasPermalink(roomAlias.value)
|
||||
|
|
|
|||
|
|
@ -16,11 +16,11 @@
|
|||
|
||||
package io.element.android.libraries.matrix.impl.room
|
||||
|
||||
import io.element.android.libraries.matrix.api.room.Mention
|
||||
import io.element.android.libraries.matrix.api.room.IntentionalMention
|
||||
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.value }
|
||||
return Mentions(userIds, hasAtRoom)
|
||||
fun List<IntentionalMention>.map(): Mentions {
|
||||
val hasRoom = any { it is IntentionalMention.Room }
|
||||
val userIds = filterIsInstance<IntentionalMention.User>().map { it.userId.value }
|
||||
return Mentions(userIds, hasRoom)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,11 +33,11 @@ import io.element.android.libraries.matrix.api.media.MediaUploadHandler
|
|||
import io.element.android.libraries.matrix.api.media.VideoInfo
|
||||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||
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.MatrixRoom
|
||||
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.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.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.StateEventType
|
||||
|
|
@ -340,16 +340,21 @@ class RustMatrixRoom(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun editMessage(eventId: EventId, body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> = withContext(roomDispatcher) {
|
||||
override suspend fun editMessage(
|
||||
eventId: EventId,
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>
|
||||
): Result<Unit> = withContext(roomDispatcher) {
|
||||
runCatching {
|
||||
MessageEventContent.from(body, htmlBody, mentions).use { newContent ->
|
||||
MessageEventContent.from(body, htmlBody, intentionalMentions).use { newContent ->
|
||||
innerRoom.edit(eventId.value, newContent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> {
|
||||
return liveTimeline.sendMessage(body, htmlBody, mentions)
|
||||
override suspend fun sendMessage(body: String, htmlBody: String?, intentionalMentions: List<IntentionalMention>): Result<Unit> {
|
||||
return liveTimeline.sendMessage(body, htmlBody, intentionalMentions)
|
||||
}
|
||||
|
||||
override suspend fun leave(): Result<Unit> = withContext(roomDispatcher) {
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ import io.element.android.libraries.matrix.api.media.ImageInfo
|
|||
import io.element.android.libraries.matrix.api.media.MediaUploadHandler
|
||||
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.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.Mention
|
||||
import io.element.android.libraries.matrix.api.room.isDm
|
||||
import io.element.android.libraries.matrix.api.room.location.AssetType
|
||||
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
|
||||
|
|
@ -263,8 +263,12 @@ class RustTimeline(
|
|||
}
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>): Result<Unit> = withContext(dispatcher) {
|
||||
MessageEventContent.from(body, htmlBody, mentions).use { content ->
|
||||
override suspend fun sendMessage(
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
): Result<Unit> = withContext(dispatcher) {
|
||||
MessageEventContent.from(body, htmlBody, intentionalMentions).use { content ->
|
||||
runCatching<Unit> {
|
||||
inner.send(content)
|
||||
}
|
||||
|
|
@ -284,13 +288,13 @@ class RustTimeline(
|
|||
transactionId: TransactionId?,
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
mentions: List<Mention>,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
): Result<Unit> =
|
||||
withContext(dispatcher) {
|
||||
runCatching<Unit> {
|
||||
getEventTimelineItem(originalEventId, transactionId).use { item ->
|
||||
inner.edit(
|
||||
newContent = MessageEventContent.from(body, htmlBody, mentions),
|
||||
newContent = MessageEventContent.from(body, htmlBody, intentionalMentions),
|
||||
item = item,
|
||||
)
|
||||
}
|
||||
|
|
@ -301,11 +305,11 @@ class RustTimeline(
|
|||
eventId: EventId,
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
mentions: List<Mention>,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
fromNotification: Boolean,
|
||||
): Result<Unit> = withContext(dispatcher) {
|
||||
runCatching {
|
||||
val msg = MessageEventContent.from(body, htmlBody, mentions)
|
||||
val msg = MessageEventContent.from(body, htmlBody, intentionalMentions)
|
||||
inner.sendReply(msg, eventId.value)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
package io.element.android.libraries.matrix.impl.util
|
||||
|
||||
import io.element.android.libraries.matrix.api.room.Mention
|
||||
import io.element.android.libraries.matrix.api.room.IntentionalMention
|
||||
import io.element.android.libraries.matrix.impl.room.map
|
||||
import org.matrix.rustcomponents.sdk.RoomMessageEventContentWithoutRelation
|
||||
import org.matrix.rustcomponents.sdk.messageEventContentFromHtml
|
||||
|
|
@ -26,11 +26,11 @@ import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown
|
|||
* Creates a [RoomMessageEventContentWithoutRelation] from a body, an html body and a list of mentions.
|
||||
*/
|
||||
object MessageEventContent {
|
||||
fun from(body: String, htmlBody: String?, mentions: List<Mention>): RoomMessageEventContentWithoutRelation {
|
||||
fun from(body: String, htmlBody: String?, intentionalMentions: List<IntentionalMention>): RoomMessageEventContentWithoutRelation {
|
||||
return if (htmlBody != null) {
|
||||
messageEventContentFromHtml(body, htmlBody)
|
||||
} else {
|
||||
messageEventContentFromMarkdown(body)
|
||||
}.withMentions(mentions.map())
|
||||
}.withMentions(intentionalMentions.map())
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,10 +19,11 @@ package io.element.android.libraries.matrix.test.permalink
|
|||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder
|
||||
import io.element.android.tests.testutils.lambda.lambdaError
|
||||
|
||||
class FakePermalinkBuilder(
|
||||
private val permalinkForUserLambda: (UserId) -> Result<String> = { Result.failure(Exception("Not implemented")) },
|
||||
private val permalinkForRoomAliasLambda: (RoomAlias) -> Result<String> = { Result.failure(Exception("Not implemented")) },
|
||||
private val permalinkForUserLambda: (UserId) -> Result<String> = { lambdaError() },
|
||||
private val permalinkForRoomAliasLambda: (RoomAlias) -> Result<String> = { lambdaError() },
|
||||
) : PermalinkBuilder {
|
||||
override fun permalinkForUser(userId: UserId): Result<String> {
|
||||
return permalinkForUserLambda(userId)
|
||||
|
|
|
|||
|
|
@ -31,11 +31,11 @@ import io.element.android.libraries.matrix.api.media.VideoInfo
|
|||
import io.element.android.libraries.matrix.api.notificationsettings.NotificationSettingsService
|
||||
import io.element.android.libraries.matrix.api.poll.PollKind
|
||||
import io.element.android.libraries.matrix.api.room.CurrentUserMembership
|
||||
import io.element.android.libraries.matrix.api.room.IntentionalMention
|
||||
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.MatrixRoomMembersState
|
||||
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.RoomMember
|
||||
import io.element.android.libraries.matrix.api.room.RoomNotificationMode
|
||||
|
|
@ -105,8 +105,8 @@ class FakeMatrixRoom(
|
|||
private val setTopicResult: (String) -> Result<Unit> = { lambdaError() },
|
||||
private val updateAvatarResult: (String, ByteArray) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
private val removeAvatarResult: () -> Result<Unit> = { lambdaError() },
|
||||
private val editMessageLambda: (EventId, String, String?, List<Mention>) -> Result<Unit> = { _, _, _, _ -> lambdaError() },
|
||||
private val sendMessageResult: (String, String?, List<Mention>) -> Result<Unit> = { _, _, _ -> lambdaError() },
|
||||
private val editMessageLambda: (EventId, String, String?, List<IntentionalMention>) -> Result<Unit> = { _, _, _, _ -> lambdaError() },
|
||||
private val sendMessageResult: (String, String?, List<IntentionalMention>) -> Result<Unit> = { _, _, _ -> lambdaError() },
|
||||
private val updateUserRoleResult: () -> Result<Unit> = { lambdaError() },
|
||||
private val toggleReactionResult: (String, EventId) -> Result<Unit> = { _, _ -> lambdaError() },
|
||||
private val retrySendMessageResult: (TransactionId) -> Result<Unit> = { lambdaError() },
|
||||
|
|
@ -222,12 +222,12 @@ class FakeMatrixRoom(
|
|||
return updateUserRoleResult()
|
||||
}
|
||||
|
||||
override suspend fun editMessage(eventId: EventId, body: String, htmlBody: String?, mentions: List<Mention>) = simulateLongTask {
|
||||
editMessageLambda(eventId, body, htmlBody, mentions)
|
||||
override suspend fun editMessage(eventId: EventId, body: String, htmlBody: String?, intentionalMentions: List<IntentionalMention>) = simulateLongTask {
|
||||
editMessageLambda(eventId, body, htmlBody, intentionalMentions)
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(body: String, htmlBody: String?, mentions: List<Mention>) = simulateLongTask {
|
||||
sendMessageResult(body, htmlBody, mentions)
|
||||
override suspend fun sendMessage(body: String, htmlBody: String?, intentionalMentions: List<IntentionalMention>) = simulateLongTask {
|
||||
sendMessageResult(body, htmlBody, intentionalMentions)
|
||||
}
|
||||
|
||||
override suspend fun toggleReaction(emoji: String, eventId: EventId): Result<Unit> {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import io.element.android.libraries.matrix.api.media.ImageInfo
|
|||
import io.element.android.libraries.matrix.api.media.MediaUploadHandler
|
||||
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.Mention
|
||||
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.timeline.MatrixTimelineItem
|
||||
import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
||||
|
|
@ -60,7 +60,7 @@ class FakeTimeline(
|
|||
var sendMessageLambda: (
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
mentions: List<Mention>,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
) -> Result<Unit> = { _, _, _ ->
|
||||
Result.success(Unit)
|
||||
}
|
||||
|
|
@ -68,8 +68,8 @@ class FakeTimeline(
|
|||
override suspend fun sendMessage(
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
mentions: List<Mention>,
|
||||
): Result<Unit> = sendMessageLambda(body, htmlBody, mentions)
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
): Result<Unit> = sendMessageLambda(body, htmlBody, intentionalMentions)
|
||||
|
||||
var redactEventLambda: (eventId: EventId?, transactionId: TransactionId?, reason: String?) -> Result<Boolean> = { _, _, _ ->
|
||||
Result.success(true)
|
||||
|
|
@ -86,7 +86,7 @@ class FakeTimeline(
|
|||
transactionId: TransactionId?,
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
mentions: List<Mention>,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
) -> Result<Unit> = { _, _, _, _, _ ->
|
||||
Result.success(Unit)
|
||||
}
|
||||
|
|
@ -96,20 +96,20 @@ class FakeTimeline(
|
|||
transactionId: TransactionId?,
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
mentions: List<Mention>,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
): Result<Unit> = editMessageLambda(
|
||||
originalEventId,
|
||||
transactionId,
|
||||
body,
|
||||
htmlBody,
|
||||
mentions
|
||||
intentionalMentions
|
||||
)
|
||||
|
||||
var replyMessageLambda: (
|
||||
eventId: EventId,
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
mentions: List<Mention>,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
fromNotification: Boolean,
|
||||
) -> Result<Unit> = { _, _, _, _, _ ->
|
||||
Result.success(Unit)
|
||||
|
|
@ -119,13 +119,13 @@ class FakeTimeline(
|
|||
eventId: EventId,
|
||||
body: String,
|
||||
htmlBody: String?,
|
||||
mentions: List<Mention>,
|
||||
intentionalMentions: List<IntentionalMention>,
|
||||
fromNotification: Boolean,
|
||||
): Result<Unit> = replyMessageLambda(
|
||||
eventId,
|
||||
body,
|
||||
htmlBody,
|
||||
mentions,
|
||||
intentionalMentions,
|
||||
fromNotification,
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -171,14 +171,14 @@ class NotificationBroadcastReceiverHandler @Inject constructor(
|
|||
eventId = threadId.asEventId(),
|
||||
body = message,
|
||||
htmlBody = null,
|
||||
mentions = emptyList(),
|
||||
intentionalMentions = emptyList(),
|
||||
fromNotification = true,
|
||||
)
|
||||
} else {
|
||||
room.liveTimeline.sendMessage(
|
||||
body = message,
|
||||
htmlBody = null,
|
||||
mentions = emptyList()
|
||||
intentionalMentions = emptyList()
|
||||
)
|
||||
}.onFailure {
|
||||
Timber.e(it, "Failed to send smart reply message")
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import io.element.android.libraries.matrix.api.core.RoomId
|
|||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.core.ThreadId
|
||||
import io.element.android.libraries.matrix.api.core.asEventId
|
||||
import io.element.android.libraries.matrix.api.room.Mention
|
||||
import io.element.android.libraries.matrix.api.room.IntentionalMention
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
import io.element.android.libraries.matrix.api.timeline.ReceiptType
|
||||
import io.element.android.libraries.matrix.test.AN_EVENT_ID
|
||||
|
|
@ -337,8 +337,8 @@ class NotificationBroadcastReceiverHandlerTest {
|
|||
|
||||
@Test
|
||||
fun `Test send reply`() = runTest {
|
||||
val sendMessage = lambdaRecorder<String, String?, List<Mention>, Result<Unit>> { _, _, _ -> Result.success(Unit) }
|
||||
val replyMessage = lambdaRecorder<EventId, String, String?, List<Mention>, Boolean, Result<Unit>> { _, _, _, _, _ -> Result.success(Unit) }
|
||||
val sendMessage = lambdaRecorder<String, String?, List<IntentionalMention>, Result<Unit>> { _, _, _ -> Result.success(Unit) }
|
||||
val replyMessage = lambdaRecorder<EventId, String, String?, List<IntentionalMention>, Boolean, Result<Unit>> { _, _, _, _, _ -> Result.success(Unit) }
|
||||
val liveTimeline = FakeTimeline().apply {
|
||||
sendMessageLambda = sendMessage
|
||||
replyMessageLambda = replyMessage
|
||||
|
|
@ -363,7 +363,7 @@ class NotificationBroadcastReceiverHandlerTest {
|
|||
runCurrent()
|
||||
sendMessage.assertions()
|
||||
.isCalledOnce()
|
||||
.with(value(A_MESSAGE), value(null), value(emptyList<Mention>()))
|
||||
.with(value(A_MESSAGE), value(null), value(emptyList<IntentionalMention>()))
|
||||
onNotifiableEventReceivedResult.assertions()
|
||||
.isCalledOnce()
|
||||
replyMessage.assertions()
|
||||
|
|
@ -372,7 +372,7 @@ class NotificationBroadcastReceiverHandlerTest {
|
|||
|
||||
@Test
|
||||
fun `Test send reply blank message`() = runTest {
|
||||
val sendMessage = lambdaRecorder<String, String?, List<Mention>, Result<Unit>> { _, _, _ -> Result.success(Unit) }
|
||||
val sendMessage = lambdaRecorder<String, String?, List<IntentionalMention>, Result<Unit>> { _, _, _ -> Result.success(Unit) }
|
||||
val liveTimeline = FakeTimeline().apply {
|
||||
sendMessageLambda = sendMessage
|
||||
}
|
||||
|
|
@ -396,8 +396,8 @@ class NotificationBroadcastReceiverHandlerTest {
|
|||
|
||||
@Test
|
||||
fun `Test send reply to thread`() = runTest {
|
||||
val sendMessage = lambdaRecorder<String, String?, List<Mention>, Result<Unit>> { _, _, _ -> Result.success(Unit) }
|
||||
val replyMessage = lambdaRecorder<EventId, String, String?, List<Mention>, Boolean, Result<Unit>> { _, _, _, _, _ -> Result.success(Unit) }
|
||||
val sendMessage = lambdaRecorder<String, String?, List<IntentionalMention>, Result<Unit>> { _, _, _ -> Result.success(Unit) }
|
||||
val replyMessage = lambdaRecorder<EventId, String, String?, List<IntentionalMention>, Boolean, Result<Unit>> { _, _, _, _, _ -> Result.success(Unit) }
|
||||
val liveTimeline = FakeTimeline().apply {
|
||||
sendMessageLambda = sendMessage
|
||||
replyMessageLambda = replyMessage
|
||||
|
|
@ -427,7 +427,7 @@ class NotificationBroadcastReceiverHandlerTest {
|
|||
.isCalledOnce()
|
||||
replyMessage.assertions()
|
||||
.isCalledOnce()
|
||||
.with(value(A_THREAD_ID.asEventId()), value(A_MESSAGE), value(null), value(emptyList<Mention>()), value(true))
|
||||
.with(value(A_THREAD_ID.asEventId()), value(A_MESSAGE), value(null), value(emptyList<IntentionalMention>()), value(true))
|
||||
}
|
||||
|
||||
private fun createIntent(
|
||||
|
|
|
|||
|
|
@ -111,13 +111,13 @@ fun MarkdownTextInput(
|
|||
state.text.update(editable, false)
|
||||
state.lineCount = lineCount
|
||||
|
||||
state.currentMentionSuggestion = editable?.checkSuggestionNeeded()
|
||||
onReceiveSuggestion(state.currentMentionSuggestion)
|
||||
state.currentSuggestion = editable?.checkSuggestionNeeded()
|
||||
onReceiveSuggestion(state.currentSuggestion)
|
||||
}
|
||||
onSelectionChangeListener = { selStart, selEnd ->
|
||||
state.selection = selStart..selEnd
|
||||
state.currentMentionSuggestion = editableText.checkSuggestionNeeded()
|
||||
onReceiveSuggestion(state.currentMentionSuggestion)
|
||||
state.currentSuggestion = editableText.checkSuggestionNeeded()
|
||||
onReceiveSuggestion(state.currentSuggestion)
|
||||
}
|
||||
if (onSelectRichContent != null) {
|
||||
ViewCompat.setOnReceiveContentListener(
|
||||
|
|
|
|||
|
|
@ -17,10 +17,13 @@
|
|||
package io.element.android.libraries.textcomposer.mentions
|
||||
|
||||
import androidx.compose.runtime.Immutable
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.room.RoomMember
|
||||
import io.element.android.libraries.matrix.api.roomlist.RoomSummary
|
||||
|
||||
@Immutable
|
||||
sealed interface ResolvedMentionSuggestion {
|
||||
data object AtRoom : ResolvedMentionSuggestion
|
||||
data class Member(val roomMember: RoomMember) : ResolvedMentionSuggestion
|
||||
sealed interface ResolvedSuggestion {
|
||||
data object AtRoom : ResolvedSuggestion
|
||||
data class Member(val roomMember: RoomMember) : ResolvedSuggestion
|
||||
data class Alias(val roomAlias: RoomAlias, val roomSummary: RoomSummary) : ResolvedSuggestion
|
||||
}
|
||||
|
|
@ -31,13 +31,14 @@ import androidx.compose.runtime.saveable.SaverScope
|
|||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.core.text.getSpans
|
||||
import io.element.android.libraries.matrix.api.core.RoomAlias
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkBuilder
|
||||
import io.element.android.libraries.matrix.api.room.Mention
|
||||
import io.element.android.libraries.matrix.api.room.IntentionalMention
|
||||
import io.element.android.libraries.textcomposer.components.markdown.StableCharSequence
|
||||
import io.element.android.libraries.textcomposer.mentions.MentionSpan
|
||||
import io.element.android.libraries.textcomposer.mentions.MentionSpanProvider
|
||||
import io.element.android.libraries.textcomposer.mentions.ResolvedMentionSuggestion
|
||||
import io.element.android.libraries.textcomposer.mentions.ResolvedSuggestion
|
||||
import io.element.android.libraries.textcomposer.mentions.getMentionSpans
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
|
|
@ -51,16 +52,16 @@ class MarkdownTextEditorState(
|
|||
var hasFocus by mutableStateOf(initialFocus)
|
||||
var requestFocusAction by mutableStateOf({})
|
||||
var lineCount by mutableIntStateOf(1)
|
||||
var currentMentionSuggestion by mutableStateOf<Suggestion?>(null)
|
||||
var currentSuggestion by mutableStateOf<Suggestion?>(null)
|
||||
|
||||
fun insertMention(
|
||||
mention: ResolvedMentionSuggestion,
|
||||
fun insertSuggestion(
|
||||
resolvedSuggestion: ResolvedSuggestion,
|
||||
mentionSpanProvider: MentionSpanProvider,
|
||||
permalinkBuilder: PermalinkBuilder,
|
||||
) {
|
||||
val suggestion = currentMentionSuggestion ?: return
|
||||
when (mention) {
|
||||
is ResolvedMentionSuggestion.AtRoom -> {
|
||||
val suggestion = currentSuggestion ?: return
|
||||
when (resolvedSuggestion) {
|
||||
is ResolvedSuggestion.AtRoom -> {
|
||||
val currentText = SpannableStringBuilder(text.value())
|
||||
val replaceText = "@room"
|
||||
val roomPill = mentionSpanProvider.getMentionSpanFor(replaceText, "")
|
||||
|
|
@ -70,10 +71,10 @@ class MarkdownTextEditorState(
|
|||
text.update(currentText, true)
|
||||
selection = IntRange(end + 1, end + 1)
|
||||
}
|
||||
is ResolvedMentionSuggestion.Member -> {
|
||||
is ResolvedSuggestion.Member -> {
|
||||
val currentText = SpannableStringBuilder(text.value())
|
||||
val text = mention.roomMember.displayName?.prependIndent("@") ?: mention.roomMember.userId.value
|
||||
val link = permalinkBuilder.permalinkForUser(mention.roomMember.userId).getOrNull() ?: return
|
||||
val text = resolvedSuggestion.roomMember.displayName?.prependIndent("@") ?: resolvedSuggestion.roomMember.userId.value
|
||||
val link = permalinkBuilder.permalinkForUser(resolvedSuggestion.roomMember.userId).getOrNull() ?: return
|
||||
val mentionPill = mentionSpanProvider.getMentionSpanFor(text, link)
|
||||
currentText.replace(suggestion.start, suggestion.end, "@ ")
|
||||
val end = suggestion.start + 1
|
||||
|
|
@ -81,6 +82,17 @@ class MarkdownTextEditorState(
|
|||
this.text.update(currentText, true)
|
||||
this.selection = IntRange(end + 1, end + 1)
|
||||
}
|
||||
is ResolvedSuggestion.Alias -> {
|
||||
val currentText = SpannableStringBuilder(text.value())
|
||||
val text = resolvedSuggestion.roomAlias.value
|
||||
val link = permalinkBuilder.permalinkForRoomAlias(resolvedSuggestion.roomAlias).getOrNull() ?: return
|
||||
val mentionPill = mentionSpanProvider.getMentionSpanFor(text, link)
|
||||
currentText.replace(suggestion.start, suggestion.end, "# ")
|
||||
val end = suggestion.start + 1
|
||||
currentText.setSpan(mentionPill, suggestion.start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
this.text.update(currentText, true)
|
||||
this.selection = IntRange(end + 1, end + 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -96,14 +108,18 @@ class MarkdownTextEditorState(
|
|||
val end = charSequence.getSpanEnd(mention)
|
||||
when (mention.type) {
|
||||
MentionSpan.Type.USER -> {
|
||||
val link = permalinkBuilder.permalinkForUser(UserId(mention.rawValue)).getOrNull() ?: continue
|
||||
replace(start, end, "[${mention.rawValue}]($link)")
|
||||
permalinkBuilder.permalinkForUser(UserId(mention.rawValue)).getOrNull()?.let { link ->
|
||||
replace(start, end, "[${mention.rawValue}]($link)")
|
||||
}
|
||||
}
|
||||
MentionSpan.Type.EVERYONE -> {
|
||||
replace(start, end, "@room")
|
||||
}
|
||||
// Nothing to do here yet
|
||||
MentionSpan.Type.ROOM -> Unit
|
||||
MentionSpan.Type.ROOM -> {
|
||||
permalinkBuilder.permalinkForRoomAlias(RoomAlias(mention.rawValue)).getOrNull()?.let { link ->
|
||||
replace(start, end, "[${mention.text}]($link)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -113,13 +129,13 @@ class MarkdownTextEditorState(
|
|||
}
|
||||
}
|
||||
|
||||
fun getMentions(): List<Mention> {
|
||||
fun getMentions(): List<IntentionalMention> {
|
||||
val text = SpannableString(text.value())
|
||||
val mentionSpans = text.getSpans<MentionSpan>(0, text.length)
|
||||
return mentionSpans.mapNotNull { mentionSpan ->
|
||||
when (mentionSpan.type) {
|
||||
MentionSpan.Type.USER -> Mention.User(UserId(mentionSpan.rawValue))
|
||||
MentionSpan.Type.EVERYONE -> Mention.AtRoom
|
||||
MentionSpan.Type.USER -> IntentionalMention.User(UserId(mentionSpan.rawValue))
|
||||
MentionSpan.Type.EVERYONE -> IntentionalMention.Room
|
||||
MentionSpan.Type.ROOM -> null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@
|
|||
|
||||
package io.element.android.libraries.textcomposer.model
|
||||
|
||||
import io.element.android.libraries.matrix.api.room.Mention
|
||||
import io.element.android.libraries.matrix.api.room.IntentionalMention
|
||||
|
||||
data class Message(
|
||||
val html: String?,
|
||||
val markdown: String,
|
||||
val mentions: List<Mention>,
|
||||
val intentionalMentions: List<IntentionalMention>,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ import io.element.android.libraries.textcomposer.components.markdown.MarkdownTex
|
|||
import io.element.android.libraries.textcomposer.components.markdown.aMarkdownTextEditorState
|
||||
import io.element.android.libraries.textcomposer.mentions.MentionSpan
|
||||
import io.element.android.libraries.textcomposer.mentions.MentionSpanProvider
|
||||
import io.element.android.libraries.textcomposer.mentions.ResolvedMentionSuggestion
|
||||
import io.element.android.libraries.textcomposer.mentions.ResolvedSuggestion
|
||||
import io.element.android.libraries.textcomposer.model.MarkdownTextEditorState
|
||||
import io.element.android.libraries.textcomposer.model.Suggestion
|
||||
import io.element.android.libraries.textcomposer.model.SuggestionType
|
||||
|
|
@ -157,13 +157,13 @@ class MarkdownTextInputTest {
|
|||
val permalinkParser = FakePermalinkParser(result = { PermalinkData.UserLink(A_SESSION_ID) })
|
||||
val permalinkBuilder = FakePermalinkBuilder(permalinkForUserLambda = { Result.success("https://matrix.to/#/$A_SESSION_ID") })
|
||||
val state = aMarkdownTextEditorState(initialText = "@", initialFocus = true)
|
||||
state.currentMentionSuggestion = Suggestion(0, 1, SuggestionType.Mention, "")
|
||||
state.currentSuggestion = Suggestion(0, 1, SuggestionType.Mention, "")
|
||||
rule.setMarkdownTextInput(state = state)
|
||||
var editor: EditText? = null
|
||||
rule.activityRule.scenario.onActivity {
|
||||
editor = it.findEditor()
|
||||
state.insertMention(
|
||||
ResolvedMentionSuggestion.Member(roomMember = aRoomMember()),
|
||||
state.insertSuggestion(
|
||||
ResolvedSuggestion.Member(roomMember = aRoomMember()),
|
||||
MentionSpanProvider(permalinkParser = permalinkParser),
|
||||
permalinkBuilder,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ import org.junit.runner.RunWith
|
|||
import org.robolectric.RobolectricTestRunner
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
class MentionSpanProviderTest {
|
||||
class IntentionalMentionSpanProviderTest {
|
||||
@JvmField @Rule
|
||||
val warmUpRule = WarmUpRule()
|
||||
|
||||
|
|
@ -21,14 +21,17 @@ import androidx.core.text.buildSpannedString
|
|||
import androidx.core.text.inSpans
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import io.element.android.libraries.matrix.api.core.toRoomIdOrAlias
|
||||
import io.element.android.libraries.matrix.api.permalink.PermalinkData
|
||||
import io.element.android.libraries.matrix.api.room.Mention
|
||||
import io.element.android.libraries.matrix.api.room.IntentionalMention
|
||||
import io.element.android.libraries.matrix.test.A_ROOM_ALIAS
|
||||
import io.element.android.libraries.matrix.test.permalink.FakePermalinkBuilder
|
||||
import io.element.android.libraries.matrix.test.permalink.FakePermalinkParser
|
||||
import io.element.android.libraries.matrix.test.room.aRoomMember
|
||||
import io.element.android.libraries.matrix.test.room.aRoomSummary
|
||||
import io.element.android.libraries.textcomposer.mentions.MentionSpan
|
||||
import io.element.android.libraries.textcomposer.mentions.MentionSpanProvider
|
||||
import io.element.android.libraries.textcomposer.mentions.ResolvedMentionSuggestion
|
||||
import io.element.android.libraries.textcomposer.mentions.ResolvedSuggestion
|
||||
import io.element.android.libraries.textcomposer.model.MarkdownTextEditorState
|
||||
import io.element.android.libraries.textcomposer.model.Suggestion
|
||||
import io.element.android.libraries.textcomposer.model.SuggestionType
|
||||
|
|
@ -38,68 +41,102 @@ import org.junit.runner.RunWith
|
|||
@RunWith(AndroidJUnit4::class)
|
||||
class MarkdownTextEditorStateTest {
|
||||
@Test
|
||||
fun `insertMention - with no currentMentionSuggestion does nothing`() {
|
||||
fun `insertMention - room alias - getMentions return empty list`() {
|
||||
val state = MarkdownTextEditorState(initialText = "Hello @", initialFocus = true)
|
||||
val suggestion = ResolvedSuggestion.Alias(A_ROOM_ALIAS, aRoomSummary(canonicalAlias = A_ROOM_ALIAS))
|
||||
val permalinkBuilder = FakePermalinkBuilder()
|
||||
val mentionSpanProvider = aMentionSpanProvider()
|
||||
state.insertSuggestion(suggestion, mentionSpanProvider, permalinkBuilder)
|
||||
assertThat(state.getMentions()).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `insertSuggestion - room alias - with member but failed PermalinkBuilder result`() {
|
||||
val state = MarkdownTextEditorState(initialText = "Hello #", initialFocus = true).apply {
|
||||
currentSuggestion = Suggestion(start = 6, end = 7, type = SuggestionType.Room, text = "")
|
||||
}
|
||||
val suggestion = ResolvedSuggestion.Alias(A_ROOM_ALIAS, aRoomSummary(canonicalAlias = A_ROOM_ALIAS))
|
||||
val permalinkParser = FakePermalinkParser(result = { PermalinkData.RoomLink(A_ROOM_ALIAS.toRoomIdOrAlias()) })
|
||||
val permalinkBuilder = FakePermalinkBuilder(permalinkForRoomAliasLambda = { Result.failure(IllegalStateException("Failed")) })
|
||||
val mentionSpanProvider = aMentionSpanProvider(permalinkParser = permalinkParser)
|
||||
state.insertSuggestion(suggestion, mentionSpanProvider, permalinkBuilder)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `insertSuggestion - room alias`() {
|
||||
val state = MarkdownTextEditorState(initialText = "Hello #", initialFocus = true).apply {
|
||||
currentSuggestion = Suggestion(start = 6, end = 7, type = SuggestionType.Room, text = "")
|
||||
}
|
||||
val suggestion = ResolvedSuggestion.Alias(A_ROOM_ALIAS, aRoomSummary(canonicalAlias = A_ROOM_ALIAS))
|
||||
val permalinkParser = FakePermalinkParser(result = { PermalinkData.RoomLink(A_ROOM_ALIAS.toRoomIdOrAlias()) })
|
||||
val permalinkBuilder = FakePermalinkBuilder(permalinkForRoomAliasLambda = { Result.success("https://matrix.to/#/${A_ROOM_ALIAS.value}") })
|
||||
val mentionSpanProvider = aMentionSpanProvider(permalinkParser = permalinkParser)
|
||||
state.insertSuggestion(suggestion, mentionSpanProvider, permalinkBuilder)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `insertSuggestion - with no currentMentionSuggestion does nothing`() {
|
||||
val state = MarkdownTextEditorState(initialText = "Hello @", initialFocus = true)
|
||||
val member = aRoomMember()
|
||||
val mention = ResolvedMentionSuggestion.Member(member)
|
||||
val mention = ResolvedSuggestion.Member(member)
|
||||
val permalinkBuilder = FakePermalinkBuilder()
|
||||
val mentionSpanProvider = aMentionSpanProvider()
|
||||
|
||||
state.insertMention(mention, mentionSpanProvider, permalinkBuilder)
|
||||
state.insertSuggestion(mention, mentionSpanProvider, permalinkBuilder)
|
||||
|
||||
assertThat(state.getMentions()).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `insertMention - with member but failed PermalinkBuilder result`() {
|
||||
fun `insertSuggestion - with member but failed PermalinkBuilder result`() {
|
||||
val state = MarkdownTextEditorState(initialText = "Hello @", initialFocus = true).apply {
|
||||
currentMentionSuggestion = Suggestion(start = 6, end = 7, type = SuggestionType.Mention, text = "")
|
||||
currentSuggestion = Suggestion(start = 6, end = 7, type = SuggestionType.Mention, text = "")
|
||||
}
|
||||
val member = aRoomMember()
|
||||
val mention = ResolvedMentionSuggestion.Member(member)
|
||||
val mention = ResolvedSuggestion.Member(member)
|
||||
val permalinkParser = FakePermalinkParser(result = { PermalinkData.UserLink(member.userId) })
|
||||
val permalinkBuilder = FakePermalinkBuilder(permalinkForUserLambda = { Result.failure(IllegalStateException("Failed")) })
|
||||
val mentionSpanProvider = aMentionSpanProvider(permalinkParser = permalinkParser)
|
||||
|
||||
state.insertMention(mention, mentionSpanProvider, permalinkBuilder)
|
||||
state.insertSuggestion(mention, mentionSpanProvider, permalinkBuilder)
|
||||
|
||||
val mentions = state.getMentions()
|
||||
assertThat(mentions).isEmpty()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `insertMention - with member`() {
|
||||
fun `insertSuggestion - with member`() {
|
||||
val state = MarkdownTextEditorState(initialText = "Hello @", initialFocus = true).apply {
|
||||
currentMentionSuggestion = Suggestion(start = 6, end = 7, type = SuggestionType.Mention, text = "")
|
||||
currentSuggestion = Suggestion(start = 6, end = 7, type = SuggestionType.Mention, text = "")
|
||||
}
|
||||
val member = aRoomMember()
|
||||
val mention = ResolvedMentionSuggestion.Member(member)
|
||||
val mention = ResolvedSuggestion.Member(member)
|
||||
val permalinkParser = FakePermalinkParser(result = { PermalinkData.UserLink(member.userId) })
|
||||
val permalinkBuilder = FakePermalinkBuilder(permalinkForUserLambda = { Result.success("https://matrix.to/#/${member.userId}") })
|
||||
val mentionSpanProvider = aMentionSpanProvider(permalinkParser = permalinkParser)
|
||||
|
||||
state.insertMention(mention, mentionSpanProvider, permalinkBuilder)
|
||||
state.insertSuggestion(mention, mentionSpanProvider, permalinkBuilder)
|
||||
|
||||
val mentions = state.getMentions()
|
||||
assertThat(mentions).isNotEmpty()
|
||||
assertThat((mentions.firstOrNull() as? Mention.User)?.userId).isEqualTo(member.userId)
|
||||
assertThat((mentions.firstOrNull() as? IntentionalMention.User)?.userId).isEqualTo(member.userId)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `insertMention - with @room`() {
|
||||
fun `insertSuggestion - with @room`() {
|
||||
val state = MarkdownTextEditorState(initialText = "Hello @", initialFocus = true).apply {
|
||||
currentMentionSuggestion = Suggestion(start = 6, end = 7, type = SuggestionType.Mention, text = "")
|
||||
currentSuggestion = Suggestion(start = 6, end = 7, type = SuggestionType.Mention, text = "")
|
||||
}
|
||||
val mention = ResolvedMentionSuggestion.AtRoom
|
||||
val mention = ResolvedSuggestion.AtRoom
|
||||
val permalinkBuilder = FakePermalinkBuilder()
|
||||
val permalinkParser = FakePermalinkParser(result = { PermalinkData.FallbackLink(Uri.EMPTY, false) })
|
||||
val mentionSpanProvider = aMentionSpanProvider(permalinkParser = permalinkParser)
|
||||
|
||||
state.insertMention(mention, mentionSpanProvider, permalinkBuilder)
|
||||
state.insertSuggestion(mention, mentionSpanProvider, permalinkBuilder)
|
||||
|
||||
val mentions = state.getMentions()
|
||||
assertThat(mentions).isNotEmpty()
|
||||
assertThat(mentions.firstOrNull()).isInstanceOf(Mention.AtRoom::class.java)
|
||||
assertThat(mentions.firstOrNull()).isInstanceOf(IntentionalMention.Room::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -115,14 +152,18 @@ class MarkdownTextEditorStateTest {
|
|||
@Test
|
||||
fun `getMessageMarkdown - when there are MentionSpans returns the same text with links to the mentions`() {
|
||||
val text = "No mentions here"
|
||||
val permalinkBuilder = FakePermalinkBuilder(permalinkForUserLambda = { Result.success("https://matrix.to/#/$it") })
|
||||
val permalinkBuilder = FakePermalinkBuilder(
|
||||
permalinkForUserLambda = { Result.success("https://matrix.to/#/$it") },
|
||||
permalinkForRoomAliasLambda = { Result.success("https://matrix.to/#/$it") },
|
||||
)
|
||||
val state = MarkdownTextEditorState(initialText = text, initialFocus = true)
|
||||
state.text.update(aMarkdownTextWithMentions(), needsDisplaying = false)
|
||||
|
||||
val markdown = state.getMessageMarkdown(permalinkBuilder = permalinkBuilder)
|
||||
|
||||
assertThat(markdown).isEqualTo(
|
||||
"Hello [@alice:matrix.org](https://matrix.to/#/@alice:matrix.org) and everyone in @room"
|
||||
"Hello [@alice:matrix.org](https://matrix.to/#/@alice:matrix.org) and everyone in @room" +
|
||||
" and a room [#room:domain.org](https://matrix.to/#/#room:domain.org)"
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -141,8 +182,8 @@ class MarkdownTextEditorStateTest {
|
|||
val mentions = state.getMentions()
|
||||
|
||||
assertThat(mentions).isNotEmpty()
|
||||
assertThat((mentions.firstOrNull() as? Mention.User)?.userId?.value).isEqualTo("@alice:matrix.org")
|
||||
assertThat(mentions.lastOrNull()).isInstanceOf(Mention.AtRoom::class.java)
|
||||
assertThat((mentions.firstOrNull() as? IntentionalMention.User)?.userId?.value).isEqualTo("@alice:matrix.org")
|
||||
assertThat(mentions.lastOrNull()).isInstanceOf(IntentionalMention.Room::class.java)
|
||||
}
|
||||
|
||||
private fun aMentionSpanProvider(
|
||||
|
|
@ -154,6 +195,7 @@ class MarkdownTextEditorStateTest {
|
|||
private fun aMarkdownTextWithMentions(): CharSequence {
|
||||
val userMentionSpan = MentionSpan("@Alice", "@alice:matrix.org", MentionSpan.Type.USER)
|
||||
val atRoomMentionSpan = MentionSpan("@room", "@room", MentionSpan.Type.EVERYONE)
|
||||
val roomMentionSpan = MentionSpan("#room:domain.org", "#room:domain.org", MentionSpan.Type.ROOM)
|
||||
return buildSpannedString {
|
||||
append("Hello ")
|
||||
inSpans(userMentionSpan) {
|
||||
|
|
@ -163,6 +205,10 @@ class MarkdownTextEditorStateTest {
|
|||
inSpans(atRoomMentionSpan) {
|
||||
append("@")
|
||||
}
|
||||
append(" and a room ")
|
||||
inSpans(roomMentionSpan) {
|
||||
append("#room:domain.org")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue