From d382f59b5056ee3fae323fa1e4d6a12b598e3bce Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 14 Feb 2023 12:54:06 +0100 Subject: [PATCH] Add preview for MessageEventBubble. --- .../messages/timeline/TimelineView.kt | 11 ++-- .../timeline/components/BubbleState.kt | 27 +++++++++ .../components/BubbleStateProvider.kt | 58 +++++++++++++++++++ .../timeline/components/MessageEventBubble.kt | 55 ++++++++++++++---- 4 files changed, 137 insertions(+), 14 deletions(-) create mode 100644 features/messages/src/main/kotlin/io/element/android/features/messages/timeline/components/BubbleState.kt create mode 100644 features/messages/src/main/kotlin/io/element/android/features/messages/timeline/components/BubbleStateProvider.kt diff --git a/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/TimelineView.kt b/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/TimelineView.kt index b211aead61..1a9879b2cb 100644 --- a/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/TimelineView.kt +++ b/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/TimelineView.kt @@ -54,6 +54,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import androidx.compose.ui.zIndex +import io.element.android.features.messages.timeline.components.BubbleState import io.element.android.features.messages.timeline.components.MessageEventBubble import io.element.android.features.messages.timeline.components.TimelineItemEncryptedView import io.element.android.features.messages.timeline.components.TimelineItemImageView @@ -62,8 +63,8 @@ import io.element.android.features.messages.timeline.components.TimelineItemReda import io.element.android.features.messages.timeline.components.TimelineItemTextView import io.element.android.features.messages.timeline.components.TimelineItemUnknownView import io.element.android.features.messages.timeline.model.TimelineItem -import io.element.android.features.messages.timeline.model.content.TimelineItemContentProvider import io.element.android.features.messages.timeline.model.content.TimelineItemContent +import io.element.android.features.messages.timeline.model.content.TimelineItemContentProvider import io.element.android.features.messages.timeline.model.content.TimelineItemEncryptedContent import io.element.android.features.messages.timeline.model.content.TimelineItemImageContent import io.element.android.features.messages.timeline.model.content.TimelineItemRedactedContent @@ -193,10 +194,12 @@ fun MessageEventRow( ) } MessageEventBubble( - groupPosition = messageEvent.groupPosition, - isMine = messageEvent.isMine, + state = BubbleState( + groupPosition = messageEvent.groupPosition, + isMine = messageEvent.isMine, + isHighlighted = isHighlighted, + ), interactionSource = interactionSource, - isHighlighted = isHighlighted, onClick = onClick, onLongClick = onLongClick, modifier = Modifier diff --git a/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/components/BubbleState.kt b/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/components/BubbleState.kt new file mode 100644 index 0000000000..08e1e7c584 --- /dev/null +++ b/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/components/BubbleState.kt @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.messages.timeline.components + +import androidx.compose.runtime.Stable +import io.element.android.features.messages.timeline.model.MessagesItemGroupPosition + +@Stable +data class BubbleState( + val groupPosition: MessagesItemGroupPosition, + val isMine: Boolean, + val isHighlighted: Boolean, +) diff --git a/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/components/BubbleStateProvider.kt b/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/components/BubbleStateProvider.kt new file mode 100644 index 0000000000..c2b2680283 --- /dev/null +++ b/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/components/BubbleStateProvider.kt @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.element.android.features.messages.timeline.components + +import androidx.compose.ui.tooling.preview.PreviewParameterProvider +import io.element.android.features.messages.timeline.model.MessagesItemGroupPosition + +open class BubbleStateProvider : PreviewParameterProvider { + override val values: Sequence + get() = sequenceOf( + BubbleState(MessagesItemGroupPosition.First, isMine = false, isHighlighted = false), + BubbleState(MessagesItemGroupPosition.First, isMine = false, isHighlighted = true), + BubbleState(MessagesItemGroupPosition.First, isMine = false, isHighlighted = false), + BubbleState(MessagesItemGroupPosition.First, isMine = false, isHighlighted = true), + BubbleState(MessagesItemGroupPosition.First, isMine = true, isHighlighted = false), + BubbleState(MessagesItemGroupPosition.First, isMine = true, isHighlighted = true), + BubbleState(MessagesItemGroupPosition.First, isMine = true, isHighlighted = false), + BubbleState(MessagesItemGroupPosition.First, isMine = true, isHighlighted = true), + + BubbleState(MessagesItemGroupPosition.Middle, isMine = false, isHighlighted = false), + BubbleState(MessagesItemGroupPosition.Middle, isMine = false, isHighlighted = true), + BubbleState(MessagesItemGroupPosition.Middle, isMine = false, isHighlighted = false), + BubbleState(MessagesItemGroupPosition.Middle, isMine = false, isHighlighted = true), + BubbleState(MessagesItemGroupPosition.Middle, isMine = true, isHighlighted = false), + BubbleState(MessagesItemGroupPosition.Middle, isMine = true, isHighlighted = true), + BubbleState(MessagesItemGroupPosition.Middle, isMine = true, isHighlighted = false), + BubbleState(MessagesItemGroupPosition.Middle, isMine = true, isHighlighted = true), + + BubbleState(MessagesItemGroupPosition.Last, isMine = false, isHighlighted = false), + BubbleState(MessagesItemGroupPosition.Last, isMine = false, isHighlighted = true), + BubbleState(MessagesItemGroupPosition.Last, isMine = false, isHighlighted = false), + BubbleState(MessagesItemGroupPosition.Last, isMine = false, isHighlighted = true), + BubbleState(MessagesItemGroupPosition.Last, isMine = true, isHighlighted = false), + BubbleState(MessagesItemGroupPosition.Last, isMine = true, isHighlighted = true), + BubbleState(MessagesItemGroupPosition.Last, isMine = true, isHighlighted = false), + BubbleState(MessagesItemGroupPosition.Last, isMine = true, isHighlighted = true), + ) +} + +fun aBubbleState() = BubbleState( + groupPosition = MessagesItemGroupPosition.First, + isMine = false, + isHighlighted = false, +) diff --git a/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/components/MessageEventBubble.kt b/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/components/MessageEventBubble.kt index 02fa87ec29..06c1d078aa 100644 --- a/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/components/MessageEventBubble.kt +++ b/features/messages/src/main/kotlin/io/element/android/features/messages/timeline/components/MessageEventBubble.kt @@ -19,16 +19,25 @@ package io.element.android.features.messages.timeline.components import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.offset +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.ripple.rememberRipple import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.unit.dp import io.element.android.features.messages.timeline.model.MessagesItemGroupPosition +import io.element.android.libraries.designsystem.preview.ElementPreviewDark +import io.element.android.libraries.designsystem.preview.ElementPreviewLight import io.element.android.libraries.designsystem.theme.ElementTheme import io.element.android.libraries.designsystem.theme.components.Surface @@ -37,28 +46,26 @@ private val BUBBLE_RADIUS = 16.dp @OptIn(ExperimentalFoundationApi::class) @Composable fun MessageEventBubble( - groupPosition: MessagesItemGroupPosition, - isMine: Boolean, + state: BubbleState, interactionSource: MutableInteractionSource, - isHighlighted: Boolean, modifier: Modifier = Modifier, onClick: () -> Unit = {}, onLongClick: () -> Unit = {}, content: @Composable () -> Unit = {}, ) { fun bubbleShape(): Shape { - return when (groupPosition) { - MessagesItemGroupPosition.First -> if (isMine) { + return when (state.groupPosition) { + MessagesItemGroupPosition.First -> if (state.isMine) { RoundedCornerShape(BUBBLE_RADIUS, BUBBLE_RADIUS, 0.dp, BUBBLE_RADIUS) } else { RoundedCornerShape(BUBBLE_RADIUS, BUBBLE_RADIUS, BUBBLE_RADIUS, 0.dp) } - MessagesItemGroupPosition.Middle -> if (isMine) { + MessagesItemGroupPosition.Middle -> if (state.isMine) { RoundedCornerShape(BUBBLE_RADIUS, 0.dp, 0.dp, BUBBLE_RADIUS) } else { RoundedCornerShape(0.dp, BUBBLE_RADIUS, BUBBLE_RADIUS, 0.dp) } - MessagesItemGroupPosition.Last -> if (isMine) { + MessagesItemGroupPosition.Last -> if (state.isMine) { RoundedCornerShape(BUBBLE_RADIUS, 0.dp, BUBBLE_RADIUS, BUBBLE_RADIUS) } else { RoundedCornerShape(0.dp, BUBBLE_RADIUS, BUBBLE_RADIUS, BUBBLE_RADIUS) @@ -74,17 +81,17 @@ fun MessageEventBubble( } fun Modifier.offsetForItem(): Modifier { - return if (isMine) { + return if (state.isMine) { offset(y = -(12.dp)) } else { offset(x = 20.dp, y = -(12.dp)) } } - val backgroundBubbleColor = if (isHighlighted) { + val backgroundBubbleColor = if (state.isHighlighted) { ElementTheme.colors.messageHighlightedBackground } else { - if (isMine) { + if (state.isMine) { ElementTheme.colors.messageFromMeBackground } else { ElementTheme.colors.messageFromOtherBackground @@ -107,3 +114,31 @@ fun MessageEventBubble( content = content ) } + +@Preview +@Composable +internal fun MessageEventBubbleLightPreview(@PreviewParameter(BubbleStateProvider::class) state: BubbleState) = + ElementPreviewLight { ContentToPreview(state) } + +@Preview +@Composable +internal fun MessageEventBubbleDarkPreview(@PreviewParameter(BubbleStateProvider::class) state: BubbleState) = + ElementPreviewDark { ContentToPreview(state) } + +@Composable +private fun ContentToPreview(state: BubbleState) { + // Due to y offset, surround with a Box + Box( + modifier = Modifier + .size(width = 240.dp, height = 64.dp) + .padding(8.dp), + contentAlignment = Alignment.CenterStart, + ) { + MessageEventBubble( + state = state, + interactionSource = MutableInteractionSource(), + ) { + Spacer(modifier = Modifier.size(width = 120.dp, height = 32.dp)) + } + } +}