Commit 05409b63 authored by Richard Blechinger's avatar Richard Blechinger
Browse files

Extract 'add playlist' modal to a custom page

parent 83d3a0d2
<style></style>
<template>
<b-container class="tw-mb-8">
<h1>Playliste {{ playlistEditor.id }} für Sendung '{{ this.selectedShow.name }}' bearbeiten</h1>
<div
v-if="!selectedShow"
class="tw-text-center tw-my-8"
>
Lädt...
</div>
<b-container
v-else
class="tw-mb-8"
>
<h1>
<template v-if="id">
Playliste {{ playlistEditor.id }} für Sendung '{{ selectedShow.name }}' bearbeiten
</template>
<template v-else>
Neue Playliste für Sendung '{{ selectedShow.name }}' hinzufügen
</template>
</h1>
<b-row class="tw-my-8 tw-items-center">
<b-col cols="2">
......@@ -84,7 +101,7 @@
</span>
<span v-else>
{{ data.item.uri}}
{{ data.item.uri }}
</span>
</template>
......@@ -109,9 +126,9 @@
</span>
<span
v-else-if="data.item.duration"
v-b-tooltip="'Klicken um zu bearbeiten'"
class="tw-underline hover:tw-no-underline tw-cursor-pointer"
v-else-if="data.item.duration"
@click="toggleDurationField(data.index)"
>
{{ prettyNanoseconds(data.item.duration) }}
......@@ -248,7 +265,12 @@
export default {
mixins: [prettyDate],
props: ['id'],
props: {
id: {
type: [ Number, Boolean ],
default: false,
},
},
// include the modal for displaying file import logs
data() {
......@@ -327,7 +349,10 @@
},
created() {
this.editPlaylist(this.id);
this.$store.dispatch('shows/fetchShows');
this.id
? this.editPlaylist()
: this.addPlaylist()
},
methods: {
......@@ -397,10 +422,6 @@
}
},
editPlaylistItemDuration(index) {
console.log(this.playlistEditor.entries, index);
},
movePlaylistItemUp(index) {
if (index > 0 && index < this.playlistEditor.entries.length) {
let temp = this.playlistEditor.entries[index - 1]
......@@ -465,12 +486,20 @@
this.$bvModal.show('modal-edit-playlist')
},
editPlaylist(id) {
editPlaylist() {
let playlist = this.getPlaylistById(this.id);
console.log("AAAAA", playlist);
if (!playlist) {
this.$router.push({ name: 'addPlaylist' })
return;
}
this.playlistEditor.mode = 'edit'
this.playlistEditor.entries = []
this.playlistEditor.id = id
let playlist = this.getPlaylistById(id)
this.playlistEditor.id = this.id
this.playlistEditor.description = playlist.description
for (let i in playlist.entries) {
let entry = {}
if (playlist.entries[i].file) {
......
......@@ -99,15 +99,17 @@
<b-form-select
v-model="repetitionRule"
:options="repetitionOptions"
>
</b-form-select>
/>
</label>
<b-checkbox v-model="useSameTime">
Use same start and end time
</b-checkbox>
</b-col>
<b-col cols="6" v-if="!useSameTime">
<b-col
v-if="!useSameTime"
cols="6"
>
<label class="tw-leading-loose">
Repeat at
<b-form-input type="time" />
......
<template>
<b-modal
id="modal-edit-playlist"
size="lg"
:title="playlistEditor.mode === 'edit' ? 'Edit playlist' : 'Add new playlist'"
@ok="storePlaylist"
>
<b-row
v-if="playlistEditor.mode === 'edit'"
style="padding-bottom: 1em;"
>
<b-col
cols="3"
>
Playlist index:
</b-col>
<b-col>{{ playlistEditor.id }}</b-col>
</b-row>
<b-row>
<b-col cols="3">
Description:
</b-col>
<b-col>
<b-form-input
v-model="playlistEditor.description"
type="text"
/>
</b-col>
</b-row>
<hr>
<p class="d-flex justify-content-between">
<span class="font-weight-bold">Playlist entries</span>
<span>Duration: {{ playlistDuration }}</span>
</p>
<!-- If no entries are here (i.e. we add a new playlist), only show
a hint that there's nothing here yet. -->
<div
v-if="playlistEditor.entries.length === 0"
align="center"
>
No entries yet. Add some.
</div>
<!-- As soon as we have at least one entry in our temporary playlist
we can display a table with all the info and action buttons -->
<div v-else>
<b-table
ref="playlistEditTable"
striped
:items="playlistEditor.entries"
:fields="playlistEditTableFields"
>
<!-- Column: Index
Here we just use the array index, because the playlist entries
are ordered as an array, without the need for an extra id field
-->
<template v-slot:cell(id)="data">
{{ data.index + 1 }}.
</template>
<!-- Column: Type
Based on the entry content (either file or uri), we display
a small badge indicating which type of source this is
-->
<template v-slot:cell(type)="data">
<b-badge
v-if="data.item.file"
variant="success"
>
File
</b-badge>
<b-badge
v-else-if="data.item.uri.startsWith('line://')"
variant="info"
>
Line-in
</b-badge>
<b-badge
v-else-if="data.item.uri.startsWith('http://') || data.item.uri.startsWith('https://')"
variant="light"
>
Stream
</b-badge>
<b-badge
v-else
variant="dark"
>
Other
</b-badge>
</template>
<!-- Column: Source
Here we display where this playlist entry is coming from
-->
<template v-slot:cell(source)="data">
<span v-if="data.item.file">
{{ getFileTitleForPlaylist(data.item.file.show, data.item.file.id) }}
<small><i>( file://{{ data.item.file.show }}/{{ data.item.file.id }} )</i></small>
</span>
<span v-else>
{{ data.item.uri }}
</span>
</template>
<!-- Column: Actions
Finally some buttons to reorder or delete playlist entries
-->
<template v-slot:cell(actions)="data">
<b-button-group size="sm">
<b-button
:disabled="data.index === 0"
@click="movePlaylistItemUp(data.index)"
>
<b class="upDownArrows">&uarr;</b>
</b-button>
<b-button
:disabled="data.index === playlistEditor.entries.length - 1"
@click="movePlaylistItemDown(data.index)"
>
<b class="upDownArrows">&darr;</b>
</b-button>
<b-button
v-if="!data.item.file"
variant="info"
@click="editPlaylistItemDuration(data.index)"
>
Duration
</b-button>
<b-button
variant="danger"
@click="deletePlaylistItem(data.index)"
>
Delete
</b-button>
</b-button-group>
</template>
</b-table>
</div>
<hr>
<!-- Below the table with the playlists entry we display buttons to
add new entries to the table - these can either be files from our
uploaded/imported files, or one of the preconfigured inputs, or a
stream.
TODO: should we disable choosing files that are still being imported?
TODO: make the inputs configurable
-->
<div>
<b-modal
id="modal-edit-playlist-add-stream"
title="Add stream to the playlist"
@ok="addPlaylistItemStream('save')"
>
<b-input
v-model="playlistEditor.newStreamURL"
type="url"
>
...
</b-input>
</b-modal>
Add:
<b-button-group>
<b-dropdown text="File">
<b-dropdown-item
v-for="(file, index) in files"
:key="index"
@click="addPlaylistItemFile(file.show, file.id)"
>
{{ file.id }}: {{ file.metadata.title ? file.metadata.title : "" }} ({{ prettyNanoseconds(file.duration) }}, {{ prettyFileSize(file.size) }}, {{ file.source.uri }})
</b-dropdown-item>
</b-dropdown>
<b-dropdown text="Line-in">
<b-dropdown-item @click="addPlaylistItemLine('0')">
Studio 1
</b-dropdown-item>
<b-dropdown-item @click="addPlaylistItemLine('1')">
Preprod
</b-dropdown-item>
<b-dropdown-item @click="addPlaylistItemLine('2')">
Line 2
</b-dropdown-item>
</b-dropdown>
<b-button
@click="addPlaylistItemStream('openModal')"
>
Stream
</b-button>
</b-button-group>
</div>
</b-modal>
</template>
<script>
import { mapGetters } from 'vuex'
import prettyDate from '../../mixins/prettyDate'
import filesize from 'filesize'
export default {
mixins: [ prettyDate ],
// include the modal for displaying file import logs
data () {
return {
// for adding and editing the playlists we need some temporary storage
playlistEditor: {
id: null,
mode: 'add', // should be either 'add' or 'edit'
description: '',
entries: [],
newStreamURL: null
},
// configuration of the playlists entries table
playlistEditTableFields: [
{ key: 'id', label: 'Index' },
{ key: 'type', label: 'Type' },
{ key: 'source', label: 'Source' },
{ key: 'actions', label: 'Actions', class: 'text-right' },
]
}
},
computed: {
loaded () {
return {
playlists: this.$store.state.playlists.loaded.playlists,
}
},
playlistDuration() {
const totalDuration = this.playlistEditor.entries.reduce((acc, entry) => {
const file = entry.file || {}
const { id } = file
const savedFile = this.getFileById(id) || {};
if (Number.isNaN(acc + savedFile.duration)) {
return acc;
}
return acc + savedFile.duration;
}, 0);
return this.prettyNanoseconds(totalDuration)
},
...mapGetters({
selectedShow: 'shows/selectedShow',
getPlaylistById: 'playlists/getPlaylistById',
files: 'files/files',
getFileById: 'files/getFileById',
})
},
methods: {
// We want to have a nice format for the file size, given a size in Bytes.
// For that we're useing the filesize npm module.
prettyFileSize: function (s) {
return filesize(s)
},
// return a string representing a file entry for the playlist editor
getFileTitleForPlaylist (show, id) {
// TODO: change structure of files array, so we can access all shows
// the user has access to.
let file = this.getFileById(id)
if (file && file.metadata.title) {
return show + ": " + file.metadata.title
} else {
return ""
}
},
editPlaylistItemDuration (index) {
console.log(this.playlistEditor.entries, index);
},
movePlaylistItemUp (index) {
if (index > 0 && index < this.playlistEditor.entries.length) {
let temp = this.playlistEditor.entries[index - 1]
this.playlistEditor.entries[index - 1] = this.playlistEditor.entries[index]
this.playlistEditor.entries[index] = temp
this.$refs.playlistEditTable.refresh()
}
},
movePlaylistItemDown (index) {
if (index < this.playlistEditor.entries.length - 1 && index >= 0) {
let temp = this.playlistEditor.entries[index + 1]
this.playlistEditor.entries[index + 1] = this.playlistEditor.entries[index]
this.playlistEditor.entries[index] = temp
this.$refs.playlistEditTable.refresh()
}
},
deletePlaylistItem (index) {
if (index >= 0 && index < this.playlistEditor.entries.length) {
this.playlistEditor.entries.splice(index, 1)
this.$refs.playlistEditTable.refresh()
}
},
// add a file from the file manager to the playlist that is being edited
addPlaylistItemFile (show, id) {
let item = {}
item.file = {show: show, id: id}
this.playlistEditor.entries.push(item)
},
// add a line input to the playlist that is being edited
addPlaylistItemLine (line) {
this.playlistEditor.entries.push({ uri: 'line://' + line, duration: 999999 })
},
// controls sub-modal to add a new URI to the playlist editor
addPlaylistItemStream (action) {
if (action === 'openModal') {
// the function gets called with the action 'openModal' when the users
// clicks on the add new stream button. then we clear all temp data and
// open the modal
this.playlistEditor.newStreamURL = ''
this.$bvModal.show('modal-edit-playlist-add-stream')
} else {
// when the user hits ok to add the new uri, this function gets called
// with the action 'save' and the modal closes automagically. so we
// just have to push a new entry to the playlist in the editor
if (this.playlistEditor.newStreamURL.trim().length > 0) {
this.playlistEditor.entries.push({uri: this.playlistEditor.newStreamURL})
}
// if an empty string was provided, we just do nothing
}
},
addPlaylist () {
this.playlistEditor.mode = 'add'
this.playlistEditor.id = null
this.playlistEditor.description = ''
this.playlistEditor.entries = []
this.$bvModal.show('modal-edit-playlist')
},
editPlaylist (id) {
this.playlistEditor.mode = 'edit'
this.playlistEditor.entries = []
this.playlistEditor.id = id
let playlist = this.getPlaylistById(id)
this.playlistEditor.description = playlist.description
for (let i in playlist.entries) {
let entry = {}
if (playlist.entries[i].file) {
entry.file = {}
entry.file.show = playlist.entries[i].file.show
entry.file.id = playlist.entries[i].file.id
} else {
entry.uri = playlist.entries[i].uri
}
this.playlistEditor.entries.push(entry)
}
this.$bvModal.show('modal-edit-playlist')
},
storePlaylist () {
// TODO: add some spinner or other visualisation while the playlist is added/updated
let playlist = {
description: this.playlistEditor.description,
entries: this.playlistEditor.entries
}
if (this.playlistEditor.mode === 'add') {
this.$store.dispatch('playlists/add', {
slug: this.selectedShow.slug,
playlist: playlist
})
} else if (this.playlistEditor.mode === 'edit') {
this.$store.dispatch('playlists/update', {
slug: this.selectedShow.slug,
id: this.playlistEditor.id,
playlist: playlist,
callback: () => {
this.$root.$emit('bv::refresh::table', 'playlistsTable')
}
})
}
},
},
}
</script>
......@@ -30,7 +30,7 @@
</b-alert>
<b-button
variant="success"
@click="addPlaylist"
to="/playlist/new"
>
Create a playlist
</b-button>
......@@ -43,7 +43,7 @@
>
<b-button
variant="success"
@click="addPlaylist"
to="/playlist/new"
>
Create a playlist
</b-button>
......@@ -58,7 +58,10 @@
This column displays the number of entries of the playlist.
-->
<template v-slot:cell(entries)="data">
<span v-b-tooltip.html="playlistToolTip(data.value)" class="tw-underline hover:tw-no-underline tw-cursor-help">
<span
v-b-tooltip.html="playlistToolTip(data.value)"
class="tw-underline hover:tw-no-underline tw-cursor-help"
>
{{ data.value.length }} items
</span>
</template>
......@@ -84,7 +87,7 @@
<template v-slot:cell(actions)="data">
<b-button-group size="sm">
<b-button
:to="'/playlists/' + data.item.id"
:to="'/playlist/' + data.item.id"
>
Edit
</b-button>
......@@ -107,13 +110,8 @@
<script>
import { mapGetters } from 'vuex'
import prettyDate from '../../mixins/prettyDate'
import editPlaylistsModal from './EditPlaylistModal.vue'
export default {
components: {
'edit-playlists-modal': editPlaylistsModal,
},
mixins: [ prettyDate ],
// include the modal for displaying file import logs
......@@ -178,14 +176,6 @@ export default {
})
},
addPlaylist () {
this.$refs.editPlaylistsModal.addPlaylist()
},
editPlaylist (id) {
this.$refs.editPlaylistsModal.editPlaylist(id)
},
playlistDuration({ item }) {
if (!item.entries) {
return 0;
......
......@@ -7,7 +7,7 @@ import Settings from '@/Pages/Settings'
import ShowManager from '@/Pages/ShowManager'
import FileManager from '@/Pages/FileManager'
import EmissionManager from '@/Pages/EmissionManager'
import EditPlaylist from '@/Pages/EditPlaylist'
import AddOrEditPlaylist from '@/Pages/AddOrEditPlaylist'
Vue.use(Router)
......@@ -21,6 +21,7 @@ export default new Router({
{ path: '/help', name: 'help', component: Help },
{ path: '/settings', name: 'settings', component: Settings },
{ path: '/credits', name: 'credits', component: Credits },
{ path: '/playlists/:id', name: 'editPlaylist', component: EditPlaylist, props: true }
{ path: '/playlist/new', name: 'addPlaylist', component: AddOrEditPlaylist, props: { id: false, }},
{ path: '/playlist/:id', name: 'editPlaylist', component: AddOrEditPlaylist, props: true }
]
})
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment