From f0638b176634658f92920af20cfec24f12e4aee4 Mon Sep 17 00:00:00 2001 From: greenart7c3 <115044884+greenart7c3@users.noreply.github.com> Date: Fri, 9 Jun 2023 08:28:05 -0300 Subject: [PATCH 1/3] add option to opt-out from automatic spam and report filters --- .../amethyst/LocalPreferences.kt | 6 +++++- .../amethyst/OptOutFromFilters.kt | 9 ++++++++ .../vitorpamplona/amethyst/ServiceManager.kt | 1 + .../vitorpamplona/amethyst/model/Account.kt | 18 +++++++++++++++- .../amethyst/model/AntiSpamFilter.kt | 3 +++ .../ui/screen/loggedIn/HiddenUsersScreen.kt | 21 +++++++++++++++++++ app/src/main/res/values/strings.xml | 1 + 7 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 app/src/main/java/com/vitorpamplona/amethyst/OptOutFromFilters.kt diff --git a/app/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt b/app/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt index b8aba0e2d..8c539ab7f 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt @@ -61,6 +61,7 @@ private object PrefKeys { const val USE_PROXY = "use_proxy" const val PROXY_PORT = "proxy_port" const val SHOW_SENSITIVE_CONTENT = "show_sensitive_content" + const val OPT_OUT_FILTERS = "opt_out_filters" val LAST_READ: (String) -> String = { route -> "last_read_route_$route" } } @@ -217,6 +218,7 @@ object LocalPreferences { putBoolean(PrefKeys.HIDE_BLOCK_ALERT_DIALOG, account.hideBlockAlertDialog) putBoolean(PrefKeys.USE_PROXY, account.proxy != null) putInt(PrefKeys.PROXY_PORT, account.proxyPort) + putBoolean(PrefKeys.OPT_OUT_FILTERS, account.optOutFromFilters) if (account.showSensitiveContent == null) { remove(PrefKeys.SHOW_SENSITIVE_CONTENT) @@ -306,6 +308,7 @@ object LocalPreferences { } else { null } + val optOutFromFilters = getBoolean(PrefKeys.OPT_OUT_FILTERS, false) val a = Account( Persona(privKey = privKey?.hexToByteArray(), pubKey = pubKey.hexToByteArray()), @@ -327,7 +330,8 @@ object LocalPreferences { latestContactList, proxy, proxyPort, - showSensitiveContent + showSensitiveContent, + optOutFromFilters ) return a diff --git a/app/src/main/java/com/vitorpamplona/amethyst/OptOutFromFilters.kt b/app/src/main/java/com/vitorpamplona/amethyst/OptOutFromFilters.kt new file mode 100644 index 000000000..736be2916 --- /dev/null +++ b/app/src/main/java/com/vitorpamplona/amethyst/OptOutFromFilters.kt @@ -0,0 +1,9 @@ +package com.vitorpamplona.amethyst + +object OptOutFromFilters { + var optOutFromFilters: Boolean = false + + fun start(optOut: Boolean) { + optOutFromFilters = optOut + } +} diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ServiceManager.kt b/app/src/main/java/com/vitorpamplona/amethyst/ServiceManager.kt index 9acaa0d60..a85741a2e 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ServiceManager.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ServiceManager.kt @@ -36,6 +36,7 @@ object ServiceManager { // Resets Proxy Use HttpClient.start(account) + OptOutFromFilters.start(account?.optOutFromFilters ?: false) Coil.setImageLoader { ImageLoader.Builder(context).components { if (Build.VERSION.SDK_INT >= 28) { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index d90f42cc4..3205b8009 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -6,6 +6,7 @@ import androidx.compose.runtime.Immutable import androidx.compose.runtime.Stable import androidx.core.os.ConfigurationCompat import androidx.lifecycle.LiveData +import com.vitorpamplona.amethyst.OptOutFromFilters import com.vitorpamplona.amethyst.service.FileHeader import com.vitorpamplona.amethyst.service.NostrLnZapPaymentResponseDataSource import com.vitorpamplona.amethyst.service.model.* @@ -66,7 +67,8 @@ class Account( var backupContactList: ContactListEvent? = null, var proxy: Proxy?, var proxyPort: Int, - var showSensitiveContent: Boolean? = null + var showSensitiveContent: Boolean? = null, + var optOutFromFilters: Boolean = false ) { var transientHiddenUsers: Set = setOf() @@ -77,6 +79,13 @@ class Account( var userProfileCache: User? = null + fun updateOptOutFromFilters(value: Boolean) { + optOutFromFilters = value + OptOutFromFilters.start(optOutFromFilters) + live.invalidateData() + saveable.invalidateData() + } + fun userProfile(): User { return userProfileCache ?: run { val myUser: User = LocalCache.getOrCreateUser(loggedIn.pubKey.toHexKey()) @@ -1057,12 +1066,19 @@ class Account( } fun isAcceptable(user: User): Boolean { + if (optOutFromFilters) { + return !isHidden(user) && // if user hasn't hided this author + user.reportsBy(userProfile()).isEmpty() // if user has not reported this post + } return !isHidden(user) && // if user hasn't hided this author user.reportsBy(userProfile()).isEmpty() && // if user has not reported this post user.countReportAuthorsBy(followingKeySet()) < 5 } fun isAcceptableDirect(note: Note): Boolean { + if (optOutFromFilters) { + return note.reportsBy(userProfile()).isEmpty() + } return note.reportsBy(userProfile()).isEmpty() && // if user has not reported this post note.countReportAuthorsBy(followingKeySet()) < 5 // if it has 5 reports by reliable users } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/AntiSpamFilter.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/AntiSpamFilter.kt index 32d258fb0..4686bcfb5 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/AntiSpamFilter.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/AntiSpamFilter.kt @@ -3,6 +3,7 @@ package com.vitorpamplona.amethyst.model import android.util.Log import android.util.LruCache import androidx.lifecycle.LiveData +import com.vitorpamplona.amethyst.OptOutFromFilters import com.vitorpamplona.amethyst.service.checkNotInMainThread import com.vitorpamplona.amethyst.service.model.Event import com.vitorpamplona.amethyst.service.nip19.Nip19 @@ -20,6 +21,8 @@ class AntiSpamFilter { fun isSpam(event: Event, relay: Relay?): Boolean { checkNotInMainThread() + if (OptOutFromFilters.optOutFromFilters) return false + val idHex = event.id // if short message, ok diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HiddenUsersScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HiddenUsersScreen.kt index 58c5d628a..9a2d84954 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HiddenUsersScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HiddenUsersScreen.kt @@ -2,10 +2,12 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.padding import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.rememberPagerState +import androidx.compose.material.Checkbox import androidx.compose.material.MaterialTheme import androidx.compose.material.Tab import androidx.compose.material.TabRow @@ -15,7 +17,11 @@ import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalLifecycleOwner import androidx.compose.ui.res.stringResource @@ -23,6 +29,7 @@ import androidx.compose.ui.unit.dp import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.viewmodel.compose.viewModel +import com.vitorpamplona.amethyst.LocalPreferences import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.ui.screen.NostrHiddenAccountsFeedViewModel import com.vitorpamplona.amethyst.ui.screen.RefreshingFeedUserFeedView @@ -67,6 +74,20 @@ fun HiddenUsersScreen( Column(modifier = Modifier.padding(start = 10.dp, end = 10.dp)) { val pagerState = rememberPagerState() val coroutineScope = rememberCoroutineScope() + var checked by remember { mutableStateOf(accountViewModel.account.optOutFromFilters) } + + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox( + checked = checked, + onCheckedChange = { + checked = it + accountViewModel.account.updateOptOutFromFilters(checked) + LocalPreferences.saveToEncryptedStorage(accountViewModel.account) + } + ) + + Text(stringResource(R.string.opt_out_from_filters)) + } TabRow( backgroundColor = MaterialTheme.colors.background, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5666371e1..9aa32df18 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -412,4 +412,5 @@ Always show content warnings Recommends: + Opt-out from automatic filters From 8f668cb5fba3a09bb6c100bbda2c856f81a536f2 Mon Sep 17 00:00:00 2001 From: greenart7c3 <115044884+greenart7c3@users.noreply.github.com> Date: Mon, 12 Jun 2023 07:38:32 -0300 Subject: [PATCH 2/3] separated spam and report filter options --- .../amethyst/LocalPreferences.kt | 12 ++++++---- .../amethyst/OptOutFromFilters.kt | 8 ++++--- .../vitorpamplona/amethyst/ServiceManager.kt | 2 +- .../vitorpamplona/amethyst/model/Account.kt | 17 ++++++++----- .../amethyst/model/AntiSpamFilter.kt | 2 +- .../ui/screen/loggedIn/HiddenUsersScreen.kt | 24 +++++++++++++++---- app/src/main/res/values/strings.xml | 3 ++- 7 files changed, 47 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt b/app/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt index 8c539ab7f..5e85af2d2 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/LocalPreferences.kt @@ -61,7 +61,8 @@ private object PrefKeys { const val USE_PROXY = "use_proxy" const val PROXY_PORT = "proxy_port" const val SHOW_SENSITIVE_CONTENT = "show_sensitive_content" - const val OPT_OUT_FILTERS = "opt_out_filters" + const val WARN_ABOUT_REPORTS = "warn_about_reports" + const val FILTER_SPAM_FROM_STRANGERS = "filter_spam_from_strangers" val LAST_READ: (String) -> String = { route -> "last_read_route_$route" } } @@ -218,7 +219,8 @@ object LocalPreferences { putBoolean(PrefKeys.HIDE_BLOCK_ALERT_DIALOG, account.hideBlockAlertDialog) putBoolean(PrefKeys.USE_PROXY, account.proxy != null) putInt(PrefKeys.PROXY_PORT, account.proxyPort) - putBoolean(PrefKeys.OPT_OUT_FILTERS, account.optOutFromFilters) + putBoolean(PrefKeys.WARN_ABOUT_REPORTS, account.warnAboutPostsWithReports) + putBoolean(PrefKeys.FILTER_SPAM_FROM_STRANGERS, account.filterSpamFromStrangers) if (account.showSensitiveContent == null) { remove(PrefKeys.SHOW_SENSITIVE_CONTENT) @@ -308,7 +310,8 @@ object LocalPreferences { } else { null } - val optOutFromFilters = getBoolean(PrefKeys.OPT_OUT_FILTERS, false) + val filterSpam = getBoolean(PrefKeys.FILTER_SPAM_FROM_STRANGERS, true) + val warnAboutReports = getBoolean(PrefKeys.WARN_ABOUT_REPORTS, true) val a = Account( Persona(privKey = privKey?.hexToByteArray(), pubKey = pubKey.hexToByteArray()), @@ -331,7 +334,8 @@ object LocalPreferences { proxy, proxyPort, showSensitiveContent, - optOutFromFilters + warnAboutReports, + filterSpam ) return a diff --git a/app/src/main/java/com/vitorpamplona/amethyst/OptOutFromFilters.kt b/app/src/main/java/com/vitorpamplona/amethyst/OptOutFromFilters.kt index 736be2916..1a2ffcb3f 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/OptOutFromFilters.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/OptOutFromFilters.kt @@ -1,9 +1,11 @@ package com.vitorpamplona.amethyst object OptOutFromFilters { - var optOutFromFilters: Boolean = false + var warnAboutPostsWithReports: Boolean = true + var filterSpamFromStrangers: Boolean = true - fun start(optOut: Boolean) { - optOutFromFilters = optOut + fun start(warnAboutReports: Boolean, filterSpam: Boolean) { + warnAboutPostsWithReports = warnAboutReports + filterSpamFromStrangers = filterSpam } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ServiceManager.kt b/app/src/main/java/com/vitorpamplona/amethyst/ServiceManager.kt index a85741a2e..ecccd9ca9 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ServiceManager.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ServiceManager.kt @@ -36,7 +36,7 @@ object ServiceManager { // Resets Proxy Use HttpClient.start(account) - OptOutFromFilters.start(account?.optOutFromFilters ?: false) + OptOutFromFilters.start(account?.warnAboutPostsWithReports ?: true, account?.filterSpamFromStrangers ?: true) Coil.setImageLoader { ImageLoader.Builder(context).components { if (Build.VERSION.SDK_INT >= 28) { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index 3205b8009..87a98cbb4 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -68,7 +68,8 @@ class Account( var proxy: Proxy?, var proxyPort: Int, var showSensitiveContent: Boolean? = null, - var optOutFromFilters: Boolean = false + var warnAboutPostsWithReports: Boolean = true, + var filterSpamFromStrangers: Boolean = true ) { var transientHiddenUsers: Set = setOf() @@ -79,9 +80,13 @@ class Account( var userProfileCache: User? = null - fun updateOptOutFromFilters(value: Boolean) { - optOutFromFilters = value - OptOutFromFilters.start(optOutFromFilters) + fun updateOptOutOptions(warnReports: Boolean, filterSpam: Boolean) { + warnAboutPostsWithReports = warnReports + filterSpamFromStrangers = filterSpam + OptOutFromFilters.start(warnAboutPostsWithReports, filterSpamFromStrangers) + if (!filterSpamFromStrangers) { + transientHiddenUsers = setOf() + } live.invalidateData() saveable.invalidateData() } @@ -1066,7 +1071,7 @@ class Account( } fun isAcceptable(user: User): Boolean { - if (optOutFromFilters) { + if (!warnAboutPostsWithReports) { return !isHidden(user) && // if user hasn't hided this author user.reportsBy(userProfile()).isEmpty() // if user has not reported this post } @@ -1076,7 +1081,7 @@ class Account( } fun isAcceptableDirect(note: Note): Boolean { - if (optOutFromFilters) { + if (!warnAboutPostsWithReports) { return note.reportsBy(userProfile()).isEmpty() } return note.reportsBy(userProfile()).isEmpty() && // if user has not reported this post diff --git a/app/src/main/java/com/vitorpamplona/amethyst/model/AntiSpamFilter.kt b/app/src/main/java/com/vitorpamplona/amethyst/model/AntiSpamFilter.kt index 4686bcfb5..cce464c9f 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/model/AntiSpamFilter.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/model/AntiSpamFilter.kt @@ -21,7 +21,7 @@ class AntiSpamFilter { fun isSpam(event: Event, relay: Relay?): Boolean { checkNotInMainThread() - if (OptOutFromFilters.optOutFromFilters) return false + if (!OptOutFromFilters.filterSpamFromStrangers) return false val idHex = event.id diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HiddenUsersScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HiddenUsersScreen.kt index 9a2d84954..bacd226d5 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HiddenUsersScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/HiddenUsersScreen.kt @@ -74,19 +74,33 @@ fun HiddenUsersScreen( Column(modifier = Modifier.padding(start = 10.dp, end = 10.dp)) { val pagerState = rememberPagerState() val coroutineScope = rememberCoroutineScope() - var checked by remember { mutableStateOf(accountViewModel.account.optOutFromFilters) } + var warnAboutReports by remember { mutableStateOf(accountViewModel.account.warnAboutPostsWithReports) } + var filterSpam by remember { mutableStateOf(accountViewModel.account.filterSpamFromStrangers) } Row(verticalAlignment = Alignment.CenterVertically) { Checkbox( - checked = checked, + checked = warnAboutReports, onCheckedChange = { - checked = it - accountViewModel.account.updateOptOutFromFilters(checked) + warnAboutReports = it + accountViewModel.account.updateOptOutOptions(warnAboutReports, filterSpam) LocalPreferences.saveToEncryptedStorage(accountViewModel.account) } ) - Text(stringResource(R.string.opt_out_from_filters)) + Text(stringResource(R.string.warn_when_posts_have_reports_from_your_follows)) + } + + Row(verticalAlignment = Alignment.CenterVertically) { + Checkbox( + checked = filterSpam, + onCheckedChange = { + filterSpam = it + accountViewModel.account.updateOptOutOptions(warnAboutReports, filterSpam) + LocalPreferences.saveToEncryptedStorage(accountViewModel.account) + } + ) + + Text(stringResource(R.string.filter_spam_from_strangers)) } TabRow( diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 9aa32df18..c2deff8ad 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -412,5 +412,6 @@ Always show content warnings Recommends: - Opt-out from automatic filters + Filter spam from strangers + Warn when posts have reports from your follows From 97964ed65c6ca6525df3f158c5ec508499fd5789 Mon Sep 17 00:00:00 2001 From: Licaon_Kter Date: Mon, 12 Jun 2023 15:04:25 +0000 Subject: [PATCH 3/3] Typo fix --- PRIVACY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PRIVACY.md b/PRIVACY.md index b1efccfd7..a63c9a218 100644 --- a/PRIVACY.md +++ b/PRIVACY.md @@ -39,7 +39,7 @@ You cannot use the Amethyst app for Android to submit Objectionable Content to r ### For versions downloaded from F-Droid -We do not control the distribution of the application in F-droid. Legal matters should be resolved between the user and F-droid. +We do not control the distribution of the application in F-Droid. Legal matters should be resolved between the user and F-Droid. ## Other Notes