Add preview for MessageEventBubble.
This commit is contained in:
parent
04248b4996
commit
cbbcd49bc1
4 changed files with 137 additions and 14 deletions
|
|
@ -54,6 +54,7 @@ import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.zIndex
|
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.MessageEventBubble
|
||||||
import io.element.android.features.messages.timeline.components.TimelineItemEncryptedView
|
import io.element.android.features.messages.timeline.components.TimelineItemEncryptedView
|
||||||
import io.element.android.features.messages.timeline.components.TimelineItemImageView
|
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.TimelineItemTextView
|
||||||
import io.element.android.features.messages.timeline.components.TimelineItemUnknownView
|
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.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.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.TimelineItemEncryptedContent
|
||||||
import io.element.android.features.messages.timeline.model.content.TimelineItemImageContent
|
import io.element.android.features.messages.timeline.model.content.TimelineItemImageContent
|
||||||
import io.element.android.features.messages.timeline.model.content.TimelineItemRedactedContent
|
import io.element.android.features.messages.timeline.model.content.TimelineItemRedactedContent
|
||||||
|
|
@ -193,10 +194,12 @@ fun MessageEventRow(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
MessageEventBubble(
|
MessageEventBubble(
|
||||||
groupPosition = messageEvent.groupPosition,
|
state = BubbleState(
|
||||||
isMine = messageEvent.isMine,
|
groupPosition = messageEvent.groupPosition,
|
||||||
|
isMine = messageEvent.isMine,
|
||||||
|
isHighlighted = isHighlighted,
|
||||||
|
),
|
||||||
interactionSource = interactionSource,
|
interactionSource = interactionSource,
|
||||||
isHighlighted = isHighlighted,
|
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
onLongClick = onLongClick,
|
onLongClick = onLongClick,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
)
|
||||||
|
|
@ -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<BubbleState> {
|
||||||
|
override val values: Sequence<BubbleState>
|
||||||
|
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,
|
||||||
|
)
|
||||||
|
|
@ -19,16 +19,25 @@ package io.element.android.features.messages.timeline.components
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||||
import androidx.compose.foundation.combinedClickable
|
import androidx.compose.foundation.combinedClickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
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.offset
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.widthIn
|
import androidx.compose.foundation.layout.widthIn
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.ripple.rememberRipple
|
import androidx.compose.material.ripple.rememberRipple
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Shape
|
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 androidx.compose.ui.unit.dp
|
||||||
import io.element.android.features.messages.timeline.model.MessagesItemGroupPosition
|
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.ElementTheme
|
||||||
import io.element.android.libraries.designsystem.theme.components.Surface
|
import io.element.android.libraries.designsystem.theme.components.Surface
|
||||||
|
|
||||||
|
|
@ -37,28 +46,26 @@ private val BUBBLE_RADIUS = 16.dp
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun MessageEventBubble(
|
fun MessageEventBubble(
|
||||||
groupPosition: MessagesItemGroupPosition,
|
state: BubbleState,
|
||||||
isMine: Boolean,
|
|
||||||
interactionSource: MutableInteractionSource,
|
interactionSource: MutableInteractionSource,
|
||||||
isHighlighted: Boolean,
|
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
onClick: () -> Unit = {},
|
onClick: () -> Unit = {},
|
||||||
onLongClick: () -> Unit = {},
|
onLongClick: () -> Unit = {},
|
||||||
content: @Composable () -> Unit = {},
|
content: @Composable () -> Unit = {},
|
||||||
) {
|
) {
|
||||||
fun bubbleShape(): Shape {
|
fun bubbleShape(): Shape {
|
||||||
return when (groupPosition) {
|
return when (state.groupPosition) {
|
||||||
MessagesItemGroupPosition.First -> if (isMine) {
|
MessagesItemGroupPosition.First -> if (state.isMine) {
|
||||||
RoundedCornerShape(BUBBLE_RADIUS, BUBBLE_RADIUS, 0.dp, BUBBLE_RADIUS)
|
RoundedCornerShape(BUBBLE_RADIUS, BUBBLE_RADIUS, 0.dp, BUBBLE_RADIUS)
|
||||||
} else {
|
} else {
|
||||||
RoundedCornerShape(BUBBLE_RADIUS, BUBBLE_RADIUS, BUBBLE_RADIUS, 0.dp)
|
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)
|
RoundedCornerShape(BUBBLE_RADIUS, 0.dp, 0.dp, BUBBLE_RADIUS)
|
||||||
} else {
|
} else {
|
||||||
RoundedCornerShape(0.dp, BUBBLE_RADIUS, BUBBLE_RADIUS, 0.dp)
|
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)
|
RoundedCornerShape(BUBBLE_RADIUS, 0.dp, BUBBLE_RADIUS, BUBBLE_RADIUS)
|
||||||
} else {
|
} else {
|
||||||
RoundedCornerShape(0.dp, BUBBLE_RADIUS, BUBBLE_RADIUS, BUBBLE_RADIUS)
|
RoundedCornerShape(0.dp, BUBBLE_RADIUS, BUBBLE_RADIUS, BUBBLE_RADIUS)
|
||||||
|
|
@ -74,17 +81,17 @@ fun MessageEventBubble(
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Modifier.offsetForItem(): Modifier {
|
fun Modifier.offsetForItem(): Modifier {
|
||||||
return if (isMine) {
|
return if (state.isMine) {
|
||||||
offset(y = -(12.dp))
|
offset(y = -(12.dp))
|
||||||
} else {
|
} else {
|
||||||
offset(x = 20.dp, y = -(12.dp))
|
offset(x = 20.dp, y = -(12.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val backgroundBubbleColor = if (isHighlighted) {
|
val backgroundBubbleColor = if (state.isHighlighted) {
|
||||||
ElementTheme.colors.messageHighlightedBackground
|
ElementTheme.colors.messageHighlightedBackground
|
||||||
} else {
|
} else {
|
||||||
if (isMine) {
|
if (state.isMine) {
|
||||||
ElementTheme.colors.messageFromMeBackground
|
ElementTheme.colors.messageFromMeBackground
|
||||||
} else {
|
} else {
|
||||||
ElementTheme.colors.messageFromOtherBackground
|
ElementTheme.colors.messageFromOtherBackground
|
||||||
|
|
@ -107,3 +114,31 @@ fun MessageEventBubble(
|
||||||
content = content
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue