mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-09 08:01:20 +02:00
Use series details page
This commit is contained in:
parent
887dd7001c
commit
833e1d2ea1
11 changed files with 283 additions and 29 deletions
|
|
@ -25,14 +25,14 @@ data class BaseItem(
|
||||||
@Transient
|
@Transient
|
||||||
val indexNumber = data.indexNumber
|
val indexNumber = data.indexNumber
|
||||||
|
|
||||||
fun destination(): Destination.MediaItem {
|
fun destination(): Destination {
|
||||||
val result =
|
val result =
|
||||||
// Redirect episodes & seasons to their series if possible
|
// Redirect episodes & seasons to their series if possible
|
||||||
when (type) {
|
when (type) {
|
||||||
BaseItemKind.EPISODE -> {
|
BaseItemKind.EPISODE -> {
|
||||||
data.indexNumber?.let { episode ->
|
data.indexNumber?.let { episode ->
|
||||||
data.parentIndexNumber?.let { season ->
|
data.parentIndexNumber?.let { season ->
|
||||||
Destination.MediaItem(
|
Destination.SeriesOverview(
|
||||||
data.seriesId!!,
|
data.seriesId!!,
|
||||||
BaseItemKind.SERIES,
|
BaseItemKind.SERIES,
|
||||||
this,
|
this,
|
||||||
|
|
@ -43,8 +43,8 @@ data class BaseItem(
|
||||||
}
|
}
|
||||||
|
|
||||||
BaseItemKind.SEASON ->
|
BaseItemKind.SEASON ->
|
||||||
data.parentIndexNumber?.let { season ->
|
data.indexNumber?.let { season ->
|
||||||
Destination.MediaItem(
|
Destination.SeriesOverview(
|
||||||
data.seriesId!!,
|
data.seriesId!!,
|
||||||
BaseItemKind.SERIES,
|
BaseItemKind.SERIES,
|
||||||
this,
|
this,
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ fun CardGrid(
|
||||||
positionCallback: ((columns: Int, position: Int) -> Unit)? = null,
|
positionCallback: ((columns: Int, position: Int) -> Unit)? = null,
|
||||||
) {
|
) {
|
||||||
val startPosition = initialPosition.coerceIn(0, (pager.size - 1).coerceAtLeast(0))
|
val startPosition = initialPosition.coerceIn(0, (pager.size - 1).coerceAtLeast(0))
|
||||||
val columns = 5
|
val columns = 6
|
||||||
|
|
||||||
val gridState = rememberLazyGridState()
|
val gridState = rememberLazyGridState()
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
|
||||||
|
|
@ -30,15 +30,17 @@ abstract class ItemViewModel<T : DolphinModel>(
|
||||||
potential: BaseItem?,
|
potential: BaseItem?,
|
||||||
): BaseItem? =
|
): BaseItem? =
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
val fetchedItem =
|
// val fetchedItem =
|
||||||
when {
|
// when {
|
||||||
item.value == null && potential?.id == itemId -> potential
|
// item.value == null && potential?.id == itemId -> potential
|
||||||
item.value?.id == itemId -> item.value
|
// item.value?.id == itemId -> item.value
|
||||||
else -> {
|
// else -> {
|
||||||
val it = api.userLibraryApi.getItem(itemId).content
|
// val it = api.userLibraryApi.getItem(itemId).content
|
||||||
BaseItem.from(it, api)
|
// BaseItem.from(it, api)
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
val it = api.userLibraryApi.getItem(itemId).content
|
||||||
|
val fetchedItem = BaseItem.from(it, api)
|
||||||
return@withContext fetchedItem?.let {
|
return@withContext fetchedItem?.let {
|
||||||
val modelInstance = convertModel(fetchedItem.data, api)
|
val modelInstance = convertModel(fetchedItem.data, api)
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,61 @@
|
||||||
package com.github.damontecres.dolphin.ui.detail
|
package com.github.damontecres.dolphin.ui.detail
|
||||||
|
|
||||||
|
import androidx.compose.foundation.LocalIndication
|
||||||
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.interaction.collectIsFocusedAsState
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
import androidx.compose.runtime.livedata.observeAsState
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import androidx.compose.ui.draw.drawWithContent
|
||||||
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
|
import androidx.tv.material3.MaterialTheme
|
||||||
import androidx.tv.material3.Text
|
import androidx.tv.material3.Text
|
||||||
|
import coil3.compose.AsyncImage
|
||||||
|
import com.github.damontecres.dolphin.data.model.BaseItem
|
||||||
|
import com.github.damontecres.dolphin.data.model.Person
|
||||||
import com.github.damontecres.dolphin.preferences.UserPreferences
|
import com.github.damontecres.dolphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.dolphin.ui.cards.ItemRow
|
import com.github.damontecres.dolphin.ui.cards.ItemRow
|
||||||
|
import com.github.damontecres.dolphin.ui.cards.PersonRow
|
||||||
|
import com.github.damontecres.dolphin.ui.components.DotSeparatedRow
|
||||||
import com.github.damontecres.dolphin.ui.components.ErrorMessage
|
import com.github.damontecres.dolphin.ui.components.ErrorMessage
|
||||||
import com.github.damontecres.dolphin.ui.components.LoadingPage
|
import com.github.damontecres.dolphin.ui.components.LoadingPage
|
||||||
|
import com.github.damontecres.dolphin.ui.components.StarRating
|
||||||
|
import com.github.damontecres.dolphin.ui.components.StarRatingPrecision
|
||||||
|
import com.github.damontecres.dolphin.ui.isNotNullOrBlank
|
||||||
|
import com.github.damontecres.dolphin.ui.letNotEmpty
|
||||||
import com.github.damontecres.dolphin.ui.nav.Destination
|
import com.github.damontecres.dolphin.ui.nav.Destination
|
||||||
import com.github.damontecres.dolphin.ui.nav.NavigationManager
|
import com.github.damontecres.dolphin.ui.nav.NavigationManager
|
||||||
|
import com.github.damontecres.dolphin.ui.playOnClickSound
|
||||||
|
import com.github.damontecres.dolphin.ui.playSoundOnFocus
|
||||||
|
import com.github.damontecres.dolphin.ui.roundMinutes
|
||||||
|
import com.github.damontecres.dolphin.ui.tryRequestFocus
|
||||||
import com.github.damontecres.dolphin.util.LoadingState
|
import com.github.damontecres.dolphin.util.LoadingState
|
||||||
|
import org.jellyfin.sdk.model.extensions.ticks
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SeriesDetails(
|
fun SeriesDetails(
|
||||||
|
|
@ -28,25 +68,119 @@ fun SeriesDetails(
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
viewModel.init(destination.itemId, destination.item, null, null)
|
viewModel.init(destination.itemId, destination.item, null, null)
|
||||||
}
|
}
|
||||||
|
val loading by viewModel.loading.observeAsState(LoadingState.Loading)
|
||||||
|
|
||||||
val item by viewModel.item.observeAsState()
|
val item by viewModel.item.observeAsState()
|
||||||
val seasons by viewModel.seasons.observeAsState(ItemListAndMapping.empty())
|
val seasons by viewModel.seasons.observeAsState(ItemListAndMapping.empty())
|
||||||
val loading by viewModel.loading.observeAsState(LoadingState.Loading)
|
val people by viewModel.people.observeAsState(listOf())
|
||||||
|
|
||||||
when (val state = loading) {
|
when (val state = loading) {
|
||||||
is LoadingState.Error -> ErrorMessage(state)
|
is LoadingState.Error -> ErrorMessage(state)
|
||||||
LoadingState.Loading -> LoadingPage()
|
LoadingState.Loading -> LoadingPage()
|
||||||
LoadingState.Success -> {
|
LoadingState.Success -> {
|
||||||
item?.let { item ->
|
item?.let { item ->
|
||||||
LazyColumn(modifier = modifier) {
|
SeriesDetailsContent(
|
||||||
|
preferences = preferences,
|
||||||
|
navigationManager = navigationManager,
|
||||||
|
series = item,
|
||||||
|
seasons = seasons,
|
||||||
|
people = people,
|
||||||
|
modifier = modifier,
|
||||||
|
overviewOnClick = {}, // TODO
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SeriesDetailsContent(
|
||||||
|
preferences: UserPreferences,
|
||||||
|
navigationManager: NavigationManager,
|
||||||
|
series: BaseItem,
|
||||||
|
seasons: ItemListAndMapping,
|
||||||
|
people: List<Person>,
|
||||||
|
overviewOnClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val dto = series.data
|
||||||
|
|
||||||
|
val seasonsFocusRequester = remember { FocusRequester() }
|
||||||
|
if (seasons.items.isNotEmpty()) {
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
seasonsFocusRequester.tryRequestFocus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
if (series.backdropImageUrl.isNotNullOrBlank()) {
|
||||||
|
val gradientColor = MaterialTheme.colorScheme.background
|
||||||
|
AsyncImage(
|
||||||
|
model = series.backdropImageUrl,
|
||||||
|
contentDescription = null,
|
||||||
|
contentScale = ContentScale.Crop,
|
||||||
|
alignment = Alignment.TopEnd,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.fillMaxHeight(.75f)
|
||||||
|
.alpha(.5f)
|
||||||
|
.drawWithContent {
|
||||||
|
drawContent()
|
||||||
|
drawRect(
|
||||||
|
Brush.verticalGradient(
|
||||||
|
colors = listOf(Color.Transparent, gradientColor),
|
||||||
|
startY = size.height * .5f,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
drawRect(
|
||||||
|
Brush.horizontalGradient(
|
||||||
|
colors = listOf(Color.Transparent, gradientColor),
|
||||||
|
endX = 0f,
|
||||||
|
startX = size.width * .75f,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.padding(16.dp)
|
||||||
|
.fillMaxSize(),
|
||||||
|
) {
|
||||||
|
LazyColumn(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
item {
|
||||||
|
SeriesDetailsHeader(
|
||||||
|
series = series,
|
||||||
|
overviewOnClick = overviewOnClick,
|
||||||
|
modifier = Modifier.fillMaxWidth(.7f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
item {
|
||||||
|
ItemRow(
|
||||||
|
title = "Seasons",
|
||||||
|
items = seasons.items,
|
||||||
|
onClickItem = { navigationManager.navigateTo(it.destination()) },
|
||||||
|
onLongClickItem = { },
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.focusRequester(seasonsFocusRequester),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (people.isNotEmpty()) {
|
||||||
item {
|
item {
|
||||||
Text(text = item.name ?: "Unknown")
|
PersonRow(
|
||||||
}
|
people = people,
|
||||||
item {
|
onClick = {},
|
||||||
ItemRow(
|
onLongClick = {},
|
||||||
title = "Seasons",
|
|
||||||
items = seasons.items,
|
|
||||||
onClickItem = { navigationManager.navigateTo(it.destination()) },
|
|
||||||
onLongClickItem = { },
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -55,3 +189,94 @@ fun SeriesDetails(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SeriesDetailsHeader(
|
||||||
|
series: BaseItem,
|
||||||
|
overviewOnClick: () -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
val dto = series.data
|
||||||
|
val details =
|
||||||
|
buildList {
|
||||||
|
dto.productionYear?.let { add(it.toString()) }
|
||||||
|
dto.runTimeTicks
|
||||||
|
?.ticks
|
||||||
|
?.roundMinutes
|
||||||
|
?.let { add(it.toString()) }
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = series.name ?: "Unknown",
|
||||||
|
style = MaterialTheme.typography.displayMedium,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
)
|
||||||
|
DotSeparatedRow(
|
||||||
|
texts = details,
|
||||||
|
textStyle = MaterialTheme.typography.headlineSmall,
|
||||||
|
)
|
||||||
|
|
||||||
|
dto.genres?.letNotEmpty {
|
||||||
|
Text(
|
||||||
|
text = it.joinToString(", "),
|
||||||
|
style = MaterialTheme.typography.bodyLarge,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
modifier = Modifier,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
dto.communityRating?.let {
|
||||||
|
if (it >= 0f) {
|
||||||
|
StarRating(
|
||||||
|
rating100 = (it * 10).toInt(),
|
||||||
|
onRatingChange = {},
|
||||||
|
enabled = false,
|
||||||
|
precision = StarRatingPrecision.HALF,
|
||||||
|
playSoundOnFocus = true,
|
||||||
|
modifier = Modifier.height(32.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dto.overview?.let { overview ->
|
||||||
|
val interactionSource = remember { MutableInteractionSource() }
|
||||||
|
val isFocused = interactionSource.collectIsFocusedAsState().value
|
||||||
|
val bgColor =
|
||||||
|
if (isFocused) {
|
||||||
|
MaterialTheme.colorScheme.onPrimary.copy(alpha = .4f)
|
||||||
|
} else {
|
||||||
|
Color.Unspecified
|
||||||
|
}
|
||||||
|
Box(
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.background(bgColor, shape = RoundedCornerShape(8.dp))
|
||||||
|
.playSoundOnFocus(true)
|
||||||
|
.clickable(
|
||||||
|
enabled = true,
|
||||||
|
interactionSource = interactionSource,
|
||||||
|
indication = LocalIndication.current,
|
||||||
|
) {
|
||||||
|
playOnClickSound(context)
|
||||||
|
overviewOnClick.invoke()
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = overview,
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
maxLines = 3,
|
||||||
|
overflow = TextOverflow.Ellipsis,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
.height(60.dp),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,10 @@ import androidx.media3.datasource.okhttp.OkHttpDataSource
|
||||||
import androidx.media3.exoplayer.ExoPlayer
|
import androidx.media3.exoplayer.ExoPlayer
|
||||||
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
|
import androidx.media3.exoplayer.source.DefaultMediaSourceFactory
|
||||||
import com.github.damontecres.dolphin.data.model.BaseItem
|
import com.github.damontecres.dolphin.data.model.BaseItem
|
||||||
|
import com.github.damontecres.dolphin.data.model.Person
|
||||||
import com.github.damontecres.dolphin.data.model.Video
|
import com.github.damontecres.dolphin.data.model.Video
|
||||||
import com.github.damontecres.dolphin.hilt.AuthOkHttpClient
|
import com.github.damontecres.dolphin.hilt.AuthOkHttpClient
|
||||||
|
import com.github.damontecres.dolphin.ui.letNotEmpty
|
||||||
import com.github.damontecres.dolphin.util.ApiRequestPager
|
import com.github.damontecres.dolphin.util.ApiRequestPager
|
||||||
import com.github.damontecres.dolphin.util.ExceptionHandler
|
import com.github.damontecres.dolphin.util.ExceptionHandler
|
||||||
import com.github.damontecres.dolphin.util.GetEpisodesRequestHandler
|
import com.github.damontecres.dolphin.util.GetEpisodesRequestHandler
|
||||||
|
|
@ -54,6 +56,7 @@ class SeriesViewModel
|
||||||
val loading = MutableLiveData<LoadingState>(LoadingState.Loading)
|
val loading = MutableLiveData<LoadingState>(LoadingState.Loading)
|
||||||
val seasons = MutableLiveData<ItemListAndMapping>(ItemListAndMapping.empty())
|
val seasons = MutableLiveData<ItemListAndMapping>(ItemListAndMapping.empty())
|
||||||
val episodes = MutableLiveData<ItemListAndMapping>(ItemListAndMapping.empty())
|
val episodes = MutableLiveData<ItemListAndMapping>(ItemListAndMapping.empty())
|
||||||
|
val people = MutableLiveData<List<Person>>(listOf())
|
||||||
|
|
||||||
fun init(
|
fun init(
|
||||||
itemId: UUID,
|
itemId: UUID,
|
||||||
|
|
@ -80,6 +83,11 @@ class SeriesViewModel
|
||||||
seasons.value = seasonsInfo
|
seasons.value = seasonsInfo
|
||||||
episodes.value = episodeInfo
|
episodes.value = episodeInfo
|
||||||
loading.value = LoadingState.Success
|
loading.value = LoadingState.Success
|
||||||
|
people.value =
|
||||||
|
item.data.people
|
||||||
|
?.letNotEmpty { people ->
|
||||||
|
people.map { Person.fromDto(it, api) }
|
||||||
|
}.orEmpty()
|
||||||
}
|
}
|
||||||
maybePlayThemeSong()
|
maybePlayThemeSong()
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,7 @@ data class SeriesOverviewPosition(
|
||||||
fun SeriesOverview(
|
fun SeriesOverview(
|
||||||
preferences: UserPreferences,
|
preferences: UserPreferences,
|
||||||
navigationManager: NavigationManager,
|
navigationManager: NavigationManager,
|
||||||
destination: Destination.MediaItem,
|
destination: Destination.SeriesOverview,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: SeriesViewModel = hiltViewModel(),
|
viewModel: SeriesViewModel = hiltViewModel(),
|
||||||
initialSeasonEpisode: SeasonEpisode? = null,
|
initialSeasonEpisode: SeasonEpisode? = null,
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ class MainViewModel
|
||||||
api.userLibraryApi
|
api.userLibraryApi
|
||||||
.getLatestMedia(request)
|
.getLatestMedia(request)
|
||||||
.content
|
.content
|
||||||
.map { BaseItem.Companion.from(it, api, true) }
|
.map { BaseItem.from(it, api, true) }
|
||||||
HomeRow(
|
HomeRow(
|
||||||
section = section,
|
section = section,
|
||||||
items = latest,
|
items = latest,
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ import com.github.damontecres.dolphin.data.JellyfinServer
|
||||||
import com.github.damontecres.dolphin.data.JellyfinUser
|
import com.github.damontecres.dolphin.data.JellyfinUser
|
||||||
import com.github.damontecres.dolphin.preferences.UserPreferences
|
import com.github.damontecres.dolphin.preferences.UserPreferences
|
||||||
import org.jellyfin.sdk.model.api.DeviceProfile
|
import org.jellyfin.sdk.model.api.DeviceProfile
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ApplicationContent(
|
fun ApplicationContent(
|
||||||
|
|
@ -34,6 +35,7 @@ fun ApplicationContent(
|
||||||
entryProvider = { key ->
|
entryProvider = { key ->
|
||||||
key as Destination
|
key as Destination
|
||||||
val contentKey = "${key}_${server.id}_${user.id}"
|
val contentKey = "${key}_${server.id}_${user.id}"
|
||||||
|
Timber.d("Navigate: %s", key)
|
||||||
NavEntry(key, contentKey = contentKey) {
|
NavEntry(key, contentKey = contentKey) {
|
||||||
if (key.fullScreen) {
|
if (key.fullScreen) {
|
||||||
DestinationContent(
|
DestinationContent(
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,14 @@ sealed class Destination(
|
||||||
@Serializable
|
@Serializable
|
||||||
data object Search : Destination()
|
data object Search : Destination()
|
||||||
|
|
||||||
|
@Serializable
|
||||||
|
data class SeriesOverview(
|
||||||
|
val itemId: UUID,
|
||||||
|
val type: BaseItemKind,
|
||||||
|
@Transient val item: BaseItem? = null,
|
||||||
|
val seasonEpisode: SeasonEpisode? = null,
|
||||||
|
) : Destination()
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class MediaItem(
|
data class MediaItem(
|
||||||
val itemId: UUID,
|
val itemId: UUID,
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import com.github.damontecres.dolphin.ui.components.LicenseInfo
|
||||||
import com.github.damontecres.dolphin.ui.detail.CollectionFolderDetails
|
import com.github.damontecres.dolphin.ui.detail.CollectionFolderDetails
|
||||||
import com.github.damontecres.dolphin.ui.detail.EpisodeDetails
|
import com.github.damontecres.dolphin.ui.detail.EpisodeDetails
|
||||||
import com.github.damontecres.dolphin.ui.detail.SeasonDetails
|
import com.github.damontecres.dolphin.ui.detail.SeasonDetails
|
||||||
|
import com.github.damontecres.dolphin.ui.detail.SeriesDetails
|
||||||
import com.github.damontecres.dolphin.ui.detail.VideoDetails
|
import com.github.damontecres.dolphin.ui.detail.VideoDetails
|
||||||
import com.github.damontecres.dolphin.ui.detail.movie.MovieDetails
|
import com.github.damontecres.dolphin.ui.detail.movie.MovieDetails
|
||||||
import com.github.damontecres.dolphin.ui.detail.series.SeriesOverview
|
import com.github.damontecres.dolphin.ui.detail.series.SeriesOverview
|
||||||
|
|
@ -56,15 +57,23 @@ fun DestinationContent(
|
||||||
modifier,
|
modifier,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
is Destination.SeriesOverview ->
|
||||||
|
SeriesOverview(
|
||||||
|
preferences,
|
||||||
|
navigationManager,
|
||||||
|
destination,
|
||||||
|
modifier,
|
||||||
|
initialSeasonEpisode = destination.seasonEpisode,
|
||||||
|
)
|
||||||
|
|
||||||
is Destination.MediaItem ->
|
is Destination.MediaItem ->
|
||||||
when (destination.type) {
|
when (destination.type) {
|
||||||
BaseItemKind.SERIES ->
|
BaseItemKind.SERIES ->
|
||||||
SeriesOverview(
|
SeriesDetails(
|
||||||
preferences,
|
preferences,
|
||||||
navigationManager,
|
navigationManager,
|
||||||
destination,
|
destination,
|
||||||
modifier,
|
modifier,
|
||||||
initialSeasonEpisode = destination.seasonEpisode,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
BaseItemKind.SEASON ->
|
BaseItemKind.SEASON ->
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
fun formatDateTime(dateTime: LocalDateTime): String =
|
fun formatDateTime(dateTime: LocalDateTime): String =
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
val formatter = DateTimeFormatter.ofPattern("MMMM d, yyyy")
|
val formatter = DateTimeFormatter.ofPattern("MMM d, yyyy")
|
||||||
formatter.format(dateTime)
|
formatter.format(dateTime)
|
||||||
} else if (dateTime.toString().length >= 10) {
|
} else if (dateTime.toString().length >= 10) {
|
||||||
dateTime.toString().substring(0, 10)
|
dateTime.toString().substring(0, 10)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue