From b0a50f9f500502f636b6360ae30ddb920665c1ed Mon Sep 17 00:00:00 2001 From: Lakoja Date: Thu, 27 Jul 2023 16:39:03 +0200 Subject: [PATCH 1/7] 3891: Add feedback (snackbars) for hashtag actions --- .../keylesspalace/tusky/StatusListActivity.kt | 26 +++++++++++++++++-- app/src/main/res/values/strings.xml | 6 +++++ 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt b/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt index c055043d1..ddf58ed7e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt @@ -27,6 +27,7 @@ import at.connyduck.calladapter.networkresult.fold import com.google.android.material.snackbar.Snackbar import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.appstore.PreferenceChangedEvent +import com.keylesspalace.tusky.components.filters.FiltersActivity import com.keylesspalace.tusky.components.timeline.TimelineFragment import com.keylesspalace.tusky.components.timeline.viewmodel.TimelineViewModel.Kind import com.keylesspalace.tusky.databinding.ActivityStatuslistBinding @@ -132,6 +133,8 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { { followTagItem?.isVisible = false unfollowTagItem?.isVisible = true + + Snackbar.make(binding.root, getString(R.string.following_hashtag_success_format, tag), Snackbar.LENGTH_LONG).show() }, { Snackbar.make(binding.root, getString(R.string.error_following_hashtag_format, tag), Snackbar.LENGTH_SHORT).show() @@ -152,6 +155,8 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { { followTagItem?.isVisible = true unfollowTagItem?.isVisible = false + + Snackbar.make(binding.root, getString(R.string.unfollowing_hashtag_success_format, tag), Snackbar.LENGTH_SHORT).show() }, { Snackbar.make(binding.root, getString(R.string.error_unfollowing_hashtag_format, tag), Snackbar.LENGTH_SHORT).show() @@ -219,8 +224,11 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { private fun muteTag(): Boolean { val tag = hashtag ?: return true + val hashedTag = if (tag.startsWith('#')) tag else "#" + tag lifecycleScope.launch { + var filterCreateSuccess = false + mastodonApi.createFilter( title = "#$tag", context = listOf(FilterV1.HOME), @@ -230,8 +238,9 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { { filter -> if (mastodonApi.addFilterKeyword(filterId = filter.id, keyword = tag, wholeWord = true).isSuccess) { mutedFilter = filter - updateTagMuteState(true) + // TODO the preference key here ("home") is not meaningful; should probably be another event if any eventHub.dispatch(PreferenceChangedEvent(filter.context[0])) + filterCreateSuccess = true } else { Snackbar.make(binding.root, getString(R.string.error_muting_hashtag_format, tag), Snackbar.LENGTH_SHORT).show() Log.e(TAG, "Failed to mute #$tag") @@ -248,8 +257,8 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { ).fold( { filter -> mutedFilterV1 = filter - updateTagMuteState(true) eventHub.dispatch(PreferenceChangedEvent(filter.context[0])) + filterCreateSuccess = true }, { throwable -> Snackbar.make(binding.root, getString(R.string.error_muting_hashtag_format, tag), Snackbar.LENGTH_SHORT).show() @@ -262,6 +271,16 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { } } ) + + if (filterCreateSuccess) { + updateTagMuteState(true) + Snackbar.make(binding.root, getString(R.string.muting_hashtag_success_format, tag), Snackbar.LENGTH_LONG).apply { + setAction(R.string.action_view_filters) { + startActivityWithSlideInAnimation(Intent(this@StatusListActivity, FiltersActivity::class.java)) + } + show() + } + } } return true @@ -281,6 +300,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { } else { mastodonApi.deleteFilter(filter.id) } + } else if (mutedFilterV1 != null) { mutedFilterV1?.let { filter -> if (filter.context.size > 1) { @@ -307,6 +327,8 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { eventHub.dispatch(PreferenceChangedEvent(Filter.Kind.HOME.kind)) mutedFilterV1 = null mutedFilter = null + + Snackbar.make(binding.root, getString(R.string.unmuting_hashtag_success_format, tag), Snackbar.LENGTH_SHORT).show() }, { throwable -> Snackbar.make(binding.root, getString(R.string.error_unmuting_hashtag_format, tag), Snackbar.LENGTH_SHORT).show() diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 06d25d4e7..30538f4ae 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -614,6 +614,12 @@ Filter Apply + Muting %s as a warning + Unmuting %s + View filters + Now following hashtag %s + No longer following hashtag %s + Compose post Compose From a883ec71d5d8f0c6a2705a70d6569a973142cef9 Mon Sep 17 00:00:00 2001 From: Lakoja Date: Thu, 27 Jul 2023 17:16:01 +0200 Subject: [PATCH 2/7] 3891: Only mute the actual hashtag (with #) --- .../com/keylesspalace/tusky/StatusListActivity.kt | 11 ++++++----- app/src/main/res/values/strings.xml | 8 ++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt b/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt index ddf58ed7e..798e2429a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt @@ -174,6 +174,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { */ private fun updateMuteTagMenuItems() { val tag = hashtag ?: return + val hashedTag = if (tag.startsWith('#')) tag else "#" + tag muteTagItem?.isVisible = true muteTagItem?.isEnabled = false @@ -184,7 +185,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { { filters -> mutedFilter = filters.firstOrNull { filter -> filter.context.contains(Filter.Kind.HOME.kind) && filter.keywords.any { - it.keyword == tag + it.keyword == hashedTag } } updateTagMuteState(mutedFilter != null) @@ -194,7 +195,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { mastodonApi.getFiltersV1().fold( { filters -> mutedFilterV1 = filters.firstOrNull { filter -> - tag == filter.phrase && filter.context.contains(FilterV1.HOME) + hashedTag == filter.phrase && filter.context.contains(FilterV1.HOME) } updateTagMuteState(mutedFilterV1 != null) }, @@ -224,10 +225,10 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { private fun muteTag(): Boolean { val tag = hashtag ?: return true - val hashedTag = if (tag.startsWith('#')) tag else "#" + tag lifecycleScope.launch { var filterCreateSuccess = false + val hashedTag = if (tag.startsWith('#')) tag else "#" + tag mastodonApi.createFilter( title = "#$tag", @@ -236,7 +237,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { expiresInSeconds = null ).fold( { filter -> - if (mastodonApi.addFilterKeyword(filterId = filter.id, keyword = tag, wholeWord = true).isSuccess) { + if (mastodonApi.addFilterKeyword(filterId = filter.id, keyword = hashedTag, wholeWord = true).isSuccess) { mutedFilter = filter // TODO the preference key here ("home") is not meaningful; should probably be another event if any eventHub.dispatch(PreferenceChangedEvent(filter.context[0])) @@ -249,7 +250,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { { throwable -> if (throwable is HttpException && throwable.code() == 404) { mastodonApi.createFilterV1( - tag, + hashedTag, listOf(FilterV1.HOME), irreversible = false, wholeWord = true, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 30538f4ae..b4ed815e6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -614,11 +614,11 @@ Filter Apply - Muting %s as a warning - Unmuting %s + Muting hashtag #%s as a warning + Unmuting hashtag #%s View filters - Now following hashtag %s - No longer following hashtag %s + Now following hashtag #%s + No longer following hashtag #%s Compose post Compose From e5456b01974af3ad200979e69fee4e4eaa42975d Mon Sep 17 00:00:00 2001 From: Lakoja Date: Thu, 27 Jul 2023 17:36:43 +0200 Subject: [PATCH 3/7] 3891: Directly start the edit filter activity --- .../keylesspalace/tusky/StatusListActivity.kt | 18 +++++++++++++++--- .../keylesspalace/tusky/network/MastodonApi.kt | 5 +++++ app/src/main/res/values/strings.xml | 2 +- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt b/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt index 798e2429a..b33fb775e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt @@ -27,6 +27,7 @@ import at.connyduck.calladapter.networkresult.fold import com.google.android.material.snackbar.Snackbar import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.appstore.PreferenceChangedEvent +import com.keylesspalace.tusky.components.filters.EditFilterActivity import com.keylesspalace.tusky.components.filters.FiltersActivity import com.keylesspalace.tusky.components.timeline.TimelineFragment import com.keylesspalace.tusky.components.timeline.viewmodel.TimelineViewModel.Kind @@ -184,6 +185,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { mastodonApi.getFilters().fold( { filters -> mutedFilter = filters.firstOrNull { filter -> + // TODO shouldn't this be an exact match (only one keyword; exactly the hashtag)? filter.context.contains(Filter.Kind.HOME.kind) && filter.keywords.any { it.keyword == hashedTag } @@ -238,7 +240,9 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { ).fold( { filter -> if (mastodonApi.addFilterKeyword(filterId = filter.id, keyword = hashedTag, wholeWord = true).isSuccess) { - mutedFilter = filter + // must be requested again; otherwise does not contain the keyword (but server does) + mutedFilter = mastodonApi.getFilter(filter.id).getOrNull() + // TODO the preference key here ("home") is not meaningful; should probably be another event if any eventHub.dispatch(PreferenceChangedEvent(filter.context[0])) filterCreateSuccess = true @@ -276,8 +280,16 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { if (filterCreateSuccess) { updateTagMuteState(true) Snackbar.make(binding.root, getString(R.string.muting_hashtag_success_format, tag), Snackbar.LENGTH_LONG).apply { - setAction(R.string.action_view_filters) { - startActivityWithSlideInAnimation(Intent(this@StatusListActivity, FiltersActivity::class.java)) + setAction(R.string.action_view_filter) { + val intent = if (mutedFilter != null) { + Intent(this@StatusListActivity, EditFilterActivity::class.java).apply { + putExtra(EditFilterActivity.FILTER_TO_EDIT, mutedFilter) + } + } else { + Intent(this@StatusListActivity, FiltersActivity::class.java) + } + + startActivityWithSlideInAnimation(intent) } show() } diff --git a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt index c64690d1a..043b54d9a 100644 --- a/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt +++ b/app/src/main/java/com/keylesspalace/tusky/network/MastodonApi.kt @@ -89,6 +89,11 @@ interface MastodonApi { @GET("api/v1/filters") suspend fun getFiltersV1(): NetworkResult> + @GET("api/v2/filters/{filterId}") + suspend fun getFilter( + @Path("filterId") filterId: String + ): NetworkResult + @GET("api/v2/filters") suspend fun getFilters(): NetworkResult> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b4ed815e6..51bafc02b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -616,7 +616,7 @@ Muting hashtag #%s as a warning Unmuting hashtag #%s - View filters + View filter Now following hashtag #%s No longer following hashtag #%s From 96028c841a1211435c7894897ce06bb921f52d60 Mon Sep 17 00:00:00 2001 From: Lakoja Date: Fri, 28 Jul 2023 09:33:23 +0200 Subject: [PATCH 4/7] 3891: (use string templates) --- .../main/java/com/keylesspalace/tusky/StatusListActivity.kt | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt b/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt index b33fb775e..7ebed833c 100644 --- a/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt @@ -175,7 +175,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { */ private fun updateMuteTagMenuItems() { val tag = hashtag ?: return - val hashedTag = if (tag.startsWith('#')) tag else "#" + tag + val hashedTag = if (tag.startsWith('#')) tag else "#$tag" muteTagItem?.isVisible = true muteTagItem?.isEnabled = false @@ -230,7 +230,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { lifecycleScope.launch { var filterCreateSuccess = false - val hashedTag = if (tag.startsWith('#')) tag else "#" + tag + val hashedTag = if (tag.startsWith('#')) tag else "#$tag" mastodonApi.createFilter( title = "#$tag", @@ -313,7 +313,6 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { } else { mastodonApi.deleteFilter(filter.id) } - } else if (mutedFilterV1 != null) { mutedFilterV1?.let { filter -> if (filter.context.size > 1) { From 6bd1c234d2f7eb8a57c967642ee5cf59f12bb839 Mon Sep 17 00:00:00 2001 From: Lakoja Date: Mon, 7 Aug 2023 10:09:56 +0200 Subject: [PATCH 5/7] 3891: Change length to short consistently --- app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt b/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt index 7ebed833c..3ed1b5f69 100644 --- a/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt @@ -135,7 +135,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { followTagItem?.isVisible = false unfollowTagItem?.isVisible = true - Snackbar.make(binding.root, getString(R.string.following_hashtag_success_format, tag), Snackbar.LENGTH_LONG).show() + Snackbar.make(binding.root, getString(R.string.following_hashtag_success_format, tag), Snackbar.LENGTH_SHORT).show() }, { Snackbar.make(binding.root, getString(R.string.error_following_hashtag_format, tag), Snackbar.LENGTH_SHORT).show() From 35fd702472821b4d973e138c15abbae5d534da5a Mon Sep 17 00:00:00 2001 From: Lakoja Date: Mon, 7 Aug 2023 10:12:57 +0200 Subject: [PATCH 6/7] 3891: Remove unnecessary if (use "this.hashtag" consistently) --- .../main/java/com/keylesspalace/tusky/StatusListActivity.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt b/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt index 3ed1b5f69..9e4e93052 100644 --- a/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt @@ -175,7 +175,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { */ private fun updateMuteTagMenuItems() { val tag = hashtag ?: return - val hashedTag = if (tag.startsWith('#')) tag else "#$tag" + val hashedTag = "#$tag" muteTagItem?.isVisible = true muteTagItem?.isEnabled = false @@ -230,7 +230,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { lifecycleScope.launch { var filterCreateSuccess = false - val hashedTag = if (tag.startsWith('#')) tag else "#$tag" + val hashedTag = "#$tag" mastodonApi.createFilter( title = "#$tag", From 70d86345a87994f2f219594b0ecfe236a27d1f4e Mon Sep 17 00:00:00 2001 From: Lakoja Date: Thu, 24 Aug 2023 15:00:52 +0200 Subject: [PATCH 7/7] 3891: Match (only) title for muting state --- .../main/java/com/keylesspalace/tusky/StatusListActivity.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt b/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt index 9e4e93052..39cd0ad09 100644 --- a/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/StatusListActivity.kt @@ -186,9 +186,7 @@ class StatusListActivity : BottomSheetActivity(), HasAndroidInjector { { filters -> mutedFilter = filters.firstOrNull { filter -> // TODO shouldn't this be an exact match (only one keyword; exactly the hashtag)? - filter.context.contains(Filter.Kind.HOME.kind) && filter.keywords.any { - it.keyword == hashedTag - } + filter.context.contains(Filter.Kind.HOME.kind) && filter.title == hashedTag } updateTagMuteState(mutedFilter != null) },