refactor(feeds): push content warning into ZoomableContentView per media

Move the blurhash-aware sensitivity warning from feed card wrappers into
ZoomableContentView so every media item shows its own warning sized to
its own aspect ratio over its own blurhash. Grid posts now warn per
image instead of once over the whole grid.

- Add isSensitive to MediaUrl{Image,Video} (defaults to contentWarning != null)
- Populate isSensitive and contentWarning when building media in the
  picture, video, and file-header feeds plus their display variants
- Drop the outer SensitivityWarning wrappers now handled inside
  ZoomableContentView via SensitivityWarningOverBlurhash
This commit is contained in:
Claude
2026-04-18 18:51:17 +00:00
parent 75c96115cb
commit 1166654149
8 changed files with 158 additions and 155 deletions
@@ -76,31 +76,6 @@ fun SensitivityWarning(
note.event?.let { SensitivityWarning(it, accountViewModel, content) } note.event?.let { SensitivityWarning(it, accountViewModel, content) }
} }
@Composable
fun SensitivityWarning(
note: Note,
blurhash: String?,
ratio: Float?,
description: String?,
accountViewModel: AccountViewModel,
content: @Composable () -> Unit,
) {
val event = note.event
if (event == null) {
content()
return
}
val hasSensitiveContent = remember(event) { event.isSensitiveOrNSFW() }
if (hasSensitiveContent) {
val reason = remember(event) { event.contentWarningReason() }
ObserveSensitivityWarningOverBlurhash(reason, blurhash, ratio, description, accountViewModel, content)
} else {
content()
}
}
@Composable @Composable
fun SensitivityWarning( fun SensitivityWarning(
event: Event, event: Event,
@@ -130,6 +105,38 @@ fun SensitivityWarning(
} }
} }
@Composable
fun SensitivityWarningOverBlurhash(
isSensitive: Boolean,
reason: String?,
blurhash: String?,
ratio: Float?,
description: String?,
accountViewModel: AccountViewModel,
content: @Composable () -> Unit,
) {
if (!isSensitive) {
content()
return
}
val accountState = accountViewModel.showSensitiveContent().collectAsStateWithLifecycle()
var showContentWarningNote by remember(accountState) { mutableStateOf(accountState.value != true) }
CrossfadeIfEnabled(targetState = showContentWarningNote, accountViewModel = accountViewModel) {
if (it) {
if (blurhash != null) {
ContentWarningOverBlurhash(reason, blurhash, ratio, description) { showContentWarningNote = false }
} else {
ContentWarningNote(reason) { showContentWarningNote = false }
}
} else {
content()
}
}
}
@Composable @Composable
fun ObserveSensitivityWarning( fun ObserveSensitivityWarning(
reason: String?, reason: String?,
@@ -149,32 +156,6 @@ fun ObserveSensitivityWarning(
} }
} }
@Composable
fun ObserveSensitivityWarningOverBlurhash(
reason: String?,
blurhash: String?,
ratio: Float?,
description: String?,
accountViewModel: AccountViewModel,
content: @Composable () -> Unit,
) {
val accountState = accountViewModel.showSensitiveContent().collectAsStateWithLifecycle()
var showContentWarningNote by remember(accountState) { mutableStateOf(accountState.value != true) }
CrossfadeIfEnabled(targetState = showContentWarningNote, accountViewModel = accountViewModel) {
if (it) {
if (blurhash != null) {
ContentWarningOverBlurhash(reason, blurhash, ratio, description) { showContentWarningNote = false }
} else {
ContentWarningNote(reason) { showContentWarningNote = false }
}
} else {
content()
}
}
}
@Preview @Preview
@Composable @Composable
fun ContentWarningNotePreview() { fun ContentWarningNotePreview() {
@@ -150,7 +150,15 @@ fun ZoomableContentView(
when (content) { when (content) {
is MediaUrlImage -> { is MediaUrlImage -> {
SensitivityWarning(content.contentWarning, accountViewModel) { val ratio = content.dim?.aspectRatio() ?: MediaAspectRatioCache.get(content.url)
SensitivityWarningOverBlurhash(
isSensitive = content.isSensitive,
reason = content.contentWarning,
blurhash = content.blurhash,
ratio = ratio,
description = content.description,
accountViewModel = accountViewModel,
) {
TwoSecondController(content) { controllerVisible -> TwoSecondController(content) { controllerVisible ->
val mainImageModifier = val mainImageModifier =
Modifier Modifier
@@ -164,7 +172,15 @@ fun ZoomableContentView(
} }
is MediaUrlVideo -> { is MediaUrlVideo -> {
SensitivityWarning(content.contentWarning, accountViewModel) { val ratio = content.dim?.aspectRatio() ?: MediaAspectRatioCache.get(content.url)
SensitivityWarningOverBlurhash(
isSensitive = content.isSensitive,
reason = content.contentWarning,
blurhash = content.blurhash,
ratio = ratio,
description = content.description,
accountViewModel = accountViewModel,
) {
Box( Box(
modifier = Modifier.fillMaxWidth().then(boundsTrackingModifier), modifier = Modifier.fillMaxWidth().then(boundsTrackingModifier),
contentAlignment = Alignment.Center, contentAlignment = Alignment.Center,
@@ -39,12 +39,13 @@ import com.vitorpamplona.amethyst.commons.model.EmptyTagList
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.components.AutoNonlazyGrid import com.vitorpamplona.amethyst.ui.components.AutoNonlazyGrid
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer import com.vitorpamplona.amethyst.ui.components.TranslatableRichTextViewer
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarningReason
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitiveOrNSFW
import com.vitorpamplona.quartz.nip68Picture.PictureEvent import com.vitorpamplona.quartz.nip68Picture.PictureEvent
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
@@ -60,6 +61,8 @@ fun PictureDisplay(
) { ) {
val event = (note.event as? PictureEvent) ?: return val event = (note.event as? PictureEvent) ?: return
val uri = note.toNostrUri() val uri = note.toNostrUri()
val isSensitive = event.isSensitiveOrNSFW()
val contentWarning = event.contentWarningReason()
val images by val images by
remember(note) { remember(note) {
@@ -74,6 +77,8 @@ fun PictureDisplay(
blurhash = it.blurhash, blurhash = it.blurhash,
dim = it.dimension, dim = it.dimension,
uri = uri, uri = uri,
contentWarning = contentWarning,
isSensitive = isSensitive,
mimeType = it.mimeType, mimeType = it.mimeType,
) )
}.toImmutableList(), }.toImmutableList(),
@@ -85,53 +90,51 @@ fun PictureDisplay(
if (first != null) { if (first != null) {
val title = event.title() val title = event.title()
SensitivityWarning(note = note, accountViewModel = accountViewModel) { Column {
Column { if (title != null) {
if (title != null) { Text(
Text( modifier = Modifier.padding(padding),
modifier = Modifier.padding(padding), text = title,
text = title, style = MaterialTheme.typography.bodyLarge,
style = MaterialTheme.typography.bodyLarge, maxLines = 1,
maxLines = 1, overflow = TextOverflow.Ellipsis,
overflow = TextOverflow.Ellipsis, )
) } else {
} else { Spacer(StdVertSpacer)
Spacer(StdVertSpacer) }
}
if (images.size == 1) { if (images.size == 1) {
ZoomableContentView(
content = images.first(),
images = images,
roundedCorner = roundedCorner,
contentScale = ContentScale.FillWidth,
accountViewModel = accountViewModel,
)
} else {
AutoNonlazyGrid(images.size) {
ZoomableContentView( ZoomableContentView(
content = images.first(), content = images[it],
images = images, images = images,
roundedCorner = roundedCorner, roundedCorner = roundedCorner,
contentScale = ContentScale.FillWidth, contentScale = ContentScale.Crop,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
) )
} else {
AutoNonlazyGrid(images.size) {
ZoomableContentView(
content = images[it],
images = images,
roundedCorner = roundedCorner,
contentScale = ContentScale.Crop,
accountViewModel = accountViewModel,
)
}
} }
TranslatableRichTextViewer(
content = event.content,
canPreview = false,
quotesLeft = 0,
modifier = Modifier.padding(padding),
tags = EmptyTagList,
backgroundColor = backgroundColor,
id = note.idHex,
callbackUri = uri,
accountViewModel = accountViewModel,
nav = nav,
)
} }
TranslatableRichTextViewer(
content = event.content,
canPreview = false,
quotesLeft = 0,
modifier = Modifier.padding(padding),
tags = EmptyTagList,
backgroundColor = backgroundColor,
id = note.idHex,
callbackUri = uri,
accountViewModel = accountViewModel,
nav = nav,
)
} }
} }
} }
@@ -30,11 +30,12 @@ import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlVideo import com.vitorpamplona.amethyst.commons.richtext.MediaUrlVideo
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarningReason
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitiveOrNSFW
import com.vitorpamplona.quartz.nip71Video.VideoEvent import com.vitorpamplona.quartz.nip71Video.VideoEvent
@Composable @Composable
@@ -53,6 +54,8 @@ fun JustVideoDisplay(
remember(note) { remember(note) {
val description = event.content.ifEmpty { null } ?: imeta.alt ?: event.alt() val description = event.content.ifEmpty { null } ?: imeta.alt ?: event.alt()
val isImage = imeta.mimeType?.startsWith("image/") == true || RichTextParser.isImageUrl(imeta.url) val isImage = imeta.mimeType?.startsWith("image/") == true || RichTextParser.isImageUrl(imeta.url)
val isSensitive = event.isSensitiveOrNSFW()
val contentWarning = event.contentWarningReason()
mutableStateOf<BaseMediaContent>( mutableStateOf<BaseMediaContent>(
if (isImage) { if (isImage) {
@@ -63,6 +66,8 @@ fun JustVideoDisplay(
blurhash = imeta.blurhash, blurhash = imeta.blurhash,
dim = imeta.dimension, dim = imeta.dimension,
uri = note.toNostrUri(), uri = note.toNostrUri(),
contentWarning = contentWarning,
isSensitive = isSensitive,
mimeType = imeta.mimeType, mimeType = imeta.mimeType,
) )
} else { } else {
@@ -74,18 +79,18 @@ fun JustVideoDisplay(
dim = imeta.dimension, dim = imeta.dimension,
uri = note.toNostrUri(), uri = note.toNostrUri(),
authorName = note.author?.toBestDisplayName(), authorName = note.author?.toBestDisplayName(),
contentWarning = contentWarning,
isSensitive = isSensitive,
mimeType = imeta.mimeType, mimeType = imeta.mimeType,
) )
}, },
) )
} }
SensitivityWarning(note = note, accountViewModel = accountViewModel) { ZoomableContentView(
ZoomableContentView( content = content,
content = content, roundedCorner = roundedCorner,
roundedCorner = roundedCorner, contentScale = contentScale,
contentScale = contentScale, accountViewModel = accountViewModel,
accountViewModel = accountViewModel, )
)
}
} }
@@ -39,15 +39,15 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage
import com.vitorpamplona.amethyst.model.MediaAspectRatioCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.components.AutoNonlazyGrid import com.vitorpamplona.amethyst.ui.components.AutoNonlazyGrid
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.note.ReactionsRow import com.vitorpamplona.amethyst.ui.note.ReactionsRow
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.UserCardHeader import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.UserCardHeader
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarningReason
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitiveOrNSFW
import com.vitorpamplona.quartz.nip68Picture.PictureEvent import com.vitorpamplona.quartz.nip68Picture.PictureEvent
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
@@ -92,6 +92,8 @@ private fun PictureCardImage(
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
) { ) {
val uri = note.toNostrUri() val uri = note.toNostrUri()
val isSensitive = event.isSensitiveOrNSFW()
val contentWarning = event.contentWarningReason()
val images by val images by
remember(note) { remember(note) {
@@ -106,6 +108,8 @@ private fun PictureCardImage(
blurhash = it.blurhash, blurhash = it.blurhash,
dim = it.dimension, dim = it.dimension,
uri = uri, uri = uri,
contentWarning = contentWarning,
isSensitive = isSensitive,
mimeType = it.mimeType, mimeType = it.mimeType,
) )
}.toImmutableList(), }.toImmutableList(),
@@ -113,34 +117,23 @@ private fun PictureCardImage(
} }
if (images.isNotEmpty()) { if (images.isNotEmpty()) {
val first = images.first() if (images.size == 1) {
val ratio = first.dim?.aspectRatio() ?: MediaAspectRatioCache.get(first.url) ZoomableContentView(
content = images.first(),
SensitivityWarning( images = images,
note = note, roundedCorner = false,
blurhash = first.blurhash, contentScale = ContentScale.FillWidth,
ratio = ratio, accountViewModel = accountViewModel,
description = first.description, )
accountViewModel = accountViewModel, } else {
) { AutoNonlazyGrid(images.size) {
if (images.size == 1) {
ZoomableContentView( ZoomableContentView(
content = first, content = images[it],
images = images, images = images,
roundedCorner = false, roundedCorner = false,
contentScale = ContentScale.FillWidth, contentScale = ContentScale.Crop,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
) )
} else {
AutoNonlazyGrid(images.size) {
ZoomableContentView(
content = images[it],
images = images,
roundedCorner = false,
contentScale = ContentScale.Crop,
accountViewModel = accountViewModel,
)
}
} }
} }
} }
@@ -42,9 +42,7 @@ import com.vitorpamplona.amethyst.commons.richtext.BaseMediaContent
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlVideo import com.vitorpamplona.amethyst.commons.richtext.MediaUrlVideo
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
import com.vitorpamplona.amethyst.model.MediaAspectRatioCache
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.note.ReactionsRow import com.vitorpamplona.amethyst.ui.note.ReactionsRow
@@ -52,6 +50,8 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.UserCardHeader import com.vitorpamplona.amethyst.ui.screen.loggedIn.video.UserCardHeader
import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarningReason
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitiveOrNSFW
import com.vitorpamplona.quartz.nip71Video.VideoEvent import com.vitorpamplona.quartz.nip71Video.VideoEvent
import kotlin.text.ifEmpty import kotlin.text.ifEmpty
@@ -104,6 +104,8 @@ private fun VideoCardImage(
remember(note) { remember(note) {
val description = event.content.ifEmpty { null } ?: imeta.alt ?: event.alt() val description = event.content.ifEmpty { null } ?: imeta.alt ?: event.alt()
val isImage = imeta.mimeType?.startsWith("image/") == true || RichTextParser.isImageUrl(imeta.url) val isImage = imeta.mimeType?.startsWith("image/") == true || RichTextParser.isImageUrl(imeta.url)
val isSensitive = event.isSensitiveOrNSFW()
val contentWarning = event.contentWarningReason()
mutableStateOf<BaseMediaContent>( mutableStateOf<BaseMediaContent>(
if (isImage) { if (isImage) {
@@ -114,6 +116,8 @@ private fun VideoCardImage(
blurhash = imeta.blurhash, blurhash = imeta.blurhash,
dim = imeta.dimension, dim = imeta.dimension,
uri = note.toNostrUri(), uri = note.toNostrUri(),
contentWarning = contentWarning,
isSensitive = isSensitive,
mimeType = imeta.mimeType, mimeType = imeta.mimeType,
) )
} else { } else {
@@ -125,28 +129,20 @@ private fun VideoCardImage(
dim = imeta.dimension, dim = imeta.dimension,
uri = note.toNostrUri(), uri = note.toNostrUri(),
authorName = note.author?.toBestDisplayName(), authorName = note.author?.toBestDisplayName(),
contentWarning = contentWarning,
isSensitive = isSensitive,
mimeType = imeta.mimeType, mimeType = imeta.mimeType,
) )
}, },
) )
} }
val ratio = imeta.dimension?.aspectRatio() ?: MediaAspectRatioCache.get(imeta.url) ZoomableContentView(
content = content,
SensitivityWarning( roundedCorner = false,
note = note, contentScale = ContentScale.FillWidth,
blurhash = imeta.blurhash,
ratio = ratio,
description = content.description,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
) { )
ZoomableContentView(
content = content,
roundedCorner = false,
contentScale = ContentScale.FillWidth,
accountViewModel = accountViewModel,
)
}
} }
@Composable @Composable
@@ -43,13 +43,14 @@ import com.vitorpamplona.amethyst.commons.richtext.MediaUrlImage
import com.vitorpamplona.amethyst.commons.richtext.MediaUrlVideo import com.vitorpamplona.amethyst.commons.richtext.MediaUrlVideo
import com.vitorpamplona.amethyst.commons.richtext.RichTextParser import com.vitorpamplona.amethyst.commons.richtext.RichTextParser
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.components.SensitivityWarning
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
import com.vitorpamplona.amethyst.ui.navigation.navs.INav import com.vitorpamplona.amethyst.ui.navigation.navs.INav
import com.vitorpamplona.amethyst.ui.note.ReactionsRow import com.vitorpamplona.amethyst.ui.note.ReactionsRow
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.quartz.nip01Core.core.Event import com.vitorpamplona.quartz.nip01Core.core.Event
import com.vitorpamplona.quartz.nip31Alts.alt import com.vitorpamplona.quartz.nip31Alts.alt
import com.vitorpamplona.quartz.nip36SensitiveContent.contentWarningReason
import com.vitorpamplona.quartz.nip36SensitiveContent.isSensitiveOrNSFW
import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent
import kotlin.text.ifEmpty import kotlin.text.ifEmpty
@@ -103,6 +104,8 @@ private fun FileHeaderCardImage(
val isImage = event.mimeType()?.startsWith("image/") == true || RichTextParser.isImageUrl(fullUrl) val isImage = event.mimeType()?.startsWith("image/") == true || RichTextParser.isImageUrl(fullUrl)
val uri = note.toNostrUri() val uri = note.toNostrUri()
val mimeType = event.mimeType() val mimeType = event.mimeType()
val isSensitive = event.isSensitiveOrNSFW()
val contentWarning = event.contentWarningReason()
mutableStateOf<BaseMediaContent>( mutableStateOf<BaseMediaContent>(
if (isImage) { if (isImage) {
@@ -113,6 +116,8 @@ private fun FileHeaderCardImage(
blurhash = blurHash, blurhash = blurHash,
dim = dimensions, dim = dimensions,
uri = uri, uri = uri,
contentWarning = contentWarning,
isSensitive = isSensitive,
mimeType = mimeType, mimeType = mimeType,
) )
} else { } else {
@@ -124,20 +129,20 @@ private fun FileHeaderCardImage(
dim = dimensions, dim = dimensions,
uri = uri, uri = uri,
authorName = note.author?.toBestDisplayName(), authorName = note.author?.toBestDisplayName(),
contentWarning = contentWarning,
isSensitive = isSensitive,
mimeType = mimeType, mimeType = mimeType,
) )
}, },
) )
} }
SensitivityWarning(note = note, accountViewModel = accountViewModel) { ZoomableContentView(
ZoomableContentView( content = content,
content = content, roundedCorner = false,
roundedCorner = false, contentScale = ContentScale.FillWidth,
contentScale = ContentScale.FillWidth, accountViewModel = accountViewModel,
accountViewModel = accountViewModel, )
)
}
} }
@Composable @Composable
@@ -51,6 +51,7 @@ open class MediaUrlImage(
dim: DimensionTag? = null, dim: DimensionTag? = null,
uri: String? = null, uri: String? = null,
val contentWarning: String? = null, val contentWarning: String? = null,
val isSensitive: Boolean = contentWarning != null,
mimeType: String? = null, mimeType: String? = null,
) : MediaUrlContent(url, description, hash, dim, blurhash, uri, mimeType) ) : MediaUrlContent(url, description, hash, dim, blurhash, uri, mimeType)
@@ -62,11 +63,12 @@ class EncryptedMediaUrlImage(
dim: DimensionTag? = null, dim: DimensionTag? = null,
uri: String? = null, uri: String? = null,
contentWarning: String? = null, contentWarning: String? = null,
isSensitive: Boolean = contentWarning != null,
mimeType: String? = null, mimeType: String? = null,
val encryptionAlgo: String, val encryptionAlgo: String,
val encryptionKey: ByteArray, val encryptionKey: ByteArray,
val encryptionNonce: ByteArray, val encryptionNonce: ByteArray,
) : MediaUrlImage(url, description, hash, blurhash, dim, uri, contentWarning, mimeType) ) : MediaUrlImage(url, description, hash, blurhash, dim, uri, contentWarning, isSensitive, mimeType)
@Immutable @Immutable
open class MediaUrlVideo( open class MediaUrlVideo(
@@ -79,6 +81,7 @@ open class MediaUrlVideo(
val authorName: String? = null, val authorName: String? = null,
blurhash: String? = null, blurhash: String? = null,
val contentWarning: String? = null, val contentWarning: String? = null,
val isSensitive: Boolean = contentWarning != null,
mimeType: String? = null, mimeType: String? = null,
) : MediaUrlContent(url, description, hash, dim, blurhash, uri, mimeType) ) : MediaUrlContent(url, description, hash, dim, blurhash, uri, mimeType)
@@ -93,11 +96,12 @@ class EncryptedMediaUrlVideo(
authorName: String? = null, authorName: String? = null,
blurhash: String? = null, blurhash: String? = null,
contentWarning: String? = null, contentWarning: String? = null,
isSensitive: Boolean = contentWarning != null,
mimeType: String? = null, mimeType: String? = null,
val encryptionAlgo: String, val encryptionAlgo: String,
val encryptionKey: ByteArray, val encryptionKey: ByteArray,
val encryptionNonce: ByteArray, val encryptionNonce: ByteArray,
) : MediaUrlVideo(url, description, hash, dim, uri, artworkUri, authorName, blurhash, contentWarning, mimeType) ) : MediaUrlVideo(url, description, hash, dim, uri, artworkUri, authorName, blurhash, contentWarning, isSensitive, mimeType)
@Immutable @Immutable
abstract class MediaPreloadedContent( abstract class MediaPreloadedContent(