mirror of
https://github.com/narrrl/proton-stream.git
synced 2026-11-03 16:41:26 +01:00
feat(android,ios): mark a whole season watched from the title page
Marking was one episode at a time on the title page, or the whole title from the library's long-press menu, so joining a show at season three meant ticking every earlier episode. The season row gains a mark button beside its download one, with an Undo, through a new markEpisodes that setTitleWatched now uses too. Episodes already in the asked-for state are left alone, so Undo restores only what changed. Season chips were keyed by label, and two seasons with one label is a crash in a lazy list; they are keyed by position now.
This commit is contained in:
1 parent
6d1943f2bd
commit
01c252b794
8 files changed
+44
-8
No files matched your search
@@ -551,11 +551,17 @@ class AppViewModel(context: Context, private val workManager: WorkManager) : Vie
|
||||
* title's positions are what this throws away, and a mis-tap on a tile's
|
||||
* menu should not cost them.
|
||||
*/
|
||||
fun setTitleWatched(title: TitleRecord, watched: Boolean) {
|
||||
val episodes = title.seasons.flatMap { it.episodes }
|
||||
fun setTitleWatched(title: TitleRecord, watched: Boolean) =
|
||||
markEpisodes(title.seasons.flatMap { it.episodes }, watched, title.displayName)
|
||||
|
||||
/**
|
||||
* Some episodes seen or unseen — a season, or a whole title — with an
|
||||
* Undo; [what] names them in the message.
|
||||
*/
|
||||
fun markEpisodes(episodes: List<EpisodeRecord>, watched: Boolean, what: String) {
|
||||
changeWatch(
|
||||
episodes,
|
||||
"${title.displayName} marked ${if (watched) "watched" else "unwatched"}",
|
||||
episodes.filter { it.watched != watched },
|
||||
"$what marked ${if (watched) "watched" else "unwatched"}",
|
||||
) { engine, episode, before ->
|
||||
val duration = before?.durationSecs
|
||||
engine.saveWatchState(
|
||||
|
||||
@@ -247,6 +247,7 @@ fun ProtonStreamApp(
|
||||
model::reloadAfterMetadataChange,
|
||||
model::setWatched,
|
||||
body,
|
||||
onMarkEpisodes = model::markEpisodes,
|
||||
franchise = selected.franchise.mapNotNull { key ->
|
||||
state.titles.firstOrNull { it.key == key }
|
||||
},
|
||||
|
||||
@@ -38,8 +38,10 @@ import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.CheckCircle
|
||||
import androidx.compose.material.icons.outlined.CheckCircle
|
||||
import androidx.compose.material.icons.filled.DoneAll
|
||||
import androidx.compose.material.icons.filled.Download
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.RemoveDone
|
||||
import androidx.compose.material.icons.filled.Pause
|
||||
import androidx.compose.material.icons.filled.PlayArrow
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
@@ -97,6 +99,8 @@ internal fun TitleScreen(
|
||||
onMetadataChanged: () -> Unit,
|
||||
onSetWatched: (EpisodeRecord, Boolean) -> Unit,
|
||||
padding: PaddingValues,
|
||||
/** Several episodes seen or unseen at once, named for the message. */
|
||||
onMarkEpisodes: (List<EpisodeRecord>, Boolean, String) -> Unit = { _, _, _ -> },
|
||||
/** Every title of its franchise, in release order, this one included. */
|
||||
franchise: List<TitleRecord> = emptyList(),
|
||||
onOpenTitle: (TitleRecord) -> Unit = {},
|
||||
@@ -343,7 +347,9 @@ internal fun TitleScreen(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
LazyRow(Modifier.weight(1f), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
itemsIndexed(title.seasons, key = { _, it -> it.label }) { index, entry ->
|
||||
// By position: two seasons can share a label, and a
|
||||
// repeated key is a crash in a lazy list.
|
||||
itemsIndexed(title.seasons, key = { index, _ -> index }) { index, entry ->
|
||||
FilterChip(
|
||||
selected = index == seasonIndex,
|
||||
onClick = { seasonIndex = index },
|
||||
@@ -352,6 +358,15 @@ internal fun TitleScreen(
|
||||
}
|
||||
}
|
||||
season?.let {
|
||||
// For someone joining the show partway, who would
|
||||
// otherwise tick every earlier episode one by one.
|
||||
val seen = it.episodes.all(EpisodeRecord::watched)
|
||||
IconButton(onClick = { onMarkEpisodes(it.episodes, !seen, "${title.displayName} ${it.label}") }) {
|
||||
Icon(
|
||||
if (seen) Icons.Default.RemoveDone else Icons.Default.DoneAll,
|
||||
contentDescription = "Mark ${it.label} ${if (seen) "unwatched" else "watched"}",
|
||||
)
|
||||
}
|
||||
IconButton(onClick = { DownloadCoordinator.enqueue(context, it.episodes) }) {
|
||||
Icon(Icons.Default.Download, contentDescription = "Download ${it.label}")
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 631 KiB After Width: | Height: | Size: 632 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 669 KiB After Width: | Height: | Size: 670 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
Reference in new issue
Block a user