Merge pull request #5204 from vector-im/feature/fga/reactions_ui_improvements

Feature/fga/reactions UI improvements
This commit is contained in:
ganfra 2022-02-11 15:17:44 +01:00 committed by GitHub
commit f1376eac82
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 209 additions and 51 deletions

1
changelog.d/5204.feature Normal file
View file

@ -0,0 +1 @@
Improve UI of reactions in timeline, including quick add reaction.

View file

@ -23,4 +23,15 @@
<item name="android:backgroundTint">?vctr_content_quinary</item>
</style>
<style name="TimelineReactionView">
<item name="android:paddingStart">6dp</item>
<item name="android:paddingEnd">6dp</item>
<item name="android:paddingTop">1dp</item>
<item name="android:paddingBottom">1dp</item>
<item name="android:minHeight">28dp</item>
<item name="android:minWidth">40dp</item>
<item name="android:gravity">center</item>
</style>
</resources>

View file

@ -66,11 +66,11 @@ internal class UIEchoManager(private val listener: Listener) {
return existingState != sendState
}
fun onLocalEchoCreated(timelineEvent: TimelineEvent): Boolean {
fun onLocalEchoCreated(timelineEvent: TimelineEvent): Boolean {
when (timelineEvent.root.getClearType()) {
EventType.REDACTION -> {
}
EventType.REACTION -> {
EventType.REACTION -> {
val content: ReactionContent? = timelineEvent.root.content?.toModel<ReactionContent>()
if (RelationType.ANNOTATION == content?.relatesTo?.type) {
val reaction = content.relatesTo.key
@ -104,8 +104,8 @@ internal class UIEchoManager(private val listener: Listener) {
val updateReactions = existingAnnotationSummary.reactionsSummary.toMutableList()
contents.forEach { uiEchoReaction ->
val existing = updateReactions.firstOrNull { it.key == uiEchoReaction.reaction }
if (existing == null) {
val indexOfExistingReaction = updateReactions.indexOfFirst { it.key == uiEchoReaction.reaction }
if (indexOfExistingReaction == -1) {
// just add the new key
ReactionAggregatedSummary(
key = uiEchoReaction.reaction,
@ -117,6 +117,7 @@ internal class UIEchoManager(private val listener: Listener) {
).let { updateReactions.add(it) }
} else {
// update Existing Key
val existing = updateReactions[indexOfExistingReaction]
if (!existing.localEchoEvents.contains(uiEchoReaction.localEchoId)) {
updateReactions.remove(existing)
// only update if echo is not yet there
@ -128,7 +129,7 @@ internal class UIEchoManager(private val listener: Listener) {
sourceEvents = existing.sourceEvents,
localEchoEvents = existing.localEchoEvents + uiEchoReaction.localEchoId
).let { updateReactions.add(it) }
).let { updateReactions.add(indexOfExistingReaction, it) }
}
}
}

View file

@ -18,6 +18,9 @@ package im.vector.app.core.extensions
import android.content.Context
import android.graphics.drawable.Drawable
import android.text.Spannable
import android.text.SpannableString
import android.text.style.ImageSpan
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
@ -34,6 +37,16 @@ fun Context.singletonEntryPoint(): SingletonEntryPoint {
return EntryPoints.get(applicationContext, SingletonEntryPoint::class.java)
}
fun Context.getDrawableAsSpannable(@DrawableRes drawableRes: Int, alignment: Int = ImageSpan.ALIGN_BOTTOM): Spannable {
return SpannableString(" ").apply {
val span = ContextCompat.getDrawable(this@getDrawableAsSpannable, drawableRes)?.let {
it.setBounds(0, 0, it.intrinsicWidth, it.intrinsicHeight)
ImageSpan(it, alignment)
}
setSpan(span, 0, 1, Spannable.SPAN_INCLUSIVE_EXCLUSIVE)
}
}
fun Context.getResTintedDrawable(@DrawableRes drawableRes: Int, @ColorRes tint: Int, @FloatRange(from = 0.0, to = 1.0) alpha: Float = 1f): Drawable? {
return getTintedDrawable(drawableRes, ContextCompat.getColor(this, tint), alpha)
}

View file

@ -1915,6 +1915,10 @@ class TimelineFragment @Inject constructor(
timelineViewModel.handle(RoomDetailAction.LoadMoreTimelineEvents(direction))
}
override fun onAddMoreReaction(event: TimelineEvent) {
openEmojiReactionPicker(event.eventId)
}
override fun onEventCellClicked(informationData: MessageInformationData, messageContent: Any?, view: View, isRootThreadEvent: Boolean) {
when (messageContent) {
is MessageVerificationRequestContent -> {
@ -2119,7 +2123,7 @@ class TimelineFragment @Inject constructor(
openRoomMemberProfile(action.userId)
}
is EventSharedAction.AddReaction -> {
emojiActivityResultLauncher.launch(EmojiReactionPickerActivity.intent(requireContext(), action.eventId))
openEmojiReactionPicker(action.eventId)
}
is EventSharedAction.ViewReactions -> {
ViewReactionsBottomSheet.newInstance(timelineArgs.roomId, action.messageInformationData)
@ -2241,6 +2245,10 @@ class TimelineFragment @Inject constructor(
}
}
private fun openEmojiReactionPicker(eventId: String) {
emojiActivityResultLauncher.launch(EmojiReactionPickerActivity.intent(requireContext(), eventId))
}
private fun askConfirmationToEndPoll(eventId: String) {
MaterialAlertDialogBuilder(requireContext(), R.style.ThemeOverlay_Vector_MaterialAlertDialog)
.setTitle(R.string.end_poll_confirmation_title)

View file

@ -41,6 +41,7 @@ import im.vector.app.features.home.room.detail.timeline.factory.TimelineItemFact
import im.vector.app.features.home.room.detail.timeline.factory.TimelineItemFactoryParams
import im.vector.app.features.home.room.detail.timeline.helper.ContentDownloadStateTrackerBinder
import im.vector.app.features.home.room.detail.timeline.helper.ContentUploadStateTrackerBinder
import im.vector.app.features.home.room.detail.timeline.helper.ReactionsSummaryFactory
import im.vector.app.features.home.room.detail.timeline.helper.TimelineControllerInterceptorHelper
import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventDiffUtilCallback
import im.vector.app.features.home.room.detail.timeline.helper.TimelineEventVisibilityHelper
@ -86,7 +87,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
@TimelineEventControllerHandler
private val backgroundHandler: Handler,
private val timelineEventVisibilityHelper: TimelineEventVisibilityHelper,
private val readReceiptsItemFactory: ReadReceiptsItemFactory
private val readReceiptsItemFactory: ReadReceiptsItemFactory,
private val reactionListFactory: ReactionsSummaryFactory
) : EpoxyController(backgroundHandler, backgroundHandler), Timeline.Listener, EpoxyController.Interceptor {
/**
@ -138,6 +140,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
fun getPreviewUrlRetriever(): PreviewUrlRetriever
fun onVoiceControlButtonClicked(eventId: String, messageAudioContent: MessageAudioContent)
fun onAddMoreReaction(event: TimelineEvent)
}
interface ReactionPillCallback {
@ -283,6 +287,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
super.onAttachedToRecyclerView(recyclerView)
timeline?.addListener(this)
timelineMediaSizeProvider.recyclerView = recyclerView
reactionListFactory.onRequestBuild = { requestModelBuild() }
}
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
@ -290,6 +295,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
contentUploadStateTrackerBinder.clear()
contentDownloadStateTrackerBinder.clear()
timeline?.removeListener(this)
reactionListFactory.onRequestBuild = null
super.onDetachedFromRecyclerView(recyclerView)
}
@ -383,7 +389,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
val event = currentSnapshot[position]
val nextEvent = currentSnapshot.nextOrNull(position)
// Should be build if not cached or if model should be refreshed
if (modelCache[position] == null || modelCache[position]?.isCacheable(partialState) == false) {
if (modelCache[position] == null || modelCache[position]?.isCacheable(partialState) == false || reactionListFactory.needsRebuild(event)) {
val prevEvent = currentSnapshot.prevOrNull(position)
val prevDisplayableEvent = currentSnapshot.subList(0, position).lastOrNull {
timelineEventVisibilityHelper.shouldShowEvent(

View file

@ -24,7 +24,6 @@ import im.vector.app.features.home.room.detail.timeline.item.E2EDecoration
import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData
import im.vector.app.features.home.room.detail.timeline.item.PollResponseData
import im.vector.app.features.home.room.detail.timeline.item.PollVoteSummaryData
import im.vector.app.features.home.room.detail.timeline.item.ReactionInfoData
import im.vector.app.features.home.room.detail.timeline.item.ReferencesInfoData
import im.vector.app.features.home.room.detail.timeline.item.SendStateDecoration
import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayoutFactory
@ -50,7 +49,8 @@ import javax.inject.Inject
*/
class MessageInformationDataFactory @Inject constructor(private val session: Session,
private val dateFormatter: VectorDateFormatter,
private val messageLayoutFactory: TimelineMessageLayoutFactory) {
private val messageLayoutFactory: TimelineMessageLayoutFactory,
private val reactionsSummaryFactory: ReactionsSummaryFactory) {
fun create(params: TimelineItemFactoryParams): MessageInformationData {
val event = params.event
@ -93,11 +93,7 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
avatarUrl = event.senderInfo.avatarUrl,
memberName = event.senderInfo.disambiguatedDisplayName,
messageLayout = messageLayout,
orderedReactionList = event.annotations?.reactionsSummary
// ?.filter { isSingleEmoji(it.key) }
?.map {
ReactionInfoData(it.key, it.count, it.addedByMe, it.localEchoEvents.isEmpty())
},
reactionsSummary = reactionsSummaryFactory.create(event, params.callback),
pollResponseAggregatedSummary = event.annotations?.pollResponseSummary?.let {
PollResponseData(
myVote = it.aggregatedContent?.myVote,

View file

@ -0,0 +1,65 @@
/*
* Copyright (c) 2021 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 im.vector.app.features.home.room.detail.timeline.helper
import dagger.hilt.android.scopes.ActivityScoped
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
import im.vector.app.features.home.room.detail.timeline.item.ReactionInfoData
import im.vector.app.features.home.room.detail.timeline.item.ReactionsSummaryData
import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent
import javax.inject.Inject
@ActivityScoped
class ReactionsSummaryFactory @Inject constructor() {
var onRequestBuild: (() -> Unit)? = null
private val showAllReactionsByEvent = HashSet<String>()
private val eventsRequestingBuild = HashSet<String>()
fun needsRebuild(event: TimelineEvent): Boolean {
return eventsRequestingBuild.remove(event.eventId)
}
fun create(event: TimelineEvent, callback: TimelineEventController.Callback?): ReactionsSummaryData {
val eventId = event.eventId
val showAllStates = showAllReactionsByEvent.contains(eventId)
val reactions = event.annotations?.reactionsSummary
?.map {
ReactionInfoData(it.key, it.count, it.addedByMe, it.localEchoEvents.isEmpty())
}
return ReactionsSummaryData(
reactions = reactions,
showAll = showAllStates,
onShowMoreClicked = {
showAllReactionsByEvent.add(eventId)
onRequestBuild(eventId)
},
onShowLessClicked = {
showAllReactionsByEvent.remove(eventId)
onRequestBuild(eventId)
},
onAddMoreClicked = {
callback?.onAddMoreReaction(event)
}
)
}
private fun onRequestBuild(eventId: String) {
eventsRequestingBuild.add(eventId)
onRequestBuild?.invoke()
}
}

View file

@ -16,24 +16,34 @@
package im.vector.app.features.home.room.detail.timeline.item
import android.annotation.SuppressLint
import android.graphics.Typeface
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.IdRes
import androidx.appcompat.view.ContextThemeWrapper
import androidx.core.content.ContextCompat.getDrawable
import androidx.core.view.isVisible
import androidx.core.widget.TextViewCompat
import im.vector.app.R
import im.vector.app.core.epoxy.ClickListener
import im.vector.app.core.epoxy.onClick
import im.vector.app.core.extensions.getDrawableAsSpannable
import im.vector.app.core.ui.views.ShieldImageView
import im.vector.app.core.utils.DimensionConverter
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.timeline.MessageColorProvider
import im.vector.app.features.home.room.detail.timeline.TimelineEventController
import im.vector.app.features.home.room.detail.timeline.view.TimelineMessageLayoutRenderer
import im.vector.app.features.reactions.widget.ReactionButton
import im.vector.app.features.themes.ThemeUtils
import org.matrix.android.sdk.api.crypto.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.session.room.send.SendState
private const val MAX_REACTIONS_TO_SHOW = 8
/**
* Base timeline item with reactions and read receipts.
* Manages associated click listeners and send status.
@ -65,27 +75,10 @@ abstract class AbsBaseMessageItem<H : AbsBaseMessageItem.Holder> : BaseEventItem
return listOf(baseAttributes.informationData.eventId)
}
@SuppressLint("SetTextI18n")
override fun bind(holder: H) {
super.bind(holder)
val reactions = baseAttributes.informationData.orderedReactionList
if (!shouldShowReactionAtBottom() || reactions.isNullOrEmpty()) {
holder.reactionsContainer.isVisible = false
} else {
holder.reactionsContainer.isVisible = true
holder.reactionsContainer.removeAllViews()
reactions.take(8).forEach { reaction ->
val reactionButton = ReactionButton(holder.view.context)
reactionButton.reactedListener = reactionClickListener
reactionButton.setTag(R.id.reactionsContainer, reaction.key)
reactionButton.reactionString = reaction.key
reactionButton.reactionCount = reaction.count
reactionButton.setChecked(reaction.addedByMe)
reactionButton.isEnabled = reaction.synced
holder.reactionsContainer.addView(reactionButton)
}
holder.reactionsContainer.setOnLongClickListener(baseAttributes.itemLongClickListener)
}
renderReactions(holder, baseAttributes.informationData.reactionsSummary)
when (baseAttributes.informationData.e2eDecoration) {
E2EDecoration.NONE -> {
holder.e2EDecorationView.render(null)
@ -102,6 +95,58 @@ abstract class AbsBaseMessageItem<H : AbsBaseMessageItem.Holder> : BaseEventItem
(holder.view as? TimelineMessageLayoutRenderer)?.renderMessageLayout(baseAttributes.informationData.messageLayout)
}
private fun renderReactions(holder: H, reactionsSummary: ReactionsSummaryData) {
val reactions = reactionsSummary.reactions
if (!shouldShowReactionAtBottom() || reactions.isNullOrEmpty()) {
holder.reactionsContainer.isVisible = false
} else {
holder.reactionsContainer.isVisible = true
holder.reactionsContainer.removeAllViews()
val reactionsToShow = if (reactionsSummary.showAll) {
reactions
} else {
reactions.take(MAX_REACTIONS_TO_SHOW)
}
reactionsToShow.forEach { reaction ->
val reactionButton = ReactionButton(holder.view.context)
reactionButton.reactedListener = reactionClickListener
reactionButton.setTag(R.id.reactionsContainer, reaction.key)
reactionButton.reactionString = reaction.key
reactionButton.reactionCount = reaction.count
reactionButton.setChecked(reaction.addedByMe)
reactionButton.isEnabled = reaction.synced
holder.reactionsContainer.addView(reactionButton)
}
if (reactions.count() > MAX_REACTIONS_TO_SHOW) {
val showReactionsTextView = createReactionTextView(holder)
if (reactionsSummary.showAll) {
showReactionsTextView.setText(R.string.message_reaction_show_less)
showReactionsTextView.onClick { reactionsSummary.onShowLessClicked() }
} else {
val moreCount = reactions.count() - MAX_REACTIONS_TO_SHOW
showReactionsTextView.text = holder.view.resources.getString(R.string.message_reaction_show_more, moreCount)
showReactionsTextView.onClick { reactionsSummary.onShowMoreClicked() }
}
holder.reactionsContainer.addView(showReactionsTextView)
}
val addMoreReactionsTextView = createReactionTextView(holder)
addMoreReactionsTextView.text = holder.view.context.getDrawableAsSpannable(R.drawable.ic_add_reaction_small)
addMoreReactionsTextView.onClick { reactionsSummary.onAddMoreClicked() }
holder.reactionsContainer.addView(addMoreReactionsTextView)
holder.reactionsContainer.setOnLongClickListener(baseAttributes.itemLongClickListener)
}
}
private fun createReactionTextView(holder: H): TextView {
return TextView(ContextThemeWrapper(holder.view.context, R.style.TimelineReactionView)).apply {
background = getDrawable(context, R.drawable.reaction_rounded_rect_shape_off)
TextViewCompat.setTextAppearance(this, R.style.TextAppearance_Vector_Micro)
setTypeface(typeface, Typeface.BOLD)
setTextColor(ThemeUtils.getColor(context, R.attr.vctr_content_secondary))
}
}
override fun unbind(holder: H) {
holder.reactionsContainer.setOnLongClickListener(null)
super.unbind(holder)
@ -115,6 +160,9 @@ abstract class AbsBaseMessageItem<H : AbsBaseMessageItem.Holder> : BaseEventItem
}
abstract class Holder(@IdRes stubId: Int) : BaseEventItem.BaseHolder(stubId) {
val dimensionConverter by lazy {
DimensionConverter(view.resources)
}
val reactionsContainer by bind<ViewGroup>(R.id.reactionsContainer)
val e2EDecorationView by bind<ShieldImageView>(R.id.messageE2EDecoration)
}

View file

@ -33,8 +33,7 @@ data class MessageInformationData(
val avatarUrl: String?,
val memberName: CharSequence? = null,
val messageLayout: TimelineMessageLayout,
/*List of reactions (emoji,count,isSelected)*/
val orderedReactionList: List<ReactionInfoData>? = null,
val reactionsSummary: ReactionsSummaryData,
val pollResponseAggregatedSummary: PollResponseData? = null,
val hasBeenEdited: Boolean = false,
val hasPendingEdits: Boolean = false,
@ -55,6 +54,16 @@ data class ReferencesInfoData(
val verificationStatus: VerificationState
) : Parcelable
@Parcelize
data class ReactionsSummaryData(
/*List of reactions (emoji,count,isSelected)*/
val reactions: List<ReactionInfoData>? = null,
val showAll: Boolean = false,
val onShowMoreClicked: () -> Unit,
val onShowLessClicked: () -> Unit,
val onAddMoreClicked: () -> Unit
) : Parcelable
@Parcelize
data class ReactionInfoData(
val key: String,

View file

@ -18,7 +18,6 @@ package im.vector.app.features.reactions.widget
import android.content.Context
import android.graphics.drawable.Drawable
import android.util.AttributeSet
import android.view.Gravity
import android.view.View
import android.widget.LinearLayout
import androidx.core.content.ContextCompat
@ -26,7 +25,6 @@ import androidx.core.content.withStyledAttributes
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.EmojiSpanify
import im.vector.app.R
import im.vector.app.core.utils.DimensionConverter
import im.vector.app.core.utils.TextUtils
import im.vector.app.databinding.ReactionButtonBinding
import javax.inject.Inject
@ -38,8 +36,9 @@ import javax.inject.Inject
@AndroidEntryPoint
class ReactionButton @JvmOverloads constructor(context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0) :
LinearLayout(context, attrs, defStyleAttr), View.OnClickListener, View.OnLongClickListener {
defStyleAttr: Int = 0,
defStyleRes: Int = R.style.TimelineReactionView) :
LinearLayout(context, attrs, defStyleAttr, defStyleRes), View.OnClickListener, View.OnLongClickListener {
@Inject lateinit var emojiSpanify: EmojiSpanify
@ -68,8 +67,6 @@ class ReactionButton @JvmOverloads constructor(context: Context,
init {
inflate(context, R.layout.reaction_button, this)
orientation = HORIZONTAL
minimumHeight = DimensionConverter(context.resources).dpToPx(30)
gravity = Gravity.CENTER
layoutDirection = View.LAYOUT_DIRECTION_LOCALE
views = ReactionButtonBinding.bind(this)
views.reactionCount.text = TextUtils.formatCountToShortDecimal(reactionCount)

View file

@ -0,0 +1,4 @@
<vector android:height="14dp" android:viewportHeight="16"
android:viewportWidth="16" android:width="14dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#737D8C" android:fillType="evenOdd" android:pathData="M13.3334,0.667C12.9652,0.667 12.6667,0.9655 12.6667,1.3337V2.667L11.3334,2.667C10.9652,2.667 10.6667,2.9655 10.6667,3.3337C10.6667,3.7018 10.9652,4.0003 11.3334,4.0003H12.6667V5.3337C12.6667,5.7018 12.9652,6.0003 13.3334,6.0003C13.7016,6.0003 14,5.7018 14,5.3337V4.0003H15.3334C15.7016,4.0003 16,3.7018 16,3.3337C16,2.9655 15.7016,2.667 15.3334,2.667L14,2.667V1.3337C14,0.9655 13.7016,0.667 13.3334,0.667ZM4.6667,6.3337C4.6667,5.7803 5.1134,5.3337 5.6667,5.3337C6.22,5.3337 6.6667,5.7803 6.6667,6.3337C6.6667,6.887 6.22,7.3337 5.6667,7.3337C5.1134,7.3337 4.6667,6.887 4.6667,6.3337ZM10.3334,7.3337C10.8867,7.3337 11.3334,6.887 11.3334,6.3337C11.3334,5.7803 10.8867,5.3337 10.3334,5.3337C9.78,5.3337 9.3334,5.7803 9.3334,6.3337C9.3334,6.887 9.78,7.3337 10.3334,7.3337ZM8,11.667C9.5534,11.667 10.8734,10.6937 11.4067,9.3337H4.5934C5.1267,10.6937 6.4467,11.667 8,11.667ZM2.6667,8.0003C2.6667,5.0548 5.0545,2.667 8,2.667C8.4073,2.667 8.803,2.7125 9.1828,2.7985C9.542,2.8797 9.8989,2.6545 9.9802,2.2954C10.0615,1.9363 9.8362,1.5793 9.4771,1.498C9.0014,1.3903 8.5069,1.3337 8,1.3337C4.3181,1.3337 1.3334,4.3184 1.3334,8.0003C1.3334,11.6822 4.3181,14.667 8,14.667C11.6819,14.667 14.6667,11.6822 14.6667,8.0003C14.6667,7.8589 14.6623,7.7184 14.6536,7.579C14.6306,7.2115 14.3141,6.9322 13.9467,6.9552C13.5792,6.9781 13.2999,7.2946 13.3228,7.6621C13.3298,7.7738 13.3334,7.8866 13.3334,8.0003C13.3334,10.9458 10.9456,13.3337 8,13.3337C5.0545,13.3337 2.6667,10.9458 2.6667,8.0003Z"/>
</vector>

View file

@ -2,7 +2,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size
android:width="8dp"
android:height="8dp" />
android:width="4dp"
android:height="4dp" />
<solid android:color="#00000000" />
</shape>

View file

@ -3,11 +3,10 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="26dp"
android:layout_height="wrap_content"
android:background="@drawable/reaction_rounded_rect_shape"
android:clipChildren="false"
android:gravity="center"
android:minWidth="44dp"
tools:parentTag="android.widget.LinearLayout">
<!--<View-->
@ -20,12 +19,10 @@
android:id="@+id/reactionText"
style="@style/Widget.Vector.TextView.Caption"
android:layout_width="wrap_content"
android:layout_height="20dp"
android:layout_marginStart="6dp"
android:layout_height="wrap_content"
android:ellipsize="middle"
android:gravity="center"
android:maxEms="10"
android:minWidth="20dp"
android:singleLine="true"
android:textColor="@color/emoji_color"
tools:text="* Party Parrot Again * 👀" />
@ -36,7 +33,6 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="2dp"
android:layout_marginEnd="8dp"
android:gravity="center"
android:maxLines="1"
android:textColor="?vctr_content_secondary"

View file

@ -177,7 +177,7 @@
<com.google.android.flexbox.FlexboxLayout
android:id="@+id/reactionsContainer"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="4dp"
app:dividerDrawable="@drawable/reaction_divider"

View file

@ -3779,4 +3779,7 @@
<string name="tooltip_attachment_poll">Create poll</string>
<string name="tooltip_attachment_location">Share location</string>
<string name="message_reaction_show_less">Show less</string>
<string name="message_reaction_show_more">"%1$d more"</string>
</resources>