emojireactions: Add a list showing who reacted on emoji
Based on these patches by a1ba: -97ffa14268
-7e10c531bc
This commit is contained in:
parent
e17f8b8613
commit
8d0a1f0927
|
@ -770,6 +770,7 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder {
|
|||
|
||||
EmojiReactions.setupReactions(emojiReactionsView, reactions, (Chip chip, View view, EmojiReaction reaction) -> {
|
||||
chip.setChecked(reaction.getMe());
|
||||
listener.onEmojiReactMenu(view, reaction, statusId);
|
||||
return null;
|
||||
}, displayOptions.animateEmojis());
|
||||
|
||||
|
|
|
@ -38,7 +38,8 @@ class AccountListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
MUTES,
|
||||
FOLLOW_REQUESTS,
|
||||
REBLOGGED,
|
||||
FAVOURITED
|
||||
FAVOURITED,
|
||||
REACTED,
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
|
@ -49,6 +50,7 @@ class AccountListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
val type = intent.getSerializableExtra(EXTRA_TYPE) as Type
|
||||
val id: String? = intent.getStringExtra(EXTRA_ID)
|
||||
val accountLocked: Boolean = intent.getBooleanExtra(EXTRA_ACCOUNT_LOCKED, false)
|
||||
val emoji: String? = intent.getStringExtra(EXTRA_EMOJI)
|
||||
|
||||
setSupportActionBar(binding.includedToolbar.toolbar)
|
||||
supportActionBar?.apply {
|
||||
|
@ -60,13 +62,14 @@ class AccountListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
Type.FOLLOWS -> setTitle(R.string.title_follows)
|
||||
Type.REBLOGGED -> setTitle(R.string.title_reblogged_by)
|
||||
Type.FAVOURITED -> setTitle(R.string.title_favourited_by)
|
||||
Type.REACTED -> setTitle(String.format(getString(R.string.title_emoji_reacted_by), emoji))
|
||||
}
|
||||
setDisplayHomeAsUpEnabled(true)
|
||||
setDisplayShowHomeEnabled(true)
|
||||
}
|
||||
|
||||
supportFragmentManager.commit {
|
||||
replace(R.id.fragment_container, AccountListFragment.newInstance(type, id, accountLocked))
|
||||
replace(R.id.fragment_container, AccountListFragment.newInstance(type, id, accountLocked, emoji))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -76,12 +79,14 @@ class AccountListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
private const val EXTRA_TYPE = "type"
|
||||
private const val EXTRA_ID = "id"
|
||||
private const val EXTRA_ACCOUNT_LOCKED = "acc_locked"
|
||||
private const val EXTRA_EMOJI = "emoji"
|
||||
|
||||
fun newIntent(context: Context, type: Type, id: String? = null, accountLocked: Boolean = false): Intent {
|
||||
fun newIntent(context: Context, type: Type, id: String? = null, accountLocked: Boolean = false, emoji: String? = null): Intent {
|
||||
return Intent(context, AccountListActivity::class.java).apply {
|
||||
putExtra(EXTRA_TYPE, type)
|
||||
putExtra(EXTRA_ID, id)
|
||||
putExtra(EXTRA_ACCOUNT_LOCKED, accountLocked)
|
||||
putExtra(EXTRA_EMOJI, emoji)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,6 +47,7 @@ import com.keylesspalace.tusky.components.accountlist.adapter.MutesAdapter
|
|||
import com.keylesspalace.tusky.databinding.FragmentAccountListBinding
|
||||
import com.keylesspalace.tusky.db.AccountManager
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.entity.EmojiReaction
|
||||
import com.keylesspalace.tusky.entity.Relationship
|
||||
import com.keylesspalace.tusky.entity.TimelineAccount
|
||||
import com.keylesspalace.tusky.interfaces.AccountActionListener
|
||||
|
@ -80,6 +81,7 @@ class AccountListFragment :
|
|||
|
||||
private lateinit var type: Type
|
||||
private var id: String? = null
|
||||
private var emojiReaction: String? = null
|
||||
|
||||
private lateinit var scrollListener: EndlessOnScrollListener
|
||||
private lateinit var adapter: AccountAdapter<*>
|
||||
|
@ -90,6 +92,7 @@ class AccountListFragment :
|
|||
super.onCreate(savedInstanceState)
|
||||
type = requireArguments().getSerializable(ARG_TYPE) as Type
|
||||
id = requireArguments().getString(ARG_ID)
|
||||
emojiReaction = arguments?.getString(ARG_EMOJI)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
|
@ -294,11 +297,21 @@ class AccountListFragment :
|
|||
val statusId = requireId(type, id)
|
||||
api.statusFavouritedBy(statusId, fromId)
|
||||
}
|
||||
Type.REACTED -> throw IllegalArgumentException("Use getEmojiReactionFetchCall() instead")
|
||||
}
|
||||
}
|
||||
|
||||
private fun requireId(type: Type, id: String?): String {
|
||||
return requireNotNull(id) { "id must not be null for type " + type.name }
|
||||
private suspend fun getEmojiReactionFetchCall(): Response<List<EmojiReaction>> {
|
||||
val statusId = requireId(type, id)
|
||||
// FIXME: akkoma returns empty lists when asked for a custom emoji.
|
||||
// we must currently get the full reaction list and find later the emoji we need.
|
||||
// val emoji = requireId(type, emojiReaction, "emoji")
|
||||
// return api.statusReactedBy(statusId, emoji)
|
||||
return api.statusReactedBy(statusId, "")
|
||||
}
|
||||
|
||||
private fun requireId(type: Type, id: String?, name: String = "id"): String {
|
||||
return requireNotNull(id) { name+" must not be null for type "+type.name }
|
||||
}
|
||||
|
||||
private fun fetchAccounts(fromId: String? = null) {
|
||||
|
@ -312,26 +325,58 @@ class AccountListFragment :
|
|||
binding.recyclerView.post { adapter.setBottomLoading(true) }
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
try {
|
||||
val response = getFetchCallByListType(fromId)
|
||||
if (type == Type.REACTED) {
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
try {
|
||||
val response = getEmojiReactionFetchCall()
|
||||
|
||||
if (!response.isSuccessful) {
|
||||
onFetchAccountsFailure(Exception(response.message()))
|
||||
return@launch
|
||||
if (!response.isSuccessful) {
|
||||
onFetchAccountsFailure(Exception(response.message()))
|
||||
return@launch
|
||||
}
|
||||
|
||||
val emojiReactionList = response.body()
|
||||
|
||||
if (emojiReactionList == null) {
|
||||
onFetchAccountsFailure(Exception(response.message()))
|
||||
return@launch
|
||||
}
|
||||
|
||||
val emojiName = requireId(type, emojiReaction, "emoji")
|
||||
val reaction = emojiReactionList.find { it.name == emojiName }
|
||||
if (reaction?.accounts == null) {
|
||||
onFetchAccountsFailure(Exception("Couldn't get account list for an emoji"))
|
||||
return@launch
|
||||
}
|
||||
|
||||
val linkHeader = response.headers()["Link"]
|
||||
onFetchAccountsSuccess(reaction.accounts, linkHeader)
|
||||
} catch (throwable: IOException) {
|
||||
onFetchAccountsFailure(throwable)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
try {
|
||||
val response = getFetchCallByListType(fromId)
|
||||
|
||||
val accountList = response.body()
|
||||
if (!response.isSuccessful) {
|
||||
onFetchAccountsFailure(Exception(response.message()))
|
||||
return@launch
|
||||
}
|
||||
|
||||
if (accountList == null) {
|
||||
onFetchAccountsFailure(Exception(response.message()))
|
||||
return@launch
|
||||
val accountList = response.body()
|
||||
|
||||
if (accountList == null) {
|
||||
onFetchAccountsFailure(Exception(response.message()))
|
||||
return@launch
|
||||
}
|
||||
|
||||
val linkHeader = response.headers()["Link"]
|
||||
onFetchAccountsSuccess(accountList, linkHeader)
|
||||
} catch (throwable: IOException) {
|
||||
onFetchAccountsFailure(throwable)
|
||||
}
|
||||
|
||||
val linkHeader = response.headers()["Link"]
|
||||
onFetchAccountsSuccess(accountList, linkHeader)
|
||||
} catch (exception: IOException) {
|
||||
onFetchAccountsFailure(exception)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -405,13 +450,15 @@ class AccountListFragment :
|
|||
private const val ARG_TYPE = "type"
|
||||
private const val ARG_ID = "id"
|
||||
private const val ARG_ACCOUNT_LOCKED = "acc_locked"
|
||||
private const val ARG_EMOJI = "emoji"
|
||||
|
||||
fun newInstance(type: Type, id: String? = null, accountLocked: Boolean = false): AccountListFragment {
|
||||
fun newInstance(type: Type, id: String? = null, accountLocked: Boolean = false, emoji: String? = null): AccountListFragment {
|
||||
return AccountListFragment().apply {
|
||||
arguments = Bundle(3).apply {
|
||||
putSerializable(ARG_TYPE, type)
|
||||
putString(ARG_ID, id)
|
||||
putBoolean(ARG_ACCOUNT_LOCKED, accountLocked)
|
||||
putString(ARG_EMOJI, emoji)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ import com.keylesspalace.tusky.components.account.AccountActivity
|
|||
import com.keylesspalace.tusky.databinding.FragmentTimelineBinding
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
import com.keylesspalace.tusky.entity.EmojiReaction
|
||||
import com.keylesspalace.tusky.fragment.SFragment
|
||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity
|
||||
import com.keylesspalace.tusky.interfaces.ReselectableFragment
|
||||
|
|
|
@ -50,6 +50,7 @@ import com.keylesspalace.tusky.components.search.adapter.SearchStatusesAdapter
|
|||
import com.keylesspalace.tusky.db.AccountEntity
|
||||
import com.keylesspalace.tusky.db.AccountManager
|
||||
import com.keylesspalace.tusky.entity.Attachment
|
||||
import com.keylesspalace.tusky.entity.EmojiReaction
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import com.keylesspalace.tusky.entity.Status.Mention
|
||||
import com.keylesspalace.tusky.interfaces.AccountSelectionListener
|
||||
|
|
|
@ -55,6 +55,7 @@ import com.keylesspalace.tusky.components.timeline.viewmodel.TimelineViewModel
|
|||
import com.keylesspalace.tusky.databinding.FragmentTimelineBinding
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
import com.keylesspalace.tusky.entity.EmojiReaction
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import com.keylesspalace.tusky.fragment.SFragment
|
||||
import com.keylesspalace.tusky.interfaces.ActionButtonActivity
|
||||
|
@ -515,6 +516,10 @@ class TimelineFragment :
|
|||
super.viewAccount(id)
|
||||
}
|
||||
|
||||
override fun onEmojiReactMenu(view: View, emoji: EmojiReaction, statusId: String) {
|
||||
super.emojiReactMenu(statusId, view, emoji)
|
||||
}
|
||||
|
||||
private fun onPreferenceChanged(key: String) {
|
||||
val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
when (key) {
|
||||
|
|
|
@ -44,6 +44,7 @@ import com.keylesspalace.tusky.components.viewthread.edits.ViewEditsFragment
|
|||
import com.keylesspalace.tusky.databinding.FragmentViewThreadBinding
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.di.ViewModelFactory
|
||||
import com.keylesspalace.tusky.entity.EmojiReaction
|
||||
import com.keylesspalace.tusky.fragment.SFragment
|
||||
import com.keylesspalace.tusky.interfaces.StatusActionListener
|
||||
import com.keylesspalace.tusky.settings.PrefKeys
|
||||
|
@ -401,6 +402,10 @@ class ViewThreadFragment :
|
|||
super.viewAccount(id)
|
||||
}
|
||||
|
||||
override fun onEmojiReactMenu(view: View, emoji: EmojiReaction, statusId: String) {
|
||||
super.emojiReactMenu(statusId, view, emoji)
|
||||
}
|
||||
|
||||
public override fun removeItem(position: Int) {
|
||||
adapter.currentList.getOrNull(position)?.let { status ->
|
||||
if (status.isDetailed) {
|
||||
|
|
|
@ -16,11 +16,7 @@ package com.keylesspalace.tusky.fragment
|
|||
|
||||
import android.Manifest
|
||||
import android.app.DownloadManager
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.content.*
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
|
@ -43,6 +39,8 @@ import com.keylesspalace.tusky.PostLookupFallbackBehavior
|
|||
import com.keylesspalace.tusky.R
|
||||
import com.keylesspalace.tusky.StatusListActivity.Companion.newHashtagIntent
|
||||
import com.keylesspalace.tusky.ViewMediaActivity.Companion.newIntent
|
||||
import com.keylesspalace.tusky.components.accountlist.AccountListActivity
|
||||
import com.keylesspalace.tusky.components.accountlist.AccountListActivity.Companion.newIntent
|
||||
import com.keylesspalace.tusky.components.compose.ComposeActivity
|
||||
import com.keylesspalace.tusky.components.compose.ComposeActivity.Companion.startIntent
|
||||
import com.keylesspalace.tusky.components.compose.ComposeActivity.ComposeOptions
|
||||
|
@ -51,6 +49,7 @@ import com.keylesspalace.tusky.db.AccountEntity
|
|||
import com.keylesspalace.tusky.db.AccountManager
|
||||
import com.keylesspalace.tusky.di.Injectable
|
||||
import com.keylesspalace.tusky.entity.Attachment
|
||||
import com.keylesspalace.tusky.entity.EmojiReaction
|
||||
import com.keylesspalace.tusky.entity.Status
|
||||
import com.keylesspalace.tusky.interfaces.AccountSelectionListener
|
||||
import com.keylesspalace.tusky.network.MastodonApi
|
||||
|
@ -62,6 +61,7 @@ import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
|||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
||||
/* Note from Andrew on Jan. 22, 2017: This class is a design problem for me, so I left it with an
|
||||
* awkward name. TimelineFragment and NotificationFragment have significant overlap but the nature
|
||||
* of that is complicated by how they're coupled with Status and Notification and the corresponding
|
||||
|
@ -140,6 +140,22 @@ abstract class SFragment : Fragment(), Injectable {
|
|||
requireActivity().startActivity(intent)
|
||||
}
|
||||
|
||||
protected open fun emojiReactMenu(statusId: String, view: View, reaction: EmojiReaction) {
|
||||
val popup = PopupMenu(requireContext(), view)
|
||||
popup.inflate(R.menu.emoji_reaction_more)
|
||||
popup.setOnMenuItemClickListener { item: MenuItem ->
|
||||
when (item.itemId) {
|
||||
R.id.emoji_reacted_by -> {
|
||||
val intent = newIntent(requireContext(), AccountListActivity.Type.REACTED, statusId, false /* FIXME */, reaction.name)
|
||||
(requireActivity() as BaseActivity).startActivityWithSlideInAnimation(intent)
|
||||
return@setOnMenuItemClickListener true
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
popup.show()
|
||||
}
|
||||
|
||||
protected fun more(status: Status, view: View, position: Int) {
|
||||
val id = status.actionableId
|
||||
val accountId = status.actionableStatus.account.id
|
||||
|
|
|
@ -22,6 +22,8 @@ import java.util.List;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.keylesspalace.tusky.entity.EmojiReaction;
|
||||
|
||||
public interface StatusActionListener extends LinkListener {
|
||||
void onReply(int position);
|
||||
void onReblog(final boolean reblog, final int position);
|
||||
|
@ -67,4 +69,5 @@ public interface StatusActionListener extends LinkListener {
|
|||
|
||||
void clearWarningAction(int position);
|
||||
|
||||
default void onEmojiReactMenu(@NonNull View view, @NonNull final EmojiReaction emoji, String statusId) {}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/emoji_reacted_by"
|
||||
android:title="@string/action_emoji_reacted_by" />
|
||||
</menu>
|
|
@ -6,4 +6,7 @@
|
|||
<string name="admin">Admin</string>
|
||||
<string name="moderator">Moderator</string>
|
||||
|
||||
<string name="action_emoji_reacted_by">Who reacted</string>
|
||||
<string name="title_emoji_reacted_by">%s reacted by</string>
|
||||
|
||||
</resources>
|
Loading…
Reference in New Issue