diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt index 870271d18..147cca8c3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -525,6 +525,14 @@ class Account( return false } + suspend fun updateReportWarningThreshold(threshold: Int): Boolean { + if (settings.updateReportWarningThreshold(threshold.coerceAtLeast(1))) { + sendNewAppSpecificData() + return true + } + return false + } + suspend fun updateSendKind0EventsToLocalRelay(send: Boolean): Boolean { if (settings.changeSendKind0EventsToLocalRelay(send)) { sendNewAppSpecificData() @@ -2905,7 +2913,7 @@ class Account( return true } - if (!settings.syncedSettings.security.warnAboutPostsWithReports) { + if (!settings.syncedSettings.security.warnAboutPostsWithReports.value) { if (isHidden(user)) return false val reports = user.reportsOrNull() ?: return true @@ -2916,20 +2924,22 @@ class Account( if (isHidden(user)) return false val reports = user.reportsOrNull() ?: return true + val reportWarningThreshold = settings.syncedSettings.security.reportWarningThreshold.value.coerceAtLeast(1) // if user hasn't hided this author return reports.reportsBy(userProfile()).isEmpty() && // if user has not reported this post - reports.countReportAuthorsBy(followingKeySet()) < 5 + reports.countReportAuthorsBy(followingKeySet()) < reportWarningThreshold } private fun isAcceptableDirect(note: Note): Boolean { - if (!settings.syncedSettings.security.warnAboutPostsWithReports) { + if (!settings.syncedSettings.security.warnAboutPostsWithReports.value) { return !note.hasReportsBy(userProfile()) } + val reportWarningThreshold = settings.syncedSettings.security.reportWarningThreshold.value.coerceAtLeast(1) return !note.hasReportsBy(userProfile()) && // if user has not reported this post - note.countReportAuthorsBy(followingKeySet()) < 5 // if it has 5 reports by reliable users + note.countReportAuthorsBy(followingKeySet()) < reportWarningThreshold } fun isDecryptedContentHidden(noteEvent: PrivateDmEvent): Boolean = diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSettings.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSettings.kt index daa4ae66e..8a61a24f3 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSettings.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSettings.kt @@ -1083,6 +1083,14 @@ class AccountSettings( false } + fun updateReportWarningThreshold(threshold: Int): Boolean = + if (syncedSettings.security.updateReportWarningThreshold(threshold)) { + saveAccountSettings() + true + } else { + false + } + fun updateFilterSpam(filterSpam: Boolean): Boolean = if (syncedSettings.security.updateFilterSpam(filterSpam)) { saveAccountSettings() diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSyncedSettings.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSyncedSettings.kt index 9d3721b3b..540106e85 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSyncedSettings.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSyncedSettings.kt @@ -51,7 +51,8 @@ class AccountSyncedSettings( val security = AccountSecurityPreferences( MutableStateFlow(internalSettings.security.showSensitiveContent), - internalSettings.security.warnAboutPostsWithReports, + MutableStateFlow(internalSettings.security.warnAboutPostsWithReports), + MutableStateFlow(internalSettings.security.reportWarningThreshold), MutableStateFlow(internalSettings.security.filterSpamFromStrangers), MutableStateFlow(internalSettings.security.maxHashtagLimit), MutableStateFlow(internalSettings.security.sendKind0EventsToLocalRelay), @@ -79,7 +80,8 @@ class AccountSyncedSettings( security = AccountSecurityPreferencesInternal( security.showSensitiveContent.value, - security.warnAboutPostsWithReports, + security.warnAboutPostsWithReports.value, + security.reportWarningThreshold.value, security.filterSpamFromStrangers.value, security.maxHashtagLimit.value, security.sendKind0EventsToLocalRelay.value, @@ -128,8 +130,12 @@ class AccountSyncedSettings( security.filterSpamFromStrangers.tryEmit(syncedSettingsInternal.security.filterSpamFromStrangers) } - if (security.warnAboutPostsWithReports != syncedSettingsInternal.security.warnAboutPostsWithReports) { - security.warnAboutPostsWithReports = syncedSettingsInternal.security.warnAboutPostsWithReports + if (security.warnAboutPostsWithReports.value != syncedSettingsInternal.security.warnAboutPostsWithReports) { + security.warnAboutPostsWithReports.tryEmit(syncedSettingsInternal.security.warnAboutPostsWithReports) + } + + if (security.reportWarningThreshold.value != syncedSettingsInternal.security.reportWarningThreshold) { + security.reportWarningThreshold.tryEmit(syncedSettingsInternal.security.reportWarningThreshold) } if (security.maxHashtagLimit.value != syncedSettingsInternal.security.maxHashtagLimit) { @@ -235,7 +241,8 @@ class AccountLanguagePreferences( @Stable class AccountSecurityPreferences( val showSensitiveContent: MutableStateFlow = MutableStateFlow(null), - var warnAboutPostsWithReports: Boolean = true, + val warnAboutPostsWithReports: MutableStateFlow = MutableStateFlow(true), + val reportWarningThreshold: MutableStateFlow = MutableStateFlow(DefaultReportWarningThreshold), var filterSpamFromStrangers: MutableStateFlow = MutableStateFlow(true), val maxHashtagLimit: MutableStateFlow = MutableStateFlow(5), var sendKind0EventsToLocalRelay: MutableStateFlow = MutableStateFlow(false), @@ -250,8 +257,16 @@ class AccountSecurityPreferences( } fun updateWarnReports(warnReports: Boolean): Boolean = - if (warnAboutPostsWithReports != warnReports) { - warnAboutPostsWithReports = warnReports + if (warnAboutPostsWithReports.value != warnReports) { + warnAboutPostsWithReports.tryEmit(warnReports) + true + } else { + false + } + + fun updateReportWarningThreshold(threshold: Int): Boolean = + if (reportWarningThreshold.value != threshold) { + reportWarningThreshold.update { threshold } true } else { false diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSyncedSettingsInternal.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSyncedSettingsInternal.kt index a272bfe02..59469a96d 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSyncedSettingsInternal.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSyncedSettingsInternal.kt @@ -38,6 +38,7 @@ val DefaultReactions = ) val DefaultZapAmounts = listOf(100L, 500L, 1000L) +val DefaultReportWarningThreshold = 5 @Serializable enum class ReactionRowAction { @@ -144,6 +145,7 @@ class AccountLanguagePreferencesInternal( class AccountSecurityPreferencesInternal( val showSensitiveContent: Boolean? = null, var warnAboutPostsWithReports: Boolean = true, + val reportWarningThreshold: Int = DefaultReportWarningThreshold, var filterSpamFromStrangers: Boolean = true, val maxHashtagLimit: Int = 5, var sendKind0EventsToLocalRelay: Boolean = false, diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt index f2f295a76..26e4f10c5 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/AccountViewModel.kt @@ -531,7 +531,11 @@ class AccountViewModel( account.kind3FollowList.flow, note.flow().author(), note.flow().metadata.stateFlow, - note.flow().reports.stateFlow, + combine( + note.flow().reports.stateFlow, + account.settings.syncedSettings.security.warnAboutPostsWithReports, + account.settings.syncedSettings.security.reportWarningThreshold, + ) { reports, _, _ -> reports }, ) { hiddenUsers, followingUsers, _, metadata, _ -> emit(isNoteAcceptable(metadata.note, hiddenUsers, followingUsers.authors)) }.onStart { @@ -1152,6 +1156,8 @@ class AccountViewModel( fun updateWarnReports(warnReports: Boolean) = launchSigner { account.updateWarnReports(warnReports) } + fun updateReportWarningThreshold(threshold: Int) = launchSigner { account.updateReportWarningThreshold(threshold) } + fun updateDisableClientTag(disable: Boolean) = launchSigner { account.updateDisableClientTag(disable) } fun updateFilterSpam(filterSpam: Boolean) = diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SecurityFiltersScreen.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SecurityFiltersScreen.kt index ce9c37f36..fc7b2e6ca 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SecurityFiltersScreen.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/settings/SecurityFiltersScreen.kt @@ -341,12 +341,12 @@ private fun HeaderOptions(accountViewModel: AccountViewModel) { horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = spacedBy(10.dp), ) { + var warnAboutReports by remember { mutableStateOf(accountViewModel.account.settings.syncedSettings.security.warnAboutPostsWithReports.value) } + SettingsRow( R.string.warn_when_posts_have_reports_from_your_follows_title, R.string.warn_when_posts_have_reports_from_your_follows_explainer, ) { - var warnAboutReports by remember { mutableStateOf(accountViewModel.account.settings.syncedSettings.security.warnAboutPostsWithReports) } - Switch( checked = warnAboutReports, onCheckedChange = { @@ -356,6 +356,33 @@ private fun HeaderOptions(accountViewModel: AccountViewModel) { ) } + if (warnAboutReports) { + SettingsRow( + R.string.report_warning_threshold_title, + R.string.report_warning_threshold_explainer, + ) { + var reportWarningThreshold by remember { + mutableStateOf(accountViewModel.account.settings.syncedSettings.security.reportWarningThreshold.value.coerceAtLeast(1).toString()) + } + + OutlinedTextField( + value = reportWarningThreshold, + onValueChange = { + val updatedThreshold = (it.toIntOrNull() ?: 1).coerceAtLeast(1) + reportWarningThreshold = updatedThreshold.toString() + accountViewModel.updateReportWarningThreshold(updatedThreshold) + }, + keyboardOptions = + KeyboardOptions.Default.copy( + keyboardType = KeyboardType.Number, + imeAction = ImeAction.Done, + ), + singleLine = true, + modifier = Modifier.padding(start = 8.dp), + ) + } + } + SettingsRow( R.string.filter_spam_from_strangers_title, R.string.filter_spam_from_strangers_explainer, diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index d9c144df8..967a4bfe4 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -1219,7 +1219,9 @@ Don\'t add client tag to my events When enabled, Amethyst will not append a NIP-89 client tag to events you publish. Warn on reports - Shows a warning message when posts have 5 or more reports from your follows + Shows a warning message when posts or profiles have reports from your follows + Report warning threshold + Warns when posts or profiles reach this many reports from people you follow Show sensitive content Shows a warning message when the author of the post marked it as sensitive Max hashtags per post