mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Updates to series detail page
This commit is contained in:
parent
993798d14b
commit
8cda98d6aa
10 changed files with 243 additions and 31 deletions
|
|
@ -127,6 +127,7 @@ dependencies {
|
|||
implementation(libs.androidx.compose.material3)
|
||||
implementation(libs.androidx.lifecycle.viewmodel.navigation3)
|
||||
implementation(libs.androidx.hilt.navigation.compose)
|
||||
implementation(libs.androidx.preference.ktx)
|
||||
ksp(libs.androidx.room.compiler)
|
||||
ksp(libs.hilt.android.compiler)
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ fun ItemCard(
|
|||
onLongClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
cardWidth: Dp? = null,
|
||||
cardHeight: Dp = 200.dp * .85f,
|
||||
cardHeight: Dp? = 200.dp * .85f,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
) {
|
||||
val hideOverlayDelay = 750L
|
||||
|
|
@ -112,7 +112,7 @@ fun ItemCard(
|
|||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.height(cardHeight),
|
||||
.ifElse(cardHeight != null, { Modifier.height(cardHeight!!) }),
|
||||
)
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(0.dp),
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import com.github.damontecres.dolphin.ui.ifElse
|
|||
fun NullCard(
|
||||
modifier: Modifier = Modifier,
|
||||
cardWidth: Dp? = null,
|
||||
cardHeight: Dp = 200.dp * .75f,
|
||||
cardHeight: Dp? = 200.dp * .75f,
|
||||
interactionSource: MutableInteractionSource? = null,
|
||||
) {
|
||||
Card(
|
||||
|
|
@ -27,7 +27,7 @@ fun NullCard(
|
|||
Column(
|
||||
modifier =
|
||||
Modifier
|
||||
.height(cardHeight)
|
||||
.ifElse(cardHeight != null, { Modifier.height(cardHeight!!) })
|
||||
.ifElse(cardWidth != null, { Modifier.width(cardWidth!!) }),
|
||||
) {
|
||||
Text(
|
||||
|
|
|
|||
|
|
@ -9,12 +9,16 @@ import androidx.compose.foundation.lazy.LazyRow
|
|||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.focusRestorer
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import com.github.damontecres.dolphin.data.model.Person
|
||||
import com.github.damontecres.dolphin.ui.ifElse
|
||||
|
||||
@Composable
|
||||
fun PersonRow(
|
||||
|
|
@ -23,6 +27,7 @@ fun PersonRow(
|
|||
modifier: Modifier = Modifier,
|
||||
onLongClick: ((Person) -> Unit)? = null,
|
||||
) {
|
||||
val firstFocus = remember { FocusRequester() }
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
modifier = modifier,
|
||||
|
|
@ -40,14 +45,14 @@ fun PersonRow(
|
|||
Modifier
|
||||
.padding(start = 16.dp)
|
||||
.fillMaxWidth()
|
||||
.focusRestorer(),
|
||||
.focusRestorer(firstFocus),
|
||||
) {
|
||||
itemsIndexed(people) { index, item ->
|
||||
PersonCard(
|
||||
item = item,
|
||||
onClick = { onClick.invoke(item) },
|
||||
onLongClick = { onLongClick?.invoke(item) },
|
||||
modifier = Modifier,
|
||||
modifier = Modifier.ifElse(index == 0, Modifier.focusRequester(firstFocus)),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ import androidx.compose.foundation.layout.Arrangement
|
|||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
|
|
@ -38,16 +40,19 @@ import androidx.compose.ui.input.key.key
|
|||
import androidx.compose.ui.input.key.onKeyEvent
|
||||
import androidx.compose.ui.input.key.type
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.window.Dialog
|
||||
import androidx.compose.ui.window.DialogProperties
|
||||
import androidx.tv.material3.Button
|
||||
import androidx.tv.material3.Icon
|
||||
import androidx.tv.material3.ListItem
|
||||
import androidx.tv.material3.LocalContentColor
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import androidx.tv.material3.surfaceColorAtElevation
|
||||
import com.github.damontecres.dolphin.R
|
||||
import com.github.damontecres.dolphin.ui.FontAwesome
|
||||
import com.github.damontecres.dolphin.util.ExceptionHandler
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
@ -282,6 +287,7 @@ fun ScrollableDialog(
|
|||
fun BasicDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
properties: DialogProperties = DialogProperties(),
|
||||
elevation: Dp = 3.dp,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
Dialog(
|
||||
|
|
@ -292,7 +298,7 @@ fun BasicDialog(
|
|||
modifier =
|
||||
Modifier
|
||||
.background(
|
||||
MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp),
|
||||
MaterialTheme.colorScheme.surfaceColorAtElevation(elevation),
|
||||
shape = RoundedCornerShape(8.dp),
|
||||
),
|
||||
) {
|
||||
|
|
@ -300,3 +306,75 @@ fun BasicDialog(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ConfirmDialog(
|
||||
title: String,
|
||||
body: String?,
|
||||
onCancel: () -> Unit,
|
||||
onConfirm: () -> Unit,
|
||||
properties: DialogProperties = DialogProperties(),
|
||||
elevation: Dp = 3.dp,
|
||||
) = BasicDialog(
|
||||
onDismissRequest = onCancel,
|
||||
properties = properties,
|
||||
elevation = elevation,
|
||||
content = {
|
||||
ConfirmDialogContent(title, body, onCancel, onConfirm, Modifier)
|
||||
},
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun ConfirmDialogContent(
|
||||
title: String,
|
||||
body: String?,
|
||||
onCancel: () -> Unit,
|
||||
onConfirm: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
LazyColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
contentPadding = PaddingValues(16.dp),
|
||||
modifier = modifier,
|
||||
) {
|
||||
item {
|
||||
Text(
|
||||
text = title,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillParentMaxWidth(),
|
||||
)
|
||||
}
|
||||
body?.let {
|
||||
item {
|
||||
Text(
|
||||
text = body,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
item {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.SpaceEvenly,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
Button(
|
||||
onClick = onCancel,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.cancel),
|
||||
)
|
||||
}
|
||||
Button(
|
||||
onClick = onConfirm,
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.confirm),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -319,6 +319,7 @@ fun CardGrid(
|
|||
item = item,
|
||||
onClick = { if (item != null) itemOnClick.invoke(item) },
|
||||
onLongClick = { if (item != null) longClicker.invoke(item) },
|
||||
cardHeight = null,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,18 +8,26 @@ 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.PaddingValues
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
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.relocation.BringIntoViewRequester
|
||||
import androidx.compose.foundation.relocation.bringIntoViewRequester
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.PlayArrow
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
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.alpha
|
||||
|
|
@ -36,16 +44,22 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
|||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import coil3.compose.AsyncImage
|
||||
import com.github.damontecres.dolphin.R
|
||||
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.ui.cards.ItemRow
|
||||
import com.github.damontecres.dolphin.ui.cards.PersonRow
|
||||
import com.github.damontecres.dolphin.ui.components.ConfirmDialog
|
||||
import com.github.damontecres.dolphin.ui.components.DotSeparatedRow
|
||||
import com.github.damontecres.dolphin.ui.components.ErrorMessage
|
||||
import com.github.damontecres.dolphin.ui.components.ExpandableFaButton
|
||||
import com.github.damontecres.dolphin.ui.components.ExpandablePlayButton
|
||||
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.data.ItemDetailsDialog
|
||||
import com.github.damontecres.dolphin.ui.data.ItemDetailsDialogInfo
|
||||
import com.github.damontecres.dolphin.ui.isNotNullOrBlank
|
||||
import com.github.damontecres.dolphin.ui.letNotEmpty
|
||||
import com.github.damontecres.dolphin.ui.nav.Destination
|
||||
|
|
@ -56,6 +70,7 @@ import com.github.damontecres.dolphin.ui.roundMinutes
|
|||
import com.github.damontecres.dolphin.ui.tryRequestFocus
|
||||
import com.github.damontecres.dolphin.util.LoadingState
|
||||
import org.jellyfin.sdk.model.extensions.ticks
|
||||
import kotlin.time.Duration
|
||||
|
||||
@Composable
|
||||
fun SeriesDetails(
|
||||
|
|
@ -74,23 +89,57 @@ fun SeriesDetails(
|
|||
val seasons by viewModel.seasons.observeAsState(ItemListAndMapping.empty())
|
||||
val people by viewModel.people.observeAsState(listOf())
|
||||
|
||||
var overviewDialog by remember { mutableStateOf<ItemDetailsDialogInfo?>(null) }
|
||||
var showWatchConfirmation by remember { mutableStateOf(false) }
|
||||
|
||||
when (val state = loading) {
|
||||
is LoadingState.Error -> ErrorMessage(state)
|
||||
LoadingState.Loading -> LoadingPage()
|
||||
LoadingState.Success -> {
|
||||
item?.let { item ->
|
||||
val played = item.data.userData?.played ?: false
|
||||
SeriesDetailsContent(
|
||||
preferences = preferences,
|
||||
navigationManager = navigationManager,
|
||||
series = item,
|
||||
seasons = seasons,
|
||||
people = people,
|
||||
played = played,
|
||||
modifier = modifier,
|
||||
overviewOnClick = {}, // TODO
|
||||
overviewOnClick = {
|
||||
overviewDialog =
|
||||
ItemDetailsDialogInfo(
|
||||
title = item.name ?: "Unknown",
|
||||
overview = item.data.overview,
|
||||
files = listOf(),
|
||||
)
|
||||
},
|
||||
playOnClick = { viewModel.playNextUp(navigationManager) },
|
||||
watchOnClick = { showWatchConfirmation = true },
|
||||
)
|
||||
if (showWatchConfirmation) {
|
||||
ConfirmDialog(
|
||||
title = item.name ?: "",
|
||||
body = if (played) "Mark entire series as unplayed?" else "Mark entire series as played?",
|
||||
onCancel = {
|
||||
showWatchConfirmation = false
|
||||
},
|
||||
onConfirm = {
|
||||
viewModel.setWatchedSeries(!played)
|
||||
showWatchConfirmation = false
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
overviewDialog?.let { info ->
|
||||
ItemDetailsDialog(
|
||||
info = info,
|
||||
onDismissRequest = { overviewDialog = null },
|
||||
modifier = Modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -100,18 +149,16 @@ fun SeriesDetailsContent(
|
|||
series: BaseItem,
|
||||
seasons: ItemListAndMapping,
|
||||
people: List<Person>,
|
||||
played: Boolean,
|
||||
overviewOnClick: () -> Unit,
|
||||
playOnClick: () -> Unit,
|
||||
watchOnClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val dto = series.data
|
||||
val bringIntoViewRequester = remember { BringIntoViewRequester() }
|
||||
|
||||
val seasonsFocusRequester = remember { FocusRequester() }
|
||||
if (seasons.items.isNotEmpty()) {
|
||||
LaunchedEffect(Unit) {
|
||||
seasonsFocusRequester.tryRequestFocus()
|
||||
}
|
||||
}
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
LaunchedEffect(Unit) { focusRequester.tryRequestFocus() }
|
||||
|
||||
Box(
|
||||
modifier = modifier,
|
||||
|
|
@ -153,14 +200,23 @@ fun SeriesDetailsContent(
|
|||
.fillMaxSize(),
|
||||
) {
|
||||
LazyColumn(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
contentPadding = PaddingValues(bottom = 80.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||
modifier = modifier,
|
||||
) {
|
||||
item {
|
||||
SeriesDetailsHeader(
|
||||
series = series,
|
||||
played = played,
|
||||
overviewOnClick = overviewOnClick,
|
||||
modifier = Modifier.fillMaxWidth(.7f),
|
||||
playOnClick = playOnClick,
|
||||
watchOnClick = watchOnClick,
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth(.6f)
|
||||
.bringIntoViewRequester(bringIntoViewRequester)
|
||||
.focusRequester(focusRequester)
|
||||
.padding(bottom = 80.dp),
|
||||
)
|
||||
}
|
||||
item {
|
||||
|
|
@ -169,10 +225,16 @@ fun SeriesDetailsContent(
|
|||
items = seasons.items,
|
||||
onClickItem = { navigationManager.navigateTo(it.destination()) },
|
||||
onLongClickItem = { },
|
||||
cardOnFocus = { isFocused, index ->
|
||||
// if (isFocused) {
|
||||
// scope.launch(ExceptionHandler()) {
|
||||
// bringIntoViewRequester.bringIntoView()
|
||||
// }
|
||||
// }
|
||||
},
|
||||
modifier =
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.focusRequester(seasonsFocusRequester),
|
||||
.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
if (people.isNotEmpty()) {
|
||||
|
|
@ -193,7 +255,10 @@ fun SeriesDetailsContent(
|
|||
@Composable
|
||||
fun SeriesDetailsHeader(
|
||||
series: BaseItem,
|
||||
played: Boolean,
|
||||
overviewOnClick: () -> Unit,
|
||||
playOnClick: () -> Unit,
|
||||
watchOnClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
|
|
@ -205,6 +270,7 @@ fun SeriesDetailsHeader(
|
|||
?.ticks
|
||||
?.roundMinutes
|
||||
?.let { add(it.toString()) }
|
||||
dto.officialRating?.let(::add)
|
||||
}
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
|
|
@ -212,12 +278,12 @@ fun SeriesDetailsHeader(
|
|||
) {
|
||||
Text(
|
||||
text = series.name ?: "Unknown",
|
||||
style = MaterialTheme.typography.displayMedium,
|
||||
style = MaterialTheme.typography.displaySmall,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
DotSeparatedRow(
|
||||
texts = details,
|
||||
textStyle = MaterialTheme.typography.headlineSmall,
|
||||
textStyle = MaterialTheme.typography.titleMedium,
|
||||
)
|
||||
|
||||
dto.genres?.letNotEmpty {
|
||||
|
|
@ -237,7 +303,7 @@ fun SeriesDetailsHeader(
|
|||
enabled = false,
|
||||
precision = StarRatingPrecision.HALF,
|
||||
playSoundOnFocus = true,
|
||||
modifier = Modifier.height(32.dp),
|
||||
modifier = Modifier.height(24.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -278,5 +344,23 @@ fun SeriesDetailsHeader(
|
|||
)
|
||||
}
|
||||
}
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
modifier = Modifier.padding(start = 16.dp),
|
||||
) {
|
||||
ExpandablePlayButton(
|
||||
title = R.string.play,
|
||||
resume = Duration.ZERO,
|
||||
icon = Icons.Default.PlayArrow,
|
||||
onClick = { playOnClick.invoke() },
|
||||
modifier = Modifier,
|
||||
)
|
||||
ExpandableFaButton(
|
||||
title = if (played) R.string.mark_unwatched else R.string.mark_watched,
|
||||
iconStringRes = if (played) R.string.fa_eye else R.string.fa_eye_slash,
|
||||
onClick = watchOnClick,
|
||||
modifier = Modifier,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package com.github.damontecres.dolphin.ui.detail
|
||||
|
||||
import android.content.Context
|
||||
import android.widget.Toast
|
||||
import androidx.annotation.OptIn
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
|
|
@ -17,6 +18,8 @@ import com.github.damontecres.dolphin.hilt.AuthOkHttpClient
|
|||
import com.github.damontecres.dolphin.preferences.ThemeSongVolume
|
||||
import com.github.damontecres.dolphin.preferences.UserPreferences
|
||||
import com.github.damontecres.dolphin.ui.letNotEmpty
|
||||
import com.github.damontecres.dolphin.ui.nav.Destination
|
||||
import com.github.damontecres.dolphin.ui.nav.NavigationManager
|
||||
import com.github.damontecres.dolphin.util.ApiRequestPager
|
||||
import com.github.damontecres.dolphin.util.ExceptionHandler
|
||||
import com.github.damontecres.dolphin.util.GetEpisodesRequestHandler
|
||||
|
|
@ -33,6 +36,7 @@ import okhttp3.OkHttpClient
|
|||
import org.jellyfin.sdk.api.client.ApiClient
|
||||
import org.jellyfin.sdk.api.client.extensions.libraryApi
|
||||
import org.jellyfin.sdk.api.client.extensions.playStateApi
|
||||
import org.jellyfin.sdk.api.client.extensions.tvShowsApi
|
||||
import org.jellyfin.sdk.api.client.extensions.universalAudioApi
|
||||
import org.jellyfin.sdk.api.client.extensions.userLibraryApi
|
||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||
|
|
@ -55,6 +59,7 @@ class SeriesViewModel
|
|||
) : ItemViewModel<Video>(api) {
|
||||
private var player: Player? = null
|
||||
private lateinit var seriesId: UUID
|
||||
private lateinit var prefs: UserPreferences
|
||||
val loading = MutableLiveData<LoadingState>(LoadingState.Loading)
|
||||
val seasons = MutableLiveData<ItemListAndMapping>(ItemListAndMapping.empty())
|
||||
val episodes = MutableLiveData<ItemListAndMapping>(ItemListAndMapping.empty())
|
||||
|
|
@ -68,6 +73,7 @@ class SeriesViewModel
|
|||
episode: Int?,
|
||||
) {
|
||||
this.seriesId = itemId
|
||||
this.prefs = prefs
|
||||
viewModelScope.launch(
|
||||
LoadingExceptionHandler(
|
||||
loading,
|
||||
|
|
@ -241,20 +247,52 @@ class SeriesViewModel
|
|||
refreshEpisode(itemId, listIndex)
|
||||
}
|
||||
|
||||
fun setWatchedSeries(played: Boolean) =
|
||||
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
||||
if (played) {
|
||||
api.playStateApi.markPlayedItem(seriesId)
|
||||
} else {
|
||||
api.playStateApi.markUnplayedItem(seriesId)
|
||||
}
|
||||
init(prefs, seriesId, null, null, null)
|
||||
}
|
||||
|
||||
fun refreshEpisode(
|
||||
itemId: UUID,
|
||||
listIndex: Int,
|
||||
) = viewModelScope.launch(ExceptionHandler()) {
|
||||
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
||||
val base = api.userLibraryApi.getItem(itemId).content
|
||||
val item = BaseItem.Companion.from(base, api)
|
||||
val eps = episodes.value!!
|
||||
episodes.value =
|
||||
eps.copy(
|
||||
items =
|
||||
eps.items.toMutableList().apply {
|
||||
this[listIndex] = item
|
||||
},
|
||||
)
|
||||
withContext(Dispatchers.Main) {
|
||||
episodes.value =
|
||||
eps.copy(
|
||||
items =
|
||||
eps.items.toMutableList().apply {
|
||||
this[listIndex] = item
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun playNextUp(nav: NavigationManager) {
|
||||
viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
||||
val result by api.tvShowsApi.getNextUp(seriesId = seriesId)
|
||||
val nextUp =
|
||||
result.items.firstOrNull() ?: api.tvShowsApi
|
||||
.getEpisodes(
|
||||
seriesId,
|
||||
limit = 1,
|
||||
).content.items
|
||||
.firstOrNull()
|
||||
if (nextUp != null) {
|
||||
nav.navigateTo(Destination.Playback(nextUp.id, 0L))
|
||||
} else {
|
||||
Toast
|
||||
.makeText(context, "Could not find an episode to play", Toast.LENGTH_SHORT)
|
||||
.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,5 +56,8 @@
|
|||
<string name="sort_by_date_played">Date Played</string>
|
||||
<string name="sort_by_date_released">Date Released</string>
|
||||
<string name="skip_back_on_resume_preference">Skip back when resuming playback</string>
|
||||
<string name="pref_key_update_last_check_threshold">preference.update.threshold</string>
|
||||
<string name="pref_key_update_last_check">preference.update.lastTimestamp</string>
|
||||
<string name="confirm">Confirm</string>
|
||||
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ protobuf-javalite = "4.32.1"
|
|||
hilt = "2.57.2"
|
||||
room = "2.8.1"
|
||||
material3 = "1.4.0"
|
||||
preferenceKtx = "1.2.1"
|
||||
|
||||
[libraries]
|
||||
aboutlibraries-core = { module = "com.mikepenz:aboutlibraries-core", version.ref = "aboutLibraries" }
|
||||
|
|
@ -85,6 +86,7 @@ androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref =
|
|||
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
|
||||
|
||||
timber = { module = "com.jakewharton.timber:timber", version.ref = "timber" }
|
||||
androidx-preference-ktx = { group = "androidx.preference", name = "preference-ktx", version.ref = "preferenceKtx" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue