From 7eecfd72ac8444f0ebba4736dc877d8b350b3b56 Mon Sep 17 00:00:00 2001 From: Jeena Date: Sat, 10 Oct 2026 10:27:30 +0000 Subject: [PATCH] ui: Fix compile errors in first build Composable API mismatches with tv-material 1.0.1: NavigationDrawerItem takes leadingContent/content (not icon/label), Card needs no shape. Move VideoListItem to top level so its supertype reference resolves. Pass onOpenVideo by name because the trailing lambda slot on VideoListScreen goes to the non-functional viewModel parameter. Nullable search param on local channel listings; make StateFlow collection use collectAsState directly to avoid a JVM signature clash. Verified by building app-debug.apk inside the container (JDK 21 installed there for this purpose). --- .../net/jeena/jtubetv/data/api/PeerTubeApi.kt | 2 +- .../net/jeena/jtubetv/data/network/Network.kt | 1 + .../java/net/jeena/jtubetv/ui/AppViewModel.kt | 2 +- .../jtubetv/ui/list/VideoListViewModel.kt | 12 +-- .../jtubetv/ui/screens/ChannelsScreen.kt | 3 +- .../jeena/jtubetv/ui/screens/HomeScreen.kt | 77 ++++++++++--------- .../jeena/jtubetv/ui/screens/SetupScreen.kt | 12 +-- .../ui/screens/list/VideoListScreen.kt | 10 ++- 8 files changed, 60 insertions(+), 59 deletions(-) diff --git a/app/src/main/java/net/jeena/jtubetv/data/api/PeerTubeApi.kt b/app/src/main/java/net/jeena/jtubetv/data/api/PeerTubeApi.kt index 3c9db7d..8c29964 100644 --- a/app/src/main/java/net/jeena/jtubetv/data/api/PeerTubeApi.kt +++ b/app/src/main/java/net/jeena/jtubetv/data/api/PeerTubeApi.kt @@ -72,7 +72,7 @@ interface PeerTubeApi { @GET("video-channels") suspend fun channels( - @Query("search") search: String, + @Query("search") search: String?, @Query("searchTarget") searchTarget: String = "local", @Query("start") start: Long, @Query("count") count: Int, diff --git a/app/src/main/java/net/jeena/jtubetv/data/network/Network.kt b/app/src/main/java/net/jeena/jtubetv/data/network/Network.kt index a555d6b..dd2b68d 100644 --- a/app/src/main/java/net/jeena/jtubetv/data/network/Network.kt +++ b/app/src/main/java/net/jeena/jtubetv/data/network/Network.kt @@ -5,6 +5,7 @@ import net.jeena.jtubetv.data.api.PeerTubeApi import net.jeena.jtubetv.data.api.TokenResponse import net.jeena.jtubetv.data.auth.TokenStore import okhttp3.FormBody +import okhttp3.Authenticator import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.MediaType.Companion.toMediaType import okhttp3.OkHttpClient diff --git a/app/src/main/java/net/jeena/jtubetv/ui/AppViewModel.kt b/app/src/main/java/net/jeena/jtubetv/ui/AppViewModel.kt index 4e0bae6..03721c3 100644 --- a/app/src/main/java/net/jeena/jtubetv/ui/AppViewModel.kt +++ b/app/src/main/java/net/jeena/jtubetv/ui/AppViewModel.kt @@ -16,8 +16,8 @@ sealed interface SessionState { class AppViewModel(private val repository: PeerTubeRepository = AppGraph.repository) : ViewModel() { - val sessionState: StateFlow = _sessionState private val _sessionState = MutableStateFlow(SessionState.Loading) + val sessionState: StateFlow = _sessionState val loginError = MutableStateFlow(null) val loggingIn = MutableStateFlow(false) diff --git a/app/src/main/java/net/jeena/jtubetv/ui/list/VideoListViewModel.kt b/app/src/main/java/net/jeena/jtubetv/ui/list/VideoListViewModel.kt index 162f230..90264f3 100644 --- a/app/src/main/java/net/jeena/jtubetv/ui/list/VideoListViewModel.kt +++ b/app/src/main/java/net/jeena/jtubetv/ui/list/VideoListViewModel.kt @@ -16,6 +16,12 @@ sealed interface ListSource { data class Channel(val handle: String, val displayName: String) : ListSource } +/** One list row: a plain video, or a history entry (same video, server metadata). */ +sealed interface VideoListItem { + data class Video(val video: net.jeena.jtubetv.data.api.VideoDto) : VideoListItem + data class HistoryEntry(val history: net.jeena.jtubetv.data.api.HistoryElement) : VideoListItem +} + class VideoListViewModel( private val repository: PeerTubeRepository, val source: ListSource, @@ -53,12 +59,6 @@ class VideoListViewModel( if (items.isEmpty() && loadingInitial) loadMore(force = true) } - /** History/search entries can hide the underlying video shape. */ - sealed interface VideoListItem { - data class Video(val video: net.jeena.jtubetv.data.api.VideoDto) : VideoListItem - data class HistoryEntry(val history: net.jeena.jtubetv.data.api.HistoryElement) : VideoListItem - } - companion object { fun factory(source: ListSource): ViewModelProvider.Factory = object : ViewModelProvider.Factory { diff --git a/app/src/main/java/net/jeena/jtubetv/ui/screens/ChannelsScreen.kt b/app/src/main/java/net/jeena/jtubetv/ui/screens/ChannelsScreen.kt index 01c1eee..6a7ef4f 100644 --- a/app/src/main/java/net/jeena/jtubetv/ui/screens/ChannelsScreen.kt +++ b/app/src/main/java/net/jeena/jtubetv/ui/screens/ChannelsScreen.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.lazy.LazyColumn @@ -119,8 +120,6 @@ private fun ChannelCard(channel: VideoChannelDto, onClick: () -> Unit) { modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp, vertical = 6.dp), - shape = androidx.compose.foundation.shape.RoundedCornerShape(8.dp), - colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface), ) { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(12.dp)) { val avatar = channel.avatars.maxByOrNull { it.width } diff --git a/app/src/main/java/net/jeena/jtubetv/ui/screens/HomeScreen.kt b/app/src/main/java/net/jeena/jtubetv/ui/screens/HomeScreen.kt index 7f1fcb2..ea64518 100644 --- a/app/src/main/java/net/jeena/jtubetv/ui/screens/HomeScreen.kt +++ b/app/src/main/java/net/jeena/jtubetv/ui/screens/HomeScreen.kt @@ -60,59 +60,66 @@ fun HomeScreen() { } NavigationDrawer(drawerContent = { - NavigationDrawerColumn( - selected = destination, - onSelect = { - destination = it - channelDetails = null - }, - ) + androidx.compose.foundation.layout.Column( + modifier = Modifier.width(220.dp).padding(vertical = 16.dp), + ) { + Text( + text = "JTube", + modifier = Modifier.padding(start = 28.dp, bottom = 16.dp), + style = androidx.tv.material3.MaterialTheme.typography.headlineSmall, + ) + Destination.entries.forEach { dest -> + NavigationDrawerItem( + selected = dest == destination, + onClick = { + destination = dest + channelDetails = null + }, + leadingContent = { + Icon(imageVector = dest.icon(), contentDescription = null) + }, + content = { Text(dest.title) }, + ) + } + } }) { Row(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) { when (destination) { Destination.LocalVideos -> - VideoListScreen(ListSource.LocalVideos) { playingVideo = it.shortUUID } + VideoListScreen( + source = ListSource.LocalVideos, + onOpenVideo = { playingVideo = it.shortUUID }, + ) Destination.MyVideos -> - VideoListScreen(ListSource.MyVideos) { playingVideo = it.shortUUID } + VideoListScreen( + source = ListSource.MyVideos, + onOpenVideo = { playingVideo = it.shortUUID }, + ) Destination.Channels -> ChannelsScreen( onOpenChannel = { handle -> channelDetails = handle }, ) Destination.Search -> - VideoListScreen(ListSource.Search) { playingVideo = it.shortUUID } + VideoListScreen( + source = ListSource.Search, + onOpenVideo = { playingVideo = it.shortUUID }, + ) Destination.Subscriptions -> - VideoListScreen(ListSource.Subscriptions) { playingVideo = it.shortUUID } + VideoListScreen( + source = ListSource.Subscriptions, + onOpenVideo = { playingVideo = it.shortUUID }, + ) Destination.History -> - VideoListScreen(ListSource.History) { playingVideo = it.shortUUID } + VideoListScreen( + source = ListSource.History, + onOpenVideo = { playingVideo = it.shortUUID }, + ) } } } } } -@Composable -private fun NavigationDrawerColumn( - selected: Destination, - onSelect: (Destination) -> Unit, - modifier: Modifier = Modifier, -) { - androidx.compose.foundation.layout.Column(modifier = modifier.width(220.dp).padding(vertical = 16.dp)) { - Text( - text = "JTube", - modifier = Modifier.padding(start = 28.dp, bottom = 16.dp), - style = androidx.tv.material3.MaterialTheme.typography.headlineSmall, - ) - Destination.entries.forEach { dest -> - NavigationDrawerItem( - icon = { Icon(imageVector = dest.icon(), contentDescription = null) }, - label = { Text(dest.title) }, - selected = dest == selected, - onClick = { onSelect(dest) }, - ) - } - } -} - private fun Destination.icon(): androidx.compose.ui.graphics.vector.ImageVector = when (this) { Destination.LocalVideos -> Icons.Filled.VideoLibrary diff --git a/app/src/main/java/net/jeena/jtubetv/ui/screens/SetupScreen.kt b/app/src/main/java/net/jeena/jtubetv/ui/screens/SetupScreen.kt index 0da3e79..52e15bb 100644 --- a/app/src/main/java/net/jeena/jtubetv/ui/screens/SetupScreen.kt +++ b/app/src/main/java/net/jeena/jtubetv/ui/screens/SetupScreen.kt @@ -34,8 +34,8 @@ fun SetupScreen( val url = rememberSaveable { mutableStateOf("https://tube.jeena.net") } val username = rememberSaveable { mutableStateOf("") } val password = rememberSaveable { mutableStateOf("") } - val busy = loggingIn.collectAsStateFlow() - val message = error.collectAsStateFlow() + val busy = loggingIn.collectAsState() + val message = error.collectAsState() Column( modifier = Modifier @@ -92,11 +92,3 @@ fun SetupScreen( } } } - -@Composable -private fun StateFlow.collectAsStateFlow(): State = - collectAsState() - -@Composable -private fun StateFlow.collectAsStateFlow(): State = - collectAsState() \ No newline at end of file diff --git a/app/src/main/java/net/jeena/jtubetv/ui/screens/list/VideoListScreen.kt b/app/src/main/java/net/jeena/jtubetv/ui/screens/list/VideoListScreen.kt index a122511..b6f278f 100644 --- a/app/src/main/java/net/jeena/jtubetv/ui/screens/list/VideoListScreen.kt +++ b/app/src/main/java/net/jeena/jtubetv/ui/screens/list/VideoListScreen.kt @@ -27,6 +27,7 @@ import net.jeena.jtubetv.R import net.jeena.jtubetv.data.api.VideoDto import net.jeena.jtubetv.ui.list.PagedListViewModel import net.jeena.jtubetv.ui.list.ListSource +import net.jeena.jtubetv.ui.list.VideoListItem import net.jeena.jtubetv.ui.list.VideoListViewModel @Composable @@ -82,10 +83,11 @@ fun VideoListScreen( } LazyColumn(modifier = Modifier.fillMaxSize().padding(vertical = 8.dp)) { - itemsIndexed(items) { index, item -> - val video = when (item) { - is VideoListViewModel.VideoListItem.HistoryEntry -> item.history.video - is VideoListViewModel.VideoListItem.Video -> item.video + itemsIndexed(items) { index, item -> + val video: VideoDto = when (item) { + is VideoListItem.HistoryEntry -> item.history.video + is VideoListItem.Video -> item.video + else -> return@itemsIndexed } VideoCard( video = video,