Display content warning reason

This commit is contained in:
Vitor Pamplona
2026-03-11 14:10:55 -04:00
parent a4010189cd
commit ffe5e14b2b
6 changed files with 71 additions and 31 deletions
@@ -60,6 +60,7 @@ import com.vitorpamplona.amethyst.ui.theme.ButtonPadding
import com.vitorpamplona.amethyst.ui.theme.PaddingHorizontal12Modifier import com.vitorpamplona.amethyst.ui.theme.PaddingHorizontal12Modifier
import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn import com.vitorpamplona.amethyst.ui.theme.ThemeComparisonColumn
import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarningReason
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitiveOrNSFW import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitiveOrNSFW
@Composable @Composable
@@ -79,17 +80,9 @@ fun SensitivityWarning(
) { ) {
val hasSensitiveContent = remember(event) { event.isSensitiveOrNSFW() } val hasSensitiveContent = remember(event) { event.isSensitiveOrNSFW() }
SensitivityWarning(hasSensitiveContent, accountViewModel, content)
}
@Composable
fun SensitivityWarning(
hasSensitiveContent: Boolean,
accountViewModel: AccountViewModel,
content: @Composable () -> Unit,
) {
if (hasSensitiveContent) { if (hasSensitiveContent) {
SensitivityWarning(accountViewModel, content) val reason = remember(event) { event.contentWarningReason() }
ObserveSensitivityWarning(reason, accountViewModel, content)
} else { } else {
content() content()
} }
@@ -97,6 +90,20 @@ fun SensitivityWarning(
@Composable @Composable
fun SensitivityWarning( fun SensitivityWarning(
reason: String?,
accountViewModel: AccountViewModel,
content: @Composable () -> Unit,
) {
if (reason != null) {
ObserveSensitivityWarning(reason, accountViewModel, content)
} else {
content()
}
}
@Composable
fun ObserveSensitivityWarning(
reason: String?,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
content: @Composable () -> Unit, content: @Composable () -> Unit,
) { ) {
@@ -106,7 +113,7 @@ fun SensitivityWarning(
CrossfadeIfEnabled(targetState = showContentWarningNote, accountViewModel = accountViewModel) { CrossfadeIfEnabled(targetState = showContentWarningNote, accountViewModel = accountViewModel) {
if (it) { if (it) {
ContentWarningNote { showContentWarningNote = false } ContentWarningNote(reason) { showContentWarningNote = false }
} else { } else {
content() content()
} }
@@ -117,12 +124,31 @@ fun SensitivityWarning(
@Composable @Composable
fun ContentWarningNotePreview() { fun ContentWarningNotePreview() {
ThemeComparisonColumn { ThemeComparisonColumn {
ContentWarningNote {} ContentWarningNote(null) {}
}
}
@Preview
@Composable
fun ContentWarningNoteWithReasonPreview() {
ThemeComparisonColumn {
ContentWarningNote("Spoilers") {}
}
}
@Preview
@Composable
fun ContentWarningNoteWithBigReasonPreview() {
ThemeComparisonColumn {
ContentWarningNote("Spoilers, monkeys, bannanas, and other things") {}
} }
} }
@Composable @Composable
fun ContentWarningNote(onDismiss: () -> Unit) { fun ContentWarningNote(
reason: String?,
onDismiss: () -> Unit,
) {
Column { Column {
Row(modifier = PaddingHorizontal12Modifier) { Row(modifier = PaddingHorizontal12Modifier) {
Column(modifier = Modifier.padding(start = 10.dp)) { Column(modifier = Modifier.padding(start = 10.dp)) {
@@ -155,19 +181,28 @@ fun ContentWarningNote(onDismiss: () -> Unit) {
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
Text( Text(
text = stringRes(R.string.content_warning), text =
if (reason.isNullOrBlank()) {
stringRes(R.string.content_warning)
} else {
stringRes(R.string.content_warning_with_reason, reason)
},
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
fontSize = 18.sp, fontSize = 18.sp,
softWrap = true,
textAlign = TextAlign.Center,
) )
} }
Row { if (reason.isNullOrBlank()) {
Text( Row {
text = stringRes(R.string.content_warning_explanation), Text(
color = Color.Gray, text = stringRes(R.string.content_warning_explanation),
modifier = Modifier.padding(top = 10.dp), color = Color.Gray,
textAlign = TextAlign.Center, modifier = Modifier.padding(top = 10.dp),
) textAlign = TextAlign.Center,
)
}
} }
Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) { Row(Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.Center) {
@@ -140,7 +140,7 @@ fun ZoomableContentView(
when (content) { when (content) {
is MediaUrlImage -> { is MediaUrlImage -> {
SensitivityWarning(content.contentWarning != null, accountViewModel) { SensitivityWarning(content.contentWarning, accountViewModel) {
TwoSecondController(content) { controllerVisible -> TwoSecondController(content) { controllerVisible ->
val mainImageModifier = val mainImageModifier =
Modifier Modifier
@@ -153,7 +153,7 @@ fun ZoomableContentView(
} }
is MediaUrlVideo -> { is MediaUrlVideo -> {
SensitivityWarning(content.contentWarning != null, accountViewModel) { SensitivityWarning(content.contentWarning, accountViewModel) {
Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) { Box(modifier = Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
VideoView( VideoView(
videoUri = content.url, videoUri = content.url,
@@ -171,10 +171,13 @@ fun GalleryContentView(
AutoNonlazyGrid(contentList.size, modifier = Modifier.fillMaxSize()) { contentIndex -> AutoNonlazyGrid(contentList.size, modifier = Modifier.fillMaxSize()) { contentIndex ->
when (val content = contentList[contentIndex]) { when (val content = contentList[contentIndex]) {
is MediaUrlContent -> { is MediaUrlContent -> {
val hasSensitiveContent = val sensitivityReason =
(content is MediaUrlVideo && content.contentWarning != null) || when (content) {
(content is MediaUrlImage && content.contentWarning != null) is MediaUrlVideo -> content.contentWarning
SensitivityWarning(hasSensitiveContent, accountViewModel) { is MediaUrlImage -> content.contentWarning
else -> null
}
SensitivityWarning(sensitivityReason, accountViewModel) {
UrlImageView(content, accountViewModel) UrlImageView(content, accountViewModel)
} }
} }
+1
View File
@@ -743,6 +743,7 @@
<string name="today">Today</string> <string name="today">Today</string>
<string name="content_warning">Content warning</string> <string name="content_warning">Content warning</string>
<string name="content_warning_with_reason">Warning: %1$s</string>
<string name="content_warning_explanation">This post contains sensitive content which some people may find offensive or disturbing</string> <string name="content_warning_explanation">This post contains sensitive content which some people may find offensive or disturbing</string>
<string name="content_warning_hide_all_sensitive_content">Always hide sensitive content</string> <string name="content_warning_hide_all_sensitive_content">Always hide sensitive content</string>
<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>
@@ -22,6 +22,7 @@ package com.vitorpamplona.quartz.nip36SensitiveContent
import com.vitorpamplona.quartz.nip01Core.core.has import com.vitorpamplona.quartz.nip01Core.core.has
import com.vitorpamplona.quartz.utils.arrayOfNotNull import com.vitorpamplona.quartz.utils.arrayOfNotNull
import com.vitorpamplona.quartz.utils.ensure
class ContentWarningTag( class ContentWarningTag(
val reason: String? = null, val reason: String? = null,
@@ -33,9 +34,9 @@ class ContentWarningTag(
fun isTag(tag: Array<String>) = tag.has(0) && tag[0] == TAG_NAME fun isTag(tag: Array<String>) = tag.has(0) && tag[0] == TAG_NAME
fun parse(tags: Array<String>): ContentWarningTag { fun parse(tags: Array<String>): ContentWarningTag? {
require(tags[0] == TAG_NAME) ensure(tags[0] == TAG_NAME) { return null }
return ContentWarningTag(tags.getOrNull(1)) return ContentWarningTag(tags.getOrNull(1).ifBlank { null })
} }
fun assemble() = arrayOfNotNull(TAG_NAME) fun assemble() = arrayOfNotNull(TAG_NAME)
@@ -29,4 +29,4 @@ fun TagArray.isSensitive() = this.any(ContentWarningTag::isTag)
fun TagArray.isSensitiveOrNSFW() = this.any { ContentWarningTag.isTag(it) || HashtagTag.isAnyTagged(it, nsfwTags) } fun TagArray.isSensitiveOrNSFW() = this.any { ContentWarningTag.isTag(it) || HashtagTag.isAnyTagged(it, nsfwTags) }
fun TagArray.contentWarningReason() = this.firstOrNull(ContentWarningTag::isTag)?.getOrNull(1) fun TagArray.contentWarningReason() = this.firstNotNullOfOrNull(ContentWarningTag::parse)?.reason