mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
WIP playlist updates
This commit is contained in:
parent
5f5b07e861
commit
a699170583
2 changed files with 163 additions and 113 deletions
|
|
@ -265,7 +265,7 @@ suspend fun upgradeApp(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (previous.isEqualOrBefore(Version.fromString("0.5.2-1=g0"))) {
|
if (previous.isEqualOrBefore(Version.fromString("0.5.3-1-g0"))) {
|
||||||
appPreferences.updateData {
|
appPreferences.updateData {
|
||||||
it.updateMusicPreferences {
|
it.updateMusicPreferences {
|
||||||
showBackdrop = true
|
showBackdrop = true
|
||||||
|
|
|
||||||
|
|
@ -21,14 +21,12 @@ import androidx.compose.foundation.lazy.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.itemsIndexed
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowForward
|
|
||||||
import androidx.compose.material.icons.filled.PlayArrow
|
import androidx.compose.material.icons.filled.PlayArrow
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.Immutable
|
import androidx.compose.runtime.Immutable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.livedata.observeAsState
|
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
|
@ -47,7 +45,6 @@ import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.MutableLiveData
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import androidx.tv.material3.ListItem
|
import androidx.tv.material3.ListItem
|
||||||
import androidx.tv.material3.MaterialTheme
|
import androidx.tv.material3.MaterialTheme
|
||||||
|
|
@ -63,6 +60,7 @@ import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.data.model.GetItemsFilter
|
import com.github.damontecres.wholphin.data.model.GetItemsFilter
|
||||||
import com.github.damontecres.wholphin.data.model.LibraryDisplayInfo
|
import com.github.damontecres.wholphin.data.model.LibraryDisplayInfo
|
||||||
import com.github.damontecres.wholphin.services.BackdropService
|
import com.github.damontecres.wholphin.services.BackdropService
|
||||||
|
import com.github.damontecres.wholphin.services.FavoriteWatchManager
|
||||||
import com.github.damontecres.wholphin.services.MusicService
|
import com.github.damontecres.wholphin.services.MusicService
|
||||||
import com.github.damontecres.wholphin.services.MusicServiceState
|
import com.github.damontecres.wholphin.services.MusicServiceState
|
||||||
import com.github.damontecres.wholphin.services.NavigationManager
|
import com.github.damontecres.wholphin.services.NavigationManager
|
||||||
|
|
@ -70,7 +68,6 @@ import com.github.damontecres.wholphin.ui.DefaultItemFields
|
||||||
import com.github.damontecres.wholphin.ui.TimeFormatter
|
import com.github.damontecres.wholphin.ui.TimeFormatter
|
||||||
import com.github.damontecres.wholphin.ui.cards.ItemCardImage
|
import com.github.damontecres.wholphin.ui.cards.ItemCardImage
|
||||||
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
import com.github.damontecres.wholphin.ui.components.BasicDialog
|
||||||
import com.github.damontecres.wholphin.ui.components.DialogItem
|
|
||||||
import com.github.damontecres.wholphin.ui.components.DialogParams
|
import com.github.damontecres.wholphin.ui.components.DialogParams
|
||||||
import com.github.damontecres.wholphin.ui.components.DialogPopup
|
import com.github.damontecres.wholphin.ui.components.DialogPopup
|
||||||
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
|
|
@ -83,7 +80,10 @@ import com.github.damontecres.wholphin.ui.components.SortByButton
|
||||||
import com.github.damontecres.wholphin.ui.components.TextButton
|
import com.github.damontecres.wholphin.ui.components.TextButton
|
||||||
import com.github.damontecres.wholphin.ui.data.BoxSetSortOptions
|
import com.github.damontecres.wholphin.ui.data.BoxSetSortOptions
|
||||||
import com.github.damontecres.wholphin.ui.data.SortAndDirection
|
import com.github.damontecres.wholphin.ui.data.SortAndDirection
|
||||||
|
import com.github.damontecres.wholphin.ui.detail.music.MusicMoreDialogActions
|
||||||
import com.github.damontecres.wholphin.ui.detail.music.MusicQueueMarker
|
import com.github.damontecres.wholphin.ui.detail.music.MusicQueueMarker
|
||||||
|
import com.github.damontecres.wholphin.ui.detail.music.MusicViewModel
|
||||||
|
import com.github.damontecres.wholphin.ui.detail.music.buildMoreDialogForMusic
|
||||||
import com.github.damontecres.wholphin.ui.enableMarquee
|
import com.github.damontecres.wholphin.ui.enableMarquee
|
||||||
import com.github.damontecres.wholphin.ui.equalsNotNull
|
import com.github.damontecres.wholphin.ui.equalsNotNull
|
||||||
import com.github.damontecres.wholphin.ui.formatDateTime
|
import com.github.damontecres.wholphin.ui.formatDateTime
|
||||||
|
|
@ -92,21 +92,24 @@ import com.github.damontecres.wholphin.ui.launchDefault
|
||||||
import com.github.damontecres.wholphin.ui.launchIO
|
import com.github.damontecres.wholphin.ui.launchIO
|
||||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
import com.github.damontecres.wholphin.ui.roundMinutes
|
import com.github.damontecres.wholphin.ui.roundMinutes
|
||||||
import com.github.damontecres.wholphin.ui.setValueOnMain
|
import com.github.damontecres.wholphin.ui.toServerString
|
||||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||||
import com.github.damontecres.wholphin.ui.util.FilterUtils
|
import com.github.damontecres.wholphin.ui.util.FilterUtils
|
||||||
import com.github.damontecres.wholphin.ui.util.LocalClock
|
import com.github.damontecres.wholphin.ui.util.LocalClock
|
||||||
import com.github.damontecres.wholphin.util.ApiRequestPager
|
import com.github.damontecres.wholphin.util.ApiRequestPager
|
||||||
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
|
import com.github.damontecres.wholphin.util.GetItemsRequestHandler
|
||||||
import com.github.damontecres.wholphin.util.LoadingExceptionHandler
|
|
||||||
import com.github.damontecres.wholphin.util.LoadingState
|
import com.github.damontecres.wholphin.util.LoadingState
|
||||||
|
import dagger.assisted.Assisted
|
||||||
|
import dagger.assisted.AssistedFactory
|
||||||
|
import dagger.assisted.AssistedInject
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import org.jellyfin.sdk.api.client.ApiClient
|
import org.jellyfin.sdk.api.client.ApiClient
|
||||||
|
import org.jellyfin.sdk.api.client.extensions.userLibraryApi
|
||||||
import org.jellyfin.sdk.model.api.BaseItemKind
|
import org.jellyfin.sdk.model.api.BaseItemKind
|
||||||
import org.jellyfin.sdk.model.api.ItemSortBy
|
import org.jellyfin.sdk.model.api.ItemSortBy
|
||||||
import org.jellyfin.sdk.model.api.MediaType
|
import org.jellyfin.sdk.model.api.MediaType
|
||||||
|
|
@ -115,55 +118,55 @@ import org.jellyfin.sdk.model.api.request.GetItemsRequest
|
||||||
import org.jellyfin.sdk.model.extensions.ticks
|
import org.jellyfin.sdk.model.extensions.ticks
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import javax.inject.Inject
|
|
||||||
import kotlin.time.Duration
|
import kotlin.time.Duration
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel(assistedFactory = PlaylistViewModel.Factory::class)
|
||||||
class PlaylistViewModel
|
class PlaylistViewModel
|
||||||
@Inject
|
@AssistedInject
|
||||||
constructor(
|
constructor(
|
||||||
api: ApiClient,
|
api: ApiClient,
|
||||||
val navigationManager: NavigationManager,
|
navigationManager: NavigationManager,
|
||||||
|
musicService: MusicService,
|
||||||
private val backdropService: BackdropService,
|
private val backdropService: BackdropService,
|
||||||
private val serverRepository: ServerRepository,
|
private val serverRepository: ServerRepository,
|
||||||
private val libraryDisplayInfoDao: LibraryDisplayInfoDao,
|
private val libraryDisplayInfoDao: LibraryDisplayInfoDao,
|
||||||
private val musicService: MusicService,
|
private val favoriteWatchManager: FavoriteWatchManager,
|
||||||
) : ItemViewModel(api) {
|
@Assisted val itemId: UUID,
|
||||||
val loading = MutableLiveData<LoadingState>(LoadingState.Pending)
|
) : MusicViewModel(api, musicService, navigationManager) {
|
||||||
val items = MutableLiveData<List<BaseItem?>>(listOf())
|
@AssistedFactory
|
||||||
val filterAndSort =
|
interface Factory {
|
||||||
MutableStateFlow<FilterAndSort>(
|
fun create(itemId: UUID): PlaylistViewModel
|
||||||
FilterAndSort(
|
}
|
||||||
filter = GetItemsFilter(),
|
|
||||||
sortAndDirection =
|
val state = MutableStateFlow(PlaylistDetailsState())
|
||||||
SortAndDirection(
|
val musicState = musicService.state
|
||||||
|
|
||||||
|
init {
|
||||||
|
state.update { it.copy(loading = LoadingState.Loading) }
|
||||||
|
viewModelScope.launchDefault {
|
||||||
|
try {
|
||||||
|
val playlist =
|
||||||
|
api.userLibraryApi
|
||||||
|
.getItem(itemId)
|
||||||
|
.content
|
||||||
|
.let { BaseItem(it, false) }
|
||||||
|
state.update { it.copy(playlist = playlist) }
|
||||||
|
val libraryDisplayInfo =
|
||||||
|
serverRepository.currentUser.value?.let { user ->
|
||||||
|
libraryDisplayInfoDao.getItem(user, itemId)
|
||||||
|
}
|
||||||
|
val filter = libraryDisplayInfo?.filter ?: GetItemsFilter()
|
||||||
|
val sortAndDirection =
|
||||||
|
libraryDisplayInfo?.sortAndDirection ?: SortAndDirection(
|
||||||
ItemSortBy.DEFAULT,
|
ItemSortBy.DEFAULT,
|
||||||
SortOrder.ASCENDING,
|
SortOrder.ASCENDING,
|
||||||
),
|
)
|
||||||
),
|
loadItems(filter, sortAndDirection).join()
|
||||||
)
|
determineMediaType()
|
||||||
val musicState = musicService.state
|
} catch (ex: Exception) {
|
||||||
val playlistMediaType = MutableStateFlow(MediaType.UNKNOWN)
|
Timber.e(ex, "Error fetching playlist %s", itemId)
|
||||||
|
state.update { it.copy(loading = LoadingState.Error(ex)) }
|
||||||
fun init(playlistId: UUID) {
|
}
|
||||||
loading.value = LoadingState.Loading
|
|
||||||
viewModelScope.launch(
|
|
||||||
Dispatchers.IO +
|
|
||||||
LoadingExceptionHandler(loading, "Failed to fetch playlist $playlistId"),
|
|
||||||
) {
|
|
||||||
val playlist = fetchItem(playlistId)
|
|
||||||
val libraryDisplayInfo =
|
|
||||||
serverRepository.currentUser.value?.let { user ->
|
|
||||||
libraryDisplayInfoDao.getItem(user, itemId)
|
|
||||||
}
|
|
||||||
val filter = libraryDisplayInfo?.filter ?: GetItemsFilter()
|
|
||||||
val sortAndDirection =
|
|
||||||
libraryDisplayInfo?.sortAndDirection ?: SortAndDirection(
|
|
||||||
ItemSortBy.DEFAULT,
|
|
||||||
SortOrder.ASCENDING,
|
|
||||||
)
|
|
||||||
loadItems(filter, sortAndDirection).join()
|
|
||||||
determineMediaType()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -172,13 +175,14 @@ class PlaylistViewModel
|
||||||
sortAndDirection: SortAndDirection,
|
sortAndDirection: SortAndDirection,
|
||||||
) = viewModelScope.launchIO {
|
) = viewModelScope.launchIO {
|
||||||
backdropService.clearBackdrop()
|
backdropService.clearBackdrop()
|
||||||
loading.setValueOnMain(LoadingState.Loading)
|
state.update {
|
||||||
this@PlaylistViewModel.filterAndSort.update {
|
it.copy(
|
||||||
FilterAndSort(filter, sortAndDirection)
|
loading = LoadingState.Loading,
|
||||||
|
filterAndSort = FilterAndSort(filter, sortAndDirection),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
serverRepository.currentUser.value?.let { user ->
|
serverRepository.currentUser.value?.let { user ->
|
||||||
val playlistId = item.value!!.id
|
|
||||||
viewModelScope.launchIO {
|
viewModelScope.launchIO {
|
||||||
val libraryDisplayInfo =
|
val libraryDisplayInfo =
|
||||||
libraryDisplayInfoDao.getItem(user, itemId)?.copy(
|
libraryDisplayInfoDao.getItem(user, itemId)?.copy(
|
||||||
|
|
@ -188,7 +192,7 @@ class PlaylistViewModel
|
||||||
)
|
)
|
||||||
?: LibraryDisplayInfo(
|
?: LibraryDisplayInfo(
|
||||||
userId = user.rowId,
|
userId = user.rowId,
|
||||||
itemId = itemId,
|
itemId = itemId.toServerString(),
|
||||||
sort = sortAndDirection.sort,
|
sort = sortAndDirection.sort,
|
||||||
direction = sortAndDirection.direction,
|
direction = sortAndDirection.direction,
|
||||||
filter = filter,
|
filter = filter,
|
||||||
|
|
@ -200,7 +204,7 @@ class PlaylistViewModel
|
||||||
val request =
|
val request =
|
||||||
filter.applyTo(
|
filter.applyTo(
|
||||||
GetItemsRequest(
|
GetItemsRequest(
|
||||||
parentId = playlistId,
|
parentId = itemId,
|
||||||
userId = user.id,
|
userId = user.id,
|
||||||
fields = DefaultItemFields,
|
fields = DefaultItemFields,
|
||||||
sortBy = listOf(sortAndDirection.sort),
|
sortBy = listOf(sortAndDirection.sort),
|
||||||
|
|
@ -215,16 +219,19 @@ class PlaylistViewModel
|
||||||
GetItemsRequestHandler,
|
GetItemsRequestHandler,
|
||||||
viewModelScope,
|
viewModelScope,
|
||||||
).init()
|
).init()
|
||||||
|
state.update {
|
||||||
withContext(Dispatchers.Main) {
|
it.copy(
|
||||||
items.value = pager
|
items = pager,
|
||||||
loading.value = LoadingState.Success
|
loading = LoadingState.Success,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} catch (ex: Exception) {
|
} catch (ex: Exception) {
|
||||||
Timber.e(ex, "Error fetching playlist %s", itemId)
|
Timber.e(ex, "Error fetching playlist %s", itemId)
|
||||||
withContext(Dispatchers.Main) {
|
state.update {
|
||||||
items.value = listOf()
|
it.copy(
|
||||||
loading.value = LoadingState.Error(ex)
|
items = emptyList(),
|
||||||
|
loading = LoadingState.Error(ex),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -238,13 +245,13 @@ class PlaylistViewModel
|
||||||
private suspend fun determineMediaType() {
|
private suspend fun determineMediaType() {
|
||||||
// Use the type the server says
|
// Use the type the server says
|
||||||
var mediaType =
|
var mediaType =
|
||||||
super.item.value
|
state.value.playlist
|
||||||
?.data
|
?.data
|
||||||
?.mediaType ?: MediaType.UNKNOWN
|
?.mediaType ?: MediaType.UNKNOWN
|
||||||
mediaType =
|
mediaType =
|
||||||
if (mediaType == MediaType.UNKNOWN) {
|
if (mediaType == MediaType.UNKNOWN) {
|
||||||
// Otherwise, if a most of the list is one type, we can assume that type
|
// Otherwise, if a most of the list is one type, we can assume that type
|
||||||
val pager = (this@PlaylistViewModel.items.value as? ApiRequestPager<*>)
|
val pager = (state.value.items as? ApiRequestPager<*>)
|
||||||
if (pager != null && pager.size <= 50) {
|
if (pager != null && pager.size <= 50) {
|
||||||
val types =
|
val types =
|
||||||
(0..<50.coerceAtMost(pager.size)).groupBy { index ->
|
(0..<50.coerceAtMost(pager.size)).groupBy { index ->
|
||||||
|
|
@ -273,14 +280,16 @@ class PlaylistViewModel
|
||||||
mediaType
|
mediaType
|
||||||
}
|
}
|
||||||
Timber.d("mediaType=%s", mediaType)
|
Timber.d("mediaType=%s", mediaType)
|
||||||
playlistMediaType.update { mediaType }
|
state.update {
|
||||||
|
it.copy(mediaType = mediaType)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getFilterOptionValues(filterOption: ItemFilterBy<*>): List<FilterValueOption> =
|
suspend fun getFilterOptionValues(filterOption: ItemFilterBy<*>): List<FilterValueOption> =
|
||||||
FilterUtils.getFilterOptionValues(
|
FilterUtils.getFilterOptionValues(
|
||||||
api,
|
api,
|
||||||
serverRepository.currentUser.value?.id,
|
serverRepository.currentUser.value?.id,
|
||||||
itemUuid,
|
itemId,
|
||||||
filterOption,
|
filterOption,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -290,15 +299,18 @@ class PlaylistViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun playMusic(
|
fun setWatched(
|
||||||
index: Int,
|
itemId: UUID,
|
||||||
shuffle: Boolean,
|
played: Boolean,
|
||||||
) {
|
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
||||||
viewModelScope.launchDefault {
|
favoriteWatchManager.setWatched(itemId, played)
|
||||||
(items.value as? ApiRequestPager<*>)?.let {
|
}
|
||||||
musicService.setQueue(it, index, shuffle)
|
|
||||||
}
|
fun setFavorite(
|
||||||
}
|
itemId: UUID,
|
||||||
|
favorite: Boolean,
|
||||||
|
) = viewModelScope.launch(ExceptionHandler() + Dispatchers.IO) {
|
||||||
|
favoriteWatchManager.setFavorite(itemId, favorite)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -308,68 +320,100 @@ data class FilterAndSort(
|
||||||
val sortAndDirection: SortAndDirection,
|
val sortAndDirection: SortAndDirection,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
data class PlaylistDetailsState(
|
||||||
|
val playlist: BaseItem? = null,
|
||||||
|
val mediaType: MediaType = MediaType.UNKNOWN,
|
||||||
|
val items: List<BaseItem?> = emptyList(),
|
||||||
|
val filterAndSort: FilterAndSort =
|
||||||
|
FilterAndSort(
|
||||||
|
filter = GetItemsFilter(),
|
||||||
|
sortAndDirection =
|
||||||
|
SortAndDirection(
|
||||||
|
ItemSortBy.DEFAULT,
|
||||||
|
SortOrder.ASCENDING,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
val loading: LoadingState = LoadingState.Pending,
|
||||||
|
)
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PlaylistDetails(
|
fun PlaylistDetails(
|
||||||
destination: Destination.MediaItem,
|
destination: Destination.MediaItem,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: PlaylistViewModel = hiltViewModel(),
|
viewModel: PlaylistViewModel =
|
||||||
|
hiltViewModel<PlaylistViewModel, PlaylistViewModel.Factory>(
|
||||||
|
creationCallback = { it.create(destination.itemId) },
|
||||||
|
),
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
LaunchedEffect(Unit) {
|
val state by viewModel.state.collectAsState()
|
||||||
viewModel.init(destination.itemId)
|
|
||||||
}
|
|
||||||
val loading by viewModel.loading.observeAsState(LoadingState.Pending)
|
|
||||||
val playlist by viewModel.item.observeAsState(null)
|
|
||||||
val items by viewModel.items.observeAsState(listOf())
|
|
||||||
val filterAndSort by viewModel.filterAndSort.collectAsState()
|
|
||||||
val musicState by viewModel.musicState.collectAsState()
|
val musicState by viewModel.musicState.collectAsState()
|
||||||
val playlistMediaType by viewModel.playlistMediaType.collectAsState()
|
|
||||||
|
|
||||||
var longClickDialog by remember { mutableStateOf<DialogParams?>(null) }
|
var longClickDialog by remember { mutableStateOf<DialogParams?>(null) }
|
||||||
var showConfirmTypeDialog by remember { mutableStateOf<Pair<Int, Boolean>?>(null) }
|
var showConfirmTypeDialog by remember { mutableStateOf<Triple<Int, BaseItem, Boolean>?>(null) }
|
||||||
|
|
||||||
val goToString = stringResource(R.string.go_to)
|
|
||||||
val playFromHereString = stringResource(R.string.play_from_here)
|
|
||||||
|
|
||||||
fun play(
|
fun play(
|
||||||
index: Int,
|
index: Int,
|
||||||
|
item: BaseItem,
|
||||||
shuffle: Boolean,
|
shuffle: Boolean,
|
||||||
mediaTypeOverride: MediaType? = null,
|
mediaTypeOverride: MediaType? = null,
|
||||||
) {
|
) {
|
||||||
when (mediaTypeOverride ?: playlistMediaType) {
|
when (mediaTypeOverride ?: state.mediaType) {
|
||||||
MediaType.VIDEO -> {
|
MediaType.VIDEO -> {
|
||||||
viewModel.navigationManager.navigateTo(
|
viewModel.navigationManager.navigateTo(
|
||||||
Destination.PlaybackList(
|
Destination.PlaybackList(
|
||||||
itemId = destination.itemId,
|
itemId = destination.itemId,
|
||||||
startIndex = index,
|
startIndex = index,
|
||||||
shuffle = shuffle,
|
shuffle = shuffle,
|
||||||
filter = filterAndSort.filter,
|
filter = state.filterAndSort.filter,
|
||||||
sortAndDirection = filterAndSort.sortAndDirection,
|
sortAndDirection = state.filterAndSort.sortAndDirection,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaType.AUDIO -> {
|
MediaType.AUDIO -> {
|
||||||
viewModel.playMusic(index, shuffle)
|
viewModel.play(item, index, shuffle)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
showConfirmTypeDialog = Pair(index, shuffle)
|
showConfirmTypeDialog = Triple(index, item, shuffle)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val musicMoreActions =
|
||||||
|
MusicMoreDialogActions(
|
||||||
|
onNavigate = { viewModel.navigationManager.navigateTo(it) },
|
||||||
|
onClickPlay = { index, item -> play(index, item, false, MediaType.AUDIO) },
|
||||||
|
onClickPlayNext = { index, item -> viewModel.playNext(item) },
|
||||||
|
onClickAddToQueue = { index, item -> viewModel.addToQueue(item, Int.MAX_VALUE) },
|
||||||
|
onClickFavorite = { id, favorite -> },
|
||||||
|
onClickAddPlaylist = {},
|
||||||
|
onClickRemoveFromQueue = {},
|
||||||
|
)
|
||||||
|
val moreActions =
|
||||||
|
MoreDialogActions(
|
||||||
|
navigateTo = { viewModel.navigationManager.navigateTo(it) },
|
||||||
|
onClickWatch = { id, watched -> },
|
||||||
|
onClickFavorite = { id, favorite -> },
|
||||||
|
onClickAddPlaylist = {},
|
||||||
|
onSendMediaInfo = {},
|
||||||
|
onClickDelete = {},
|
||||||
|
)
|
||||||
|
|
||||||
PlaylistDetailsContent(
|
PlaylistDetailsContent(
|
||||||
loadingState = loading,
|
loadingState = state.loading,
|
||||||
playlist = playlist,
|
playlist = state.playlist,
|
||||||
items = items,
|
items = state.items,
|
||||||
musicState = musicState,
|
musicState = musicState,
|
||||||
onChangeBackdrop = viewModel::updateBackdrop,
|
onChangeBackdrop = viewModel::updateBackdrop,
|
||||||
onClickIndex = { index, item ->
|
onClickIndex = { index, item ->
|
||||||
play(index, false)
|
play(index, item, false)
|
||||||
},
|
},
|
||||||
onClickPlay = { shuffle ->
|
onClickPlay = { shuffle ->
|
||||||
play(0, shuffle)
|
state.playlist?.let {
|
||||||
|
play(0, it, shuffle)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
onLongClickIndex = { index, item ->
|
onLongClickIndex = { index, item ->
|
||||||
longClickDialog =
|
longClickDialog =
|
||||||
|
|
@ -377,23 +421,29 @@ fun PlaylistDetails(
|
||||||
fromLongClick = true,
|
fromLongClick = true,
|
||||||
title = item.name ?: "",
|
title = item.name ?: "",
|
||||||
items =
|
items =
|
||||||
listOf(
|
if (item.type == BaseItemKind.AUDIO) {
|
||||||
DialogItem(
|
buildMoreDialogForMusic(
|
||||||
goToString,
|
context = context,
|
||||||
Icons.Default.ArrowForward,
|
actions = musicMoreActions,
|
||||||
) {
|
item = item,
|
||||||
viewModel.navigationManager.navigateTo(item.destination())
|
index = index,
|
||||||
},
|
canRemove = false,
|
||||||
DialogItem(
|
)
|
||||||
playFromHereString,
|
} else {
|
||||||
Icons.Default.PlayArrow,
|
buildMoreDialogItemsForHome(
|
||||||
) {
|
context = context,
|
||||||
play(index, false)
|
item = item,
|
||||||
},
|
seriesId = item.data.seriesId,
|
||||||
),
|
playbackPosition = item.playbackPosition,
|
||||||
|
watched = item.played,
|
||||||
|
favorite = item.favorite,
|
||||||
|
canDelete = false,
|
||||||
|
actions = moreActions,
|
||||||
|
)
|
||||||
|
},
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
filterAndSort = filterAndSort,
|
filterAndSort = state.filterAndSort,
|
||||||
onFilterAndSortChange = viewModel::loadItems,
|
onFilterAndSortChange = viewModel::loadItems,
|
||||||
getPossibleFilterValues = viewModel::getFilterOptionValues,
|
getPossibleFilterValues = viewModel::getFilterOptionValues,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
|
|
@ -404,9 +454,9 @@ fun PlaylistDetails(
|
||||||
onDismissRequest = { longClickDialog = null },
|
onDismissRequest = { longClickDialog = null },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
showConfirmTypeDialog?.let { (index, shuffle) ->
|
showConfirmTypeDialog?.let { (index, item, shuffle) ->
|
||||||
ConfirmMediaTypeDialog(
|
ConfirmMediaTypeDialog(
|
||||||
onConfirm = { mediaType -> play(index, shuffle, mediaType) },
|
onConfirm = { mediaType -> play(index, item, shuffle, mediaType) },
|
||||||
onCancel = { showConfirmTypeDialog = null },
|
onCancel = { showConfirmTypeDialog = null },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue