mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Delete media follow up (#1040)
## Description A follow up to #1014 to add the delete option in more context menus. Also improves the "reaction" logic which updates previous pages from deletions on later pages. ### Related issues Addresses https://github.com/damontecres/Wholphin/pull/1014#issuecomment-3987205355 Related to #104 ### Testing Emulator ## Screenshots N/A ## AI or LLM usage None
This commit is contained in:
parent
ce6461d23b
commit
354577e2e8
18 changed files with 261 additions and 28 deletions
|
|
@ -5,6 +5,7 @@ import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.github.damontecres.wholphin.data.ServerRepository
|
import com.github.damontecres.wholphin.data.ServerRepository
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
|
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||||
import com.github.damontecres.wholphin.ui.launchIO
|
import com.github.damontecres.wholphin.ui.launchIO
|
||||||
import com.github.damontecres.wholphin.ui.showToast
|
import com.github.damontecres.wholphin.ui.showToast
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
|
@ -40,11 +41,16 @@ class MediaManagementService
|
||||||
val deletedItemFlow: SharedFlow<DeletedItem> = _deletedItemFlow
|
val deletedItemFlow: SharedFlow<DeletedItem> = _deletedItemFlow
|
||||||
|
|
||||||
suspend fun canDelete(item: BaseItem): Boolean {
|
suspend fun canDelete(item: BaseItem): Boolean {
|
||||||
|
val appPreferences = userPreferencesService.getCurrent().appPreferences
|
||||||
|
return canDelete(item, appPreferences)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun canDelete(
|
||||||
|
item: BaseItem,
|
||||||
|
appPreferences: AppPreferences,
|
||||||
|
): Boolean {
|
||||||
Timber.v("canDelete %s: %s", item.id, item.canDelete)
|
Timber.v("canDelete %s: %s", item.id, item.canDelete)
|
||||||
val enabled =
|
val enabled = appPreferences.interfacePreferences.enableMediaManagement
|
||||||
userPreferencesService
|
|
||||||
.getCurrent()
|
|
||||||
.appPreferences.interfacePreferences.enableMediaManagement
|
|
||||||
return enabled &&
|
return enabled &&
|
||||||
item.canDelete &&
|
item.canDelete &&
|
||||||
if (item.type == BaseItemKind.RECORDING) {
|
if (item.type == BaseItemKind.RECORDING) {
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ import com.github.damontecres.wholphin.data.model.CollectionFolderFilter
|
||||||
import com.github.damontecres.wholphin.data.model.GetItemsFilter
|
import com.github.damontecres.wholphin.data.model.GetItemsFilter
|
||||||
import com.github.damontecres.wholphin.data.model.GetItemsFilterOverride
|
import com.github.damontecres.wholphin.data.model.GetItemsFilterOverride
|
||||||
import com.github.damontecres.wholphin.data.model.LibraryDisplayInfo
|
import com.github.damontecres.wholphin.data.model.LibraryDisplayInfo
|
||||||
|
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
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.FavoriteWatchManager
|
||||||
|
|
@ -65,6 +66,7 @@ import com.github.damontecres.wholphin.services.MediaReportService
|
||||||
import com.github.damontecres.wholphin.services.NavigationManager
|
import com.github.damontecres.wholphin.services.NavigationManager
|
||||||
import com.github.damontecres.wholphin.services.ThemeSongPlayer
|
import com.github.damontecres.wholphin.services.ThemeSongPlayer
|
||||||
import com.github.damontecres.wholphin.services.UserPreferencesService
|
import com.github.damontecres.wholphin.services.UserPreferencesService
|
||||||
|
import com.github.damontecres.wholphin.services.deleteItem
|
||||||
import com.github.damontecres.wholphin.ui.AspectRatios
|
import com.github.damontecres.wholphin.ui.AspectRatios
|
||||||
import com.github.damontecres.wholphin.ui.RequestOrRestoreFocus
|
import com.github.damontecres.wholphin.ui.RequestOrRestoreFocus
|
||||||
import com.github.damontecres.wholphin.ui.SlimItemFields
|
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||||
|
|
@ -77,6 +79,7 @@ import com.github.damontecres.wholphin.ui.detail.MoreDialogActions
|
||||||
import com.github.damontecres.wholphin.ui.detail.PlaylistDialog
|
import com.github.damontecres.wholphin.ui.detail.PlaylistDialog
|
||||||
import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState
|
import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState
|
||||||
import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForHome
|
import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForHome
|
||||||
|
import com.github.damontecres.wholphin.ui.equalsNotNull
|
||||||
import com.github.damontecres.wholphin.ui.launchDefault
|
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.main.HomePageHeader
|
import com.github.damontecres.wholphin.ui.main.HomePageHeader
|
||||||
|
|
@ -84,6 +87,7 @@ import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
import com.github.damontecres.wholphin.ui.playback.scale
|
import com.github.damontecres.wholphin.ui.playback.scale
|
||||||
import com.github.damontecres.wholphin.ui.rememberInt
|
import com.github.damontecres.wholphin.ui.rememberInt
|
||||||
import com.github.damontecres.wholphin.ui.setValueOnMain
|
import com.github.damontecres.wholphin.ui.setValueOnMain
|
||||||
|
import com.github.damontecres.wholphin.ui.showToast
|
||||||
import com.github.damontecres.wholphin.ui.toServerString
|
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
|
||||||
|
|
@ -100,6 +104,9 @@ import dagger.assisted.AssistedInject
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.jellyfin.sdk.api.client.ApiClient
|
import org.jellyfin.sdk.api.client.ApiClient
|
||||||
|
|
@ -202,18 +209,34 @@ class CollectionFolderViewModel
|
||||||
loading.setValueOnMain(DataLoadingState.Error(ex))
|
loading.setValueOnMain(DataLoadingState.Error(ex))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
viewModelScope.launchDefault {
|
mediaManagementService.deletedItemFlow
|
||||||
mediaManagementService.deletedItemFlow.collect { deletedItem ->
|
.onEach { deletedItem ->
|
||||||
val pager =
|
refreshAfterDelete(position, deletedItem.item)
|
||||||
((loading.value as? DataLoadingState.Success)?.data as? ApiRequestPager<*>)
|
}.catch { ex ->
|
||||||
position.let {
|
Timber.e(ex, "Error refreshing after deleted item")
|
||||||
Timber.v("Item deleted: position=%s, id=%s", it, deletedItem.item.id)
|
}.launchIn(viewModelScope)
|
||||||
val item = pager?.get(it)
|
}
|
||||||
if (item?.id == deletedItem.item.id) {
|
|
||||||
pager.refreshPagesAfter(position)
|
private suspend fun refreshAfterDelete(
|
||||||
}
|
position: Int,
|
||||||
|
deletedItem: BaseItem,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
val pager =
|
||||||
|
((loading.value as? DataLoadingState.Success)?.data as? ApiRequestPager<*>)
|
||||||
|
position.let {
|
||||||
|
Timber.v("Item deleted: position=%s, id=%s", it, itemId)
|
||||||
|
val item = pager?.get(it)
|
||||||
|
// Exact item deleted (eg a movie) or deleted item was within the series
|
||||||
|
if (item?.id == deletedItem.id ||
|
||||||
|
equalsNotNull(item?.data?.id, deletedItem.data.seriesId)
|
||||||
|
) {
|
||||||
|
pager?.refreshPagesAfter(position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (ex: Exception) {
|
||||||
|
Timber.e(ex, "Error refreshing after deleted item %s", itemId)
|
||||||
|
showToast(context, "Error refreshing after item deleted")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -491,6 +514,22 @@ class CollectionFolderViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteItem(
|
||||||
|
index: Int,
|
||||||
|
item: BaseItem,
|
||||||
|
) {
|
||||||
|
deleteItem(context, mediaManagementService, item) {
|
||||||
|
viewModelScope.launchDefault {
|
||||||
|
refreshAfterDelete(index, item)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun canDelete(
|
||||||
|
item: BaseItem,
|
||||||
|
appPreferences: AppPreferences,
|
||||||
|
): Boolean = mediaManagementService.canDelete(item, appPreferences)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -578,6 +617,7 @@ fun CollectionFolderGrid(
|
||||||
|
|
||||||
var moreDialog by remember { mutableStateOf<Optional<PositionItem>>(Optional.absent()) }
|
var moreDialog by remember { mutableStateOf<Optional<PositionItem>>(Optional.absent()) }
|
||||||
var showPlaylistDialog by remember { mutableStateOf<Optional<UUID>>(Optional.absent()) }
|
var showPlaylistDialog by remember { mutableStateOf<Optional<UUID>>(Optional.absent()) }
|
||||||
|
var showDeleteDialog by remember { mutableStateOf<PositionItem?>(null) }
|
||||||
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
||||||
|
|
||||||
when (val state = loading) {
|
when (val state = loading) {
|
||||||
|
|
@ -713,6 +753,7 @@ fun CollectionFolderGrid(
|
||||||
playbackPosition = item.playbackPosition,
|
playbackPosition = item.playbackPosition,
|
||||||
watched = item.played,
|
watched = item.played,
|
||||||
favorite = item.favorite,
|
favorite = item.favorite,
|
||||||
|
canDelete = viewModel.canDelete(item, preferences.appPreferences),
|
||||||
actions =
|
actions =
|
||||||
MoreDialogActions(
|
MoreDialogActions(
|
||||||
navigateTo = { viewModel.navigateTo(it) },
|
navigateTo = { viewModel.navigateTo(it) },
|
||||||
|
|
@ -727,6 +768,9 @@ fun CollectionFolderGrid(
|
||||||
showPlaylistDialog.makePresent(it)
|
showPlaylistDialog.makePresent(it)
|
||||||
},
|
},
|
||||||
onSendMediaInfo = viewModel.mediaReportService::sendReportFor,
|
onSendMediaInfo = viewModel.mediaReportService::sendReportFor,
|
||||||
|
onClickDelete = {
|
||||||
|
showDeleteDialog = PositionItem(position, item)
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onDismissRequest = { moreDialog.makeAbsent() },
|
onDismissRequest = { moreDialog.makeAbsent() },
|
||||||
|
|
@ -751,6 +795,16 @@ fun CollectionFolderGrid(
|
||||||
elevation = 3.dp,
|
elevation = 3.dp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
showDeleteDialog?.let { (position, item) ->
|
||||||
|
ConfirmDeleteDialog(
|
||||||
|
itemTitle = listOfNotNull(item.title, item.subtitle).joinToString(" - "),
|
||||||
|
onCancel = { showDeleteDialog = null },
|
||||||
|
onConfirm = {
|
||||||
|
viewModel.deleteItem(position, item)
|
||||||
|
showDeleteDialog = null
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
|
|
|
||||||
|
|
@ -72,12 +72,14 @@ fun ExpandablePlayButtons(
|
||||||
resumePosition: Duration,
|
resumePosition: Duration,
|
||||||
watched: Boolean,
|
watched: Boolean,
|
||||||
favorite: Boolean,
|
favorite: Boolean,
|
||||||
|
canDelete: Boolean,
|
||||||
trailers: List<Trailer>?,
|
trailers: List<Trailer>?,
|
||||||
playOnClick: (position: Duration) -> Unit,
|
playOnClick: (position: Duration) -> Unit,
|
||||||
watchOnClick: () -> Unit,
|
watchOnClick: () -> Unit,
|
||||||
favoriteOnClick: () -> Unit,
|
favoriteOnClick: () -> Unit,
|
||||||
moreOnClick: () -> Unit,
|
moreOnClick: () -> Unit,
|
||||||
trailerOnClick: (Trailer) -> Unit,
|
trailerOnClick: (Trailer) -> Unit,
|
||||||
|
deleteOnClick: () -> Unit,
|
||||||
buttonOnFocusChanged: (FocusState) -> Unit,
|
buttonOnFocusChanged: (FocusState) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
|
|
@ -158,6 +160,16 @@ fun ExpandablePlayButtons(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (canDelete) {
|
||||||
|
item("delete") {
|
||||||
|
DeleteButton(
|
||||||
|
onClick = deleteOnClick,
|
||||||
|
modifier =
|
||||||
|
Modifier
|
||||||
|
.onFocusChanged(buttonOnFocusChanged),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// More button
|
// More button
|
||||||
item("more") {
|
item("more") {
|
||||||
|
|
@ -424,6 +436,8 @@ private fun ExpandablePlayButtonsPreview() {
|
||||||
buttonOnFocusChanged = {},
|
buttonOnFocusChanged = {},
|
||||||
trailers = listOf(),
|
trailers = listOf(),
|
||||||
trailerOnClick = {},
|
trailerOnClick = {},
|
||||||
|
canDelete = true,
|
||||||
|
deleteOnClick = {},
|
||||||
modifier = Modifier,
|
modifier = Modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -19,11 +19,14 @@ import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.github.damontecres.wholphin.R
|
import com.github.damontecres.wholphin.R
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
|
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
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.FavoriteWatchManager
|
||||||
|
import com.github.damontecres.wholphin.services.MediaManagementService
|
||||||
import com.github.damontecres.wholphin.services.MediaReportService
|
import com.github.damontecres.wholphin.services.MediaReportService
|
||||||
import com.github.damontecres.wholphin.services.NavigationManager
|
import com.github.damontecres.wholphin.services.NavigationManager
|
||||||
|
import com.github.damontecres.wholphin.services.deleteItem
|
||||||
import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
import com.github.damontecres.wholphin.ui.OneTimeLaunchedEffect
|
||||||
import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel
|
import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel
|
||||||
import com.github.damontecres.wholphin.ui.data.RowColumn
|
import com.github.damontecres.wholphin.ui.data.RowColumn
|
||||||
|
|
@ -31,6 +34,7 @@ import com.github.damontecres.wholphin.ui.detail.MoreDialogActions
|
||||||
import com.github.damontecres.wholphin.ui.detail.PlaylistDialog
|
import com.github.damontecres.wholphin.ui.detail.PlaylistDialog
|
||||||
import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState
|
import com.github.damontecres.wholphin.ui.detail.PlaylistLoadingState
|
||||||
import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForHome
|
import com.github.damontecres.wholphin.ui.detail.buildMoreDialogItemsForHome
|
||||||
|
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.main.HomePageContent
|
import com.github.damontecres.wholphin.ui.main.HomePageContent
|
||||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
|
|
@ -51,6 +55,7 @@ abstract class RecommendedViewModel(
|
||||||
val favoriteWatchManager: FavoriteWatchManager,
|
val favoriteWatchManager: FavoriteWatchManager,
|
||||||
val mediaReportService: MediaReportService,
|
val mediaReportService: MediaReportService,
|
||||||
private val backdropService: BackdropService,
|
private val backdropService: BackdropService,
|
||||||
|
private val mediaManagementService: MediaManagementService,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
abstract fun init()
|
abstract fun init()
|
||||||
|
|
||||||
|
|
@ -117,6 +122,25 @@ abstract class RecommendedViewModel(
|
||||||
}
|
}
|
||||||
update(title, row)
|
update(title, row)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteItem(
|
||||||
|
position: RowColumn,
|
||||||
|
item: BaseItem,
|
||||||
|
) {
|
||||||
|
deleteItem(context, mediaManagementService, item) {
|
||||||
|
viewModelScope.launchDefault {
|
||||||
|
val row = rows.value.getOrNull(position.row)
|
||||||
|
if (row is HomeRowLoadingState.Success) {
|
||||||
|
(row.items as? ApiRequestPager<*>)?.refreshPagesAfter(position.column)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun canDelete(
|
||||||
|
item: BaseItem,
|
||||||
|
appPreferences: AppPreferences,
|
||||||
|
): Boolean = mediaManagementService.canDelete(item, appPreferences)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -130,6 +154,7 @@ fun RecommendedContent(
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
var moreDialog by remember { mutableStateOf<Optional<RowColumnItem>>(Optional.absent()) }
|
var moreDialog by remember { mutableStateOf<Optional<RowColumnItem>>(Optional.absent()) }
|
||||||
var showPlaylistDialog by remember { mutableStateOf<Optional<UUID>>(Optional.absent()) }
|
var showPlaylistDialog by remember { mutableStateOf<Optional<UUID>>(Optional.absent()) }
|
||||||
|
var showDeleteDialog by remember { mutableStateOf<RowColumnItem?>(null) }
|
||||||
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
||||||
|
|
||||||
OneTimeLaunchedEffect {
|
OneTimeLaunchedEffect {
|
||||||
|
|
@ -196,6 +221,7 @@ fun RecommendedContent(
|
||||||
playbackPosition = item.playbackPosition,
|
playbackPosition = item.playbackPosition,
|
||||||
watched = item.played,
|
watched = item.played,
|
||||||
favorite = item.favorite,
|
favorite = item.favorite,
|
||||||
|
canDelete = viewModel.canDelete(item, preferences.appPreferences),
|
||||||
actions =
|
actions =
|
||||||
MoreDialogActions(
|
MoreDialogActions(
|
||||||
navigateTo = { viewModel.navigationManager.navigateTo(it) },
|
navigateTo = { viewModel.navigationManager.navigateTo(it) },
|
||||||
|
|
@ -210,6 +236,7 @@ fun RecommendedContent(
|
||||||
showPlaylistDialog.makePresent(it)
|
showPlaylistDialog.makePresent(it)
|
||||||
},
|
},
|
||||||
onSendMediaInfo = viewModel.mediaReportService::sendReportFor,
|
onSendMediaInfo = viewModel.mediaReportService::sendReportFor,
|
||||||
|
onClickDelete = { showDeleteDialog = RowColumnItem(position, item) },
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onDismissRequest = { moreDialog.makeAbsent() },
|
onDismissRequest = { moreDialog.makeAbsent() },
|
||||||
|
|
@ -234,9 +261,19 @@ fun RecommendedContent(
|
||||||
elevation = 3.dp,
|
elevation = 3.dp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
showDeleteDialog?.let { (position, item) ->
|
||||||
|
ConfirmDeleteDialog(
|
||||||
|
itemTitle = listOfNotNull(item.title, item.subtitle).joinToString(" - "),
|
||||||
|
onCancel = { showDeleteDialog = null },
|
||||||
|
onConfirm = {
|
||||||
|
viewModel.deleteItem(position, item)
|
||||||
|
showDeleteDialog = null
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private data class RowColumnItem(
|
data class RowColumnItem(
|
||||||
val position: RowColumn,
|
val position: RowColumn,
|
||||||
val item: BaseItem,
|
val item: BaseItem,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
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.FavoriteWatchManager
|
||||||
|
import com.github.damontecres.wholphin.services.MediaManagementService
|
||||||
import com.github.damontecres.wholphin.services.MediaReportService
|
import com.github.damontecres.wholphin.services.MediaReportService
|
||||||
import com.github.damontecres.wholphin.services.NavigationManager
|
import com.github.damontecres.wholphin.services.NavigationManager
|
||||||
import com.github.damontecres.wholphin.services.SuggestionService
|
import com.github.damontecres.wholphin.services.SuggestionService
|
||||||
|
|
@ -63,12 +64,14 @@ class RecommendedMovieViewModel
|
||||||
favoriteWatchManager: FavoriteWatchManager,
|
favoriteWatchManager: FavoriteWatchManager,
|
||||||
mediaReportService: MediaReportService,
|
mediaReportService: MediaReportService,
|
||||||
backdropService: BackdropService,
|
backdropService: BackdropService,
|
||||||
|
mediaManagementService: MediaManagementService,
|
||||||
) : RecommendedViewModel(
|
) : RecommendedViewModel(
|
||||||
context,
|
context,
|
||||||
navigationManager,
|
navigationManager,
|
||||||
favoriteWatchManager,
|
favoriteWatchManager,
|
||||||
mediaReportService,
|
mediaReportService,
|
||||||
backdropService,
|
backdropService,
|
||||||
|
mediaManagementService,
|
||||||
) {
|
) {
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
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.FavoriteWatchManager
|
||||||
import com.github.damontecres.wholphin.services.LatestNextUpService
|
import com.github.damontecres.wholphin.services.LatestNextUpService
|
||||||
|
import com.github.damontecres.wholphin.services.MediaManagementService
|
||||||
import com.github.damontecres.wholphin.services.MediaReportService
|
import com.github.damontecres.wholphin.services.MediaReportService
|
||||||
import com.github.damontecres.wholphin.services.NavigationManager
|
import com.github.damontecres.wholphin.services.NavigationManager
|
||||||
import com.github.damontecres.wholphin.services.SuggestionService
|
import com.github.damontecres.wholphin.services.SuggestionService
|
||||||
|
|
@ -67,12 +68,14 @@ class RecommendedTvShowViewModel
|
||||||
favoriteWatchManager: FavoriteWatchManager,
|
favoriteWatchManager: FavoriteWatchManager,
|
||||||
mediaReportService: MediaReportService,
|
mediaReportService: MediaReportService,
|
||||||
backdropService: BackdropService,
|
backdropService: BackdropService,
|
||||||
|
mediaManagementService: MediaManagementService,
|
||||||
) : RecommendedViewModel(
|
) : RecommendedViewModel(
|
||||||
context,
|
context,
|
||||||
navigationManager,
|
navigationManager,
|
||||||
favoriteWatchManager,
|
favoriteWatchManager,
|
||||||
mediaReportService,
|
mediaReportService,
|
||||||
backdropService,
|
backdropService,
|
||||||
|
mediaManagementService,
|
||||||
) {
|
) {
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,7 @@ data class MoreDialogActions(
|
||||||
val onClickFavorite: (UUID, Boolean) -> Unit,
|
val onClickFavorite: (UUID, Boolean) -> Unit,
|
||||||
val onClickAddPlaylist: (UUID) -> Unit,
|
val onClickAddPlaylist: (UUID) -> Unit,
|
||||||
val onSendMediaInfo: (UUID) -> Unit,
|
val onSendMediaInfo: (UUID) -> Unit,
|
||||||
val onClickDelete: (BaseItem) -> Unit = {},
|
val onClickDelete: (BaseItem) -> Unit,
|
||||||
)
|
)
|
||||||
|
|
||||||
enum class ClearChosenStreams {
|
enum class ClearChosenStreams {
|
||||||
|
|
@ -63,7 +63,7 @@ fun buildMoreDialogItems(
|
||||||
watched: Boolean,
|
watched: Boolean,
|
||||||
favorite: Boolean,
|
favorite: Boolean,
|
||||||
canClearChosenStreams: Boolean,
|
canClearChosenStreams: Boolean,
|
||||||
canDelete: Boolean = false,
|
canDelete: Boolean,
|
||||||
actions: MoreDialogActions,
|
actions: MoreDialogActions,
|
||||||
onChooseVersion: () -> Unit,
|
onChooseVersion: () -> Unit,
|
||||||
onChooseTracks: (MediaStreamType) -> Unit,
|
onChooseTracks: (MediaStreamType) -> Unit,
|
||||||
|
|
@ -236,7 +236,7 @@ fun buildMoreDialogItemsForHome(
|
||||||
playbackPosition: Duration,
|
playbackPosition: Duration,
|
||||||
watched: Boolean,
|
watched: Boolean,
|
||||||
favorite: Boolean,
|
favorite: Boolean,
|
||||||
canDelete: Boolean = false,
|
canDelete: Boolean,
|
||||||
actions: MoreDialogActions,
|
actions: MoreDialogActions,
|
||||||
): List<DialogItem> =
|
): List<DialogItem> =
|
||||||
buildList {
|
buildList {
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,6 @@ import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||||
import com.github.damontecres.wholphin.ui.data.ItemDetailsDialog
|
import com.github.damontecres.wholphin.ui.data.ItemDetailsDialog
|
||||||
import com.github.damontecres.wholphin.ui.data.ItemDetailsDialogInfo
|
import com.github.damontecres.wholphin.ui.data.ItemDetailsDialogInfo
|
||||||
import com.github.damontecres.wholphin.ui.detail.MoreDialogActions
|
|
||||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
import com.github.damontecres.wholphin.ui.rememberInt
|
import com.github.damontecres.wholphin.ui.rememberInt
|
||||||
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
import com.github.damontecres.wholphin.ui.tryRequestFocus
|
||||||
|
|
@ -102,15 +101,6 @@ fun DiscoverMovieDetails(
|
||||||
val requestStr = stringResource(R.string.request)
|
val requestStr = stringResource(R.string.request)
|
||||||
val request4kStr = stringResource(R.string.request_4k)
|
val request4kStr = stringResource(R.string.request_4k)
|
||||||
|
|
||||||
val moreActions =
|
|
||||||
MoreDialogActions(
|
|
||||||
navigateTo = viewModel::navigateTo,
|
|
||||||
onClickWatch = { itemId, watched -> },
|
|
||||||
onClickFavorite = { itemId, favorite -> },
|
|
||||||
onClickAddPlaylist = { itemId -> },
|
|
||||||
onSendMediaInfo = {},
|
|
||||||
)
|
|
||||||
|
|
||||||
when (val state = loading) {
|
when (val state = loading) {
|
||||||
is LoadingState.Error -> {
|
is LoadingState.Error -> {
|
||||||
ErrorMessage(state, modifier)
|
ErrorMessage(state, modifier)
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ import com.github.damontecres.wholphin.data.ChosenStreams
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||||
import com.github.damontecres.wholphin.ui.RequestOrRestoreFocus
|
import com.github.damontecres.wholphin.ui.RequestOrRestoreFocus
|
||||||
|
import com.github.damontecres.wholphin.ui.components.ConfirmDeleteDialog
|
||||||
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
|
||||||
|
|
@ -82,6 +83,7 @@ fun EpisodeDetails(
|
||||||
var moreDialog by remember { mutableStateOf<DialogParams?>(null) }
|
var moreDialog by remember { mutableStateOf<DialogParams?>(null) }
|
||||||
var chooseVersion by remember { mutableStateOf<DialogParams?>(null) }
|
var chooseVersion by remember { mutableStateOf<DialogParams?>(null) }
|
||||||
var showPlaylistDialog by remember { mutableStateOf<Optional<UUID>>(Optional.absent()) }
|
var showPlaylistDialog by remember { mutableStateOf<Optional<UUID>>(Optional.absent()) }
|
||||||
|
var showDeleteDialog by remember { mutableStateOf<BaseItem?>(null) }
|
||||||
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
||||||
|
|
||||||
val moreActions =
|
val moreActions =
|
||||||
|
|
@ -98,6 +100,7 @@ fun EpisodeDetails(
|
||||||
showPlaylistDialog.makePresent(itemId)
|
showPlaylistDialog.makePresent(itemId)
|
||||||
},
|
},
|
||||||
onSendMediaInfo = viewModel.mediaReportService::sendReportFor,
|
onSendMediaInfo = viewModel.mediaReportService::sendReportFor,
|
||||||
|
onClickDelete = { showDeleteDialog = it },
|
||||||
)
|
)
|
||||||
|
|
||||||
when (val state = loading) {
|
when (val state = loading) {
|
||||||
|
|
@ -215,6 +218,7 @@ fun EpisodeDetails(
|
||||||
onClearChosenStreams = {
|
onClearChosenStreams = {
|
||||||
viewModel.clearChosenStreams(chosenStreams)
|
viewModel.clearChosenStreams(chosenStreams)
|
||||||
},
|
},
|
||||||
|
canDelete = viewModel.canDelete,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
@ -224,6 +228,8 @@ fun EpisodeDetails(
|
||||||
favoriteOnClick = {
|
favoriteOnClick = {
|
||||||
viewModel.setFavorite(ep.id, !ep.favorite)
|
viewModel.setFavorite(ep.id, !ep.favorite)
|
||||||
},
|
},
|
||||||
|
canDelete = viewModel.canDelete,
|
||||||
|
deleteOnClick = { showDeleteDialog = ep },
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -276,6 +282,16 @@ fun EpisodeDetails(
|
||||||
elevation = 3.dp,
|
elevation = 3.dp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
showDeleteDialog?.let { item ->
|
||||||
|
ConfirmDeleteDialog(
|
||||||
|
itemTitle = listOfNotNull(item.title, item.subtitle).joinToString(" - "),
|
||||||
|
onCancel = { showDeleteDialog = null },
|
||||||
|
onConfirm = {
|
||||||
|
viewModel.deleteItem(item)
|
||||||
|
showDeleteDialog = null
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private const val HEADER_ROW = 0
|
private const val HEADER_ROW = 0
|
||||||
|
|
@ -290,6 +306,8 @@ fun EpisodeDetailsContent(
|
||||||
watchOnClick: () -> Unit,
|
watchOnClick: () -> Unit,
|
||||||
favoriteOnClick: () -> Unit,
|
favoriteOnClick: () -> Unit,
|
||||||
moreOnClick: () -> Unit,
|
moreOnClick: () -> Unit,
|
||||||
|
canDelete: Boolean,
|
||||||
|
deleteOnClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
@ -347,6 +365,8 @@ fun EpisodeDetailsContent(
|
||||||
},
|
},
|
||||||
trailers = null,
|
trailers = null,
|
||||||
trailerOnClick = {},
|
trailerOnClick = {},
|
||||||
|
canDelete = canDelete,
|
||||||
|
deleteOnClick = deleteOnClick,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|
|
||||||
|
|
@ -12,11 +12,13 @@ import com.github.damontecres.wholphin.data.model.ItemPlayback
|
||||||
import com.github.damontecres.wholphin.preferences.ThemeSongVolume
|
import com.github.damontecres.wholphin.preferences.ThemeSongVolume
|
||||||
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.FavoriteWatchManager
|
||||||
|
import com.github.damontecres.wholphin.services.MediaManagementService
|
||||||
import com.github.damontecres.wholphin.services.MediaReportService
|
import com.github.damontecres.wholphin.services.MediaReportService
|
||||||
import com.github.damontecres.wholphin.services.NavigationManager
|
import com.github.damontecres.wholphin.services.NavigationManager
|
||||||
import com.github.damontecres.wholphin.services.StreamChoiceService
|
import com.github.damontecres.wholphin.services.StreamChoiceService
|
||||||
import com.github.damontecres.wholphin.services.ThemeSongPlayer
|
import com.github.damontecres.wholphin.services.ThemeSongPlayer
|
||||||
import com.github.damontecres.wholphin.services.UserPreferencesService
|
import com.github.damontecres.wholphin.services.UserPreferencesService
|
||||||
|
import com.github.damontecres.wholphin.services.deleteItem
|
||||||
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.setValueOnMain
|
import com.github.damontecres.wholphin.ui.setValueOnMain
|
||||||
|
|
@ -54,6 +56,7 @@ class EpisodeViewModel
|
||||||
private val favoriteWatchManager: FavoriteWatchManager,
|
private val favoriteWatchManager: FavoriteWatchManager,
|
||||||
private val userPreferencesService: UserPreferencesService,
|
private val userPreferencesService: UserPreferencesService,
|
||||||
private val backdropService: BackdropService,
|
private val backdropService: BackdropService,
|
||||||
|
private val mediaManagementService: MediaManagementService,
|
||||||
@Assisted val itemId: UUID,
|
@Assisted val itemId: UUID,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
|
|
@ -65,6 +68,9 @@ class EpisodeViewModel
|
||||||
val item = MutableLiveData<BaseItem?>(null)
|
val item = MutableLiveData<BaseItem?>(null)
|
||||||
val chosenStreams = MutableLiveData<ChosenStreams?>(null)
|
val chosenStreams = MutableLiveData<ChosenStreams?>(null)
|
||||||
|
|
||||||
|
var canDelete: Boolean = false
|
||||||
|
private set
|
||||||
|
|
||||||
init {
|
init {
|
||||||
init()
|
init()
|
||||||
}
|
}
|
||||||
|
|
@ -95,6 +101,7 @@ class EpisodeViewModel
|
||||||
) {
|
) {
|
||||||
val prefs = userPreferencesService.getCurrent()
|
val prefs = userPreferencesService.getCurrent()
|
||||||
val item = fetchAndSetItem().await()
|
val item = fetchAndSetItem().await()
|
||||||
|
canDelete = mediaManagementService.canDelete(item)
|
||||||
val result = itemPlaybackRepository.getSelectedTracks(item.id, item, prefs)
|
val result = itemPlaybackRepository.getSelectedTracks(item.id, item, prefs)
|
||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
this@EpisodeViewModel.item.value = item
|
this@EpisodeViewModel.item.value = item
|
||||||
|
|
@ -199,4 +206,10 @@ class EpisodeViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteItem(item: BaseItem) {
|
||||||
|
deleteItem(context, mediaManagementService, item) {
|
||||||
|
navigationManager.goBack()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -315,6 +315,8 @@ fun MovieDetails(
|
||||||
onClickDiscover = { index, item ->
|
onClickDiscover = { index, item ->
|
||||||
viewModel.navigateTo(item.destination)
|
viewModel.navigateTo(item.destination)
|
||||||
},
|
},
|
||||||
|
canDelete = viewModel.canDelete,
|
||||||
|
deleteOnClick = { showDeleteDialog = movie },
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -410,6 +412,8 @@ fun MovieDetailsContent(
|
||||||
onLongClickSimilar: (Int, BaseItem) -> Unit,
|
onLongClickSimilar: (Int, BaseItem) -> Unit,
|
||||||
onClickExtra: (Int, ExtrasItem) -> Unit,
|
onClickExtra: (Int, ExtrasItem) -> Unit,
|
||||||
onClickDiscover: (Int, DiscoverItem) -> Unit,
|
onClickDiscover: (Int, DiscoverItem) -> Unit,
|
||||||
|
canDelete: Boolean,
|
||||||
|
deleteOnClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
@ -472,6 +476,8 @@ fun MovieDetailsContent(
|
||||||
position = TRAILER_ROW
|
position = TRAILER_ROW
|
||||||
trailerOnClick.invoke(it)
|
trailerOnClick.invoke(it)
|
||||||
},
|
},
|
||||||
|
canDelete = canDelete,
|
||||||
|
deleteOnClick = deleteOnClick,
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,8 @@ fun FocusedEpisodeFooter(
|
||||||
moreOnClick: () -> Unit,
|
moreOnClick: () -> Unit,
|
||||||
watchOnClick: () -> Unit,
|
watchOnClick: () -> Unit,
|
||||||
favoriteOnClick: () -> Unit,
|
favoriteOnClick: () -> Unit,
|
||||||
|
canDelete: Boolean,
|
||||||
|
deleteOnClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
buttonOnFocusChanged: (FocusState) -> Unit = {},
|
buttonOnFocusChanged: (FocusState) -> Unit = {},
|
||||||
) {
|
) {
|
||||||
|
|
@ -47,6 +49,8 @@ fun FocusedEpisodeFooter(
|
||||||
buttonOnFocusChanged = buttonOnFocusChanged,
|
buttonOnFocusChanged = buttonOnFocusChanged,
|
||||||
trailers = null,
|
trailers = null,
|
||||||
trailerOnClick = {},
|
trailerOnClick = {},
|
||||||
|
canDelete = canDelete,
|
||||||
|
deleteOnClick = deleteOnClick,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -584,6 +584,7 @@ fun SeriesDetailsContent(
|
||||||
watched = item.played,
|
watched = item.played,
|
||||||
favorite = item.favorite,
|
favorite = item.favorite,
|
||||||
actions = moreActions,
|
actions = moreActions,
|
||||||
|
canDelete = false,
|
||||||
)
|
)
|
||||||
moreDialog =
|
moreDialog =
|
||||||
DialogParams(
|
DialogParams(
|
||||||
|
|
|
||||||
|
|
@ -383,6 +383,8 @@ fun SeriesOverview(
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
canDelete = { viewModel.canDelete(it, preferences.appPreferences) },
|
||||||
|
deleteOnClick = { showDeleteDialog = it },
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,8 @@ fun SeriesOverviewContent(
|
||||||
moreOnClick: () -> Unit,
|
moreOnClick: () -> Unit,
|
||||||
overviewOnClick: () -> Unit,
|
overviewOnClick: () -> Unit,
|
||||||
personOnClick: (Person) -> Unit,
|
personOnClick: (Person) -> Unit,
|
||||||
|
canDelete: (BaseItem) -> Boolean,
|
||||||
|
deleteOnClick: (BaseItem) -> Unit,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
@ -294,6 +296,8 @@ fun SeriesOverviewContent(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
canDelete = canDelete.invoke(ep),
|
||||||
|
deleteOnClick = { deleteOnClick.invoke(ep) },
|
||||||
modifier =
|
modifier =
|
||||||
Modifier
|
Modifier
|
||||||
.padding(top = 4.dp)
|
.padding(top = 4.dp)
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import com.github.damontecres.wholphin.data.model.DiscoverItem
|
||||||
import com.github.damontecres.wholphin.data.model.ItemPlayback
|
import com.github.damontecres.wholphin.data.model.ItemPlayback
|
||||||
import com.github.damontecres.wholphin.data.model.Person
|
import com.github.damontecres.wholphin.data.model.Person
|
||||||
import com.github.damontecres.wholphin.data.model.Trailer
|
import com.github.damontecres.wholphin.data.model.Trailer
|
||||||
|
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||||
import com.github.damontecres.wholphin.services.BackdropService
|
import com.github.damontecres.wholphin.services.BackdropService
|
||||||
import com.github.damontecres.wholphin.services.ExtrasService
|
import com.github.damontecres.wholphin.services.ExtrasService
|
||||||
import com.github.damontecres.wholphin.services.FavoriteWatchManager
|
import com.github.damontecres.wholphin.services.FavoriteWatchManager
|
||||||
|
|
@ -56,6 +57,9 @@ import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
|
@ -229,6 +233,20 @@ class SeriesViewModel
|
||||||
discovered.update { results }
|
discovered.update { results }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
mediaManagementService.deletedItemFlow
|
||||||
|
.onEach { deletedItem ->
|
||||||
|
if (deletedItem.item.data.seriesId == seriesId) {
|
||||||
|
Timber.d(
|
||||||
|
"Item %s deleted from series %s",
|
||||||
|
deletedItem.item.id,
|
||||||
|
seriesId,
|
||||||
|
)
|
||||||
|
val seasons = getSeasons(item, seasonEpisodeIds?.seasonNumber).await()
|
||||||
|
this@SeriesViewModel.seasons.setValueOnMain(seasons)
|
||||||
|
}
|
||||||
|
}.catch { ex ->
|
||||||
|
Timber.e(ex, "Error refreshing after deleted item")
|
||||||
|
}.launchIn(viewModelScope)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -614,6 +632,11 @@ class SeriesViewModel
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun canDelete(item: BaseItem): Boolean = mediaManagementService.canDelete(item)
|
suspend fun canDelete(item: BaseItem): Boolean = mediaManagementService.canDelete(item)
|
||||||
|
|
||||||
|
fun canDelete(
|
||||||
|
item: BaseItem,
|
||||||
|
appPreferences: AppPreferences,
|
||||||
|
): Boolean = mediaManagementService.canDelete(item, appPreferences)
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed interface EpisodeList {
|
sealed interface EpisodeList {
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ import com.github.damontecres.wholphin.ui.cards.BannerCardWithTitle
|
||||||
import com.github.damontecres.wholphin.ui.cards.GenreCard
|
import com.github.damontecres.wholphin.ui.cards.GenreCard
|
||||||
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
||||||
import com.github.damontecres.wholphin.ui.components.CircularProgress
|
import com.github.damontecres.wholphin.ui.components.CircularProgress
|
||||||
|
import com.github.damontecres.wholphin.ui.components.ConfirmDeleteDialog
|
||||||
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.EpisodeName
|
import com.github.damontecres.wholphin.ui.components.EpisodeName
|
||||||
|
|
@ -62,6 +63,7 @@ import com.github.damontecres.wholphin.ui.components.ErrorMessage
|
||||||
import com.github.damontecres.wholphin.ui.components.FocusableItemRow
|
import com.github.damontecres.wholphin.ui.components.FocusableItemRow
|
||||||
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
import com.github.damontecres.wholphin.ui.components.LoadingPage
|
||||||
import com.github.damontecres.wholphin.ui.components.QuickDetails
|
import com.github.damontecres.wholphin.ui.components.QuickDetails
|
||||||
|
import com.github.damontecres.wholphin.ui.components.RowColumnItem
|
||||||
import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel
|
import com.github.damontecres.wholphin.ui.data.AddPlaylistViewModel
|
||||||
import com.github.damontecres.wholphin.ui.data.RowColumn
|
import com.github.damontecres.wholphin.ui.data.RowColumn
|
||||||
import com.github.damontecres.wholphin.ui.detail.MoreDialogActions
|
import com.github.damontecres.wholphin.ui.detail.MoreDialogActions
|
||||||
|
|
@ -116,6 +118,7 @@ fun HomePage(
|
||||||
LoadingState.Success -> {
|
LoadingState.Success -> {
|
||||||
var dialog by remember { mutableStateOf<DialogParams?>(null) }
|
var dialog by remember { mutableStateOf<DialogParams?>(null) }
|
||||||
var showPlaylistDialog by remember { mutableStateOf<UUID?>(null) }
|
var showPlaylistDialog by remember { mutableStateOf<UUID?>(null) }
|
||||||
|
var showDeleteDialog by remember { mutableStateOf<RowColumnItem?>(null) }
|
||||||
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
||||||
var position by rememberPosition()
|
var position by rememberPosition()
|
||||||
HomePageContent(
|
HomePageContent(
|
||||||
|
|
@ -136,6 +139,7 @@ fun HomePage(
|
||||||
playbackPosition = item.playbackPosition,
|
playbackPosition = item.playbackPosition,
|
||||||
watched = item.played,
|
watched = item.played,
|
||||||
favorite = item.favorite,
|
favorite = item.favorite,
|
||||||
|
canDelete = viewModel.canDelete(item, preferences.appPreferences),
|
||||||
actions =
|
actions =
|
||||||
MoreDialogActions(
|
MoreDialogActions(
|
||||||
navigateTo = viewModel.navigationManager::navigateTo,
|
navigateTo = viewModel.navigationManager::navigateTo,
|
||||||
|
|
@ -150,6 +154,9 @@ fun HomePage(
|
||||||
showPlaylistDialog = itemId
|
showPlaylistDialog = itemId
|
||||||
},
|
},
|
||||||
onSendMediaInfo = viewModel.mediaReportService::sendReportFor,
|
onSendMediaInfo = viewModel.mediaReportService::sendReportFor,
|
||||||
|
onClickDelete = {
|
||||||
|
showDeleteDialog = RowColumnItem(position, item)
|
||||||
|
},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
dialog =
|
dialog =
|
||||||
|
|
@ -190,6 +197,16 @@ fun HomePage(
|
||||||
elevation = 3.dp,
|
elevation = 3.dp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
showDeleteDialog?.let { (position, item) ->
|
||||||
|
ConfirmDeleteDialog(
|
||||||
|
itemTitle = listOfNotNull(item.title, item.subtitle).joinToString(" - "),
|
||||||
|
onCancel = { showDeleteDialog = null },
|
||||||
|
onConfirm = {
|
||||||
|
viewModel.deleteItem(position, item)
|
||||||
|
showDeleteDialog = null
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,16 +6,21 @@ import androidx.lifecycle.viewModelScope
|
||||||
import com.github.damontecres.wholphin.data.ServerRepository
|
import com.github.damontecres.wholphin.data.ServerRepository
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
import com.github.damontecres.wholphin.data.model.BaseItem
|
||||||
import com.github.damontecres.wholphin.data.model.HomeRowConfig
|
import com.github.damontecres.wholphin.data.model.HomeRowConfig
|
||||||
|
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||||
import com.github.damontecres.wholphin.services.BackdropService
|
import com.github.damontecres.wholphin.services.BackdropService
|
||||||
import com.github.damontecres.wholphin.services.DatePlayedService
|
import com.github.damontecres.wholphin.services.DatePlayedService
|
||||||
import com.github.damontecres.wholphin.services.FavoriteWatchManager
|
import com.github.damontecres.wholphin.services.FavoriteWatchManager
|
||||||
import com.github.damontecres.wholphin.services.HomePageResolvedSettings
|
import com.github.damontecres.wholphin.services.HomePageResolvedSettings
|
||||||
import com.github.damontecres.wholphin.services.HomeSettingsService
|
import com.github.damontecres.wholphin.services.HomeSettingsService
|
||||||
|
import com.github.damontecres.wholphin.services.MediaManagementService
|
||||||
import com.github.damontecres.wholphin.services.MediaReportService
|
import com.github.damontecres.wholphin.services.MediaReportService
|
||||||
import com.github.damontecres.wholphin.services.NavDrawerService
|
import com.github.damontecres.wholphin.services.NavDrawerService
|
||||||
import com.github.damontecres.wholphin.services.NavigationManager
|
import com.github.damontecres.wholphin.services.NavigationManager
|
||||||
import com.github.damontecres.wholphin.services.UserPreferencesService
|
import com.github.damontecres.wholphin.services.UserPreferencesService
|
||||||
|
import com.github.damontecres.wholphin.services.deleteItem
|
||||||
import com.github.damontecres.wholphin.services.tvAccess
|
import com.github.damontecres.wholphin.services.tvAccess
|
||||||
|
import com.github.damontecres.wholphin.ui.data.RowColumn
|
||||||
|
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.showToast
|
import com.github.damontecres.wholphin.ui.showToast
|
||||||
import com.github.damontecres.wholphin.util.ExceptionHandler
|
import com.github.damontecres.wholphin.util.ExceptionHandler
|
||||||
|
|
@ -52,6 +57,7 @@ class HomeViewModel
|
||||||
private val datePlayedService: DatePlayedService,
|
private val datePlayedService: DatePlayedService,
|
||||||
private val backdropService: BackdropService,
|
private val backdropService: BackdropService,
|
||||||
private val userPreferencesService: UserPreferencesService,
|
private val userPreferencesService: UserPreferencesService,
|
||||||
|
private val mediaManagementService: MediaManagementService,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
private val _state = MutableStateFlow(HomeState.EMPTY)
|
private val _state = MutableStateFlow(HomeState.EMPTY)
|
||||||
val state: StateFlow<HomeState> = _state
|
val state: StateFlow<HomeState> = _state
|
||||||
|
|
@ -189,6 +195,36 @@ class HomeViewModel
|
||||||
backdropService.submit(item)
|
backdropService.submit(item)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun deleteItem(
|
||||||
|
position: RowColumn,
|
||||||
|
item: BaseItem,
|
||||||
|
) {
|
||||||
|
deleteItem(context, mediaManagementService, item) {
|
||||||
|
viewModelScope.launchDefault {
|
||||||
|
val row = state.value.homeRows.getOrNull(position.row)
|
||||||
|
if (row is HomeRowLoadingState.Success) {
|
||||||
|
_state.update {
|
||||||
|
val newRow =
|
||||||
|
row.items.toMutableList().apply {
|
||||||
|
removeAt(position.column)
|
||||||
|
}
|
||||||
|
it.copy(
|
||||||
|
homeRows =
|
||||||
|
it.homeRows.toMutableList().apply {
|
||||||
|
set(position.row, row.copy(items = newRow))
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun canDelete(
|
||||||
|
item: BaseItem,
|
||||||
|
appPreferences: AppPreferences,
|
||||||
|
): Boolean = mediaManagementService.canDelete(item, appPreferences)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class HomeState(
|
data class HomeState(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue