mirror of
https://github.com/bbara04/Purefin.git
synced 2026-07-23 19:26:50 +00:00
feat(ui): add watched toggle to detail screens and home/library cards
Add mark-as-watched/unwatched bottom sheet actions to home screen NextUp cards and library browse cards, and to library screen grid items. Add a single-button watched toggle (with dual icon/color states) to Movie, Episode, and Series detail screens. Inject JellyfinMediaMetadataUpdater into all four affected ViewModels.
This commit is contained in:
@@ -21,7 +21,9 @@ import androidx.compose.foundation.rememberScrollState
|
|||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.CheckCircle
|
||||||
import androidx.compose.material.icons.outlined.Person
|
import androidx.compose.material.icons.outlined.Person
|
||||||
|
import androidx.compose.material.icons.outlined.RadioButtonUnchecked
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
@@ -95,6 +97,12 @@ sealed interface MediaDetailSecondaryActionUiModel {
|
|||||||
val onClick: () -> Unit = {},
|
val onClick: () -> Unit = {},
|
||||||
override val testTag: String? = null,
|
override val testTag: String? = null,
|
||||||
) : MediaDetailSecondaryActionUiModel
|
) : MediaDetailSecondaryActionUiModel
|
||||||
|
|
||||||
|
data class MarkAsWatched(
|
||||||
|
val watched: Boolean,
|
||||||
|
val onClick: () -> Unit,
|
||||||
|
override val testTag: String? = null,
|
||||||
|
) : MediaDetailSecondaryActionUiModel
|
||||||
}
|
}
|
||||||
|
|
||||||
data class MediaDetailSynopsisUiModel(
|
data class MediaDetailSynopsisUiModel(
|
||||||
@@ -356,6 +364,25 @@ private fun MediaDetailActions(
|
|||||||
modifier = Modifier.optionalTestTag(action.testTag)
|
modifier = Modifier.optionalTestTag(action.testTag)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
is MediaDetailSecondaryActionUiModel.MarkAsWatched -> {
|
||||||
|
MediaActionButton(
|
||||||
|
backgroundColor = MaterialTheme.colorScheme.surface,
|
||||||
|
iconColor = if (action.watched) {
|
||||||
|
MaterialTheme.colorScheme.primary
|
||||||
|
} else {
|
||||||
|
MaterialTheme.colorScheme.onSurface
|
||||||
|
},
|
||||||
|
icon = if (action.watched) {
|
||||||
|
Icons.Outlined.CheckCircle
|
||||||
|
} else {
|
||||||
|
Icons.Outlined.RadioButtonUnchecked
|
||||||
|
},
|
||||||
|
height = 48.dp,
|
||||||
|
onClick = action.onClick,
|
||||||
|
modifier = Modifier.optionalTestTag(action.testTag)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import hu.bbara.purefin.ui.common.media.mediaPlaybackProgress
|
|||||||
import hu.bbara.purefin.ui.common.permission.rememberNotificationPermissionGate
|
import hu.bbara.purefin.ui.common.permission.rememberNotificationPermissionGate
|
||||||
import hu.bbara.purefin.ui.screen.episode.components.EpisodeDownloadButtonTag
|
import hu.bbara.purefin.ui.screen.episode.components.EpisodeDownloadButtonTag
|
||||||
import hu.bbara.purefin.ui.screen.episode.components.EpisodePlayButtonTag
|
import hu.bbara.purefin.ui.screen.episode.components.EpisodePlayButtonTag
|
||||||
|
import hu.bbara.purefin.ui.screen.episode.components.EpisodeWatchedButtonTag
|
||||||
import hu.bbara.purefin.ui.screen.episode.components.EpisodeTopBar
|
import hu.bbara.purefin.ui.screen.episode.components.EpisodeTopBar
|
||||||
import hu.bbara.purefin.ui.screen.episode.components.EpisodeTopBarShortcut
|
import hu.bbara.purefin.ui.screen.episode.components.EpisodeTopBarShortcut
|
||||||
import hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen
|
import hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen
|
||||||
@@ -83,6 +84,7 @@ fun EpisodeScreen(
|
|||||||
downloadState = downloadState.value,
|
downloadState = downloadState.value,
|
||||||
onBack = viewModel::onBack,
|
onBack = viewModel::onBack,
|
||||||
onDownloadClick = onDownloadClick,
|
onDownloadClick = onDownloadClick,
|
||||||
|
onMarkAsWatched = viewModel::markAsWatched,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -95,6 +97,7 @@ private fun EpisodeScreenInternal(
|
|||||||
downloadState: DownloadState,
|
downloadState: DownloadState,
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
onDownloadClick: () -> Unit,
|
onDownloadClick: () -> Unit,
|
||||||
|
onMarkAsWatched: (Boolean) -> Unit = {},
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@@ -110,7 +113,8 @@ private fun EpisodeScreenInternal(
|
|||||||
uiModel = episode.toMediaDetailScaffoldUiModel(
|
uiModel = episode.toMediaDetailScaffoldUiModel(
|
||||||
onPlayClick = playAction,
|
onPlayClick = playAction,
|
||||||
downloadState = downloadState,
|
downloadState = downloadState,
|
||||||
onDownloadClick = onDownloadClick
|
onDownloadClick = onDownloadClick,
|
||||||
|
onMarkAsWatched = onMarkAsWatched,
|
||||||
),
|
),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
topBar = { scrollBehavior ->
|
topBar = { scrollBehavior ->
|
||||||
@@ -127,6 +131,7 @@ private fun Episode.toMediaDetailScaffoldUiModel(
|
|||||||
onPlayClick: () -> Unit,
|
onPlayClick: () -> Unit,
|
||||||
downloadState: DownloadState,
|
downloadState: DownloadState,
|
||||||
onDownloadClick: () -> Unit,
|
onDownloadClick: () -> Unit,
|
||||||
|
onMarkAsWatched: (Boolean) -> Unit,
|
||||||
): MediaDetailScaffoldUiModel = MediaDetailScaffoldUiModel(
|
): MediaDetailScaffoldUiModel = MediaDetailScaffoldUiModel(
|
||||||
imageUrl = ImageUrlBuilder.finishImageUrl(imageUrlPrefix, ArtworkKind.PRIMARY),
|
imageUrl = ImageUrlBuilder.finishImageUrl(imageUrlPrefix, ArtworkKind.PRIMARY),
|
||||||
title = title,
|
title = title,
|
||||||
@@ -141,6 +146,11 @@ private fun Episode.toMediaDetailScaffoldUiModel(
|
|||||||
testTag = EpisodePlayButtonTag
|
testTag = EpisodePlayButtonTag
|
||||||
),
|
),
|
||||||
secondaryActions = listOf(
|
secondaryActions = listOf(
|
||||||
|
MediaDetailSecondaryActionUiModel.MarkAsWatched(
|
||||||
|
watched = watched,
|
||||||
|
onClick = { onMarkAsWatched(!watched) },
|
||||||
|
testTag = EpisodeWatchedButtonTag
|
||||||
|
),
|
||||||
MediaDetailSecondaryActionUiModel.Download(
|
MediaDetailSecondaryActionUiModel.Download(
|
||||||
downloadState = downloadState,
|
downloadState = downloadState,
|
||||||
onClick = onDownloadClick,
|
onClick = onDownloadClick,
|
||||||
|
|||||||
@@ -88,3 +88,4 @@ internal fun EpisodeTopBar(
|
|||||||
internal const val EpisodeSeriesButtonTag = "episode-series-button"
|
internal const val EpisodeSeriesButtonTag = "episode-series-button"
|
||||||
internal const val EpisodePlayButtonTag = "episode-play-button"
|
internal const val EpisodePlayButtonTag = "episode-play-button"
|
||||||
internal const val EpisodeDownloadButtonTag = "episode-download-button"
|
internal const val EpisodeDownloadButtonTag = "episode-download-button"
|
||||||
|
internal const val EpisodeWatchedButtonTag = "episode-watched-button"
|
||||||
|
|||||||
@@ -121,11 +121,12 @@ fun HomeContent(
|
|||||||
|
|
||||||
if (nextUp.isNotEmpty()) {
|
if (nextUp.isNotEmpty()) {
|
||||||
item(key = "next-up") {
|
item(key = "next-up") {
|
||||||
NextUpSection(
|
NextUpSection(
|
||||||
items = nextUp,
|
items = nextUp,
|
||||||
onMediaSelected = onMediaSelected,
|
onMediaSelected = onMediaSelected,
|
||||||
modifier = Modifier.testTag(HomeNextUpSectionTag)
|
onMarkAsWatched = onMarkAsWatched,
|
||||||
)
|
modifier = Modifier.testTag(HomeNextUpSectionTag)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,6 +139,7 @@ fun HomeContent(
|
|||||||
items = libraryContent[library.id].orEmpty(),
|
items = libraryContent[library.id].orEmpty(),
|
||||||
onLibrarySelected = onLibrarySelected,
|
onLibrarySelected = onLibrarySelected,
|
||||||
onMediaSelected = onMediaSelected,
|
onMediaSelected = onMediaSelected,
|
||||||
|
onMarkAsWatched = onMarkAsWatched,
|
||||||
modifier = Modifier.testTag("$HomeLibrarySectionTagPrefix${library.id}")
|
modifier = Modifier.testTag("$HomeLibrarySectionTagPrefix${library.id}")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,28 +10,41 @@ import hu.bbara.purefin.core.model.EpisodeUiModel
|
|||||||
import hu.bbara.purefin.core.model.MediaUiModel
|
import hu.bbara.purefin.core.model.MediaUiModel
|
||||||
import hu.bbara.purefin.core.model.MovieUiModel
|
import hu.bbara.purefin.core.model.MovieUiModel
|
||||||
import hu.bbara.purefin.core.model.SeriesUiModel
|
import hu.bbara.purefin.core.model.SeriesUiModel
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import hu.bbara.purefin.ui.common.badge.UnwatchedEpisodeBadge
|
import hu.bbara.purefin.ui.common.badge.UnwatchedEpisodeBadge
|
||||||
import hu.bbara.purefin.ui.common.badge.WatchStateBadge
|
import hu.bbara.purefin.ui.common.badge.WatchStateBadge
|
||||||
import hu.bbara.purefin.ui.common.card.MediaImageCard
|
import hu.bbara.purefin.ui.common.card.MediaImageCard
|
||||||
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsSource
|
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsSource
|
||||||
import hu.bbara.purefin.ui.common.media.rememberHomeMediaSharedBoundsClick
|
import hu.bbara.purefin.ui.common.media.rememberHomeMediaSharedBoundsClick
|
||||||
|
import hu.bbara.purefin.ui.model.MediaAction
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun HomeBrowseCard(
|
internal fun HomeBrowseCard(
|
||||||
uiModel: MediaUiModel,
|
uiModel: MediaUiModel,
|
||||||
sharedBoundsKey: String,
|
sharedBoundsKey: String,
|
||||||
onMediaSelected: (MediaUiModel) -> Unit,
|
onMediaSelected: (MediaUiModel) -> Unit,
|
||||||
|
onMarkAsWatched: (MediaUiModel, Boolean) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val onClick = rememberHomeMediaSharedBoundsClick(sharedBoundsKey) {
|
val onClick = rememberHomeMediaSharedBoundsClick(sharedBoundsKey) {
|
||||||
onMediaSelected(uiModel)
|
onMediaSelected(uiModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val popupActions = remember(uiModel, onMarkAsWatched) {
|
||||||
|
buildList {
|
||||||
|
if (uiModel is MovieUiModel || uiModel is EpisodeUiModel || uiModel is SeriesUiModel) {
|
||||||
|
add(MediaAction(name = "Mark as watched") { onMarkAsWatched(uiModel, true) })
|
||||||
|
add(MediaAction(name = "Mark as unwatched") { onMarkAsWatched(uiModel, false) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MediaImageCard(
|
MediaImageCard(
|
||||||
imageUrl = uiModel.primaryImageUrl,
|
imageUrl = uiModel.primaryImageUrl,
|
||||||
title = uiModel.primaryText,
|
title = uiModel.primaryText,
|
||||||
subtitle = uiModel.secondaryText,
|
subtitle = uiModel.secondaryText,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
|
popupActions = popupActions,
|
||||||
imageModifier = Modifier.homeMediaSharedBoundsSource(sharedBoundsKey),
|
imageModifier = Modifier.homeMediaSharedBoundsSource(sharedBoundsKey),
|
||||||
imageAspectRatio = 15f / 16f,
|
imageAspectRatio = 15f / 16f,
|
||||||
modifier = modifier.width(188.dp)
|
modifier = modifier.width(188.dp)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ fun LibraryPosterSection(
|
|||||||
items: List<MediaUiModel>,
|
items: List<MediaUiModel>,
|
||||||
onLibrarySelected: (LibraryUiModel) -> Unit,
|
onLibrarySelected: (LibraryUiModel) -> Unit,
|
||||||
onMediaSelected: (MediaUiModel) -> Unit,
|
onMediaSelected: (MediaUiModel) -> Unit,
|
||||||
|
onMarkAsWatched: (MediaUiModel, Boolean) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
if (items.isEmpty()) return
|
if (items.isEmpty()) return
|
||||||
@@ -61,6 +62,7 @@ fun LibraryPosterSection(
|
|||||||
mediaId = item.id
|
mediaId = item.id
|
||||||
),
|
),
|
||||||
onMediaSelected = onMediaSelected,
|
onMediaSelected = onMediaSelected,
|
||||||
|
onMarkAsWatched = onMarkAsWatched,
|
||||||
modifier = Modifier.testTag("$HomeLibraryItemTagPrefix${library.id}-$index")
|
modifier = Modifier.testTag("$HomeLibraryItemTagPrefix${library.id}-$index")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.PaddingValues
|
|||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
@@ -13,24 +14,39 @@ import androidx.compose.ui.unit.dp
|
|||||||
import hu.bbara.purefin.ui.common.card.MediaImageCard
|
import hu.bbara.purefin.ui.common.card.MediaImageCard
|
||||||
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsSource
|
import hu.bbara.purefin.ui.common.media.homeMediaSharedBoundsSource
|
||||||
import hu.bbara.purefin.ui.common.media.rememberHomeMediaSharedBoundsClick
|
import hu.bbara.purefin.ui.common.media.rememberHomeMediaSharedBoundsClick
|
||||||
|
import hu.bbara.purefin.ui.model.MediaAction
|
||||||
import hu.bbara.purefin.core.model.MediaUiModel
|
import hu.bbara.purefin.core.model.MediaUiModel
|
||||||
|
import hu.bbara.purefin.core.model.MovieUiModel
|
||||||
|
import hu.bbara.purefin.core.model.EpisodeUiModel
|
||||||
|
import hu.bbara.purefin.core.model.SeriesUiModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
internal fun NextUpCard(
|
internal fun NextUpCard(
|
||||||
uiModel: MediaUiModel,
|
uiModel: MediaUiModel,
|
||||||
sharedBoundsKey: String,
|
sharedBoundsKey: String,
|
||||||
onMediaSelected: (MediaUiModel) -> Unit,
|
onMediaSelected: (MediaUiModel) -> Unit,
|
||||||
|
onMarkAsWatched: (MediaUiModel, Boolean) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val onClick = rememberHomeMediaSharedBoundsClick(sharedBoundsKey) {
|
val onClick = rememberHomeMediaSharedBoundsClick(sharedBoundsKey) {
|
||||||
onMediaSelected(uiModel)
|
onMediaSelected(uiModel)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val popupActions = remember(uiModel, onMarkAsWatched) {
|
||||||
|
buildList {
|
||||||
|
if (uiModel is MovieUiModel || uiModel is EpisodeUiModel || uiModel is SeriesUiModel) {
|
||||||
|
add(MediaAction(name = "Mark as watched") { onMarkAsWatched(uiModel, true) })
|
||||||
|
add(MediaAction(name = "Mark as unwatched") { onMarkAsWatched(uiModel, false) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MediaImageCard(
|
MediaImageCard(
|
||||||
imageUrl = uiModel.primaryImageUrl,
|
imageUrl = uiModel.primaryImageUrl,
|
||||||
title = uiModel.primaryText,
|
title = uiModel.primaryText,
|
||||||
subtitle = uiModel.secondaryText,
|
subtitle = uiModel.secondaryText,
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
|
popupActions = popupActions,
|
||||||
imageModifier = Modifier.homeMediaSharedBoundsSource(sharedBoundsKey),
|
imageModifier = Modifier.homeMediaSharedBoundsSource(sharedBoundsKey),
|
||||||
shapeSize = 24.dp,
|
shapeSize = 24.dp,
|
||||||
titleStyle = MaterialTheme.typography.titleSmall,
|
titleStyle = MaterialTheme.typography.titleSmall,
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import hu.bbara.purefin.core.model.MediaUiModel
|
|||||||
fun NextUpSection(
|
fun NextUpSection(
|
||||||
items: List<MediaUiModel>,
|
items: List<MediaUiModel>,
|
||||||
onMediaSelected: (MediaUiModel) -> Unit,
|
onMediaSelected: (MediaUiModel) -> Unit,
|
||||||
|
onMarkAsWatched: (MediaUiModel, Boolean) -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
if (items.isEmpty()) return
|
if (items.isEmpty()) return
|
||||||
@@ -51,6 +52,7 @@ fun NextUpSection(
|
|||||||
uiModel = item,
|
uiModel = item,
|
||||||
sharedBoundsKey = homeMediaSharedBoundsKey("next-up-$index", item.id),
|
sharedBoundsKey = homeMediaSharedBoundsKey("next-up-$index", item.id),
|
||||||
onMediaSelected = onMediaSelected,
|
onMediaSelected = onMediaSelected,
|
||||||
|
onMarkAsWatched = onMarkAsWatched,
|
||||||
modifier = Modifier.testTag("$HomeNextUpItemTagPrefix$index")
|
modifier = Modifier.testTag("$HomeNextUpItemTagPrefix$index")
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -30,6 +31,7 @@ import hu.bbara.purefin.core.model.SeriesUiModel
|
|||||||
import hu.bbara.purefin.core.navigation.LibraryDto
|
import hu.bbara.purefin.core.navigation.LibraryDto
|
||||||
import hu.bbara.purefin.ui.common.badge.WatchStateBadge
|
import hu.bbara.purefin.ui.common.badge.WatchStateBadge
|
||||||
import hu.bbara.purefin.ui.common.card.MediaImageCard
|
import hu.bbara.purefin.ui.common.card.MediaImageCard
|
||||||
|
import hu.bbara.purefin.ui.model.MediaAction
|
||||||
import hu.bbara.purefin.ui.screen.home.components.rememberDefaultTopBarScrollBehavior
|
import hu.bbara.purefin.ui.screen.home.components.rememberDefaultTopBarScrollBehavior
|
||||||
import hu.bbara.purefin.ui.screen.library.components.LibraryTopBar
|
import hu.bbara.purefin.ui.screen.library.components.LibraryTopBar
|
||||||
|
|
||||||
@@ -66,7 +68,10 @@ fun LibraryScreen(
|
|||||||
.background(MaterialTheme.colorScheme.background)
|
.background(MaterialTheme.colorScheme.background)
|
||||||
.padding(innerPadding)
|
.padding(innerPadding)
|
||||||
) {
|
) {
|
||||||
LibraryPosterGrid(libraryItems = libraryItems.value)
|
LibraryPosterGrid(
|
||||||
|
libraryItems = libraryItems.value,
|
||||||
|
onMarkAsWatched = viewModel::markAsWatched,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,6 +80,7 @@ fun LibraryScreen(
|
|||||||
internal fun LibraryPosterGrid(
|
internal fun LibraryPosterGrid(
|
||||||
libraryItems: List<MediaUiModel>,
|
libraryItems: List<MediaUiModel>,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
onMarkAsWatched: (MediaUiModel, Boolean) -> Unit,
|
||||||
viewModel: LibraryViewModel = hiltViewModel()
|
viewModel: LibraryViewModel = hiltViewModel()
|
||||||
) {
|
) {
|
||||||
BoxWithConstraints(
|
BoxWithConstraints(
|
||||||
@@ -91,6 +97,15 @@ internal fun LibraryPosterGrid(
|
|||||||
verticalArrangement = Arrangement.spacedBy(16.dp),
|
verticalArrangement = Arrangement.spacedBy(16.dp),
|
||||||
) {
|
) {
|
||||||
items(libraryItems, key = { item -> item.id }) { item ->
|
items(libraryItems, key = { item -> item.id }) { item ->
|
||||||
|
val popupActions = remember {
|
||||||
|
buildList {
|
||||||
|
if (item is MovieUiModel || item is EpisodeUiModel || item is SeriesUiModel) {
|
||||||
|
add(MediaAction(name = "Mark as watched") { onMarkAsWatched(item, true) })
|
||||||
|
add(MediaAction(name = "Mark as unwatched") { onMarkAsWatched(item, false) })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MediaImageCard(
|
MediaImageCard(
|
||||||
imageUrl = item.primaryImageUrl,
|
imageUrl = item.primaryImageUrl,
|
||||||
title = item.primaryText,
|
title = item.primaryText,
|
||||||
@@ -102,6 +117,7 @@ internal fun LibraryPosterGrid(
|
|||||||
is EpisodeUiModel -> Unit
|
is EpisodeUiModel -> Unit
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
popupActions = popupActions,
|
||||||
modifier = Modifier.testTag("$LibraryPosterItemTagPrefix${item.id}")
|
modifier = Modifier.testTag("$LibraryPosterItemTagPrefix${item.id}")
|
||||||
) {
|
) {
|
||||||
when (item) {
|
when (item) {
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import hu.bbara.purefin.ui.common.media.MediaDetailScaffoldUiModel
|
|||||||
import hu.bbara.purefin.ui.common.media.MediaDetailSecondaryActionUiModel
|
import hu.bbara.purefin.ui.common.media.MediaDetailSecondaryActionUiModel
|
||||||
import hu.bbara.purefin.ui.common.media.MediaDetailSynopsisUiModel
|
import hu.bbara.purefin.ui.common.media.MediaDetailSynopsisUiModel
|
||||||
import hu.bbara.purefin.ui.common.media.mediaPlayButtonText
|
import hu.bbara.purefin.ui.common.media.mediaPlayButtonText
|
||||||
|
import hu.bbara.purefin.ui.screen.movie.components.MovieWatchedButtonTag
|
||||||
import hu.bbara.purefin.ui.common.media.mediaPlaybackProgress
|
import hu.bbara.purefin.ui.common.media.mediaPlaybackProgress
|
||||||
import hu.bbara.purefin.ui.common.permission.rememberNotificationPermissionGate
|
import hu.bbara.purefin.ui.common.permission.rememberNotificationPermissionGate
|
||||||
import hu.bbara.purefin.ui.screen.movie.components.MovieDownloadButtonTag
|
import hu.bbara.purefin.ui.screen.movie.components.MovieDownloadButtonTag
|
||||||
@@ -67,6 +68,7 @@ fun MovieScreen(
|
|||||||
downloadState = downloadState.value,
|
downloadState = downloadState.value,
|
||||||
onDownloadClick = onDownloadClick,
|
onDownloadClick = onDownloadClick,
|
||||||
onBack = viewModel::onBack,
|
onBack = viewModel::onBack,
|
||||||
|
onMarkAsWatched = viewModel::markAsWatched,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
@@ -81,6 +83,7 @@ private fun MovieScreenInternal(
|
|||||||
downloadState: DownloadState = DownloadState.NotDownloaded,
|
downloadState: DownloadState = DownloadState.NotDownloaded,
|
||||||
onDownloadClick: () -> Unit = {},
|
onDownloadClick: () -> Unit = {},
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
|
onMarkAsWatched: (Boolean) -> Unit = {},
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
@@ -96,7 +99,8 @@ private fun MovieScreenInternal(
|
|||||||
uiModel = movie.toMediaDetailScaffoldUiModel(
|
uiModel = movie.toMediaDetailScaffoldUiModel(
|
||||||
onPlayClick = playAction,
|
onPlayClick = playAction,
|
||||||
downloadState = downloadState,
|
downloadState = downloadState,
|
||||||
onDownloadClick = onDownloadClick
|
onDownloadClick = onDownloadClick,
|
||||||
|
onMarkAsWatched = onMarkAsWatched,
|
||||||
),
|
),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
topBar = { scrollBehavior ->
|
topBar = { scrollBehavior ->
|
||||||
@@ -112,6 +116,7 @@ private fun Movie.toMediaDetailScaffoldUiModel(
|
|||||||
onPlayClick: () -> Unit,
|
onPlayClick: () -> Unit,
|
||||||
downloadState: DownloadState,
|
downloadState: DownloadState,
|
||||||
onDownloadClick: () -> Unit,
|
onDownloadClick: () -> Unit,
|
||||||
|
onMarkAsWatched: (Boolean) -> Unit,
|
||||||
): MediaDetailScaffoldUiModel = MediaDetailScaffoldUiModel(
|
): MediaDetailScaffoldUiModel = MediaDetailScaffoldUiModel(
|
||||||
imageUrl = ImageUrlBuilder.finishImageUrl(imageUrlPrefix, ArtworkKind.PRIMARY),
|
imageUrl = ImageUrlBuilder.finishImageUrl(imageUrlPrefix, ArtworkKind.PRIMARY),
|
||||||
title = title,
|
title = title,
|
||||||
@@ -125,6 +130,11 @@ private fun Movie.toMediaDetailScaffoldUiModel(
|
|||||||
testTag = MoviePlayButtonTag
|
testTag = MoviePlayButtonTag
|
||||||
),
|
),
|
||||||
secondaryActions = listOf(
|
secondaryActions = listOf(
|
||||||
|
MediaDetailSecondaryActionUiModel.MarkAsWatched(
|
||||||
|
watched = watched,
|
||||||
|
onClick = { onMarkAsWatched(!watched) },
|
||||||
|
testTag = MovieWatchedButtonTag
|
||||||
|
),
|
||||||
MediaDetailSecondaryActionUiModel.Download(
|
MediaDetailSecondaryActionUiModel.Download(
|
||||||
downloadState = downloadState,
|
downloadState = downloadState,
|
||||||
onClick = onDownloadClick,
|
onClick = onDownloadClick,
|
||||||
|
|||||||
@@ -38,3 +38,4 @@ internal fun MovieTopBar(
|
|||||||
|
|
||||||
internal const val MoviePlayButtonTag = "movie-play-button"
|
internal const val MoviePlayButtonTag = "movie-play-button"
|
||||||
internal const val MovieDownloadButtonTag = "movie-download-button"
|
internal const val MovieDownloadButtonTag = "movie-download-button"
|
||||||
|
internal const val MovieWatchedButtonTag = "movie-watched-button"
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import hu.bbara.purefin.ui.screen.series.components.SeasonTabs
|
|||||||
import hu.bbara.purefin.ui.screen.series.components.SeriesAddButtonTag
|
import hu.bbara.purefin.ui.screen.series.components.SeriesAddButtonTag
|
||||||
import hu.bbara.purefin.ui.screen.series.components.SeriesDownloadButtonTag
|
import hu.bbara.purefin.ui.screen.series.components.SeriesDownloadButtonTag
|
||||||
import hu.bbara.purefin.ui.screen.series.components.SeriesDownloadOption
|
import hu.bbara.purefin.ui.screen.series.components.SeriesDownloadOption
|
||||||
|
import hu.bbara.purefin.ui.screen.series.components.SeriesWatchedButtonTag
|
||||||
import hu.bbara.purefin.ui.screen.series.components.SeriesPlayButtonTag
|
import hu.bbara.purefin.ui.screen.series.components.SeriesPlayButtonTag
|
||||||
import hu.bbara.purefin.ui.screen.series.components.SeriesTopBar
|
import hu.bbara.purefin.ui.screen.series.components.SeriesTopBar
|
||||||
import hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen
|
import hu.bbara.purefin.ui.screen.waiting.PurefinWaitingScreen
|
||||||
@@ -117,6 +118,7 @@ fun SeriesScreen(
|
|||||||
onLoadSeasonEpisodes = viewModel::loadSeasonEpisodes,
|
onLoadSeasonEpisodes = viewModel::loadSeasonEpisodes,
|
||||||
onObserveSeasonDownloadState = viewModel::observeSeasonDownloadState,
|
onObserveSeasonDownloadState = viewModel::observeSeasonDownloadState,
|
||||||
onBack = viewModel::onGoHome,
|
onBack = viewModel::onGoHome,
|
||||||
|
onMarkAsWatched = viewModel::markAsWatched,
|
||||||
offline = series.offline,
|
offline = series.offline,
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
)
|
)
|
||||||
@@ -136,6 +138,7 @@ private fun SeriesScreenInternal(
|
|||||||
onLoadSeasonEpisodes: (UUID, UUID) -> Unit,
|
onLoadSeasonEpisodes: (UUID, UUID) -> Unit,
|
||||||
onObserveSeasonDownloadState: (List<Episode>) -> Unit,
|
onObserveSeasonDownloadState: (List<Episode>) -> Unit,
|
||||||
onBack: () -> Unit,
|
onBack: () -> Unit,
|
||||||
|
onMarkAsWatched: (Boolean) -> Unit = {},
|
||||||
offline: Boolean,
|
offline: Boolean,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
@@ -187,6 +190,7 @@ private fun SeriesScreenInternal(
|
|||||||
nextUpEpisode = nextUpEpisode,
|
nextUpEpisode = nextUpEpisode,
|
||||||
onPlayClick = playAction ?: {},
|
onPlayClick = playAction ?: {},
|
||||||
onDownloadClick = { showDownloadDialog = true },
|
onDownloadClick = { showDownloadDialog = true },
|
||||||
|
onMarkAsWatched = onMarkAsWatched,
|
||||||
bodyColor = scheme.onSurface
|
bodyColor = scheme.onSurface
|
||||||
),
|
),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
@@ -238,6 +242,7 @@ private fun Series.toMediaDetailScaffoldUiModel(
|
|||||||
nextUpEpisode: Episode?,
|
nextUpEpisode: Episode?,
|
||||||
onPlayClick: () -> Unit,
|
onPlayClick: () -> Unit,
|
||||||
onDownloadClick: () -> Unit,
|
onDownloadClick: () -> Unit,
|
||||||
|
onMarkAsWatched: (Boolean) -> Unit,
|
||||||
bodyColor: Color,
|
bodyColor: Color,
|
||||||
): MediaDetailScaffoldUiModel = MediaDetailScaffoldUiModel(
|
): MediaDetailScaffoldUiModel = MediaDetailScaffoldUiModel(
|
||||||
imageUrl = ImageUrlBuilder.finishImageUrl(imageUrlPrefix, ArtworkKind.PRIMARY),
|
imageUrl = ImageUrlBuilder.finishImageUrl(imageUrlPrefix, ArtworkKind.PRIMARY),
|
||||||
@@ -246,6 +251,7 @@ private fun Series.toMediaDetailScaffoldUiModel(
|
|||||||
titleLineHeight = 36.sp,
|
titleLineHeight = 36.sp,
|
||||||
metadataItems = listOf(year, "$seasonCount Seasons"),
|
metadataItems = listOf(year, "$seasonCount Seasons"),
|
||||||
actions = selectedSeason?.let {
|
actions = selectedSeason?.let {
|
||||||
|
val seriesWatched = unwatchedEpisodeCount == 0
|
||||||
MediaDetailActionsUiModel(
|
MediaDetailActionsUiModel(
|
||||||
primaryAction = MediaDetailPrimaryActionUiModel(
|
primaryAction = MediaDetailPrimaryActionUiModel(
|
||||||
text = mediaPlayButtonText(nextUpEpisode?.progress, nextUpEpisode?.watched),
|
text = mediaPlayButtonText(nextUpEpisode?.progress, nextUpEpisode?.watched),
|
||||||
@@ -254,6 +260,11 @@ private fun Series.toMediaDetailScaffoldUiModel(
|
|||||||
testTag = SeriesPlayButtonTag
|
testTag = SeriesPlayButtonTag
|
||||||
),
|
),
|
||||||
secondaryActions = listOf(
|
secondaryActions = listOf(
|
||||||
|
MediaDetailSecondaryActionUiModel.MarkAsWatched(
|
||||||
|
watched = seriesWatched,
|
||||||
|
onClick = { onMarkAsWatched(!seriesWatched) },
|
||||||
|
testTag = SeriesWatchedButtonTag
|
||||||
|
),
|
||||||
MediaDetailSecondaryActionUiModel.Icon(
|
MediaDetailSecondaryActionUiModel.Icon(
|
||||||
icon = Icons.Outlined.Add,
|
icon = Icons.Outlined.Add,
|
||||||
testTag = SeriesAddButtonTag
|
testTag = SeriesAddButtonTag
|
||||||
|
|||||||
@@ -394,6 +394,7 @@ private fun EpisodeCard(
|
|||||||
internal const val SeriesPlayButtonTag = "series-play-button"
|
internal const val SeriesPlayButtonTag = "series-play-button"
|
||||||
internal const val SeriesAddButtonTag = "series-add-button"
|
internal const val SeriesAddButtonTag = "series-add-button"
|
||||||
internal const val SeriesDownloadButtonTag = "series-download-button"
|
internal const val SeriesDownloadButtonTag = "series-download-button"
|
||||||
|
internal const val SeriesWatchedButtonTag = "series-watched-button"
|
||||||
internal const val SeriesDownloadDialogTag = "series-download-dialog"
|
internal const val SeriesDownloadDialogTag = "series-download-dialog"
|
||||||
internal const val SeriesDownloadSeasonButtonTag = "series-download-season-button"
|
internal const val SeriesDownloadSeasonButtonTag = "series-download-season-button"
|
||||||
internal const val SeriesDownloadAllButtonTag = "series-download-all-button"
|
internal const val SeriesDownloadAllButtonTag = "series-download-all-button"
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import androidx.lifecycle.ViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import hu.bbara.purefin.core.data.HomeRepository
|
import hu.bbara.purefin.core.data.HomeRepository
|
||||||
|
import hu.bbara.purefin.core.jellyfin.JellyfinMediaMetadataUpdater
|
||||||
import hu.bbara.purefin.core.model.MediaUiModel
|
import hu.bbara.purefin.core.model.MediaUiModel
|
||||||
import hu.bbara.purefin.core.model.MovieUiModel
|
import hu.bbara.purefin.core.model.MovieUiModel
|
||||||
import hu.bbara.purefin.core.model.SeriesUiModel
|
import hu.bbara.purefin.core.model.SeriesUiModel
|
||||||
@@ -25,6 +26,7 @@ import javax.inject.Inject
|
|||||||
class LibraryViewModel @Inject constructor(
|
class LibraryViewModel @Inject constructor(
|
||||||
private val homeRepository: HomeRepository,
|
private val homeRepository: HomeRepository,
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: NavigationManager,
|
||||||
|
private val jellyfinMediaMetadataUpdater: JellyfinMediaMetadataUpdater,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val selectedLibrary = MutableStateFlow<UUID?>(null)
|
private val selectedLibrary = MutableStateFlow<UUID?>(null)
|
||||||
@@ -69,6 +71,12 @@ class LibraryViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun markAsWatched(mediaUiModel: MediaUiModel, watched: Boolean) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
jellyfinMediaMetadataUpdater.markAsWatched(mediaUiModel.id, watched)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun onBack() {
|
fun onBack() {
|
||||||
navigationManager.pop()
|
navigationManager.pop()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import hu.bbara.purefin.core.Offline
|
|||||||
import hu.bbara.purefin.core.data.LocalMediaRepository
|
import hu.bbara.purefin.core.data.LocalMediaRepository
|
||||||
import hu.bbara.purefin.core.download.DownloadState
|
import hu.bbara.purefin.core.download.DownloadState
|
||||||
import hu.bbara.purefin.core.download.MediaDownloadController
|
import hu.bbara.purefin.core.download.MediaDownloadController
|
||||||
|
import hu.bbara.purefin.core.jellyfin.JellyfinMediaMetadataUpdater
|
||||||
import hu.bbara.purefin.core.navigation.EpisodeDto
|
import hu.bbara.purefin.core.navigation.EpisodeDto
|
||||||
import hu.bbara.purefin.core.navigation.NavigationManager
|
import hu.bbara.purefin.core.navigation.NavigationManager
|
||||||
import hu.bbara.purefin.core.navigation.Route
|
import hu.bbara.purefin.core.navigation.Route
|
||||||
@@ -30,6 +31,7 @@ class EpisodeScreenViewModel @Inject constructor(
|
|||||||
@param:Offline private val offlineMediaCatalogReader: LocalMediaRepository,
|
@param:Offline private val offlineMediaCatalogReader: LocalMediaRepository,
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: NavigationManager,
|
||||||
private val mediaDownloadManager: MediaDownloadController,
|
private val mediaDownloadManager: MediaDownloadController,
|
||||||
|
private val jellyfinMediaMetadataUpdater: JellyfinMediaMetadataUpdater,
|
||||||
): ViewModel() {
|
): ViewModel() {
|
||||||
|
|
||||||
private val _episode = MutableStateFlow<EpisodeDto?>(null)
|
private val _episode = MutableStateFlow<EpisodeDto?>(null)
|
||||||
@@ -81,6 +83,13 @@ class EpisodeScreenViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun markAsWatched(watched: Boolean) {
|
||||||
|
val episodeId = _episode.value?.id ?: return
|
||||||
|
viewModelScope.launch {
|
||||||
|
jellyfinMediaMetadataUpdater.markAsWatched(episodeId, watched)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun onDownloadClick() {
|
fun onDownloadClick() {
|
||||||
val episodeId = _episode.value?.id ?: return
|
val episodeId = _episode.value?.id ?: return
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import hu.bbara.purefin.core.Offline
|
|||||||
import hu.bbara.purefin.core.data.LocalMediaRepository
|
import hu.bbara.purefin.core.data.LocalMediaRepository
|
||||||
import hu.bbara.purefin.core.download.DownloadState
|
import hu.bbara.purefin.core.download.DownloadState
|
||||||
import hu.bbara.purefin.core.download.MediaDownloadController
|
import hu.bbara.purefin.core.download.MediaDownloadController
|
||||||
|
import hu.bbara.purefin.core.jellyfin.JellyfinMediaMetadataUpdater
|
||||||
import hu.bbara.purefin.core.navigation.MovieDto
|
import hu.bbara.purefin.core.navigation.MovieDto
|
||||||
import hu.bbara.purefin.core.navigation.NavigationManager
|
import hu.bbara.purefin.core.navigation.NavigationManager
|
||||||
import hu.bbara.purefin.core.navigation.Route
|
import hu.bbara.purefin.core.navigation.Route
|
||||||
@@ -28,6 +29,7 @@ class MovieScreenViewModel @Inject constructor(
|
|||||||
@param:Offline private val offlineMediaCatalogReader: LocalMediaRepository,
|
@param:Offline private val offlineMediaCatalogReader: LocalMediaRepository,
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: NavigationManager,
|
||||||
private val mediaDownloadManager: MediaDownloadController,
|
private val mediaDownloadManager: MediaDownloadController,
|
||||||
|
private val jellyfinMediaMetadataUpdater: JellyfinMediaMetadataUpdater,
|
||||||
): ViewModel() {
|
): ViewModel() {
|
||||||
|
|
||||||
private val _movie = MutableStateFlow<MovieDto?>(null)
|
private val _movie = MutableStateFlow<MovieDto?>(null)
|
||||||
@@ -68,6 +70,13 @@ class MovieScreenViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun markAsWatched(watched: Boolean) {
|
||||||
|
val movieId = movie.value?.id ?: return
|
||||||
|
viewModelScope.launch {
|
||||||
|
jellyfinMediaMetadataUpdater.markAsWatched(movieId, watched)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun onDownloadClick() {
|
fun onDownloadClick() {
|
||||||
val movieId = movie.value?.id ?: return
|
val movieId = movie.value?.id ?: return
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import hu.bbara.purefin.core.Offline
|
|||||||
import hu.bbara.purefin.core.data.LocalMediaRepository
|
import hu.bbara.purefin.core.data.LocalMediaRepository
|
||||||
import hu.bbara.purefin.core.download.DownloadState
|
import hu.bbara.purefin.core.download.DownloadState
|
||||||
import hu.bbara.purefin.core.download.MediaDownloadController
|
import hu.bbara.purefin.core.download.MediaDownloadController
|
||||||
|
import hu.bbara.purefin.core.jellyfin.JellyfinMediaMetadataUpdater
|
||||||
import hu.bbara.purefin.core.navigation.EpisodeDto
|
import hu.bbara.purefin.core.navigation.EpisodeDto
|
||||||
import hu.bbara.purefin.core.navigation.NavigationManager
|
import hu.bbara.purefin.core.navigation.NavigationManager
|
||||||
import hu.bbara.purefin.core.navigation.Route
|
import hu.bbara.purefin.core.navigation.Route
|
||||||
@@ -33,6 +34,7 @@ class SeriesViewModel @Inject constructor(
|
|||||||
@param:Offline private val offlineMediaCatalogReader: LocalMediaRepository,
|
@param:Offline private val offlineMediaCatalogReader: LocalMediaRepository,
|
||||||
private val navigationManager: NavigationManager,
|
private val navigationManager: NavigationManager,
|
||||||
private val mediaDownloadManager: MediaDownloadController,
|
private val mediaDownloadManager: MediaDownloadController,
|
||||||
|
private val jellyfinMediaMetadataUpdater: JellyfinMediaMetadataUpdater,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
private val _series = MutableStateFlow<SeriesDto?>(null)
|
private val _series = MutableStateFlow<SeriesDto?>(null)
|
||||||
@@ -183,6 +185,13 @@ class SeriesViewModel @Inject constructor(
|
|||||||
navigationManager.replaceAll(Route.Home)
|
navigationManager.replaceAll(Route.Home)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun markAsWatched(watched: Boolean) {
|
||||||
|
val seriesId = _series.value?.id ?: return
|
||||||
|
viewModelScope.launch {
|
||||||
|
jellyfinMediaMetadataUpdater.markAsWatched(seriesId, watched)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun selectSeries(series: SeriesDto) {
|
fun selectSeries(series: SeriesDto) {
|
||||||
_series.value = series
|
_series.value = series
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
|
|||||||
Reference in New Issue
Block a user