refactor(feeds): unify content warning behind ContentWarningGate
Collapse the per-media, per-blurhash and grid-of-blurhash variants into a
single ContentWarningGate that takes a backdrop slot and a sizing
modifier. Callers choose the backdrop (single blurhash, grid of
blurhashes, or none).
Remove the isSensitive field from MediaUrl{Image,Video}; it was a
rendering concern leaked into a media model. Sensitivity is now only
expressed to the gate. Picture/Video feeds and their display variants
compute isSensitive + reasons themselves and wrap with the gate.
ZoomableContentView keeps a default internal gate (fires when
content.contentWarning != null) so the 14 callers that don't need
grid-level handling still get the blurhash overlay automatically.
Also rename collectPictureReasons to collectContentWarningReasons.
This commit is contained in:
+44
-119
@@ -117,15 +117,13 @@ fun SensitivityWarning(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SensitivityWarningOverBlurhash(
|
fun ContentWarningGate(
|
||||||
isSensitive: Boolean,
|
isSensitive: Boolean,
|
||||||
reasons: Set<String>,
|
reasons: Set<String>,
|
||||||
blurhash: String?,
|
preloadUrls: List<String>,
|
||||||
ratio: Float?,
|
|
||||||
description: String?,
|
|
||||||
contentScale: ContentScale,
|
|
||||||
preloadUrl: String?,
|
|
||||||
accountViewModel: AccountViewModel,
|
accountViewModel: AccountViewModel,
|
||||||
|
modifier: Modifier = Modifier.fillMaxWidth(),
|
||||||
|
backdrop: (@Composable () -> Unit)? = null,
|
||||||
content: @Composable () -> Unit,
|
content: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
if (!isSensitive) {
|
if (!isSensitive) {
|
||||||
@@ -137,51 +135,12 @@ fun SensitivityWarningOverBlurhash(
|
|||||||
|
|
||||||
var showContentWarningNote by remember(accountState) { mutableStateOf(accountState.value != true) }
|
var showContentWarningNote by remember(accountState) { mutableStateOf(accountState.value != true) }
|
||||||
|
|
||||||
if (showContentWarningNote && preloadUrl != null) {
|
if (showContentWarningNote && preloadUrls.isNotEmpty()) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
LaunchedEffect(preloadUrl) {
|
LaunchedEffect(preloadUrls) {
|
||||||
runCatching {
|
preloadUrls.forEach { url ->
|
||||||
context.imageLoader.enqueue(ImageRequest.Builder(context).data(preloadUrl).build())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
CrossfadeIfEnabled(targetState = showContentWarningNote, accountViewModel = accountViewModel) {
|
|
||||||
if (it) {
|
|
||||||
if (blurhash != null) {
|
|
||||||
ContentWarningOverBlurhash(reasons, blurhash, ratio, description, contentScale) { showContentWarningNote = false }
|
|
||||||
} else {
|
|
||||||
ContentWarningNote(reasons.firstOrNull()) { showContentWarningNote = false }
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
content()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun SensitivityWarningOverBlurhashGrid(
|
|
||||||
isSensitive: Boolean,
|
|
||||||
reasons: Set<String>,
|
|
||||||
media: List<MediaUrlImage>,
|
|
||||||
accountViewModel: AccountViewModel,
|
|
||||||
content: @Composable () -> Unit,
|
|
||||||
) {
|
|
||||||
if (!isSensitive || media.isEmpty()) {
|
|
||||||
content()
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val accountState = accountViewModel.showSensitiveContent().collectAsStateWithLifecycle()
|
|
||||||
|
|
||||||
var showContentWarningNote by remember(accountState) { mutableStateOf(accountState.value != true) }
|
|
||||||
|
|
||||||
if (showContentWarningNote) {
|
|
||||||
val context = LocalContext.current
|
|
||||||
LaunchedEffect(media) {
|
|
||||||
media.forEach { item ->
|
|
||||||
runCatching {
|
runCatching {
|
||||||
context.imageLoader.enqueue(ImageRequest.Builder(context).data(item.url).build())
|
context.imageLoader.enqueue(ImageRequest.Builder(context).data(url).build())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -189,7 +148,14 @@ fun SensitivityWarningOverBlurhashGrid(
|
|||||||
|
|
||||||
CrossfadeIfEnabled(targetState = showContentWarningNote, accountViewModel = accountViewModel) {
|
CrossfadeIfEnabled(targetState = showContentWarningNote, accountViewModel = accountViewModel) {
|
||||||
if (it) {
|
if (it) {
|
||||||
ContentWarningOverBlurhashGrid(reasons, media) { showContentWarningNote = false }
|
if (backdrop != null) {
|
||||||
|
Box(modifier = modifier.clipToBounds()) {
|
||||||
|
backdrop()
|
||||||
|
ContentWarningOverlayBody(reasons) { showContentWarningNote = false }
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ContentWarningNote(reasons.firstOrNull()) { showContentWarningNote = false }
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
content()
|
content()
|
||||||
}
|
}
|
||||||
@@ -239,85 +205,44 @@ fun ContentWarningNoteWithBigReasonPreview() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Preview
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ContentWarningOverBlurhashPreview() {
|
fun BlurhashBackdrop(
|
||||||
ThemeComparisonColumn {
|
|
||||||
ContentWarningOverBlurhash(
|
|
||||||
reasons = setOf("Spoilers"),
|
|
||||||
blurhash = "LEHV6nWB2yk8pyo0adR*.7kCMdnj",
|
|
||||||
ratio = 16f / 9f,
|
|
||||||
description = null,
|
|
||||||
contentScale = ContentScale.FillWidth,
|
|
||||||
onDismiss = {},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Preview
|
|
||||||
@Composable
|
|
||||||
fun ContentWarningOverBlurhashMultiReasonPreview() {
|
|
||||||
ThemeComparisonColumn {
|
|
||||||
ContentWarningOverBlurhash(
|
|
||||||
reasons = setOf("Spoilers", "Violence", "Nudity"),
|
|
||||||
blurhash = "LEHV6nWB2yk8pyo0adR*.7kCMdnj",
|
|
||||||
ratio = 16f / 9f,
|
|
||||||
description = null,
|
|
||||||
contentScale = ContentScale.FillWidth,
|
|
||||||
onDismiss = {},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun ContentWarningOverBlurhash(
|
|
||||||
reasons: Set<String>,
|
|
||||||
blurhash: String,
|
blurhash: String,
|
||||||
ratio: Float?,
|
|
||||||
description: String?,
|
description: String?,
|
||||||
contentScale: ContentScale,
|
|
||||||
onDismiss: () -> Unit,
|
|
||||||
) {
|
) {
|
||||||
val sizingModifier =
|
DisplayBlurHash(
|
||||||
when {
|
blurhash = blurhash,
|
||||||
contentScale == ContentScale.Crop -> Modifier.fillMaxSize()
|
description = description,
|
||||||
ratio != null -> Modifier.fillMaxWidth().aspectRatio(ratio)
|
contentScale = ContentScale.Crop,
|
||||||
else -> Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxSize(),
|
||||||
}
|
)
|
||||||
|
|
||||||
Box(modifier = sizingModifier.clipToBounds()) {
|
|
||||||
DisplayBlurHash(
|
|
||||||
blurhash = blurhash,
|
|
||||||
description = description,
|
|
||||||
contentScale = ContentScale.Crop,
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
)
|
|
||||||
ContentWarningOverlayBody(reasons, onDismiss)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ContentWarningOverBlurhashGrid(
|
fun BlurhashGridBackdrop(media: List<MediaUrlImage>) {
|
||||||
reasons: Set<String>,
|
AutoNonlazyGrid(media.size) { idx ->
|
||||||
media: List<MediaUrlImage>,
|
val item = media[idx]
|
||||||
onDismiss: () -> Unit,
|
if (item.blurhash != null) {
|
||||||
) {
|
DisplayBlurHash(
|
||||||
Box(modifier = Modifier.fillMaxWidth().aspectRatio(1f).clipToBounds()) {
|
blurhash = item.blurhash,
|
||||||
AutoNonlazyGrid(media.size) { idx ->
|
description = item.description,
|
||||||
val item = media[idx]
|
contentScale = ContentScale.Crop,
|
||||||
if (item.blurhash != null) {
|
modifier = Modifier.fillMaxSize(),
|
||||||
DisplayBlurHash(
|
)
|
||||||
blurhash = item.blurhash,
|
|
||||||
description = item.description,
|
|
||||||
contentScale = ContentScale.Crop,
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
ContentWarningOverlayBody(reasons, onDismiss)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun mediaSizingModifier(
|
||||||
|
ratio: Float?,
|
||||||
|
contentScale: ContentScale,
|
||||||
|
): Modifier =
|
||||||
|
when {
|
||||||
|
contentScale == ContentScale.Crop -> Modifier.fillMaxSize()
|
||||||
|
ratio != null -> Modifier.fillMaxWidth().aspectRatio(ratio)
|
||||||
|
else -> Modifier.fillMaxWidth()
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ContentWarningOverlayBody(
|
private fun ContentWarningOverlayBody(
|
||||||
reasons: Set<String>,
|
reasons: Set<String>,
|
||||||
@@ -487,7 +412,7 @@ fun ContentWarningNote(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun collectPictureReasons(event: Event): Set<String> {
|
fun collectContentWarningReasons(event: Event): Set<String> {
|
||||||
val reasons = linkedSetOf<String>()
|
val reasons = linkedSetOf<String>()
|
||||||
event.contentWarningReason()?.takeIf { it.isNotBlank() }?.let { reasons.add(it) }
|
event.contentWarningReason()?.takeIf { it.isNotBlank() }?.let { reasons.add(it) }
|
||||||
event.imetas().forEach { iMeta ->
|
event.imetas().forEach { iMeta ->
|
||||||
|
|||||||
+10
-14
@@ -151,15 +151,13 @@ fun ZoomableContentView(
|
|||||||
when (content) {
|
when (content) {
|
||||||
is MediaUrlImage -> {
|
is MediaUrlImage -> {
|
||||||
val ratio = content.dim?.aspectRatio() ?: MediaAspectRatioCache.get(content.url)
|
val ratio = content.dim?.aspectRatio() ?: MediaAspectRatioCache.get(content.url)
|
||||||
SensitivityWarningOverBlurhash(
|
ContentWarningGate(
|
||||||
isSensitive = content.isSensitive,
|
isSensitive = content.contentWarning != null,
|
||||||
reasons = setOfNotNull(content.contentWarning),
|
reasons = setOfNotNull(content.contentWarning),
|
||||||
blurhash = content.blurhash,
|
preloadUrls = listOf(content.url),
|
||||||
ratio = ratio,
|
|
||||||
description = content.description,
|
|
||||||
contentScale = contentScale,
|
|
||||||
preloadUrl = content.url,
|
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
|
modifier = mediaSizingModifier(ratio, contentScale),
|
||||||
|
backdrop = content.blurhash?.let { blurhash -> { BlurhashBackdrop(blurhash, content.description) } },
|
||||||
) {
|
) {
|
||||||
TwoSecondController(content) { controllerVisible ->
|
TwoSecondController(content) { controllerVisible ->
|
||||||
val mainImageModifier =
|
val mainImageModifier =
|
||||||
@@ -175,15 +173,13 @@ fun ZoomableContentView(
|
|||||||
|
|
||||||
is MediaUrlVideo -> {
|
is MediaUrlVideo -> {
|
||||||
val ratio = content.dim?.aspectRatio() ?: MediaAspectRatioCache.get(content.url)
|
val ratio = content.dim?.aspectRatio() ?: MediaAspectRatioCache.get(content.url)
|
||||||
SensitivityWarningOverBlurhash(
|
ContentWarningGate(
|
||||||
isSensitive = content.isSensitive,
|
isSensitive = content.contentWarning != null,
|
||||||
reasons = setOfNotNull(content.contentWarning),
|
reasons = setOfNotNull(content.contentWarning),
|
||||||
blurhash = content.blurhash,
|
preloadUrls = emptyList(),
|
||||||
ratio = ratio,
|
|
||||||
description = content.description,
|
|
||||||
contentScale = contentScale,
|
|
||||||
preloadUrl = null,
|
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
|
modifier = mediaSizingModifier(ratio, contentScale),
|
||||||
|
backdrop = content.blurhash?.let { blurhash -> { BlurhashBackdrop(blurhash, content.description) } },
|
||||||
) {
|
) {
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier.fillMaxWidth().then(boundsTrackingModifier),
|
modifier = Modifier.fillMaxWidth().then(boundsTrackingModifier),
|
||||||
|
|||||||
@@ -23,6 +23,8 @@ package com.vitorpamplona.amethyst.ui.note.types
|
|||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
@@ -37,12 +39,16 @@ import androidx.compose.ui.layout.ContentScale
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import com.vitorpamplona.amethyst.commons.model.EmptyTagList
|
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.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.SensitivityWarningOverBlurhashGrid
|
import com.vitorpamplona.amethyst.ui.components.BlurhashBackdrop
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.BlurhashGridBackdrop
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.ContentWarningGate
|
||||||
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.components.collectPictureReasons
|
import com.vitorpamplona.amethyst.ui.components.collectContentWarningReasons
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.mediaSizingModifier
|
||||||
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
|
||||||
@@ -63,8 +69,7 @@ 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 isSensitive = event.isSensitiveOrNSFW()
|
||||||
val reasons = collectPictureReasons(event)
|
val reasons = collectContentWarningReasons(event)
|
||||||
val inGrid = event.imetaTags().size > 1
|
|
||||||
|
|
||||||
val images by
|
val images by
|
||||||
remember(note) {
|
remember(note) {
|
||||||
@@ -79,8 +84,6 @@ fun PictureDisplay(
|
|||||||
blurhash = it.blurhash,
|
blurhash = it.blurhash,
|
||||||
dim = it.dimension,
|
dim = it.dimension,
|
||||||
uri = uri,
|
uri = uri,
|
||||||
contentWarning = reasons.firstOrNull(),
|
|
||||||
isSensitive = isSensitive && !inGrid,
|
|
||||||
mimeType = it.mimeType,
|
mimeType = it.mimeType,
|
||||||
)
|
)
|
||||||
}.toImmutableList(),
|
}.toImmutableList(),
|
||||||
@@ -106,19 +109,31 @@ fun PictureDisplay(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (images.size == 1) {
|
if (images.size == 1) {
|
||||||
ZoomableContentView(
|
val ratio = first.dim?.aspectRatio() ?: MediaAspectRatioCache.get(first.url)
|
||||||
content = images.first(),
|
ContentWarningGate(
|
||||||
images = images,
|
|
||||||
roundedCorner = roundedCorner,
|
|
||||||
contentScale = ContentScale.FillWidth,
|
|
||||||
accountViewModel = accountViewModel,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
SensitivityWarningOverBlurhashGrid(
|
|
||||||
isSensitive = isSensitive,
|
isSensitive = isSensitive,
|
||||||
reasons = reasons,
|
reasons = reasons,
|
||||||
media = images,
|
preloadUrls = listOf(first.url),
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
|
modifier = mediaSizingModifier(ratio, ContentScale.FillWidth),
|
||||||
|
backdrop = first.blurhash?.let { blurhash -> { BlurhashBackdrop(blurhash, first.description) } },
|
||||||
|
) {
|
||||||
|
ZoomableContentView(
|
||||||
|
content = first,
|
||||||
|
images = images,
|
||||||
|
roundedCorner = roundedCorner,
|
||||||
|
contentScale = ContentScale.FillWidth,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ContentWarningGate(
|
||||||
|
isSensitive = isSensitive,
|
||||||
|
reasons = reasons,
|
||||||
|
preloadUrls = images.map { it.url },
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
modifier = Modifier.fillMaxWidth().aspectRatio(1f),
|
||||||
|
backdrop = { BlurhashGridBackdrop(images) },
|
||||||
) {
|
) {
|
||||||
AutoNonlazyGrid(images.size) {
|
AutoNonlazyGrid(images.size) {
|
||||||
ZoomableContentView(
|
ZoomableContentView(
|
||||||
|
|||||||
@@ -29,12 +29,16 @@ 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.BlurhashBackdrop
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.ContentWarningGate
|
||||||
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
|
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.collectContentWarningReasons
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.mediaSizingModifier
|
||||||
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.nip36SensitiveContent.isSensitiveOrNSFW
|
||||||
import com.vitorpamplona.quartz.nip71Video.VideoEvent
|
import com.vitorpamplona.quartz.nip71Video.VideoEvent
|
||||||
|
|
||||||
@@ -49,13 +53,13 @@ fun JustVideoDisplay(
|
|||||||
val event = (videoEvent as? Event) ?: return
|
val event = (videoEvent as? Event) ?: return
|
||||||
|
|
||||||
val imeta = videoEvent.imetaTags().getOrNull(0) ?: return
|
val imeta = videoEvent.imetaTags().getOrNull(0) ?: return
|
||||||
|
val isSensitive = event.isSensitiveOrNSFW()
|
||||||
|
val reasons = collectContentWarningReasons(event)
|
||||||
|
val isImage = imeta.mimeType?.startsWith("image/") == true || RichTextParser.isImageUrl(imeta.url)
|
||||||
|
|
||||||
val content by
|
val content by
|
||||||
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 isSensitive = event.isSensitiveOrNSFW()
|
|
||||||
val contentWarning = event.contentWarningReason()
|
|
||||||
|
|
||||||
mutableStateOf<BaseMediaContent>(
|
mutableStateOf<BaseMediaContent>(
|
||||||
if (isImage) {
|
if (isImage) {
|
||||||
@@ -66,8 +70,6 @@ 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 {
|
||||||
@@ -79,18 +81,27 @@ 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,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
ZoomableContentView(
|
val ratio = imeta.dimension?.aspectRatio() ?: MediaAspectRatioCache.get(imeta.url)
|
||||||
content = content,
|
|
||||||
roundedCorner = roundedCorner,
|
ContentWarningGate(
|
||||||
contentScale = contentScale,
|
isSensitive = isSensitive,
|
||||||
|
reasons = reasons,
|
||||||
|
preloadUrls = if (isImage) listOf(imeta.url) else emptyList(),
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
)
|
modifier = mediaSizingModifier(ratio, contentScale),
|
||||||
|
backdrop = imeta.blurhash?.let { blurhash -> { BlurhashBackdrop(blurhash, content.description) } },
|
||||||
|
) {
|
||||||
|
ZoomableContentView(
|
||||||
|
content = content,
|
||||||
|
roundedCorner = roundedCorner,
|
||||||
|
contentScale = contentScale,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+40
-27
@@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.pictures
|
|||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
@@ -39,11 +40,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.SensitivityWarningOverBlurhashGrid
|
import com.vitorpamplona.amethyst.ui.components.BlurhashBackdrop
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.BlurhashGridBackdrop
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.ContentWarningGate
|
||||||
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
|
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
|
||||||
import com.vitorpamplona.amethyst.ui.components.collectPictureReasons
|
import com.vitorpamplona.amethyst.ui.components.collectContentWarningReasons
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.mediaSizingModifier
|
||||||
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
|
||||||
@@ -94,8 +99,7 @@ private fun PictureCardImage(
|
|||||||
) {
|
) {
|
||||||
val uri = note.toNostrUri()
|
val uri = note.toNostrUri()
|
||||||
val isSensitive = event.isSensitiveOrNSFW()
|
val isSensitive = event.isSensitiveOrNSFW()
|
||||||
val reasons = collectPictureReasons(event)
|
val reasons = collectContentWarningReasons(event)
|
||||||
val inGrid = event.imetaTags().size > 1
|
|
||||||
|
|
||||||
val images by
|
val images by
|
||||||
remember(note) {
|
remember(note) {
|
||||||
@@ -110,41 +114,50 @@ private fun PictureCardImage(
|
|||||||
blurhash = it.blurhash,
|
blurhash = it.blurhash,
|
||||||
dim = it.dimension,
|
dim = it.dimension,
|
||||||
uri = uri,
|
uri = uri,
|
||||||
contentWarning = reasons.firstOrNull(),
|
|
||||||
// In grid mode the warning is handled at the grid level to avoid
|
|
||||||
// one overlay per image. Single-image posts still warn per media.
|
|
||||||
isSensitive = isSensitive && !inGrid,
|
|
||||||
mimeType = it.mimeType,
|
mimeType = it.mimeType,
|
||||||
)
|
)
|
||||||
}.toImmutableList(),
|
}.toImmutableList(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (images.isNotEmpty()) {
|
if (images.isEmpty()) return
|
||||||
if (images.size == 1) {
|
|
||||||
|
if (images.size == 1) {
|
||||||
|
val single = images.first()
|
||||||
|
val ratio = single.dim?.aspectRatio() ?: MediaAspectRatioCache.get(single.url)
|
||||||
|
ContentWarningGate(
|
||||||
|
isSensitive = isSensitive,
|
||||||
|
reasons = reasons,
|
||||||
|
preloadUrls = listOf(single.url),
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
modifier = mediaSizingModifier(ratio, ContentScale.FillWidth),
|
||||||
|
backdrop = single.blurhash?.let { blurhash -> { BlurhashBackdrop(blurhash, single.description) } },
|
||||||
|
) {
|
||||||
ZoomableContentView(
|
ZoomableContentView(
|
||||||
content = images.first(),
|
content = single,
|
||||||
images = images,
|
images = images,
|
||||||
roundedCorner = false,
|
roundedCorner = false,
|
||||||
contentScale = ContentScale.FillWidth,
|
contentScale = ContentScale.FillWidth,
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
)
|
)
|
||||||
} else {
|
}
|
||||||
SensitivityWarningOverBlurhashGrid(
|
} else {
|
||||||
isSensitive = isSensitive,
|
ContentWarningGate(
|
||||||
reasons = reasons,
|
isSensitive = isSensitive,
|
||||||
media = images,
|
reasons = reasons,
|
||||||
accountViewModel = accountViewModel,
|
preloadUrls = images.map { it.url },
|
||||||
) {
|
accountViewModel = accountViewModel,
|
||||||
AutoNonlazyGrid(images.size) {
|
modifier = Modifier.fillMaxWidth().aspectRatio(1f),
|
||||||
ZoomableContentView(
|
backdrop = { BlurhashGridBackdrop(images) },
|
||||||
content = images[it],
|
) {
|
||||||
images = images,
|
AutoNonlazyGrid(images.size) {
|
||||||
roundedCorner = false,
|
ZoomableContentView(
|
||||||
contentScale = ContentScale.Crop,
|
content = images[it],
|
||||||
accountViewModel = accountViewModel,
|
images = images,
|
||||||
)
|
roundedCorner = false,
|
||||||
}
|
contentScale = ContentScale.Crop,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-13
@@ -42,15 +42,19 @@ 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.BlurhashBackdrop
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.ContentWarningGate
|
||||||
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
|
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.collectContentWarningReasons
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.mediaSizingModifier
|
||||||
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.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.nip36SensitiveContent.isSensitiveOrNSFW
|
||||||
import com.vitorpamplona.quartz.nip71Video.VideoEvent
|
import com.vitorpamplona.quartz.nip71Video.VideoEvent
|
||||||
import kotlin.text.ifEmpty
|
import kotlin.text.ifEmpty
|
||||||
@@ -99,13 +103,13 @@ private fun VideoCardImage(
|
|||||||
val event = (event as? Event) ?: return
|
val event = (event as? Event) ?: return
|
||||||
|
|
||||||
val imeta = videoEvent.imetaTags().getOrNull(0) ?: return
|
val imeta = videoEvent.imetaTags().getOrNull(0) ?: return
|
||||||
|
val isSensitive = event.isSensitiveOrNSFW()
|
||||||
|
val reasons = collectContentWarningReasons(event)
|
||||||
|
val isImage = imeta.mimeType?.startsWith("image/") == true || RichTextParser.isImageUrl(imeta.url)
|
||||||
|
|
||||||
val content by
|
val content by
|
||||||
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 isSensitive = event.isSensitiveOrNSFW()
|
|
||||||
val contentWarning = event.contentWarningReason()
|
|
||||||
|
|
||||||
mutableStateOf<BaseMediaContent>(
|
mutableStateOf<BaseMediaContent>(
|
||||||
if (isImage) {
|
if (isImage) {
|
||||||
@@ -116,8 +120,6 @@ 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 {
|
||||||
@@ -129,20 +131,29 @@ 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,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
ZoomableContentView(
|
val ratio = imeta.dimension?.aspectRatio() ?: MediaAspectRatioCache.get(imeta.url)
|
||||||
content = content,
|
|
||||||
roundedCorner = false,
|
ContentWarningGate(
|
||||||
contentScale = ContentScale.FillWidth,
|
isSensitive = isSensitive,
|
||||||
|
reasons = reasons,
|
||||||
|
preloadUrls = if (isImage) listOf(imeta.url) else emptyList(),
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
)
|
modifier = mediaSizingModifier(ratio, ContentScale.FillWidth),
|
||||||
|
backdrop = imeta.blurhash?.let { blurhash -> { BlurhashBackdrop(blurhash, content.description) } },
|
||||||
|
) {
|
||||||
|
ZoomableContentView(
|
||||||
|
content = content,
|
||||||
|
roundedCorner = false,
|
||||||
|
contentScale = ContentScale.FillWidth,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
+27
-15
@@ -42,14 +42,18 @@ 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.BlurhashBackdrop
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.ContentWarningGate
|
||||||
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
|
import com.vitorpamplona.amethyst.ui.components.ZoomableContentView
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.collectContentWarningReasons
|
||||||
|
import com.vitorpamplona.amethyst.ui.components.mediaSizingModifier
|
||||||
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.nip36SensitiveContent.isSensitiveOrNSFW
|
||||||
import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent
|
import com.vitorpamplona.quartz.nip94FileMetadata.FileHeaderEvent
|
||||||
import kotlin.text.ifEmpty
|
import kotlin.text.ifEmpty
|
||||||
@@ -96,16 +100,17 @@ private fun FileHeaderCardImage(
|
|||||||
) {
|
) {
|
||||||
val fullUrl = event.url() ?: return
|
val fullUrl = event.url() ?: return
|
||||||
|
|
||||||
|
val isSensitive = event.isSensitiveOrNSFW()
|
||||||
|
val reasons = collectContentWarningReasons(event)
|
||||||
|
val isImage = event.mimeType()?.startsWith("image/") == true || RichTextParser.isImageUrl(fullUrl)
|
||||||
|
val blurHash = event.blurhash()
|
||||||
|
val dimensions = event.dimensions()
|
||||||
|
|
||||||
val content by remember(note) {
|
val content by remember(note) {
|
||||||
val blurHash = event.blurhash()
|
|
||||||
val hash = event.hash()
|
val hash = event.hash()
|
||||||
val dimensions = event.dimensions()
|
|
||||||
val description = event.content.ifEmpty { null } ?: event.alt()
|
val description = event.content.ifEmpty { null } ?: event.alt()
|
||||||
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) {
|
||||||
@@ -116,8 +121,6 @@ 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 {
|
||||||
@@ -129,20 +132,29 @@ 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,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
ZoomableContentView(
|
val ratio = dimensions?.aspectRatio() ?: MediaAspectRatioCache.get(fullUrl)
|
||||||
content = content,
|
|
||||||
roundedCorner = false,
|
ContentWarningGate(
|
||||||
contentScale = ContentScale.FillWidth,
|
isSensitive = isSensitive,
|
||||||
|
reasons = reasons,
|
||||||
|
preloadUrls = if (isImage) listOf(fullUrl) else emptyList(),
|
||||||
accountViewModel = accountViewModel,
|
accountViewModel = accountViewModel,
|
||||||
)
|
modifier = mediaSizingModifier(ratio, ContentScale.FillWidth),
|
||||||
|
backdrop = blurHash?.let { blurhash -> { BlurhashBackdrop(blurhash, content.description) } },
|
||||||
|
) {
|
||||||
|
ZoomableContentView(
|
||||||
|
content = content,
|
||||||
|
roundedCorner = false,
|
||||||
|
contentScale = ContentScale.FillWidth,
|
||||||
|
accountViewModel = accountViewModel,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
+2
-6
@@ -51,7 +51,6 @@ 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)
|
||||||
|
|
||||||
@@ -63,12 +62,11 @@ 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, isSensitive, mimeType)
|
) : MediaUrlImage(url, description, hash, blurhash, dim, uri, contentWarning, mimeType)
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
open class MediaUrlVideo(
|
open class MediaUrlVideo(
|
||||||
@@ -81,7 +79,6 @@ 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)
|
||||||
|
|
||||||
@@ -96,12 +93,11 @@ 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, isSensitive, mimeType)
|
) : MediaUrlVideo(url, description, hash, dim, uri, artworkUri, authorName, blurhash, contentWarning, mimeType)
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
abstract class MediaPreloadedContent(
|
abstract class MediaPreloadedContent(
|
||||||
|
|||||||
Reference in New Issue
Block a user