fix: Create hashtag filters with the `#` character (#3)

Previous code created hashtag filters without the `#`, so muting the
tag `#something` would filter all posts that contained `something`,
with or without the `#`.

Fix this when creating filters, and only remove filters (when unmuting)
if the title and contents match.

Show a snackbar when hashtags are successfully muted/unmuted, so the
user is aware something happened.
This commit is contained in:
Nik Clayton 2023-09-05 21:54:18 +02:00 committed by GitHub
parent 4879f0449f
commit 8a9dd7dc60
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 31 additions and 16 deletions

View File

@ -41,6 +41,10 @@ import kotlinx.coroutines.launch
import retrofit2.HttpException import retrofit2.HttpException
import javax.inject.Inject import javax.inject.Inject
/**
* Show a list of statuses of a particular type; containing a particular hashtag,
* the user's favourites, bookmarks, etc.
*/
class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
@Inject @Inject
@ -51,6 +55,11 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
private val binding: ActivityStatuslistBinding by viewBinding(ActivityStatuslistBinding::inflate) private val binding: ActivityStatuslistBinding by viewBinding(ActivityStatuslistBinding::inflate)
private lateinit var timelineKind: TimelineKind private lateinit var timelineKind: TimelineKind
/**
* If showing statuses with a hashtag, the hashtag being used, without the
* leading `#`.
*/
private var hashtag: String? = null private var hashtag: String? = null
private var followTagItem: MenuItem? = null private var followTagItem: MenuItem? = null
private var unfollowTagItem: MenuItem? = null private var unfollowTagItem: MenuItem? = null
@ -168,7 +177,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
* Determine if the current hashtag is muted, and update the UI state accordingly. * Determine if the current hashtag is muted, and update the UI state accordingly.
*/ */
private fun updateMuteTagMenuItems() { private fun updateMuteTagMenuItems() {
val tag = hashtag ?: return val tagWithHash = hashtag?.let { "#$it" } ?: return
muteTagItem?.isVisible = true muteTagItem?.isVisible = true
muteTagItem?.isEnabled = false muteTagItem?.isEnabled = false
@ -179,7 +188,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
{ filters -> { filters ->
mutedFilter = filters.firstOrNull { filter -> mutedFilter = filters.firstOrNull { filter ->
filter.context.contains(Filter.Kind.HOME.kind) && filter.keywords.any { filter.context.contains(Filter.Kind.HOME.kind) && filter.keywords.any {
it.keyword == tag it.keyword == tagWithHash
} }
} }
updateTagMuteState(mutedFilter != null) updateTagMuteState(mutedFilter != null)
@ -189,7 +198,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
mastodonApi.getFiltersV1().fold( mastodonApi.getFiltersV1().fold(
{ filters -> { filters ->
mutedFilterV1 = filters.firstOrNull { filter -> mutedFilterV1 = filters.firstOrNull { filter ->
tag == filter.phrase && filter.context.contains(FilterV1.HOME) tagWithHash == filter.phrase && filter.context.contains(FilterV1.HOME)
} }
updateTagMuteState(mutedFilterV1 != null) updateTagMuteState(mutedFilterV1 != null)
}, },
@ -218,29 +227,30 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
} }
private fun muteTag(): Boolean { private fun muteTag(): Boolean {
val tag = hashtag ?: return true val tagWithHash = hashtag?.let { "#$it" } ?: return true
lifecycleScope.launch { lifecycleScope.launch {
mastodonApi.createFilter( mastodonApi.createFilter(
title = "#$tag", title = tagWithHash,
context = listOf(FilterV1.HOME), context = listOf(FilterV1.HOME),
filterAction = Filter.Action.WARN.action, filterAction = Filter.Action.WARN.action,
expiresInSeconds = null, expiresInSeconds = null,
).fold( ).fold(
{ filter -> { filter ->
if (mastodonApi.addFilterKeyword(filterId = filter.id, keyword = tag, wholeWord = true).isSuccess) { if (mastodonApi.addFilterKeyword(filterId = filter.id, keyword = tagWithHash, wholeWord = true).isSuccess) {
mutedFilter = filter mutedFilter = filter
updateTagMuteState(true) updateTagMuteState(true)
eventHub.dispatch(PreferenceChangedEvent(filter.context[0])) eventHub.dispatch(PreferenceChangedEvent(filter.context[0]))
Snackbar.make(binding.root, getString(R.string.confirmation_hashtag_muted, hashtag), Snackbar.LENGTH_SHORT).show()
} else { } else {
Snackbar.make(binding.root, getString(R.string.error_muting_hashtag_format, tag), Snackbar.LENGTH_SHORT).show() Snackbar.make(binding.root, getString(R.string.error_muting_hashtag_format, hashtag), Snackbar.LENGTH_SHORT).show()
Log.e(TAG, "Failed to mute #$tag") Log.e(TAG, "Failed to mute $tagWithHash")
} }
}, },
{ throwable -> { throwable ->
if (throwable is HttpException && throwable.code() == 404) { if (throwable is HttpException && throwable.code() == 404) {
mastodonApi.createFilterV1( mastodonApi.createFilterV1(
tag, tagWithHash,
listOf(FilterV1.HOME), listOf(FilterV1.HOME),
irreversible = false, irreversible = false,
wholeWord = true, wholeWord = true,
@ -250,15 +260,16 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
mutedFilterV1 = filter mutedFilterV1 = filter
updateTagMuteState(true) updateTagMuteState(true)
eventHub.dispatch(PreferenceChangedEvent(filter.context[0])) eventHub.dispatch(PreferenceChangedEvent(filter.context[0]))
Snackbar.make(binding.root, getString(R.string.confirmation_hashtag_muted, hashtag), Snackbar.LENGTH_SHORT).show()
}, },
{ throwable -> { throwable ->
Snackbar.make(binding.root, getString(R.string.error_muting_hashtag_format, tag), Snackbar.LENGTH_SHORT).show() Snackbar.make(binding.root, getString(R.string.error_muting_hashtag_format, hashtag), Snackbar.LENGTH_SHORT).show()
Log.e(TAG, "Failed to mute #$tag", throwable) Log.e(TAG, "Failed to mute $tagWithHash", throwable)
}, },
) )
} else { } else {
Snackbar.make(binding.root, getString(R.string.error_muting_hashtag_format, tag), Snackbar.LENGTH_SHORT).show() Snackbar.make(binding.root, getString(R.string.error_muting_hashtag_format, hashtag), Snackbar.LENGTH_SHORT).show()
Log.e(TAG, "Failed to mute #$tag", throwable) Log.e(TAG, "Failed to mute $tagWithHash", throwable)
} }
}, },
) )
@ -269,7 +280,8 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
private fun unmuteTag(): Boolean { private fun unmuteTag(): Boolean {
lifecycleScope.launch { lifecycleScope.launch {
val tag = hashtag val tagWithHash = hashtag?.let { "#$it" } ?: return@launch
val result = if (mutedFilter != null) { val result = if (mutedFilter != null) {
val filter = mutedFilter!! val filter = mutedFilter!!
if (filter.context.size > 1) { if (filter.context.size > 1) {
@ -304,13 +316,14 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector {
result?.fold( result?.fold(
{ {
updateTagMuteState(false) updateTagMuteState(false)
Snackbar.make(binding.root, getString(R.string.confirmation_hashtag_unmuted, hashtag), Snackbar.LENGTH_SHORT).show()
eventHub.dispatch(PreferenceChangedEvent(Filter.Kind.HOME.kind)) eventHub.dispatch(PreferenceChangedEvent(Filter.Kind.HOME.kind))
mutedFilterV1 = null mutedFilterV1 = null
mutedFilter = null mutedFilter = null
}, },
{ throwable -> { throwable ->
Snackbar.make(binding.root, getString(R.string.error_unmuting_hashtag_format, tag), Snackbar.LENGTH_SHORT).show() Snackbar.make(binding.root, getString(R.string.error_unmuting_hashtag_format, hashtag), Snackbar.LENGTH_SHORT).show()
Log.e(TAG, "Failed to unmute #$tag", throwable) Log.e(TAG, "Failed to unmute $tagWithHash", throwable)
}, },
) )
} }

View File

@ -229,6 +229,8 @@
<string name="confirmation_unblocked">User unblocked</string> <string name="confirmation_unblocked">User unblocked</string>
<string name="confirmation_unmuted">User unmuted</string> <string name="confirmation_unmuted">User unmuted</string>
<string name="confirmation_domain_unmuted">%s unhidden</string> <string name="confirmation_domain_unmuted">%s unhidden</string>
<string name="confirmation_hashtag_muted">#%s hidden</string>
<string name="confirmation_hashtag_unmuted">#%s unhidden</string>
<string name="confirmation_hashtag_unfollowed">#%s unfollowed</string> <string name="confirmation_hashtag_unfollowed">#%s unfollowed</string>
<string name="post_sent">Sent!</string> <string name="post_sent">Sent!</string>