mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-09 08:01:20 +02:00
Add a collection or playlist to home page (#915)
## Description Adds a basic way to add a collection or playlist as a row on the home page. There's a simple search dialog to find the collection or playlist. ### Related issues Related to #399 & #803 ### Testing Emulator testing ## Screenshots N/A ## AI or LLM usage None
This commit is contained in:
parent
9daf679f7d
commit
219e4b01db
6 changed files with 424 additions and 8 deletions
|
|
@ -0,0 +1,248 @@
|
||||||
|
package com.github.damontecres.wholphin.ui.detail.search
|
||||||
|
|
||||||
|
import androidx.activity.compose.BackHandler
|
||||||
|
import androidx.compose.foundation.focusGroup
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxHeight
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.focus.FocusDirection
|
||||||
|
import androidx.compose.ui.focus.FocusRequester
|
||||||
|
import androidx.compose.ui.focus.focusRequester
|
||||||
|
import androidx.compose.ui.focus.focusRestorer
|
||||||
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
|
import androidx.compose.ui.input.key.Key
|
||||||
|
import androidx.compose.ui.input.key.KeyEventType
|
||||||
|
import androidx.compose.ui.input.key.key
|
||||||
|
import androidx.compose.ui.input.key.onPreviewKeyEvent
|
||||||
|
import androidx.compose.ui.input.key.type
|
||||||
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
|
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.window.DialogProperties
|
||||||
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
|
import androidx.lifecycle.compose.LifecycleResumeEffect
|
||||||
|
import androidx.tv.material3.MaterialTheme
|
||||||
|
import androidx.tv.material3.Text
|
||||||
|
import com.github.damontecres.wholphin.R
|
||||||
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
|
import com.github.damontecres.wholphin.ui.Cards
|
||||||
|
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
||||||
|
import com.github.damontecres.wholphin.ui.cards.SeasonCard
|
||||||
|
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
||||||
|
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
|
import com.github.damontecres.wholphin.ui.components.SearchEditTextBox
|
||||||
|
import com.github.damontecres.wholphin.ui.components.VoiceSearchButton
|
||||||
|
import com.github.damontecres.wholphin.ui.main.SearchResult
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SearchForContent(
|
||||||
|
searchType: BaseItemKind,
|
||||||
|
onClick: (BaseItem) -> Unit,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
viewModel: SearchForViewModel = hiltViewModel(key = searchType.serialName),
|
||||||
|
) {
|
||||||
|
val focusManager = LocalFocusManager.current
|
||||||
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
|
val state by viewModel.state.collectAsState()
|
||||||
|
|
||||||
|
var query by rememberSaveable { mutableStateOf("") }
|
||||||
|
val searchFocusRequester = remember { FocusRequester() }
|
||||||
|
val focusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
|
var immediateSearchQuery by rememberSaveable { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
|
LifecycleResumeEffect(Unit) {
|
||||||
|
onPauseOrDispose {
|
||||||
|
viewModel.voiceInputManager.stopListening()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun triggerImmediateSearch(searchQuery: String) {
|
||||||
|
immediateSearchQuery = searchQuery
|
||||||
|
viewModel.search(searchType, searchQuery)
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(query) {
|
||||||
|
when {
|
||||||
|
immediateSearchQuery == query -> {
|
||||||
|
immediateSearchQuery = null
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
delay(750L)
|
||||||
|
viewModel.search(searchType, query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
modifier = modifier,
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
contentAlignment = Alignment.Center,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
) {
|
||||||
|
var isSearchActive by remember { mutableStateOf(false) }
|
||||||
|
var isTextFieldFocused by remember { mutableStateOf(false) }
|
||||||
|
val textFieldFocusRequester = remember { FocusRequester() }
|
||||||
|
|
||||||
|
BackHandler(isTextFieldFocused) {
|
||||||
|
when {
|
||||||
|
isSearchActive -> {
|
||||||
|
isSearchActive = false
|
||||||
|
keyboardController?.hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
focusManager.moveFocus(FocusDirection.Next)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.focusGroup()
|
||||||
|
.focusRestorer(textFieldFocusRequester)
|
||||||
|
.focusRequester(searchFocusRequester),
|
||||||
|
) {
|
||||||
|
VoiceSearchButton(
|
||||||
|
onSpeechResult = { spokenText ->
|
||||||
|
query = spokenText
|
||||||
|
triggerImmediateSearch(spokenText)
|
||||||
|
},
|
||||||
|
voiceInputManager = viewModel.voiceInputManager,
|
||||||
|
)
|
||||||
|
|
||||||
|
SearchEditTextBox(
|
||||||
|
value = query,
|
||||||
|
onValueChange = {
|
||||||
|
isSearchActive = true
|
||||||
|
query = it
|
||||||
|
},
|
||||||
|
onSearchClick = { triggerImmediateSearch(query) },
|
||||||
|
readOnly = !isSearchActive,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.focusRequester(textFieldFocusRequester)
|
||||||
|
.onFocusChanged { state ->
|
||||||
|
isTextFieldFocused = state.isFocused
|
||||||
|
if (!state.isFocused) isSearchActive = false
|
||||||
|
}.onPreviewKeyEvent { event ->
|
||||||
|
val isActivationKey =
|
||||||
|
event.key in listOf(Key.DirectionCenter, Key.Enter)
|
||||||
|
if (event.type == KeyEventType.KeyUp && isActivationKey && !isSearchActive) {
|
||||||
|
isSearchActive = true
|
||||||
|
keyboardController?.show()
|
||||||
|
true
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when (val st = state.results) {
|
||||||
|
is SearchResult.Error -> {
|
||||||
|
ErrorMessage("Error", st.ex)
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchResult.NoQuery -> {
|
||||||
|
// no-op
|
||||||
|
}
|
||||||
|
|
||||||
|
SearchResult.Searching -> {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.searching),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is SearchResult.SuccessSeerr -> {
|
||||||
|
Text(
|
||||||
|
text = "Not supported",
|
||||||
|
color = MaterialTheme.colorScheme.error,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
is SearchResult.Success -> {
|
||||||
|
if (st.items.isEmpty()) {
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.no_results),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val titleRes =
|
||||||
|
remember {
|
||||||
|
when (searchType) {
|
||||||
|
BaseItemKind.BOX_SET -> R.string.collections
|
||||||
|
BaseItemKind.PLAYLIST -> R.string.playlists
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ItemRow(
|
||||||
|
title = titleRes?.let { stringResource(it) } ?: "",
|
||||||
|
items = st.items,
|
||||||
|
onClickItem = { _, item -> onClick.invoke(item) },
|
||||||
|
onLongClickItem = { _, _ -> },
|
||||||
|
modifier = Modifier.focusRequester(focusRequester),
|
||||||
|
cardContent = { index, item, mod, onClick, onLongClick ->
|
||||||
|
SeasonCard(
|
||||||
|
item = item,
|
||||||
|
onClick = {
|
||||||
|
onClick.invoke()
|
||||||
|
},
|
||||||
|
onLongClick = onLongClick,
|
||||||
|
imageHeight = Cards.height2x3,
|
||||||
|
modifier = mod,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SearchForDialog(
|
||||||
|
onDismissRequest: () -> Unit,
|
||||||
|
searchType: BaseItemKind,
|
||||||
|
onClick: (BaseItem) -> Unit,
|
||||||
|
) {
|
||||||
|
BasicDialog(
|
||||||
|
onDismissRequest = onDismissRequest,
|
||||||
|
properties =
|
||||||
|
DialogProperties(
|
||||||
|
usePlatformDefaultWidth = false,
|
||||||
|
),
|
||||||
|
) {
|
||||||
|
SearchForContent(
|
||||||
|
searchType = searchType,
|
||||||
|
onClick = onClick,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.padding(8.dp)
|
||||||
|
.fillMaxWidth(.8f)
|
||||||
|
.fillMaxHeight(.66f),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
package com.github.damontecres.wholphin.ui.detail.search
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.github.damontecres.wholphin.data.ServerRepository
|
||||||
|
import com.github.damontecres.wholphin.services.NavigationManager
|
||||||
|
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||||
|
import com.github.damontecres.wholphin.ui.components.VoiceInputManager
|
||||||
|
import com.github.damontecres.wholphin.ui.launchIO
|
||||||
|
import com.github.damontecres.wholphin.ui.main.SearchResult
|
||||||
|
import com.github.damontecres.wholphin.util.ApiRequestPager
|
||||||
|
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import org.jellyfin.sdk.api.client.ApiClient
|
||||||
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
|
import org.jellyfin.sdk.model.api.request.GetItemsRequest
|
||||||
|
import timber.log.Timber
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class SearchForViewModel
|
||||||
|
@Inject
|
||||||
|
constructor(
|
||||||
|
private val api: ApiClient,
|
||||||
|
private val serverRepository: ServerRepository,
|
||||||
|
val navigationManager: NavigationManager,
|
||||||
|
val voiceInputManager: VoiceInputManager,
|
||||||
|
) : ViewModel() {
|
||||||
|
val state = MutableStateFlow(SearchForState())
|
||||||
|
|
||||||
|
init {
|
||||||
|
state.value = SearchForState()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun search(
|
||||||
|
searchType: BaseItemKind,
|
||||||
|
query: String,
|
||||||
|
) {
|
||||||
|
viewModelScope.launchIO {
|
||||||
|
if (state.value.query != query) {
|
||||||
|
if (query.isBlank()) {
|
||||||
|
state.update { SearchForState(query, SearchResult.NoQuery) }
|
||||||
|
return@launchIO
|
||||||
|
}
|
||||||
|
state.update { SearchForState(query, SearchResult.Searching) }
|
||||||
|
try {
|
||||||
|
val request =
|
||||||
|
GetItemsRequest(
|
||||||
|
userId = serverRepository.currentUser.value?.id,
|
||||||
|
searchTerm = query,
|
||||||
|
includeItemTypes = listOf(searchType),
|
||||||
|
recursive = true,
|
||||||
|
fields = SlimItemFields,
|
||||||
|
)
|
||||||
|
val pager = ApiRequestPager(api, request, GetItemsRequestHandler, viewModelScope).init()
|
||||||
|
state.update { SearchForState(query, SearchResult.Success(pager)) }
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Timber.e(ex)
|
||||||
|
state.update { SearchForState(query, SearchResult.Error(ex)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
data class SearchForState(
|
||||||
|
val query: String = "",
|
||||||
|
val results: SearchResult = SearchResult.NoQuery,
|
||||||
|
)
|
||||||
|
|
@ -89,6 +89,24 @@ fun getSupportedRowTypes(library: Library): List<LibraryRowType> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
library.collectionType == CollectionType.BOXSETS -> {
|
||||||
|
listOf(
|
||||||
|
LibraryRowType.RECENTLY_ADDED,
|
||||||
|
LibraryRowType.RECENTLY_RELEASED,
|
||||||
|
LibraryRowType.GENRES,
|
||||||
|
LibraryRowType.COLLECTION,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
library.collectionType == CollectionType.PLAYLISTS -> {
|
||||||
|
listOf(
|
||||||
|
LibraryRowType.RECENTLY_ADDED,
|
||||||
|
LibraryRowType.RECENTLY_RELEASED,
|
||||||
|
LibraryRowType.GENRES,
|
||||||
|
LibraryRowType.PLAYLIST,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
listOf(
|
listOf(
|
||||||
LibraryRowType.RECENTLY_ADDED,
|
LibraryRowType.RECENTLY_ADDED,
|
||||||
|
|
@ -109,4 +127,6 @@ enum class LibraryRowType(
|
||||||
TV_CHANNELS(R.string.channels),
|
TV_CHANNELS(R.string.channels),
|
||||||
TV_PROGRAMS(R.string.live_tv),
|
TV_PROGRAMS(R.string.live_tv),
|
||||||
RECENTLY_RECORDED(R.string.recently_recorded),
|
RECENTLY_RECORDED(R.string.recently_recorded),
|
||||||
|
COLLECTION(R.string.collections),
|
||||||
|
PLAYLIST(R.string.playlist),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -69,11 +69,17 @@ fun HomeSettingsAddRow(
|
||||||
TitleText(stringResource(R.string.more))
|
TitleText(stringResource(R.string.more))
|
||||||
HorizontalDivider()
|
HorizontalDivider()
|
||||||
}
|
}
|
||||||
item {
|
itemsIndexed(
|
||||||
|
listOf(
|
||||||
|
MetaRowType.FAVORITES,
|
||||||
|
MetaRowType.COLLECTION,
|
||||||
|
MetaRowType.PLAYLIST,
|
||||||
|
),
|
||||||
|
) { index, type ->
|
||||||
HomeSettingsListItem(
|
HomeSettingsListItem(
|
||||||
selected = false,
|
selected = false,
|
||||||
headlineText = stringResource(MetaRowType.FAVORITES.stringId),
|
headlineText = stringResource(type.stringId),
|
||||||
onClick = { onClickMeta.invoke(MetaRowType.FAVORITES) },
|
onClick = { onClickMeta.invoke(type) },
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -99,4 +105,6 @@ enum class MetaRowType(
|
||||||
COMBINED_CONTINUE_WATCHING(R.string.combine_continue_next),
|
COMBINED_CONTINUE_WATCHING(R.string.combine_continue_next),
|
||||||
FAVORITES(R.string.favorites),
|
FAVORITES(R.string.favorites),
|
||||||
DISCOVER(R.string.discover),
|
DISCOVER(R.string.discover),
|
||||||
|
COLLECTION(R.string.collection),
|
||||||
|
PLAYLIST(R.string.playlist),
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
|
||||||
import com.github.damontecres.wholphin.preferences.AppPreferences
|
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||||
import com.github.damontecres.wholphin.ui.components.ConfirmDialog
|
import com.github.damontecres.wholphin.ui.components.ConfirmDialog
|
||||||
import com.github.damontecres.wholphin.ui.data.RowColumn
|
import com.github.damontecres.wholphin.ui.data.RowColumn
|
||||||
|
import com.github.damontecres.wholphin.ui.detail.search.SearchForDialog
|
||||||
import com.github.damontecres.wholphin.ui.launchIO
|
import com.github.damontecres.wholphin.ui.launchIO
|
||||||
import com.github.damontecres.wholphin.ui.main.HomePageContent
|
import com.github.damontecres.wholphin.ui.main.HomePageContent
|
||||||
import com.github.damontecres.wholphin.ui.main.settings.HomeSettingsDestination.ChooseRowType
|
import com.github.damontecres.wholphin.ui.main.settings.HomeSettingsDestination.ChooseRowType
|
||||||
|
|
@ -43,6 +44,7 @@ import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
import com.github.damontecres.wholphin.util.HomeRowLoadingState
|
import com.github.damontecres.wholphin.util.HomeRowLoadingState
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
|
|
||||||
val settingsWidth = 360.dp
|
val settingsWidth = 360.dp
|
||||||
|
|
@ -56,6 +58,7 @@ fun HomeSettingsPage(
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
val backStack = rememberNavBackStack(HomeSettingsDestination.RowList)
|
val backStack = rememberNavBackStack(HomeSettingsDestination.RowList)
|
||||||
var showConfirmDialog by remember { mutableStateOf<ShowConfirm?>(null) }
|
var showConfirmDialog by remember { mutableStateOf<ShowConfirm?>(null) }
|
||||||
|
var searchForDialog by remember { mutableStateOf<BaseItemKind?>(null) }
|
||||||
|
|
||||||
val state by viewModel.state.collectAsState()
|
val state by viewModel.state.collectAsState()
|
||||||
var position by rememberPosition(0, 0)
|
var position by rememberPosition(0, 0)
|
||||||
|
|
@ -149,6 +152,14 @@ fun HomeSettingsPage(
|
||||||
MetaRowType.DISCOVER -> {
|
MetaRowType.DISCOVER -> {
|
||||||
backStack.add(HomeSettingsDestination.ChooseDiscover)
|
backStack.add(HomeSettingsDestination.ChooseDiscover)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MetaRowType.COLLECTION -> {
|
||||||
|
searchForDialog = BaseItemKind.BOX_SET
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaRowType.PLAYLIST -> {
|
||||||
|
searchForDialog = BaseItemKind.PLAYLIST
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
modifier = destModifier,
|
modifier = destModifier,
|
||||||
|
|
@ -159,7 +170,19 @@ fun HomeSettingsPage(
|
||||||
HomeLibraryRowTypeList(
|
HomeLibraryRowTypeList(
|
||||||
library = dest.library,
|
library = dest.library,
|
||||||
onClick = { type ->
|
onClick = { type ->
|
||||||
addRow { viewModel.addRow(dest.library, type) }
|
when (type) {
|
||||||
|
LibraryRowType.COLLECTION -> {
|
||||||
|
searchForDialog = BaseItemKind.BOX_SET
|
||||||
|
}
|
||||||
|
|
||||||
|
LibraryRowType.PLAYLIST -> {
|
||||||
|
searchForDialog = BaseItemKind.PLAYLIST
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> {
|
||||||
|
addRow { viewModel.addRow(dest.library, type) }
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
modifier = destModifier,
|
modifier = destModifier,
|
||||||
)
|
)
|
||||||
|
|
@ -298,6 +321,16 @@ fun HomeSettingsPage(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
searchForDialog?.let { searchType ->
|
||||||
|
SearchForDialog(
|
||||||
|
onDismissRequest = { searchForDialog = null },
|
||||||
|
searchType = searchType,
|
||||||
|
onClick = {
|
||||||
|
searchForDialog = null
|
||||||
|
addRow { viewModel.addRow(searchType, it) }
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class ShowConfirm(
|
data class ShowConfirm(
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,8 @@ import com.github.damontecres.wholphin.data.model.HomeRowConfig.NextUp
|
||||||
import com.github.damontecres.wholphin.data.model.HomeRowConfig.RecentlyAdded
|
import com.github.damontecres.wholphin.data.model.HomeRowConfig.RecentlyAdded
|
||||||
import com.github.damontecres.wholphin.data.model.HomeRowConfig.RecentlyReleased
|
import com.github.damontecres.wholphin.data.model.HomeRowConfig.RecentlyReleased
|
||||||
import com.github.damontecres.wholphin.data.model.HomeRowConfig.Suggestions
|
import com.github.damontecres.wholphin.data.model.HomeRowConfig.Suggestions
|
||||||
|
import com.github.damontecres.wholphin.data.model.HomeRowConfig.TvChannels
|
||||||
|
import com.github.damontecres.wholphin.data.model.HomeRowConfig.TvPrograms
|
||||||
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
|
import com.github.damontecres.wholphin.data.model.HomeRowViewOptions
|
||||||
import com.github.damontecres.wholphin.data.model.SUPPORTED_HOME_PAGE_SETTINGS_VERSION
|
import com.github.damontecres.wholphin.data.model.SUPPORTED_HOME_PAGE_SETTINGS_VERSION
|
||||||
import com.github.damontecres.wholphin.preferences.AppPreferences
|
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||||
|
|
@ -230,8 +232,11 @@ class HomeSettingsViewModel
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
MetaRowType.FAVORITES -> {
|
MetaRowType.FAVORITES,
|
||||||
throw IllegalArgumentException("Should use addRow(BaseItemKind) instead")
|
MetaRowType.COLLECTION,
|
||||||
|
MetaRowType.PLAYLIST,
|
||||||
|
-> {
|
||||||
|
throw IllegalArgumentException("Should use a different addRow() instead")
|
||||||
}
|
}
|
||||||
|
|
||||||
MetaRowType.DISCOVER -> {
|
MetaRowType.DISCOVER -> {
|
||||||
|
|
@ -305,7 +310,7 @@ class HomeSettingsViewModel
|
||||||
id = id,
|
id = id,
|
||||||
title = title,
|
title = title,
|
||||||
config =
|
config =
|
||||||
HomeRowConfig.TvChannels(
|
TvChannels(
|
||||||
viewOptions = HomeRowViewOptions.liveTvDefault,
|
viewOptions = HomeRowViewOptions.liveTvDefault,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
@ -316,7 +321,7 @@ class HomeSettingsViewModel
|
||||||
HomeRowConfigDisplay(
|
HomeRowConfigDisplay(
|
||||||
id = id,
|
id = id,
|
||||||
title = title,
|
title = title,
|
||||||
config = HomeRowConfig.TvPrograms(),
|
config = TvPrograms(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -328,6 +333,12 @@ class HomeSettingsViewModel
|
||||||
config = RecentlyAdded(library.itemId),
|
config = RecentlyAdded(library.itemId),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LibraryRowType.COLLECTION,
|
||||||
|
LibraryRowType.PLAYLIST,
|
||||||
|
-> {
|
||||||
|
throw IllegalArgumentException("Use different addRow")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
updateState {
|
updateState {
|
||||||
it.copy(
|
it.copy(
|
||||||
|
|
@ -357,6 +368,31 @@ class HomeSettingsViewModel
|
||||||
fetchRowData()
|
fetchRowData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun addRow(
|
||||||
|
type: BaseItemKind,
|
||||||
|
parent: BaseItem,
|
||||||
|
) = viewModelScope.launchIO {
|
||||||
|
Timber.v("Adding %s row for %s", type, parent.id)
|
||||||
|
val id = idCounter++
|
||||||
|
val newRow =
|
||||||
|
HomeRowConfigDisplay(
|
||||||
|
id = id,
|
||||||
|
title = parent.name ?: "",
|
||||||
|
config =
|
||||||
|
HomeRowConfig.ByParent(
|
||||||
|
parentId = parent.id,
|
||||||
|
recursive = true,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
updateState {
|
||||||
|
it.copy(
|
||||||
|
loading = LoadingState.Loading,
|
||||||
|
rows = it.rows.toMutableList().apply { add(newRow) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
fetchRowData()
|
||||||
|
}
|
||||||
|
|
||||||
fun updateViewOptions(
|
fun updateViewOptions(
|
||||||
rowId: Int,
|
rowId: Int,
|
||||||
viewOptions: HomeRowViewOptions,
|
viewOptions: HomeRowViewOptions,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue