mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Barebones sorting
This commit is contained in:
parent
b0b791f325
commit
25859a05f7
3 changed files with 253 additions and 91 deletions
|
|
@ -0,0 +1,117 @@
|
|||
package com.github.damontecres.dolphin.ui.components
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.tv.material3.Button
|
||||
import androidx.tv.material3.MaterialTheme
|
||||
import androidx.tv.material3.Text
|
||||
import com.github.damontecres.dolphin.R
|
||||
import com.github.damontecres.dolphin.ui.FontAwesome
|
||||
import com.github.damontecres.dolphin.ui.data.SortAndDirection
|
||||
import com.github.damontecres.dolphin.ui.data.flip
|
||||
import org.jellyfin.sdk.model.api.ItemSortBy
|
||||
import org.jellyfin.sdk.model.api.SortOrder
|
||||
|
||||
@Composable
|
||||
fun SortByButton(
|
||||
sortOptions: List<ItemSortBy>,
|
||||
current: SortAndDirection,
|
||||
onSortChange: (SortAndDirection) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val currentSort = current.sort
|
||||
val currentDirection = current.direction
|
||||
var sortByDropDown by remember { mutableStateOf(false) }
|
||||
val context = LocalContext.current
|
||||
|
||||
Box(modifier = modifier) {
|
||||
Button(
|
||||
onClick = { sortByDropDown = true },
|
||||
onLongClick = {
|
||||
onSortChange.invoke(current.flip())
|
||||
},
|
||||
) {
|
||||
Text(
|
||||
text =
|
||||
buildAnnotatedString {
|
||||
withStyle(SpanStyle(fontFamily = FontAwesome)) {
|
||||
append(
|
||||
stringResource(
|
||||
if (currentDirection == SortOrder.ASCENDING) {
|
||||
R.string.fa_caret_up
|
||||
} else {
|
||||
R.string.fa_caret_down
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
||||
append(" ")
|
||||
append(currentSort.name) // TODO names
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// TODO switch to dialog?
|
||||
DropdownMenu(
|
||||
expanded = sortByDropDown,
|
||||
onDismissRequest = { sortByDropDown = false },
|
||||
) {
|
||||
sortOptions
|
||||
.sortedBy { it.name }
|
||||
.forEach { sortOption ->
|
||||
DropdownMenuItem(
|
||||
leadingIcon = {
|
||||
if (sortOption == currentSort) {
|
||||
if (currentDirection == SortOrder.ASCENDING) {
|
||||
Text(
|
||||
text = stringResource(R.string.fa_caret_up),
|
||||
color = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||
fontFamily = FontAwesome,
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = stringResource(R.string.fa_caret_down),
|
||||
color = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||
fontFamily = FontAwesome,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
text = sortOption.name,
|
||||
color = MaterialTheme.colorScheme.onSecondaryContainer,
|
||||
)
|
||||
},
|
||||
onClick = {
|
||||
sortByDropDown = false
|
||||
val newDirection =
|
||||
if (currentSort == sortOption) {
|
||||
currentDirection.flip()
|
||||
} else {
|
||||
currentDirection
|
||||
}
|
||||
onSortChange.invoke(
|
||||
SortAndDirection(
|
||||
sortOption,
|
||||
newDirection,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package com.github.damontecres.dolphin.ui.data
|
||||
|
||||
import org.jellyfin.sdk.model.api.ItemSortBy
|
||||
import org.jellyfin.sdk.model.api.SortOrder
|
||||
|
||||
data class SortAndDirection(
|
||||
val sort: ItemSortBy,
|
||||
val direction: SortOrder,
|
||||
) {
|
||||
fun flip() = copy(direction = direction.flip())
|
||||
}
|
||||
|
||||
fun SortOrder.flip() = if (this == SortOrder.ASCENDING) SortOrder.DESCENDING else SortOrder.ASCENDING
|
||||
|
||||
val MovieSortOptions =
|
||||
listOf(
|
||||
ItemSortBy.SORT_NAME,
|
||||
ItemSortBy.PRODUCTION_YEAR,
|
||||
ItemSortBy.COMMUNITY_RATING,
|
||||
ItemSortBy.DATE_CREATED,
|
||||
ItemSortBy.DATE_PLAYED,
|
||||
ItemSortBy.PLAY_COUNT,
|
||||
ItemSortBy.STUDIO,
|
||||
ItemSortBy.OFFICIAL_RATING,
|
||||
ItemSortBy.RANDOM,
|
||||
)
|
||||
|
||||
val SeriesSortOptions =
|
||||
listOf(
|
||||
ItemSortBy.SORT_NAME,
|
||||
ItemSortBy.PREMIERE_DATE,
|
||||
ItemSortBy.COMMUNITY_RATING,
|
||||
ItemSortBy.DATE_CREATED,
|
||||
ItemSortBy.DATE_LAST_CONTENT_ADDED,
|
||||
ItemSortBy.DATE_PLAYED,
|
||||
ItemSortBy.STUDIO,
|
||||
ItemSortBy.OFFICIAL_RATING,
|
||||
ItemSortBy.RANDOM,
|
||||
)
|
||||
|
||||
val VideoSortOptions =
|
||||
listOf(
|
||||
ItemSortBy.SORT_NAME,
|
||||
ItemSortBy.DATE_CREATED,
|
||||
ItemSortBy.DATE_PLAYED,
|
||||
ItemSortBy.RANDOM,
|
||||
)
|
||||
|
|
@ -21,9 +21,15 @@ import androidx.tv.material3.Text
|
|||
import com.github.damontecres.dolphin.data.model.BaseItem
|
||||
import com.github.damontecres.dolphin.data.model.Library
|
||||
import com.github.damontecres.dolphin.preferences.UserPreferences
|
||||
import com.github.damontecres.dolphin.ui.DefaultItemFields
|
||||
import com.github.damontecres.dolphin.ui.OneTimeLaunchedEffect
|
||||
import com.github.damontecres.dolphin.ui.components.ErrorMessage
|
||||
import com.github.damontecres.dolphin.ui.components.LoadingPage
|
||||
import com.github.damontecres.dolphin.ui.components.SortByButton
|
||||
import com.github.damontecres.dolphin.ui.data.MovieSortOptions
|
||||
import com.github.damontecres.dolphin.ui.data.SeriesSortOptions
|
||||
import com.github.damontecres.dolphin.ui.data.SortAndDirection
|
||||
import com.github.damontecres.dolphin.ui.data.VideoSortOptions
|
||||
import com.github.damontecres.dolphin.ui.nav.Destination
|
||||
import com.github.damontecres.dolphin.ui.nav.NavigationManager
|
||||
import com.github.damontecres.dolphin.ui.tryRequestFocus
|
||||
|
|
@ -40,7 +46,6 @@ import org.jellyfin.sdk.api.client.ApiClient
|
|||
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||
import org.jellyfin.sdk.model.api.CollectionType
|
||||
import org.jellyfin.sdk.model.api.ImageType
|
||||
import org.jellyfin.sdk.model.api.ItemFields
|
||||
import org.jellyfin.sdk.model.api.ItemSortBy
|
||||
import org.jellyfin.sdk.model.api.SortOrder
|
||||
import org.jellyfin.sdk.model.api.request.GetItemsRequest
|
||||
|
|
@ -54,11 +59,13 @@ class CollectionFolderViewModel
|
|||
api: ApiClient,
|
||||
) : ItemViewModel<Library>(api) {
|
||||
val loading = MutableLiveData<LoadingState>(LoadingState.Loading)
|
||||
val pager = MutableLiveData<ApiRequestPager<GetItemsRequest>?>()
|
||||
val pager = MutableLiveData<List<BaseItem?>>(listOf())
|
||||
val sortAndDirection = MutableLiveData<SortAndDirection>()
|
||||
|
||||
fun init(
|
||||
itemId: UUID,
|
||||
potential: BaseItem?,
|
||||
sortAndDirection: SortAndDirection,
|
||||
): Job =
|
||||
viewModelScope.launch(
|
||||
LoadingExceptionHandler(
|
||||
|
|
@ -67,53 +74,57 @@ class CollectionFolderViewModel
|
|||
) + Dispatchers.IO,
|
||||
) {
|
||||
fetchItem(itemId, potential)
|
||||
setup()
|
||||
loadResults(sortAndDirection)
|
||||
}
|
||||
|
||||
private suspend fun setup() =
|
||||
withContext(Dispatchers.IO) {
|
||||
if (!pager.isInitialized) {
|
||||
item.value?.let { item ->
|
||||
val includeItemTypes =
|
||||
when (item.data.collectionType) {
|
||||
CollectionType.UNKNOWN -> TODO()
|
||||
CollectionType.MOVIES -> listOf(BaseItemKind.MOVIE)
|
||||
CollectionType.TVSHOWS -> listOf(BaseItemKind.SERIES)
|
||||
CollectionType.MUSIC -> TODO()
|
||||
CollectionType.MUSICVIDEOS -> TODO()
|
||||
CollectionType.TRAILERS -> TODO()
|
||||
CollectionType.HOMEVIDEOS -> listOf(BaseItemKind.VIDEO)
|
||||
CollectionType.BOXSETS -> TODO()
|
||||
CollectionType.BOOKS -> TODO()
|
||||
CollectionType.PHOTOS -> TODO()
|
||||
CollectionType.LIVETV -> TODO()
|
||||
CollectionType.PLAYLISTS -> TODO()
|
||||
CollectionType.FOLDERS -> TODO()
|
||||
null -> TODO()
|
||||
}
|
||||
val request =
|
||||
GetItemsRequest(
|
||||
parentId = item.id,
|
||||
isSeries = true,
|
||||
mediaTypes = null,
|
||||
// recursive = true,
|
||||
enableImageTypes = listOf(ImageType.PRIMARY, ImageType.THUMB),
|
||||
includeItemTypes = includeItemTypes,
|
||||
sortBy = listOf(ItemSortBy.SORT_NAME),
|
||||
sortOrder = listOf(SortOrder.ASCENDING),
|
||||
fields = listOf(ItemFields.PRIMARY_IMAGE_ASPECT_RATIO),
|
||||
)
|
||||
val newPager =
|
||||
ApiRequestPager(api, request, GetItemsRequestHandler, viewModelScope)
|
||||
newPager.init()
|
||||
newPager.getBlocking(0)
|
||||
withContext(Dispatchers.Main) {
|
||||
pager.value = newPager
|
||||
loading.value = LoadingState.Success
|
||||
fun loadResults(sortAndDirection: SortAndDirection) {
|
||||
item.value?.let { item ->
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
withContext(Dispatchers.Main) {
|
||||
pager.value = listOf()
|
||||
loading.value = LoadingState.Loading
|
||||
this@CollectionFolderViewModel.sortAndDirection.value = sortAndDirection
|
||||
}
|
||||
val includeItemTypes =
|
||||
when (item.data.collectionType) {
|
||||
CollectionType.UNKNOWN -> TODO()
|
||||
CollectionType.MOVIES -> listOf(BaseItemKind.MOVIE)
|
||||
CollectionType.TVSHOWS -> listOf(BaseItemKind.SERIES)
|
||||
CollectionType.MUSIC -> TODO()
|
||||
CollectionType.MUSICVIDEOS -> TODO()
|
||||
CollectionType.TRAILERS -> TODO()
|
||||
CollectionType.HOMEVIDEOS -> listOf(BaseItemKind.VIDEO)
|
||||
CollectionType.BOXSETS -> TODO()
|
||||
CollectionType.BOOKS -> TODO()
|
||||
CollectionType.PHOTOS -> TODO()
|
||||
CollectionType.LIVETV -> TODO()
|
||||
CollectionType.PLAYLISTS -> TODO()
|
||||
CollectionType.FOLDERS -> TODO()
|
||||
null -> listOf()
|
||||
}
|
||||
val request =
|
||||
GetItemsRequest(
|
||||
parentId = item.id,
|
||||
isSeries = true,
|
||||
mediaTypes = null,
|
||||
// recursive = true,
|
||||
enableImageTypes = listOf(ImageType.PRIMARY, ImageType.THUMB),
|
||||
includeItemTypes = includeItemTypes,
|
||||
sortBy = listOf(sortAndDirection.sort),
|
||||
sortOrder = listOf(sortAndDirection.direction),
|
||||
fields = DefaultItemFields,
|
||||
)
|
||||
val newPager =
|
||||
ApiRequestPager(api, request, GetItemsRequestHandler, viewModelScope)
|
||||
newPager.init()
|
||||
newPager.getBlocking(0)
|
||||
withContext(Dispatchers.Main) {
|
||||
pager.value = newPager
|
||||
loading.value = LoadingState.Success
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -123,10 +134,16 @@ fun CollectionFolderDetails(
|
|||
destination: Destination.MediaItem,
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: CollectionFolderViewModel = hiltViewModel(),
|
||||
initialSortAndDirection: SortAndDirection =
|
||||
SortAndDirection(
|
||||
ItemSortBy.SORT_NAME,
|
||||
SortOrder.ASCENDING,
|
||||
),
|
||||
) {
|
||||
OneTimeLaunchedEffect {
|
||||
viewModel.init(destination.itemId, destination.item)
|
||||
viewModel.init(destination.itemId, destination.item, initialSortAndDirection)
|
||||
}
|
||||
val sortAndDirection by viewModel.sortAndDirection.observeAsState(initialSortAndDirection)
|
||||
val loading by viewModel.loading.observeAsState(LoadingState.Loading)
|
||||
val item by viewModel.item.observeAsState()
|
||||
val library by viewModel.model.observeAsState()
|
||||
|
|
@ -137,67 +154,42 @@ fun CollectionFolderDetails(
|
|||
LoadingState.Loading -> LoadingPage()
|
||||
LoadingState.Success -> {
|
||||
pager?.let { pager ->
|
||||
when (library!!.collectionType) {
|
||||
CollectionType.UNKNOWN -> TODO()
|
||||
|
||||
// TODO?
|
||||
CollectionType.MOVIES ->
|
||||
TVShowCollectionDetails(
|
||||
preferences,
|
||||
navigationManager,
|
||||
library!!,
|
||||
item!!,
|
||||
pager,
|
||||
modifier,
|
||||
)
|
||||
|
||||
CollectionType.TVSHOWS -> {
|
||||
TVShowCollectionDetails(
|
||||
preferences,
|
||||
navigationManager,
|
||||
library!!,
|
||||
item!!,
|
||||
pager,
|
||||
modifier,
|
||||
)
|
||||
}
|
||||
|
||||
// TODO?
|
||||
CollectionType.HOMEVIDEOS ->
|
||||
TVShowCollectionDetails(
|
||||
preferences,
|
||||
navigationManager,
|
||||
library!!,
|
||||
item!!,
|
||||
pager,
|
||||
modifier,
|
||||
)
|
||||
|
||||
CollectionType.MUSIC -> TODO()
|
||||
CollectionType.MUSICVIDEOS -> TODO()
|
||||
CollectionType.TRAILERS -> TODO()
|
||||
CollectionType.BOXSETS -> TODO()
|
||||
CollectionType.BOOKS -> TODO()
|
||||
CollectionType.PHOTOS -> TODO()
|
||||
CollectionType.LIVETV -> TODO()
|
||||
CollectionType.PLAYLISTS -> TODO()
|
||||
CollectionType.FOLDERS -> TODO()
|
||||
}
|
||||
CollectionDetails(
|
||||
preferences,
|
||||
navigationManager,
|
||||
library!!,
|
||||
item!!,
|
||||
pager,
|
||||
sortAndDirection = sortAndDirection,
|
||||
modifier = modifier,
|
||||
onSortChange = {
|
||||
viewModel.loadResults(it)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TVShowCollectionDetails(
|
||||
fun CollectionDetails(
|
||||
preferences: UserPreferences,
|
||||
navigationManager: NavigationManager,
|
||||
library: Library,
|
||||
item: BaseItem,
|
||||
pager: List<BaseItem?>,
|
||||
sortAndDirection: SortAndDirection,
|
||||
onSortChange: (SortAndDirection) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val title = library.name ?: item.data.name ?: item.data.collectionType?.name ?: "Collection"
|
||||
val sortOptions =
|
||||
when (item.data.collectionType) {
|
||||
CollectionType.MOVIES -> MovieSortOptions
|
||||
CollectionType.TVSHOWS -> SeriesSortOptions
|
||||
CollectionType.HOMEVIDEOS -> VideoSortOptions
|
||||
else -> listOf(ItemSortBy.SORT_NAME, ItemSortBy.DATE_CREATED, ItemSortBy.RANDOM)
|
||||
}
|
||||
|
||||
val gridFocusRequester = remember { FocusRequester() }
|
||||
LaunchedEffect(Unit) { gridFocusRequester.tryRequestFocus() }
|
||||
|
|
@ -212,6 +204,12 @@ fun TVShowCollectionDetails(
|
|||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
SortByButton(
|
||||
sortOptions = sortOptions,
|
||||
current = sortAndDirection,
|
||||
onSortChange = onSortChange,
|
||||
modifier = Modifier,
|
||||
)
|
||||
CardGrid(
|
||||
pager = pager,
|
||||
itemOnClick = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue