diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderGrid.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderGrid.kt index f574e883..848288d5 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderGrid.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/components/CollectionFolderGrid.kt @@ -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, + 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> = 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, playEnabled: Boolean, defaultViewOptions: ViewOptions, @@ -630,6 +667,61 @@ fun CollectionFolderGrid( var showDeleteDialog by remember { mutableStateOf(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, +) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderMusic.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderMusic.kt index 30534ffc..1911fc7a 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderMusic.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderMusic.kt @@ -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( + 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 = diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderPhotoAlbum.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderPhotoAlbum.kt index 562af214..62186f55 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderPhotoAlbum.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/CollectionFolderPhotoAlbum.kt @@ -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,22 +52,32 @@ fun CollectionFolderPhotoAlbum( var showHeader by remember { mutableStateOf(true) } CollectionFolderGrid( preferences = preferences, - 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, - recursive = recursive, - startSlideshow = false, - ) - } else { - item.destination(index) - } - viewModel.navigateTo(destination) - }, + 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, + recursive = recursive, + startSlideshow = false, + ) + } else { + item.destination(index) + } + viewModel.navigateTo(destination) + }, + ) + }, itemId = itemId.toServerString(), initialFilter = filter, showTitle = showHeader, diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/FavoritesPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/FavoritesPage.kt index 50b0053e..9b03aa0c 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/FavoritesPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/FavoritesPage.kt @@ -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(