diff --git a/app/src/main/java/com/github/damontecres/wholphin/services/MediaManagementService.kt b/app/src/main/java/com/github/damontecres/wholphin/services/MediaManagementService.kt index beab6e64..5a6d8ff0 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/services/MediaManagementService.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/services/MediaManagementService.kt @@ -6,12 +6,15 @@ import androidx.lifecycle.viewModelScope import com.github.damontecres.wholphin.data.ServerRepository import com.github.damontecres.wholphin.data.model.BaseItem import com.github.damontecres.wholphin.preferences.AppPreferences +import com.github.damontecres.wholphin.ui.combinePair import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.showToast import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.channels.BufferOverflow +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.SharedFlow +import kotlinx.coroutines.flow.collectLatest import org.jellyfin.sdk.api.client.ApiClient import org.jellyfin.sdk.api.client.extensions.libraryApi import org.jellyfin.sdk.model.api.BaseItemKind @@ -68,6 +71,16 @@ class MediaManagementService } } + suspend fun collectCanDelete( + itemFlow: Flow, + update: (Boolean) -> Unit, + ) { + itemFlow.combinePair(userPreferencesService.flow).collectLatest { (item, prefs) -> + val canDelete = item?.let { canDelete(item, prefs.appPreferences) } ?: false + update.invoke(canDelete) + } + } + /** * Delete the item. * diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/collection/CollectionDetails.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/collection/CollectionDetails.kt index 332e2a3f..4da00ef7 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/collection/CollectionDetails.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/collection/CollectionDetails.kt @@ -207,7 +207,10 @@ fun CollectionDetails( } }, canDelete = - remember(state.collection) { + remember( + state.collection, + preferences.appPreferences.interfacePreferences.enableMediaManagement, + ) { state.collection?.let { viewModel.canDelete(it, preferences.appPreferences) } ?: false diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/episode/EpisodeViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/episode/EpisodeViewModel.kt index df3b180a..2110a3a5 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/episode/EpisodeViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/episode/EpisodeViewModel.kt @@ -20,7 +20,6 @@ import com.github.damontecres.wholphin.services.StreamChoiceService import com.github.damontecres.wholphin.services.ThemeSongPlayer import com.github.damontecres.wholphin.services.UserPreferencesService import com.github.damontecres.wholphin.services.deleteItem -import com.github.damontecres.wholphin.ui.combinePair import com.github.damontecres.wholphin.ui.launchDefault import com.github.damontecres.wholphin.ui.launchIO import com.github.damontecres.wholphin.ui.nav.Destination @@ -38,9 +37,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.async import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.filterNotNull -import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -81,13 +77,9 @@ class EpisodeViewModel init { init() viewModelScope.launchDefault { - item - .asFlow() - .filterNotNull() - .combinePair(userPreferencesService.flow.map { it.appPreferences }) - .collectLatest { (item, preferences) -> - canDelete.update { mediaManagementService.canDelete(item, preferences) } - } + mediaManagementService.collectCanDelete(item.asFlow()) { canDelete -> + this@EpisodeViewModel.canDelete.update { canDelete } + } } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt index 82518409..bd2cc373 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/movie/MovieViewModel.kt @@ -42,7 +42,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update import org.jellyfin.sdk.api.client.ApiClient import org.jellyfin.sdk.api.client.extensions.libraryApi @@ -85,18 +85,9 @@ class MovieViewModel init { init() viewModelScope.launchDefault { - userPreferencesService.flow.collectLatest { preferences -> + mediaManagementService.collectCanDelete(state.map { it.movie }) { canDelete -> _state.update { - val canDelete = - it.movie?.let { - mediaManagementService.canDelete( - it, - preferences.appPreferences, - ) - } - it.copy( - canDelete = canDelete ?: false, - ) + it.copy(canDelete = canDelete) } } } diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/AlbumDetailsPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/AlbumDetailsPage.kt index 173ba49b..8d374a0c 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/AlbumDetailsPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/AlbumDetailsPage.kt @@ -94,6 +94,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.jellyfin.sdk.api.client.ApiClient @@ -136,6 +137,11 @@ class AlbumViewModel init { init() + viewModelScope.launchDefault { + mediaManagementService.collectCanDelete(state.map { it.album }) { canDelete -> + _state.update { it.copy(canDelete = canDelete) } + } + } } override fun init() { @@ -275,6 +281,7 @@ data class AlbumState( val similar: List, val loading: LoadingState, val musicVideos: List = emptyList(), + val canDelete: Boolean = false, ) { companion object { val EMPTY = AlbumState(null, false, null, emptyList(), emptyList(), LoadingState.Pending) @@ -389,6 +396,7 @@ fun AlbumDetailsPage( modifier = Modifier.fillMaxWidth(), ) MusicExpandableButtons( + title = album.title ?: "", actions = remember { MusicButtonActions( @@ -415,9 +423,13 @@ fun AlbumDetailsPage( actions = moreDialogActions, ) }, + onConfirmDelete = { + viewModel.deleteItem(album) + }, ) }, favorite = album.favorite, + canDelete = state.canDelete, buttonOnFocusChanged = { if (it.isFocused) { position = RowColumn(HEADER_ROW, 0) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/ArtistDetailsPage.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/ArtistDetailsPage.kt index 77f2e063..1f693322 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/ArtistDetailsPage.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/ArtistDetailsPage.kt @@ -92,6 +92,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.async import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import org.jellyfin.sdk.api.client.ApiClient @@ -135,6 +136,11 @@ class ArtistViewModel init { init() + viewModelScope.launchDefault { + mediaManagementService.collectCanDelete(state.map { it.artist }) { canDelete -> + _state.update { it.copy(canDelete = canDelete) } + } + } } override fun init() { @@ -269,6 +275,7 @@ data class ArtistState( val similar: List, val loading: LoadingState, val musicVideos: List, + val canDelete: Boolean, ) { companion object { val EMPTY = @@ -280,6 +287,7 @@ data class ArtistState( emptyList(), LoadingState.Pending, emptyList(), + false, ) } } @@ -378,6 +386,7 @@ fun ArtistDetailsPage( modifier = Modifier.fillMaxWidth(), ) MusicExpandableButtons( + title = artist.title ?: "", actions = remember { MusicButtonActions( @@ -406,9 +415,11 @@ fun ArtistDetailsPage( actions = moreDialogActions, ) }, + onConfirmDelete = { viewModel.deleteItem(artist) }, ) }, favorite = artist.favorite, + canDelete = state.canDelete, buttonOnFocusChanged = { if (it.isFocused) { position = RowColumn(HEADER_ROW, 0) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/MusicExpandableButtons.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/MusicExpandableButtons.kt index 78a5603b..b93483c9 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/MusicExpandableButtons.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/music/MusicExpandableButtons.kt @@ -18,14 +18,17 @@ import androidx.compose.ui.focus.onFocusChanged import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import com.github.damontecres.wholphin.R +import com.github.damontecres.wholphin.ui.components.DeleteButton import com.github.damontecres.wholphin.ui.components.ExpandableFaButton import com.github.damontecres.wholphin.ui.components.ExpandablePlayButton import kotlin.time.Duration @Composable fun MusicExpandableButtons( + title: String, actions: MusicButtonActions, favorite: Boolean, + canDelete: Boolean, buttonOnFocusChanged: (FocusState) -> Unit, modifier: Modifier = Modifier, ) { @@ -75,6 +78,15 @@ fun MusicExpandableButtons( modifier = Modifier.onFocusChanged(buttonOnFocusChanged), ) } + if (canDelete) { + item("delete") { + DeleteButton( + title = title, + onConfirmDelete = actions.onConfirmDelete, + modifier = Modifier.onFocusChanged(buttonOnFocusChanged), + ) + } + } item("more") { ExpandablePlayButton( title = R.string.more, @@ -92,4 +104,5 @@ data class MusicButtonActions( val onClickInstantMix: () -> Unit, val onClickFavorite: () -> Unit, val onClickMore: () -> Unit, + val onConfirmDelete: () -> Unit, ) diff --git a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt index c84f9b8d..4d0ce9e2 100644 --- a/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt +++ b/app/src/main/java/com/github/damontecres/wholphin/ui/detail/series/SeriesViewModel.kt @@ -59,6 +59,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.update @@ -138,7 +139,11 @@ class SeriesViewModel Timber.v("Start") addCloseable { themeSongPlayer.stop() } val item = fetchItem(seriesId) - canDeleteSeries.update { mediaManagementService.canDelete(item) } + viewModelScope.launchDefault { + mediaManagementService.collectCanDelete(flowOf(item)) { canDelete -> + canDeleteSeries.update { canDelete } + } + } backdropService.submit(item) val seasonsDeferred = getSeasons(item, seasonEpisodeIds?.seasonNumber)