Add a menu option to mute / filter a hashtag from a status list (#2882)
* Add a menu option to mute / filter a hashtag from a status list Un/muting uses the "home" filter * Set the initial mute button visibility from existing filters Check the user's filters to see if the tag is already filtered from HOME. If it is then the initial button is to unmute it. If it isn't then the initial button is to mute it. * Avoid "mute tag" menu items "popping" in - Initial state shows the "mute" option, disabled - Update the state after the API call completes
This commit is contained in:
parent
64a06bfbe2
commit
424326f99f
3 changed files with 115 additions and 0 deletions
|
@ -22,15 +22,22 @@ import android.util.Log
|
|||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import androidx.fragment.app.commit
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import at.connyduck.calladapter.networkresult.fold
|
||||
import autodispose2.androidx.lifecycle.AndroidLifecycleScopeProvider
|
||||
import autodispose2.autoDispose
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.keylesspalace.tusky.appstore.EventHub
|
||||
import com.keylesspalace.tusky.appstore.PreferenceChangedEvent
|
||||
import com.keylesspalace.tusky.components.timeline.TimelineFragment
|
||||
import com.keylesspalace.tusky.components.timeline.viewmodel.TimelineViewModel.Kind
|
||||
import com.keylesspalace.tusky.databinding.ActivityStatuslistBinding
|
||||
import com.keylesspalace.tusky.entity.Filter
|
||||
import com.keylesspalace.tusky.util.viewBinding
|
||||
import dagger.android.DispatchingAndroidInjector
|
||||
import dagger.android.HasAndroidInjector
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -39,13 +46,22 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
@Inject
|
||||
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
|
||||
|
||||
@Inject
|
||||
lateinit var eventHub: EventHub
|
||||
|
||||
private val binding: ActivityStatuslistBinding by viewBinding(ActivityStatuslistBinding::inflate)
|
||||
private lateinit var kind: Kind
|
||||
private var hashtag: String? = null
|
||||
private var followTagItem: MenuItem? = null
|
||||
private var unfollowTagItem: MenuItem? = null
|
||||
private var muteTagItem: MenuItem? = null
|
||||
private var unmuteTagItem: MenuItem? = null
|
||||
|
||||
/** The filter muting hashtag, null if unknown or hashtag is not filtered */
|
||||
private var mutedFilter: Filter? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
Log.d("StatusListActivity", "onCreate")
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(binding.root)
|
||||
|
||||
|
@ -89,10 +105,15 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
menuInflater.inflate(R.menu.view_hashtag_toolbar, menu)
|
||||
followTagItem = menu.findItem(R.id.action_follow_hashtag)
|
||||
unfollowTagItem = menu.findItem(R.id.action_unfollow_hashtag)
|
||||
muteTagItem = menu.findItem(R.id.action_mute_hashtag)
|
||||
unmuteTagItem = menu.findItem(R.id.action_unmute_hashtag)
|
||||
followTagItem?.isVisible = tagEntity.following == false
|
||||
unfollowTagItem?.isVisible = tagEntity.following == true
|
||||
followTagItem?.setOnMenuItemClickListener { followTag() }
|
||||
unfollowTagItem?.setOnMenuItemClickListener { unfollowTag() }
|
||||
muteTagItem?.setOnMenuItemClickListener { muteTag() }
|
||||
unmuteTagItem?.setOnMenuItemClickListener { unmuteTag() }
|
||||
updateMuteTagMenuItems()
|
||||
},
|
||||
{
|
||||
Log.w(TAG, "Failed to query tag #$tag", it)
|
||||
|
@ -144,6 +165,85 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
|
|||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if the current hashtag is muted, and update the UI state accordingly.
|
||||
*/
|
||||
private fun updateMuteTagMenuItems() {
|
||||
val tag = hashtag ?: return
|
||||
|
||||
muteTagItem?.isVisible = true
|
||||
muteTagItem?.isEnabled = false
|
||||
unmuteTagItem?.isVisible = false
|
||||
|
||||
mastodonApi.getFilters().observeOn(AndroidSchedulers.mainThread())
|
||||
.autoDispose(AndroidLifecycleScopeProvider.from(this, Lifecycle.Event.ON_DESTROY))
|
||||
.subscribe { filters ->
|
||||
for (filter in filters) {
|
||||
if ((tag == filter.phrase) and filter.context.contains(Filter.HOME)) {
|
||||
Log.d(TAG, "Tag $hashtag is filtered")
|
||||
muteTagItem?.isVisible = false
|
||||
unmuteTagItem?.isVisible = true
|
||||
mutedFilter = filter
|
||||
return@subscribe
|
||||
}
|
||||
}
|
||||
|
||||
Log.d(TAG, "Tag $hashtag is not filtered")
|
||||
mutedFilter = null
|
||||
muteTagItem?.isEnabled = true
|
||||
muteTagItem?.isVisible = true
|
||||
muteTagItem?.isVisible = true
|
||||
}
|
||||
}
|
||||
|
||||
private fun muteTag(): Boolean {
|
||||
val tag = hashtag ?: return true
|
||||
|
||||
lifecycleScope.launch {
|
||||
mastodonApi.createFilter(
|
||||
tag,
|
||||
listOf(Filter.HOME),
|
||||
irreversible = false,
|
||||
wholeWord = true,
|
||||
expiresInSeconds = null
|
||||
).fold(
|
||||
{ filter ->
|
||||
mutedFilter = filter
|
||||
muteTagItem?.isVisible = false
|
||||
unmuteTagItem?.isVisible = true
|
||||
eventHub.dispatch(PreferenceChangedEvent(filter.context[0]))
|
||||
},
|
||||
{
|
||||
Snackbar.make(binding.root, getString(R.string.error_muting_hashtag_format, tag), Snackbar.LENGTH_SHORT).show()
|
||||
Log.e(TAG, "Failed to mute #$tag", it)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
private fun unmuteTag(): Boolean {
|
||||
val filter = mutedFilter ?: return true
|
||||
|
||||
lifecycleScope.launch {
|
||||
mastodonApi.deleteFilter(filter.id).fold(
|
||||
{
|
||||
muteTagItem?.isVisible = true
|
||||
unmuteTagItem?.isVisible = false
|
||||
eventHub.dispatch(PreferenceChangedEvent(filter.context[0]))
|
||||
mutedFilter = null
|
||||
},
|
||||
{
|
||||
Snackbar.make(binding.root, getString(R.string.error_unmuting_hashtag_format, filter.phrase), Snackbar.LENGTH_SHORT).show()
|
||||
Log.e(TAG, "Failed to unmute #${filter.phrase}", it)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun androidInjector() = dispatchingAndroidInjector
|
||||
|
||||
companion object {
|
||||
|
|
|
@ -17,5 +17,18 @@
|
|||
app:iconTint="?attr/colorOnSurface"
|
||||
android:icon="@drawable/ic_person_remove_24dp" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_mute_hashtag"
|
||||
android:title="Mute"
|
||||
app:showAsAction="ifRoom"
|
||||
app:iconTint="?attr/colorOnSurface"
|
||||
android:icon="@drawable/ic_mute_24dp" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_unmute_hashtag"
|
||||
android:title="Unmute"
|
||||
app:showAsAction="ifRoom"
|
||||
app:iconTint="?attr/colorOnSurface"
|
||||
android:icon="@drawable/ic_unmute_24dp" />
|
||||
|
||||
</menu>
|
|
@ -25,6 +25,8 @@
|
|||
<string name="error_following_hashtag_format">Error following #%s</string>
|
||||
<string name="error_unfollowing_hashtag_format">Error unfollowing #%s</string>
|
||||
<string name="error_following_hashtags_unsupported">This instance does not support following hashtags.</string>
|
||||
<string name="error_muting_hashtag_format">Error muting #%s</string>
|
||||
<string name="error_unmuting_hashtag_format">Error unmuting #%s</string>
|
||||
|
||||
<string name="title_login">Login</string>
|
||||
<string name="title_home">Home</string>
|
||||
|
|
Loading…
Reference in a new issue