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 ca2657137..655ef6cfa 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Account.kt @@ -1144,11 +1144,16 @@ class Account( fun isWriteable(): Boolean = settings.isWriteable() - fun updateOptOutOptions( - warnReports: Boolean, - filterSpam: Boolean, - ): Boolean { - if (settings.updateOptOutOptions(warnReports, filterSpam)) { + fun updateWarnReports(warnReports: Boolean): Boolean { + if (settings.updateWarnReports(warnReports)) { + sendNewAppSpecificData() + return true + } + return false + } + + fun updateFilterSpam(filterSpam: Boolean): Boolean { + if (settings.updateFilterSpam(filterSpam)) { if (!settings.syncedSettings.security.filterSpamFromStrangers.value) { transientHiddenUsers.update { emptySet() 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 a901ab19d..a913cc07f 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSettings.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSettings.kt @@ -538,11 +538,16 @@ class AccountSettings( return false } - fun updateOptOutOptions( - warnReports: Boolean, - filterSpam: Boolean, - ): Boolean = - if (syncedSettings.security.updateOptOutOptions(warnReports, filterSpam)) { + fun updateWarnReports(warnReports: Boolean): Boolean = + if (syncedSettings.security.updateWarnReports(warnReports)) { + saveAccountSettings() + true + } else { + false + } + + fun updateFilterSpam(filterSpam: Boolean): Boolean = + if (syncedSettings.security.updateFilterSpam(filterSpam)) { saveAccountSettings() true } else { 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 7aa0fef4b..58facacd4 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSyncedSettings.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/AccountSyncedSettings.kt @@ -190,17 +190,17 @@ class AccountSecurityPreferences( return false } - // --- - // filters - // --- - fun updateOptOutOptions( - warnReports: Boolean, - filterSpam: Boolean, - ): Boolean = - if (warnAboutPostsWithReports != warnReports || filterSpam != filterSpamFromStrangers.value) { + fun updateWarnReports(warnReports: Boolean): Boolean = + if (warnAboutPostsWithReports != warnReports) { warnAboutPostsWithReports = warnReports - filterSpamFromStrangers.tryEmit(filterSpam) + true + } else { + false + } + fun updateFilterSpam(filterSpam: Boolean): Boolean = + if (filterSpam != filterSpamFromStrangers.value) { + filterSpamFromStrangers.tryEmit(filterSpam) true } else { false diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Settings.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Settings.kt index cc722dc75..b00ffdbae 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Settings.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/model/Settings.kt @@ -149,3 +149,33 @@ fun parseBooleanType(screenCode: Int): BooleanType = BooleanType.ALWAYS } } + +enum class WarningType( + val prefCode: Boolean?, + val screenCode: Int, + val resourceId: Int, +) { + WARN(null, 0, R.string.content_warning_see_warnings_option), + SHOW(true, 1, R.string.content_warning_show_all_sensitive_content_option), + HIDE(false, 2, R.string.content_warning_hide_all_sensitive_content_option), +} + +fun parseWarningType(screenCode: Int): WarningType = + when (screenCode) { + WarningType.WARN.screenCode -> WarningType.WARN + WarningType.SHOW.screenCode -> WarningType.SHOW + WarningType.HIDE.screenCode -> WarningType.HIDE + else -> { + WarningType.WARN + } + } + +fun parseWarningType(code: Boolean?): WarningType = + when (code) { + WarningType.WARN.prefCode -> WarningType.WARN + WarningType.HIDE.prefCode -> WarningType.HIDE + WarningType.SHOW.prefCode -> WarningType.SHOW + else -> { + WarningType.WARN + } + } diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DropDownMenu.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DropDownMenu.kt index 10b77f9db..c93d892d8 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DropDownMenu.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/note/elements/DropDownMenu.kt @@ -324,34 +324,6 @@ fun NoteDropDownMenu( ) } HorizontalDivider(thickness = DividerThickness) - if (state.showSensitiveContent == null || state.showSensitiveContent == true) { - DropdownMenuItem( - text = { Text(stringRes(R.string.content_warning_hide_all_sensitive_content)) }, - onClick = { - accountViewModel.hideSensitiveContent() - onDismiss() - }, - ) - } - if (state.showSensitiveContent == null || state.showSensitiveContent == false) { - DropdownMenuItem( - text = { Text(stringRes(R.string.content_warning_show_all_sensitive_content)) }, - onClick = { - accountViewModel.disableContentWarnings() - onDismiss() - }, - ) - } - if (state.showSensitiveContent != null) { - DropdownMenuItem( - text = { Text(stringRes(R.string.content_warning_see_warnings)) }, - onClick = { - accountViewModel.seeContentWarnings() - onDismiss() - }, - ) - } - HorizontalDivider(thickness = DividerThickness) if (state.isLoggedUser) { DropdownMenuItem( text = { Text(stringRes(R.string.request_deletion)) }, 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 6b60abcfb..9fbd8ebd3 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 @@ -52,6 +52,7 @@ import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.PublicChatChannel import com.vitorpamplona.amethyst.model.UrlCachedPreviewer import com.vitorpamplona.amethyst.model.User +import com.vitorpamplona.amethyst.model.WarningType import com.vitorpamplona.amethyst.model.observables.CreatedAtComparator import com.vitorpamplona.amethyst.service.CashuProcessor import com.vitorpamplona.amethyst.service.CashuToken @@ -886,21 +887,9 @@ class AccountViewModel( fun isFollowing(user: HexKey): Boolean = account.isFollowing(user) - fun hideSensitiveContent() { + fun updateContentWarnings(value: WarningType) { viewModelScope.launch(Dispatchers.IO) { - account.updateShowSensitiveContent(false) - } - } - - fun disableContentWarnings() { - viewModelScope.launch(Dispatchers.IO) { - account.updateShowSensitiveContent(true) - } - } - - fun seeContentWarnings() { - viewModelScope.launch(Dispatchers.IO) { - account.updateShowSensitiveContent(null) + account.updateShowSensitiveContent(value.prefCode) } } @@ -924,12 +913,15 @@ class AccountViewModel( fun filterSpamFromStrangers() = account.settings.syncedSettings.security.filterSpamFromStrangers - fun updateOptOutOptions( - warnReports: Boolean, - filterSpam: Boolean, - ) { + fun updateWarnReports(warnReports: Boolean) { viewModelScope.launch(Dispatchers.IO) { - if (account.updateOptOutOptions(warnReports, filterSpam)) { + account.updateWarnReports(warnReports) + } + } + + fun updateFilterSpam(filterSpam: Boolean) { + viewModelScope.launch(Dispatchers.IO) { + if (account.updateFilterSpam(filterSpam)) { LocalCache.antiSpam.active = filterSpamFromStrangers().value } } 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 751161ed4..f86d72bcb 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 @@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.settings import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Arrangement.Absolute.spacedBy import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight @@ -33,11 +34,11 @@ import androidx.compose.foundation.text.KeyboardActions import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Button import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Checkbox import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.ScrollableTabRow +import androidx.compose.material3.Switch import androidx.compose.material3.Tab import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -64,6 +65,8 @@ import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.vitorpamplona.amethyst.R +import com.vitorpamplona.amethyst.model.WarningType +import com.vitorpamplona.amethyst.model.parseWarningType import com.vitorpamplona.amethyst.service.relayClient.reqCommand.account.observeAccountIsHiddenWord import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold @@ -72,6 +75,8 @@ import com.vitorpamplona.amethyst.ui.navigation.TopBarWithBackButton import com.vitorpamplona.amethyst.ui.note.elements.AddButton import com.vitorpamplona.amethyst.ui.screen.RefreshingFeedUserFeedView import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel +import com.vitorpamplona.amethyst.ui.screen.loggedIn.TextSpinner +import com.vitorpamplona.amethyst.ui.screen.loggedIn.TitleExplainer import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.dal.HiddenAccountsFeedViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.dal.HiddenWordsFeedViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.dal.SpammerAccountsFeedViewModel @@ -80,9 +85,12 @@ import com.vitorpamplona.amethyst.ui.theme.ButtonBorder import com.vitorpamplona.amethyst.ui.theme.ButtonPadding import com.vitorpamplona.amethyst.ui.theme.DividerThickness import com.vitorpamplona.amethyst.ui.theme.HorzPadding +import com.vitorpamplona.amethyst.ui.theme.Size10dp +import com.vitorpamplona.amethyst.ui.theme.Size15dp import com.vitorpamplona.amethyst.ui.theme.StdPadding import com.vitorpamplona.amethyst.ui.theme.TabRowHeight import com.vitorpamplona.amethyst.ui.theme.placeholderText +import kotlinx.collections.immutable.persistentListOf import kotlinx.coroutines.launch @Composable @@ -153,35 +161,17 @@ fun SecurityFiltersScreen( }, accountViewModel = accountViewModel, ) { - Column(Modifier.padding(it).fillMaxHeight()) { + Column( + Modifier + .padding(it) + .fillMaxHeight(), + ) { val pagerState = rememberPagerState { 3 } val coroutineScope = rememberCoroutineScope() - var warnAboutReports by remember { mutableStateOf(accountViewModel.account.settings.syncedSettings.security.warnAboutPostsWithReports) } - var filterSpam by remember { mutableStateOf(accountViewModel.account.settings.syncedSettings.security.filterSpamFromStrangers.value) } - Row(verticalAlignment = Alignment.CenterVertically) { - Checkbox( - checked = warnAboutReports, - onCheckedChange = { - warnAboutReports = it - accountViewModel.updateOptOutOptions(warnAboutReports, filterSpam) - }, - ) + HeaderOptions(accountViewModel) - Text(stringRes(R.string.warn_when_posts_have_reports_from_your_follows)) - } - - Row(verticalAlignment = Alignment.CenterVertically) { - Checkbox( - checked = filterSpam, - onCheckedChange = { - filterSpam = it - accountViewModel.updateOptOutOptions(warnAboutReports, filterSpam) - }, - ) - - Text(stringRes(R.string.filter_spam_from_strangers)) - } + HorizontalDivider() ScrollableTabRow( containerColor = MaterialTheme.colorScheme.background, @@ -218,6 +208,69 @@ fun SecurityFiltersScreen( } } +@Composable +private fun HeaderOptions(accountViewModel: AccountViewModel) { + Column( + Modifier + .padding(top = Size10dp, bottom = Size10dp, start = Size15dp, end = Size15dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = spacedBy(10.dp), + ) { + 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 = { + warnAboutReports = it + accountViewModel.updateWarnReports(warnAboutReports) + }, + ) + } + + SettingsRow( + R.string.filter_spam_from_strangers_title, + R.string.filter_spam_from_strangers_explainer, + ) { + var filterSpam by remember { mutableStateOf(accountViewModel.account.settings.syncedSettings.security.filterSpamFromStrangers.value) } + + Switch( + checked = filterSpam, + onCheckedChange = { + filterSpam = it + accountViewModel.updateFilterSpam(filterSpam) + }, + ) + } + + SettingsRow( + R.string.show_sensitive_content_title, + R.string.show_sensitive_content_explainer, + ) { + var sensitive by remember { mutableStateOf(accountViewModel.account.settings.syncedSettings.security.showSensitiveContent.value) } + + val selectedItens = + persistentListOf( + TitleExplainer(stringRes(WarningType.WARN.resourceId)), + TitleExplainer(stringRes(WarningType.SHOW.resourceId)), + TitleExplainer(stringRes(WarningType.HIDE.resourceId)), + ) + + TextSpinner( + label = "", + placeholder = selectedItens[parseWarningType(sensitive).screenCode].title, + options = selectedItens, + onSelect = { + accountViewModel.updateContentWarnings(parseWarningType(it)) + }, + ) + } + } +} + @Composable private fun HiddenWordsFeed( hiddenWordsViewModel: HiddenWordsFeedViewModel, diff --git a/amethyst/src/main/res/values/strings.xml b/amethyst/src/main/res/values/strings.xml index 5646d9b57..bca4d2e09 100644 --- a/amethyst/src/main/res/values/strings.xml +++ b/amethyst/src/main/res/values/strings.xml @@ -585,10 +585,21 @@ Always show sensitive content Always show content warnings + Hide + Show + Warn + Recommends: Filter spam from strangers Warn when posts have reports from your follows + Filter spam + Hides posts from strangers that were exactly the same for 5 or more times + Warn on reports + Shows a warning message when posts have 5 or more reports your follows + Show sensitive content + Shows a warning message when the author of the post marked it as sensitive + New Reaction Symbol No reaction types pre-selected for this user. Long press on the heart button to change