From 6f5e1ed199a1ae170fda296026f8547b0cf53964 Mon Sep 17 00:00:00 2001 From: Kayos Date: Sat, 23 May 2026 19:54:37 -0700 Subject: [PATCH] Straw phase I: SB segment count chip + clear-history buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Detail screen: VideoDetailViewModel now also fetches SponsorBlock segment count for the user's currently-enabled categories alongside RYD. When >0, detail screen shows a "⏭ N skip(s)" AssistChip next to the like/dislike chips. Lets the user see at-a-glance whether SB is going to do anything on this video before tapping Play. Settings screen: "History" section at the bottom with two OutlinedButtons — "Clear watch history" and "Clear searches". Each calls the corresponding HistoryStore.clear* method. List updates immediately via the StateFlow. Empty-category set on SB now means "skip the API call" (was already checking this in PlayerViewModel.resolve, now also in VideoDetailViewModel). --- .../straw/feature/detail/VideoDetailScreen.kt | 6 ++++++ .../feature/detail/VideoDetailViewModel.kt | 8 ++++++++ .../straw/feature/settings/SettingsScreen.kt | 18 ++++++++++++++++++ 3 files changed, 32 insertions(+) diff --git a/strawApp/src/main/kotlin/com/sulkta/straw/feature/detail/VideoDetailScreen.kt b/strawApp/src/main/kotlin/com/sulkta/straw/feature/detail/VideoDetailScreen.kt index bda85ce24..6bbf3520a 100644 --- a/strawApp/src/main/kotlin/com/sulkta/straw/feature/detail/VideoDetailScreen.kt +++ b/strawApp/src/main/kotlin/com/sulkta/straw/feature/detail/VideoDetailScreen.kt @@ -115,6 +115,12 @@ fun VideoDetailScreen( ), ) } + if (d.sbSegmentCount > 0) { + AssistChip( + onClick = {}, + label = { Text("⏭ ${d.sbSegmentCount} skip${if (d.sbSegmentCount == 1) "" else "s"}") }, + ) + } } Spacer(modifier = Modifier.height(16.dp)) diff --git a/strawApp/src/main/kotlin/com/sulkta/straw/feature/detail/VideoDetailViewModel.kt b/strawApp/src/main/kotlin/com/sulkta/straw/feature/detail/VideoDetailViewModel.kt index 9c12d13c6..f3a205f4f 100644 --- a/strawApp/src/main/kotlin/com/sulkta/straw/feature/detail/VideoDetailViewModel.kt +++ b/strawApp/src/main/kotlin/com/sulkta/straw/feature/detail/VideoDetailViewModel.kt @@ -8,9 +8,11 @@ package com.sulkta.straw.feature.detail import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.sulkta.straw.data.History +import com.sulkta.straw.data.Settings import com.sulkta.straw.data.WatchHistoryItem import com.sulkta.straw.net.RydClient import com.sulkta.straw.net.RydVotes +import com.sulkta.straw.net.SponsorBlockClient import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -27,6 +29,7 @@ data class VideoDetail( val description: String, val thumbnail: String?, val ryd: RydVotes? = null, + val sbSegmentCount: Int = 0, ) data class VideoDetailUiState( @@ -70,6 +73,10 @@ class VideoDetailViewModel : ViewModel() { val ryd = withContext(Dispatchers.IO) { runCatching { RydClient.fetch(videoId) }.getOrNull() } + val sbCats = Settings.get().sbCategories.value.map { it.key } + val sbCount = if (sbCats.isEmpty()) 0 else withContext(Dispatchers.IO) { + runCatching { SponsorBlockClient.fetch(videoId, sbCats).size }.getOrDefault(0) + } _ui.value = VideoDetailUiState( loading = false, detail = VideoDetail( @@ -80,6 +87,7 @@ class VideoDetailViewModel : ViewModel() { description = info.description?.content ?: "", thumbnail = thumb, ryd = ryd, + sbSegmentCount = sbCount, ), streamInfo = info, ) diff --git a/strawApp/src/main/kotlin/com/sulkta/straw/feature/settings/SettingsScreen.kt b/strawApp/src/main/kotlin/com/sulkta/straw/feature/settings/SettingsScreen.kt index 764414c61..fd96ea6e7 100644 --- a/strawApp/src/main/kotlin/com/sulkta/straw/feature/settings/SettingsScreen.kt +++ b/strawApp/src/main/kotlin/com/sulkta/straw/feature/settings/SettingsScreen.kt @@ -17,6 +17,7 @@ import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedButton import androidx.compose.material3.Switch import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -26,6 +27,7 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import com.sulkta.straw.data.History import com.sulkta.straw.data.SbCategory import com.sulkta.straw.data.Settings @@ -68,6 +70,22 @@ fun SettingsScreen() { ) HorizontalDivider() } + + Spacer(modifier = Modifier.height(32.dp)) + Text( + "History", + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.SemiBold, + ) + Spacer(modifier = Modifier.height(12.dp)) + Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) { + OutlinedButton(onClick = { History.get().clearWatches() }) { + Text("Clear watch history") + } + OutlinedButton(onClick = { History.get().clearSearches() }) { + Text("Clear searches") + } + } } }