Tweak code
This commit is contained in:
155
app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/items/AlbumItem.kt
Normal file
155
app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/items/AlbumItem.kt
Normal file
@@ -0,0 +1,155 @@
|
||||
package it.vfsfitvnm.vimusic.ui.items
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.text.BasicText
|
||||
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.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.AsyncImage
|
||||
import it.vfsfitvnm.vimusic.models.Album
|
||||
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
|
||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||
import it.vfsfitvnm.vimusic.ui.styling.shimmer
|
||||
import it.vfsfitvnm.vimusic.utils.secondary
|
||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||
import it.vfsfitvnm.vimusic.utils.thumbnail
|
||||
import it.vfsfitvnm.youtubemusic.Innertube
|
||||
|
||||
@Composable
|
||||
fun AlbumItem(
|
||||
album: Album,
|
||||
thumbnailSizePx: Int,
|
||||
thumbnailSizeDp: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
alternative: Boolean = false
|
||||
) {
|
||||
AlbumItem(
|
||||
thumbnailUrl = album.thumbnailUrl,
|
||||
title = album.title,
|
||||
authors = album.authorsText,
|
||||
year = album.year,
|
||||
thumbnailSizePx = thumbnailSizePx,
|
||||
thumbnailSizeDp = thumbnailSizeDp,
|
||||
alternative = alternative,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AlbumItem(
|
||||
album: Innertube.AlbumItem,
|
||||
thumbnailSizePx: Int,
|
||||
thumbnailSizeDp: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
alternative: Boolean = false
|
||||
) {
|
||||
AlbumItem(
|
||||
thumbnailUrl = album.thumbnail?.url,
|
||||
title = album.info?.name,
|
||||
authors = album.authors?.joinToString("") { it.name ?: "" },
|
||||
year = album.year,
|
||||
thumbnailSizePx = thumbnailSizePx,
|
||||
thumbnailSizeDp = thumbnailSizeDp,
|
||||
alternative = alternative,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AlbumItem(
|
||||
thumbnailUrl: String?,
|
||||
title: String?,
|
||||
authors: String?,
|
||||
year: String?,
|
||||
thumbnailSizePx: Int,
|
||||
thumbnailSizeDp: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
alternative: Boolean = false
|
||||
) {
|
||||
val (_, typography, thumbnailShape) = LocalAppearance.current
|
||||
|
||||
ItemContainer(
|
||||
alternative = alternative,
|
||||
thumbnailSizeDp = thumbnailSizeDp,
|
||||
modifier = modifier
|
||||
) {
|
||||
AsyncImage(
|
||||
model = thumbnailUrl?.thumbnail(thumbnailSizePx),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier
|
||||
.clip(thumbnailShape)
|
||||
.size(thumbnailSizeDp)
|
||||
)
|
||||
|
||||
ItemInfoContainer {
|
||||
BasicText(
|
||||
text = title ?: "",
|
||||
style = typography.xs.semiBold,
|
||||
maxLines = if (alternative) 1 else 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
||||
if (!alternative) {
|
||||
authors?.let {
|
||||
BasicText(
|
||||
text = authors,
|
||||
style = typography.xs.semiBold.secondary,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
BasicText(
|
||||
text = year ?: "",
|
||||
style = typography.xxs.semiBold.secondary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier
|
||||
.padding(top = 4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AlbumItemPlaceholder(
|
||||
thumbnailSizeDp: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
alternative: Boolean = false
|
||||
) {
|
||||
val (colorPalette, _, thumbnailShape) = LocalAppearance.current
|
||||
|
||||
ItemContainer(
|
||||
alternative = alternative,
|
||||
thumbnailSizeDp = thumbnailSizeDp,
|
||||
modifier = modifier
|
||||
) {
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.background(color = colorPalette.shimmer, shape = thumbnailShape)
|
||||
.size(thumbnailSizeDp)
|
||||
)
|
||||
|
||||
ItemInfoContainer {
|
||||
TextPlaceholder()
|
||||
|
||||
if (!alternative) {
|
||||
TextPlaceholder()
|
||||
}
|
||||
|
||||
TextPlaceholder(
|
||||
modifier = Modifier
|
||||
.padding(top = 4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
145
app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/items/ArtistItem.kt
Normal file
145
app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/items/ArtistItem.kt
Normal file
@@ -0,0 +1,145 @@
|
||||
package it.vfsfitvnm.vimusic.ui.items
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.text.BasicText
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.AsyncImage
|
||||
import it.vfsfitvnm.vimusic.models.Artist
|
||||
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
|
||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||
import it.vfsfitvnm.vimusic.ui.styling.shimmer
|
||||
import it.vfsfitvnm.vimusic.utils.secondary
|
||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||
import it.vfsfitvnm.vimusic.utils.thumbnail
|
||||
import it.vfsfitvnm.youtubemusic.Innertube
|
||||
|
||||
@Composable
|
||||
fun ArtistItem(
|
||||
artist: Artist,
|
||||
thumbnailSizePx: Int,
|
||||
thumbnailSizeDp: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
alternative: Boolean = false,
|
||||
) {
|
||||
ArtistItem(
|
||||
thumbnailUrl = artist.thumbnailUrl,
|
||||
name = artist.name,
|
||||
subscribersCount = null,
|
||||
thumbnailSizePx = thumbnailSizePx,
|
||||
thumbnailSizeDp = thumbnailSizeDp,
|
||||
modifier = modifier,
|
||||
alternative = alternative
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ArtistItem(
|
||||
artist: Innertube.ArtistItem,
|
||||
thumbnailSizePx: Int,
|
||||
thumbnailSizeDp: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
alternative: Boolean = false,
|
||||
) {
|
||||
ArtistItem(
|
||||
thumbnailUrl = artist.thumbnail?.url,
|
||||
name = artist.info?.name,
|
||||
subscribersCount = artist.subscribersCountText,
|
||||
thumbnailSizePx = thumbnailSizePx,
|
||||
thumbnailSizeDp = thumbnailSizeDp,
|
||||
modifier = modifier,
|
||||
alternative = alternative
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ArtistItem(
|
||||
thumbnailUrl: String?,
|
||||
name: String?,
|
||||
subscribersCount: String?,
|
||||
thumbnailSizePx: Int,
|
||||
thumbnailSizeDp: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
alternative: Boolean = false,
|
||||
) {
|
||||
val (_, typography) = LocalAppearance.current
|
||||
|
||||
ItemContainer(
|
||||
alternative = alternative,
|
||||
thumbnailSizeDp = thumbnailSizeDp,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = modifier
|
||||
) {
|
||||
AsyncImage(
|
||||
model = thumbnailUrl?.thumbnail(thumbnailSizePx),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.requiredSize(thumbnailSizeDp)
|
||||
)
|
||||
|
||||
ItemInfoContainer(
|
||||
horizontalAlignment = if (alternative) Alignment.CenterHorizontally else Alignment.Start,
|
||||
) {
|
||||
BasicText(
|
||||
text = name ?: "",
|
||||
style = typography.xs.semiBold,
|
||||
maxLines = if (alternative) 1 else 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
|
||||
subscribersCount?.let {
|
||||
BasicText(
|
||||
text = subscribersCount,
|
||||
style = typography.xxs.semiBold.secondary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier
|
||||
.padding(top = 4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ArtistItemPlaceholder(
|
||||
thumbnailSizeDp: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
alternative: Boolean = false,
|
||||
) {
|
||||
val (colorPalette) = LocalAppearance.current
|
||||
|
||||
ItemContainer(
|
||||
alternative = alternative,
|
||||
thumbnailSizeDp = thumbnailSizeDp,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = modifier
|
||||
) {
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.background(color = colorPalette.shimmer, shape = CircleShape)
|
||||
.size(thumbnailSizeDp)
|
||||
)
|
||||
|
||||
ItemInfoContainer(
|
||||
horizontalAlignment = if (alternative) Alignment.CenterHorizontally else Alignment.Start,
|
||||
) {
|
||||
TextPlaceholder()
|
||||
TextPlaceholder(
|
||||
modifier = Modifier
|
||||
.padding(top = 4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package it.vfsfitvnm.vimusic.ui.items
|
||||
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import it.vfsfitvnm.vimusic.ui.styling.Dimensions
|
||||
|
||||
@Composable
|
||||
inline fun ItemContainer(
|
||||
alternative: Boolean,
|
||||
thumbnailSizeDp: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
|
||||
verticalAlignment: Alignment.Vertical = Alignment.CenterVertically,
|
||||
content: @Composable (centeredModifier: Modifier) -> Unit
|
||||
) {
|
||||
if (alternative) {
|
||||
Column(
|
||||
horizontalAlignment = horizontalAlignment,
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
modifier = modifier
|
||||
.padding(vertical = Dimensions.itemsVerticalPadding, horizontal = 16.dp)
|
||||
.width(thumbnailSizeDp)
|
||||
) {
|
||||
content(
|
||||
centeredModifier = Modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Row(
|
||||
verticalAlignment = verticalAlignment,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
modifier = modifier
|
||||
.padding(vertical = Dimensions.itemsVerticalPadding, horizontal = 16.dp)
|
||||
.fillMaxWidth()
|
||||
) {
|
||||
content(
|
||||
centeredModifier = Modifier
|
||||
.align(Alignment.CenterVertically)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
inline fun ItemInfoContainer(
|
||||
modifier: Modifier = Modifier,
|
||||
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
|
||||
content: @Composable ColumnScope.() -> Unit
|
||||
) {
|
||||
Column(
|
||||
horizontalAlignment = horizontalAlignment,
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = modifier,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
package it.vfsfitvnm.vimusic.ui.items
|
||||
|
||||
import androidx.annotation.DrawableRes
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicText
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.AsyncImage
|
||||
import it.vfsfitvnm.vimusic.Database
|
||||
import it.vfsfitvnm.vimusic.models.PlaylistPreview
|
||||
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
|
||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||
import it.vfsfitvnm.vimusic.ui.styling.onOverlay
|
||||
import it.vfsfitvnm.vimusic.ui.styling.overlay
|
||||
import it.vfsfitvnm.vimusic.ui.styling.shimmer
|
||||
import it.vfsfitvnm.vimusic.utils.color
|
||||
import it.vfsfitvnm.vimusic.utils.medium
|
||||
import it.vfsfitvnm.vimusic.utils.secondary
|
||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||
import it.vfsfitvnm.vimusic.utils.thumbnail
|
||||
import it.vfsfitvnm.youtubemusic.Innertube
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
@Composable
|
||||
fun PlaylistItem(
|
||||
@DrawableRes icon: Int,
|
||||
colorTint: Color,
|
||||
name: String?,
|
||||
songCount: Int?,
|
||||
thumbnailSizeDp: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
alternative: Boolean = false,
|
||||
) {
|
||||
PlaylistItem(
|
||||
thumbnailContent = {
|
||||
Image(
|
||||
painter = painterResource(icon),
|
||||
contentDescription = null,
|
||||
colorFilter = ColorFilter.tint(colorTint),
|
||||
modifier = Modifier
|
||||
.align(Alignment.Center)
|
||||
.size(24.dp)
|
||||
)
|
||||
},
|
||||
songCount = songCount,
|
||||
name = name,
|
||||
channelName = null,
|
||||
thumbnailSizeDp = thumbnailSizeDp,
|
||||
modifier = modifier,
|
||||
alternative = alternative
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PlaylistItem(
|
||||
playlist: PlaylistPreview,
|
||||
thumbnailSizePx: Int,
|
||||
thumbnailSizeDp: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
alternative: Boolean = false,
|
||||
) {
|
||||
val thumbnails by remember {
|
||||
Database.playlistThumbnailUrls(playlist.playlist.id).distinctUntilChanged().map {
|
||||
it.map { url ->
|
||||
url.thumbnail(thumbnailSizePx / 2)
|
||||
}
|
||||
}
|
||||
}.collectAsState(initial = emptyList(), context = Dispatchers.IO)
|
||||
|
||||
PlaylistItem(
|
||||
thumbnailContent = {
|
||||
if (thumbnails.toSet().size == 1) {
|
||||
AsyncImage(
|
||||
model = thumbnails.first().thumbnail(thumbnailSizePx),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = it
|
||||
)
|
||||
} else {
|
||||
Box(
|
||||
modifier = it
|
||||
.fillMaxSize()
|
||||
) {
|
||||
listOf(
|
||||
Alignment.TopStart,
|
||||
Alignment.TopEnd,
|
||||
Alignment.BottomStart,
|
||||
Alignment.BottomEnd
|
||||
).forEachIndexed { index, alignment ->
|
||||
AsyncImage(
|
||||
model = thumbnails.getOrNull(index),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier
|
||||
.align(alignment)
|
||||
.size(thumbnailSizeDp / 2)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
songCount = playlist.songCount,
|
||||
name = playlist.playlist.name,
|
||||
channelName = null,
|
||||
thumbnailSizeDp = thumbnailSizeDp,
|
||||
modifier = modifier,
|
||||
alternative = alternative
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PlaylistItem(
|
||||
playlist: Innertube.PlaylistItem,
|
||||
thumbnailSizePx: Int,
|
||||
thumbnailSizeDp: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
alternative: Boolean = false,
|
||||
) {
|
||||
PlaylistItem(
|
||||
thumbnailUrl = playlist.thumbnail?.url,
|
||||
songCount = playlist.songCount,
|
||||
name = playlist.info?.name,
|
||||
channelName = playlist.channel?.name,
|
||||
thumbnailSizePx = thumbnailSizePx,
|
||||
thumbnailSizeDp = thumbnailSizeDp,
|
||||
modifier = modifier,
|
||||
alternative = alternative
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PlaylistItem(
|
||||
thumbnailUrl: String?,
|
||||
songCount: Int?,
|
||||
name: String?,
|
||||
channelName: String?,
|
||||
thumbnailSizePx: Int,
|
||||
thumbnailSizeDp: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
alternative: Boolean = false,
|
||||
) {
|
||||
PlaylistItem(
|
||||
thumbnailContent = {
|
||||
AsyncImage(
|
||||
model = thumbnailUrl?.thumbnail(thumbnailSizePx),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = it
|
||||
)
|
||||
},
|
||||
songCount = songCount,
|
||||
name = name,
|
||||
channelName = channelName,
|
||||
thumbnailSizeDp = thumbnailSizeDp,
|
||||
modifier = modifier,
|
||||
alternative = alternative,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PlaylistItem(
|
||||
thumbnailContent: @Composable BoxScope.(modifier: Modifier) -> Unit,
|
||||
songCount: Int?,
|
||||
name: String?,
|
||||
channelName: String?,
|
||||
thumbnailSizeDp: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
alternative: Boolean = false,
|
||||
) {
|
||||
val (colorPalette, typography, thumbnailShape) = LocalAppearance.current
|
||||
|
||||
ItemContainer(
|
||||
alternative = alternative,
|
||||
thumbnailSizeDp = thumbnailSizeDp,
|
||||
modifier = modifier
|
||||
) { centeredModifier ->
|
||||
Box(
|
||||
modifier = centeredModifier
|
||||
.clip(thumbnailShape)
|
||||
.background(color = colorPalette.background1)
|
||||
.requiredSize(thumbnailSizeDp)
|
||||
) {
|
||||
thumbnailContent(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
)
|
||||
|
||||
songCount?.let {
|
||||
BasicText(
|
||||
text = "$songCount",
|
||||
style = typography.xxs.medium.color(colorPalette.onOverlay),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier
|
||||
.padding(all = 4.dp)
|
||||
.background(color = colorPalette.overlay, shape = RoundedCornerShape(2.dp))
|
||||
.padding(horizontal = 4.dp, vertical = 2.dp)
|
||||
.align(Alignment.BottomEnd)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ItemInfoContainer(
|
||||
horizontalAlignment = if (alternative && channelName == null) Alignment.CenterHorizontally else Alignment.Start,
|
||||
) {
|
||||
BasicText(
|
||||
text = name ?: "",
|
||||
style = typography.xs.semiBold,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
||||
channelName?.let {
|
||||
BasicText(
|
||||
text = channelName,
|
||||
style = typography.xs.semiBold.secondary,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PlaylistItemPlaceholder(
|
||||
thumbnailSizeDp: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
alternative: Boolean = false,
|
||||
) {
|
||||
val (colorPalette, _, thumbnailShape) = LocalAppearance.current
|
||||
|
||||
ItemContainer(
|
||||
alternative = alternative,
|
||||
thumbnailSizeDp = thumbnailSizeDp,
|
||||
modifier = modifier
|
||||
) {
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.background(color = colorPalette.shimmer, shape = thumbnailShape)
|
||||
.size(thumbnailSizeDp)
|
||||
)
|
||||
|
||||
ItemInfoContainer(
|
||||
horizontalAlignment = if (alternative) Alignment.CenterHorizontally else Alignment.Start,
|
||||
) {
|
||||
TextPlaceholder()
|
||||
TextPlaceholder()
|
||||
}
|
||||
}
|
||||
}
|
||||
204
app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/items/SongItem.kt
Normal file
204
app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/items/SongItem.kt
Normal file
@@ -0,0 +1,204 @@
|
||||
package it.vfsfitvnm.vimusic.ui.items
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
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.layout.size
|
||||
import androidx.compose.foundation.text.BasicText
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.media3.common.MediaItem
|
||||
import coil.compose.AsyncImage
|
||||
import it.vfsfitvnm.vimusic.models.DetailedSong
|
||||
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
|
||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||
import it.vfsfitvnm.vimusic.ui.styling.shimmer
|
||||
import it.vfsfitvnm.vimusic.utils.medium
|
||||
import it.vfsfitvnm.vimusic.utils.secondary
|
||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||
import it.vfsfitvnm.vimusic.utils.thumbnail
|
||||
import it.vfsfitvnm.youtubemusic.Innertube
|
||||
|
||||
@Composable
|
||||
fun SongItem(
|
||||
song: Innertube.SongItem,
|
||||
thumbnailSizePx: Int,
|
||||
thumbnailSizeDp: Dp,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
SongItem(
|
||||
thumbnailUrl = song.thumbnail?.size(thumbnailSizePx),
|
||||
title = song.info?.name,
|
||||
authors = song.authors?.joinToString("") { it.name ?: "" },
|
||||
duration = song.durationText,
|
||||
thumbnailSizeDp = thumbnailSizeDp,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SongItem(
|
||||
song: MediaItem,
|
||||
thumbnailSizeDp: Dp,
|
||||
thumbnailSizePx: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
onThumbnailContent: (@Composable BoxScope.() -> Unit)? = null,
|
||||
trailingContent: (@Composable () -> Unit)? = null
|
||||
) {
|
||||
SongItem(
|
||||
thumbnailUrl = song.mediaMetadata.artworkUri.thumbnail(thumbnailSizePx)?.toString(),
|
||||
title = song.mediaMetadata.title.toString(),
|
||||
authors = song.mediaMetadata.artist.toString(),
|
||||
duration = song.mediaMetadata.extras?.getString("durationText"),
|
||||
thumbnailSizeDp = thumbnailSizeDp,
|
||||
onThumbnailContent = onThumbnailContent,
|
||||
trailingContent = trailingContent,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SongItem(
|
||||
song: DetailedSong,
|
||||
thumbnailSizePx: Int,
|
||||
thumbnailSizeDp: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
onThumbnailContent: (@Composable BoxScope.() -> Unit)? = null,
|
||||
trailingContent: (@Composable () -> Unit)? = null
|
||||
) {
|
||||
SongItem(
|
||||
thumbnailUrl = song.thumbnailUrl?.thumbnail(thumbnailSizePx),
|
||||
title = song.title,
|
||||
authors = song.artistsText,
|
||||
duration = song.durationText,
|
||||
thumbnailSizeDp = thumbnailSizeDp,
|
||||
onThumbnailContent = onThumbnailContent,
|
||||
trailingContent = trailingContent,
|
||||
modifier = modifier,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SongItem(
|
||||
thumbnailUrl: String?,
|
||||
title: String?,
|
||||
authors: String?,
|
||||
duration: String?,
|
||||
thumbnailSizeDp: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
onThumbnailContent: (@Composable BoxScope.() -> Unit)? = null,
|
||||
trailingContent: (@Composable () -> Unit)? = null
|
||||
) {
|
||||
SongItem(
|
||||
title = title,
|
||||
authors = authors,
|
||||
duration = duration,
|
||||
thumbnailSizeDp = thumbnailSizeDp,
|
||||
thumbnailContent = {
|
||||
AsyncImage(
|
||||
model = thumbnailUrl,
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier
|
||||
.clip(LocalAppearance.current.thumbnailShape)
|
||||
.fillMaxSize()
|
||||
)
|
||||
|
||||
onThumbnailContent?.invoke(this)
|
||||
},
|
||||
modifier = modifier,
|
||||
trailingContent = trailingContent
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SongItem(
|
||||
thumbnailContent: @Composable BoxScope.() -> Unit,
|
||||
title: String?,
|
||||
authors: String?,
|
||||
duration: String?,
|
||||
thumbnailSizeDp: Dp,
|
||||
modifier: Modifier = Modifier,
|
||||
trailingContent: @Composable (() -> Unit)? = null,
|
||||
) {
|
||||
val (_, typography) = LocalAppearance.current
|
||||
|
||||
ItemContainer(
|
||||
alternative = false,
|
||||
thumbnailSizeDp = thumbnailSizeDp,
|
||||
modifier = modifier
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(thumbnailSizeDp)
|
||||
) {
|
||||
thumbnailContent()
|
||||
}
|
||||
|
||||
ItemInfoContainer {
|
||||
BasicText(
|
||||
text = title ?: "",
|
||||
style = typography.xs.semiBold,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
BasicText(
|
||||
text = authors ?: "",
|
||||
style = typography.xs.semiBold.secondary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Clip,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
)
|
||||
|
||||
duration?.let {
|
||||
BasicText(
|
||||
text = duration,
|
||||
style = typography.xxs.secondary.medium,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier
|
||||
.padding(top = 4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun SongItemPlaceholder(
|
||||
thumbnailSizeDp: Dp,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val (colorPalette, _, thumbnailShape) = LocalAppearance.current
|
||||
|
||||
ItemContainer(
|
||||
alternative = false,
|
||||
thumbnailSizeDp =thumbnailSizeDp,
|
||||
modifier = modifier
|
||||
) {
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.background(color = colorPalette.shimmer, shape = thumbnailShape)
|
||||
.size(thumbnailSizeDp)
|
||||
)
|
||||
|
||||
ItemInfoContainer {
|
||||
TextPlaceholder()
|
||||
TextPlaceholder()
|
||||
}
|
||||
}
|
||||
}
|
||||
149
app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/items/VideoItem.kt
Normal file
149
app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/items/VideoItem.kt
Normal file
@@ -0,0 +1,149 @@
|
||||
package it.vfsfitvnm.vimusic.ui.items
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.text.BasicText
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil.compose.AsyncImage
|
||||
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
|
||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||
import it.vfsfitvnm.vimusic.ui.styling.onOverlay
|
||||
import it.vfsfitvnm.vimusic.ui.styling.overlay
|
||||
import it.vfsfitvnm.vimusic.ui.styling.shimmer
|
||||
import it.vfsfitvnm.vimusic.utils.color
|
||||
import it.vfsfitvnm.vimusic.utils.medium
|
||||
import it.vfsfitvnm.vimusic.utils.secondary
|
||||
import it.vfsfitvnm.vimusic.utils.semiBold
|
||||
import it.vfsfitvnm.youtubemusic.Innertube
|
||||
|
||||
@Composable
|
||||
fun VideoItem(
|
||||
video: Innertube.VideoItem,
|
||||
thumbnailHeightDp: Dp,
|
||||
thumbnailWidthDp: Dp,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
VideoItem(
|
||||
thumbnailUrl = video.thumbnail?.url,
|
||||
duration = video.durationText,
|
||||
title = video.info?.name,
|
||||
uploader = video.authors?.joinToString("") { it.name ?: "" },
|
||||
views = video.viewsText,
|
||||
thumbnailHeightDp = thumbnailHeightDp,
|
||||
thumbnailWidthDp = thumbnailWidthDp,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun VideoItem(
|
||||
thumbnailUrl: String?,
|
||||
duration: String?,
|
||||
title: String?,
|
||||
uploader: String?,
|
||||
views: String?,
|
||||
thumbnailHeightDp: Dp,
|
||||
thumbnailWidthDp: Dp,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val (colorPalette, typography, thumbnailShape) = LocalAppearance.current
|
||||
|
||||
ItemContainer(
|
||||
alternative = false,
|
||||
thumbnailSizeDp = 0.dp,
|
||||
modifier = modifier
|
||||
) {
|
||||
Box {
|
||||
AsyncImage(
|
||||
model = thumbnailUrl,
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier
|
||||
.clip(thumbnailShape)
|
||||
.size(width = thumbnailWidthDp, height = thumbnailHeightDp)
|
||||
)
|
||||
|
||||
duration?.let {
|
||||
BasicText(
|
||||
text = duration,
|
||||
style = typography.xxs.medium.color(colorPalette.onOverlay),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier
|
||||
.padding(all = 4.dp)
|
||||
.background(color = colorPalette.overlay, shape = RoundedCornerShape(2.dp))
|
||||
.padding(horizontal = 4.dp, vertical = 2.dp)
|
||||
.align(Alignment.BottomEnd)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ItemInfoContainer {
|
||||
BasicText(
|
||||
text = title ?: "",
|
||||
style = typography.xs.semiBold,
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
||||
BasicText(
|
||||
text = uploader ?: "",
|
||||
style = typography.xs.semiBold.secondary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
)
|
||||
|
||||
views?.let {
|
||||
BasicText(
|
||||
text = views,
|
||||
style = typography.xxs.medium.secondary,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier
|
||||
.padding(top = 4.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun VideoItemPlaceholder(
|
||||
thumbnailHeightDp: Dp,
|
||||
thumbnailWidthDp: Dp,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val (colorPalette, _, thumbnailShape) = LocalAppearance.current
|
||||
|
||||
ItemContainer(
|
||||
alternative = false,
|
||||
thumbnailSizeDp = 0.dp,
|
||||
modifier = modifier
|
||||
) {
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.background(color = colorPalette.shimmer, shape = thumbnailShape)
|
||||
.size(width = thumbnailWidthDp, height = thumbnailHeightDp)
|
||||
)
|
||||
|
||||
ItemInfoContainer {
|
||||
TextPlaceholder()
|
||||
TextPlaceholder()
|
||||
TextPlaceholder(
|
||||
modifier = Modifier
|
||||
.padding(top = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user