[Youtube UI] More playlist actions.

- `create_playlist`
- `rename_playlist`
- `delete_playlist`
This commit is contained in:
Fabio Manganiello 2024-06-27 01:26:00 +02:00
parent d4354e81f8
commit 98a6adb7ef
Signed by untrusted user: blacklight
GPG key ID: D90FBA7F76362774
5 changed files with 229 additions and 4 deletions

View file

@ -1,5 +1,5 @@
<template>
<Modal ref="modal" :title="title">
<Modal ref="modal" :title="title" @close="close">
<div class="dialog-content">
<slot />
</div>
@ -19,7 +19,7 @@
import Modal from "@/components/Modal";
export default {
emits: ['input', 'click', 'touch'],
emits: ['input', 'click', 'close', 'touch'],
components: {Modal},
props: {
title: {
@ -49,6 +49,7 @@ export default {
close() {
this.$refs.modal.hide()
this.$emit('close')
},
show() {

View file

@ -39,7 +39,7 @@ export default {
<style lang="scss" scoped>
.floating-btn {
position: fixed;
position: absolute;
bottom: 0;
right: 0;
margin: auto 1em 1em auto;

View file

@ -59,7 +59,10 @@ export default {
methods: {
onConfirm() {
this.$emit('input', this.value_)
if (this.value_?.trim()?.length) {
this.$emit('input', this.value_)
}
this.close()
},
@ -120,6 +123,8 @@ export default {
<style lang="scss" scoped>
:deep(.modal) {
.dialog-content {
display: flex;
flex-direction: column;
padding: 1em;
}

View file

@ -112,6 +112,15 @@ export default {
}
},
async createPlaylist(name) {
this.loading = true
try {
await this.request('youtube.create_playlist', {name: name})
} finally {
this.loading = false
}
},
selectView(view) {
this.selectedView = view
if (view === 'playlists')

View file

@ -13,6 +13,14 @@
@click="$emit('select', playlist)">
<MediaImage :item="playlist" :has-play="false" />
<div class="title">{{ playlist.name }}</div>
<div class="actions">
<button title="Remove" @click.stop="deletedPlaylist = playlist.id">
<i class="fa fa-trash" />
</button>
<button title="Edit" @click.stop="editedPlaylist = playlist.id">
<i class="fa fa-pencil" />
</button>
</div>
</div>
</div>
</div>
@ -27,14 +35,74 @@
@play="$emit('play', $event)"
/>
</div>
<TextPrompt
:visible="showCreatePlaylist"
@input="createPlaylist($event)"
@close="showCreatePlaylist = false"
>
Playlist name
</TextPrompt>
<ConfirmDialog
ref="removePlaylist"
title="Remove Playlist"
:visible="deletedPlaylist != null"
@close="deletedPlaylist = null"
@input="removePlaylist"
>
Are you sure you want to remove this playlist?
</ConfirmDialog>
<Modal
ref="editPlaylist"
title="Edit Playlist"
:visible="editedPlaylist != null"
@close="clearEditPlaylist"
@open="onEditPlaylistOpen"
>
<form class="edit-playlist-form" @submit.prevent="editPlaylist">
<div class="row">
<input ref="editPlaylistName" placeholder="Playlist name" v-model="editedPlaylistName" />
</div>
<div class="row">
<input placeholder="Playlist description" v-model="editedPlaylistDescription" />
</div>
<div class="row buttons">
<div class="btn-container col-6">
<button type="submit">
<i class="fa fa-check" />&nbsp;Save
</button>
</div>
<div class="btn-container col-6">
<button @click="clearEditPlaylist">
<i class="fa fa-times" />&nbsp;Cancel
</button>
</div>
</div>
</form>
</Modal>
<FloatingButton
icon-class="fa fa-plus"
title="Create Playlist"
@click="showCreatePlaylist = true"
/>
</div>
</template>
<script>
import ConfirmDialog from "@/components/elements/ConfirmDialog";
import FloatingButton from "@/components/elements/FloatingButton";
import MediaImage from "@/components/panels/Media/MediaImage";
import Modal from "@/components/Modal";
import NoItems from "@/components/elements/NoItems";
import Loading from "@/components/Loading";
import Playlist from "./Playlist";
import TextPrompt from "@/components/elements/TextPrompt"
import Utils from "@/Utils";
export default {
@ -50,10 +118,14 @@ export default {
],
components: {
ConfirmDialog,
FloatingButton,
Loading,
MediaImage,
Modal,
NoItems,
Playlist,
TextPrompt,
},
props: {
@ -70,8 +142,13 @@ export default {
data() {
return {
deletedPlaylist: null,
editedPlaylist: null,
editedPlaylistName: '',
editedPlaylistDescription: '',
playlists: [],
loading: false,
showCreatePlaylist: false,
}
},
@ -95,6 +172,64 @@ export default {
this.loading = false
}
},
async createPlaylist(name) {
this.loading = true
try {
await this.request('youtube.create_playlist', {name: name})
this.showCreatePlaylist = false
this.loadPlaylists()
} finally {
this.loading = false
}
},
async removePlaylist() {
if (!this.deletedPlaylist)
return
this.loading = true
try {
await this.request('youtube.delete_playlist', {id: this.deletedPlaylist})
this.deletedPlaylist = null
this.loadPlaylists()
} finally {
this.loading = false
}
},
async editPlaylist() {
if (!this.editedPlaylist)
return
this.loading = true
try {
await this.request('youtube.rename_playlist', {
id: this.editedPlaylist,
name: this.editedPlaylistName,
description: this.editedPlaylistDescription,
})
this.clearEditPlaylist()
this.loadPlaylists()
} finally {
this.loading = false
}
},
clearEditPlaylist() {
this.editedPlaylist = null
this.editedPlaylistName = ''
this.editedPlaylistDescription = ''
this.$refs.editPlaylist.hide()
},
onEditPlaylistOpen() {
const playlist = this.playlistsById[this.editedPlaylist]
this.editedPlaylistName = playlist.name
this.editedPlaylistDescription = playlist.description
this.$nextTick(() => this.$refs.editPlaylistName.focus())
},
},
mounted() {
@ -106,12 +241,14 @@ export default {
<style lang="scss" scoped>
.media-youtube-playlists {
height: 100%;
position: relative;
.playlist-body {
height: 100%;
}
:deep(.playlist.item) {
position: relative;
cursor: pointer;
.title {
@ -119,6 +256,33 @@ export default {
margin-top: 0.5em;
}
.actions {
position: absolute;
top: 0;
right: 0;
display: flex;
padding: 0.5em;
background: rgba(0, 0, 0, 0.25);
opacity: 0.9;
border-radius: 0 0 0.5em 0.5em;
transition: opacity 0.2s;
z-index: 1;
button {
background: none;
border: none;
color: white;
cursor: pointer;
transition: transform 0.2s;
flex: 1;
&:hover {
color: $default-hover-fg;
transform: scale(1.2);
}
}
}
&:hover {
text-decoration: underline;
@ -127,5 +291,51 @@ export default {
}
}
}
:deep(.modal) {
.edit-playlist-form {
min-width: 300px;
display: flex;
flex-direction: column;
.row {
margin: 0.5em 0;
input {
border: $default-border;
border-radius: 1em;
padding: 0.5em;
width: 100%;
&:focus {
border: 1px solid $selected-fg;
}
}
&.buttons {
display: flex;
justify-content: flex-end;
margin-top: 0.5em;
.btn-container {
display: flex;
justify-content: center;
}
}
button {
margin: 0 0.5em;
padding: 0.5em;
border-radius: 1em;
cursor: pointer;
transition: background 0.2s;
&:hover {
background: $hover-bg;
}
}
}
}
}
}
</style>