Add detail cover placeholder & error.

This commit is contained in:
Thomas Andres Gomez 2023-04-06 17:17:08 +02:00
parent b338ea221a
commit 406e1f67d5
4 changed files with 118 additions and 21 deletions

View file

@ -2,6 +2,10 @@ package com.pixelized.biblib.ui.screen.home.detail
import android.content.res.Configuration.UI_MODE_NIGHT_NO import android.content.res.Configuration.UI_MODE_NIGHT_NO
import android.content.res.Configuration.UI_MODE_NIGHT_YES import android.content.res.Configuration.UI_MODE_NIGHT_YES
import android.graphics.drawable.Drawable
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
@ -18,6 +22,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.layout.onSizeChanged
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
@ -26,6 +31,10 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.core.text.HtmlCompat import androidx.core.text.HtmlCompat
import androidx.core.text.toSpannable import androidx.core.text.toSpannable
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.pixelized.biblib.R import com.pixelized.biblib.R
import com.pixelized.biblib.ui.composable.SpannedText import com.pixelized.biblib.ui.composable.SpannedText
import com.pixelized.biblib.ui.composable.animation.AnimatedDelayer import com.pixelized.biblib.ui.composable.animation.AnimatedDelayer
@ -114,24 +123,27 @@ fun DetailScreenContent(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
GlideImage( AnimatedOffset {
modifier = Modifier.height(MaterialTheme.bibLib.dimen.detail.cover), Cover(
previewPlaceholder = R.drawable.ic_fondatoin_cover, modifier = Modifier
circularReveal = CircularReveal(duration = 1000), .fillMaxWidth()
contentScale = ContentScale.FillHeight, .height(MaterialTheme.bibLib.dimen.detail.cover),
imageModel = book.cover, cover = book.cover,
) )
Text( }
modifier = Modifier AnimatedOffset {
.clickable(onClick = onOpenOnBibLib) Text(
.padding( modifier = Modifier
horizontal = 8.dp, .clickable(onClick = onOpenOnBibLib)
vertical = 2.dp, .padding(
), horizontal = 8.dp,
style = MaterialTheme.bibLib.typography.base.caption, vertical = 2.dp,
color = MaterialTheme.bibLib.colors.typography.emphasis, ),
text = stringResource(id = R.string.detail_open_on_biblib) style = MaterialTheme.bibLib.typography.base.caption,
) color = MaterialTheme.bibLib.colors.typography.emphasis,
text = stringResource(id = R.string.detail_open_on_biblib)
)
}
} }
AnimatedOffset { AnimatedOffset {
@ -240,6 +252,52 @@ fun DetailScreenContent(
} }
} }
@Composable
private fun Cover(
modifier: Modifier = Modifier,
cover: String,
) {
val placeholder = remember(cover) { mutableStateOf(true) }
val error = remember(cover) { mutableStateOf(false) }
Box(
modifier = modifier,
contentAlignment = Alignment.Center
) {
GlideImage(
requestListener = rememberRequestListener(
placeholder = placeholder,
error = error,
),
previewPlaceholder = R.drawable.ic_fondatoin_cover,
circularReveal = CircularReveal(duration = 500),
contentScale = ContentScale.FillHeight,
imageModel = cover,
)
Box(
modifier = Modifier
.height(MaterialTheme.bibLib.dimen.detail.cover)
.aspectRatio(ratio = remember { 1f / 1.5f })
.placeholder(
shape = MaterialTheme.bibLib.shapes.bookThumbnailCoverLarge,
isShimmering = { error.value },
visible = { placeholder.value },
)
)
AnimatedVisibility(
visible = error.value,
enter = fadeIn(),
exit = fadeOut(),
) {
Icon(
modifier = Modifier.size(64.dp),
painter = painterResource(id = R.drawable.ic_no_photography_24),
contentDescription = null
)
}
}
}
@Composable @Composable
private fun Spacer(height: State<Dp>) { private fun Spacer(height: State<Dp>) {
Spacer(modifier = Modifier.height(height = height.value)) Spacer(modifier = Modifier.height(height = height.value))
@ -269,6 +327,37 @@ private fun TitleLabel(
} }
} }
@Composable
fun rememberRequestListener(
placeholder: MutableState<Boolean>,
error: MutableState<Boolean>,
): RequestListener<Drawable> {
return remember(placeholder, error) {
object : RequestListener<Drawable> {
override fun onLoadFailed(
exception: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean
): Boolean {
error.value = true
return true
}
override fun onResourceReady(
resource: Drawable?,
model: Any?,
target: Target<Drawable>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean {
placeholder.value = false
return false
}
}
}
}
@Composable @Composable
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_NO, heightDp = 1000) @Preview(showBackground = true, uiMode = UI_MODE_NIGHT_NO, heightDp = 1000)
@Preview(showBackground = true, uiMode = UI_MODE_NIGHT_YES, heightDp = 1000) @Preview(showBackground = true, uiMode = UI_MODE_NIGHT_YES, heightDp = 1000)

View file

@ -75,7 +75,7 @@ fun bibLibLightColors(
), ),
placeHolder: BibLibColor.PlaceHolder = BibLibColor.PlaceHolder( placeHolder: BibLibColor.PlaceHolder = BibLibColor.PlaceHolder(
color = BibLibColorPalette.LightGrey, color = BibLibColorPalette.LightGrey,
shimmer = BibLibColorPalette.Grey, shimmer = BibLibColorPalette.VeryLightGrey,
), ),
) = BibLibColor( ) = BibLibColor(
base = base, base = base,

View file

@ -48,12 +48,15 @@ fun Modifier.autofill(
@Composable @Composable
fun Modifier.placeholder( fun Modifier.placeholder(
color: Color = MaterialTheme.bibLib.colors.placeHolder.color, color: Color = MaterialTheme.bibLib.colors.placeHolder.color,
shimmer: Color = MaterialTheme.bibLib.colors.placeHolder.shimmer, shimmer: Color? = MaterialTheme.bibLib.colors.placeHolder.shimmer,
shape: Shape = CircleShape, shape: Shape = CircleShape,
isShimmering: () -> Boolean = { true },
visible: () -> Boolean, visible: () -> Boolean,
) = this.placeholder( ) = this.placeholder(
visible = visible(), visible = visible(),
color = color, color = color,
shape = shape, shape = shape,
highlight = PlaceholderHighlight.shimmer(highlightColor = shimmer), highlight = shimmer
?.takeIf { isShimmering() }
?.let { PlaceholderHighlight.shimmer(highlightColor = it) },
) )

View file

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#DFDFDF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M8.9,6.07L7.48,4.66L9,3h6l1.83,2H20c1.1,0 2,0.9 2,2v12c0,0.05 -0.01,0.1 -0.02,0.16L20,17.17V7h-4.05l-1.83,-2H9.88L8.9,6.07zM20.49,23.31L18.17,21H4c-1.1,0 -2,-0.9 -2,-2V7c0,-0.59 0.27,-1.12 0.68,-1.49l-2,-2L2.1,2.1l19.8,19.8L20.49,23.31zM9.19,12.02C9.08,12.33 9,12.65 9,13c0,1.66 1.34,3 3,3c0.35,0 0.67,-0.08 0.98,-0.19L9.19,12.02zM16.17,19l-1.68,-1.68C13.76,17.75 12.91,18 12,18c-2.76,0 -5,-2.24 -5,-5c0,-0.91 0.25,-1.76 0.68,-2.49L4.17,7H4v12H16.17zM14.81,11.98l2.07,2.07C16.96,13.71 17,13.36 17,13c0,-2.76 -2.24,-5 -5,-5c-0.36,0 -0.71,0.04 -1.06,0.12l2.07,2.07C13.85,10.49 14.51,11.15 14.81,11.98z"/>
</vector>