From 232cabcb270bbf92932496135e14ab45aaf352a1 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 29 Nov 2022 16:52:25 +0100 Subject: [PATCH 1/5] Composer: Edit and reply. TODO: call renderComposerMode and highlight selected item. --- .../x/features/messages/MessagesScreen.kt | 22 +++++++ .../x/features/messages/MessagesViewModel.kt | 59 +++++++++++++++++-- .../messages/model/MessagesItemAction.kt | 2 + .../messages/model/MessagesViewState.kt | 3 + .../element/android/x/designsystem/Icons.kt | 2 + .../main/res/drawable/ic_baseline_edit_24.xml | 5 ++ .../res/drawable/ic_baseline_reply_24.xml | 5 ++ .../android/x/matrix/room/MatrixRoom.kt | 16 +++++ .../x/matrix/timeline/MatrixTimeline.kt | 8 +++ .../x/textcomposer/MessageComposerMode.kt | 18 ++++-- .../x/textcomposer/RichTextComposerLayout.kt | 5 +- .../android/x/textcomposer/TextComposer.kt | 11 +++- .../res/layout/composer_rich_text_layout.xml | 10 ++++ 13 files changed, 152 insertions(+), 14 deletions(-) create mode 100644 libraries/designsystem/src/main/res/drawable/ic_baseline_edit_24.xml create mode 100644 libraries/designsystem/src/main/res/drawable/ic_baseline_reply_24.xml diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesScreen.kt b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesScreen.kt index c6a1b58d65..91c758a28a 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesScreen.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesScreen.kt @@ -40,6 +40,7 @@ import io.element.android.x.features.messages.model.* import io.element.android.x.features.messages.model.content.* import io.element.android.x.features.messages.textcomposer.MessageComposerViewModel import io.element.android.x.features.messages.textcomposer.MessageComposerViewState +import io.element.android.x.textcomposer.MessageComposerMode import io.element.android.x.textcomposer.TextComposer import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch @@ -71,6 +72,7 @@ fun MessagesScreen( val timelineItems by viewModel.collectAsState(MessagesViewState::timelineItems) val hasMoreToLoad by viewModel.collectAsState(MessagesViewState::hasMoreToLoad) val snackBarContent by viewModel.collectAsState(MessagesViewState::snackbarContent) + val composerMode by viewModel.collectAsState(MessagesViewState::composerMode) val composerFullScreen by composerViewModel.collectAsState(MessageComposerViewState::isFullScreen) val composerCanSendMessage by composerViewModel.collectAsState(MessageComposerViewState::isSendButtonVisible) val composerText by composerViewModel.collectAsState(MessageComposerViewState::text) @@ -86,6 +88,8 @@ fun MessagesScreen( composerFullScreen = composerFullScreen, onComposerFullScreenChange = composerViewModel::onComposerFullScreenChange, onComposerTextChange = composerViewModel::updateText, + composerMode = composerMode, + onCloseSpecialMode = viewModel::setNormalMode, composerCanSendMessage = composerCanSendMessage, composerText = composerText, onClick = { @@ -106,6 +110,16 @@ fun MessagesScreen( onActionClicked = { viewModel.handleItemAction(it) coroutineScope.launch { + val targetEvent = viewModel.getTargetEvent() + when (it) { + is MessagesItemAction.Edit -> { + // Entering Edit mode, update the text in the composer. + val newComposerText = + (targetEvent?.content as? MessagesTimelineItemTextBasedContent)?.body.orEmpty() + composerViewModel.updateText(newComposerText) + } + else -> Unit + } actionsSheetState.hide() } } @@ -132,6 +146,8 @@ fun MessagesScreenContent( composerFullScreen: Boolean, onComposerFullScreenChange: () -> Unit, onComposerTextChange: (CharSequence) -> Unit, + composerMode: MessageComposerMode, + onCloseSpecialMode: () -> Unit, composerCanSendMessage: Boolean, composerText: StableCharSequence?, snackbarHostState: SnackbarHostState, @@ -154,6 +170,8 @@ fun MessagesScreenContent( onSendMessage = onSendMessage, onClick = onClick, onLongClick = onLongClick, + composerMode = composerMode, + onCloseSpecialMode = onCloseSpecialMode, composerFullScreen = composerFullScreen, onComposerFullScreenChange = onComposerFullScreenChange, onComposerTextChange = onComposerTextChange, @@ -173,6 +191,8 @@ fun MessagesContent( onSendMessage: (String) -> Unit, onClick: (MessagesTimelineItemState.MessageEvent) -> Unit, onLongClick: (MessagesTimelineItemState.MessageEvent) -> Unit, + composerMode: MessageComposerMode, + onCloseSpecialMode: () -> Unit, composerFullScreen: Boolean, onComposerFullScreenChange: () -> Unit, onComposerTextChange: (CharSequence) -> Unit, @@ -201,6 +221,8 @@ fun MessagesContent( onSendMessage = onSendMessage, fullscreen = composerFullScreen, onFullscreenToggle = onComposerFullScreenChange, + composerMode = composerMode, + onCloseSpecialMode = onCloseSpecialMode, onComposerTextChange = onComposerTextChange, composerCanSendMessage = composerCanSendMessage, composerText = composerText?.charSequence?.toString(), diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesViewModel.kt b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesViewModel.kt index 430ceb6d6a..43bf852318 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesViewModel.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesViewModel.kt @@ -10,11 +10,13 @@ import io.element.android.x.features.messages.model.MessagesItemActionsSheetStat import io.element.android.x.features.messages.model.MessagesTimelineItemState import io.element.android.x.features.messages.model.MessagesViewState import io.element.android.x.features.messages.model.content.MessagesTimelineItemRedactedContent +import io.element.android.x.features.messages.model.content.MessagesTimelineItemTextBasedContent import io.element.android.x.matrix.MatrixClient import io.element.android.x.matrix.MatrixInstance import io.element.android.x.matrix.media.MediaResolver import io.element.android.x.matrix.room.MatrixRoom import io.element.android.x.matrix.timeline.MatrixTimeline +import io.element.android.x.textcomposer.MessageComposerMode import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map @@ -66,25 +68,72 @@ class MessagesViewModel( } fun sendMessage(text: String) { - viewModelScope.launch { - timeline.sendMessage(text) + withState { state -> + viewModelScope.launch { + when (state.composerMode) { + is MessageComposerMode.Normal -> timeline.sendMessage(text) + is MessageComposerMode.Edit -> timeline.editMessage( + state.composerMode.eventId, + text + ) + is MessageComposerMode.Quote -> TODO() + is MessageComposerMode.Reply -> timeline.replyMessage( + state.composerMode.eventId, + text + ) + } + // Reset composer + setNormalMode() + } } } + suspend fun getTargetEvent(): MessagesTimelineItemState.MessageEvent? { + val currentState = awaitState() + return currentState.itemActionsSheetState.invoke()?.targetItem + } + fun handleItemAction(action: MessagesItemAction) { viewModelScope.launch(Dispatchers.Default) { val currentState = awaitState() Timber.v("Handle $action for ${currentState.itemActionsSheetState}") - val targetEvent = - currentState.itemActionsSheetState.invoke()?.targetItem ?: return@launch + val targetEvent = getTargetEvent() + ?: return@launch when (action) { MessagesItemAction.Copy -> notImplementedYet() MessagesItemAction.Forward -> notImplementedYet() MessagesItemAction.Redact -> handleActionRedact(targetEvent) + MessagesItemAction.Edit -> handleActionEdit(targetEvent) + MessagesItemAction.Reply -> handleActionReply(targetEvent) } } } + fun setNormalMode() { + setComposerMode(MessageComposerMode.Normal("")) + } + + private fun handleActionEdit(targetEvent: MessagesTimelineItemState.MessageEvent) { + setComposerMode( + MessageComposerMode.Edit( + targetEvent.id, + (targetEvent.content as? MessagesTimelineItemTextBasedContent)?.body.orEmpty() + ) + ) + } + + private fun handleActionReply(targetEvent: MessagesTimelineItemState.MessageEvent) { + setComposerMode(MessageComposerMode.Reply(targetEvent.safeSenderName, targetEvent.id, "")) + } + + private fun setComposerMode(mode: MessageComposerMode) { + setState { + copy( + composerMode = mode + ) + } + } + private fun notImplementedYet() { setSnackbarContent("Not implemented yet!") } @@ -110,10 +159,12 @@ class MessagesViewModel( emptyList() } else { mutableListOf( + MessagesItemAction.Reply, MessagesItemAction.Forward, MessagesItemAction.Copy, ).also { if (messagesTimelineItemState.isMine) { + it.add(MessagesItemAction.Edit) it.add(MessagesItemAction.Redact) } } diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesItemAction.kt b/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesItemAction.kt index 740740fdcc..0c040831a1 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesItemAction.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesItemAction.kt @@ -13,4 +13,6 @@ sealed class MessagesItemAction( object Forward : MessagesItemAction("Forward", VectorIcons.ArrowForward) object Copy : MessagesItemAction("Copy", VectorIcons.Copy) object Redact : MessagesItemAction("Redact", VectorIcons.Delete, destructive = true) + object Reply : MessagesItemAction("Reply", VectorIcons.Reply) + object Edit : MessagesItemAction("Edit", VectorIcons.Edit) } \ No newline at end of file diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesViewState.kt b/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesViewState.kt index 6ca5352db1..d77dc08c81 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesViewState.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesViewState.kt @@ -4,6 +4,7 @@ import com.airbnb.mvrx.Async import com.airbnb.mvrx.MavericksState import com.airbnb.mvrx.Uninitialized import io.element.android.x.designsystem.components.avatar.AvatarData +import io.element.android.x.textcomposer.MessageComposerMode data class MessagesViewState( val roomId: String, @@ -13,6 +14,8 @@ data class MessagesViewState( val hasMoreToLoad: Boolean = true, val itemActionsSheetState: Async = Uninitialized, val snackbarContent: String? = null, + // TODO Highlight item in reply / edit in the timeline + val composerMode: MessageComposerMode = MessageComposerMode.Normal(""), ) : MavericksState { @Suppress("unused") diff --git a/libraries/designsystem/src/main/java/io/element/android/x/designsystem/Icons.kt b/libraries/designsystem/src/main/java/io/element/android/x/designsystem/Icons.kt index 26881ddcef..bd1d941e3e 100644 --- a/libraries/designsystem/src/main/java/io/element/android/x/designsystem/Icons.kt +++ b/libraries/designsystem/src/main/java/io/element/android/x/designsystem/Icons.kt @@ -6,4 +6,6 @@ object VectorIcons { val Copy = R.drawable.ic_content_copy val ArrowForward = R.drawable.ic_content_arrow_forward val Delete = R.drawable.ic_baseline_delete_outline_24 + val Reply = R.drawable.ic_baseline_reply_24 + val Edit = R.drawable.ic_baseline_edit_24 } \ No newline at end of file diff --git a/libraries/designsystem/src/main/res/drawable/ic_baseline_edit_24.xml b/libraries/designsystem/src/main/res/drawable/ic_baseline_edit_24.xml new file mode 100644 index 0000000000..1c9bd3e6bd --- /dev/null +++ b/libraries/designsystem/src/main/res/drawable/ic_baseline_edit_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/libraries/designsystem/src/main/res/drawable/ic_baseline_reply_24.xml b/libraries/designsystem/src/main/res/drawable/ic_baseline_reply_24.xml new file mode 100644 index 0000000000..c5fba99883 --- /dev/null +++ b/libraries/designsystem/src/main/res/drawable/ic_baseline_reply_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/room/MatrixRoom.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/room/MatrixRoom.kt index c343fc2da0..940c3018cf 100644 --- a/libraries/matrix/src/main/java/io/element/android/x/matrix/room/MatrixRoom.kt +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/room/MatrixRoom.kt @@ -82,6 +82,22 @@ class MatrixRoom( } } + suspend fun editMessage(originalEventId: String, message: String): Result = withContext(coroutineDispatchers.io) { + val transactionId = genTransactionId() + val content = messageEventContentFromMarkdown(message) + runCatching { + room.edit(/* TODO use content */ message, originalEventId, transactionId) + } + } + + suspend fun replyMessage(eventId: String, message: String): Result = withContext(coroutineDispatchers.io) { + val transactionId = genTransactionId() + val content = messageEventContentFromMarkdown(message) + runCatching { + room.sendReply(/* TODO use content */ message, eventId, transactionId) + } + } + suspend fun redactEvent(eventId: String, reason: String? = null, ) = withContext(coroutineDispatchers.io) { val transactionId = genTransactionId() runCatching { diff --git a/libraries/matrix/src/main/java/io/element/android/x/matrix/timeline/MatrixTimeline.kt b/libraries/matrix/src/main/java/io/element/android/x/matrix/timeline/MatrixTimeline.kt index f4a5e27c8d..7007640fcd 100644 --- a/libraries/matrix/src/main/java/io/element/android/x/matrix/timeline/MatrixTimeline.kt +++ b/libraries/matrix/src/main/java/io/element/android/x/matrix/timeline/MatrixTimeline.kt @@ -124,6 +124,14 @@ class MatrixTimeline( return matrixRoom.sendMessage(message) } + suspend fun editMessage(originalEventId: String, message: String): Result { + return matrixRoom.editMessage(originalEventId, message = message) + } + + suspend fun replyMessage(inReplyToEventId: String, message: String): Result { + return matrixRoom.replyMessage(inReplyToEventId, message) + } + override fun onUpdate(update: TimelineDiff) { coroutineScope.launch { updateTimelineItems { diff --git a/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/MessageComposerMode.kt b/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/MessageComposerMode.kt index 86e35d5ad0..835abd0100 100644 --- a/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/MessageComposerMode.kt +++ b/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/MessageComposerMode.kt @@ -19,8 +19,18 @@ package io.element.android.x.textcomposer sealed interface MessageComposerMode { data class Normal(val content: CharSequence?) : MessageComposerMode - sealed class Special(open val event: Any /* TODO set correct type here */, open val defaultContent: CharSequence) : MessageComposerMode - data class Edit(override val event: Any, override val defaultContent: CharSequence) : Special(event, defaultContent) - class Quote(override val event: Any, override val defaultContent: CharSequence) : Special(event, defaultContent) - class Reply(override val event: Any, override val defaultContent: CharSequence) : Special(event, defaultContent) + sealed class Special(open val eventId: String, open val defaultContent: CharSequence) : + MessageComposerMode + + data class Edit(override val eventId: String, override val defaultContent: CharSequence) : + Special(eventId, defaultContent) + + class Quote(override val eventId: String, override val defaultContent: CharSequence) : + Special(eventId, defaultContent) + + class Reply( + val senderName: String, + override val eventId: String, + override val defaultContent: CharSequence + ) : Special(eventId, defaultContent) } diff --git a/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/RichTextComposerLayout.kt b/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/RichTextComposerLayout.kt index 99e300096a..95fab61f47 100644 --- a/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/RichTextComposerLayout.kt +++ b/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/RichTextComposerLayout.kt @@ -506,10 +506,7 @@ class RichTextComposerLayout @JvmOverloads constructor( views.composerModeIconView.setImageResource(R.drawable.ic_quote) } is MessageComposerMode.Reply -> { - // TODO We need sender info - // val senderInfo = mode.event.senderInfo - val userName = - "TODO Sender name" // senderInfo.displayName ?: senderInfo.disambiguatedDisplayName + val userName = mode.senderName views.composerModeTitleView.text = resources.getString(R.string.replying_to, userName) views.composerModeIconView.setImageResource(R.drawable.ic_reply) diff --git a/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/TextComposer.kt b/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/TextComposer.kt index 50a344cbcd..093cdc858e 100644 --- a/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/TextComposer.kt +++ b/libraries/textcomposer/src/main/java/io/element/android/x/textcomposer/TextComposer.kt @@ -26,6 +26,8 @@ fun TextComposer( modifier: Modifier = Modifier, fullscreen: Boolean, onFullscreenToggle: () -> Unit, + composerMode: MessageComposerMode, + onCloseSpecialMode: () -> Unit, onComposerTextChange: (CharSequence) -> Unit, composerCanSendMessage: Boolean, composerText: String?, @@ -50,6 +52,7 @@ fun TextComposer( } override fun onCloseRelatedMessage() { + onCloseSpecialMode() } override fun onSendMessage(text: CharSequence) { @@ -70,7 +73,7 @@ fun TextComposer( } setFullScreen(fullscreen, true) (this as MessageComposerView).apply { - setup(isInDarkMode) + setup(isInDarkMode, composerMode) } } }, @@ -83,6 +86,7 @@ fun TextComposer( // Example of Compose -> View communication val messageComposerView = (view as MessageComposerView) view.setFullScreen(fullscreen, false) + // TODO messageComposerView.renderComposerMode(composerMode) messageComposerView.sendButton.isInvisible = !composerCanSendMessage messageComposerView.setTextIfDifferent(composerText ?: "") } @@ -107,7 +111,7 @@ private fun FakeComposer(modifier: Modifier) { } } -private fun MessageComposerView.setup(isDarkMode: Boolean) { +private fun MessageComposerView.setup(isDarkMode: Boolean, composerMode: MessageComposerMode) { val editTextColor = if (isDarkMode) { Color.WHITE } else { @@ -118,6 +122,7 @@ private fun MessageComposerView.setup(isDarkMode: Boolean) { editText.setHint(ElementR.string.room_message_placeholder) emojiButton?.isVisible = true sendButton.isVisible = true + // TODO renderComposerMode(composerMode) } @Preview @@ -128,6 +133,8 @@ fun TextComposerPreview() { fullscreen = false, onFullscreenToggle = { }, onComposerTextChange = {}, + composerMode = MessageComposerMode.Normal(""), + onCloseSpecialMode = {}, composerCanSendMessage = true, composerText = "Message", ) diff --git a/libraries/textcomposer/src/main/res/layout/composer_rich_text_layout.xml b/libraries/textcomposer/src/main/res/layout/composer_rich_text_layout.xml index 88f96c528e..f04884f603 100644 --- a/libraries/textcomposer/src/main/res/layout/composer_rich_text_layout.xml +++ b/libraries/textcomposer/src/main/res/layout/composer_rich_text_layout.xml @@ -8,6 +8,16 @@ android:orientation="vertical" android:background="@drawable/bg_composer_rich_bottom_sheet"> + + Date: Thu, 1 Dec 2022 11:19:54 +0100 Subject: [PATCH 2/5] Composer: Edit and reply: highlight selected item. --- .../MessageTimelineItemStateMapper.kt | 12 +++++++++-- .../x/features/messages/MessagesScreen.kt | 1 + .../x/features/messages/MessagesViewModel.kt | 20 ++++++++++++++---- .../messages/components/MessageEventBubble.kt | 21 +++++++++++++------ .../model/MessagesTimelineItemState.kt | 1 + .../element/android/x/designsystem/Color.kt | 5 +++++ 6 files changed, 48 insertions(+), 12 deletions(-) diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/MessageTimelineItemStateMapper.kt b/features/messages/src/main/java/io/element/android/x/features/messages/MessageTimelineItemStateMapper.kt index cebf95d4ba..3b6ccd2de7 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/MessageTimelineItemStateMapper.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/MessageTimelineItemStateMapper.kt @@ -25,6 +25,7 @@ class MessageTimelineItemStateMapper( private val room: MatrixRoom, private val dispatcher: CoroutineDispatcher, ) { + var highlightedEventId: String? = null suspend fun map(timelineItems: List): List = withContext(dispatcher) { @@ -33,7 +34,12 @@ class MessageTimelineItemStateMapper( val currentTimelineItem = timelineItems[index] val timelineItemState = when (currentTimelineItem) { is MatrixTimelineItem.Event -> { - buildMessageEvent(currentTimelineItem, index, timelineItems) + buildMessageEvent( + currentTimelineItem, + index, + timelineItems, + highlightedEventId + ) } is MatrixTimelineItem.Virtual -> MessagesTimelineItemState.Virtual( "virtual_item_$index" @@ -48,7 +54,8 @@ class MessageTimelineItemStateMapper( private suspend fun buildMessageEvent( currentTimelineItem: MatrixTimelineItem.Event, index: Int, - timelineItems: List + timelineItems: List, + highlightedEventId: String?, ): MessagesTimelineItemState.MessageEvent { val currentSender = currentTimelineItem.event.sender() val groupPosition = @@ -68,6 +75,7 @@ class MessageTimelineItemStateMapper( senderAvatar = senderAvatarData, content = currentTimelineItem.computeContent(), isMine = currentTimelineItem.event.isOwn(), + isHighlighted = currentTimelineItem.event.eventId().orEmpty() == highlightedEventId, groupPosition = groupPosition, reactionsState = currentTimelineItem.computeReactionsState() ) diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesScreen.kt b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesScreen.kt index 91c758a28a..d8324c2fe4 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesScreen.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesScreen.kt @@ -382,6 +382,7 @@ fun MessageEventRow( MessageEventBubble( groupPosition = messageEvent.groupPosition, isMine = messageEvent.isMine, + isHighlighted = messageEvent.isHighlighted, onClick = onClick, onLongClick = onLongClick, modifier = Modifier diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesViewModel.kt b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesViewModel.kt index 43bf852318..13bf229d2d 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/MessagesViewModel.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/MessagesViewModel.kt @@ -18,9 +18,7 @@ import io.element.android.x.matrix.room.MatrixRoom import io.element.android.x.matrix.timeline.MatrixTimeline import io.element.android.x.textcomposer.MessageComposerMode import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.* import kotlinx.coroutines.launch import timber.log.Timber @@ -191,7 +189,21 @@ class MessagesViewModel( } }.launchIn(viewModelScope) - timeline.timelineItems() + combine( + timeline.timelineItems(), + stateFlow + .map { it.composerMode } + .distinctUntilChanged() + ) { timelineItems, messageComposerMode -> + // Set the highlightedEventId to messageTimelineItemStateMapper, before the mapping occurs + messageTimelineItemStateMapper.highlightedEventId = when (messageComposerMode) { + is MessageComposerMode.Normal -> null + is MessageComposerMode.Edit -> messageComposerMode.eventId + is MessageComposerMode.Quote -> null + is MessageComposerMode.Reply -> messageComposerMode.eventId + } + timelineItems + } .map(messageTimelineItemStateMapper::map) .execute { copy(timelineItems = it) diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/components/MessageEventBubble.kt b/features/messages/src/main/java/io/element/android/x/features/messages/components/MessageEventBubble.kt index f687fdd468..a0b17e4005 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/components/MessageEventBubble.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/components/MessageEventBubble.kt @@ -24,6 +24,7 @@ private val BUBBLE_RADIUS = 16.dp fun MessageEventBubble( groupPosition: MessagesItemGroupPosition, isMine: Boolean, + isHighlighted: Boolean, modifier: Modifier = Modifier, onClick: () -> Unit, onLongClick: () -> Unit, @@ -64,17 +65,25 @@ fun MessageEventBubble( } } - val backgroundBubbleColor = if (isMine) { + val backgroundBubbleColor = if (isHighlighted) { if (LocalIsDarkTheme.current) { - SystemGrey5Dark + MessageHighlightDark } else { - SystemGrey5Light + MessageHighlightLight } } else { - if (LocalIsDarkTheme.current) { - SystemGrey6Dark + if (isMine) { + if (LocalIsDarkTheme.current) { + SystemGrey5Dark + } else { + SystemGrey5Light + } } else { - SystemGrey6Light + if (LocalIsDarkTheme.current) { + SystemGrey6Dark + } else { + SystemGrey6Light + } } } val bubbleShape = bubbleShape() diff --git a/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesTimelineItemState.kt b/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesTimelineItemState.kt index e194e9a1a4..5e24423059 100644 --- a/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesTimelineItemState.kt +++ b/features/messages/src/main/java/io/element/android/x/features/messages/model/MessagesTimelineItemState.kt @@ -16,6 +16,7 @@ sealed interface MessagesTimelineItemState { val content: MessagesTimelineItemContent, val sentTime: String = "", val isMine: Boolean = false, + val isHighlighted: Boolean = false, val groupPosition: MessagesItemGroupPosition = MessagesItemGroupPosition.None, val reactionsState: MessagesItemReactionState ) : MessagesTimelineItemState { diff --git a/libraries/designsystem/src/main/java/io/element/android/x/designsystem/Color.kt b/libraries/designsystem/src/main/java/io/element/android/x/designsystem/Color.kt index 33925b8fbc..4ad0a1e99a 100644 --- a/libraries/designsystem/src/main/java/io/element/android/x/designsystem/Color.kt +++ b/libraries/designsystem/src/main/java/io/element/android/x/designsystem/Color.kt @@ -34,3 +34,8 @@ val Melon = Color(0xFFFF812D) val ElementGreen = Color(0xFF0DBD8B) val ElementOrange = Color(0xFFD9B072) val Vermilion = Color(0xFFFF5B55) + +// TODO Update color +val MessageHighlightLight = Azure +// TODO Update color +val MessageHighlightDark = Azure From 7497b511ee0a1319b78084d647eb0db9f5faf8a0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 1 Dec 2022 11:43:36 +0100 Subject: [PATCH 3/5] Composer: Fix issue in dark theme --- .../src/main/res/layout/composer_rich_text_layout.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libraries/textcomposer/src/main/res/layout/composer_rich_text_layout.xml b/libraries/textcomposer/src/main/res/layout/composer_rich_text_layout.xml index f04884f603..d099c72cab 100644 --- a/libraries/textcomposer/src/main/res/layout/composer_rich_text_layout.xml +++ b/libraries/textcomposer/src/main/res/layout/composer_rich_text_layout.xml @@ -5,8 +5,9 @@ android:id="@+id/composerLayout" android:layout_width="match_parent" android:layout_height="match_parent" - android:orientation="vertical" - android:background="@drawable/bg_composer_rich_bottom_sheet"> + android:orientation="vertical"> + +