Iterate on reactions (#668)

This commit is contained in:
Benoit Marty 2023-06-26 18:31:50 +02:00 committed by Benoit Marty
parent c7ec8bc9e9
commit 2cef47c271
8 changed files with 51 additions and 33 deletions

View file

@ -100,7 +100,7 @@ internal fun aTimelineItemEvent(
sendState: EventSendState = EventSendState.Sent(eventId), sendState: EventSendState = EventSendState.Sent(eventId),
inReplyTo: InReplyTo? = null, inReplyTo: InReplyTo? = null,
debugInfo: TimelineItemDebugInfo = aTimelineItemDebugInfo(), debugInfo: TimelineItemDebugInfo = aTimelineItemDebugInfo(),
timelineItemReactions: TimelineItemReactions = aTimelineItemReactions(isMine = isMine), timelineItemReactions: TimelineItemReactions = aTimelineItemReactions(),
): TimelineItem.Event { ): TimelineItem.Event {
return TimelineItem.Event( return TimelineItem.Event(
id = eventId.value, id = eventId.value,
@ -122,12 +122,12 @@ internal fun aTimelineItemEvent(
fun aTimelineItemReactions( fun aTimelineItemReactions(
count: Int = 1, count: Int = 1,
isMine: Boolean = true, isHighlighted: Boolean = false,
): TimelineItemReactions { ): TimelineItemReactions {
return TimelineItemReactions( return TimelineItemReactions(
reactions = buildList { reactions = buildList {
repeat(count) { repeat(count) {
add(AggregatedReaction(key = "👍", count = (it + 1).toString(), isOnMyMessage = isMine)) add(AggregatedReaction(key = "👍", count = 1 + it, isHighlighted = isHighlighted))
} }
}.toPersistentList() }.toPersistentList()
) )

View file

@ -17,6 +17,8 @@
package io.element.android.features.messages.impl.timeline.components package io.element.android.features.messages.impl.timeline.components
import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -27,6 +29,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview 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
@ -41,25 +44,36 @@ import io.element.android.libraries.designsystem.theme.components.Text
@Composable @Composable
fun MessagesReactionButton(reaction: AggregatedReaction, modifier: Modifier = Modifier) { fun MessagesReactionButton(reaction: AggregatedReaction, modifier: Modifier = Modifier) {
val backgroundColor = if (reaction.isOnMyMessage) { // First Surface is to render a border with the same background color as the background
ElementTheme.colors.messageFromMeBackground
} else {
ElementTheme.colors.messageFromOtherBackground
}
Surface( Surface(
modifier = modifier, modifier = modifier,
color = backgroundColor, // TODO Should use compound.bgSubtlePrimary
color = ElementTheme.colors.gray300,
border = BorderStroke(2.dp, MaterialTheme.colorScheme.background), border = BorderStroke(2.dp, MaterialTheme.colorScheme.background),
shape = RoundedCornerShape(corner = CornerSize(14.dp)), shape = RoundedCornerShape(corner = CornerSize(14.dp)),
) { ) {
Row( Box(modifier = Modifier.padding(2.dp)) {
modifier = Modifier.padding(vertical = 6.dp, horizontal = 12.dp), val reactionModifier = if (reaction.isHighlighted) {
verticalAlignment = Alignment.CenterVertically Modifier
) { // TODO Check the color, should use compound.borderInteractivePrimary
// TODO `reaction.isHighlighted` is not used. .border(BorderStroke(1.dp, Color(0xFF808994)), RoundedCornerShape(corner = CornerSize(12.dp)))
Text(text = reaction.key, fontSize = 14.sp) } else {
Spacer(modifier = Modifier.width(4.dp)) Modifier
Text(text = reaction.count, color = MaterialTheme.colorScheme.secondary, fontSize = 12.sp) }
Row(
modifier = reactionModifier.padding(vertical = 4.dp, horizontal = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Text(text = reaction.key, fontSize = 15.sp)
if (reaction.count > 1) {
Spacer(modifier = Modifier.width(4.dp))
Text(
text = reaction.count.toString(),
color = if (reaction.isHighlighted) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.secondary,
fontSize = 14.sp
)
}
}
} }
} }
} }

View file

@ -24,6 +24,7 @@ import io.element.android.features.messages.impl.timeline.model.TimelineItemReac
import io.element.android.libraries.core.bool.orTrue import io.element.android.libraries.core.bool.orTrue
import io.element.android.libraries.designsystem.components.avatar.AvatarData import io.element.android.libraries.designsystem.components.avatar.AvatarData
import io.element.android.libraries.designsystem.components.avatar.AvatarSize import io.element.android.libraries.designsystem.components.avatar.AvatarSize
import io.element.android.libraries.matrix.api.MatrixClient
import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem import io.element.android.libraries.matrix.api.timeline.MatrixTimelineItem
import io.element.android.libraries.matrix.api.timeline.item.event.EventSendState import io.element.android.libraries.matrix.api.timeline.item.event.EventSendState
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails import io.element.android.libraries.matrix.api.timeline.item.event.ProfileTimelineDetails
@ -34,6 +35,7 @@ import javax.inject.Inject
class TimelineItemEventFactory @Inject constructor( class TimelineItemEventFactory @Inject constructor(
private val contentFactory: TimelineItemContentFactory, private val contentFactory: TimelineItemContentFactory,
private val matrixClient: MatrixClient,
) { ) {
fun create( fun create(
@ -91,11 +93,11 @@ class TimelineItemEventFactory @Inject constructor(
val aggregatedReactions = event.reactions.map { val aggregatedReactions = event.reactions.map {
AggregatedReaction( AggregatedReaction(
key = it.key, key = it.key,
count = it.count.toString(), count = it.count.toInt(),
isHighlighted = false, isHighlighted = it.senderIds.contains(matrixClient.sessionId),
isOnMyMessage = event.isOwn,
) )
} }
aggregatedReactions.sortedByDescending { it.count }
return TimelineItemReactions(aggregatedReactions.toImmutableList()) return TimelineItemReactions(aggregatedReactions.toImmutableList())
} }

View file

@ -16,9 +16,11 @@
package io.element.android.features.messages.impl.timeline.model package io.element.android.features.messages.impl.timeline.model
data class AggregatedReaction constructor( /**
* @property isHighlighted true if the reaction has (also) been sent by the current user.
*/
data class AggregatedReaction(
val key: String, val key: String,
val count: String, val count: Int,
val isOnMyMessage: Boolean,
val isHighlighted: Boolean = false val isHighlighted: Boolean = false
) )

View file

@ -22,22 +22,18 @@ open class AggregatedReactionProvider : PreviewParameterProvider<AggregatedReact
override val values: Sequence<AggregatedReaction> override val values: Sequence<AggregatedReaction>
get() = sequenceOf(false, true).flatMap { get() = sequenceOf(false, true).flatMap {
sequenceOf( sequenceOf(
anAggregatedReaction(isOnMyMessage = it), anAggregatedReaction(isHighlighted = it),
anAggregatedReaction(isOnMyMessage = it, count = "88"), anAggregatedReaction(isHighlighted = it, count = 88),
anAggregatedReaction(isOnMyMessage = it, isHighlighted = true),
anAggregatedReaction(isOnMyMessage = it, count = "88", isHighlighted = true),
) )
} }
} }
fun anAggregatedReaction( fun anAggregatedReaction(
key: String = "👍", key: String = "👍",
count: String = "1", // TODO Why is it a String? count: Int = 1,
isOnMyMessage: Boolean = false,
isHighlighted: Boolean = false, isHighlighted: Boolean = false,
) = AggregatedReaction( ) = AggregatedReaction(
key = key, key = key,
count = count, count = count,
isOnMyMessage = isOnMyMessage,
isHighlighted = isHighlighted, isHighlighted = isHighlighted,
) )

View file

@ -47,7 +47,7 @@ internal fun aMessageEvent(
content = content, content = content,
sentTime = "", sentTime = "",
isMine = isMine, isMine = isMine,
reactionsState = aTimelineItemReactions(count = 0, isMine = isMine), reactionsState = aTimelineItemReactions(count = 0),
sendState = EventSendState.Sent(AN_EVENT_ID), sendState = EventSendState.Sent(AN_EVENT_ID),
inReplyTo = inReplyTo, inReplyTo = inReplyTo,
debugInfo = debugInfo, debugInfo = debugInfo,

View file

@ -16,7 +16,10 @@
package io.element.android.libraries.matrix.api.timeline.item.event package io.element.android.libraries.matrix.api.timeline.item.event
import io.element.android.libraries.matrix.api.core.UserId
data class EventReaction( data class EventReaction(
val key: String, val key: String,
val count: Long val count: Long,
val senderIds: List<UserId>
) )

View file

@ -78,7 +78,8 @@ private fun List<Reaction>?.map(): List<EventReaction> {
return this?.map { return this?.map {
EventReaction( EventReaction(
key = it.key, key = it.key,
count = it.count.toLong() count = it.count.toLong(),
senderIds = it.senders.map { sender -> UserId(sender) }
) )
} ?: emptyList() } ?: emptyList()
} }