Handle adding rows & back handling

This commit is contained in:
Damontecres 2026-01-23 12:06:07 -05:00
parent 6d53869180
commit 5dcdde36c3
No known key found for this signature in database
7 changed files with 270 additions and 39 deletions

View file

@ -13,29 +13,33 @@ import java.util.UUID
@Serializable @Serializable
sealed class HomeRowConfig { sealed class HomeRowConfig {
abstract val id: UUID
abstract val viewOptions: HomeRowViewOptions abstract val viewOptions: HomeRowViewOptions
@Serializable @Serializable
data class ContinueWatching( data class ContinueWatching(
override val id: UUID,
val combined: Boolean, val combined: Boolean,
override val viewOptions: HomeRowViewOptions, override val viewOptions: HomeRowViewOptions,
) : HomeRowConfig() ) : HomeRowConfig()
@Serializable @Serializable
data class RecentlyAdded( data class RecentlyAdded(
override val id: UUID,
val parentId: UUID, val parentId: UUID,
override val viewOptions: HomeRowViewOptions, override val viewOptions: HomeRowViewOptions,
) : HomeRowConfig() ) : HomeRowConfig()
@Serializable @Serializable
data class RecentlyReleased( data class RecentlyReleased(
override val id: UUID,
val parentId: UUID, val parentId: UUID,
override val viewOptions: HomeRowViewOptions, override val viewOptions: HomeRowViewOptions,
) : HomeRowConfig() ) : HomeRowConfig()
@Serializable @Serializable
data class Genres( data class Genres(
val genreId: UUID, override val id: UUID,
val parentId: UUID, val parentId: UUID,
override val viewOptions: HomeRowViewOptions, override val viewOptions: HomeRowViewOptions,
) : HomeRowConfig() ) : HomeRowConfig()

View file

@ -0,0 +1,54 @@
package com.github.damontecres.wholphin.ui.main.settings
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.focusRestorer
import androidx.compose.ui.unit.dp
import androidx.tv.material3.ListItem
import androidx.tv.material3.Text
import com.github.damontecres.wholphin.ui.ifElse
import com.github.damontecres.wholphin.ui.tryRequestFocus
@Composable
fun HomePageLibraryList(
libraries: List<Library>,
onClick: (Library) -> Unit,
modifier: Modifier,
firstFocus: FocusRequester = remember { FocusRequester() },
) {
LaunchedEffect(Unit) { firstFocus.tryRequestFocus() }
Column(modifier = modifier) {
Text(
text = "Add row for...",
)
LazyColumn(
contentPadding = PaddingValues(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier =
modifier
.fillMaxHeight()
.focusRestorer(firstFocus),
) {
itemsIndexed(libraries) { index, library ->
ListItem(
selected = false,
headlineContent = {
Text(library.name)
},
onClick = { onClick.invoke(library) },
modifier = Modifier.ifElse(index == 0, Modifier.focusRequester(firstFocus)),
)
}
}
}
}

View file

@ -0,0 +1,71 @@
package com.github.damontecres.wholphin.ui.main.settings
import androidx.annotation.StringRes
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.focus.focusRestorer
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.tv.material3.ListItem
import androidx.tv.material3.Text
import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.ui.ifElse
import com.github.damontecres.wholphin.ui.tryRequestFocus
@Composable
fun HomePageLibraryRowTypeList(
library: Library,
onClick: (LibraryRowType) -> Unit,
modifier: Modifier,
firstFocus: FocusRequester = remember { FocusRequester() },
) {
LaunchedEffect(Unit) { firstFocus.tryRequestFocus() }
Column(modifier = modifier) {
Text(
text = "Add row for ${library.name}",
)
LazyColumn(
contentPadding = PaddingValues(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
modifier =
modifier
.fillMaxHeight()
.focusRestorer(firstFocus),
) {
itemsIndexed(LibraryRowType.entries) { index, rowType ->
ListItem(
selected = false,
headlineContent = {
Text(
text = stringResource(rowType.stringId),
)
},
onClick = { onClick.invoke(rowType) },
modifier =
Modifier
.fillMaxWidth()
.ifElse(index == 0, Modifier.focusRequester(firstFocus)),
)
}
}
}
}
enum class LibraryRowType(
@param:StringRes val stringId: Int,
) {
RECENTLY_ADDED(R.string.recently_added),
RECENTLY_RELEASED(R.string.recently_released),
GENRES(R.string.genres),
}

View file

@ -4,7 +4,6 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box 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.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
@ -31,6 +30,7 @@ import androidx.tv.material3.MaterialTheme
import androidx.tv.material3.Text import androidx.tv.material3.Text
import androidx.tv.material3.surfaceColorAtElevation import androidx.tv.material3.surfaceColorAtElevation
import com.github.damontecres.wholphin.R import com.github.damontecres.wholphin.R
import com.github.damontecres.wholphin.data.model.HomeRowConfig
import com.github.damontecres.wholphin.data.model.HomeRowConfigDisplay import com.github.damontecres.wholphin.data.model.HomeRowConfigDisplay
import com.github.damontecres.wholphin.ui.FontAwesome import com.github.damontecres.wholphin.ui.FontAwesome
import com.github.damontecres.wholphin.ui.components.Button import com.github.damontecres.wholphin.ui.components.Button
@ -52,18 +52,18 @@ fun HomePageRowList(
) { ) {
Column(modifier = modifier) { Column(modifier = modifier) {
LazyColumn( LazyColumn(
contentPadding = PaddingValues(8.dp),
verticalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp),
modifier = modifier =
modifier modifier
.fillMaxHeight() .fillMaxHeight()
.focusRestorer(firstFocus), .focusRestorer(firstFocus),
) { ) {
itemsIndexed(state.rows) { index, row -> itemsIndexed(state.rows, key = { _, row -> row.config.id }) { index, row ->
HomeRowConfigContent( HomeRowConfigContent(
config = row, config = row,
moveUpAllowed = index > 0, moveUpAllowed = index > 0,
moveDownAllowed = index != state.rows.lastIndex, moveDownAllowed = index != state.rows.lastIndex,
deleteAllowed = row.config !is HomeRowConfig.ContinueWatching,
onClickMove = { onClickMove.invoke(it, index) }, onClickMove = { onClickMove.invoke(it, index) },
onClickDelete = { onClickDelete.invoke(index) }, onClickDelete = { onClickDelete.invoke(index) },
modifier = modifier =
@ -91,6 +91,7 @@ fun HomeRowConfigContent(
config: HomeRowConfigDisplay, config: HomeRowConfigDisplay,
moveUpAllowed: Boolean, moveUpAllowed: Boolean,
moveDownAllowed: Boolean, moveDownAllowed: Boolean,
deleteAllowed: Boolean,
onClickMove: (MoveDirection) -> Unit, onClickMove: (MoveDirection) -> Unit,
onClickDelete: () -> Unit, onClickDelete: () -> Unit,
modifier: Modifier, modifier: Modifier,
@ -108,14 +109,17 @@ fun HomeRowConfigContent(
.background( .background(
color = MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp), color = MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp),
shape = RoundedCornerShape(8.dp), shape = RoundedCornerShape(8.dp),
).padding(8.dp), ),
) { ) {
Text( Text(
text = config.title, text = config.title,
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.titleMedium,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(1f), modifier =
Modifier
.weight(1f)
.padding(start = 4.dp),
) )
Row( Row(
horizontalArrangement = Arrangement.spacedBy(4.dp), horizontalArrangement = Arrangement.spacedBy(4.dp),
@ -141,7 +145,7 @@ fun HomeRowConfigContent(
} }
Button( Button(
onClick = onClickDelete, onClick = onClickDelete,
enabled = true, enabled = deleteAllowed,
) { ) {
Icon( Icon(
imageVector = Icons.Default.Delete, imageVector = Icons.Default.Delete,

View file

@ -1,15 +1,18 @@
package com.github.damontecres.wholphin.ui.main.settings package com.github.damontecres.wholphin.ui.main.settings
import android.content.Context import android.content.Context
import androidx.activity.compose.BackHandler
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -96,7 +99,11 @@ class HomePageSettingsViewModel
?: context.getString(R.string.recently_added) ?: context.getString(R.string.recently_added)
HomeRowConfigDisplay( HomeRowConfigDisplay(
title, title,
HomeRowConfig.RecentlyAdded(id, HomeRowViewOptions()), HomeRowConfig.RecentlyAdded(
UUID.randomUUID(),
id,
HomeRowViewOptions(),
),
) )
} }
val rowConfig = val rowConfig =
@ -104,6 +111,7 @@ class HomePageSettingsViewModel
HomeRowConfigDisplay( HomeRowConfigDisplay(
context.getString(R.string.continue_watching), context.getString(R.string.continue_watching),
HomeRowConfig.ContinueWatching( HomeRowConfig.ContinueWatching(
UUID.randomUUID(),
prefs.combineContinueNext, prefs.combineContinueNext,
HomeRowViewOptions(), HomeRowViewOptions(),
), ),
@ -274,6 +282,66 @@ class HomePageSettingsViewModel
) )
} }
} }
fun addRow(
library: Library,
rowType: LibraryRowType,
) {
viewModelScope.launchIO {
val newRow =
when (rowType) {
LibraryRowType.RECENTLY_ADDED -> {
val title =
library.name.let { context.getString(R.string.recently_added_in, it) }
HomeRowConfigDisplay(
title,
HomeRowConfig.RecentlyAdded(
UUID.randomUUID(),
library.itemId,
HomeRowViewOptions(),
),
)
}
LibraryRowType.RECENTLY_RELEASED -> {
val title =
library.name.let {
context.getString(
R.string.recently_released_in,
it,
)
}
HomeRowConfigDisplay(
title,
HomeRowConfig.RecentlyReleased(
UUID.randomUUID(),
library.itemId,
HomeRowViewOptions(),
),
)
}
LibraryRowType.GENRES -> {
val title = library.name.let { context.getString(R.string.genres_in, it) }
HomeRowConfigDisplay(
title,
HomeRowConfig.Genres(
UUID.randomUUID(),
library.itemId,
HomeRowViewOptions(),
),
)
}
}
_state.update {
it.copy(
loading = LoadingState.Loading,
rows = it.rows.toMutableList().apply { add(newRow) },
)
}
fetchRowData()
}
}
} }
data class HomePageSettingsState( data class HomePageSettingsState(
@ -293,6 +361,7 @@ data class HomePageSettingsState(
} }
} }
@Immutable
data class Library( data class Library(
val itemId: UUID, val itemId: UUID,
val name: String, val name: String,
@ -309,10 +378,66 @@ fun HomePageSettings(
val state by viewModel.state.collectAsState() val state by viewModel.state.collectAsState()
val listState = rememberLazyListState() val listState = rememberLazyListState()
var destination by remember { mutableStateOf<HomePageSettingsDestination>(HomePageSettingsDestination.RowList) } var destination by remember { mutableStateOf<HomePageSettingsDestination>(HomePageSettingsDestination.RowList) }
BackHandler(destination is HomePageSettingsDestination.ChooseRowType) {
destination = HomePageSettingsDestination.ChooseLibrary
}
BackHandler(destination is HomePageSettingsDestination.ChooseLibrary) {
destination = HomePageSettingsDestination.RowList
}
BackHandler(destination is HomePageSettingsDestination.RowSettings) {
destination = HomePageSettingsDestination.RowList
}
Row( Row(
horizontalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp),
modifier = modifier, modifier = modifier,
) { ) {
Box(
modifier =
Modifier
.width(settingsWidth)
.fillMaxHeight()
.background(color = MaterialTheme.colorScheme.surface),
) {
val destModifier =
Modifier
.fillMaxSize()
.padding(4.dp)
when (val dest = destination) {
HomePageSettingsDestination.RowList -> {
HomePageRowList(
state = state,
onClickAdd = { destination = HomePageSettingsDestination.ChooseLibrary },
onClickMove = viewModel::moveRow,
onClickDelete = viewModel::deleteRow,
modifier = destModifier,
)
}
is HomePageSettingsDestination.ChooseLibrary -> {
HomePageLibraryList(
libraries = state.libraries,
onClick = { destination = HomePageSettingsDestination.ChooseRowType(it) },
modifier = destModifier,
)
}
is HomePageSettingsDestination.ChooseRowType -> {
HomePageLibraryRowTypeList(
library = dest.library,
onClick = {
viewModel.addRow(dest.library, it)
destination = HomePageSettingsDestination.RowList
},
modifier = destModifier,
)
}
is HomePageSettingsDestination.RowSettings -> {
TODO()
}
}
}
HomePageContent( HomePageContent(
loadingState = state.loading, loadingState = state.loading,
homeRows = state.rowData, homeRows = state.rowData,
@ -322,38 +447,10 @@ fun HomePageSettings(
showClock = false, showClock = false,
onUpdateBackdrop = viewModel::updateBackdrop, onUpdateBackdrop = viewModel::updateBackdrop,
listState = listState, listState = listState,
modifier = Modifier.fillMaxHeight().weight(1f),
)
Box(
modifier = modifier =
Modifier Modifier
.width(settingsWidth)
.fillMaxHeight() .fillMaxHeight()
.background(color = MaterialTheme.colorScheme.surface), .weight(1f),
) { )
when (destination) {
HomePageSettingsDestination.RowList -> {
HomePageRowList(
state = state,
onClickAdd = { destination = HomePageSettingsDestination.ChooseLibrary },
onClickMove = viewModel::moveRow,
onClickDelete = viewModel::deleteRow,
modifier = Modifier.fillMaxSize(),
)
}
is HomePageSettingsDestination.ChooseLibrary -> {
TODO()
}
is HomePageSettingsDestination.ChooseRow -> {
TODO()
}
is HomePageSettingsDestination.RowSettings -> {
TODO()
}
}
}
} }
} }

View file

@ -7,7 +7,7 @@ sealed interface HomePageSettingsDestination {
data object ChooseLibrary : HomePageSettingsDestination data object ChooseLibrary : HomePageSettingsDestination
data class ChooseRow( data class ChooseRowType(
val library: Library, val library: Library,
) : HomePageSettingsDestination ) : HomePageSettingsDestination

View file

@ -464,6 +464,7 @@
<string name="upgrade_mpv_toast">MPV is now the default player except for HDR.\nYou can change this in settings.</string> <string name="upgrade_mpv_toast">MPV is now the default player except for HDR.\nYou can change this in settings.</string>
<string name="add_row">Add row</string> <string name="add_row">Add row</string>
<string name="genres_in">Genres in %1$s</string> <string name="genres_in">Genres in %1$s</string>
<string name="recently_released_in">Recently released in %1$s</string>
<string-array name="theme_song_volume"> <string-array name="theme_song_volume">
<item>Disabled</item> <item>Disabled</item>