mirror of
https://github.com/JustinZeus/Wholphin.git
synced 2026-07-08 15:50:16 +02:00
Support delete
This commit is contained in:
parent
803780894c
commit
64c6e4db79
6 changed files with 216 additions and 22 deletions
|
|
@ -1,5 +1,6 @@
|
||||||
package com.github.damontecres.wholphin.ui.detail
|
package com.github.damontecres.wholphin.ui.detail
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.focusGroup
|
import androidx.compose.foundation.focusGroup
|
||||||
import androidx.compose.foundation.focusable
|
import androidx.compose.foundation.focusable
|
||||||
|
|
@ -60,8 +61,10 @@ import com.github.damontecres.wholphin.data.filter.ItemFilterBy
|
||||||
import com.github.damontecres.wholphin.data.model.BaseItem
|
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.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.MusicService
|
import com.github.damontecres.wholphin.services.MusicService
|
||||||
import com.github.damontecres.wholphin.services.MusicServiceState
|
import com.github.damontecres.wholphin.services.MusicServiceState
|
||||||
|
|
@ -70,6 +73,7 @@ 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.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
|
||||||
|
|
@ -108,6 +112,7 @@ import dagger.assisted.Assisted
|
||||||
import dagger.assisted.AssistedFactory
|
import dagger.assisted.AssistedFactory
|
||||||
import dagger.assisted.AssistedInject
|
import dagger.assisted.AssistedInject
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
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
|
||||||
|
|
@ -128,16 +133,18 @@ import kotlin.time.Duration
|
||||||
class PlaylistViewModel
|
class PlaylistViewModel
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
constructor(
|
constructor(
|
||||||
|
@ApplicationContext context: Context,
|
||||||
api: ApiClient,
|
api: ApiClient,
|
||||||
navigationManager: NavigationManager,
|
navigationManager: NavigationManager,
|
||||||
musicService: MusicService,
|
musicService: MusicService,
|
||||||
|
mediaManagementService: MediaManagementService,
|
||||||
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 favoriteWatchManager: FavoriteWatchManager,
|
private val favoriteWatchManager: FavoriteWatchManager,
|
||||||
private val mediaReportService: MediaReportService,
|
private val mediaReportService: MediaReportService,
|
||||||
@Assisted val itemId: UUID,
|
@Assisted itemId: UUID,
|
||||||
) : MusicViewModel(api, musicService, navigationManager) {
|
) : MusicViewModel(itemId, context, api, musicService, navigationManager, mediaManagementService) {
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
fun create(itemId: UUID): PlaylistViewModel
|
fun create(itemId: UUID): PlaylistViewModel
|
||||||
|
|
@ -147,6 +154,10 @@ class PlaylistViewModel
|
||||||
val musicState = musicService.state
|
val musicState = musicService.state
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun init() {
|
||||||
state.update { it.copy(loading = LoadingState.Loading) }
|
state.update { it.copy(loading = LoadingState.Loading) }
|
||||||
viewModelScope.launchDefault {
|
viewModelScope.launchDefault {
|
||||||
try {
|
try {
|
||||||
|
|
@ -347,6 +358,7 @@ data class PlaylistDetailsState(
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PlaylistDetails(
|
fun PlaylistDetails(
|
||||||
|
preferences: UserPreferences,
|
||||||
destination: Destination.MediaItem,
|
destination: Destination.MediaItem,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: PlaylistViewModel =
|
viewModel: PlaylistViewModel =
|
||||||
|
|
@ -392,7 +404,7 @@ fun PlaylistDetails(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var showDeleteDialog by remember { mutableStateOf<BaseItem?>(null) }
|
||||||
val musicMoreActions =
|
val musicMoreActions =
|
||||||
MusicMoreDialogActions(
|
MusicMoreDialogActions(
|
||||||
onNavigate = { viewModel.navigationManager.navigateTo(it) },
|
onNavigate = { viewModel.navigationManager.navigateTo(it) },
|
||||||
|
|
@ -405,6 +417,7 @@ fun PlaylistDetails(
|
||||||
showPlaylistDialog.makePresent(itemId)
|
showPlaylistDialog.makePresent(itemId)
|
||||||
},
|
},
|
||||||
onClickRemoveFromQueue = {},
|
onClickRemoveFromQueue = {},
|
||||||
|
onClickDelete = { showDeleteDialog = it },
|
||||||
)
|
)
|
||||||
val moreActions =
|
val moreActions =
|
||||||
MoreDialogActions(
|
MoreDialogActions(
|
||||||
|
|
@ -416,7 +429,7 @@ fun PlaylistDetails(
|
||||||
showPlaylistDialog.makePresent(itemId)
|
showPlaylistDialog.makePresent(itemId)
|
||||||
},
|
},
|
||||||
onSendMediaInfo = viewModel::sendMediaReport,
|
onSendMediaInfo = viewModel::sendMediaReport,
|
||||||
onClickDelete = {},
|
onClickDelete = { showDeleteDialog = it },
|
||||||
)
|
)
|
||||||
|
|
||||||
PlaylistDetailsContent(
|
PlaylistDetailsContent(
|
||||||
|
|
@ -446,6 +459,7 @@ fun PlaylistDetails(
|
||||||
item = item,
|
item = item,
|
||||||
index = index,
|
index = index,
|
||||||
canRemove = false,
|
canRemove = false,
|
||||||
|
canDelete = viewModel.canDelete(item, preferences.appPreferences),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
buildMoreDialogItemsForHome(
|
buildMoreDialogItemsForHome(
|
||||||
|
|
@ -455,7 +469,7 @@ fun PlaylistDetails(
|
||||||
playbackPosition = item.playbackPosition,
|
playbackPosition = item.playbackPosition,
|
||||||
watched = item.played,
|
watched = item.played,
|
||||||
favorite = item.favorite,
|
favorite = item.favorite,
|
||||||
canDelete = false,
|
canDelete = viewModel.canDelete(item, preferences.appPreferences),
|
||||||
actions = moreActions,
|
actions = moreActions,
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
@ -495,6 +509,16 @@ fun PlaylistDetails(
|
||||||
elevation = 3.dp,
|
elevation = 3.dp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
showDeleteDialog?.let { item ->
|
||||||
|
ConfirmDeleteDialog(
|
||||||
|
itemTitle = item.title ?: "",
|
||||||
|
onCancel = { showDeleteDialog = null },
|
||||||
|
onConfirm = {
|
||||||
|
viewModel.deleteItem(item)
|
||||||
|
showDeleteDialog = null
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,7 @@ import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.focus.onFocusChanged
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
|
@ -46,9 +47,11 @@ import coil3.compose.AsyncImage
|
||||||
import com.github.damontecres.wholphin.R
|
import com.github.damontecres.wholphin.R
|
||||||
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.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.ImageUrlService
|
import com.github.damontecres.wholphin.services.ImageUrlService
|
||||||
|
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.MusicService
|
import com.github.damontecres.wholphin.services.MusicService
|
||||||
import com.github.damontecres.wholphin.services.NavigationManager
|
import com.github.damontecres.wholphin.services.NavigationManager
|
||||||
|
|
@ -58,6 +61,7 @@ import com.github.damontecres.wholphin.ui.DefaultItemFields
|
||||||
import com.github.damontecres.wholphin.ui.SlimItemFields
|
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||||
import com.github.damontecres.wholphin.ui.cards.BannerCardWithTitle
|
import com.github.damontecres.wholphin.ui.cards.BannerCardWithTitle
|
||||||
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
||||||
|
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
|
||||||
|
|
@ -107,18 +111,19 @@ import java.util.UUID
|
||||||
class AlbumViewModel
|
class AlbumViewModel
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
constructor(
|
constructor(
|
||||||
|
@ApplicationContext context: Context,
|
||||||
api: ApiClient,
|
api: ApiClient,
|
||||||
musicService: MusicService,
|
musicService: MusicService,
|
||||||
navigationManager: NavigationManager,
|
navigationManager: NavigationManager,
|
||||||
@param:ApplicationContext private val context: Context,
|
mediaManagementService: MediaManagementService,
|
||||||
val serverRepository: ServerRepository,
|
val serverRepository: ServerRepository,
|
||||||
val mediaReportService: MediaReportService,
|
val mediaReportService: MediaReportService,
|
||||||
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 imageUrlService: ImageUrlService,
|
private val imageUrlService: ImageUrlService,
|
||||||
@Assisted val itemId: UUID,
|
@Assisted itemId: UUID,
|
||||||
) : MusicViewModel(api, musicService, navigationManager) {
|
) : MusicViewModel(itemId, context, api, musicService, navigationManager, mediaManagementService) {
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
fun create(itemId: UUID): AlbumViewModel
|
fun create(itemId: UUID): AlbumViewModel
|
||||||
|
|
@ -130,6 +135,10 @@ class AlbumViewModel
|
||||||
val currentMusic = musicService.state
|
val currentMusic = musicService.state
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun init() {
|
||||||
viewModelScope.launchIO {
|
viewModelScope.launchIO {
|
||||||
try {
|
try {
|
||||||
val itemDeferred =
|
val itemDeferred =
|
||||||
|
|
@ -280,6 +289,7 @@ private const val SIMILAR_ROW = MUSIC_VIDEO_ROW + 1
|
||||||
@Composable
|
@Composable
|
||||||
fun AlbumDetailsPage(
|
fun AlbumDetailsPage(
|
||||||
itemId: UUID,
|
itemId: UUID,
|
||||||
|
preferences: UserPreferences,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: AlbumViewModel =
|
viewModel: AlbumViewModel =
|
||||||
hiltViewModel<AlbumViewModel, AlbumViewModel.Factory>(
|
hiltViewModel<AlbumViewModel, AlbumViewModel.Factory>(
|
||||||
|
|
@ -292,9 +302,14 @@ fun AlbumDetailsPage(
|
||||||
val state by viewModel.state.collectAsState()
|
val state by viewModel.state.collectAsState()
|
||||||
val currentMusic by viewModel.currentMusic.collectAsState()
|
val currentMusic by viewModel.currentMusic.collectAsState()
|
||||||
|
|
||||||
|
var position by rememberPosition(0, 0)
|
||||||
|
val focusRequesters =
|
||||||
|
remember { List(SIMILAR_ROW + 1) { FocusRequester() } }
|
||||||
|
val focusManager = LocalFocusManager.current
|
||||||
var showPlaylistDialog by remember { mutableStateOf<Optional<UUID>>(Optional.absent()) }
|
var showPlaylistDialog by remember { mutableStateOf<Optional<UUID>>(Optional.absent()) }
|
||||||
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
||||||
var moreDialog by remember { mutableStateOf<DialogParams?>(null) }
|
var moreDialog by remember { mutableStateOf<DialogParams?>(null) }
|
||||||
|
var showDeleteDialog by remember { mutableStateOf<BaseItem?>(null) }
|
||||||
val moreDialogActions =
|
val moreDialogActions =
|
||||||
remember {
|
remember {
|
||||||
MusicMoreDialogActions(
|
MusicMoreDialogActions(
|
||||||
|
|
@ -308,6 +323,7 @@ fun AlbumDetailsPage(
|
||||||
showPlaylistDialog.makePresent(itemId)
|
showPlaylistDialog.makePresent(itemId)
|
||||||
},
|
},
|
||||||
onClickRemoveFromQueue = {},
|
onClickRemoveFromQueue = {},
|
||||||
|
onClickDelete = { showDeleteDialog = it },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
when (val loading = state.loading) {
|
when (val loading = state.loading) {
|
||||||
|
|
@ -323,15 +339,13 @@ fun AlbumDetailsPage(
|
||||||
|
|
||||||
LoadingState.Success -> {
|
LoadingState.Success -> {
|
||||||
val album = state.album!!
|
val album = state.album!!
|
||||||
var position by rememberPosition(0, 0)
|
|
||||||
|
|
||||||
val firstFocusRequester = remember { FocusRequester() }
|
val firstFocusRequester = remember { FocusRequester() }
|
||||||
val firstBringIntoViewRequester = remember { BringIntoViewRequester() }
|
val firstBringIntoViewRequester = remember { BringIntoViewRequester() }
|
||||||
val bringIntoViewRequester = remember { BringIntoViewRequester() }
|
val bringIntoViewRequester = remember { BringIntoViewRequester() }
|
||||||
val listState = rememberLazyListState()
|
val listState = rememberLazyListState()
|
||||||
val itemsBefore = 2
|
val itemsBefore = 2
|
||||||
val focusRequesters =
|
|
||||||
remember { List(SIMILAR_ROW + 1) { FocusRequester() } }
|
|
||||||
val songFocusRequester = remember { FocusRequester() }
|
val songFocusRequester = remember { FocusRequester() }
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
if (position.row == SONG_ROW) {
|
if (position.row == SONG_ROW) {
|
||||||
|
|
@ -399,6 +413,11 @@ fun AlbumDetailsPage(
|
||||||
item = album,
|
item = album,
|
||||||
index = 0,
|
index = 0,
|
||||||
canRemove = false,
|
canRemove = false,
|
||||||
|
canDelete =
|
||||||
|
viewModel.canDelete(
|
||||||
|
album,
|
||||||
|
preferences.appPreferences,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
@ -451,6 +470,11 @@ fun AlbumDetailsPage(
|
||||||
item = song,
|
item = song,
|
||||||
index = index,
|
index = index,
|
||||||
canRemove = false,
|
canRemove = false,
|
||||||
|
canDelete =
|
||||||
|
viewModel.canDelete(
|
||||||
|
song,
|
||||||
|
preferences.appPreferences,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -468,6 +492,11 @@ fun AlbumDetailsPage(
|
||||||
item = song,
|
item = song,
|
||||||
index = index,
|
index = index,
|
||||||
canRemove = false,
|
canRemove = false,
|
||||||
|
canDelete =
|
||||||
|
viewModel.canDelete(
|
||||||
|
song,
|
||||||
|
preferences.appPreferences,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -527,7 +556,24 @@ fun AlbumDetailsPage(
|
||||||
viewModel.navigationManager.navigateTo(item.destination())
|
viewModel.navigationManager.navigateTo(item.destination())
|
||||||
},
|
},
|
||||||
onLongClickItem = { index, item ->
|
onLongClickItem = { index, item ->
|
||||||
// TODO
|
moreDialog =
|
||||||
|
DialogParams(
|
||||||
|
fromLongClick = true,
|
||||||
|
title = item.name ?: "",
|
||||||
|
items =
|
||||||
|
buildMoreDialogForMusic(
|
||||||
|
context = context,
|
||||||
|
actions = moreDialogActions,
|
||||||
|
item = item,
|
||||||
|
index = index,
|
||||||
|
canRemove = false,
|
||||||
|
canDelete =
|
||||||
|
viewModel.canDelete(
|
||||||
|
item,
|
||||||
|
preferences.appPreferences,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
cardContent = { index: Int, item: BaseItem?, mod: Modifier, onClick: () -> Unit, onLongClick: () -> Unit ->
|
cardContent = { index: Int, item: BaseItem?, mod: Modifier, onClick: () -> Unit, onLongClick: () -> Unit ->
|
||||||
BannerCardWithTitle(
|
BannerCardWithTitle(
|
||||||
|
|
@ -575,6 +621,17 @@ fun AlbumDetailsPage(
|
||||||
elevation = 3.dp,
|
elevation = 3.dp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
showDeleteDialog?.let { item ->
|
||||||
|
ConfirmDeleteDialog(
|
||||||
|
itemTitle = item.title ?: "",
|
||||||
|
onCancel = { showDeleteDialog = null },
|
||||||
|
onConfirm = {
|
||||||
|
viewModel.deleteItem(item)
|
||||||
|
focusRequesters.getOrNull(position.row)?.tryRequestFocus()
|
||||||
|
showDeleteDialog = null
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ import androidx.compose.ui.focus.focusRequester
|
||||||
import androidx.compose.ui.focus.onFocusChanged
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
|
|
@ -44,9 +45,11 @@ import coil3.compose.AsyncImage
|
||||||
import com.github.damontecres.wholphin.R
|
import com.github.damontecres.wholphin.R
|
||||||
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.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.ImageUrlService
|
import com.github.damontecres.wholphin.services.ImageUrlService
|
||||||
|
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.MusicService
|
import com.github.damontecres.wholphin.services.MusicService
|
||||||
import com.github.damontecres.wholphin.services.NavigationManager
|
import com.github.damontecres.wholphin.services.NavigationManager
|
||||||
|
|
@ -56,6 +59,7 @@ import com.github.damontecres.wholphin.ui.DefaultItemFields
|
||||||
import com.github.damontecres.wholphin.ui.SlimItemFields
|
import com.github.damontecres.wholphin.ui.SlimItemFields
|
||||||
import com.github.damontecres.wholphin.ui.cards.BannerCardWithTitle
|
import com.github.damontecres.wholphin.ui.cards.BannerCardWithTitle
|
||||||
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
import com.github.damontecres.wholphin.ui.cards.ItemRow
|
||||||
|
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
|
||||||
|
|
@ -106,18 +110,19 @@ import java.util.UUID
|
||||||
class ArtistViewModel
|
class ArtistViewModel
|
||||||
@AssistedInject
|
@AssistedInject
|
||||||
constructor(
|
constructor(
|
||||||
|
@ApplicationContext context: Context,
|
||||||
api: ApiClient,
|
api: ApiClient,
|
||||||
musicService: MusicService,
|
musicService: MusicService,
|
||||||
navigationManager: NavigationManager,
|
navigationManager: NavigationManager,
|
||||||
@param:ApplicationContext private val context: Context,
|
mediaManagementService: MediaManagementService,
|
||||||
val serverRepository: ServerRepository,
|
val serverRepository: ServerRepository,
|
||||||
val mediaReportService: MediaReportService,
|
val mediaReportService: MediaReportService,
|
||||||
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 imageUrlService: ImageUrlService,
|
private val imageUrlService: ImageUrlService,
|
||||||
@Assisted val itemId: UUID,
|
@Assisted itemId: UUID,
|
||||||
) : MusicViewModel(api, musicService, navigationManager) {
|
) : MusicViewModel(itemId, context, api, musicService, navigationManager, mediaManagementService) {
|
||||||
@AssistedFactory
|
@AssistedFactory
|
||||||
interface Factory {
|
interface Factory {
|
||||||
fun create(itemId: UUID): ArtistViewModel
|
fun create(itemId: UUID): ArtistViewModel
|
||||||
|
|
@ -129,6 +134,10 @@ class ArtistViewModel
|
||||||
val currentMusic = musicService.state
|
val currentMusic = musicService.state
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun init() {
|
||||||
viewModelScope.launchIO {
|
viewModelScope.launchIO {
|
||||||
try {
|
try {
|
||||||
val itemDeferred =
|
val itemDeferred =
|
||||||
|
|
@ -230,7 +239,7 @@ class ArtistViewModel
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun init() {
|
fun refresh() {
|
||||||
viewModelScope.launchDefault {
|
viewModelScope.launchDefault {
|
||||||
state.value.artist?.let {
|
state.value.artist?.let {
|
||||||
backdropService.submit(it)
|
backdropService.submit(it)
|
||||||
|
|
@ -283,6 +292,7 @@ private const val SIMILAR_ROW = MUSIC_VIDEO_ROW + 1
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ArtistDetailsPage(
|
fun ArtistDetailsPage(
|
||||||
|
preferences: UserPreferences,
|
||||||
itemId: UUID,
|
itemId: UUID,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
viewModel: ArtistViewModel =
|
viewModel: ArtistViewModel =
|
||||||
|
|
@ -292,13 +302,18 @@ fun ArtistDetailsPage(
|
||||||
playlistViewModel: AddPlaylistViewModel = hiltViewModel(),
|
playlistViewModel: AddPlaylistViewModel = hiltViewModel(),
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
val focusManager = LocalFocusManager.current
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val state by viewModel.state.collectAsState()
|
val state by viewModel.state.collectAsState()
|
||||||
val currentMusic by viewModel.currentMusic.collectAsState()
|
val currentMusic by viewModel.currentMusic.collectAsState()
|
||||||
|
var position by rememberPosition(0, 0)
|
||||||
|
val focusRequesters =
|
||||||
|
remember { List(SIMILAR_ROW + 1) { FocusRequester() } }
|
||||||
|
|
||||||
var showPlaylistDialog by remember { mutableStateOf<Optional<UUID>>(Optional.absent()) }
|
var showPlaylistDialog by remember { mutableStateOf<Optional<UUID>>(Optional.absent()) }
|
||||||
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
val playlistState by playlistViewModel.playlistState.observeAsState(PlaylistLoadingState.Pending)
|
||||||
var moreDialog by remember { mutableStateOf<DialogParams?>(null) }
|
var moreDialog by remember { mutableStateOf<DialogParams?>(null) }
|
||||||
|
var showDeleteDialog by remember { mutableStateOf<BaseItem?>(null) }
|
||||||
val moreDialogActions =
|
val moreDialogActions =
|
||||||
remember {
|
remember {
|
||||||
MusicMoreDialogActions(
|
MusicMoreDialogActions(
|
||||||
|
|
@ -312,6 +327,7 @@ fun ArtistDetailsPage(
|
||||||
showPlaylistDialog.makePresent(itemId)
|
showPlaylistDialog.makePresent(itemId)
|
||||||
},
|
},
|
||||||
onClickRemoveFromQueue = {},
|
onClickRemoveFromQueue = {},
|
||||||
|
onClickDelete = { showDeleteDialog = it },
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -327,10 +343,7 @@ fun ArtistDetailsPage(
|
||||||
}
|
}
|
||||||
|
|
||||||
LoadingState.Success -> {
|
LoadingState.Success -> {
|
||||||
var position by rememberPosition(0, 0)
|
|
||||||
val artist = state.artist!!
|
val artist = state.artist!!
|
||||||
val focusRequesters =
|
|
||||||
remember { List(SIMILAR_ROW + 1) { FocusRequester() } }
|
|
||||||
val songFocusRequester = remember { FocusRequester() }
|
val songFocusRequester = remember { FocusRequester() }
|
||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
if (position.row == SONG_ROW) {
|
if (position.row == SONG_ROW) {
|
||||||
|
|
@ -338,7 +351,7 @@ fun ArtistDetailsPage(
|
||||||
} else {
|
} else {
|
||||||
focusRequesters.getOrNull(position.row)?.tryRequestFocus()
|
focusRequesters.getOrNull(position.row)?.tryRequestFocus()
|
||||||
}
|
}
|
||||||
viewModel.init()
|
viewModel.refresh()
|
||||||
}
|
}
|
||||||
val bringIntoViewRequester = remember { BringIntoViewRequester() }
|
val bringIntoViewRequester = remember { BringIntoViewRequester() }
|
||||||
Box(modifier = modifier) {
|
Box(modifier = modifier) {
|
||||||
|
|
@ -389,6 +402,11 @@ fun ArtistDetailsPage(
|
||||||
item = artist,
|
item = artist,
|
||||||
index = 0,
|
index = 0,
|
||||||
canRemove = false,
|
canRemove = false,
|
||||||
|
canDelete =
|
||||||
|
viewModel.canDelete(
|
||||||
|
artist,
|
||||||
|
preferences.appPreferences,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
|
@ -438,6 +456,11 @@ fun ArtistDetailsPage(
|
||||||
item = song,
|
item = song,
|
||||||
index = index,
|
index = index,
|
||||||
canRemove = false,
|
canRemove = false,
|
||||||
|
canDelete =
|
||||||
|
viewModel.canDelete(
|
||||||
|
song,
|
||||||
|
preferences.appPreferences,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -455,6 +478,11 @@ fun ArtistDetailsPage(
|
||||||
item = song,
|
item = song,
|
||||||
index = index,
|
index = index,
|
||||||
canRemove = false,
|
canRemove = false,
|
||||||
|
canDelete =
|
||||||
|
viewModel.canDelete(
|
||||||
|
song,
|
||||||
|
preferences.appPreferences,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -482,7 +510,24 @@ fun ArtistDetailsPage(
|
||||||
viewModel.navigationManager.navigateTo(album.destination())
|
viewModel.navigationManager.navigateTo(album.destination())
|
||||||
},
|
},
|
||||||
onLongClickItem = { index, album ->
|
onLongClickItem = { index, album ->
|
||||||
// TODO
|
moreDialog =
|
||||||
|
DialogParams(
|
||||||
|
fromLongClick = true,
|
||||||
|
title = album.name ?: "",
|
||||||
|
items =
|
||||||
|
buildMoreDialogForMusic(
|
||||||
|
context = context,
|
||||||
|
actions = moreDialogActions,
|
||||||
|
item = album,
|
||||||
|
index = index,
|
||||||
|
canRemove = false,
|
||||||
|
canDelete =
|
||||||
|
viewModel.canDelete(
|
||||||
|
album,
|
||||||
|
preferences.appPreferences,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
cardContent = { index: Int, album: BaseItem?, mod: Modifier, onClick: () -> Unit, onLongClick: () -> Unit ->
|
cardContent = { index: Int, album: BaseItem?, mod: Modifier, onClick: () -> Unit, onLongClick: () -> Unit ->
|
||||||
BannerCardWithTitle(
|
BannerCardWithTitle(
|
||||||
|
|
@ -534,7 +579,24 @@ fun ArtistDetailsPage(
|
||||||
viewModel.navigationManager.navigateTo(item.destination())
|
viewModel.navigationManager.navigateTo(item.destination())
|
||||||
},
|
},
|
||||||
onLongClickItem = { index, item ->
|
onLongClickItem = { index, item ->
|
||||||
// TODO
|
moreDialog =
|
||||||
|
DialogParams(
|
||||||
|
fromLongClick = true,
|
||||||
|
title = item.name ?: "",
|
||||||
|
items =
|
||||||
|
buildMoreDialogForMusic(
|
||||||
|
context = context,
|
||||||
|
actions = moreDialogActions,
|
||||||
|
item = item,
|
||||||
|
index = index,
|
||||||
|
canRemove = false,
|
||||||
|
canDelete =
|
||||||
|
viewModel.canDelete(
|
||||||
|
item,
|
||||||
|
preferences.appPreferences,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
},
|
},
|
||||||
cardContent = { index: Int, item: BaseItem?, mod: Modifier, onClick: () -> Unit, onLongClick: () -> Unit ->
|
cardContent = { index: Int, item: BaseItem?, mod: Modifier, onClick: () -> Unit, onLongClick: () -> Unit ->
|
||||||
BannerCardWithTitle(
|
BannerCardWithTitle(
|
||||||
|
|
@ -582,6 +644,17 @@ fun ArtistDetailsPage(
|
||||||
elevation = 3.dp,
|
elevation = 3.dp,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
showDeleteDialog?.let { item ->
|
||||||
|
ConfirmDeleteDialog(
|
||||||
|
itemTitle = item.title ?: "",
|
||||||
|
onCancel = { showDeleteDialog = null },
|
||||||
|
onConfirm = {
|
||||||
|
viewModel.deleteItem(item)
|
||||||
|
focusRequesters.getOrNull(position.row)?.tryRequestFocus()
|
||||||
|
showDeleteDialog = null
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,14 @@
|
||||||
package com.github.damontecres.wholphin.ui.detail.music
|
package com.github.damontecres.wholphin.ui.detail.music
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
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.services.MediaManagementService
|
||||||
import com.github.damontecres.wholphin.services.MusicService
|
import com.github.damontecres.wholphin.services.MusicService
|
||||||
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.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.nav.Destination
|
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||||
|
|
@ -17,9 +21,12 @@ import timber.log.Timber
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
abstract class MusicViewModel(
|
abstract class MusicViewModel(
|
||||||
|
internal val itemId: UUID,
|
||||||
|
internal val context: Context,
|
||||||
internal val api: ApiClient,
|
internal val api: ApiClient,
|
||||||
internal val musicService: MusicService,
|
internal val musicService: MusicService,
|
||||||
internal val navigationManager: NavigationManager,
|
internal val navigationManager: NavigationManager,
|
||||||
|
internal val mediaManagementService: MediaManagementService,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
fun play(
|
fun play(
|
||||||
pager: ApiRequestPager<*>,
|
pager: ApiRequestPager<*>,
|
||||||
|
|
@ -115,4 +122,21 @@ abstract class MusicViewModel(
|
||||||
navigationManager.navigateTo(Destination.NowPlaying)
|
navigationManager.navigateTo(Destination.NowPlaying)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun canDelete(
|
||||||
|
item: BaseItem,
|
||||||
|
appPreferences: AppPreferences,
|
||||||
|
): Boolean = mediaManagementService.canDelete(item, appPreferences)
|
||||||
|
|
||||||
|
fun deleteItem(item: BaseItem) {
|
||||||
|
deleteItem(context, mediaManagementService, item) {
|
||||||
|
if (item.id == itemId) {
|
||||||
|
navigationManager.goBack()
|
||||||
|
} else {
|
||||||
|
init()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal abstract fun init()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ data class MusicMoreDialogActions(
|
||||||
onNavigate.invoke(Destination.MediaItem(itemId = it, type = BaseItemKind.MUSIC_ARTIST))
|
onNavigate.invoke(Destination.MediaItem(itemId = it, type = BaseItemKind.MUSIC_ARTIST))
|
||||||
},
|
},
|
||||||
val onClickRemoveFromQueue: (Int) -> Unit,
|
val onClickRemoveFromQueue: (Int) -> Unit,
|
||||||
|
val onClickDelete: (BaseItem) -> Unit,
|
||||||
)
|
)
|
||||||
|
|
||||||
fun buildMoreDialogForMusic(
|
fun buildMoreDialogForMusic(
|
||||||
|
|
@ -45,6 +46,7 @@ fun buildMoreDialogForMusic(
|
||||||
item: BaseItem,
|
item: BaseItem,
|
||||||
index: Int,
|
index: Int,
|
||||||
canRemove: Boolean,
|
canRemove: Boolean,
|
||||||
|
canDelete: Boolean,
|
||||||
): List<DialogItem> =
|
): List<DialogItem> =
|
||||||
buildList {
|
buildList {
|
||||||
add(
|
add(
|
||||||
|
|
@ -91,6 +93,17 @@ fun buildMoreDialogForMusic(
|
||||||
actions.onClickAddPlaylist.invoke(item.id)
|
actions.onClickAddPlaylist.invoke(item.id)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
if (canDelete) {
|
||||||
|
add(
|
||||||
|
DialogItem(
|
||||||
|
context.getString(R.string.delete),
|
||||||
|
Icons.Default.Delete,
|
||||||
|
iconColor = Color.Red.copy(alpha = .8f),
|
||||||
|
) {
|
||||||
|
actions.onClickDelete.invoke(item)
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
add(
|
add(
|
||||||
DialogItem(
|
DialogItem(
|
||||||
text = if (item.favorite) R.string.remove_favorite else R.string.add_favorite,
|
text = if (item.favorite) R.string.remove_favorite else R.string.add_favorite,
|
||||||
|
|
|
||||||
|
|
@ -158,6 +158,7 @@ fun DestinationContent(
|
||||||
BaseItemKind.PLAYLIST -> {
|
BaseItemKind.PLAYLIST -> {
|
||||||
LaunchedEffect(Unit) { onClearBackdrop.invoke() }
|
LaunchedEffect(Unit) { onClearBackdrop.invoke() }
|
||||||
PlaylistDetails(
|
PlaylistDetails(
|
||||||
|
preferences = preferences,
|
||||||
destination = destination,
|
destination = destination,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
|
@ -221,6 +222,7 @@ fun DestinationContent(
|
||||||
BaseItemKind.MUSIC_ALBUM -> {
|
BaseItemKind.MUSIC_ALBUM -> {
|
||||||
LaunchedEffect(Unit) { onClearBackdrop.invoke() }
|
LaunchedEffect(Unit) { onClearBackdrop.invoke() }
|
||||||
AlbumDetailsPage(
|
AlbumDetailsPage(
|
||||||
|
preferences = preferences,
|
||||||
itemId = destination.itemId,
|
itemId = destination.itemId,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
|
@ -229,6 +231,7 @@ fun DestinationContent(
|
||||||
BaseItemKind.MUSIC_ARTIST -> {
|
BaseItemKind.MUSIC_ARTIST -> {
|
||||||
LaunchedEffect(Unit) { onClearBackdrop.invoke() }
|
LaunchedEffect(Unit) { onClearBackdrop.invoke() }
|
||||||
ArtistDetailsPage(
|
ArtistDetailsPage(
|
||||||
|
preferences = preferences,
|
||||||
itemId = destination.itemId,
|
itemId = destination.itemId,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue