Avoids displaying loading symbol on our cached AsyncImage implementation

This commit is contained in:
Vitor Pamplona
2025-07-10 11:49:16 -04:00
parent ca41d4e10a
commit 8dbb1a889e
5 changed files with 115 additions and 65 deletions
@@ -82,8 +82,10 @@ fun MyAsyncImage(
Box(contentAlignment = Alignment.Center) {
if (onLoadingBackground != null) {
onLoadingBackground()
LoadingAnimation(Size40dp, Size6dp)
} else {
DisplayUrlWithLoadingSymbol(imageUrl)
}
DisplayUrlWithLoadingSymbol(imageUrl)
}
}
}
@@ -96,12 +98,11 @@ fun MyAsyncImage(
}
} else {
Box(loadedImageModifier, contentAlignment = Alignment.Center) {
if (onLoadingBackground != null) {
onLoadingBackground()
}
DisplayUrlWithLoadingSymbol(imageUrl)
onError()
}
}
} else {
ClickableUrl(urlText = imageUrl, url = imageUrl)
}
}
is AsyncImagePainter.State.Success -> {
@@ -20,12 +20,12 @@
*/
package com.vitorpamplona.amethyst.ui.screen.loggedIn.discover.nip90DVMs
import android.R.attr.author
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
@@ -35,7 +35,6 @@ import androidx.compose.runtime.Immutable
import androidx.compose.ui.Alignment.Companion.BottomStart
import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
@@ -44,10 +43,11 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import coil3.compose.AsyncImage
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.UserFinderFilterAssemblerSubscription
import com.vitorpamplona.amethyst.ui.components.MyAsyncImage
import com.vitorpamplona.amethyst.ui.layouts.LeftPictureLayout
import com.vitorpamplona.amethyst.ui.navigation.EmptyNav.nav
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.note.LikeReaction
import com.vitorpamplona.amethyst.ui.note.ZapReaction
@@ -55,13 +55,14 @@ import com.vitorpamplona.amethyst.ui.note.elements.BannerImage
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.screen.loggedIn.dvms.observeAppDefinition
import com.vitorpamplona.amethyst.ui.theme.HalfTopPadding
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
import com.vitorpamplona.amethyst.ui.theme.RowColSpacing5dp
import com.vitorpamplona.amethyst.ui.theme.SimpleImageBorder
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
import com.vitorpamplona.amethyst.ui.theme.bitcoinColor
import com.vitorpamplona.amethyst.ui.theme.grayText
import com.vitorpamplona.amethyst.ui.theme.nip05
import com.vitorpamplona.quartz.nip01Core.relay.client.listeners.EmptyClientListener.onError
@Immutable
data class DVMCard(
@@ -87,25 +88,28 @@ fun RenderContentDVMThumb(
onImage = {
card.cover?.let {
Box(contentAlignment = BottomStart) {
AsyncImage(
model = it,
contentDescription = null,
MyAsyncImage(
imageUrl = it,
contentDescription = card.name,
contentScale = ContentScale.Crop,
modifier =
Modifier
.fillMaxSize()
.clip(QuoteBorder),
mainImageModifier = Modifier,
loadedImageModifier = SimpleImageBorder,
accountViewModel = accountViewModel,
onLoadingBackground = {
baseNote.author?.let { author ->
BannerImage(author, SimpleImageBorder, accountViewModel)
}
},
onError = {
baseNote.author?.let { author ->
BannerImage(author, SimpleImageBorder, accountViewModel)
}
},
)
}
} ?: run {
baseNote.author?.let {
BannerImage(
it,
Modifier
.fillMaxSize()
.clip(QuoteBorder),
accountViewModel,
)
baseNote.author?.let { author ->
BannerImage(author, SimpleImageBorder, accountViewModel)
}
}
},
@@ -22,6 +22,7 @@ package com.vitorpamplona.amethyst.ui.screen.loggedIn.dvms
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -44,8 +45,8 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Alignment.Companion.BottomStart
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
@@ -56,7 +57,6 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
import coil3.compose.AsyncImage
import com.vitorpamplona.amethyst.R
import com.vitorpamplona.amethyst.model.Note
import com.vitorpamplona.amethyst.model.User
@@ -66,17 +66,18 @@ import com.vitorpamplona.amethyst.service.relayClient.reqCommand.event.observeNo
import com.vitorpamplona.amethyst.service.relayClient.reqCommand.user.UserFinderFilterAssemblerSubscription
import com.vitorpamplona.amethyst.ui.actions.CrossfadeIfEnabled
import com.vitorpamplona.amethyst.ui.components.LoadNote
import com.vitorpamplona.amethyst.ui.components.MyAsyncImage
import com.vitorpamplona.amethyst.ui.feeds.FeedEmpty
import com.vitorpamplona.amethyst.ui.feeds.RefresheableBox
import com.vitorpamplona.amethyst.ui.layouts.DisappearingScaffold
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.note.NoteAuthorPicture
import com.vitorpamplona.amethyst.ui.note.ObserveZapIcon
import com.vitorpamplona.amethyst.ui.note.PayViaIntentDialog
import com.vitorpamplona.amethyst.ui.note.WatchNoteEvent
import com.vitorpamplona.amethyst.ui.note.ZapAmountChoicePopup
import com.vitorpamplona.amethyst.ui.note.ZapIcon
import com.vitorpamplona.amethyst.ui.note.ZappedIcon
import com.vitorpamplona.amethyst.ui.note.elements.BannerImage
import com.vitorpamplona.amethyst.ui.note.elements.customZapClick
import com.vitorpamplona.amethyst.ui.note.payViaIntent
import com.vitorpamplona.amethyst.ui.screen.RenderFeedState
@@ -87,10 +88,9 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.dvms.dal.NIP90ContentDiscov
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.DoubleVertSpacer
import com.vitorpamplona.amethyst.ui.theme.ModifierWidth3dp
import com.vitorpamplona.amethyst.ui.theme.QuoteBorder
import com.vitorpamplona.amethyst.ui.theme.SimpleImage75Modifier
import com.vitorpamplona.amethyst.ui.theme.Size20Modifier
import com.vitorpamplona.amethyst.ui.theme.Size35dp
import com.vitorpamplona.amethyst.ui.theme.Size75dp
import com.vitorpamplona.quartz.lightning.LnInvoiceUtil
import com.vitorpamplona.quartz.nip47WalletConnect.PayInvoiceErrorResponse
import com.vitorpamplona.quartz.nip89AppHandlers.definition.AppDefinitionEvent
@@ -324,16 +324,33 @@ fun FeedDVM(
val card = observeAppDefinition(appDefinitionNote, accountViewModel)
card.cover?.let {
AsyncImage(
model = it,
contentDescription = null,
contentScale = ContentScale.Crop,
modifier =
Modifier
.size(Size75dp)
.clip(QuoteBorder),
)
} ?: run { NoteAuthorPicture(appDefinitionNote, Size75dp, accountViewModel = accountViewModel, nav = nav) }
Box(contentAlignment = BottomStart) {
MyAsyncImage(
imageUrl = it,
contentDescription = card.name,
contentScale = ContentScale.Crop,
mainImageModifier = Modifier,
loadedImageModifier = SimpleImage75Modifier,
accountViewModel = accountViewModel,
onLoadingBackground = {
appDefinitionNote.author?.let { author ->
BannerImage(author, SimpleImage75Modifier, accountViewModel)
}
},
onError = {
appDefinitionNote.author?.let { author ->
BannerImage(author, SimpleImage75Modifier, accountViewModel)
}
},
)
}
} ?: run {
appDefinitionNote.author?.let { author ->
Box(contentAlignment = BottomStart) {
BannerImage(author, SimpleImage75Modifier, accountViewModel)
}
}
}
Spacer(modifier = DoubleVertSpacer)
@@ -592,16 +609,33 @@ fun FeedEmptyWithStatus(
val card = observeAppDefinition(appDefinitionNote, accountViewModel)
card.cover?.let {
AsyncImage(
model = it,
contentDescription = null,
contentScale = ContentScale.Crop,
modifier =
Modifier
.size(Size75dp)
.clip(QuoteBorder),
)
} ?: run { NoteAuthorPicture(appDefinitionNote, Size75dp, accountViewModel = accountViewModel, nav = nav) }
Box(contentAlignment = BottomStart) {
MyAsyncImage(
imageUrl = it,
contentDescription = card.name,
contentScale = ContentScale.Crop,
mainImageModifier = Modifier,
loadedImageModifier = SimpleImage75Modifier,
accountViewModel = accountViewModel,
onLoadingBackground = {
appDefinitionNote.author?.let { author ->
BannerImage(author, SimpleImage75Modifier, accountViewModel)
}
},
onError = {
appDefinitionNote.author?.let { author ->
BannerImage(author, SimpleImage75Modifier, accountViewModel)
}
},
)
}
} ?: run {
appDefinitionNote.author?.let { author ->
Box(contentAlignment = BottomStart) {
BannerImage(author, SimpleImage75Modifier, accountViewModel)
}
}
}
Spacer(modifier = DoubleVertSpacer)
@@ -21,23 +21,19 @@
package com.vitorpamplona.amethyst.ui.screen.loggedIn.dvms
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import coil3.compose.AsyncImage
import com.vitorpamplona.amethyst.ui.components.LoadNote
import com.vitorpamplona.amethyst.ui.components.MyAsyncImage
import com.vitorpamplona.amethyst.ui.navigation.INav
import com.vitorpamplona.amethyst.ui.navigation.TopBarExtensibleWithBackButton
import com.vitorpamplona.amethyst.ui.note.NoteAuthorPicture
import com.vitorpamplona.amethyst.ui.note.elements.BannerImage
import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel
import com.vitorpamplona.amethyst.ui.theme.DoubleHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.Size34dp
import com.vitorpamplona.amethyst.ui.theme.SimpleImage35Modifier
@Composable
fun DvmTopBar(
@@ -52,22 +48,34 @@ fun DvmTopBar(
val card = observeAppDefinition(appDefinitionNote, accountViewModel)
card.cover?.let {
AsyncImage(
model = it,
contentDescription = null,
MyAsyncImage(
imageUrl = it,
contentDescription = card.name,
contentScale = ContentScale.Crop,
modifier =
Modifier
.size(Size34dp)
.clip(shape = CircleShape),
mainImageModifier = Modifier,
loadedImageModifier = SimpleImage35Modifier,
accountViewModel = accountViewModel,
onLoadingBackground = {
appDefinitionNote.author?.let { author ->
BannerImage(author, SimpleImage35Modifier, accountViewModel)
}
},
onError = {
appDefinitionNote.author?.let { author ->
BannerImage(author, SimpleImage35Modifier, accountViewModel)
}
},
)
} ?: run { NoteAuthorPicture(baseNote = appDefinitionNote, size = Size34dp, accountViewModel = accountViewModel) }
} ?: run {
appDefinitionNote.author?.let { author ->
BannerImage(author, SimpleImage35Modifier, accountViewModel)
}
}
Spacer(modifier = DoubleHorzSpacer)
Text(
text = card.name,
fontWeight = FontWeight.Bold,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
@@ -340,6 +340,9 @@ val FollowSetImageModifier =
.clip(QuoteBorder)
.aspectRatio(ratio = 21f / 9f)
val SimpleImage75Modifier = Modifier.size(Size75dp).clip(QuoteBorder)
val SimpleImage35Modifier = Modifier.size(Size34dp).clip(shape = CircleShape)
val SimpleImageBorder = Modifier.fillMaxSize().clip(QuoteBorder)
val SimpleHeaderImage = Modifier.fillMaxWidth().heightIn(max = 200.dp)