[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:
parent
09dce64a78
commit
93bef32825
|
@ -95,6 +95,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
private final SparkButton reblogButton;
|
private final SparkButton reblogButton;
|
||||||
private final SparkButton favouriteButton;
|
private final SparkButton favouriteButton;
|
||||||
private final SparkButton bookmarkButton;
|
private final SparkButton bookmarkButton;
|
||||||
|
private final ImageButton reactButton;
|
||||||
private final ImageButton moreButton;
|
private final ImageButton moreButton;
|
||||||
private final ConstraintLayout mediaContainer;
|
private final ConstraintLayout mediaContainer;
|
||||||
protected final MediaPreviewLayout mediaPreview;
|
protected final MediaPreviewLayout mediaPreview;
|
||||||
|
@ -151,6 +152,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
reblogButton = itemView.findViewById(R.id.status_inset);
|
reblogButton = itemView.findViewById(R.id.status_inset);
|
||||||
favouriteButton = itemView.findViewById(R.id.status_favourite);
|
favouriteButton = itemView.findViewById(R.id.status_favourite);
|
||||||
bookmarkButton = itemView.findViewById(R.id.status_bookmark);
|
bookmarkButton = itemView.findViewById(R.id.status_bookmark);
|
||||||
|
reactButton = itemView.findViewById(R.id.status_react);
|
||||||
moreButton = itemView.findViewById(R.id.status_more);
|
moreButton = itemView.findViewById(R.id.status_more);
|
||||||
emojiReactionsView = itemView.findViewById(R.id.status_emoji_reactions);
|
emojiReactionsView = itemView.findViewById(R.id.status_emoji_reactions);
|
||||||
quoteView = itemView.findViewById(R.id.status_quote_view);
|
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));
|
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) {
|
protected void setDisplayName(String name, List<Emoji> customEmojis, StatusDisplayOptions statusDisplayOptions) {
|
||||||
|
@ -732,6 +734,15 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (reactButton != null) {
|
||||||
|
reactButton.setOnClickListener(v -> {
|
||||||
|
int position = getBindingAdapterPosition();
|
||||||
|
if (position != RecyclerView.NO_POSITION) {
|
||||||
|
listener.openReactionPicker(v, position);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
moreButton.setOnClickListener(v -> {
|
moreButton.setOnClickListener(v -> {
|
||||||
int position = getBindingAdapterPosition();
|
int position = getBindingAdapterPosition();
|
||||||
if (position != RecyclerView.NO_POSITION) {
|
if (position != RecyclerView.NO_POSITION) {
|
||||||
|
|
|
@ -24,6 +24,7 @@ import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.accessibility.AccessibilityManager
|
import android.view.accessibility.AccessibilityManager
|
||||||
|
import android.widget.PopupWindow
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.view.MenuProvider
|
import androidx.core.view.MenuProvider
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
|
@ -31,16 +32,14 @@ import androidx.lifecycle.ViewModelProvider
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.paging.LoadState
|
import androidx.paging.LoadState
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.recyclerview.widget.DividerItemDecoration
|
import androidx.recyclerview.widget.*
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import androidx.recyclerview.widget.SimpleItemAnimator
|
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
|
||||||
import at.connyduck.sparkbutton.helpers.Utils
|
import at.connyduck.sparkbutton.helpers.Utils
|
||||||
import autodispose2.androidx.lifecycle.autoDispose
|
import autodispose2.androidx.lifecycle.autoDispose
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import com.keylesspalace.tusky.BaseActivity
|
import com.keylesspalace.tusky.BaseActivity
|
||||||
import com.keylesspalace.tusky.R
|
import com.keylesspalace.tusky.R
|
||||||
|
import com.keylesspalace.tusky.adapter.OnEmojiSelectedListener
|
||||||
import com.keylesspalace.tusky.adapter.StatusBaseViewHolder
|
import com.keylesspalace.tusky.adapter.StatusBaseViewHolder
|
||||||
import com.keylesspalace.tusky.appstore.EventHub
|
import com.keylesspalace.tusky.appstore.EventHub
|
||||||
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent
|
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.show
|
||||||
import com.keylesspalace.tusky.util.unsafeLazy
|
import com.keylesspalace.tusky.util.unsafeLazy
|
||||||
import com.keylesspalace.tusky.util.viewBinding
|
import com.keylesspalace.tusky.util.viewBinding
|
||||||
|
import com.keylesspalace.tusky.view.EmojiPicker
|
||||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
||||||
import com.keylesspalace.tusky.viewdata.StatusViewData
|
import com.keylesspalace.tusky.viewdata.StatusViewData
|
||||||
import com.mikepenz.iconics.IconicsDrawable
|
import com.mikepenz.iconics.IconicsDrawable
|
||||||
|
@ -90,7 +90,8 @@ class TimelineFragment :
|
||||||
Injectable,
|
Injectable,
|
||||||
ReselectableFragment,
|
ReselectableFragment,
|
||||||
RefreshableFragment,
|
RefreshableFragment,
|
||||||
MenuProvider {
|
MenuProvider,
|
||||||
|
OnEmojiSelectedListener {
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
lateinit var viewModelFactory: ViewModelFactory
|
lateinit var viewModelFactory: ViewModelFactory
|
||||||
|
@ -147,6 +148,25 @@ class TimelineFragment :
|
||||||
/** The user's preferred reading order */
|
/** The user's preferred reading order */
|
||||||
private lateinit var readingOrder: ReadingOrder
|
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?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
@ -178,7 +198,7 @@ class TimelineFragment :
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
readingOrder = ReadingOrder.from(preferences.getString(PrefKeys.READING_ORDER, null))
|
readingOrder = ReadingOrder.from(preferences.getString(PrefKeys.READING_ORDER, null))
|
||||||
|
|
||||||
val statusDisplayOptions = StatusDisplayOptions(
|
statusDisplayOptions = StatusDisplayOptions(
|
||||||
animateAvatars = preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false),
|
animateAvatars = preferences.getBoolean(PrefKeys.ANIMATE_GIF_AVATARS, false),
|
||||||
mediaPreviewEnabled = accountManager.activeAccount!!.mediaPreviewEnabled,
|
mediaPreviewEnabled = accountManager.activeAccount!!.mediaPreviewEnabled,
|
||||||
useAbsoluteTime = preferences.getBoolean(PrefKeys.ABSOLUTE_TIME_VIEW, false),
|
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()) {
|
if (actionButtonPresent()) {
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
val preferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||||
hideFab = preferences.getBoolean("fabHide", false)
|
hideFab = preferences.getBoolean("fabHide", false)
|
||||||
|
@ -460,6 +486,17 @@ class TimelineFragment :
|
||||||
(activity as BaseActivity).startActivityWithSlideInAnimation(intent)
|
(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) {
|
override fun onShowFavs(position: Int) {
|
||||||
val statusId = adapter.peek(position)?.asStatusOrNull()?.id ?: return
|
val statusId = adapter.peek(position)?.asStatusOrNull()?.id ?: return
|
||||||
val intent = newIntent(requireContext(), AccountListActivity.Type.FAVOURITED, statusId)
|
val intent = newIntent(requireContext(), AccountListActivity.Type.FAVOURITED, statusId)
|
||||||
|
|
|
@ -438,7 +438,7 @@ abstract class SFragment : Fragment(), Injectable {
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
open fun onEmojiReact(id: String, emoji: String, react: Boolean) {
|
fun onEmojiReact(id: String, emoji: String, react: Boolean) {
|
||||||
lifecycleScope.launch {
|
lifecycleScope.launch {
|
||||||
val result = timelineCases.react(id, emoji, react).exceptionOrNull()
|
val result = timelineCases.react(id, emoji, react).exceptionOrNull()
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
|
|
|
@ -72,4 +72,6 @@ public interface StatusActionListener extends LinkListener {
|
||||||
void clearWarningAction(int position);
|
void clearWarningAction(int position);
|
||||||
|
|
||||||
default void onEmojiReactMenu(@NonNull View view, @NonNull final EmojiReaction emoji, String statusId) {}
|
default void onEmojiReactMenu(@NonNull View view, @NonNull final EmojiReaction emoji, String statusId) {}
|
||||||
|
|
||||||
|
default void openReactionPicker(View target, int position) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -410,7 +410,7 @@
|
||||||
android:contentDescription="@string/action_favourite"
|
android:contentDescription="@string/action_favourite"
|
||||||
android:importantForAccessibility="no"
|
android:importantForAccessibility="no"
|
||||||
android:padding="4dp"
|
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_constraintStart_toEndOf="@id/status_inset"
|
||||||
app:layout_constraintTop_toTopOf="@id/status_inset"
|
app:layout_constraintTop_toTopOf="@id/status_inset"
|
||||||
sparkbutton:activeImage="@drawable/ic_favourite_active_24dp"
|
sparkbutton:activeImage="@drawable/ic_favourite_active_24dp"
|
||||||
|
@ -430,6 +430,20 @@
|
||||||
app:layout_constraintTop_toTopOf="@id/status_inset"
|
app:layout_constraintTop_toTopOf="@id/status_inset"
|
||||||
tools:text="" />
|
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
|
<at.connyduck.sparkbutton.SparkButton
|
||||||
android:id="@+id/status_bookmark"
|
android:id="@+id/status_bookmark"
|
||||||
android:layout_width="52dp"
|
android:layout_width="52dp"
|
||||||
|
@ -439,7 +453,7 @@
|
||||||
android:importantForAccessibility="no"
|
android:importantForAccessibility="no"
|
||||||
android:padding="4dp"
|
android:padding="4dp"
|
||||||
app:layout_constraintEnd_toStartOf="@id/status_more"
|
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"
|
app:layout_constraintTop_toTopOf="@id/status_reply"
|
||||||
sparkbutton:activeImage="@drawable/ic_bookmark_active_24dp"
|
sparkbutton:activeImage="@drawable/ic_bookmark_active_24dp"
|
||||||
sparkbutton:iconSize="28dp"
|
sparkbutton:iconSize="28dp"
|
||||||
|
|
|
@ -425,7 +425,7 @@
|
||||||
android:contentDescription="@string/action_favourite"
|
android:contentDescription="@string/action_favourite"
|
||||||
android:importantForAccessibility="no"
|
android:importantForAccessibility="no"
|
||||||
android:padding="4dp"
|
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_constraintStart_toEndOf="@id/status_inset"
|
||||||
app:layout_constraintTop_toTopOf="@id/status_inset"
|
app:layout_constraintTop_toTopOf="@id/status_inset"
|
||||||
sparkbutton:activeImage="@drawable/ic_favourite_active_24dp"
|
sparkbutton:activeImage="@drawable/ic_favourite_active_24dp"
|
||||||
|
@ -434,6 +434,19 @@
|
||||||
sparkbutton:primaryColor="@color/tusky_orange"
|
sparkbutton:primaryColor="@color/tusky_orange"
|
||||||
sparkbutton:secondaryColor="@color/tusky_orange_light" />
|
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
|
<at.connyduck.sparkbutton.SparkButton
|
||||||
android:id="@+id/status_bookmark"
|
android:id="@+id/status_bookmark"
|
||||||
android:layout_width="52dp"
|
android:layout_width="52dp"
|
||||||
|
@ -443,7 +456,7 @@
|
||||||
android:importantForAccessibility="no"
|
android:importantForAccessibility="no"
|
||||||
android:padding="4dp"
|
android:padding="4dp"
|
||||||
app:layout_constraintEnd_toStartOf="@id/status_more"
|
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"
|
app:layout_constraintTop_toTopOf="@id/status_reply"
|
||||||
sparkbutton:activeImage="@drawable/ic_bookmark_active_24dp"
|
sparkbutton:activeImage="@drawable/ic_bookmark_active_24dp"
|
||||||
sparkbutton:iconSize="28dp"
|
sparkbutton:iconSize="28dp"
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
<string name="admin">Admin</string>
|
<string name="admin">Admin</string>
|
||||||
<string name="moderator">Moderator</string>
|
<string name="moderator">Moderator</string>
|
||||||
|
|
||||||
|
<string name="action_react">React</string>
|
||||||
<string name="action_emoji_react">Add reaction</string>
|
<string name="action_emoji_react">Add reaction</string>
|
||||||
<string name="action_emoji_unreact">Remove reaction</string>
|
<string name="action_emoji_unreact">Remove reaction</string>
|
||||||
<string name="action_emoji_reacted_by">Who reacted</string>
|
<string name="action_emoji_reacted_by">Who reacted</string>
|
||||||
|
|
Loading…
Reference in New Issue