Skip to content

Commit 8ab391f

Browse files
authored
[Rich text editor] Add feature flag for rich text editor (#1289)
1 parent f327726 commit 8ab391f

File tree

16 files changed

+84
-25
lines changed

16 files changed

+84
-25
lines changed

changelog.d/1289.feature

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[Rich text editor] Add feature flag for rich text editor. Markdown support can now be enabled by disabling the rich text editor.

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesPresenter.kt

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ import io.element.android.libraries.designsystem.components.avatar.AvatarSize
6666
import io.element.android.libraries.designsystem.utils.SnackbarDispatcher
6767
import io.element.android.libraries.designsystem.utils.SnackbarMessage
6868
import io.element.android.libraries.designsystem.utils.collectSnackbarMessageAsState
69+
import io.element.android.libraries.featureflag.api.FeatureFlagService
70+
import io.element.android.libraries.featureflag.api.FeatureFlags
6971
import io.element.android.libraries.matrix.api.core.EventId
7072
import io.element.android.libraries.matrix.api.room.MatrixRoom
7173
import io.element.android.libraries.matrix.api.room.MatrixRoomMembersState
@@ -95,6 +97,7 @@ class MessagesPresenter @AssistedInject constructor(
9597
private val dispatchers: CoroutineDispatchers,
9698
private val clipboardHelper: ClipboardHelper,
9799
private val analyticsService: AnalyticsService,
100+
private val featureFlagService: FeatureFlagService,
98101
@Assisted private val navigator: MessagesNavigator,
99102
) : Presenter<MessagesState> {
100103

@@ -143,6 +146,11 @@ class MessagesPresenter @AssistedInject constructor(
143146
timelineState.eventSink(TimelineEvents.SetHighlightedEvent(composerState.mode.relatedEventId))
144147
}
145148

149+
var enableTextFormatting by remember { mutableStateOf(true) }
150+
LaunchedEffect(Unit) {
151+
enableTextFormatting = featureFlagService.isFeatureEnabled(FeatureFlags.RichTextEditor)
152+
}
153+
146154
fun handleEvents(event: MessagesEvents) {
147155
when (event) {
148156
is MessagesEvents.HandleAction -> {
@@ -178,6 +186,7 @@ class MessagesPresenter @AssistedInject constructor(
178186
snackbarMessage = snackbarMessage,
179187
showReinvitePrompt = showReinvitePrompt,
180188
inviteProgress = inviteProgress.value,
189+
enableTextFormatting = enableTextFormatting,
181190
eventSink = { handleEvents(it) }
182191
)
183192
}
@@ -250,11 +259,15 @@ class MessagesPresenter @AssistedInject constructor(
250259
}
251260
}
252261

253-
private fun handleActionEdit(targetEvent: TimelineItem.Event, composerState: MessageComposerState) {
262+
private suspend fun handleActionEdit(targetEvent: TimelineItem.Event, composerState: MessageComposerState) {
254263
val composerMode = MessageComposerMode.Edit(
255264
targetEvent.eventId,
256265
(targetEvent.content as? TimelineItemTextBasedContent)?.let {
257-
it.htmlBody ?: it.body
266+
if (featureFlagService.isFeatureEnabled(FeatureFlags.RichTextEditor)) {
267+
it.htmlBody ?: it.body
268+
} else {
269+
it.body
270+
}
258271
}.orEmpty(),
259272
targetEvent.transactionId,
260273
)

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesState.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,5 +45,6 @@ data class MessagesState(
4545
val snackbarMessage: SnackbarMessage?,
4646
val inviteProgress: Async<Unit>,
4747
val showReinvitePrompt: Boolean,
48+
val enableTextFormatting: Boolean,
4849
val eventSink: (MessagesEvents) -> Unit
4950
)

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesStateProvider.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,5 +82,6 @@ fun aMessagesState() = MessagesState(
8282
snackbarMessage = null,
8383
inviteProgress = Async.Uninitialized,
8484
showReinvitePrompt = false,
85+
enableTextFormatting = true,
8586
eventSink = {}
8687
)

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/MessagesView.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ private fun MessagesViewContent(
304304
state = state.composerState,
305305
onSendLocationClicked = onSendLocationClicked,
306306
onCreatePollClicked = onCreatePollClicked,
307+
enableTextFormatting = state.enableTextFormatting,
307308
modifier = Modifier
308309
.fillMaxWidth()
309310
.wrapContentHeight(Alignment.Bottom)

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/AttachmentsBottomSheet.kt

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ internal fun AttachmentsBottomSheet(
5555
state: MessageComposerState,
5656
onSendLocationClicked: () -> Unit,
5757
onCreatePollClicked: () -> Unit,
58+
enableTextFormatting: Boolean,
5859
modifier: Modifier = Modifier,
5960
) {
6061
val localView = LocalView.current
@@ -87,6 +88,7 @@ internal fun AttachmentsBottomSheet(
8788
) {
8889
AttachmentSourcePickerMenu(
8990
state = state,
91+
enableTextFormatting = enableTextFormatting,
9092
onSendLocationClicked = onSendLocationClicked,
9193
onCreatePollClicked = onCreatePollClicked,
9294
)
@@ -100,6 +102,7 @@ internal fun AttachmentSourcePickerMenu(
100102
state: MessageComposerState,
101103
onSendLocationClicked: () -> Unit,
102104
onCreatePollClicked: () -> Unit,
105+
enableTextFormatting: Boolean,
103106
modifier: Modifier = Modifier,
104107
) {
105108
Column(
@@ -146,11 +149,13 @@ internal fun AttachmentSourcePickerMenu(
146149
text = { Text(stringResource(R.string.screen_room_attachment_source_poll)) },
147150
)
148151
}
149-
ListItem(
150-
modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.ToggleTextFormatting(enabled = true)) },
151-
icon = { Icon(Icons.Default.FormatColorText, null) },
152-
text = { Text(stringResource(R.string.screen_room_attachment_text_formatting)) },
153-
)
152+
if (enableTextFormatting) {
153+
ListItem(
154+
modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.ToggleTextFormatting(enabled = true)) },
155+
icon = { Icon(Icons.Default.FormatColorText, null) },
156+
text = { Text(stringResource(R.string.screen_room_attachment_text_formatting)) },
157+
)
158+
}
154159
}
155160
}
156161

@@ -163,5 +168,6 @@ internal fun AttachmentSourcePickerMenuPreview() = ElementPreview {
163168
),
164169
onSendLocationClicked = {},
165170
onCreatePollClicked = {},
171+
enableTextFormatting = true,
166172
)
167173
}

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/messagecomposer/MessageComposerView.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ fun MessageComposerView(
3131
state: MessageComposerState,
3232
onSendLocationClicked: () -> Unit,
3333
onCreatePollClicked: () -> Unit,
34+
enableTextFormatting: Boolean,
3435
modifier: Modifier = Modifier,
3536
) {
3637
fun onFullscreenToggle() {
@@ -62,6 +63,7 @@ fun MessageComposerView(
6263
state = state,
6364
onSendLocationClicked = onSendLocationClicked,
6465
onCreatePollClicked = onCreatePollClicked,
66+
enableTextFormatting = enableTextFormatting,
6567
)
6668

6769
TextComposer(
@@ -74,6 +76,7 @@ fun MessageComposerView(
7476
onResetComposerMode = ::onCloseSpecialMode,
7577
onAddAttachment = ::onAddAttachment,
7678
onDismissTextFormatting = ::onDismissTextFormatting,
79+
enableTextFormatting = enableTextFormatting,
7780
onError = ::onError,
7881
)
7982
}
@@ -95,5 +98,6 @@ private fun ContentToPreview(state: MessageComposerState) {
9598
state = state,
9699
onSendLocationClicked = {},
97100
onCreatePollClicked = {},
101+
enableTextFormatting = true,
98102
)
99103
}

features/messages/impl/src/test/kotlin/io/element/android/features/messages/MessagesPresenterTest.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,7 @@ class MessagesPresenterTest {
628628
val customReactionPresenter = CustomReactionPresenter(emojibaseProvider = FakeEmojibaseProvider())
629629
val reactionSummaryPresenter = ReactionSummaryPresenter(room = matrixRoom)
630630
val retrySendMenuPresenter = RetrySendMenuPresenter(room = matrixRoom)
631+
val featureFlagsService = FakeFeatureFlagService(mapOf(FeatureFlags.RichTextEditor.key to true))
631632
return MessagesPresenter(
632633
room = matrixRoom,
633634
composerPresenter = messageComposerPresenter,
@@ -642,6 +643,7 @@ class MessagesPresenterTest {
642643
navigator = navigator,
643644
clipboardHelper = clipboardHelper,
644645
analyticsService = analyticsService,
646+
featureFlagService = featureFlagsService,
645647
dispatchers = coroutineDispatchers,
646648
)
647649
}

gradle/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ dependencyanalysis = "1.21.0"
4646
stem = "2.3.0"
4747
sqldelight = "1.5.5"
4848
telephoto = "0.6.0"
49-
wysiwyg = "2.9.0"
49+
wysiwyg = "2.10.0"
5050

5151
# DI
5252
dagger = "2.48"

libraries/featureflag/api/src/main/kotlin/io/element/android/libraries/featureflag/api/FeatureFlags.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,9 @@ enum class FeatureFlags(
4444
// Do not forget to edit StaticFeatureFlagProvider when enabling the feature.
4545
defaultValue = false,
4646
),
47+
RichTextEditor(
48+
key = "feature.richtexteditor",
49+
title = "Enable rich text editor",
50+
defaultValue = true,
51+
),
4752
}

libraries/featureflag/impl/src/main/kotlin/io/element/android/libraries/featureflag/impl/StaticFeatureFlagProvider.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ class StaticFeatureFlagProvider @Inject constructor() :
3535
FeatureFlags.LocationSharing -> true
3636
FeatureFlags.Polls -> true
3737
FeatureFlags.NotificationSettings -> false
38+
FeatureFlags.RichTextEditor -> true
3839
}
3940
} else {
4041
false

libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/room/MatrixRoom.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,11 @@ interface MatrixRoom : Closeable {
7979

8080
suspend fun userAvatarUrl(userId: UserId): Result<String?>
8181

82-
suspend fun sendMessage(body: String, htmlBody: String): Result<Unit>
82+
suspend fun sendMessage(body: String, htmlBody: String?): Result<Unit>
8383

84-
suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String): Result<Unit>
84+
suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?): Result<Unit>
8585

