Fixes matching image borders in the loading step of preview cards

This commit is contained in:
Vitor Pamplona
2025-07-10 09:43:36 -04:00
parent a2d16c173c
commit ec985d58e7
10 changed files with 36 additions and 31 deletions

View File

@@ -90,8 +90,19 @@ fun MyAsyncImage(
} }
is AsyncImagePainter.State.Error -> { is AsyncImagePainter.State.Error -> {
if (onError != null) { if (onError != null) {
if (ratio != null) {
Box(loadedImageModifier.aspectRatio(ratio), contentAlignment = Alignment.Center) {
onError() onError()
} }
} else {
Box(loadedImageModifier, contentAlignment = Alignment.Center) {
if (onLoadingBackground != null) {
onLoadingBackground()
}
DisplayUrlWithLoadingSymbol(imageUrl)
}
}
}
} }
is AsyncImagePainter.State.Success -> { is AsyncImagePainter.State.Success -> {
SubcomposeAsyncImageContent(loadedImageModifier) SubcomposeAsyncImageContent(loadedImageModifier)

View File

@@ -20,26 +20,23 @@
*/ */
package com.vitorpamplona.amethyst.ui.note package com.vitorpamplona.amethyst.ui.note
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.ui.note.elements.BannerImage import com.vitorpamplona.amethyst.ui.note.elements.BannerImage
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder import com.vitorpamplona.amethyst.ui.theme.SimpleImageBorder
@Composable @Composable
fun DisplayAuthorBanner( fun DisplayAuthorBanner(
note: Note, note: Note,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
modifier: Modifier = SimpleImageBorder,
) { ) {
WatchAuthor(note, accountViewModel) { WatchAuthor(note, accountViewModel) {
BannerImage( BannerImage(
it, it,
Modifier modifier,
.fillMaxSize()
.clip(QuoteBorder),
accountViewModel, accountViewModel,
) )
} }

View File

@@ -22,15 +22,12 @@ package com.vitorpamplona.amethyst.ui.note.elements
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.blur import androidx.compose.ui.draw.blur
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.unit.dp
import com.vitorpamplona.amethyst.R import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Note import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.model.User
@@ -41,6 +38,7 @@ import com.vitorpamplona.amethyst.ui.note.WatchAuthor
import com.vitorpamplona.amethyst.ui.painterRes import com.vitorpamplona.amethyst.ui.painterRes
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.SimpleHeaderImage
import com.vitorpamplona.amethyst.ui.theme.Size16dp import com.vitorpamplona.amethyst.ui.theme.Size16dp
import com.vitorpamplona.amethyst.ui.theme.Size55dp import com.vitorpamplona.amethyst.ui.theme.Size55dp
import com.vitorpamplona.amethyst.ui.theme.authorNotePictureForImageHeader import com.vitorpamplona.amethyst.ui.theme.authorNotePictureForImageHeader
@@ -49,10 +47,11 @@ import com.vitorpamplona.amethyst.ui.theme.authorNotePictureForImageHeader
fun DefaultImageHeader( fun DefaultImageHeader(
note: Note, note: Note,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
modifier: Modifier = SimpleHeaderImage,
) { ) {
WatchAuthor(baseNote = note, accountViewModel) { WatchAuthor(baseNote = note, accountViewModel) {
Box { Box {
BannerImage(it, Modifier.fillMaxWidth().heightIn(max = 200.dp), accountViewModel) BannerImage(it, modifier, accountViewModel)
Box(authorNotePictureForImageHeader.align(Alignment.BottomStart)) { Box(authorNotePictureForImageHeader.align(Alignment.BottomStart)) {
BaseUserPicture(it, Size55dp, accountViewModel, Modifier) BaseUserPicture(it, Size55dp, accountViewModel, Modifier)
@@ -65,10 +64,11 @@ fun DefaultImageHeader(
fun DefaultImageHeaderBackground( fun DefaultImageHeaderBackground(
note: Note, note: Note,
accountViewModel: AccountViewModel, accountViewModel: AccountViewModel,
modifier: Modifier = SimpleHeaderImage,
) { ) {
WatchAuthor(baseNote = note, accountViewModel) { WatchAuthor(baseNote = note, accountViewModel) {
Box { Box {
BannerImage(it, Modifier.fillMaxWidth().heightIn(max = 200.dp).blur(Size16dp), accountViewModel) BannerImage(it, modifier.blur(Size16dp), accountViewModel)
Box(authorNotePictureForImageHeader.align(Alignment.BottomStart)) { Box(authorNotePictureForImageHeader.align(Alignment.BottomStart)) {
BaseUserPicture(it, Size55dp, accountViewModel, Modifier) BaseUserPicture(it, Size55dp, accountViewModel, Modifier)

View File

@@ -24,6 +24,7 @@ import androidx.compose.foundation.border
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.Spacer import androidx.compose.foundation.layout.Spacer
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
@@ -99,7 +100,7 @@ fun RenderClassifieds(
) )
} }
} ?: run { } ?: run {
DefaultImageHeader(note, accountViewModel) DefaultImageHeader(note, accountViewModel, Modifier.fillMaxWidth())
} }
} }

View File

@@ -59,6 +59,7 @@ import com.vitorpamplona.amethyst.ui.note.getGradient
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.DividerThickness import com.vitorpamplona.amethyst.ui.theme.DividerThickness
import com.vitorpamplona.amethyst.ui.theme.FollowSetImageModifier
import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUserIds import com.vitorpamplona.quartz.nip01Core.tags.people.taggedUserIds
import com.vitorpamplona.quartz.nip51Lists.FollowListEvent import com.vitorpamplona.quartz.nip51Lists.FollowListEvent
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
@@ -97,13 +98,13 @@ fun DisplayFollowList(
), ),
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
mainImageModifier = Modifier.fillMaxWidth(), mainImageModifier = Modifier.fillMaxWidth(),
loadedImageModifier = Modifier, loadedImageModifier = FollowSetImageModifier,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
onLoadingBackground = { DefaultImageHeaderBackground(baseNote, accountViewModel) }, onLoadingBackground = { DefaultImageHeaderBackground(baseNote, accountViewModel) },
onError = { DefaultImageHeader(baseNote, accountViewModel) }, onError = { DefaultImageHeader(baseNote, accountViewModel) },
) )
} ?: run { } ?: run {
DefaultImageHeader(baseNote, accountViewModel) DefaultImageHeader(baseNote, accountViewModel, FollowSetImageModifier)
} }
Text( Text(

View File

@@ -262,7 +262,7 @@ fun DisplayEntryForNote(
RenderUserAsClickableText(author, null, accountViewModel, nav) RenderUserAsClickableText(author, null, accountViewModel, nav)
} }
val noteEvent = noteState?.note?.event as? BaseThreadedEvent ?: return val noteEvent = noteState.note.event as? BaseThreadedEvent ?: return
val description = remember(noteEvent) { noteEvent.tags.firstTagValueFor("title", "subject", "alt") } val description = remember(noteEvent) { noteEvent.tags.firstTagValueFor("title", "subject", "alt") }
@@ -274,7 +274,7 @@ fun DisplayEntryForNote(
onClick = { routeFor(note, accountViewModel.userProfile())?.let { nav.nav(it) } }, onClick = { routeFor(note, accountViewModel.userProfile())?.let { nav.nav(it) } },
) )
} else { } else {
DisplayEvent(noteEvent.id, noteEvent.kind, note.toNostrUri(), null, accountViewModel, nav) DisplayEvent(noteEvent.id, note.toNostrUri(), null, accountViewModel, nav)
} }
} }

View File

@@ -237,7 +237,7 @@ fun RenderLiveActivityEventInner(
onError = { DefaultImageHeader(baseNote, accountViewModel) }, onError = { DefaultImageHeader(baseNote, accountViewModel) },
) )
} }
} ?: run { DisplayAuthorBanner(baseNote, accountViewModel) } } ?: run { DisplayAuthorBanner(baseNote, accountViewModel, MaterialTheme.colorScheme.imageModifier) }
Text( Text(
text = stringRes(id = R.string.live_stream_has_ended), text = stringRes(id = R.string.live_stream_has_ended),

View File

@@ -44,6 +44,7 @@ import com.vitorpamplona.amethyst.ui.note.elements.DefaultImageHeaderBackground
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
import com.vitorpamplona.amethyst.ui.theme.SimpleImageBorder
import com.vitorpamplona.amethyst.ui.theme.Size5dp import com.vitorpamplona.amethyst.ui.theme.Size5dp
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
import com.vitorpamplona.amethyst.ui.theme.subtleBorder import com.vitorpamplona.amethyst.ui.theme.subtleBorder
@@ -93,14 +94,14 @@ fun LongFormHeader(
it, it,
), ),
contentScale = ContentScale.FillWidth, contentScale = ContentScale.FillWidth,
mainImageModifier = Modifier.fillMaxWidth(), mainImageModifier = SimpleImageBorder,
loadedImageModifier = Modifier, loadedImageModifier = Modifier,
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
onLoadingBackground = { DefaultImageHeaderBackground(note, accountViewModel) }, onLoadingBackground = { DefaultImageHeaderBackground(note, accountViewModel) },
onError = { DefaultImageHeader(note, accountViewModel) }, onError = { DefaultImageHeader(note, accountViewModel) },
) )
} ?: run { } ?: run {
DefaultImageHeader(note, accountViewModel) DefaultImageHeader(note, accountViewModel, SimpleImageBorder)
} }
title?.let { title?.let {
Text( Text(

View File

@@ -24,7 +24,6 @@ import androidx.compose.foundation.layout.Box
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.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.padding import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
@@ -35,7 +34,6 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.CenterVertically import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
@@ -58,7 +56,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.mockAccountViewModel
import com.vitorpamplona.amethyst.ui.stringRes import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder import com.vitorpamplona.amethyst.ui.theme.FollowSetImageModifier
import com.vitorpamplona.amethyst.ui.theme.RowColSpacing5dp import com.vitorpamplona.amethyst.ui.theme.RowColSpacing5dp
import com.vitorpamplona.amethyst.ui.theme.Size10dp import com.vitorpamplona.amethyst.ui.theme.Size10dp
import com.vitorpamplona.amethyst.ui.theme.Size25dp import com.vitorpamplona.amethyst.ui.theme.Size25dp
@@ -158,17 +156,13 @@ fun RenderFollowSetThumb(
it, it,
), ),
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
mainImageModifier = Modifier.fillMaxWidth(), mainImageModifier = Modifier,
loadedImageModifier = loadedImageModifier = FollowSetImageModifier,
Modifier
.fillMaxWidth()
.aspectRatio(ratio = 21f / 9f)
.clip(QuoteBorder),
accountViewModel = accountViewModel, accountViewModel = accountViewModel,
onLoadingBackground = { DefaultImageHeaderBackground(baseNote, accountViewModel) }, onLoadingBackground = { DefaultImageHeaderBackground(baseNote, accountViewModel) },
onError = { DefaultImageHeader(baseNote, accountViewModel) }, onError = { DefaultImageHeader(baseNote, accountViewModel) },
) )
} ?: run { DefaultImageHeader(baseNote, accountViewModel) } } ?: run { DefaultImageHeader(baseNote, accountViewModel, FollowSetImageModifier) }
Gallery(card.users, Modifier.padding(Size10dp), accountViewModel, nav) Gallery(card.users, Modifier.padding(Size10dp), accountViewModel, nav)
} }

View File

@@ -118,7 +118,7 @@ fun InnerRenderClassifiedsThumb(
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
) )
} ?: run { DisplayAuthorBanner(note, accountViewModel) } } ?: run { DisplayAuthorBanner(note, accountViewModel, Modifier.fillMaxSize()) }
Row( Row(
Modifier Modifier