From a7f4adf7ad03836c3bbb9502c2d139e9bd575209 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Tue, 13 Jun 2023 14:26:32 +0200 Subject: [PATCH 01/14] chore: update kalium reference --- kalium | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kalium b/kalium index 76e6a38757f..e353d3d0016 160000 --- a/kalium +++ b/kalium @@ -1 +1 @@ -Subproject commit 76e6a38757f039879ecb997cfa955746947140ed +Subproject commit e353d3d0016ae8338c795e79ec1d24c31a7f81cc From 3b875bddc1f4a19c5519fb250d4b669183c431c9 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Tue, 13 Jun 2023 14:27:06 +0200 Subject: [PATCH 02/14] feat: add text formatting buttons and drawables --- .../home/messagecomposer/RichTextOptions.kt | 152 ++++++++++++++++++ .../main/res/drawable/ic_rich_text_bold.xml | 10 ++ .../main/res/drawable/ic_rich_text_header.xml | 10 ++ .../main/res/drawable/ic_rich_text_italic.xml | 10 ++ app/src/main/res/values/strings.xml | 3 + 5 files changed, 185 insertions(+) create mode 100644 app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/RichTextOptions.kt create mode 100644 app/src/main/res/drawable/ic_rich_text_bold.xml create mode 100644 app/src/main/res/drawable/ic_rich_text_header.xml create mode 100644 app/src/main/res/drawable/ic_rich_text_italic.xml diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/RichTextOptions.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/RichTextOptions.kt new file mode 100644 index 00000000000..b0b5febafbd --- /dev/null +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/RichTextOptions.kt @@ -0,0 +1,152 @@ +/* + * Wire + * Copyright (C) 2023 Wire Swiss GmbH + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see http://www.gnu.org/licenses/. + */ +package com.wire.android.ui.home.messagecomposer + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import com.wire.android.R +import com.wire.android.ui.common.button.WireSecondaryIconButton +import com.wire.android.ui.common.dimensions +import com.wire.android.ui.theme.wireDimensions + +@Composable +fun RichTextOptions( + onRichTextHeaderButtonClicked: () -> Unit, + onRichTextBoldButtonClicked: () -> Unit, + onRichTextItalicButtonClicked: () -> Unit, + onCloseRichTextEditingButtonClicked: () -> Unit +) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Absolute.SpaceEvenly, + modifier = Modifier + .fillMaxWidth() + .fillMaxHeight() + ) { + val modifier = Modifier + .fillMaxWidth() + .weight(1f) + .padding(horizontal = dimensions().spacing0x) + + HeaderButton( + modifier = modifier, + onRichTextHeaderButtonClicked = onRichTextHeaderButtonClicked + ) + BoldButton( + modifier = modifier, + onRichTextBoldButtonClicked = onRichTextBoldButtonClicked + ) + ItalicButton( + modifier = modifier, + onRichTextItalicButtonClicked = onRichTextItalicButtonClicked, + ) + CloseButton( + onCloseRichTextEditingButtonClicked = onCloseRichTextEditingButtonClicked + ) + } +} + +@Composable +private fun HeaderButton( + modifier: Modifier, + onRichTextHeaderButtonClicked: () -> Unit +) { + WireSecondaryIconButton( + onButtonClicked = onRichTextHeaderButtonClicked, + iconResource = R.drawable.ic_rich_text_header, + contentDescription = R.string.content_description_conversation_rich_text_header, + iconModifier = modifier, + modifier = modifier + .padding(start = dimensions().spacing8x), + shape = RoundedCornerShape( + topStart = MaterialTheme.wireDimensions.buttonCornerSize, + bottomStart = MaterialTheme.wireDimensions.buttonCornerSize, + topEnd = MaterialTheme.wireDimensions.spacing0x, + bottomEnd = MaterialTheme.wireDimensions.spacing0x + ) + ) +} + +@Composable +private fun BoldButton( + modifier: Modifier, + onRichTextBoldButtonClicked: () -> Unit +) { + WireSecondaryIconButton( + onButtonClicked = onRichTextBoldButtonClicked, + iconResource = R.drawable.ic_rich_text_bold, + contentDescription = R.string.content_description_conversation_rich_text_bold, + iconModifier = modifier, + modifier = modifier, + shape = RoundedCornerShape( + topStart = MaterialTheme.wireDimensions.spacing0x, + bottomStart = MaterialTheme.wireDimensions.spacing0x, + topEnd = MaterialTheme.wireDimensions.spacing0x, + bottomEnd = MaterialTheme.wireDimensions.spacing0x + ) + ) +} + +@Composable +private fun ItalicButton( + modifier: Modifier, + onRichTextItalicButtonClicked: () -> Unit +) { + WireSecondaryIconButton( + onButtonClicked = onRichTextItalicButtonClicked, + iconResource = R.drawable.ic_rich_text_italic, + contentDescription = R.string.content_description_conversation_rich_text_italic, + iconModifier = modifier, + modifier = modifier, + shape = RoundedCornerShape( + topStart = MaterialTheme.wireDimensions.spacing0x, + bottomStart = MaterialTheme.wireDimensions.spacing0x, + topEnd = MaterialTheme.wireDimensions.buttonCornerSize, + bottomEnd = MaterialTheme.wireDimensions.buttonCornerSize + ) + ) +} + +@Composable +private fun CloseButton( + onCloseRichTextEditingButtonClicked: () -> Unit +) { + IconButton( + onClick = onCloseRichTextEditingButtonClicked, + modifier = Modifier + .padding(end = dimensions().spacing8x) + ) { + Icon( + imageVector = Icons.Filled.Close, + contentDescription = stringResource(R.string.content_description_close_button) + ) + } +} diff --git a/app/src/main/res/drawable/ic_rich_text_bold.xml b/app/src/main/res/drawable/ic_rich_text_bold.xml new file mode 100644 index 00000000000..10b63ca9dbe --- /dev/null +++ b/app/src/main/res/drawable/ic_rich_text_bold.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_rich_text_header.xml b/app/src/main/res/drawable/ic_rich_text_header.xml new file mode 100644 index 00000000000..8af31229f10 --- /dev/null +++ b/app/src/main/res/drawable/ic_rich_text_header.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_rich_text_italic.xml b/app/src/main/res/drawable/ic_rich_text_italic.xml new file mode 100644 index 00000000000..b4fc379f5fd --- /dev/null +++ b/app/src/main/res/drawable/ic_rich_text_italic.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d98ff7555e3..c2e487b1e35 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -93,6 +93,9 @@ Wire Conversation search icon Enable rich text mode button + Rich text formatting Header + Rich text formatting Bold + Rich text formatting Italic Send Emoticon button Send GIF button Mention someone From a7ea7a8448c04022aedd8c45c34df1d16eac6411 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Tue, 13 Jun 2023 14:31:56 +0200 Subject: [PATCH 03/14] feat: make rich text icon feature visible --- .../com/wire/android/util/debug/FeatureVisibilityFlags.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/kotlin/com/wire/android/util/debug/FeatureVisibilityFlags.kt b/app/src/main/kotlin/com/wire/android/util/debug/FeatureVisibilityFlags.kt index 37cd4cd1d0e..3b9c44f834e 100644 --- a/app/src/main/kotlin/com/wire/android/util/debug/FeatureVisibilityFlags.kt +++ b/app/src/main/kotlin/com/wire/android/util/debug/FeatureVisibilityFlags.kt @@ -49,7 +49,7 @@ object FeatureVisibilityFlags { const val BackUpSettings = true const val AudioMessagesIcon = false const val ShareLocationIcon = false - const val RichTextIcon = false + const val RichTextIcon = true const val EmojiIcon = false const val GifIcon = false const val PingIcon = true From 4d30e3c9d37df6bc93e76dac2f7e07e944586a77 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Tue, 13 Jun 2023 14:32:15 +0200 Subject: [PATCH 04/14] feat: add icon modifier for wire secondary icon button --- .../android/ui/common/button/WireSecondaryIconButton.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/ui/common/button/WireSecondaryIconButton.kt b/app/src/main/kotlin/com/wire/android/ui/common/button/WireSecondaryIconButton.kt index 24c7c228e47..11bec3f8bca 100644 --- a/app/src/main/kotlin/com/wire/android/ui/common/button/WireSecondaryIconButton.kt +++ b/app/src/main/kotlin/com/wire/android/ui/common/button/WireSecondaryIconButton.kt @@ -53,7 +53,8 @@ fun WireSecondaryIconButton( state: WireButtonState = WireButtonState.Default, colors: WireButtonColors = wireSecondaryButtonColors(), clickBlockParams: ClickBlockParams = ClickBlockParams(), - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + iconModifier: Modifier = Modifier ) { WireSecondaryButton( onClick = onButtonClicked, @@ -61,7 +62,8 @@ fun WireSecondaryIconButton( Icon( painter = painterResource(id = iconResource), contentDescription = stringResource(contentDescription), - modifier = Modifier.size(iconSize) + modifier = iconModifier + .size(iconSize) ) }, shape = shape, From 983183159e59b862f70c168a9ab2c6a33ed486bd Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Tue, 13 Jun 2023 14:33:57 +0200 Subject: [PATCH 05/14] feat: add new rich text formatting input type and state --- .../state/MessageComposeInputState.kt | 29 ++++++++++++++++--- .../state/MessageComposerState.kt | 26 +++++++++++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/state/MessageComposeInputState.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/state/MessageComposeInputState.kt index 18df9cc0d42..c70fdf502d9 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/state/MessageComposeInputState.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/state/MessageComposeInputState.kt @@ -88,16 +88,30 @@ sealed class MessageComposeInputState { val isEditMessage: Boolean get() = this is Active && this.type is MessageComposeInputType.EditMessage val editSaveButtonEnabled: Boolean - get() = this is Active && this.type is MessageComposeInputType.EditMessage && messageText.text.trim().isNotBlank() - && messageText.text != this.type.originalText + get() = this is Active + && ((this.type is MessageComposeInputType.EditMessage + && messageText.text != this.type.originalText) + || (this.type is MessageComposeInputType.RichTextFormattingMessage + && messageText.text != this.type.originalText)) + && messageText.text.trim().isNotBlank() val sendButtonEnabled: Boolean - get() = this is Active && this.type is MessageComposeInputType.NewMessage && messageText.text.trim().isNotBlank() + get() = this is Active + && (this.type is MessageComposeInputType.NewMessage + || this.type is MessageComposeInputType.RichTextFormattingMessage) + && messageText.text.trim().isNotBlank() val sendEphemeralMessageButtonEnabled: Boolean - get() = this is Active && this.type is MessageComposeInputType.SelfDeletingMessage && messageText.text.trim().isNotBlank() + get() = this is Active + && (this.type is MessageComposeInputType.SelfDeletingMessage + || this.type is MessageComposeInputType.RichTextFormattingMessage) + && messageText.text.trim().isNotBlank() val isEphemeral: Boolean get() = this is Active && this.type is MessageComposeInputType.SelfDeletingMessage + + val isRichTextFormatting: Boolean + get() = this is Active + && this.type is MessageComposeInputType.RichTextFormattingMessage } enum class MessageComposeInputSize { @@ -126,6 +140,13 @@ sealed class MessageComposeInputType { val isEnforced: Boolean, val attachmentOptionsDisplayed: Boolean = false ) : MessageComposeInputType() + + @Stable + data class RichTextFormattingMessage( + val messageId: String?, + val originalText: String, + val previousInputType: MessageComposeInputType + ) : MessageComposeInputType() } @Suppress("MagicNumber") diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/state/MessageComposerState.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/state/MessageComposerState.kt index 57bce4bab9d..5cabf4dccd7 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/state/MessageComposerState.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/state/MessageComposerState.kt @@ -170,6 +170,32 @@ data class MessageComposerState( inputFocusRequester.requestFocus() } + fun toRichTextEditingOptions( + originalText: String + ) { + messageComposeInputState = MessageComposeInputState.Active( + messageText = messageComposeInputState.messageText, + type = MessageComposeInputType.RichTextFormattingMessage( + messageId = ((messageComposeInputState as? MessageComposeInputState.Active) + ?.type as? MessageComposeInputType.EditMessage) + ?.messageId, + originalText = originalText, + previousInputType = (messageComposeInputState as? MessageComposeInputState.Active) + ?.type + ?: MessageComposeInputType.NewMessage(attachmentOptionsDisplayed = false) + ) + ) + } + + fun toCloseRichTextEditingOptions() { + messageComposeInputState = MessageComposeInputState.Active( + messageText = messageComposeInputState.messageText, + type = ((messageComposeInputState as MessageComposeInputState.Active) + .type as MessageComposeInputType.RichTextFormattingMessage) + .previousInputType + ) + } + fun showAttachmentOptions() = changeAttachmentOptionsVisibility(true) fun hideAttachmentOptions() = changeAttachmentOptionsVisibility(false) private fun changeAttachmentOptionsVisibility(newValue: Boolean) { From c806239b42c3d8b215d16dbb1307f396c4c2f2eb Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Tue, 13 Jun 2023 14:39:05 +0200 Subject: [PATCH 06/14] feat: add text formatting --- .../home/messagecomposer/MessageActionsBox.kt | 88 ++++++++++++++----- .../home/messagecomposer/MessageComposer.kt | 85 +++++++++++++++++- .../messagecomposer/MessageComposerInput.kt | 24 ++++- .../home/messagecomposer/MessageInputRow.kt | 14 ++- .../home/messagecomposer/RichTextOptions.kt | 4 + 5 files changed, 189 insertions(+), 26 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageActionsBox.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageActionsBox.kt index bad002e09bd..c076369486f 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageActionsBox.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageActionsBox.kt @@ -62,7 +62,13 @@ fun MessageComposeActionsBox( onPingClicked: () -> Unit, onSelfDeletionOptionButtonClicked: () -> Unit, showSelfDeletingOption: Boolean, - modifier: Modifier = Modifier, + onRichTextEditingButtonClicked: () -> Unit, + onCloseRichTextEditingButtonClicked: () -> Unit, + onRichTextEditingHeaderButtonClicked: () -> Unit, + onRichTextEditingBoldButtonClicked: () -> Unit, + onRichTextEditingItalicButtonClicked: () -> Unit, + showRichTextEditingOptions: Boolean, + modifier: Modifier = Modifier ) { Column(modifier.wrapContentSize()) { Divider(color = MaterialTheme.wireColorScheme.outline) @@ -85,7 +91,13 @@ fun MessageComposeActionsBox( onAdditionalOptionButtonClicked, onPingClicked, onSelfDeletionOptionButtonClicked, - showSelfDeletingOption + showSelfDeletingOption, + showRichTextEditingOptions, + onRichTextEditingButtonClicked, + onCloseRichTextEditingButtonClicked, + onRichTextEditingHeaderButtonClicked, + onRichTextEditingBoldButtonClicked, + onRichTextEditingItalicButtonClicked ) } } @@ -105,39 +117,56 @@ private fun MessageComposeActions( onPingClicked: () -> Unit, onSelfDeletionOptionButtonClicked: () -> Unit, showSelfDeletingOption: Boolean, + richTextEditingSelected: Boolean, + onRichTextEditingButtonClicked: () -> Unit, + onCloseRichTextEditingButtonClicked: () -> Unit, + onRichTextEditingHeaderButtonClicked: () -> Unit, + onRichTextEditingBoldButtonClicked: () -> Unit, + onRichTextEditingItalicButtonClicked: () -> Unit ) { val localFeatureVisibilityFlags = LocalFeatureVisibilityFlags.current Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceEvenly, + horizontalArrangement = if (richTextEditingSelected) Arrangement.SpaceBetween else Arrangement.SpaceEvenly, modifier = Modifier .fillMaxWidth() .height(dimensions().spacing56x) ) { - with(localFeatureVisibilityFlags) { - if (!isEditMessage) AdditionalOptionButton( - isSelected = attachmentOptionsDisplayed, - isEnabled = isFileSharingEnabled, - onClick = onAdditionalOptionButtonClicked - ) - if (RichTextIcon) RichTextEditingAction() - if (!isEditMessage && EmojiIcon) AddEmojiAction() - if (!isEditMessage && GifIcon) AddGifAction() - if (!isEditMessage && showSelfDeletingOption) SelfDeletingMessageAction( - isSelected = selfDeletingOptionSelected, - onButtonClicked = onSelfDeletionOptionButtonClicked + if (richTextEditingSelected) { + RichTextOptions( + onRichTextHeaderButtonClicked = onRichTextEditingHeaderButtonClicked, + onRichTextBoldButtonClicked = onRichTextEditingBoldButtonClicked, + onRichTextItalicButtonClicked = onRichTextEditingItalicButtonClicked, + onCloseRichTextEditingButtonClicked = onCloseRichTextEditingButtonClicked ) - if (!isEditMessage && PingIcon) PingAction(onPingClicked = onPingClicked) - AddMentionAction(isMentionsSelected, startMention) + } else { + with(localFeatureVisibilityFlags) { + if (!isEditMessage) AdditionalOptionButton( + isSelected = attachmentOptionsDisplayed, + isEnabled = isFileSharingEnabled, + onClick = onAdditionalOptionButtonClicked + ) + if (RichTextIcon) RichTextEditingAction( + onButtonClicked = onRichTextEditingButtonClicked + ) + if (!isEditMessage && EmojiIcon) AddEmojiAction() + if (!isEditMessage && GifIcon) AddGifAction() + if (!isEditMessage && showSelfDeletingOption) SelfDeletingMessageAction( + isSelected = selfDeletingOptionSelected, + onButtonClicked = onSelfDeletionOptionButtonClicked + ) + if (!isEditMessage && PingIcon) PingAction(onPingClicked = onPingClicked) + AddMentionAction(isMentionsSelected, startMention) + } } } } @Composable -private fun RichTextEditingAction() { +private fun RichTextEditingAction(onButtonClicked: () -> Unit) { WireSecondaryIconButton( - onButtonClicked = {}, + onButtonClicked = onButtonClicked, clickBlockParams = ClickBlockParams(blockWhenSyncing = true, blockWhenConnecting = true), iconResource = R.drawable.ic_rich_text, contentDescription = R.string.content_description_conversation_enable_rich_text_mode @@ -210,10 +239,29 @@ fun PreviewMessageActionsBox() { .height(dimensions().spacing56x) ) { AdditionalOptionButton(isSelected = false, isEnabled = true, onClick = {}) - RichTextEditingAction() + RichTextEditingAction(onButtonClicked = {}) AddEmojiAction() AddGifAction() AddMentionAction(isSelected = false, addMentionAction = {}) PingAction {} } } + +@Preview +@Composable +fun PreviewRichTextOptions() { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.SpaceEvenly, + modifier = Modifier + .fillMaxWidth() + .height(dimensions().spacing56x) + ) { + RichTextOptions( + onRichTextHeaderButtonClicked = {}, + onRichTextBoldButtonClicked = {}, + onRichTextItalicButtonClicked = {}, + onCloseRichTextEditingButtonClicked = {} + ) + } +} diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposer.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposer.kt index ab2c61932d9..93284cf3226 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposer.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposer.kt @@ -47,7 +47,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.text.input.getSelectedText import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import com.wire.android.R @@ -65,6 +67,7 @@ import com.wire.android.ui.home.messagecomposer.state.MessageComposeInputType import com.wire.android.ui.home.messagecomposer.state.MessageComposerState import com.wire.android.ui.home.newconversation.model.Contact import com.wire.android.ui.theme.wireColorScheme +import com.wire.android.util.EMPTY import com.wire.kalium.logic.feature.conversation.InteractionAvailability import com.wire.kalium.logic.feature.conversation.SecurityClassificationType @@ -109,6 +112,7 @@ fun MessageComposer( { (messageComposerState.messageComposeInputState as? MessageComposeInputState.Active)?.let { (it.type as? MessageComposeInputType.EditMessage)?.messageId + ?: (it.type as? MessageComposeInputType.RichTextFormattingMessage)?.messageId }?.let { originalMessageId -> onSendEditTextMessage( EditMessageBundle( @@ -277,7 +281,32 @@ private fun MessageComposer( }, onEditSaveButtonClicked = onEditSaveButtonClicked, onEditCancelButtonClicked = messageComposerState::closeEditToInactive, - onSelfDeletionOptionButtonClicked = onShowSelfDeletionOption + onSelfDeletionOptionButtonClicked = onShowSelfDeletionOption, + onRichTextEditingButtonClicked = { + messageComposerState.toRichTextEditingOptions( + originalText = messageComposerState.messageComposeInputState.messageText.text + ) + }, + onCloseRichTextEditingButtonClicked = messageComposerState::toCloseRichTextEditingOptions, + toRichTextEditingHeader = { + addOrRemoveMessageMarkdown( + messageComposerState = messageComposerState, + markdown = RICH_TEXT_MARKDOWN_HEADER, + isHeader = true + ) + }, + toRichTextEditingBold = { + addOrRemoveMessageMarkdown( + messageComposerState = messageComposerState, + markdown = RICH_TEXT_MARKDOWN_BOLD + ) + }, + toRichTextEditingItalic = { + addOrRemoveMessageMarkdown( + messageComposerState = messageComposerState, + markdown = RICH_TEXT_MARKDOWN_ITALIC + ) + } ) } ) @@ -360,6 +389,60 @@ private fun MembersMentionList( } } +private fun addOrRemoveMessageMarkdown( + messageComposerState: MessageComposerState, + markdown: String, + isHeader: Boolean = false +) { + val originalValue = messageComposerState + .messageComposeInputState + .messageText + + val range = originalValue.selection + val selectedText = originalValue.getSelectedText() + val markdownSize = markdown.length + val markdownSizeComplete = markdownSize * 2 + + + val (text, start, end) = if (selectedText.contains(markdown)) { + // Remove Markdown + val stringBuilder = StringBuilder(originalValue.annotatedString) + stringBuilder.replace( + range.start, + range.end, + selectedText.toString().replace(markdown, String.EMPTY) + ) + + Triple( + stringBuilder.toString(), + range.start, + (range.end - if (isHeader) markdownSize else markdownSizeComplete) + ) + } else { + // Add Markdown + val stringBuilder = StringBuilder(originalValue.annotatedString) + stringBuilder.insert(range.start, markdown) + if (isHeader.not()) stringBuilder.insert(range.end + markdownSize, markdown) + + Triple( + stringBuilder.toString(), + range.start, + (range.end + if (isHeader) markdownSize else markdownSizeComplete) + ) + } + + // Set new text + messageComposerState.setMessageTextValue( + text = TextFieldValue( + text = text, + selection = TextRange( + start = start, + end = end + ) + ) + ) +} + sealed class KeyboardHeight(open val height: Dp) { object NotKnown : KeyboardHeight(DEFAULT_KEYBOARD_TOP_SCREEN_OFFSET) data class Known(override val height: Dp) : KeyboardHeight(height) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposerInput.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposerInput.kt index 84e4af5fa4b..ee8dd6919e8 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposerInput.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposerInput.kt @@ -147,7 +147,13 @@ private fun EnabledMessageComposerInput( modifier = Modifier.background(colorsScheme().messageComposerBackgroundColor), onPingClicked = actions.onPingClicked, onSelfDeletionOptionButtonClicked = actions.onSelfDeletionOptionButtonClicked, - showSelfDeletingOption = showSelfDeletingOption + showSelfDeletingOption = showSelfDeletingOption, + onRichTextEditingButtonClicked = actions.onRichTextEditingButtonClicked, + onCloseRichTextEditingButtonClicked = actions.onCloseRichTextEditingButtonClicked, + onRichTextEditingHeaderButtonClicked = actions.toRichTextEditingHeader, + onRichTextEditingBoldButtonClicked = actions.toRichTextEditingBold, + onRichTextEditingItalicButtonClicked = actions.toRichTextEditingItalic, + showRichTextEditingOptions = messageComposeInputState.isRichTextFormatting ) } if (membersToMention.isNotEmpty() && messageComposeInputState.isExpanded) { @@ -275,7 +281,12 @@ data class MessageComposerInputActions( val onEditCancelButtonClicked: () -> Unit = {}, val onPingClicked: () -> Unit = {}, val onSelfDeletionOptionButtonClicked: () -> Unit = { }, - val onSendSelfDeletingMessageClicked: () -> Unit = {} + val onSendSelfDeletingMessageClicked: () -> Unit = {}, + val onRichTextEditingButtonClicked: () -> Unit = {}, + val onCloseRichTextEditingButtonClicked: () -> Unit = {}, + val toRichTextEditingHeader: () -> Unit = {}, + val toRichTextEditingBold: () -> Unit = {}, + val toRichTextEditingItalic: () -> Unit = {} ) @Composable @@ -314,5 +325,12 @@ fun PreviewEnabledMessageComposerInputActiveExpanded() { @Preview @Composable fun PreviewEnabledMessageComposerInputActiveEdit() { - generatePreviewWithState(MessageComposeInputState.Active(type = MessageComposeInputType.EditMessage("", ""))) + generatePreviewWithState( + MessageComposeInputState.Active( + type = MessageComposeInputType.EditMessage( + "", + "" + ) + ) + ) } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageInputRow.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageInputRow.kt index 716a9798b73..829b6a64b61 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageInputRow.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageInputRow.kt @@ -127,7 +127,13 @@ fun MessageComposerInputRow( onSelectedLineIndexChanged = onSelectedLineIndexChanged, onLineBottomYCoordinateChanged = onLineBottomYCoordinateChanged ) - AnimatedVisibility(messageComposeInputState.isEditMessage) { + AnimatedVisibility( + messageComposeInputState.isEditMessage + || (messageComposeInputState.isRichTextFormatting + && ((messageComposeInputState as? MessageComposeInputState.Active) + ?.type as? MessageComposeInputType.RichTextFormattingMessage) + ?.previousInputType is MessageComposeInputType.EditMessage) + ) { MessageEditActions( onEditSaveButtonClicked = onEditSaveButtonClicked, onEditCancelButtonClicked = onEditCancelButtonClicked, @@ -136,7 +142,11 @@ fun MessageComposerInputRow( } } if (messageComposeInputState is MessageComposeInputState.Active) { - when (val type = messageComposeInputState.type) { + val type = if (messageComposeInputState.type + is MessageComposeInputType.RichTextFormattingMessage + ) messageComposeInputState.type.previousInputType else messageComposeInputState.type + + when (type) { is MessageComposeInputType.NewMessage -> MessageSendActions( onSendButtonClicked = onSendButtonClicked, diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/RichTextOptions.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/RichTextOptions.kt index b0b5febafbd..e358051d6f0 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/RichTextOptions.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/RichTextOptions.kt @@ -150,3 +150,7 @@ private fun CloseButton( ) } } + +const val RICH_TEXT_MARKDOWN_HEADER = "#### " +const val RICH_TEXT_MARKDOWN_BOLD = "**" +const val RICH_TEXT_MARKDOWN_ITALIC = "_" From c86083ae4d243fe95df10eb1c001c3b335aea2a9 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Wed, 14 Jun 2023 11:29:31 +0200 Subject: [PATCH 07/14] feat: adjust logic for cursor position --- .../home/messagecomposer/MessageComposer.kt | 41 ++++++++++--------- .../home/messagecomposer/RichTextOptions.kt | 3 +- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposer.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposer.kt index 93284cf3226..91787d19068 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposer.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposer.kt @@ -400,44 +400,45 @@ private fun addOrRemoveMessageMarkdown( val range = originalValue.selection val selectedText = originalValue.getSelectedText() - val markdownSize = markdown.length - val markdownSizeComplete = markdownSize * 2 + val stringBuilder = StringBuilder(originalValue.annotatedString) + val markdownLength = markdown.length + val markdownLengthComplete = + if (isHeader) markdownLength else (markdownLength * RICH_TEXT_MARKDOWN_MULTIPLIER) - - val (text, start, end) = if (selectedText.contains(markdown)) { + val rangeEnd = if (selectedText.contains(markdown)) { // Remove Markdown - val stringBuilder = StringBuilder(originalValue.annotatedString) stringBuilder.replace( range.start, range.end, selectedText.toString().replace(markdown, String.EMPTY) ) - Triple( - stringBuilder.toString(), - range.start, - (range.end - if (isHeader) markdownSize else markdownSizeComplete) - ) + range.end - markdownLengthComplete } else { // Add Markdown - val stringBuilder = StringBuilder(originalValue.annotatedString) stringBuilder.insert(range.start, markdown) - if (isHeader.not()) stringBuilder.insert(range.end + markdownSize, markdown) + if (isHeader.not()) stringBuilder.insert(range.end + markdownLength, markdown) - Triple( - stringBuilder.toString(), - range.start, - (range.end + if (isHeader) markdownSize else markdownSizeComplete) - ) + range.end + markdownLengthComplete + } + + val (selectionStart, selectionEnd) = if (range.start == range.end) { + if (isHeader) Pair(rangeEnd, rangeEnd) + else { + val middleMarkdownRange = rangeEnd - markdownLength + Pair(middleMarkdownRange, middleMarkdownRange) + } + } else { + Pair(range.start, rangeEnd) } // Set new text messageComposerState.setMessageTextValue( text = TextFieldValue( - text = text, + text = stringBuilder.toString(), selection = TextRange( - start = start, - end = end + start = selectionStart, + end = selectionEnd ) ) ) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/RichTextOptions.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/RichTextOptions.kt index e358051d6f0..d7e754daa63 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/RichTextOptions.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/RichTextOptions.kt @@ -151,6 +151,7 @@ private fun CloseButton( } } -const val RICH_TEXT_MARKDOWN_HEADER = "#### " +const val RICH_TEXT_MARKDOWN_MULTIPLIER = 2 +const val RICH_TEXT_MARKDOWN_HEADER = "# " const val RICH_TEXT_MARKDOWN_BOLD = "**" const val RICH_TEXT_MARKDOWN_ITALIC = "_" From 1b46c5ac02cc253ca1e8b4ee62c4ad2634f6731d Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Wed, 14 Jun 2023 11:30:06 +0200 Subject: [PATCH 08/14] chore: update kalium reference --- kalium | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kalium b/kalium index e353d3d0016..f4835325800 160000 --- a/kalium +++ b/kalium @@ -1 +1 @@ -Subproject commit e353d3d0016ae8338c795e79ec1d24c31a7f81cc +Subproject commit f483532580040c641c70cf64fd16df79170cd0e5 From 2d276013dbeb7ebabdd92777f0d05b39845ba7b7 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Wed, 14 Jun 2023 11:30:58 +0200 Subject: [PATCH 09/14] Trigger Build From 6a4dbe21c671a305354b20f69a2cf5dcb5c6f253 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Wed, 14 Jun 2023 12:21:43 +0200 Subject: [PATCH 10/14] chore: adjust detekt --- .../com/wire/android/ui/home/messagecomposer/MessageInputRow.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageInputRow.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageInputRow.kt index 829b6a64b61..284e5bb2d11 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageInputRow.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageInputRow.kt @@ -59,6 +59,7 @@ import com.wire.android.ui.home.messagecomposer.state.MessageComposeInputType import com.wire.android.ui.home.messagecomposer.state.SelfDeletionDuration import com.wire.android.ui.theme.wireTypography +@Suppress("ComplexMethod") @OptIn(ExperimentalAnimationApi::class) @Composable fun MessageComposerInputRow( From 7b0e020ecb729145174907957e920e3ac3214589 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Mon, 19 Jun 2023 10:27:42 +0200 Subject: [PATCH 11/14] chore: update kalium reference --- kalium | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kalium b/kalium index 38fd64db43a..ed3bdbbccde 160000 --- a/kalium +++ b/kalium @@ -1 +1 @@ -Subproject commit 38fd64db43a8295da4ff4380c57d3252ecf3623c +Subproject commit ed3bdbbccde0ee88c6b65ff9de2cab4d3430a2ee From c91bf2bc0240e5075f52c7660341c99fa4190c49 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Mon, 19 Jun 2023 10:57:44 +0200 Subject: [PATCH 12/14] chore: change iconModifier to fillMaxWidth param --- .../android/ui/common/button/WireSecondaryIconButton.kt | 6 +++--- .../android/ui/home/messagecomposer/RichTextOptions.kt | 7 +++---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/ui/common/button/WireSecondaryIconButton.kt b/app/src/main/kotlin/com/wire/android/ui/common/button/WireSecondaryIconButton.kt index 11bec3f8bca..bd26bb45786 100644 --- a/app/src/main/kotlin/com/wire/android/ui/common/button/WireSecondaryIconButton.kt +++ b/app/src/main/kotlin/com/wire/android/ui/common/button/WireSecondaryIconButton.kt @@ -54,7 +54,7 @@ fun WireSecondaryIconButton( colors: WireButtonColors = wireSecondaryButtonColors(), clickBlockParams: ClickBlockParams = ClickBlockParams(), modifier: Modifier = Modifier, - iconModifier: Modifier = Modifier + fillMaxWidth: Boolean = false ) { WireSecondaryButton( onClick = onButtonClicked, @@ -62,7 +62,7 @@ fun WireSecondaryIconButton( Icon( painter = painterResource(id = iconResource), contentDescription = stringResource(contentDescription), - modifier = iconModifier + modifier = Modifier .size(iconSize) ) }, @@ -74,7 +74,7 @@ fun WireSecondaryIconButton( state = state, colors = colors, clickBlockParams = clickBlockParams, - fillMaxWidth = false, + fillMaxWidth = fillMaxWidth, modifier = modifier ) } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/RichTextOptions.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/RichTextOptions.kt index d7e754daa63..73594ef4889 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/RichTextOptions.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/RichTextOptions.kt @@ -52,7 +52,6 @@ fun RichTextOptions( .fillMaxHeight() ) { val modifier = Modifier - .fillMaxWidth() .weight(1f) .padding(horizontal = dimensions().spacing0x) @@ -83,9 +82,9 @@ private fun HeaderButton( onButtonClicked = onRichTextHeaderButtonClicked, iconResource = R.drawable.ic_rich_text_header, contentDescription = R.string.content_description_conversation_rich_text_header, - iconModifier = modifier, modifier = modifier .padding(start = dimensions().spacing8x), + fillMaxWidth = true, shape = RoundedCornerShape( topStart = MaterialTheme.wireDimensions.buttonCornerSize, bottomStart = MaterialTheme.wireDimensions.buttonCornerSize, @@ -104,8 +103,8 @@ private fun BoldButton( onButtonClicked = onRichTextBoldButtonClicked, iconResource = R.drawable.ic_rich_text_bold, contentDescription = R.string.content_description_conversation_rich_text_bold, - iconModifier = modifier, modifier = modifier, + fillMaxWidth = true, shape = RoundedCornerShape( topStart = MaterialTheme.wireDimensions.spacing0x, bottomStart = MaterialTheme.wireDimensions.spacing0x, @@ -124,8 +123,8 @@ private fun ItalicButton( onButtonClicked = onRichTextItalicButtonClicked, iconResource = R.drawable.ic_rich_text_italic, contentDescription = R.string.content_description_conversation_rich_text_italic, - iconModifier = modifier, modifier = modifier, + fillMaxWidth = true, shape = RoundedCornerShape( topStart = MaterialTheme.wireDimensions.spacing0x, bottomStart = MaterialTheme.wireDimensions.spacing0x, From d766b217b6bfd13cd049f0546e3b1197a06595c6 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Mon, 19 Jun 2023 11:57:11 +0200 Subject: [PATCH 13/14] chore: remove rich text editing type and change to type parameter --- .../home/messagecomposer/MessageActionsBox.kt | 9 ++- .../home/messagecomposer/MessageComposer.kt | 10 +-- .../messagecomposer/MessageComposerInput.kt | 3 +- .../home/messagecomposer/MessageInputRow.kt | 14 +---- .../state/MessageComposeInputState.kt | 34 +++++------ .../state/MessageComposerState.kt | 61 +++++++++++-------- 6 files changed, 63 insertions(+), 68 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageActionsBox.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageActionsBox.kt index c076369486f..c8589a477cc 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageActionsBox.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageActionsBox.kt @@ -67,7 +67,6 @@ fun MessageComposeActionsBox( onRichTextEditingHeaderButtonClicked: () -> Unit, onRichTextEditingBoldButtonClicked: () -> Unit, onRichTextEditingItalicButtonClicked: () -> Unit, - showRichTextEditingOptions: Boolean, modifier: Modifier = Modifier ) { Column(modifier.wrapContentSize()) { @@ -92,7 +91,7 @@ fun MessageComposeActionsBox( onPingClicked, onSelfDeletionOptionButtonClicked, showSelfDeletingOption, - showRichTextEditingOptions, + state.isRichTextFormattingOptionsDisplayed, onRichTextEditingButtonClicked, onCloseRichTextEditingButtonClicked, onRichTextEditingHeaderButtonClicked, @@ -117,7 +116,7 @@ private fun MessageComposeActions( onPingClicked: () -> Unit, onSelfDeletionOptionButtonClicked: () -> Unit, showSelfDeletingOption: Boolean, - richTextEditingSelected: Boolean, + richTextEditingDisplayed: Boolean, onRichTextEditingButtonClicked: () -> Unit, onCloseRichTextEditingButtonClicked: () -> Unit, onRichTextEditingHeaderButtonClicked: () -> Unit, @@ -128,12 +127,12 @@ private fun MessageComposeActions( Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = if (richTextEditingSelected) Arrangement.SpaceBetween else Arrangement.SpaceEvenly, + horizontalArrangement = if (richTextEditingDisplayed) Arrangement.SpaceBetween else Arrangement.SpaceEvenly, modifier = Modifier .fillMaxWidth() .height(dimensions().spacing56x) ) { - if (richTextEditingSelected) { + if (richTextEditingDisplayed) { RichTextOptions( onRichTextHeaderButtonClicked = onRichTextEditingHeaderButtonClicked, onRichTextBoldButtonClicked = onRichTextEditingBoldButtonClicked, diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposer.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposer.kt index 91787d19068..a8ba3621a1e 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposer.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposer.kt @@ -112,7 +112,6 @@ fun MessageComposer( { (messageComposerState.messageComposeInputState as? MessageComposeInputState.Active)?.let { (it.type as? MessageComposeInputType.EditMessage)?.messageId - ?: (it.type as? MessageComposeInputType.RichTextFormattingMessage)?.messageId }?.let { originalMessageId -> onSendEditTextMessage( EditMessageBundle( @@ -283,11 +282,12 @@ private fun MessageComposer( onEditCancelButtonClicked = messageComposerState::closeEditToInactive, onSelfDeletionOptionButtonClicked = onShowSelfDeletionOption, onRichTextEditingButtonClicked = { - messageComposerState.toRichTextEditingOptions( - originalText = messageComposerState.messageComposeInputState.messageText.text - ) + messageComposerState.toActive() + messageComposerState.showRichTextEditingOptions() + }, + onCloseRichTextEditingButtonClicked = { + messageComposerState.hideRichTextEditingOptions() }, - onCloseRichTextEditingButtonClicked = messageComposerState::toCloseRichTextEditingOptions, toRichTextEditingHeader = { addOrRemoveMessageMarkdown( messageComposerState = messageComposerState, diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposerInput.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposerInput.kt index ee8dd6919e8..7e397c3557e 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposerInput.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageComposerInput.kt @@ -152,8 +152,7 @@ private fun EnabledMessageComposerInput( onCloseRichTextEditingButtonClicked = actions.onCloseRichTextEditingButtonClicked, onRichTextEditingHeaderButtonClicked = actions.toRichTextEditingHeader, onRichTextEditingBoldButtonClicked = actions.toRichTextEditingBold, - onRichTextEditingItalicButtonClicked = actions.toRichTextEditingItalic, - showRichTextEditingOptions = messageComposeInputState.isRichTextFormatting + onRichTextEditingItalicButtonClicked = actions.toRichTextEditingItalic ) } if (membersToMention.isNotEmpty() && messageComposeInputState.isExpanded) { diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageInputRow.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageInputRow.kt index 284e5bb2d11..98ffefecdb3 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageInputRow.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/MessageInputRow.kt @@ -128,13 +128,7 @@ fun MessageComposerInputRow( onSelectedLineIndexChanged = onSelectedLineIndexChanged, onLineBottomYCoordinateChanged = onLineBottomYCoordinateChanged ) - AnimatedVisibility( - messageComposeInputState.isEditMessage - || (messageComposeInputState.isRichTextFormatting - && ((messageComposeInputState as? MessageComposeInputState.Active) - ?.type as? MessageComposeInputType.RichTextFormattingMessage) - ?.previousInputType is MessageComposeInputType.EditMessage) - ) { + AnimatedVisibility(messageComposeInputState.isEditMessage) { MessageEditActions( onEditSaveButtonClicked = onEditSaveButtonClicked, onEditCancelButtonClicked = onEditCancelButtonClicked, @@ -143,11 +137,7 @@ fun MessageComposerInputRow( } } if (messageComposeInputState is MessageComposeInputState.Active) { - val type = if (messageComposeInputState.type - is MessageComposeInputType.RichTextFormattingMessage - ) messageComposeInputState.type.previousInputType else messageComposeInputState.type - - when (type) { + when (val type = messageComposeInputState.type) { is MessageComposeInputType.NewMessage -> MessageSendActions( onSendButtonClicked = onSendButtonClicked, diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/state/MessageComposeInputState.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/state/MessageComposeInputState.kt index c70fdf502d9..9242b2d936b 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/state/MessageComposeInputState.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/state/MessageComposeInputState.kt @@ -81,37 +81,37 @@ sealed class MessageComposeInputState { val isExpanded: Boolean get() = this is Active && this.size == MessageComposeInputSize.EXPANDED + val attachmentOptionsDisplayed: Boolean get() = (this is Active && this.type is MessageComposeInputType.NewMessage && this.type.attachmentOptionsDisplayed) || (this is Active && this.type is MessageComposeInputType.SelfDeletingMessage && this.type.attachmentOptionsDisplayed) val isEditMessage: Boolean get() = this is Active && this.type is MessageComposeInputType.EditMessage + val editSaveButtonEnabled: Boolean get() = this is Active - && ((this.type is MessageComposeInputType.EditMessage - && messageText.text != this.type.originalText) - || (this.type is MessageComposeInputType.RichTextFormattingMessage - && messageText.text != this.type.originalText)) + && this.type is MessageComposeInputType.EditMessage + && messageText.text != this.type.originalText && messageText.text.trim().isNotBlank() + val sendButtonEnabled: Boolean get() = this is Active - && (this.type is MessageComposeInputType.NewMessage - || this.type is MessageComposeInputType.RichTextFormattingMessage) + && this.type is MessageComposeInputType.NewMessage && messageText.text.trim().isNotBlank() val sendEphemeralMessageButtonEnabled: Boolean get() = this is Active - && (this.type is MessageComposeInputType.SelfDeletingMessage - || this.type is MessageComposeInputType.RichTextFormattingMessage) + && this.type is MessageComposeInputType.SelfDeletingMessage && messageText.text.trim().isNotBlank() val isEphemeral: Boolean get() = this is Active && this.type is MessageComposeInputType.SelfDeletingMessage - val isRichTextFormatting: Boolean - get() = this is Active - && this.type is MessageComposeInputType.RichTextFormattingMessage + val isRichTextFormattingOptionsDisplayed: Boolean + get() = (this is Active && this.type is MessageComposeInputType.NewMessage && this.type.richTextFormattingOptionsDisplayed) + || (this is Active && this.type is MessageComposeInputType.EditMessage && this.type.richTextFormattingOptionsDisplayed) + || (this is Active && this.type is MessageComposeInputType.SelfDeletingMessage && this.type.richTextFormattingOptionsDisplayed) } enum class MessageComposeInputSize { @@ -126,26 +126,22 @@ sealed class MessageComposeInputType { @Stable data class NewMessage( val attachmentOptionsDisplayed: Boolean = false, + val richTextFormattingOptionsDisplayed: Boolean = false ) : MessageComposeInputType() @Stable data class EditMessage( val messageId: String, val originalText: String, + val richTextFormattingOptionsDisplayed: Boolean = false ) : MessageComposeInputType() @Stable data class SelfDeletingMessage( val selfDeletionDuration: SelfDeletionDuration, val isEnforced: Boolean, - val attachmentOptionsDisplayed: Boolean = false - ) : MessageComposeInputType() - - @Stable - data class RichTextFormattingMessage( - val messageId: String?, - val originalText: String, - val previousInputType: MessageComposeInputType + val attachmentOptionsDisplayed: Boolean = false, + val richTextFormattingOptionsDisplayed: Boolean = false ) : MessageComposeInputType() } diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/state/MessageComposerState.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/state/MessageComposerState.kt index 5cabf4dccd7..e22f265c508 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/state/MessageComposerState.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/state/MessageComposerState.kt @@ -170,30 +170,39 @@ data class MessageComposerState( inputFocusRequester.requestFocus() } - fun toRichTextEditingOptions( - originalText: String - ) { - messageComposeInputState = MessageComposeInputState.Active( - messageText = messageComposeInputState.messageText, - type = MessageComposeInputType.RichTextFormattingMessage( - messageId = ((messageComposeInputState as? MessageComposeInputState.Active) - ?.type as? MessageComposeInputType.EditMessage) - ?.messageId, - originalText = originalText, - previousInputType = (messageComposeInputState as? MessageComposeInputState.Active) - ?.type - ?: MessageComposeInputType.NewMessage(attachmentOptionsDisplayed = false) - ) - ) - } + fun showRichTextEditingOptions() = changeRichTextEditingOptionsVisibility(true) + fun hideRichTextEditingOptions() = changeRichTextEditingOptionsVisibility(false) - fun toCloseRichTextEditingOptions() { - messageComposeInputState = MessageComposeInputState.Active( - messageText = messageComposeInputState.messageText, - type = ((messageComposeInputState as MessageComposeInputState.Active) - .type as MessageComposeInputType.RichTextFormattingMessage) - .previousInputType - ) + private fun changeRichTextEditingOptionsVisibility(show: Boolean) { + (messageComposeInputState as? MessageComposeInputState.Active)?.let { activeState -> + when (val currentType = activeState.type) { + is MessageComposeInputType.NewMessage -> { + messageComposeInputState = activeState.copy( + type = currentType.copy( + richTextFormattingOptionsDisplayed = show, + attachmentOptionsDisplayed = false + ) + ) + } + + is MessageComposeInputType.EditMessage -> { + messageComposeInputState = activeState.copy( + type = currentType.copy( + richTextFormattingOptionsDisplayed = show + ) + ) + } + + is MessageComposeInputType.SelfDeletingMessage -> { + messageComposeInputState = activeState.copy( + type = currentType.copy( + richTextFormattingOptionsDisplayed = show, + attachmentOptionsDisplayed = false + ) + ) + } + } + } } fun showAttachmentOptions() = changeAttachmentOptionsVisibility(true) @@ -204,7 +213,8 @@ data class MessageComposerState( is MessageComposeInputType.NewMessage -> { messageComposeInputState = activeState.copy( type = currentType.copy( - attachmentOptionsDisplayed = newValue + attachmentOptionsDisplayed = newValue, + richTextFormattingOptionsDisplayed = false ) ) } @@ -212,7 +222,8 @@ data class MessageComposerState( is MessageComposeInputType.SelfDeletingMessage -> { messageComposeInputState = activeState.copy( type = currentType.copy( - attachmentOptionsDisplayed = newValue + attachmentOptionsDisplayed = newValue, + richTextFormattingOptionsDisplayed = false ) ) } From f8f2ab1ba1e70b6254c575c259b5363913a5fce4 Mon Sep 17 00:00:00 2001 From: alexandreferris Date: Mon, 19 Jun 2023 12:10:15 +0200 Subject: [PATCH 14/14] chore: fix detekt --- .../messagecomposer/state/MessageComposeInputState.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/state/MessageComposeInputState.kt b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/state/MessageComposeInputState.kt index 9242b2d936b..3c8c0e79238 100644 --- a/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/state/MessageComposeInputState.kt +++ b/app/src/main/kotlin/com/wire/android/ui/home/messagecomposer/state/MessageComposeInputState.kt @@ -109,9 +109,12 @@ sealed class MessageComposeInputState { get() = this is Active && this.type is MessageComposeInputType.SelfDeletingMessage val isRichTextFormattingOptionsDisplayed: Boolean - get() = (this is Active && this.type is MessageComposeInputType.NewMessage && this.type.richTextFormattingOptionsDisplayed) - || (this is Active && this.type is MessageComposeInputType.EditMessage && this.type.richTextFormattingOptionsDisplayed) - || (this is Active && this.type is MessageComposeInputType.SelfDeletingMessage && this.type.richTextFormattingOptionsDisplayed) + get() = (this is Active && this.type is MessageComposeInputType.NewMessage + && this.type.richTextFormattingOptionsDisplayed) + || (this is Active && this.type is MessageComposeInputType.EditMessage + && this.type.richTextFormattingOptionsDisplayed) + || (this is Active && this.type is MessageComposeInputType.SelfDeletingMessage + && this.type.richTextFormattingOptionsDisplayed) } enum class MessageComposeInputSize {