WIP lyric selection

This commit is contained in:
Damontecres 2026-02-28 20:38:32 -05:00
parent 6fa3f550b0
commit d37c7604d2
No known key found for this signature in database
3 changed files with 49 additions and 24 deletions

View file

@ -22,7 +22,6 @@ import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.tv.material3.Text
import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.model.CollectionFolderFilter import com.github.damontecres.wholphin.data.model.CollectionFolderFilter
import com.github.damontecres.wholphin.data.model.GetItemsFilter import com.github.damontecres.wholphin.data.model.GetItemsFilter
@ -55,7 +54,6 @@ fun CollectionFolderMusic(
stringResource(R.string.recommended), stringResource(R.string.recommended),
stringResource(R.string.albums), stringResource(R.string.albums),
stringResource(R.string.artists), stringResource(R.string.artists),
stringResource(R.string.playlists),
stringResource(R.string.genres), stringResource(R.string.genres),
stringResource(R.string.songs), stringResource(R.string.songs),
) )
@ -173,14 +171,8 @@ fun CollectionFolderMusic(
) )
} }
// Playlists
3 -> {
// TODO
Text("TODO")
}
// Genres // Genres
4 -> { 3 -> {
GenreCardGrid( GenreCardGrid(
itemId = destination.itemId, itemId = destination.itemId,
includeItemTypes = listOf(BaseItemKind.MUSIC_ALBUM, BaseItemKind.AUDIO), includeItemTypes = listOf(BaseItemKind.MUSIC_ALBUM, BaseItemKind.AUDIO),
@ -192,7 +184,7 @@ fun CollectionFolderMusic(
} }
// Songs // Songs
5 -> { 4 -> {
CollectionFolderGrid( CollectionFolderGrid(
preferences = preferences, preferences = preferences,
onClickItem = { _, item -> onClickItem = { _, item ->

View file

@ -3,20 +3,28 @@ package com.github.damontecres.wholphin.ui.detail.music
import androidx.compose.animation.animateColorAsState import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.LinearEasing import androidx.compose.animation.core.LinearEasing
import androidx.compose.animation.core.tween import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsFocusedAsState
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.relocation.BringIntoViewRequester import androidx.compose.foundation.relocation.BringIntoViewRequester
import androidx.compose.foundation.relocation.bringIntoViewRequester import androidx.compose.foundation.relocation.bringIntoViewRequester
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.tv.material3.MaterialTheme import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text import androidx.tv.material3.Text
@ -26,6 +34,7 @@ import org.jellyfin.sdk.model.api.LyricLine
@Composable @Composable
fun LyricsContent( fun LyricsContent(
synced: Boolean,
lyrics: LyricDto?, lyrics: LyricDto?,
currentLyricPosition: Int?, currentLyricPosition: Int?,
onClick: (LyricLine) -> Unit, onClick: (LyricLine) -> Unit,
@ -33,16 +42,23 @@ fun LyricsContent(
) { ) {
val listState = rememberLazyListState(currentLyricPosition ?: 0) val listState = rememberLazyListState(currentLyricPosition ?: 0)
val bringIntoViewRequester = remember { BringIntoViewRequester() } val bringIntoViewRequester = remember { BringIntoViewRequester() }
if (synced) {
LaunchedEffect(currentLyricPosition) { LaunchedEffect(currentLyricPosition) {
if (currentLyricPosition != null) { if (currentLyricPosition != null) {
listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index?.let {
if (currentLyricPosition !in 0..it) {
listState.animateScrollToItem(currentLyricPosition)
}
}
bringIntoViewRequester.bringIntoView() bringIntoViewRequester.bringIntoView()
} }
} }
}
Column(modifier) { Column(modifier) {
LazyColumn( LazyColumn(
state = listState, state = listState,
contentPadding = PaddingValues(), contentPadding = PaddingValues(),
verticalArrangement = Arrangement.spacedBy(16.dp), verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
) { ) {
if (lyrics?.lyrics?.isNotEmpty() == true) { if (lyrics?.lyrics?.isNotEmpty() == true) {
@ -55,13 +71,28 @@ fun LyricsContent(
}, },
animationSpec = tween(durationMillis = 500, easing = LinearEasing), animationSpec = tween(durationMillis = 500, easing = LinearEasing),
) )
val interactionSource = remember { MutableInteractionSource() }
val focused by interactionSource.collectIsFocusedAsState()
Box(
modifier =
Modifier
.background(
color = if (focused) MaterialTheme.colorScheme.border.copy(alpha = .66f) else Color.Unspecified,
shape = RoundedCornerShape(8.dp),
),
) {
Text( Text(
text = lyric.text, text = lyric.text,
style = MaterialTheme.typography.bodyLarge, style = MaterialTheme.typography.bodyLarge,
color = color, color = color,
modifier = modifier =
Modifier.ifElse( Modifier
.padding(4.dp)
.clickable(
enabled = !synced,
onClick = { onClick.invoke(lyric) },
interactionSource = interactionSource,
).ifElse(
index == currentLyricPosition, index == currentLyricPosition,
Modifier.bringIntoViewRequester(bringIntoViewRequester), Modifier.bringIntoViewRequester(bringIntoViewRequester),
), ),
@ -71,3 +102,4 @@ fun LyricsContent(
} }
} }
} }
}

View file

@ -104,6 +104,7 @@ fun NowPlayingPage(
modifier = Modifier, modifier = Modifier,
) { ) {
LyricsContent( LyricsContent(
synced = true,
lyrics = state.lyrics, lyrics = state.lyrics,
currentLyricPosition = state.currentLyricIndex, currentLyricPosition = state.currentLyricIndex,
onClick = {}, onClick = {},