[WIP] EMOJI REACTION PICKER!!

Honestly I don't know why most of the things here work.

It's mostly done, but it's missing a few key things I'd like to have, but
either don't know how to do it or just didn't finish it yet.

- add the button functionality to more views (i've only added it to
  the timeline - thread, search, conversations and whatsonot don't react
  to it)
- add unicode emojis to tabs (because pleroma doesn't support custom
  emojis reactions lulz)
- set first emoji as a group name
- etc
This commit is contained in:
fruye 2022-12-04 14:44:59 +01:00
parent 09dce64a78
commit 93bef32825
7 changed files with 90 additions and 12 deletions

View File

@ -95,6 +95,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
private final SparkButton reblogButton;
private final SparkButton favouriteButton;
private final SparkButton bookmarkButton;
private final ImageButton reactButton;
private final ImageButton moreButton;
private final ConstraintLayout mediaContainer;
protected final MediaPreviewLayout mediaPreview;
@ -151,6 +152,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
reblogButton = itemView.findViewById(R.id.status_inset);
favouriteButton = itemView.findViewById(R.id.status_favourite);
bookmarkButton = itemView.findViewById(R.id.status_bookmark);
reactButton = itemView.findViewById(R.id.status_react);
moreButton = itemView.findViewById(R.id.status_more);
emojiReactionsView = itemView.findViewById(R.id.status_emoji_reactions);
quoteView = itemView.findViewById(R.id.status_quote_view);
@ -199,7 +201,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
mediaPreviewUnloaded = new ColorDrawable(MaterialColors.getColor(itemView, R.attr.colorBackgroundAccent));
TouchDelegateHelper.expandTouchSizeToFillRow((ViewGroup) itemView, CollectionsKt.listOfNotNull(replyButton, reblogButton, favouriteButton, bookmarkButton, moreButton));
TouchDelegateHelper.expandTouchSizeToFillRow((ViewGroup) itemView, CollectionsKt.listOfNotNull(replyButton, reblogButton, favouriteButton, bookmarkButton, reactButton, moreButton));
}
protected void setDisplayName(String name, List<Emoji> customEmojis, StatusDisplayOptions statusDisplayOptions) {
@ -732,6 +734,15 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
return true;
});
if (reactButton != null) {
reactButton.setOnClickListener(v -> {
int position = getBindingAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
listener.openReactionPicker(v, position);
}
});
}
moreButton.setOnClickListener(v -> {
int position = getBindingAdapterPosition();
if (position != RecyclerView.NO_POSITION) {

View File

@ -24,6 +24,7 @@ import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
import android.view.accessibility.AccessibilityManager
import android.widget.PopupWindow
import androidx.core.content.ContextCompat
import androidx.core.view.MenuProvider
import androidx.lifecycle.Lifecycle
@ -31,16 +32,14 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.paging.LoadState
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator
import androidx.recyclerview.widget.*
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import at.connyduck.sparkbutton.helpers.Utils
import autodispose2.androidx.lifecycle.autoDispose
import com.google.android.material.color.MaterialColors
import com.keylesspalace.tusky.BaseActivity
import com.keylesspalace.tusky.R
import com.keylesspalace.tusky.adapter.OnEmojiSelectedListener
import com.keylesspalace.tusky.adapter.StatusBaseViewHolder
import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent
@ -70,6 +69,7 @@ import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show
import com.keylesspalace.tusky.util.unsafeLazy
import com.keylesspalace.tusky.util.viewBinding
import com.keylesspalace.tusky.view.EmojiPicker
import com.keylesspalace.tusky.viewdata.AttachmentViewData
import com.keylesspalace.tusky.viewdata.StatusViewData
import com.mikepenz.iconics.IconicsDrawable
@ -90,7 +90,8 @@ class TimelineFragment :
Injectable,
ReselectableFragment,
RefreshableFragment,
MenuProvider {
MenuProvider,
OnEmojiSelectedListener {
@Inject
lateinit var viewModelFactory: ViewModelFactory
@ -147,6 +148,25 @@ class TimelineFragment :
/** The user's preferred reading order */
private lateinit var readingOrder: ReadingOrder
private lateinit var emojiView : View
private val emojiPicker : EmojiPicker by lazy {
emojiView = layoutInflater.inflate(R.layout.emoji_picker, null)
EmojiPicker(emojiView)
}
private val pickerDialog : PopupWindow by lazy {
PopupWindow(requireContext())
.apply {
contentView = emojiView
isFocusable = true
setOnDismissListener {
pickerCurrentStatusId = null
}
}
}
private var pickerCurrentStatusId : String? = null
private lateinit var statusDisplayOptions : StatusDisplayOptions
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -178,7 +198,7 @@ class TimelineFragment :
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
readingOrder = ReadingOrder.from(preferences.getString(PrefKeys.READING_ORDER, null))
val statusDisplayOptions = StatusDisplayOptions(
statusDisplayOptions = StatusDisplayOptions(
animateAvatars = preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false),
mediaPreviewEnabled = accountManager.activeAccount!!.mediaPreviewEnabled,
useAbsoluteTime = preferences.getBoolean(PrefKeys.ABSOLUTE_TIME_VIEW, false),
@ -277,6 +297,12 @@ class TimelineFragment :
}
}
viewLifecycleOwner.lifecycleScope.launch {
viewModel.emoji.collectLatest {
emojiPicker.populateEmojis(it, this@TimelineFragment, statusDisplayOptions.animateEmojis)
}
}
if (actionButtonPresent()) {
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
hideFab = preferences.getBoolean("fabHide", false)
@ -460,6 +486,17 @@ class TimelineFragment :
(activity as BaseActivity).startActivityWithSlideInAnimation(intent)
}
override fun openReactionPicker(target: View, position: Int) {
val statusId = adapter.peek(position)?.asStatusOrNull()?.id ?: return
pickerCurrentStatusId = statusId
pickerDialog.showAsDropDown(target)
}
override fun onEmojiSelected(shortcode: String) {
onEmojiReact(pickerCurrentStatusId!!, shortcode, true)
pickerDialog.dismiss()
}
override fun onShowFavs(position: Int) {
val statusId = adapter.peek(position)?.asStatusOrNull()?.id ?: return
val intent = newIntent(requireContext(), AccountListActivity.Type.FAVOURITED, statusId)

View File

@ -438,7 +438,7 @@ abstract class SFragment : Fragment(), Injectable {
.show()
}
open fun onEmojiReact(id: String, emoji: String, react: Boolean) {
fun onEmojiReact(id: String, emoji: String, react: Boolean) {
lifecycleScope.launch {
val result = timelineCases.react(id, emoji, react).exceptionOrNull()
if (result != null) {

View File

@ -72,4 +72,6 @@ public interface StatusActionListener extends LinkListener {
void clearWarningAction(int position);
default void onEmojiReactMenu(@NonNull View view, @NonNull final EmojiReaction emoji, String statusId) {}
default void openReactionPicker(View target, int position) {}
}

View File

@ -410,7 +410,7 @@
android:contentDescription="@string/action_favourite"
android:importantForAccessibility="no"
android:padding="4dp"
app:layout_constraintEnd_toStartOf="@id/status_bookmark"
app:layout_constraintEnd_toStartOf="@id/status_react"
app:layout_constraintStart_toEndOf="@id/status_inset"
app:layout_constraintTop_toTopOf="@id/status_inset"
sparkbutton:activeImage="@drawable/ic_favourite_active_24dp"
@ -430,6 +430,20 @@
app:layout_constraintTop_toTopOf="@id/status_inset"
tools:text="" />
<ImageButton
android:id="@+id/status_react"
style="@style/TuskyImageButton"
android:layout_width="52dp"
android:layout_height="48dp"
android:contentDescription="@string/action_react"
android:importantForAccessibility="no"
android:padding="4dp"
app:layout_constraintBottom_toBottomOf="@id/status_reply"
app:layout_constraintEnd_toStartOf="@id/status_bookmark"
app:layout_constraintStart_toEndOf="@id/status_favourite"
app:layout_constraintTop_toTopOf="@id/status_reply"
app:srcCompat="@drawable/ic_emoji_24dp" />
<at.connyduck.sparkbutton.SparkButton
android:id="@+id/status_bookmark"
android:layout_width="52dp"
@ -439,7 +453,7 @@
android:importantForAccessibility="no"
android:padding="4dp"
app:layout_constraintEnd_toStartOf="@id/status_more"
app:layout_constraintStart_toEndOf="@id/status_favourite"
app:layout_constraintStart_toEndOf="@id/status_react"
app:layout_constraintTop_toTopOf="@id/status_reply"
sparkbutton:activeImage="@drawable/ic_bookmark_active_24dp"
sparkbutton:iconSize="28dp"

View File

@ -425,7 +425,7 @@
android:contentDescription="@string/action_favourite"
android:importantForAccessibility="no"
android:padding="4dp"
app:layout_constraintEnd_toStartOf="@id/status_bookmark"
app:layout_constraintEnd_toStartOf="@id/status_react"
app:layout_constraintStart_toEndOf="@id/status_inset"
app:layout_constraintTop_toTopOf="@id/status_inset"
sparkbutton:activeImage="@drawable/ic_favourite_active_24dp"
@ -434,6 +434,19 @@
sparkbutton:primaryColor="@color/tusky_orange"
sparkbutton:secondaryColor="@color/tusky_orange_light" />
<ImageButton
android:id="@+id/status_react"
style="@style/TuskyImageButton"
android:layout_width="52dp"
android:layout_height="48dp"
android:contentDescription="@string/action_react"
android:importantForAccessibility="no"
android:padding="4dp"
app:layout_constraintEnd_toStartOf="@id/status_bookmark"
app:layout_constraintStart_toEndOf="@id/status_favourite"
app:layout_constraintTop_toTopOf="@id/status_favourite"
app:srcCompat="@drawable/ic_emoji_24dp" />
<at.connyduck.sparkbutton.SparkButton
android:id="@+id/status_bookmark"
android:layout_width="52dp"
@ -443,7 +456,7 @@
android:importantForAccessibility="no"
android:padding="4dp"
app:layout_constraintEnd_toStartOf="@id/status_more"
app:layout_constraintStart_toEndOf="@id/status_favourite"
app:layout_constraintStart_toEndOf="@id/status_react"
app:layout_constraintTop_toTopOf="@id/status_reply"
sparkbutton:activeImage="@drawable/ic_bookmark_active_24dp"
sparkbutton:iconSize="28dp"

View File

@ -13,6 +13,7 @@
<string name="admin">Admin</string>
<string name="moderator">Moderator</string>
<string name="action_react">React</string>
<string name="action_emoji_react">Add reaction</string>
<string name="action_emoji_unreact">Remove reaction</string>
<string name="action_emoji_reacted_by">Who reacted</string>