86-
suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String): Result<Unit>
86+
suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?): Result<Unit>
8787

8888
suspend fun redactEvent(eventId: EventId, reason: String? = null): Result<Unit>
8989

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/room/RustMatrixRoom.kt

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -63,10 +63,12 @@ import org.matrix.rustcomponents.sdk.RequiredState
6363
import org.matrix.rustcomponents.sdk.Room
6464
import org.matrix.rustcomponents.sdk.RoomListItem
6565
import org.matrix.rustcomponents.sdk.RoomMember
66+
import org.matrix.rustcomponents.sdk.RoomMessageEventContentWithoutRelation
6667
import org.matrix.rustcomponents.sdk.RoomSubscription
6768
import org.matrix.rustcomponents.sdk.SendAttachmentJoinHandle
6869
import org.matrix.rustcomponents.sdk.genTransactionId
6970
import org.matrix.rustcomponents.sdk.messageEventContentFromHtml
71+
import org.matrix.rustcomponents.sdk.messageEventContentFromMarkdown
7072
import timber.log.Timber
7173
import java.io.File
7274

@@ -227,32 +229,32 @@ class RustMatrixRoom(
227229
}
228230
}
229231

230-
override suspend fun sendMessage(body: String, htmlBody: String): Result<Unit> = withContext(roomDispatcher) {
232+
override suspend fun sendMessage(body: String, htmlBody: String?): Result<Unit> = withContext(roomDispatcher) {
231233
val transactionId = genTransactionId()
232-
messageEventContentFromHtml(body, htmlBody).use { content ->
234+
messageEventContentFromParts(body, htmlBody).use { content ->
233235
runCatching {
234236
innerRoom.send(content, transactionId)
235237
}
236238
}
237239
}
238240

239-
override suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String): Result<Unit> =
241+
override suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?): Result<Unit> =
240242
withContext(roomDispatcher) {
241243
if (originalEventId != null) {
242244
runCatching {
243-
innerRoom.edit(messageEventContentFromHtml(body, htmlBody), originalEventId.value, transactionId?.value)
245+
innerRoom.edit(messageEventContentFromParts(body, htmlBody), originalEventId.value, transactionId?.value)
244246
}
245247
} else {
246248
runCatching {
247249
transactionId?.let { cancelSend(it) }
248-
innerRoom.send(messageEventContentFromHtml(body, htmlBody), genTransactionId())
250+
innerRoom.send(messageEventContentFromParts(body, htmlBody), genTransactionId())
249251
}
250252
}
251253
}
252254

