[Rich text editor] Add feature flag for rich text editor (#1289)
This commit is contained in:
@@ -44,4 +44,9 @@ enum class FeatureFlags(
|
||||
// Do not forget to edit StaticFeatureFlagProvider when enabling the feature.
|
||||
defaultValue = false,
|
||||
),
|
||||
RichTextEditor(
|
||||
key = "feature.richtexteditor",
|
||||
title = "Enable rich text editor",
|
||||
defaultValue = true,
|
||||
),
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ class StaticFeatureFlagProvider @Inject constructor() :
|
||||
FeatureFlags.LocationSharing -> true
|
||||
FeatureFlags.Polls -> true
|
||||
FeatureFlags.NotificationSettings -> false
|
||||
FeatureFlags.RichTextEditor -> true
|
||||
}
|
||||
} else {
|
||||
false
|
||||
|
||||
@@ -79,11 +79,11 @@ interface MatrixRoom : Closeable {
|
||||
|
||||
suspend fun userAvatarUrl(userId: UserId): Result<String?>
|
||||
|
||||
suspend fun sendMessage(body: String, htmlBody: String): Result<Unit>
|
||||
suspend fun sendMessage(body: String, htmlBody: String?): 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?): Result<Unit>
|
||||
|
||||
suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String): Result<Unit>
|
||||
suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?): Result<Unit>
|
||||
|
||||
suspend fun redactEvent(eventId: EventId, reason: String? = null): Result<Unit>
|
||||
|
||||
|
||||
@@ -63,10 +63,12 @@ import org.matrix.rustcomponents.sdk.RequiredState
|
||||
import org.matrix.rustcomponents.sdk.Room
|
||||
import org.matrix.rustcomponents.sdk.RoomListItem
|
||||
import org.matrix.rustcomponents.sdk.RoomMember
|
||||
import org.matrix.rustcomponents.sdk.RoomMessageEventContentWithoutRelation
|
||||
import org.matrix.rustcomponents.sdk.RoomSubscription
|
||||
import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle
|
||||
import org.matrix.rustcomponents.sdk.genTransactionId
|
||||
import org.matrix.rustcomponents.sdk.messageEventContentFromHtml
|
||||
import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
||||
@@ -227,32 +229,32 @@ class RustMatrixRoom(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(body: String, htmlBody: String): Result<Unit> = withContext(roomDispatcher) {
|
||||
override suspend fun sendMessage(body: String, htmlBody: String?): Result<Unit> = withContext(roomDispatcher) {
|
||||
val transactionId = genTransactionId()
|
||||
messageEventContentFromHtml(body, htmlBody).use { content ->
|
||||
messageEventContentFromParts(body, htmlBody).use { content ->
|
||||
runCatching {
|
||||
innerRoom.send(content, transactionId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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?): Result<Unit> =
|
||||
withContext(roomDispatcher) {
|
||||
if (originalEventId != null) {
|
||||
runCatching {
|
||||
innerRoom.edit(messageEventContentFromHtml(body, htmlBody), originalEventId.value, transactionId?.value)
|
||||
innerRoom.edit(messageEventContentFromParts(body, htmlBody), originalEventId.value, transactionId?.value)
|
||||
}
|
||||
} else {
|
||||
runCatching {
|
||||
transactionId?.let { cancelSend(it) }
|
||||
innerRoom.send(messageEventContentFromHtml(body, htmlBody), genTransactionId())
|
||||
innerRoom.send(messageEventContentFromParts(body, htmlBody), genTransactionId())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String): Result<Unit> = withContext(roomDispatcher) {
|
||||
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?): Result<Unit> = withContext(roomDispatcher) {
|
||||
runCatching {
|
||||
innerRoom.sendReply(messageEventContentFromHtml(body, htmlBody), eventId.value, genTransactionId())
|
||||
innerRoom.sendReply(messageEventContentFromParts(body, htmlBody), eventId.value, genTransactionId())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -456,4 +458,11 @@ class RustMatrixRoom(
|
||||
MediaUploadHandlerImpl(files, handle())
|
||||
}
|
||||
}
|
||||
|
||||
private fun messageEventContentFromParts(body: String, htmlBody: String?): RoomMessageEventContentWithoutRelation =
|
||||
if(htmlBody != null) {
|
||||
messageEventContentFromHtml(body, htmlBody)
|
||||
} else {
|
||||
messageEventContentFromMarkdown(body)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ class FakeMatrixRoom(
|
||||
private var sendPollResponseResult = Result.success(Unit)
|
||||
private var endPollResult = Result.success(Unit)
|
||||
private var progressCallbackValues = emptyList<Pair<Long, Long>>()
|
||||
val editMessageCalls = mutableListOf<Pair<String, String>>()
|
||||
val editMessageCalls = mutableListOf<Pair<String, String?>>()
|
||||
|
||||
var sendMediaCount = 0
|
||||
private set
|
||||
@@ -171,7 +171,7 @@ class FakeMatrixRoom(
|
||||
userAvatarUrlResult
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(body: String, htmlBody: String) = simulateLongTask {
|
||||
override suspend fun sendMessage(body: String, htmlBody: String?) = simulateLongTask {
|
||||
Result.success(Unit)
|
||||
}
|
||||
|
||||
@@ -200,15 +200,15 @@ class FakeMatrixRoom(
|
||||
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?): Result<Unit> {
|
||||
editMessageCalls += body to htmlBody
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
var replyMessageParameter: Pair<String, String>? = null
|
||||
var replyMessageParameter: Pair<String, String?>? = null
|
||||
private set
|
||||
|
||||
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String): Result<Unit> {
|
||||
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?): Result<Unit> {
|
||||
replyMessageParameter = body to htmlBody
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,6 @@
|
||||
package io.element.android.libraries.textcomposer
|
||||
|
||||
data class Message(
|
||||
val html: String,
|
||||
val html: String?,
|
||||
val markdown: String,
|
||||
)
|
||||
|
||||
@@ -91,6 +91,7 @@ fun TextComposer(
|
||||
state: RichTextEditorState,
|
||||
composerMode: MessageComposerMode,
|
||||
canSendMessage: Boolean,
|
||||
enableTextFormatting: Boolean,
|
||||
modifier: Modifier = Modifier,
|
||||
showTextFormatting: Boolean = false,
|
||||
onRequestFocus: () -> Unit = {},
|
||||
@@ -101,7 +102,8 @@ fun TextComposer(
|
||||
onError: (Throwable) -> Unit = {},
|
||||
) {
|
||||
val onSendClicked = {
|
||||
onSendMessage(Message(html = state.messageHtml, markdown = state.messageMarkdown))
|
||||
val html = if (enableTextFormatting) state.messageHtml else null
|
||||
onSendMessage(Message(html = html, markdown = state.messageMarkdown))
|
||||
}
|
||||
|
||||
Column(
|
||||
@@ -600,6 +602,7 @@ internal fun TextComposerSimplePreview() = ElementPreview {
|
||||
onSendMessage = {},
|
||||
composerMode = MessageComposerMode.Normal(""),
|
||||
onResetComposerMode = {},
|
||||
enableTextFormatting = true,
|
||||
)
|
||||
TextComposer(
|
||||
RichTextEditorState("A message", fake = true).apply { requestFocus() },
|
||||
@@ -607,6 +610,7 @@ internal fun TextComposerSimplePreview() = ElementPreview {
|
||||
onSendMessage = {},
|
||||
composerMode = MessageComposerMode.Normal(""),
|
||||
onResetComposerMode = {},
|
||||
enableTextFormatting = true,
|
||||
)
|
||||
TextComposer(
|
||||
RichTextEditorState(
|
||||
@@ -619,6 +623,7 @@ internal fun TextComposerSimplePreview() = ElementPreview {
|
||||
onSendMessage = {},
|
||||
composerMode = MessageComposerMode.Normal(""),
|
||||
onResetComposerMode = {},
|
||||
enableTextFormatting = true,
|
||||
)
|
||||
TextComposer(
|
||||
RichTextEditorState("A message without focus", fake = true),
|
||||
@@ -626,6 +631,7 @@ internal fun TextComposerSimplePreview() = ElementPreview {
|
||||
onSendMessage = {},
|
||||
composerMode = MessageComposerMode.Normal(""),
|
||||
onResetComposerMode = {},
|
||||
enableTextFormatting = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -639,18 +645,21 @@ internal fun TextComposerFormattingPreview() = ElementPreview {
|
||||
canSendMessage = false,
|
||||
showTextFormatting = true,
|
||||
composerMode = MessageComposerMode.Normal(""),
|
||||
enableTextFormatting = true,
|
||||
)
|
||||
TextComposer(
|
||||
RichTextEditorState("A message", fake = true),
|
||||
canSendMessage = true,
|
||||
showTextFormatting = true,
|
||||
composerMode = MessageComposerMode.Normal(""),
|
||||
enableTextFormatting = true,
|
||||
)
|
||||
TextComposer(
|
||||
RichTextEditorState("A message\nWith several lines\nTo preview larger textfields and long lines with overflow", fake = true),
|
||||
canSendMessage = true,
|
||||
showTextFormatting = true,
|
||||
composerMode = MessageComposerMode.Normal(""),
|
||||
enableTextFormatting = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -664,6 +673,7 @@ internal fun TextComposerEditPreview() = ElementPreview {
|
||||
onSendMessage = {},
|
||||
composerMode = MessageComposerMode.Edit(EventId("$1234"), "Some text", TransactionId("1234")),
|
||||
onResetComposerMode = {},
|
||||
enableTextFormatting = true,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -684,6 +694,7 @@ internal fun TextComposerReplyPreview() = ElementPreview {
|
||||
"To preview larger textfields and long lines with overflow"
|
||||
),
|
||||
onResetComposerMode = {},
|
||||
enableTextFormatting = true,
|
||||
)
|
||||
TextComposer(
|
||||
RichTextEditorState("A message", fake = true),
|
||||
@@ -701,6 +712,7 @@ internal fun TextComposerReplyPreview() = ElementPreview {
|
||||
defaultContent = "image.jpg"
|
||||
),
|
||||
onResetComposerMode = {},
|
||||
enableTextFormatting = true,
|
||||
)
|
||||
TextComposer(
|
||||
RichTextEditorState("A message", fake = true),
|
||||
@@ -718,6 +730,7 @@ internal fun TextComposerReplyPreview() = ElementPreview {
|
||||
defaultContent = "video.mp4"
|
||||
),
|
||||
onResetComposerMode = {},
|
||||
enableTextFormatting = true,
|
||||
)
|
||||
TextComposer(
|
||||
RichTextEditorState("A message", fake = true),
|
||||
@@ -735,6 +748,7 @@ internal fun TextComposerReplyPreview() = ElementPreview {
|
||||
defaultContent = "logs.txt"
|
||||
),
|
||||
onResetComposerMode = {},
|
||||
enableTextFormatting = true,
|
||||
)
|
||||
TextComposer(
|
||||
RichTextEditorState("A message", fake = true).apply { requestFocus() },
|
||||
@@ -752,6 +766,7 @@ internal fun TextComposerReplyPreview() = ElementPreview {
|
||||
defaultContent = "Shared location"
|
||||
),
|
||||
onResetComposerMode = {},
|
||||
enableTextFormatting = true,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user