[Rich text editor] Add formatting menu (#1261)

---------

Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
jonnyandrew 2023-09-08 17:23:15 +01:00 committed by GitHub
parent 7564d195b5
commit a05f4d8999
55 changed files with 706 additions and 210 deletions

View file

@ -26,6 +26,7 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.AttachFile
import androidx.compose.material.icons.filled.BarChart
import androidx.compose.material.icons.filled.Collections
import androidx.compose.material.icons.filled.FormatColorText
import androidx.compose.material.icons.filled.LocationOn
import androidx.compose.material.icons.filled.PhotoCamera
import androidx.compose.material.icons.filled.Videocam
@ -145,6 +146,11 @@ internal fun AttachmentSourcePickerMenu(
text = { Text(stringResource(R.string.screen_room_attachment_source_poll)) },
)
}
ListItem(
modifier = Modifier.clickable { state.eventSink(MessageComposerEvents.ToggleTextFormatting(enabled = true)) },
icon = { Icon(Icons.Default.FormatColorText, null) },
text = { Text(stringResource(R.string.screen_room_attachment_text_formatting)) },
)
}
}

View file

@ -36,6 +36,7 @@ sealed interface MessageComposerEvents {
data object Location : PickAttachmentSource
data object Poll : PickAttachmentSource
}
data class ToggleTextFormatting(val enabled: Boolean) : MessageComposerEvents
data object CancelSendAttachment : MessageComposerEvents
data class Error(val error: Throwable) : MessageComposerEvents
}

View file

@ -110,6 +110,7 @@ class MessageComposerPresenter @Inject constructor(
val ongoingSendAttachmentJob = remember { mutableStateOf<Job?>(null) }
var showAttachmentSourcePicker: Boolean by remember { mutableStateOf(false) }
var showTextFormatting: Boolean by remember { mutableStateOf(false) }
LaunchedEffect(messageComposerContext.composerMode) {
when (val modeValue = messageComposerContext.composerMode) {
@ -190,6 +191,10 @@ class MessageComposerPresenter @Inject constructor(
ongoingSendAttachmentJob.value == null
}
}
is MessageComposerEvents.ToggleTextFormatting -> {
showAttachmentSourcePicker = false
showTextFormatting = event.enabled
}
is MessageComposerEvents.Error -> {
analyticsService.trackError(event.error)
}
@ -201,6 +206,7 @@ class MessageComposerPresenter @Inject constructor(
isFullScreen = isFullScreen.value,
mode = messageComposerContext.composerMode,
showAttachmentSourcePicker = showAttachmentSourcePicker,
showTextFormatting = showTextFormatting,
canShareLocation = canShareLocation.value,
canCreatePoll = canCreatePoll.value,
attachmentsState = attachmentsState.value,

View file

@ -28,6 +28,7 @@ data class MessageComposerState(
val isFullScreen: Boolean,
val mode: MessageComposerMode,
val showAttachmentSourcePicker: Boolean,
val showTextFormatting: Boolean,
val canShareLocation: Boolean,
val canCreatePoll: Boolean,
val attachmentsState: AttachmentsState,

View file

@ -32,6 +32,7 @@ fun aMessageComposerState(
composerState: RichTextEditorState = RichTextEditorState("", fake = true),
isFullScreen: Boolean = false,
mode: MessageComposerMode = MessageComposerMode.Normal(content = ""),
showTextFormatting: Boolean = false,
showAttachmentSourcePicker: Boolean = false,
canShareLocation: Boolean = true,
canCreatePoll: Boolean = true,
@ -40,6 +41,7 @@ fun aMessageComposerState(
richTextEditorState = composerState.apply { if(requestFocus) requestFocus() },
isFullScreen = isFullScreen,
mode = mode,
showTextFormatting = showTextFormatting,
showAttachmentSourcePicker = showAttachmentSourcePicker,
canShareLocation = canShareLocation,
canCreatePoll = canCreatePoll,

View file

@ -49,6 +49,10 @@ fun MessageComposerView(
state.eventSink(MessageComposerEvents.CloseSpecialMode)
}
fun onDismissTextFormatting() {
state.eventSink(MessageComposerEvents.ToggleTextFormatting(enabled = false))
}
fun onError(error: Throwable) {
state.eventSink(MessageComposerEvents.Error(error))
}
@ -66,8 +70,10 @@ fun MessageComposerView(
onRequestFocus = { state.richTextEditorState.requestFocus() },
onSendMessage = ::sendMessage,
composerMode = state.mode,
showTextFormatting = state.showTextFormatting,
onResetComposerMode = ::onCloseSpecialMode,
onAddAttachment = ::onAddAttachment,
onDismissTextFormatting = ::onDismissTextFormatting,
onError = ::onError,
)
}

View file

@ -539,6 +539,29 @@ class MessageComposerPresenterTest {
}
}
@Test
fun `present - ToggleTextFormatting toggles text formatting`() = runTest {
val presenter = createPresenter(this)
moleculeFlow(RecompositionMode.Immediate) {
presenter.present()
}.test {
skipItems(1)
val initialState = awaitItem()
assertThat(initialState.showTextFormatting).isFalse()
initialState.eventSink(MessageComposerEvents.AddAttachment)
val composerOptions = awaitItem()
assertThat(composerOptions.showAttachmentSourcePicker).isTrue()
composerOptions.eventSink(MessageComposerEvents.ToggleTextFormatting(true))
awaitItem() // composer options closed
val showTextFormatting = awaitItem()
assertThat(showTextFormatting.showAttachmentSourcePicker).isFalse()
assertThat(showTextFormatting.showTextFormatting).isTrue()
showTextFormatting.eventSink(MessageComposerEvents.ToggleTextFormatting(false))
val finished = awaitItem()
assertThat(finished.showTextFormatting).isFalse()
}
}
private suspend fun ReceiveTurbine<MessageComposerState>.backToNormalMode(state: MessageComposerState, skipCount: Int = 0) {
state.eventSink.invoke(MessageComposerEvents.CloseSpecialMode)
skipItems(skipCount)