Fallback to title text when logos are unavailable

This commit is contained in:
tapframe 2026-06-12 20:46:08 +05:30
parent 7f200c7c7d
commit f2c9978f76
6 changed files with 71 additions and 22 deletions

View file

@ -111,7 +111,7 @@ fun DetailFloatingHeader(
.padding(horizontal = 10.dp),
contentAlignment = Alignment.Center,
) {
if (meta.logo != null && !logoLoadError) {
if (!meta.logo.isNullOrBlank() && !logoLoadError) {
AsyncImage(
model = meta.logo,
contentDescription = stringResource(Res.string.detail_logo_content_description, meta.name),

View file

@ -32,7 +32,9 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
@ -79,6 +81,10 @@ fun DetailHero(
val heroChromeTopPadding = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() +
8.dp +
((40.dp - muteIconSize) / 2)
var logoLoadError by remember(meta.id, meta.logo) {
mutableStateOf(false)
}
val logoUrl = meta.logo?.takeIf { it.isNotBlank() }
Box(
modifier = Modifier
@ -198,9 +204,9 @@ fun DetailHero(
.padding(bottom = 8.dp),
horizontalAlignment = Alignment.CenterHorizontally,
) {
if (meta.logo != null) {
if (logoUrl != null && !logoLoadError) {
AsyncImage(
model = meta.logo,
model = logoUrl,
contentDescription = stringResource(Res.string.detail_logo_content_description, meta.name),
modifier = Modifier
.fillMaxWidth(if (isTablet) 0.56f else 0.6f)
@ -208,6 +214,7 @@ fun DetailHero(
.height(if (isTablet) 72.dp else 80.dp),
alignment = Alignment.Center,
contentScale = ContentScale.Fit,
onError = { logoLoadError = true },
)
} else {
Text(

View file

@ -30,8 +30,10 @@ import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@ -347,13 +349,18 @@ private fun HeroContentBlock(
layout: HomeHeroLayout,
onItemClick: ((MetaPreview) -> Unit)?,
) {
var logoLoadError by remember(item.type, item.id, item.logo) {
mutableStateOf(false)
}
val logoUrl = item.logo?.takeIf { it.isNotBlank() }
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = if (layout.isTablet) Alignment.Start else Alignment.CenterHorizontally,
) {
if (item.logo != null) {
if (logoUrl != null && !logoLoadError) {
AsyncImage(
model = item.logo,
model = logoUrl,
contentDescription = item.name,
modifier = Modifier
.fillMaxWidth(layout.logoWidthFraction)
@ -363,6 +370,7 @@ private fun HeroContentBlock(
},
alignment = if (layout.isTablet) Alignment.CenterStart else Alignment.Center,
contentScale = ContentScale.Fit,
onError = { logoLoadError = true },
)
} else {
Text(

View file

@ -47,6 +47,9 @@ import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@ -122,6 +125,8 @@ internal fun OpeningOverlay(
),
label = "openingOverlayContentScale",
)
var logoLoadError by remember(logo) { mutableStateOf(false) }
val logoUrl = logo?.takeIf { it.isNotBlank() }
Box(
modifier = modifier
@ -178,14 +183,14 @@ internal fun OpeningOverlay(
label = "openingOverlayP2pProgress",
)
val progressActive = targetProgress != null
if (logo != null) {
if (logoUrl != null && !logoLoadError) {
Box(
modifier = Modifier
.width(300.dp)
.height(180.dp),
) {
AsyncImage(
model = logo,
model = logoUrl,
contentDescription = null,
modifier = Modifier
.fillMaxSize()
@ -197,10 +202,11 @@ internal fun OpeningOverlay(
}
},
contentScale = ContentScale.Fit,
onError = { logoLoadError = true },
)
if (progressActive) {
AsyncImage(
model = logo,
model = logoUrl,
contentDescription = null,
modifier = Modifier
.fillMaxSize()
@ -377,6 +383,9 @@ internal fun PauseMetadataOverlay(
horizontalSafePadding: Dp,
modifier: Modifier = Modifier,
) {
var logoLoadError by remember(logo) { mutableStateOf(false) }
val logoUrl = logo?.takeIf { it.isNotBlank() }
BoxWithConstraints(
modifier = modifier
.background(
@ -429,13 +438,14 @@ internal fun PauseMetadataOverlay(
)
androidx.compose.foundation.layout.Spacer(modifier = Modifier.height(if (compactHeight) 8.dp else 12.dp))
if (!logo.isNullOrBlank()) {
if (logoUrl != null && !logoLoadError) {
AsyncImage(
model = logo,
model = logoUrl,
contentDescription = title,
contentScale = ContentScale.Fit,
alignment = Alignment.BottomStart,
modifier = Modifier.height(logoHeight),
onError = { logoLoadError = true },
)
} else {
Text(

View file

@ -152,6 +152,8 @@ fun StreamsScreen(
val noDirectStreamLinkText = stringResource(Res.string.streams_no_direct_link)
var streamActionsTarget by remember(videoId) { mutableStateOf<StreamItem?>(null) }
var preferredFilterApplied by remember(videoId) { mutableStateOf(false) }
var autoPlayOverlayLogoLoadError by remember(logo) { mutableStateOf(false) }
val autoPlayOverlayLogoUrl = logo?.takeIf { it.isNotBlank() }
val storedProgress = if (startFromBeginning) {
null
} else {
@ -316,13 +318,24 @@ fun StreamsScreen(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
if (!logo.isNullOrBlank()) {
if (autoPlayOverlayLogoUrl != null && !autoPlayOverlayLogoLoadError) {
AsyncImage(
model = logo,
contentDescription = null,
model = autoPlayOverlayLogoUrl,
contentDescription = title,
modifier = Modifier
.height(48.dp),
contentScale = ContentScale.Fit,
onError = { autoPlayOverlayLogoLoadError = true },
)
} else if (title.isNotBlank()) {
Text(
text = title,
style = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight.Black),
color = Color.White,
textAlign = TextAlign.Center,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.padding(horizontal = 24.dp),
)
}
CircularProgressIndicator(
@ -532,6 +545,9 @@ private fun MovieHeroBlock(
logo: String?,
modifier: Modifier = Modifier,
) {
var logoLoadError by remember(logo) { mutableStateOf(false) }
val logoUrl = logo?.takeIf { it.isNotBlank() }
Box(
modifier = modifier
.fillMaxWidth()
@ -539,14 +555,15 @@ private fun MovieHeroBlock(
.windowInsetsPadding(WindowInsets.safeDrawing.only(WindowInsetsSides.Top)),
contentAlignment = Alignment.Center,
) {
if (logo != null) {
if (logoUrl != null && !logoLoadError) {
AsyncImage(
model = logo,
contentDescription = null,
model = logoUrl,
contentDescription = title,
modifier = Modifier
.height(80.dp)
.fillMaxWidth(0.85f),
contentScale = ContentScale.Fit,
onError = { logoLoadError = true },
)
} else {
Text(

View file

@ -222,19 +222,23 @@ private fun TabletMovieInfoPanel(
logo: String?,
modifier: Modifier = Modifier,
) {
var logoLoadError by remember(logo) { mutableStateOf(false) }
val logoUrl = logo?.takeIf { it.isNotBlank() }
Column(
modifier = modifier.fillMaxWidth(0.8f),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
if (!logo.isNullOrBlank()) {
if (logoUrl != null && !logoLoadError) {
AsyncImage(
model = logo,
contentDescription = null,
model = logoUrl,
contentDescription = title,
modifier = Modifier
.fillMaxWidth()
.height(120.dp),
contentScale = ContentScale.Fit,
onError = { logoLoadError = true },
)
} else if (title.isNotBlank()) {
Text(
@ -277,6 +281,8 @@ private fun TabletEpisodeInfoPanel(
showTitle: String,
modifier: Modifier = Modifier,
) {
var logoLoadError by remember(logo) { mutableStateOf(false) }
val logoUrl = logo?.takeIf { it.isNotBlank() }
val textShadow = Shadow(
color = Color.Black,
offset = Offset(0f, 0f),
@ -288,14 +294,15 @@ private fun TabletEpisodeInfoPanel(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
if (!logo.isNullOrBlank()) {
if (logoUrl != null && !logoLoadError) {
AsyncImage(
model = logo,
contentDescription = null,
model = logoUrl,
contentDescription = showTitle,
modifier = Modifier
.fillMaxWidth()
.height(120.dp),
contentScale = ContentScale.Fit,
onError = { logoLoadError = true },
)
} else {
Text(