Display emoji reactions

Based partially on these commits by alba:
- e1eaea9d5a
- 97ffa14268
- 3320e6a0da
This commit is contained in:
fruye 2022-11-08 16:30:39 +01:00
parent 8126e70440
commit 893e804a6e
6 changed files with 60 additions and 5 deletions

View File

@ -34,6 +34,8 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestBuilder;
import com.google.android.material.button.MaterialButton;
import com.google.android.material.color.MaterialColors;
import com.google.android.material.chip.Chip;
import com.google.android.material.chip.ChipGroup;
import com.google.android.material.imageview.ShapeableImageView;
import com.google.android.material.shape.CornerFamily;
import com.google.android.material.shape.ShapeAppearanceModel;
@ -44,6 +46,7 @@ import com.keylesspalace.tusky.entity.Attachment.Focus;
import com.keylesspalace.tusky.entity.Attachment.MetaData;
import com.keylesspalace.tusky.entity.Card;
import com.keylesspalace.tusky.entity.Emoji;
import com.keylesspalace.tusky.entity.EmojiReaction;
import com.keylesspalace.tusky.entity.Filter;
import com.keylesspalace.tusky.entity.FilterResult;
import com.keylesspalace.tusky.entity.HashTag;
@ -53,6 +56,7 @@ import com.keylesspalace.tusky.util.AbsoluteTimeFormatter;
import com.keylesspalace.tusky.util.AttachmentHelper;
import com.keylesspalace.tusky.util.CardViewMode;
import com.keylesspalace.tusky.util.CustomEmojiHelper;
import com.keylesspalace.tusky.util.EmojiReactions;
import com.keylesspalace.tusky.util.ImageLoadingHelper;
import com.keylesspalace.tusky.util.LinkHelper;
import com.keylesspalace.tusky.util.NumberUtils;
@ -128,6 +132,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
private final Drawable mediaPreviewUnloaded;
private ChipGroup emojiReactionsView;
protected StatusBaseViewHolder(View itemView) {
super(itemView);
displayName = itemView.findViewById(R.id.status_display_name);
@ -141,6 +147,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
favouriteButton = itemView.findViewById(R.id.status_favourite);
bookmarkButton = itemView.findViewById(R.id.status_bookmark);
moreButton = itemView.findViewById(R.id.status_more);
emojiReactionsView = itemView.findViewById(R.id.status_emoji_reactions);
mediaContainer = itemView.findViewById(R.id.status_media_preview_container);
mediaContainer.setClipToOutline(true);
@ -750,6 +757,27 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
popup.show();
}
private void setEmojiReactions(@Nullable List<EmojiReaction> reactions, final StatusActionListener listener, final String statusId, final StatusDisplayOptions displayOptions) {
if (emojiReactionsView == null) {
return;
}
if (reactions == null || reactions.size() == 0) {
emojiReactionsView.setVisibility(View.GONE);
return;
}
emojiReactionsView.setVisibility(View.VISIBLE);
EmojiReactions.setupReactions(emojiReactionsView, reactions, (Chip chip, View view, EmojiReaction reaction) -> {
chip.setChecked(reaction.getMe());
return null;
}, displayOptions.animateEmojis());
while (emojiReactionsView.getChildCount() > reactions.size()) {
emojiReactionsView.removeViewAt(reactions.size());
}
}
public void setupWithStatus(StatusViewData.Concrete status, final StatusActionListener listener,
StatusDisplayOptions statusDisplayOptions) {
this.setupWithStatus(status, listener, statusDisplayOptions, null);
@ -802,6 +830,8 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
setDescriptionForStatus(status, statusDisplayOptions);
setEmojiReactions(status.getEmojiReactions(), listener, status.getId(), statusDisplayOptions);
// Workaround for RecyclerView 1.0.0 / androidx.core 1.0.0
// RecyclerView tries to set AccessibilityDelegateCompat to null
// but ViewCompat code replaces is with the default one. RecyclerView never

View File

@ -79,7 +79,7 @@ class AnnouncementAdapter(
// hide button if announcement badge limit is already reached
addReactionChip.visible(item.reactions.size < 8)
chips.setupReactions(item.reactions, fun(_: View, reaction: EmojiReaction) {
chips.setupReactions(item.reactions, fun(_: Chip, _: View, reaction: EmojiReaction) {
if (reaction.me) {
listener.removeReaction(item.id, reaction.name)
} else {

View File

@ -1,3 +1,4 @@
@file:JvmName("EmojiReactions")
package com.keylesspalace.tusky.util
import android.os.Build
@ -11,7 +12,7 @@ import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.entity.EmojiReaction
import java.lang.ref.WeakReference
fun ChipGroup.setupReactions(reactions: List<EmojiReaction>, clickListener: (view: View, reaction: EmojiReaction) -> Unit, animateEmojis: Boolean) {
fun ChipGroup.setupReactions(reactions: List<EmojiReaction>, clickListener: (chip: Chip, view: View, reaction: EmojiReaction) -> Unit, animateEmojis: Boolean) {
val chips = this
reactions.forEachIndexed { i, reaction ->
(
@ -44,7 +45,7 @@ fun ChipGroup.setupReactions(reactions: List<EmojiReaction>, clickListener: (vie
isChecked = reaction.me
setOnClickListener {
clickListener(it, reaction)
clickListener(this, it, reaction)
}
}
}

View File

@ -16,6 +16,7 @@ package com.keylesspalace.tusky.viewdata
import android.os.Build
import android.text.Spanned
import com.keylesspalace.tusky.entity.EmojiReaction
import com.keylesspalace.tusky.entity.Filter
import com.keylesspalace.tusky.entity.Status
import com.keylesspalace.tusky.util.parseAsMastodonHtml
@ -76,6 +77,9 @@ sealed class StatusViewData {
val rebloggingStatus: Status?
get() = if (status.reblog != null) status else null
val emojiReactions: List<EmojiReaction>?
get() = status.actionableStatus.pleroma?.emojiReactions
init {
if (Build.VERSION.SDK_INT == 23) {
// https://github.com/tuskyapp/Tusky/issues/563

View File

@ -310,6 +310,16 @@
app:layout_constraintTop_toBottomOf="@id/status_poll_button"
tools:text="7 votes • 7 hours remaining" />
<com.google.android.material.chip.ChipGroup
android:id="@+id/status_emoji_reactions"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="4dp"
android:visibility="gone"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_description"
app:layout_constraintEnd_toEndOf="parent" />
<ImageButton
android:id="@+id/status_reply"
style="@style/TuskyImageButton"
@ -322,7 +332,7 @@
app:layout_constraintEnd_toStartOf="@id/status_inset"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintStart_toStartOf="@id/status_display_name"
app:layout_constraintTop_toBottomOf="@id/status_poll_description"
app:layout_constraintTop_toBottomOf="@id/status_emoji_reactions"
app:srcCompat="@drawable/ic_reply_24dp" />
<TextView

View File

@ -265,6 +265,16 @@
app:layout_constraintTop_toBottomOf="@id/status_poll_button"
tools:text="7 votes • 7 hours remaining" />
<com.google.android.material.chip.ChipGroup
android:id="@+id/status_emoji_reactions"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_poll_description" />
<TextView
android:id="@+id/status_meta_info"
android:layout_width="0dp"
@ -279,7 +289,7 @@
android:textSize="?attr/status_text_medium"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/status_poll_description"
app:layout_constraintTop_toBottomOf="@id/status_emoji_reactions"
tools:text="21 Dec 2018 18:45" />
<View