Moves Content Sensitivity setting to the Security filter screen

This commit is contained in:
Vitor Pamplona
2025-05-13 20:09:14 -04:00
parent f3e2327676
commit d7d8f02a80
8 changed files with 160 additions and 92 deletions
@@ -1144,11 +1144,16 @@ class Account(
fun isWriteable(): Boolean = settings.isWriteable() fun isWriteable(): Boolean = settings.isWriteable()
fun updateOptOutOptions( fun updateWarnReports(warnReports: Boolean): Boolean {
warnReports: Boolean, if (settings.updateWarnReports(warnReports)) {
filterSpam: Boolean, sendNewAppSpecificData()
): Boolean { return true
if (settings.updateOptOutOptions(warnReports, filterSpam)) { }
return false
}
fun updateFilterSpam(filterSpam: Boolean): Boolean {
if (settings.updateFilterSpam(filterSpam)) {
if (!settings.syncedSettings.security.filterSpamFromStrangers.value) { if (!settings.syncedSettings.security.filterSpamFromStrangers.value) {
transientHiddenUsers.update { transientHiddenUsers.update {
emptySet() emptySet()
@@ -538,11 +538,16 @@ class AccountSettings(
return false return false
} }
fun updateOptOutOptions( fun updateWarnReports(warnReports: Boolean): Boolean =
warnReports: Boolean, if (syncedSettings.security.updateWarnReports(warnReports)) {
filterSpam: Boolean, saveAccountSettings()
): Boolean = true
if (syncedSettings.security.updateOptOutOptions(warnReports, filterSpam)) { } else {
false
}
fun updateFilterSpam(filterSpam: Boolean): Boolean =
if (syncedSettings.security.updateFilterSpam(filterSpam)) {
saveAccountSettings() saveAccountSettings()
true true
} else { } else {
@@ -190,17 +190,17 @@ class AccountSecurityPreferences(
return false return false
} }
// --- fun updateWarnReports(warnReports: Boolean): Boolean =
// filters if (warnAboutPostsWithReports != warnReports) {
// ---
fun updateOptOutOptions(
warnReports: Boolean,
filterSpam: Boolean,
): Boolean =
if (warnAboutPostsWithReports != warnReports || filterSpam != filterSpamFromStrangers.value) {
warnAboutPostsWithReports = warnReports warnAboutPostsWithReports = warnReports
filterSpamFromStrangers.tryEmit(filterSpam) true
} else {
false
}
fun updateFilterSpam(filterSpam: Boolean): Boolean =
if (filterSpam != filterSpamFromStrangers.value) {
filterSpamFromStrangers.tryEmit(filterSpam)
true true
} else { } else {
false false
@@ -149,3 +149,33 @@ fun parseBooleanType(screenCode: Int): BooleanType =
BooleanType.ALWAYS 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
}
}
@@ -324,34 +324,6 @@ fun NoteDropDownMenu(
) )
} }
HorizontalDivider(thickness = DividerThickness) 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) { if (state.isLoggedUser) {
DropdownMenuItem( DropdownMenuItem(
text = { Text(stringRes(R.string.request_deletion)) }, text = { Text(stringRes(R.string.request_deletion)) },
@@ -52,6 +52,7 @@ import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.PublicChatChannel import com.vitorpamplona.amethyst.model.PublicChatChannel
import com.vitorpamplona.amethyst.model.UrlCachedPreviewer import com.vitorpamplona.amethyst.model.UrlCachedPreviewer
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
import com.vitorpamplona.amethyst.model.WarningType
import com.vitorpamplona.amethyst.model.observables.CreatedAtComparator import com.vitorpamplona.amethyst.model.observables.CreatedAtComparator
import com.vitorpamplona.amethyst.service.CashuProcessor import com.vitorpamplona.amethyst.service.CashuProcessor
import com.vitorpamplona.amethyst.service.CashuToken import com.vitorpamplona.amethyst.service.CashuToken
@@ -886,21 +887,9 @@ class AccountViewModel(
fun isFollowing(user: HexKey): Boolean = account.isFollowing(user) fun isFollowing(user: HexKey): Boolean = account.isFollowing(user)
fun hideSensitiveContent() { fun updateContentWarnings(value: WarningType) {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
account.updateShowSensitiveContent(false) account.updateShowSensitiveContent(value.prefCode)
}
}
fun disableContentWarnings() {
viewModelScope.launch(Dispatchers.IO) {
account.updateShowSensitiveContent(true)
}
}
fun seeContentWarnings() {
viewModelScope.launch(Dispatchers.IO) {
account.updateShowSensitiveContent(null)
} }
} }
@@ -924,12 +913,15 @@ class AccountViewModel(
fun filterSpamFromStrangers() = account.settings.syncedSettings.security.filterSpamFromStrangers fun filterSpamFromStrangers() = account.settings.syncedSettings.security.filterSpamFromStrangers
fun updateOptOutOptions( fun updateWarnReports(warnReports: Boolean) {
warnReports: Boolean,
filterSpam: Boolean,
) {
viewModelScope.launch(Dispatchers.IO) { 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 LocalCache.antiSpam.active = filterSpamFromStrangers().value
} }
} }
@@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.settings
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Arrangement 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.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight 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.foundation.text.KeyboardOptions
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Checkbox
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.ScrollableTabRow import androidx.compose.material3.ScrollableTabRow
import androidx.compose.material3.Switch
import androidx.compose.material3.Tab import androidx.compose.material3.Tab
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -64,6 +65,8 @@ import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel import androidx.lifecycle.viewmodel.compose.viewModel
import com.vitorpamplona.amethyst.R 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.service.relayClient.reqCommand.account.observeAccountIsHiddenWord
import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold 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.note.elements.AddButton
import com.vitorpamplona.amethyst.ui.screen.RefreshingFeedUserFeedView import com.vitorpamplona.amethyst.ui.screen.RefreshingFeedUserFeedView
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel 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.HiddenAccountsFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.dal.HiddenWordsFeedViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.dal.HiddenWordsFeedViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.settings.dal.SpammerAccountsFeedViewModel 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.ButtonPadding
import com.vitorpamplona.amethyst.ui.theme.DividerThickness import com.vitorpamplona.amethyst.ui.theme.DividerThickness
import com.vitorpamplona.amethyst.ui.theme.HorzPadding 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.StdPadding
import com.vitorpamplona.amethyst.ui.theme.TabRowHeight import com.vitorpamplona.amethyst.ui.theme.TabRowHeight
import com.vitorpamplona.amethyst.ui.theme.placeholderText import com.vitorpamplona.amethyst.ui.theme.placeholderText
import kotlinx.collections.immutable.persistentListOf
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
@@ -153,35 +161,17 @@ fun SecurityFiltersScreen(
}, },
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
) { ) {
Column(Modifier.padding(it).fillMaxHeight()) { Column(
Modifier
.padding(it)
.fillMaxHeight(),
) {
val pagerState = rememberPagerState { 3 } val pagerState = rememberPagerState { 3 }
val coroutineScope = rememberCoroutineScope() 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) { HeaderOptions(accountViewModel)
Checkbox(
checked = warnAboutReports,
onCheckedChange = {
warnAboutReports = it
accountViewModel.updateOptOutOptions(warnAboutReports, filterSpam)
},
)
Text(stringRes(R.string.warn_when_posts_have_reports_from_your_follows)) HorizontalDivider()
}
Row(verticalAlignment = Alignment.CenterVertically) {
Checkbox(
checked = filterSpam,
onCheckedChange = {
filterSpam = it
accountViewModel.updateOptOutOptions(warnAboutReports, filterSpam)
},
)
Text(stringRes(R.string.filter_spam_from_strangers))
}
ScrollableTabRow( ScrollableTabRow(
containerColor = MaterialTheme.colorScheme.background, 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 @Composable
private fun HiddenWordsFeed( private fun HiddenWordsFeed(
hiddenWordsViewModel: HiddenWordsFeedViewModel, hiddenWordsViewModel: HiddenWordsFeedViewModel,
+11
View File
@@ -585,10 +585,21 @@
<string name="content_warning_show_all_sensitive_content">Always show sensitive content</string> <string name="content_warning_show_all_sensitive_content">Always show sensitive content</string>
<string name="content_warning_see_warnings">Always show content warnings</string> <string name="content_warning_see_warnings">Always show content warnings</string>
<string name="content_warning_hide_all_sensitive_content_option">Hide</string>
<string name="content_warning_show_all_sensitive_content_option">Show</string>
<string name="content_warning_see_warnings_option">Warn</string>
<string name="recommended_apps">Recommends: </string> <string name="recommended_apps">Recommends: </string>
<string name="filter_spam_from_strangers">Filter spam from strangers</string> <string name="filter_spam_from_strangers">Filter spam from strangers</string>
<string name="warn_when_posts_have_reports_from_your_follows">Warn when posts have reports from your follows</string> <string name="warn_when_posts_have_reports_from_your_follows">Warn when posts have reports from your follows</string>
<string name="filter_spam_from_strangers_title">Filter spam</string>
<string name="filter_spam_from_strangers_explainer">Hides posts from strangers that were exactly the same for 5 or more times</string>
<string name="warn_when_posts_have_reports_from_your_follows_title">Warn on reports</string>
<string name="warn_when_posts_have_reports_from_your_follows_explainer">Shows a warning message when posts have 5 or more reports your follows</string>
<string name="show_sensitive_content_title">Show sensitive content</string>
<string name="show_sensitive_content_explainer">Shows a warning message when the author of the post marked it as sensitive</string>
<string name="new_reaction_symbol">New Reaction Symbol</string> <string name="new_reaction_symbol">New Reaction Symbol</string>
<string name="no_reaction_type_setup_long_press_to_change">No reaction types pre-selected for this user. Long press on the heart button to change</string> <string name="no_reaction_type_setup_long_press_to_change">No reaction types pre-selected for this user. Long press on the heart button to change</string>