253-
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String): Result<Unit> = withContext(roomDispatcher) {
255+
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?): Result<Unit> = withContext(roomDispatcher) {
254256
runCatching {
255-
innerRoom.sendReply(messageEventContentFromHtml(body, htmlBody), eventId.value, genTransactionId())
257+
innerRoom.sendReply(messageEventContentFromParts(body, htmlBody), eventId.value, genTransactionId())
256258
}
257259
}
258260

@@ -456,4 +458,11 @@ class RustMatrixRoom(
456458
MediaUploadHandlerImpl(files, handle())
457459
}
458460
}
461+
462+
private fun messageEventContentFromParts(body: String, htmlBody: String?): RoomMessageEventContentWithoutRelation =
463+
if(htmlBody != null) {
464+
messageEventContentFromHtml(body, htmlBody)
465+
} else {
466+
messageEventContentFromMarkdown(body)
467+
}
459468
}

libraries/matrix/test/src/main/kotlin/io/element/android/libraries/matrix/test/room/FakeMatrixRoom.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ class FakeMatrixRoom(
9292
private var sendPollResponseResult = Result.success(Unit)
9393
private var endPollResult = Result.success(Unit)
9494
private var progressCallbackValues = emptyList<Pair<Long, Long>>()
95-
val editMessageCalls = mutableListOf<Pair<String, String>>()
95+
val editMessageCalls = mutableListOf<Pair<String, String?>>()
9696

9797
var sendMediaCount = 0
9898
private set
@@ -171,7 +171,7 @@ class FakeMatrixRoom(
171171
userAvatarUrlResult
172172
}
173173

174-
override suspend fun sendMessage(body: String, htmlBody: String) = simulateLongTask {
174+
override suspend fun sendMessage(body: String, htmlBody: String?) = simulateLongTask {
175175
Result.success(Unit)
176176
}
177177

@@ -200,15 +200,15 @@ class FakeMatrixRoom(
200200
return cancelSendResult
201201
}
202202

203-
override suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String): Result<Unit> {
203+
override suspend fun editMessage(originalEventId: EventId?, transactionId: TransactionId?, body: String, htmlBody: String?): Result<Unit> {
204204
editMessageCalls += body to htmlBody
205205
return Result.success(Unit)
206206
}
207207

208-
var replyMessageParameter: Pair<String, String>? = null
208+
var replyMessageParameter: Pair<String, String?>? = null
209209
private set
210210

211-
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String): Result<Unit> {
211+
override suspend fun replyMessage(eventId: EventId, body: String, htmlBody: String?): Result<Unit> {
212212
replyMessageParameter = body to htmlBody
213213
return Result.success(Unit)
214214
}

libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/Message.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,6 @@
1717
package io.element.android.libraries.textcomposer
1818

1919
data class Message(
20-
val html: String,
20+
val html: String?,
2121
val markdown: String,
2222
)

0 commit comments

Comments
 (0)