From 5dcdde36c30fcf4068ade5914cb6e252eff449d7 Mon Sep 17 00:00:00 2001 From: Damontecres Date: Fri, 23 Jan 2026 12:06:07 -0500 Subject: [PATCH] Handle adding rows & back handling --- .../wholphin/data/model/HomeRowConfig.kt | 6 +- .../ui/main/settings/HomePageLibraryList.kt | 54 ++++++ .../settings/HomePageLibraryRowTypeList.kt | 71 ++++++++ .../ui/main/settings/HomePageRowList.kt | 16 +- .../ui/main/settings/HomePageSettings.kt | 159 ++++++++++++++---- .../settings/HomePageSettingsDestination.kt | 2 +- app/src/main/res/values/strings.xml | 1 + 7 files changed, 270 insertions(+), 39 deletions(-) create mode 100644 app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomePageLibraryList.kt create mode 100644 app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomePageLibraryRowTypeList.kt diff --git a/app/src/main/java/com/github/damontecres/wholphin/data/model/HomeRowConfig.kt b/app/src/main/java/com/github/damontecres/wholphin/data/model/HomeRowConfig.kt index c88612cc..00dfd1aa 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/data/model/HomeRowConfig.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/data/model/HomeRowConfig.kt @@ -13,29 +13,33 @@ import java.util.UUID @Serializable sealed class HomeRowConfig { + abstract val id: UUID abstract val viewOptions: HomeRowViewOptions @Serializable data class ContinueWatching( + override val id: UUID, val combined: Boolean, override val viewOptions: HomeRowViewOptions, ) : HomeRowConfig() @Serializable data class RecentlyAdded( + override val id: UUID, val parentId: UUID, override val viewOptions: HomeRowViewOptions, ) : HomeRowConfig() @Serializable data class RecentlyReleased( + override val id: UUID, val parentId: UUID, override val viewOptions: HomeRowViewOptions, ) : HomeRowConfig() @Serializable data class Genres( - val genreId: UUID, + override val id: UUID, val parentId: UUID, override val viewOptions: HomeRowViewOptions, ) : HomeRowConfig() diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomePageLibraryList.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomePageLibraryList.kt new file mode 100644 index 00000000..73276da6 --- /dev/null +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomePageLibraryList.kt @@ -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, + 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)), + ) + } + } + } +} diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomePageLibraryRowTypeList.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomePageLibraryRowTypeList.kt new file mode 100644 index 00000000..0206d46a --- /dev/null +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomePageLibraryRowTypeList.kt @@ -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), +} diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomePageRowList.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomePageRowList.kt index b011b480..81342db9 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomePageRowList.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomePageRowList.kt @@ -4,7 +4,6 @@ import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth @@ -31,6 +30,7 @@ import androidx.tv.material3.MaterialTheme import androidx.tv.material3.Text import androidx.tv.material3.surfaceColorAtElevation 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.ui.FontAwesome import com.github.damontecres.wholphin.ui.components.Button @@ -52,18 +52,18 @@ fun HomePageRowList( ) { Column(modifier = modifier) { LazyColumn( - contentPadding = PaddingValues(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp), modifier = modifier .fillMaxHeight() .focusRestorer(firstFocus), ) { - itemsIndexed(state.rows) { index, row -> + itemsIndexed(state.rows, key = { _, row -> row.config.id }) { index, row -> HomeRowConfigContent( config = row, moveUpAllowed = index > 0, moveDownAllowed = index != state.rows.lastIndex, + deleteAllowed = row.config !is HomeRowConfig.ContinueWatching, onClickMove = { onClickMove.invoke(it, index) }, onClickDelete = { onClickDelete.invoke(index) }, modifier = @@ -91,6 +91,7 @@ fun HomeRowConfigContent( config: HomeRowConfigDisplay, moveUpAllowed: Boolean, moveDownAllowed: Boolean, + deleteAllowed: Boolean, onClickMove: (MoveDirection) -> Unit, onClickDelete: () -> Unit, modifier: Modifier, @@ -108,14 +109,17 @@ fun HomeRowConfigContent( .background( color = MaterialTheme.colorScheme.surfaceColorAtElevation(1.dp), shape = RoundedCornerShape(8.dp), - ).padding(8.dp), + ), ) { Text( text = config.title, color = MaterialTheme.colorScheme.onSurface, style = MaterialTheme.typography.titleMedium, overflow = TextOverflow.Ellipsis, - modifier = Modifier.weight(1f), + modifier = + Modifier + .weight(1f) + .padding(start = 4.dp), ) Row( horizontalArrangement = Arrangement.spacedBy(4.dp), @@ -141,7 +145,7 @@ fun HomeRowConfigContent( } Button( onClick = onClickDelete, - enabled = true, + enabled = deleteAllowed, ) { Icon( imageVector = Icons.Default.Delete, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomePageSettings.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomePageSettings.kt index 79979638..0070bd2d 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomePageSettings.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomePageSettings.kt @@ -1,15 +1,18 @@ package com.github.damontecres.wholphin.ui.main.settings import android.content.Context +import androidx.activity.compose.BackHandler import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.runtime.Composable +import androidx.compose.runtime.Immutable import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -96,7 +99,11 @@ class HomePageSettingsViewModel ?: context.getString(R.string.recently_added) HomeRowConfigDisplay( title, - HomeRowConfig.RecentlyAdded(id, HomeRowViewOptions()), + HomeRowConfig.RecentlyAdded( + UUID.randomUUID(), + id, + HomeRowViewOptions(), + ), ) } val rowConfig = @@ -104,6 +111,7 @@ class HomePageSettingsViewModel HomeRowConfigDisplay( context.getString(R.string.continue_watching), HomeRowConfig.ContinueWatching( + UUID.randomUUID(), prefs.combineContinueNext, 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( @@ -293,6 +361,7 @@ data class HomePageSettingsState( } } +@Immutable data class Library( val itemId: UUID, val name: String, @@ -309,10 +378,66 @@ fun HomePageSettings( val state by viewModel.state.collectAsState() val listState = rememberLazyListState() var destination by remember { mutableStateOf(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( horizontalArrangement = Arrangement.spacedBy(8.dp), 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( loadingState = state.loading, homeRows = state.rowData, @@ -322,38 +447,10 @@ fun HomePageSettings( showClock = false, onUpdateBackdrop = viewModel::updateBackdrop, listState = listState, - modifier = Modifier.fillMaxHeight().weight(1f), - ) - Box( modifier = Modifier - .width(settingsWidth) .fillMaxHeight() - .background(color = MaterialTheme.colorScheme.surface), - ) { - 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() - } - } - } + .weight(1f), + ) } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomePageSettingsDestination.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomePageSettingsDestination.kt index 3ebdc166..51d7a8e8 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomePageSettingsDestination.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/main/settings/HomePageSettingsDestination.kt @@ -7,7 +7,7 @@ sealed interface HomePageSettingsDestination { data object ChooseLibrary : HomePageSettingsDestination - data class ChooseRow( + data class ChooseRowType( val library: Library, ) : HomePageSettingsDestination diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8fa26d53..e9629e19 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -464,6 +464,7 @@ MPV is now the default player except for HDR.\nYou can change this in settings. Add row Genres in %1$s + Recently released in %1$s Disabled