mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Fix play/shuffle buttons on music library tabs (#1307)
## Description Fixes the play & shuffle buttons on music library tabs. They previously attempted to play the audio via the video playback method. This PR adds the ability to override the buttons and does so for music libraries. ### Related issues Fixes #1304 ### Testing Emulator, compared results against the web UI ## Screenshots N/A ## AI or LLM usage None
This commit is contained in:
parent
50174db22e
commit
3f0613f5f4
4 changed files with 233 additions and 89 deletions
|
|
@ -570,7 +570,44 @@ fun CollectionFolderGrid(
|
|||
itemId.toServerString(),
|
||||
initialFilter,
|
||||
recursive,
|
||||
onClickItem,
|
||||
GridClickActions(onClickItem = onClickItem),
|
||||
sortOptions,
|
||||
playEnabled,
|
||||
viewModelKey = viewModelKey,
|
||||
defaultViewOptions = defaultViewOptions,
|
||||
modifier = modifier,
|
||||
initialSortAndDirection = initialSortAndDirection,
|
||||
showTitle = showTitle,
|
||||
positionCallback = positionCallback,
|
||||
useSeriesForPrimary = useSeriesForPrimary,
|
||||
filterOptions = filterOptions,
|
||||
focusRequesterOnEmpty = focusRequesterOnEmpty,
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun CollectionFolderGrid(
|
||||
preferences: UserPreferences,
|
||||
itemId: UUID,
|
||||
initialFilter: CollectionFolderFilter,
|
||||
recursive: Boolean,
|
||||
actions: GridClickActions,
|
||||
sortOptions: List<ItemSortBy>,
|
||||
playEnabled: Boolean,
|
||||
defaultViewOptions: ViewOptions,
|
||||
modifier: Modifier = Modifier,
|
||||
viewModelKey: String? = itemId.toServerString(),
|
||||
initialSortAndDirection: SortAndDirection? = null,
|
||||
showTitle: Boolean = true,
|
||||
positionCallback: ((columns: Int, position: Int) -> Unit)? = null,
|
||||
useSeriesForPrimary: Boolean = true,
|
||||
filterOptions: List<ItemFilterBy<*>> = DefaultFilterOptions,
|
||||
focusRequesterOnEmpty: FocusRequester? = null,
|
||||
) = CollectionFolderGrid(
|
||||
preferences,
|
||||
itemId.toServerString(),
|
||||
initialFilter,
|
||||
recursive,
|
||||
actions,
|
||||
sortOptions,
|
||||
playEnabled,
|
||||
viewModelKey = viewModelKey,
|
||||
|
|
@ -590,7 +627,7 @@ fun CollectionFolderGrid(
|
|||
itemId: String,
|
||||
initialFilter: CollectionFolderFilter,
|
||||
recursive: Boolean,
|
||||
onClickItem: (Int, BaseItem) -> Unit,
|
||||
actions: GridClickActions,
|
||||
sortOptions: List<ItemSortBy>,
|
||||
playEnabled: Boolean,
|
||||
defaultViewOptions: ViewOptions,
|
||||
|
|
@ -630,6 +667,61 @@ fun CollectionFolderGrid(
|
|||
var showDeleteDialog by remember { mutableStateOf<PositionItem?>(null) }
|
||||
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
||||
|
||||
val gridActions =
|
||||
remember(actions) {
|
||||
GridClickActions(
|
||||
onClickItem = actions.onClickItem,
|
||||
onLongClickItem =
|
||||
actions.onLongClickItem ?: { position, item ->
|
||||
moreDialog.makePresent(PositionItem(position, item))
|
||||
},
|
||||
onClickPlayAll =
|
||||
actions.onClickPlayAll ?: { shuffle ->
|
||||
itemId.toUUIDOrNull()?.let {
|
||||
val destination =
|
||||
if (item?.type == BaseItemKind.PHOTO_ALBUM) {
|
||||
Destination.Slideshow(
|
||||
parentId = it,
|
||||
index = 0,
|
||||
filter = CollectionFolderFilter(filter = filter),
|
||||
sortAndDirection = sortAndDirection,
|
||||
recursive = true,
|
||||
startSlideshow = true,
|
||||
)
|
||||
} else {
|
||||
Destination.PlaybackList(
|
||||
itemId = it,
|
||||
startIndex = 0,
|
||||
shuffle = shuffle,
|
||||
recursive = recursive,
|
||||
sortAndDirection = sortAndDirection,
|
||||
filter = filter,
|
||||
)
|
||||
}
|
||||
viewModel.navigateTo(destination)
|
||||
}
|
||||
Unit
|
||||
},
|
||||
onClickPlayRemoteButton =
|
||||
actions.onClickPlayRemoteButton ?: { index, item ->
|
||||
val destination =
|
||||
if (item.type == BaseItemKind.PHOTO_ALBUM) {
|
||||
Destination.Slideshow(
|
||||
parentId = item.id,
|
||||
index = index,
|
||||
filter = CollectionFolderFilter(filter = filter),
|
||||
sortAndDirection = sortAndDirection,
|
||||
recursive = true,
|
||||
startSlideshow = true,
|
||||
)
|
||||
} else {
|
||||
Destination.Playback(item)
|
||||
}
|
||||
viewModel.navigateTo(destination)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
when (val state = loading) {
|
||||
DataLoadingState.Loading,
|
||||
DataLoadingState.Pending,
|
||||
|
|
@ -662,10 +754,8 @@ fun CollectionFolderGrid(
|
|||
sortAndDirection = sortAndDirection!!,
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
focusRequesterOnEmpty = focusRequesterOnEmpty,
|
||||
onClickItem = onClickItem,
|
||||
onLongClickItem = { position, item ->
|
||||
moreDialog.makePresent(PositionItem(position, item))
|
||||
},
|
||||
onClickItem = gridActions.onClickItem,
|
||||
onLongClickItem = gridActions.onLongClickItem!!,
|
||||
onSortChange = {
|
||||
viewModel.onSortChange(it, recursive, filter)
|
||||
},
|
||||
|
|
@ -689,47 +779,8 @@ fun CollectionFolderGrid(
|
|||
onSaveViewOptions = { viewModel.saveViewOptions(it) },
|
||||
onChangeBackdrop = viewModel::updateBackdrop,
|
||||
playEnabled = playEnabled,
|
||||
onClickPlay = { index, item ->
|
||||
val destination =
|
||||
if (item.type == BaseItemKind.PHOTO_ALBUM) {
|
||||
Destination.Slideshow(
|
||||
parentId = item.id,
|
||||
index = index,
|
||||
filter = CollectionFolderFilter(filter = filter),
|
||||
sortAndDirection = sortAndDirection,
|
||||
recursive = true,
|
||||
startSlideshow = true,
|
||||
)
|
||||
} else {
|
||||
Destination.Playback(item)
|
||||
}
|
||||
viewModel.navigateTo(destination)
|
||||
},
|
||||
onClickPlayAll = { shuffle ->
|
||||
itemId.toUUIDOrNull()?.let {
|
||||
val destination =
|
||||
if (item?.type == BaseItemKind.PHOTO_ALBUM) {
|
||||
Destination.Slideshow(
|
||||
parentId = it,
|
||||
index = 0,
|
||||
filter = CollectionFolderFilter(filter = filter),
|
||||
sortAndDirection = sortAndDirection,
|
||||
recursive = true,
|
||||
startSlideshow = true,
|
||||
)
|
||||
} else {
|
||||
Destination.PlaybackList(
|
||||
itemId = it,
|
||||
startIndex = 0,
|
||||
shuffle = shuffle,
|
||||
recursive = recursive,
|
||||
sortAndDirection = sortAndDirection,
|
||||
filter = filter,
|
||||
)
|
||||
}
|
||||
viewModel.navigateTo(destination)
|
||||
}
|
||||
},
|
||||
onClickPlay = gridActions.onClickPlayRemoteButton!!,
|
||||
onClickPlayAll = gridActions.onClickPlayAll!!,
|
||||
)
|
||||
|
||||
AnimatedVisibility(
|
||||
|
|
@ -782,7 +833,7 @@ fun CollectionFolderGrid(
|
|||
showDeleteDialog = PositionItem(position, item)
|
||||
},
|
||||
onClickGoTo = {
|
||||
onClickItem.invoke(position, it)
|
||||
gridActions.onClickItem.invoke(position, it)
|
||||
},
|
||||
onClickAddToQueue = {
|
||||
viewModel.addToQueue(it, 0)
|
||||
|
|
@ -1165,3 +1216,10 @@ fun GridTitle(
|
|||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = modifier.fillMaxWidth(),
|
||||
)
|
||||
|
||||
data class GridClickActions(
|
||||
val onClickItem: (Int, BaseItem) -> Unit,
|
||||
val onLongClickItem: ((Int, BaseItem) -> Unit)? = null,
|
||||
val onClickPlayAll: ((shuffle: Boolean) -> Unit)? = null,
|
||||
val onClickPlayRemoteButton: ((Int, BaseItem) -> Unit)? = null,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -23,14 +23,18 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.github.damontecres.wholphin.R
|
||||
import com.github.damontecres.wholphin.data.ServerRepository
|
||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||
import com.github.damontecres.wholphin.data.model.CollectionFolderFilter
|
||||
import com.github.damontecres.wholphin.data.model.GetItemsFilter
|
||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||
import com.github.damontecres.wholphin.services.BackdropService
|
||||
import com.github.damontecres.wholphin.services.MusicService
|
||||
import com.github.damontecres.wholphin.services.NavigationManager
|
||||
import com.github.damontecres.wholphin.ui.components.CollectionFolderGrid
|
||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||
import com.github.damontecres.wholphin.ui.components.GenreCardGrid
|
||||
import com.github.damontecres.wholphin.ui.components.GridClickActions
|
||||
import com.github.damontecres.wholphin.ui.components.RecommendedMusic
|
||||
import com.github.damontecres.wholphin.ui.components.TabRow
|
||||
import com.github.damontecres.wholphin.ui.components.ViewOptionsSquare
|
||||
|
|
@ -40,17 +44,36 @@ import com.github.damontecres.wholphin.ui.data.SongSortOptions
|
|||
import com.github.damontecres.wholphin.ui.launchDefault
|
||||
import com.github.damontecres.wholphin.ui.logTab
|
||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||
import com.github.damontecres.wholphin.ui.preferences.PreferencesViewModel
|
||||
import com.github.damontecres.wholphin.util.ApiRequestPager
|
||||
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
|
||||
import com.github.damontecres.wholphin.util.RememberTabManager
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedFactory
|
||||
import dagger.assisted.AssistedInject
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import org.jellyfin.sdk.api.client.ApiClient
|
||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||
import javax.inject.Inject
|
||||
import org.jellyfin.sdk.model.api.request.GetItemsRequest
|
||||
import java.util.UUID
|
||||
|
||||
@HiltViewModel
|
||||
@HiltViewModel(assistedFactory = CollectionFolderMusicViewModel.Factory::class)
|
||||
class CollectionFolderMusicViewModel
|
||||
@Inject
|
||||
@AssistedInject
|
||||
constructor(
|
||||
private val api: ApiClient,
|
||||
private val serverRepository: ServerRepository,
|
||||
private val musicService: MusicService,
|
||||
) : ViewModel() {
|
||||
private val navigationManager: NavigationManager,
|
||||
val backdropService: BackdropService,
|
||||
private val rememberTabManager: RememberTabManager,
|
||||
@Assisted private val itemId: UUID,
|
||||
) : ViewModel(),
|
||||
RememberTabManager by rememberTabManager {
|
||||
@AssistedFactory
|
||||
interface Factory {
|
||||
fun create(itemId: UUID): CollectionFolderMusicViewModel
|
||||
}
|
||||
|
||||
fun play(item: BaseItem) {
|
||||
if (item.type == BaseItemKind.AUDIO) {
|
||||
viewModelScope.launchDefault {
|
||||
|
|
@ -58,6 +81,44 @@ class CollectionFolderMusicViewModel
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onClick(
|
||||
index: Int,
|
||||
item: BaseItem,
|
||||
) {
|
||||
if (item.type == BaseItemKind.AUDIO) {
|
||||
viewModelScope.launchDefault {
|
||||
musicService.setQueue(listOf(item), false)
|
||||
}
|
||||
} else {
|
||||
navigationManager.navigateTo(item.destination())
|
||||
}
|
||||
}
|
||||
|
||||
fun onClickPlayAll(shuffle: Boolean) {
|
||||
viewModelScope.launchDefault {
|
||||
val request =
|
||||
GetItemsRequest(
|
||||
userId = serverRepository.currentUser.value?.id,
|
||||
parentId = itemId,
|
||||
includeItemTypes = listOf(BaseItemKind.AUDIO),
|
||||
recursive = true,
|
||||
)
|
||||
val pager = ApiRequestPager(api, request, GetItemsRequestHandler, viewModelScope).init()
|
||||
musicService.setQueue(pager, 0, shuffle)
|
||||
}
|
||||
}
|
||||
|
||||
fun onClickPlayRemoteButton(
|
||||
index: Int,
|
||||
item: BaseItem,
|
||||
) {
|
||||
if (item.type == BaseItemKind.AUDIO) {
|
||||
viewModelScope.launchDefault {
|
||||
musicService.setQueue(listOf(item), false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
|
@ -65,11 +126,13 @@ fun CollectionFolderMusic(
|
|||
preferences: UserPreferences,
|
||||
destination: Destination.MediaItem,
|
||||
modifier: Modifier = Modifier,
|
||||
viewModel: CollectionFolderMusicViewModel = hiltViewModel(),
|
||||
preferencesViewModel: PreferencesViewModel = hiltViewModel(),
|
||||
viewModel: CollectionFolderMusicViewModel =
|
||||
hiltViewModel<CollectionFolderMusicViewModel, CollectionFolderMusicViewModel.Factory>(
|
||||
creationCallback = { it.create(destination.itemId) },
|
||||
),
|
||||
) {
|
||||
val rememberedTabIndex =
|
||||
remember { preferencesViewModel.getRememberedTab(preferences, destination.itemId, 0) }
|
||||
remember { viewModel.getRememberedTab(preferences, destination.itemId, 0) }
|
||||
|
||||
val tabs =
|
||||
listOf(
|
||||
|
|
@ -88,12 +151,22 @@ fun CollectionFolderMusic(
|
|||
|
||||
LaunchedEffect(selectedTabIndex) {
|
||||
logTab("music", selectedTabIndex)
|
||||
preferencesViewModel.saveRememberedTab(preferences, destination.itemId, selectedTabIndex)
|
||||
preferencesViewModel.backdropService.clearBackdrop()
|
||||
viewModel.saveRememberedTab(preferences, destination.itemId, selectedTabIndex)
|
||||
viewModel.backdropService.clearBackdrop()
|
||||
}
|
||||
|
||||
var showHeader by rememberSaveable { mutableStateOf(true) }
|
||||
|
||||
val actions =
|
||||
remember {
|
||||
GridClickActions(
|
||||
onClickItem = viewModel::onClick,
|
||||
onLongClickItem = null,
|
||||
onClickPlayAll = viewModel::onClickPlayAll,
|
||||
onClickPlayRemoteButton = viewModel::onClickPlayRemoteButton,
|
||||
)
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = modifier,
|
||||
) {
|
||||
|
|
@ -133,9 +206,7 @@ fun CollectionFolderMusic(
|
|||
1 -> {
|
||||
CollectionFolderGrid(
|
||||
preferences = preferences,
|
||||
onClickItem = { _, item ->
|
||||
preferencesViewModel.navigationManager.navigateTo(item.destination())
|
||||
},
|
||||
actions = actions,
|
||||
itemId = destination.itemId,
|
||||
viewModelKey = "${destination.itemId}_albums",
|
||||
initialFilter =
|
||||
|
|
@ -165,9 +236,7 @@ fun CollectionFolderMusic(
|
|||
2 -> {
|
||||
CollectionFolderGrid(
|
||||
preferences = preferences,
|
||||
onClickItem = { _, item ->
|
||||
preferencesViewModel.navigationManager.navigateTo(item.destination())
|
||||
},
|
||||
actions = actions,
|
||||
itemId = destination.itemId,
|
||||
viewModelKey = "${destination.itemId}_artists",
|
||||
initialFilter =
|
||||
|
|
@ -209,9 +278,7 @@ fun CollectionFolderMusic(
|
|||
4 -> {
|
||||
CollectionFolderGrid(
|
||||
preferences = preferences,
|
||||
onClickItem = { _, item ->
|
||||
viewModel.play(item)
|
||||
},
|
||||
actions = actions,
|
||||
itemId = destination.itemId,
|
||||
viewModelKey = "${destination.itemId}_songs",
|
||||
initialFilter =
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import com.github.damontecres.wholphin.data.model.GetItemsFilter
|
|||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||
import com.github.damontecres.wholphin.ui.components.CollectionFolderGrid
|
||||
import com.github.damontecres.wholphin.ui.components.CollectionFolderViewModel
|
||||
import com.github.damontecres.wholphin.ui.components.GridClickActions
|
||||
import com.github.damontecres.wholphin.ui.components.ViewOptionsWide
|
||||
import com.github.damontecres.wholphin.ui.data.SortAndDirection
|
||||
import com.github.damontecres.wholphin.ui.data.VideoSortOptions
|
||||
|
|
@ -51,14 +52,22 @@ fun CollectionFolderPhotoAlbum(
|
|||
var showHeader by remember { mutableStateOf(true) }
|
||||
CollectionFolderGrid(
|
||||
preferences = preferences,
|
||||
actions =
|
||||
remember {
|
||||
GridClickActions(
|
||||
onClickItem = { index, item ->
|
||||
val destination =
|
||||
if (item.type == BaseItemKind.PHOTO) {
|
||||
Destination.Slideshow(
|
||||
parentId = itemId,
|
||||
index = index,
|
||||
filter = CollectionFolderFilter(filter = viewModel.filter.value ?: GetItemsFilter()),
|
||||
sortAndDirection = viewModel.sortAndDirection.value ?: SortAndDirection.DEFAULT,
|
||||
filter =
|
||||
CollectionFolderFilter(
|
||||
filter = viewModel.filter.value ?: GetItemsFilter(),
|
||||
),
|
||||
sortAndDirection =
|
||||
viewModel.sortAndDirection.value
|
||||
?: SortAndDirection.DEFAULT,
|
||||
recursive = recursive,
|
||||
startSlideshow = false,
|
||||
)
|
||||
|
|
@ -67,6 +76,8 @@ fun CollectionFolderPhotoAlbum(
|
|||
}
|
||||
viewModel.navigateTo(destination)
|
||||
},
|
||||
)
|
||||
},
|
||||
itemId = itemId.toServerString(),
|
||||
initialFilter = filter,
|
||||
showTitle = showHeader,
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import com.github.damontecres.wholphin.data.model.GetItemsFilterOverride
|
|||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||
import com.github.damontecres.wholphin.ui.components.CollectionFolderGrid
|
||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||
import com.github.damontecres.wholphin.ui.components.GridClickActions
|
||||
import com.github.damontecres.wholphin.ui.components.TabRow
|
||||
import com.github.damontecres.wholphin.ui.components.ViewOptionsPoster
|
||||
import com.github.damontecres.wholphin.ui.components.ViewOptionsSquare
|
||||
|
|
@ -109,13 +110,20 @@ fun FavoritesPage(
|
|||
focusRequesters = tabFocusRequesters,
|
||||
)
|
||||
}
|
||||
val actions =
|
||||
remember {
|
||||
GridClickActions(
|
||||
onClickItem = { _, item -> onClickItem.invoke(item) },
|
||||
)
|
||||
}
|
||||
|
||||
// TODO playEnabled = true for movies & episodes
|
||||
when (selectedTabIndex) {
|
||||
// Movies
|
||||
0 -> {
|
||||
CollectionFolderGrid(
|
||||
preferences = preferences,
|
||||
onClickItem = { _, item -> onClickItem.invoke(item) },
|
||||
actions = actions,
|
||||
itemId = "${NavDrawerItem.Favorites.id}_movies",
|
||||
initialFilter =
|
||||
CollectionFolderFilter(
|
||||
|
|
@ -147,7 +155,7 @@ fun FavoritesPage(
|
|||
1 -> {
|
||||
CollectionFolderGrid(
|
||||
preferences = preferences,
|
||||
onClickItem = { _, item -> onClickItem.invoke(item) },
|
||||
actions = actions,
|
||||
itemId = "${NavDrawerItem.Favorites.id}_series",
|
||||
initialFilter =
|
||||
CollectionFolderFilter(
|
||||
|
|
@ -179,7 +187,7 @@ fun FavoritesPage(
|
|||
2 -> {
|
||||
CollectionFolderGrid(
|
||||
preferences = preferences,
|
||||
onClickItem = { _, item -> onClickItem.invoke(item) },
|
||||
actions = actions,
|
||||
itemId = "${NavDrawerItem.Favorites.id}_episodes",
|
||||
initialFilter =
|
||||
CollectionFolderFilter(
|
||||
|
|
@ -212,7 +220,7 @@ fun FavoritesPage(
|
|||
3 -> {
|
||||
CollectionFolderGrid(
|
||||
preferences = preferences,
|
||||
onClickItem = { _, item -> onClickItem.invoke(item) },
|
||||
actions = actions,
|
||||
itemId = "${NavDrawerItem.Favorites.id}_videos",
|
||||
initialFilter =
|
||||
CollectionFolderFilter(
|
||||
|
|
@ -244,7 +252,7 @@ fun FavoritesPage(
|
|||
4 -> {
|
||||
CollectionFolderGrid(
|
||||
preferences = preferences,
|
||||
onClickItem = { _, item -> onClickItem.invoke(item) },
|
||||
actions = actions,
|
||||
itemId = "${NavDrawerItem.Favorites.id}_playlists",
|
||||
initialFilter =
|
||||
CollectionFolderFilter(
|
||||
|
|
@ -276,7 +284,7 @@ fun FavoritesPage(
|
|||
5 -> {
|
||||
CollectionFolderGrid(
|
||||
preferences = preferences,
|
||||
onClickItem = { _, item -> onClickItem.invoke(item) },
|
||||
actions = actions,
|
||||
itemId = "${NavDrawerItem.Favorites.id}_people",
|
||||
initialFilter =
|
||||
CollectionFolderFilter(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue