Highlight user's reactions in message actions menu (#778)
Part of #342 --------- Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
parent
b2ffad5663
commit
4a2b6b4b8a
15 changed files with 120 additions and 75 deletions
|
|
@ -19,6 +19,7 @@ package io.element.android.features.messages.impl.actionlist
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
import io.element.android.features.messages.impl.actionlist.model.TimelineItemAction
|
||||||
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
|
||||||
|
import io.element.android.features.messages.impl.timeline.aTimelineItemReactions
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemFileContent
|
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemFileContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
|
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
|
||||||
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemLocationContent
|
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemLocationContent
|
||||||
|
|
@ -28,47 +29,62 @@ import kotlinx.collections.immutable.persistentListOf
|
||||||
|
|
||||||
open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
|
open class ActionListStateProvider : PreviewParameterProvider<ActionListState> {
|
||||||
override val values: Sequence<ActionListState>
|
override val values: Sequence<ActionListState>
|
||||||
get() = sequenceOf(
|
get() {
|
||||||
anActionListState(),
|
val reactionsState = aTimelineItemReactions(1, isHighlighted = true)
|
||||||
anActionListState().copy(target = ActionListState.Target.Loading(aTimelineItemEvent())),
|
return sequenceOf(
|
||||||
anActionListState().copy(
|
anActionListState(),
|
||||||
target = ActionListState.Target.Success(
|
anActionListState().copy(target = ActionListState.Target.Loading(aTimelineItemEvent())),
|
||||||
event = aTimelineItemEvent(),
|
anActionListState().copy(
|
||||||
actions = aTimelineItemActionList(),
|
target = ActionListState.Target.Success(
|
||||||
)
|
event = aTimelineItemEvent().copy(
|
||||||
),
|
reactionsState = reactionsState
|
||||||
anActionListState().copy(
|
),
|
||||||
target = ActionListState.Target.Success(
|
actions = aTimelineItemActionList(),
|
||||||
event = aTimelineItemEvent(content = aTimelineItemImageContent()),
|
)
|
||||||
actions = aTimelineItemActionList(),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
anActionListState().copy(
|
|
||||||
target = ActionListState.Target.Success(
|
|
||||||
event = aTimelineItemEvent(content = aTimelineItemVideoContent()),
|
|
||||||
actions = aTimelineItemActionList(),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
anActionListState().copy(
|
|
||||||
target = ActionListState.Target.Success(
|
|
||||||
event = aTimelineItemEvent(content = aTimelineItemFileContent()),
|
|
||||||
actions = aTimelineItemActionList(),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
anActionListState().copy(
|
|
||||||
target = ActionListState.Target.Success(
|
|
||||||
event = aTimelineItemEvent(content = aTimelineItemLocationContent()),
|
|
||||||
actions = aTimelineItemActionList(),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
anActionListState().copy(
|
|
||||||
target = ActionListState.Target.Success(
|
|
||||||
event = aTimelineItemEvent(content = aTimelineItemLocationContent()),
|
|
||||||
actions = aTimelineItemActionList(),
|
|
||||||
),
|
),
|
||||||
displayEmojiReactions = false,
|
anActionListState().copy(
|
||||||
),
|
target = ActionListState.Target.Success(
|
||||||
)
|
event = aTimelineItemEvent(content = aTimelineItemImageContent()).copy(
|
||||||
|
reactionsState = reactionsState
|
||||||
|
),
|
||||||
|
actions = aTimelineItemActionList(),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
anActionListState().copy(
|
||||||
|
target = ActionListState.Target.Success(
|
||||||
|
event = aTimelineItemEvent(content = aTimelineItemVideoContent()).copy(
|
||||||
|
reactionsState = reactionsState
|
||||||
|
),
|
||||||
|
actions = aTimelineItemActionList(),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
anActionListState().copy(
|
||||||
|
target = ActionListState.Target.Success(
|
||||||
|
event = aTimelineItemEvent(content = aTimelineItemFileContent()).copy(
|
||||||
|
reactionsState = reactionsState
|
||||||
|
),
|
||||||
|
actions = aTimelineItemActionList(),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
anActionListState().copy(
|
||||||
|
target = ActionListState.Target.Success(
|
||||||
|
event = aTimelineItemEvent(content = aTimelineItemLocationContent()).copy(
|
||||||
|
reactionsState = reactionsState
|
||||||
|
),
|
||||||
|
actions = aTimelineItemActionList(),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
anActionListState().copy(
|
||||||
|
target = ActionListState.Target.Success(
|
||||||
|
event = aTimelineItemEvent(content = aTimelineItemLocationContent()).copy(
|
||||||
|
reactionsState = reactionsState
|
||||||
|
),
|
||||||
|
actions = aTimelineItemActionList(),
|
||||||
|
),
|
||||||
|
displayEmojiReactions = false,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun anActionListState() = ActionListState(
|
fun anActionListState() = ActionListState(
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.actionlist
|
package io.element.android.features.messages.impl.actionlist
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
|
@ -46,6 +47,7 @@ import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.Alignment
|
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.Color
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
|
@ -78,7 +80,9 @@ import io.element.android.libraries.designsystem.theme.components.hide
|
||||||
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail
|
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnail
|
||||||
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
|
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailInfo
|
||||||
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
|
import io.element.android.libraries.matrix.ui.components.AttachmentThumbnailType
|
||||||
|
import io.element.android.libraries.theme.ElementTheme
|
||||||
import io.element.android.libraries.ui.strings.CommonStrings
|
import io.element.android.libraries.ui.strings.CommonStrings
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -178,6 +182,7 @@ private fun SheetContent(
|
||||||
if (state.displayEmojiReactions) {
|
if (state.displayEmojiReactions) {
|
||||||
item {
|
item {
|
||||||
EmojiReactionsRow(
|
EmojiReactionsRow(
|
||||||
|
highlightedEmojis = target.event.reactionsState.highlightedKeys,
|
||||||
onEmojiReactionClicked = onEmojiReactionClicked,
|
onEmojiReactionClicked = onEmojiReactionClicked,
|
||||||
onCustomReactionClicked = onCustomReactionClicked,
|
onCustomReactionClicked = onCustomReactionClicked,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
|
@ -322,6 +327,7 @@ private val emojiRippleRadius = 24.dp
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun EmojiReactionsRow(
|
internal fun EmojiReactionsRow(
|
||||||
|
highlightedEmojis: ImmutableList<String>,
|
||||||
onEmojiReactionClicked: (String) -> Unit,
|
onEmojiReactionClicked: (String) -> Unit,
|
||||||
onCustomReactionClicked: () -> Unit,
|
onCustomReactionClicked: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
|
@ -335,7 +341,8 @@ internal fun EmojiReactionsRow(
|
||||||
"👍", "👎", "🔥", "❤️", "👏"
|
"👍", "👎", "🔥", "❤️", "👏"
|
||||||
)
|
)
|
||||||
for (emoji in defaultEmojis) {
|
for (emoji in defaultEmojis) {
|
||||||
EmojiButton(emoji, onEmojiReactionClicked)
|
val isHighlighted = highlightedEmojis.contains(emoji)
|
||||||
|
EmojiButton(emoji, isHighlighted, onEmojiReactionClicked)
|
||||||
}
|
}
|
||||||
|
|
||||||
Icon(
|
Icon(
|
||||||
|
|
@ -358,19 +365,34 @@ internal fun EmojiReactionsRow(
|
||||||
@Composable
|
@Composable
|
||||||
private fun EmojiButton(
|
private fun EmojiButton(
|
||||||
emoji: String,
|
emoji: String,
|
||||||
|
isHighlighted: Boolean,
|
||||||
onClicked: (String) -> Unit,
|
onClicked: (String) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
Text(
|
val backgroundColor = if (isHighlighted) {
|
||||||
emoji,
|
ElementTheme.colors.bgActionPrimaryRest
|
||||||
fontSize = 28.dp.toSp(),
|
} else {
|
||||||
modifier = modifier.clickable(
|
Color.Transparent
|
||||||
enabled = true,
|
}
|
||||||
onClick = { onClicked(emoji) },
|
Box(
|
||||||
indication = rememberRipple(bounded = false, radius = emojiRippleRadius),
|
modifier = modifier
|
||||||
interactionSource = remember { MutableInteractionSource() }
|
.size(48.dp)
|
||||||
|
.background(backgroundColor, RoundedCornerShape(24.dp)),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
emoji,
|
||||||
|
fontSize = 28.dp.toSp(),
|
||||||
|
color = Color.White,
|
||||||
|
modifier = Modifier
|
||||||
|
.clickable(
|
||||||
|
enabled = true,
|
||||||
|
onClick = { onClicked(emoji) },
|
||||||
|
indication = rememberRipple(bounded = false, radius = emojiRippleRadius),
|
||||||
|
interactionSource = remember { MutableInteractionSource() }
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,14 @@
|
||||||
package io.element.android.features.messages.impl.timeline.model
|
package io.element.android.features.messages.impl.timeline.model
|
||||||
|
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
|
|
||||||
data class TimelineItemReactions(
|
data class TimelineItemReactions(
|
||||||
val reactions: ImmutableList<AggregatedReaction>
|
val reactions: ImmutableList<AggregatedReaction>
|
||||||
)
|
) {
|
||||||
|
val highlightedKeys: ImmutableList<String>
|
||||||
|
get() = reactions
|
||||||
|
.filter { it.isHighlighted }
|
||||||
|
.map { it.key }
|
||||||
|
.toPersistentList()
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:5ce2edea622636c3709d7da3d01c931a2ced65d31738d9cfc9e5f318a374ca30
|
oid sha256:5ccab2f54c80d52438b71a8afee8eb2fbd755b44cf3912c7b8a6b4cce77ee2a1
|
||||||
size 37850
|
size 39327
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:aa58a3f5fa6ee6e9a7f89f87a64b7b43577fca75cd44193b521875c8d6a184e3
|
oid sha256:a03f5e42a98c9664175d7630923b89ed9f657745e457a1dd3061693e523992dd
|
||||||
size 43823
|
size 45340
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:50cbc5955836725e0f58f2058e2c43cce0639c122036470fac58b89f3208c7f2
|
oid sha256:de5fc56ef0c5a093895cfff39972f4f472207fe39c36458c29789f34ef875f60
|
||||||
size 44139
|
size 45865
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:e6787391e332dcf4dd0998c18447962a1ee03314e276f6b7d0a6bd6717f30f43
|
oid sha256:2c6a00bfbe9b860736900fad85d5727e06ef2589c0ac15b8f5dc47a68685fef8
|
||||||
size 38214
|
size 39719
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:a90e03f54c92c91388b3218942e633ced908fd3b77defbeefc492d5c97de97f5
|
oid sha256:8918fa3ef7b1e73d7f9c85ae279016693c36e83271ca46f808ae6beee862d30e
|
||||||
size 39371
|
size 41053
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:12f2f8146898375b4556dfd0718b02b18ac99859efe7e857914b2cf424ca17ba
|
oid sha256:c385584daf063f769882c424507a526e33c876bddd6c76126c4c7eeb55ecd370
|
||||||
size 25792
|
size 25797
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:acaa3c96e40cd2d2d0cace906e87a6e36801a0836b1eff059a2f0a031e41ddd8
|
oid sha256:b683ddb79ad09ef856af89d616499790af0283baa50e93150ff96ace0eac5ab3
|
||||||
size 38770
|
size 40309
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:b0ac4827780ad236721b793b1d07a3f7ee514811ca3d8f29c8d447cdb99e739e
|
oid sha256:9ddaa5bdfaddc2cb86d40782660d5617192b1826ff886e24c1c5aee92a3b443b
|
||||||
size 45293
|
size 46995
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:57f7684c300496d7c8ca31e500b2191b85e3bc2ecbce489e77f1c71edec3d877
|
oid sha256:4ac90b67d42bd6bedf9c4b08ae86545b0908c5663503ac8d8b8ee71d6d373a12
|
||||||
size 45790
|
size 47432
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:fb973d123139c6da77e5b764cbca51866310ea81caf7057c584064677d2deff8
|
oid sha256:ee3c88ad82154b1e8b85dd9bed76944dd5fdddec9df0f6f2d077c64360933560
|
||||||
size 39233
|
size 40865
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:884654e20bd77864e1f7b7f137a539f8111ea50b8dd9c17ab689e246c70b2ead
|
oid sha256:fef9feae283ba58d9819428d8e980dc56d40eb34ecd2b49070db18b60f5bd72c
|
||||||
size 40705
|
size 42138
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
version https://git-lfs.github.com/spec/v1
|
||||||
oid sha256:2822e203b85195a5584fa0d3e0be2b260ff233e725ae104f237c46978f6ae068
|
oid sha256:23901280b9a5f60481cf368f5fb6b122cf21da2ffa4a73367e27cdc3cf52e377
|
||||||
size 27337
|
size 27334
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue