feat: add dedicated Mark as Watched button to details page (#958)

This commit is contained in:
MukeshCheekatla 2026-05-07 21:22:13 +05:30
parent fda5812060
commit 5ab7ea8dfc
3 changed files with 63 additions and 0 deletions

View file

@ -93,6 +93,7 @@ import com.nuvio.app.features.trailer.TrailerPlaybackSource
import com.nuvio.app.features.watched.WatchedRepository import com.nuvio.app.features.watched.WatchedRepository
import com.nuvio.app.features.watched.previousReleasedEpisodesBefore import com.nuvio.app.features.watched.previousReleasedEpisodesBefore
import com.nuvio.app.features.watched.releasedEpisodesForSeason import com.nuvio.app.features.watched.releasedEpisodesForSeason
import com.nuvio.app.features.watched.watchedItemKey
import com.nuvio.app.features.watchprogress.CurrentDateProvider import com.nuvio.app.features.watchprogress.CurrentDateProvider
import com.nuvio.app.features.watchprogress.WatchProgressEntry import com.nuvio.app.features.watchprogress.WatchProgressEntry
import com.nuvio.app.features.watchprogress.WatchProgressRepository import com.nuvio.app.features.watchprogress.WatchProgressRepository
@ -662,10 +663,12 @@ fun MetaDetailsScreen(
isTablet = isTablet, isTablet = isTablet,
playButtonLabel = playButtonLabel, playButtonLabel = playButtonLabel,
isSaved = isSaved, isSaved = isSaved,
isMarkedAsWatched = watchedUiState.watchedKeys.contains(watchedItemKey(meta.type, meta.id)),
onPrimaryPlayClick = onPrimaryPlayClick, onPrimaryPlayClick = onPrimaryPlayClick,
onPrimaryPlayLongClick = onPrimaryPlayLongClick, onPrimaryPlayLongClick = onPrimaryPlayLongClick,
onSaveClick = toggleSaved, onSaveClick = toggleSaved,
onSaveLongClick = openLibraryListPicker, onSaveLongClick = openLibraryListPicker,
onWatchedClick = { WatchingActions.toggleMetaWatched(meta) },
showManualPlayOption = showManualPlayOption, showManualPlayOption = showManualPlayOption,
preferredEpisodeSeasonNumber = seriesAction?.seasonNumber, preferredEpisodeSeasonNumber = seriesAction?.seasonNumber,
preferredEpisodeNumber = seriesAction?.episodeNumber, preferredEpisodeNumber = seriesAction?.episodeNumber,
@ -996,10 +999,12 @@ private fun ConfiguredMetaSections(
isTablet: Boolean, isTablet: Boolean,
playButtonLabel: String, playButtonLabel: String,
isSaved: Boolean, isSaved: Boolean,
isMarkedAsWatched: Boolean,
onPrimaryPlayClick: () -> Unit, onPrimaryPlayClick: () -> Unit,
onPrimaryPlayLongClick: (() -> Unit)?, onPrimaryPlayLongClick: (() -> Unit)?,
onSaveClick: () -> Unit, onSaveClick: () -> Unit,
onSaveLongClick: (() -> Unit)?, onSaveLongClick: (() -> Unit)?,
onWatchedClick: () -> Unit,
showManualPlayOption: Boolean, showManualPlayOption: Boolean,
preferredEpisodeSeasonNumber: Int?, preferredEpisodeSeasonNumber: Int?,
preferredEpisodeNumber: Int?, preferredEpisodeNumber: Int?,
@ -1062,11 +1067,13 @@ private fun ConfiguredMetaSections(
stringResource(Res.string.action_save) stringResource(Res.string.action_save)
}, },
isSaved = isSaved, isSaved = isSaved,
isWatched = isMarkedAsWatched,
isTablet = isTablet, isTablet = isTablet,
onPlayClick = onPrimaryPlayClick, onPlayClick = onPrimaryPlayClick,
onPlayLongClick = if (showManualPlayOption) onPrimaryPlayLongClick else null, onPlayLongClick = if (showManualPlayOption) onPrimaryPlayLongClick else null,
onSaveClick = onSaveClick, onSaveClick = onSaveClick,
onSaveLongClick = onSaveLongClick, onSaveLongClick = onSaveLongClick,
onWatchedClick = onWatchedClick,
) )
} }
MetaScreenSectionKey.OVERVIEW -> { MetaScreenSectionKey.OVERVIEW -> {

View file

@ -13,6 +13,8 @@ import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.Visibility
import androidx.compose.material3.IconButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
@ -37,11 +39,13 @@ fun DetailActionButtons(
playLabel: String = stringResource(Res.string.action_play), playLabel: String = stringResource(Res.string.action_play),
saveLabel: String = stringResource(Res.string.action_save), saveLabel: String = stringResource(Res.string.action_save),
isSaved: Boolean = false, isSaved: Boolean = false,
isWatched: Boolean = false,
isTablet: Boolean = false, isTablet: Boolean = false,
onPlayClick: () -> Unit = {}, onPlayClick: () -> Unit = {},
onPlayLongClick: (() -> Unit)? = null, onPlayLongClick: (() -> Unit)? = null,
onSaveClick: () -> Unit = {}, onSaveClick: () -> Unit = {},
onSaveLongClick: (() -> Unit)? = null, onSaveLongClick: (() -> Unit)? = null,
onWatchedClick: () -> Unit = {},
) { ) {
val playPainter = appIconPainter(AppIconResource.PlayerPlay) val playPainter = appIconPainter(AppIconResource.PlayerPlay)
val libraryAddPainter = appIconPainter(AppIconResource.LibraryAddPlus) val libraryAddPainter = appIconPainter(AppIconResource.LibraryAddPlus)
@ -138,5 +142,21 @@ fun DetailActionButtons(
) )
} }
} }
Surface(
modifier = Modifier.size(50.dp),
shape = RoundedCornerShape(40.dp),
border = BorderStroke(1.dp, MaterialTheme.colorScheme.outline),
color = if (isWatched) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surface.copy(alpha = 0f),
contentColor = if (isWatched) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurface,
) {
IconButton(onClick = onWatchedClick) {
Icon(
imageVector = Icons.Default.Visibility,
contentDescription = null,
modifier = Modifier.size(20.dp),
)
}
}
} }
} }

View file

@ -22,6 +22,42 @@ import kotlinx.coroutines.launch
object WatchingActions { object WatchingActions {
private val actionScope = CoroutineScope(SupervisorJob() + Dispatchers.Default) private val actionScope = CoroutineScope(SupervisorJob() + Dispatchers.Default)
fun toggleMetaWatched(meta: MetaDetails) {
if (meta.type != "series" && meta.type != "show" && meta.type != "tv") {
WatchedRepository.toggleWatched(
WatchedItem(
id = meta.id,
type = meta.type,
name = meta.name,
poster = meta.poster,
releaseInfo = meta.releaseInfo,
markedAtEpochMs = 0L
)
)
return
}
val isCurrentlyWatched = WatchedRepository.isWatched(
id = meta.id,
type = meta.type,
)
val todayIsoDate = CurrentDateProvider.todayIsoDate()
val seriesItems = buildList {
add(meta.toSeriesWatchedItem())
addAll(meta.releasedPlayableEpisodes(todayIsoDate).map(meta::toEpisodeWatchedItem))
}
if (isCurrentlyWatched) {
WatchedRepository.unmarkWatched(seriesItems)
} else {
WatchedRepository.markWatched(seriesItems)
WatchProgressRepository.clearProgress(
meta.releasedPlayableEpisodes(todayIsoDate).map(meta::episodePlaybackId),
)
}
}
suspend fun togglePosterWatched(preview: MetaPreview) { suspend fun togglePosterWatched(preview: MetaPreview) {
if (preview.type != "series") { if (preview.type != "series") {
WatchedRepository.toggleWatched(preview.toWatchedItem(markedAtEpochMs = 0L)) WatchedRepository.toggleWatched(preview.toWatchedItem(markedAtEpochMs = 0L))