forked from platypush/platypush
[Youtube UI] More playlist actions.
- `create_playlist` - `rename_playlist` - `delete_playlist`
This commit is contained in:
parent
d4354e81f8
commit
98a6adb7ef
5 changed files with 229 additions and 4 deletions
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<Modal ref="modal" :title="title">
|
<Modal ref="modal" :title="title" @close="close">
|
||||||
<div class="dialog-content">
|
<div class="dialog-content">
|
||||||
<slot />
|
<slot />
|
||||||
</div>
|
</div>
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
import Modal from "@/components/Modal";
|
import Modal from "@/components/Modal";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
emits: ['input', 'click', 'touch'],
|
emits: ['input', 'click', 'close', 'touch'],
|
||||||
components: {Modal},
|
components: {Modal},
|
||||||
props: {
|
props: {
|
||||||
title: {
|
title: {
|
||||||
|
@ -49,6 +49,7 @@ export default {
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.$refs.modal.hide()
|
this.$refs.modal.hide()
|
||||||
|
this.$emit('close')
|
||||||
},
|
},
|
||||||
|
|
||||||
show() {
|
show() {
|
||||||
|
|
|
@ -39,7 +39,7 @@ export default {
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.floating-btn {
|
.floating-btn {
|
||||||
position: fixed;
|
position: absolute;
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
margin: auto 1em 1em auto;
|
margin: auto 1em 1em auto;
|
||||||
|
|
|
@ -59,7 +59,10 @@ export default {
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
onConfirm() {
|
onConfirm() {
|
||||||
|
if (this.value_?.trim()?.length) {
|
||||||
this.$emit('input', this.value_)
|
this.$emit('input', this.value_)
|
||||||
|
}
|
||||||
|
|
||||||
this.close()
|
this.close()
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -120,6 +123,8 @@ export default {
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
:deep(.modal) {
|
:deep(.modal) {
|
||||||
.dialog-content {
|
.dialog-content {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
padding: 1em;
|
padding: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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) {
|
selectView(view) {
|
||||||
this.selectedView = view
|
this.selectedView = view
|
||||||
if (view === 'playlists')
|
if (view === 'playlists')
|
||||||
|
|
|
@ -13,6 +13,14 @@
|
||||||
@click="$emit('select', playlist)">
|
@click="$emit('select', playlist)">
|
||||||
<MediaImage :item="playlist" :has-play="false" />
|
<MediaImage :item="playlist" :has-play="false" />
|
||||||
<div class="title">{{ playlist.name }}</div>
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,14 +35,74 @@
|
||||||
@play="$emit('play', $event)"
|
@play="$emit('play', $event)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</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" /> Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="btn-container col-6">
|
||||||
|
<button @click="clearEditPlaylist">
|
||||||
|
<i class="fa fa-times" /> Cancel
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
|
||||||
|
<FloatingButton
|
||||||
|
icon-class="fa fa-plus"
|
||||||
|
title="Create Playlist"
|
||||||
|
@click="showCreatePlaylist = true"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import ConfirmDialog from "@/components/elements/ConfirmDialog";
|
||||||
|
import FloatingButton from "@/components/elements/FloatingButton";
|
||||||
import MediaImage from "@/components/panels/Media/MediaImage";
|
import MediaImage from "@/components/panels/Media/MediaImage";
|
||||||
|
import Modal from "@/components/Modal";
|
||||||
import NoItems from "@/components/elements/NoItems";
|
import NoItems from "@/components/elements/NoItems";
|
||||||
import Loading from "@/components/Loading";
|
import Loading from "@/components/Loading";
|
||||||
import Playlist from "./Playlist";
|
import Playlist from "./Playlist";
|
||||||
|
import TextPrompt from "@/components/elements/TextPrompt"
|
||||||
import Utils from "@/Utils";
|
import Utils from "@/Utils";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
@ -50,10 +118,14 @@ export default {
|
||||||
],
|
],
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
|
ConfirmDialog,
|
||||||
|
FloatingButton,
|
||||||
Loading,
|
Loading,
|
||||||
MediaImage,
|
MediaImage,
|
||||||
|
Modal,
|
||||||
NoItems,
|
NoItems,
|
||||||
Playlist,
|
Playlist,
|
||||||
|
TextPrompt,
|
||||||
},
|
},
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
|
@ -70,8 +142,13 @@ export default {
|
||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
|
deletedPlaylist: null,
|
||||||
|
editedPlaylist: null,
|
||||||
|
editedPlaylistName: '',
|
||||||
|
editedPlaylistDescription: '',
|
||||||
playlists: [],
|
playlists: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
|
showCreatePlaylist: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -95,6 +172,64 @@ export default {
|
||||||
this.loading = false
|
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() {
|
mounted() {
|
||||||
|
@ -106,12 +241,14 @@ export default {
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.media-youtube-playlists {
|
.media-youtube-playlists {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
.playlist-body {
|
.playlist-body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(.playlist.item) {
|
:deep(.playlist.item) {
|
||||||
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
.title {
|
.title {
|
||||||
|
@ -119,6 +256,33 @@ export default {
|
||||||
margin-top: 0.5em;
|
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 {
|
&:hover {
|
||||||
text-decoration: underline;
|
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>
|
</style>
|
||||||
|
|
Loading…
Reference in a new issue