mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-09 08:01:20 +02:00
Handle genre cards
This commit is contained in:
parent
45e023d1b2
commit
00f6de8ec4
6 changed files with 181 additions and 104 deletions
|
|
@ -32,6 +32,7 @@ import kotlin.time.Duration
|
|||
data class BaseItem(
|
||||
val data: BaseItemDto,
|
||||
val useSeriesForPrimary: Boolean,
|
||||
val imageUrlOverride: String? = null,
|
||||
) : CardGridItem {
|
||||
val id get() = data.id
|
||||
|
||||
|
|
|
|||
|
|
@ -78,7 +78,8 @@ fun BannerCard(
|
|||
val imageUrl =
|
||||
remember(item, fillHeight, imageType) {
|
||||
if (item != null) {
|
||||
imageUrlService.getItemImageUrl(
|
||||
item.imageUrlOverride
|
||||
?: imageUrlService.getItemImageUrl(
|
||||
item,
|
||||
imageType,
|
||||
fillWidth = null,
|
||||
|
|
|
|||
|
|
@ -42,11 +42,29 @@ fun GenreCard(
|
|||
onLongClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
) = GenreCard(
|
||||
genreId = genre?.id,
|
||||
name = genre?.name,
|
||||
imageUrl = genre?.imageUrl,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
modifier = modifier,
|
||||
interactionSource = interactionSource,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun GenreCard(
|
||||
genreId: UUID?,
|
||||
name: String?,
|
||||
imageUrl: String?,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
) {
|
||||
val background = rememberIdColor(genre?.id).copy(alpha = .6f)
|
||||
val background = rememberIdColor(genreId).copy(alpha = .6f)
|
||||
Card(
|
||||
modifier =
|
||||
modifier,
|
||||
modifier = modifier,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
interactionSource = interactionSource,
|
||||
|
|
@ -63,12 +81,12 @@ fun GenreCard(
|
|||
.fillMaxSize()
|
||||
.clip(RoundedCornerShape(8.dp)),
|
||||
) {
|
||||
if (genre?.imageUrl.isNotNullOrBlank()) {
|
||||
if (imageUrl != null) {
|
||||
AsyncImage(
|
||||
model =
|
||||
ImageRequest
|
||||
.Builder(LocalContext.current)
|
||||
.data(genre.imageUrl)
|
||||
.data(imageUrl)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
contentScale = ContentScale.FillBounds,
|
||||
|
|
@ -88,7 +106,7 @@ fun GenreCard(
|
|||
.background(background),
|
||||
) {
|
||||
Text(
|
||||
text = genre?.name ?: "",
|
||||
text = name ?: "",
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
fontWeight = FontWeight.Bold,
|
||||
|
|
@ -112,7 +130,6 @@ private fun GenreCardPreview() {
|
|||
UUID.randomUUID(),
|
||||
"Adventure",
|
||||
null,
|
||||
Color.Black,
|
||||
)
|
||||
GenreCard(
|
||||
genre = genre,
|
||||
|
|
|
|||
|
|
@ -5,12 +5,12 @@ import androidx.compose.foundation.layout.Box
|
|||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
|
@ -102,51 +102,21 @@ class GenreViewModel
|
|||
.execute(api, request)
|
||||
.content.items
|
||||
.map {
|
||||
Genre(it.id, it.name ?: "", null, Color.Black)
|
||||
Genre(it.id, it.name ?: "", null)
|
||||
}
|
||||
withContext(Dispatchers.Main) {
|
||||
this@GenreViewModel.genres.value = genres
|
||||
loading.value = LoadingState.Success
|
||||
}
|
||||
val genreToUrl = ConcurrentHashMap<UUID, String?>()
|
||||
val semaphore = Semaphore(4)
|
||||
genres
|
||||
.map { genre ->
|
||||
viewModelScope.async(Dispatchers.IO) {
|
||||
semaphore.withPermit {
|
||||
val item =
|
||||
GetItemsRequestHandler
|
||||
.execute(
|
||||
api,
|
||||
GetItemsRequest(
|
||||
val genreToUrl =
|
||||
getGenreImageMap(
|
||||
api = api,
|
||||
imageUrlService = imageUrlService,
|
||||
genres = genres.map { it.id },
|
||||
parentId = itemId,
|
||||
recursive = true,
|
||||
limit = 1,
|
||||
sortBy = listOf(ItemSortBy.RANDOM),
|
||||
fields = listOf(ItemFields.GENRES),
|
||||
imageTypes = listOf(ImageType.BACKDROP),
|
||||
imageTypeLimit = 1,
|
||||
includeItemTypes = includeItemTypes,
|
||||
genreIds = listOf(genre.id),
|
||||
enableTotalRecordCount = false,
|
||||
),
|
||||
).content.items
|
||||
.firstOrNull()
|
||||
if (item != null) {
|
||||
genreToUrl[genre.id] =
|
||||
imageUrlService.getItemImageUrl(
|
||||
itemId = item.id,
|
||||
itemType = item.type,
|
||||
seriesId = null,
|
||||
useSeriesForPrimary = true,
|
||||
imageType = ImageType.BACKDROP,
|
||||
imageTags = item.imageTags.orEmpty(),
|
||||
fillWidth = cardWidthPx,
|
||||
cardWidthPx = cardWidthPx,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.awaitAll()
|
||||
val genresWithImages =
|
||||
genres.map {
|
||||
it.copy(
|
||||
|
|
@ -171,11 +141,61 @@ class GenreViewModel
|
|||
}
|
||||
}
|
||||
|
||||
suspend fun ViewModel.getGenreImageMap(
|
||||
api: ApiClient,
|
||||
imageUrlService: ImageUrlService,
|
||||
genres: List<UUID>,
|
||||
parentId: UUID,
|
||||
includeItemTypes: List<BaseItemKind>?,
|
||||
cardWidthPx: Int?,
|
||||
): Map<UUID, String?> {
|
||||
val genreToUrl = ConcurrentHashMap<UUID, String?>()
|
||||
val semaphore = Semaphore(4)
|
||||
genres
|
||||
.map { genreId ->
|
||||
viewModelScope.async(Dispatchers.IO) {
|
||||
semaphore.withPermit {
|
||||
val item =
|
||||
GetItemsRequestHandler
|
||||
.execute(
|
||||
api,
|
||||
GetItemsRequest(
|
||||
parentId = parentId,
|
||||
recursive = true,
|
||||
limit = 1,
|
||||
sortBy = listOf(ItemSortBy.RANDOM),
|
||||
fields = listOf(ItemFields.GENRES),
|
||||
imageTypes = listOf(ImageType.BACKDROP),
|
||||
imageTypeLimit = 1,
|
||||
includeItemTypes = includeItemTypes,
|
||||
genreIds = listOf(genreId),
|
||||
enableTotalRecordCount = false,
|
||||
),
|
||||
).content.items
|
||||
.firstOrNull()
|
||||
if (item != null) {
|
||||
genreToUrl[genreId] =
|
||||
imageUrlService.getItemImageUrl(
|
||||
itemId = item.id,
|
||||
itemType = item.type,
|
||||
seriesId = null,
|
||||
useSeriesForPrimary = true,
|
||||
imageType = ImageType.BACKDROP,
|
||||
imageTags = item.imageTags.orEmpty(),
|
||||
fillWidth = cardWidthPx,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}.awaitAll()
|
||||
return genreToUrl
|
||||
}
|
||||
|
||||
@Stable
|
||||
data class Genre(
|
||||
val id: UUID,
|
||||
val name: String,
|
||||
val imageUrl: String?,
|
||||
val color: Color,
|
||||
) : CardGridItem {
|
||||
override val gridId: String get() = id.toString()
|
||||
override val playable: Boolean = false
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ import com.github.damontecres.wholphin.data.model.BaseItem
|
|||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||
import com.github.damontecres.wholphin.ui.Cards
|
||||
import com.github.damontecres.wholphin.ui.cards.BannerCard
|
||||
import com.github.damontecres.wholphin.ui.cards.GenreCard
|
||||
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
||||
import com.github.damontecres.wholphin.ui.components.CircularProgress
|
||||
import com.github.damontecres.wholphin.ui.components.DialogParams
|
||||
|
|
@ -326,26 +327,11 @@ fun HomePageContent(
|
|||
.animateItem(),
|
||||
horizontalPadding = viewOptions.spacing.dp,
|
||||
cardContent = { index, item, cardModifier, onClick, onLongClick ->
|
||||
BannerCard(
|
||||
name = item?.data?.seriesName ?: item?.name,
|
||||
item = item,
|
||||
aspectRatio = viewOptions.aspectRatio.ratio,
|
||||
imageType = viewOptions.imageType.imageType,
|
||||
imageContentScale = viewOptions.contentScale.scale,
|
||||
cornerText = item?.ui?.episdodeUnplayedCornerText,
|
||||
played = item?.data?.userData?.played ?: false,
|
||||
favorite = item?.favorite ?: false,
|
||||
playPercent =
|
||||
item?.data?.userData?.playedPercentage
|
||||
?: 0.0,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
modifier =
|
||||
val mod =
|
||||
cardModifier
|
||||
.onFocusChanged {
|
||||
if (it.isFocused) {
|
||||
position = RowColumn(rowIndex, index)
|
||||
// item?.let(onUpdateBackdrop)
|
||||
}
|
||||
if (it.isFocused && onFocusPosition != null) {
|
||||
val nonEmptyRowBefore =
|
||||
|
|
@ -368,10 +354,40 @@ fun HomePageContent(
|
|||
return@onKeyEvent true
|
||||
}
|
||||
return@onKeyEvent false
|
||||
},
|
||||
}
|
||||
when (item?.type) {
|
||||
BaseItemKind.GENRE -> {
|
||||
GenreCard(
|
||||
genreId = item.id,
|
||||
name = item.name,
|
||||
imageUrl = item.imageUrlOverride,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
modifier = mod.height(viewOptions.heightDp.dp),
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
BannerCard(
|
||||
name = item?.data?.seriesName ?: item?.name,
|
||||
item = item,
|
||||
aspectRatio = viewOptions.aspectRatio.ratio,
|
||||
imageType = viewOptions.imageType.imageType,
|
||||
imageContentScale = viewOptions.contentScale.scale,
|
||||
cornerText = item?.ui?.episdodeUnplayedCornerText,
|
||||
played = item?.data?.userData?.played ?: false,
|
||||
favorite = item?.favorite ?: false,
|
||||
playPercent =
|
||||
item?.data?.userData?.playedPercentage
|
||||
?: 0.0,
|
||||
onClick = onClick,
|
||||
onLongClick = onLongClick,
|
||||
modifier = mod,
|
||||
interactionSource = null,
|
||||
cardHeight = viewOptions.heightDp.dp,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,9 +32,13 @@ import com.github.damontecres.wholphin.data.model.HomeRowConfig
|
|||
import com.github.damontecres.wholphin.data.model.HomeRowConfigDisplay
|
||||
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
|
||||
import com.github.damontecres.wholphin.services.BackdropService
|
||||
import com.github.damontecres.wholphin.services.ImageUrlService
|
||||
import com.github.damontecres.wholphin.services.LatestNextUpService
|
||||
import com.github.damontecres.wholphin.services.UserPreferencesService
|
||||
import com.github.damontecres.wholphin.ui.AspectRatio
|
||||
import com.github.damontecres.wholphin.ui.Cards
|
||||
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||
import com.github.damontecres.wholphin.ui.components.getGenreImageMap
|
||||
import com.github.damontecres.wholphin.ui.launchIO
|
||||
import com.github.damontecres.wholphin.ui.main.HomePageContent
|
||||
import com.github.damontecres.wholphin.ui.main.LatestData
|
||||
|
|
@ -65,6 +69,7 @@ class HomePageSettingsViewModel
|
|||
private val navDrawerItemRepository: NavDrawerItemRepository,
|
||||
private val backdropService: BackdropService,
|
||||
private val latestNextUpService: LatestNextUpService,
|
||||
private val imageUrlService: ImageUrlService,
|
||||
) : ViewModel() {
|
||||
private val _state = MutableStateFlow(HomePageSettingsState.EMPTY)
|
||||
val state: StateFlow<HomePageSettingsState> = _state
|
||||
|
|
@ -197,7 +202,21 @@ class HomePageSettingsViewModel
|
|||
GetGenresRequestHandler
|
||||
.execute(api, request)
|
||||
.content.items
|
||||
.map { BaseItem(it, false) }
|
||||
val genreIds = items.map { it.id }
|
||||
val genreImages =
|
||||
getGenreImageMap(
|
||||
api = api,
|
||||
imageUrlService = imageUrlService,
|
||||
genres = genreIds,
|
||||
parentId = row.parentId,
|
||||
includeItemTypes = null,
|
||||
cardWidthPx = null,
|
||||
)
|
||||
val genres =
|
||||
items.map {
|
||||
BaseItem(it, false, genreImages[it.id])
|
||||
}
|
||||
|
||||
val name =
|
||||
_state.value.libraries
|
||||
.firstOrNull { it.itemId == row.parentId }
|
||||
|
|
@ -208,7 +227,7 @@ class HomePageSettingsViewModel
|
|||
listOf(
|
||||
HomeRowLoadingState.Success(
|
||||
title,
|
||||
items,
|
||||
genres,
|
||||
viewOptions = row.viewOptions,
|
||||
),
|
||||
)
|
||||
|
|
@ -347,7 +366,10 @@ class HomePageSettingsViewModel
|
|||
HomeRowConfig.Genres(
|
||||
UUID.randomUUID(),
|
||||
library.itemId,
|
||||
HomeRowViewOptions(),
|
||||
HomeRowViewOptions(
|
||||
heightDp = (Cards.HEIGHT_2X3_DP * .75f).toInt(),
|
||||
aspectRatio = AspectRatio.WIDE,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue