mirror of
https://github.com/libre-tube/LibreTube.git
synced 2025-04-27 23:40:33 +05:30
refactor: simplify add to playlist dialog (#7074)
This commit is contained in:
parent
20db67d229
commit
2ae689c74d
@ -71,7 +71,9 @@ object PlaylistsHelper {
|
||||
playlistsRepository.createPlaylist(playlistName)
|
||||
|
||||
suspend fun addToPlaylist(playlistId: String, vararg videos: StreamItem) =
|
||||
playlistsRepository.addToPlaylist(playlistId, *videos)
|
||||
withContext(Dispatchers.IO) {
|
||||
playlistsRepository.addToPlaylist(playlistId, *videos)
|
||||
}
|
||||
|
||||
suspend fun renamePlaylist(playlistId: String, newName: String) =
|
||||
playlistsRepository.renamePlaylist(playlistId, newName)
|
||||
|
@ -1,12 +1,15 @@
|
||||
package com.github.libretube.api.obj
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
@Parcelize
|
||||
data class Playlists(
|
||||
val id: String? = null,
|
||||
var name: String? = null,
|
||||
var shortDescription: String? = null,
|
||||
val thumbnail: String? = null,
|
||||
val videos: Long = 0
|
||||
)
|
||||
) : Parcelable
|
||||
|
@ -1,6 +1,5 @@
|
||||
package com.github.libretube.ui.dialogs
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Dialog
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
@ -8,34 +7,24 @@ import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.DialogFragment
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.fragment.app.setFragmentResult
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.PlaylistsHelper
|
||||
import com.github.libretube.api.obj.Playlists
|
||||
import com.github.libretube.api.obj.StreamItem
|
||||
import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.databinding.DialogAddToPlaylistBinding
|
||||
import com.github.libretube.extensions.TAG
|
||||
import com.github.libretube.extensions.parcelable
|
||||
import com.github.libretube.extensions.toastFromMainDispatcher
|
||||
import com.github.libretube.ui.models.PlaylistViewModel
|
||||
import com.github.libretube.util.PlayingQueue
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* Dialog to insert new videos to a playlist
|
||||
* videoId: The id of the video to add. If non is provided, insert the whole playing queue
|
||||
*/
|
||||
class AddToPlaylistDialog : DialogFragment() {
|
||||
private var videoInfo: StreamItem? = null
|
||||
private val viewModel: PlaylistViewModel by activityViewModels()
|
||||
|
||||
var playlists = emptyList<Playlists>()
|
||||
private var videoInfo: StreamItem? = null
|
||||
private val viewModel: PlaylistViewModel by viewModels { PlaylistViewModel.Factory }
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -44,19 +33,39 @@ class AddToPlaylistDialog : DialogFragment() {
|
||||
}
|
||||
|
||||
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
|
||||
val binding = DialogAddToPlaylistBinding.inflate(layoutInflater)
|
||||
|
||||
childFragmentManager.setFragmentResultListener(
|
||||
CreatePlaylistDialog.CREATE_PLAYLIST_DIALOG_REQUEST_KEY,
|
||||
this
|
||||
) { _, resultBundle ->
|
||||
val addedToPlaylist = resultBundle.getBoolean(IntentData.playlistTask)
|
||||
if (addedToPlaylist) {
|
||||
fetchPlaylists(binding)
|
||||
viewModel.fetchPlaylists()
|
||||
}
|
||||
}
|
||||
|
||||
fetchPlaylists(binding)
|
||||
val binding = DialogAddToPlaylistBinding.inflate(layoutInflater)
|
||||
viewModel.uiState.observe(this) { (lastSelectedPlaylistId, playlists, msg, saved) ->
|
||||
binding.playlistsSpinner.items = playlists.mapNotNull { it.name }
|
||||
|
||||
// select the last used playlist
|
||||
lastSelectedPlaylistId?.let { id ->
|
||||
binding.playlistsSpinner.selectedItemPosition = playlists
|
||||
.indexOfFirst { it.id == id }
|
||||
.takeIf { it >= 0 } ?: 0
|
||||
}
|
||||
|
||||
msg?.let {
|
||||
with(binding.root.context) {
|
||||
Toast.makeText(this, getString(it.resId, it.formatArgs), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
viewModel.onMessageShown()
|
||||
}
|
||||
|
||||
saved?.let {
|
||||
dismiss()
|
||||
viewModel.onDismissed()
|
||||
}
|
||||
}
|
||||
|
||||
return MaterialAlertDialogBuilder(requireContext())
|
||||
.setTitle(R.string.addToPlaylist)
|
||||
@ -65,71 +74,17 @@ class AddToPlaylistDialog : DialogFragment() {
|
||||
.setView(binding.root)
|
||||
.show()
|
||||
.apply {
|
||||
// Click listeners without closing the dialog
|
||||
getButton(DialogInterface.BUTTON_NEGATIVE).setOnClickListener {
|
||||
CreatePlaylistDialog().show(childFragmentManager, null)
|
||||
}
|
||||
getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener {
|
||||
val playlistIndex = binding.playlistsSpinner.selectedItemPosition
|
||||
|
||||
val playlist = playlists.getOrElse(playlistIndex) { return@setOnClickListener }
|
||||
viewModel.lastSelectedPlaylistId = playlist.id!!
|
||||
|
||||
dialog?.hide()
|
||||
lifecycleScope.launch {
|
||||
addToPlaylist(playlist.id, playlist.name!!)
|
||||
dialog?.dismiss()
|
||||
}
|
||||
val selectedItemPosition = binding.playlistsSpinner.selectedItemPosition
|
||||
viewModel.onAddToPlaylist(selectedItemPosition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchPlaylists(binding: DialogAddToPlaylistBinding) {
|
||||
lifecycleScope.launch {
|
||||
repeatOnLifecycle(Lifecycle.State.CREATED) {
|
||||
playlists = try {
|
||||
PlaylistsHelper.getPlaylists()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG(), e.toString())
|
||||
Toast.makeText(context, R.string.unknown_error, Toast.LENGTH_SHORT).show()
|
||||
return@repeatOnLifecycle
|
||||
}.filter { !it.name.isNullOrEmpty() }
|
||||
|
||||
binding.playlistsSpinner.items = playlists.map { it.name!! }
|
||||
|
||||
if (playlists.isEmpty()) return@repeatOnLifecycle
|
||||
|
||||
// select the last used playlist
|
||||
viewModel.lastSelectedPlaylistId?.let { id ->
|
||||
binding.playlistsSpinner.selectedItemPosition = playlists
|
||||
.indexOfFirst { it.id == id }
|
||||
.takeIf { it >= 0 } ?: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("StringFormatInvalid")
|
||||
private suspend fun addToPlaylist(playlistId: String, playlistName: String) {
|
||||
val appContext = context?.applicationContext ?: return
|
||||
val streams = videoInfo?.let { listOf(it) } ?: PlayingQueue.getStreams()
|
||||
|
||||
val success = try {
|
||||
if (streams.isEmpty()) throw IllegalArgumentException()
|
||||
PlaylistsHelper.addToPlaylist(playlistId, *streams.toTypedArray())
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG(), e.toString())
|
||||
appContext.toastFromMainDispatcher(R.string.unknown_error)
|
||||
return
|
||||
}
|
||||
if (success) {
|
||||
appContext.toastFromMainDispatcher(
|
||||
appContext.getString(R.string.added_to_playlist, playlistName)
|
||||
)
|
||||
} else {
|
||||
appContext.toastFromMainDispatcher(R.string.fail)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDismiss(dialog: DialogInterface) {
|
||||
super.onDismiss(dialog)
|
||||
|
||||
|
@ -1,7 +1,109 @@
|
||||
package com.github.libretube.ui.models
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.lifecycle.createSavedStateHandle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.initializer
|
||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.PlaylistsHelper
|
||||
import com.github.libretube.api.obj.Playlists
|
||||
import com.github.libretube.api.obj.StreamItem
|
||||
import com.github.libretube.constants.IntentData
|
||||
import com.github.libretube.util.PlayingQueue
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import kotlinx.parcelize.RawValue
|
||||
|
||||
class PlaylistViewModel : ViewModel() {
|
||||
var lastSelectedPlaylistId: String? = null
|
||||
class PlaylistViewModel(
|
||||
private val savedStateHandle: SavedStateHandle,
|
||||
) : ViewModel() {
|
||||
|
||||
private val _uiState = savedStateHandle.getStateFlow(UI_STATE, UiState())
|
||||
val uiState = _uiState.asLiveData()
|
||||
|
||||
init {
|
||||
fetchPlaylists()
|
||||
}
|
||||
|
||||
fun fetchPlaylists() {
|
||||
viewModelScope.launch {
|
||||
kotlin.runCatching {
|
||||
PlaylistsHelper.getPlaylists()
|
||||
}.onSuccess { playlists ->
|
||||
savedStateHandle[UI_STATE] = _uiState.value.copy(
|
||||
playlists = playlists.filterNot { list -> list.name.isNullOrEmpty() }
|
||||
)
|
||||
}.onFailure {
|
||||
savedStateHandle[UI_STATE] = _uiState.value.copy(
|
||||
message = UiState.Message(R.string.unknown_error)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onAddToPlaylist(playlistIndex: Int) {
|
||||
val playlist = _uiState.value.playlists.getOrElse(playlistIndex) { return }
|
||||
savedStateHandle[UI_STATE] = _uiState.value.copy(lastSelectedPlaylistId = playlist.id)
|
||||
|
||||
val videoInfo = savedStateHandle.get<StreamItem>(IntentData.videoInfo)
|
||||
val streams = videoInfo?.let { listOf(it) } ?: PlayingQueue.getStreams()
|
||||
|
||||
viewModelScope.launch {
|
||||
kotlin.runCatching {
|
||||
if (streams.isEmpty()) {
|
||||
throw IllegalArgumentException()
|
||||
}
|
||||
PlaylistsHelper.addToPlaylist(playlist.id!!, *streams.toTypedArray())
|
||||
}.onSuccess {
|
||||
savedStateHandle[UI_STATE] = _uiState.value.copy(
|
||||
message = UiState.Message(R.string.added_to_playlist, listOf(playlist.name!!)),
|
||||
saved = Unit,
|
||||
)
|
||||
}
|
||||
.onFailure {
|
||||
savedStateHandle[UI_STATE] = _uiState.value.copy(
|
||||
message = UiState.Message(R.string.unknown_error)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun onMessageShown() {
|
||||
savedStateHandle[UI_STATE] = _uiState.value.copy(message = null)
|
||||
}
|
||||
|
||||
fun onDismissed() {
|
||||
savedStateHandle[UI_STATE] = _uiState.value.copy(saved = null)
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class UiState(
|
||||
val lastSelectedPlaylistId: String? = null,
|
||||
val playlists: List<Playlists> = emptyList(),
|
||||
val message: Message? = null,
|
||||
val saved: Unit? = null,
|
||||
) : Parcelable {
|
||||
@Parcelize
|
||||
data class Message(
|
||||
@StringRes val resId: Int,
|
||||
val formatArgs: List<@RawValue Any>? = null,
|
||||
) : Parcelable
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val UI_STATE = "ui_state"
|
||||
|
||||
val Factory = viewModelFactory {
|
||||
initializer {
|
||||
PlaylistViewModel(
|
||||
savedStateHandle = createSavedStateHandle(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user