migrate to vue3; autocomplete still needs work
This commit is contained in:
parent
ab8858154b
commit
5b788ca28f
|
@ -63,9 +63,8 @@ class SoundSerializer(serializers.ModelSerializer):
|
|||
|
||||
def get_field_names(self, *args):
|
||||
names = super().get_field_names(*args)
|
||||
if 'request' in self.context and self.context['request'].user.is_staff and \
|
||||
self.instance.is_public:
|
||||
names.push('path')
|
||||
if 'request' in self.context and self.context['request'].user.is_staff:
|
||||
names.append('path')
|
||||
return names
|
||||
|
||||
class PodcastSerializer(serializers.ModelSerializer):
|
||||
|
|
1
aircox/static/aircox/149a6b9ab513cdae59f1.woff2
Normal file
1
aircox/static/aircox/149a6b9ab513cdae59f1.woff2
Normal file
|
@ -0,0 +1 @@
|
|||
export default __webpack_public_path__ + "fonts/fa-regular-400.woff2";
|
1
aircox/static/aircox/3f9b34bb47c232621b2b.ttf
Normal file
1
aircox/static/aircox/3f9b34bb47c232621b2b.ttf
Normal file
|
@ -0,0 +1 @@
|
|||
export default __webpack_public_path__ + "fonts/fa-regular-400.ttf";
|
1
aircox/static/aircox/480cdb587f2f4adc1e42.woff2
Normal file
1
aircox/static/aircox/480cdb587f2f4adc1e42.woff2
Normal file
|
@ -0,0 +1 @@
|
|||
export default __webpack_public_path__ + "fonts/fa-v4compatibility.woff2";
|
1
aircox/static/aircox/5b2688feed672622c768.ttf
Normal file
1
aircox/static/aircox/5b2688feed672622c768.ttf
Normal file
|
@ -0,0 +1 @@
|
|||
export default __webpack_public_path__ + "fonts/fa-brands-400.ttf";
|
1
aircox/static/aircox/a45ef01cd4a352f3ea26.ttf
Normal file
1
aircox/static/aircox/a45ef01cd4a352f3ea26.ttf
Normal file
|
@ -0,0 +1 @@
|
|||
export default __webpack_public_path__ + "fonts/fa-solid-900.ttf";
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
1
aircox/static/aircox/f6ed548623081f235c71.woff2
Normal file
1
aircox/static/aircox/f6ed548623081f235c71.woff2
Normal file
|
@ -0,0 +1 @@
|
|||
export default __webpack_public_path__ + "fonts/fa-solid-900.woff2";
|
1
aircox/static/aircox/f915aad9e9e202b0e601.woff2
Normal file
1
aircox/static/aircox/f915aad9e9e202b0e601.woff2
Normal file
|
@ -0,0 +1 @@
|
|||
export default __webpack_public_path__ + "fonts/fa-brands-400.woff2";
|
1
aircox/static/aircox/ffa45c576a4d482cc80b.ttf
Normal file
1
aircox/static/aircox/ffa45c576a4d482cc80b.ttf
Normal file
|
@ -0,0 +1 @@
|
|||
export default __webpack_public_path__ + "fonts/fa-v4compatibility.ttf";
|
BIN
aircox/static/aircox/fonts/fa-v4compatibility.ttf
Normal file
BIN
aircox/static/aircox/fonts/fa-v4compatibility.ttf
Normal file
Binary file not shown.
BIN
aircox/static/aircox/fonts/fa-v4compatibility.woff2
Normal file
BIN
aircox/static/aircox/fonts/fa-v4compatibility.woff2
Normal file
Binary file not shown.
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
|
@ -4,16 +4,17 @@
|
|||
<head>
|
||||
<title>{% block title %}{% endblock %}</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="{% static "aircox/vendor.css" %}">
|
||||
<!-- <link rel="stylesheet" type="text/css" href="{% static "aircox/vendor.css" %}"> -->
|
||||
<link rel="stylesheet" type="text/css" href="{% static "admin/css/base.css" %}">
|
||||
|
||||
<script src="{% static "aircox/main.js" %}"></script>
|
||||
<script src="{% static "aircox/vendor.js" %}"></script>
|
||||
<script src="{% static "aircox/admin.js" %}"></script>
|
||||
<script src="{% static "aircox/public.js" %}"></script>
|
||||
|
||||
{% block extrastyle %}{% endblock %}
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="{% static "aircox/main.css" %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static "aircox/vendor.css" %}"/>
|
||||
<link rel="stylesheet" type="text/css" href="{% static "aircox/main.css" %}"/>
|
||||
<link rel="stylesheet" type="text/css" href="{% static "aircox/admin.css" %}">
|
||||
|
||||
{% if LANGUAGE_BIDI %}<link rel="stylesheet" type="text/css" href="{% block stylesheet_rtl %}{% static "admin/css/rtl.css" %}{% endblock %}">{% endif %}
|
||||
|
@ -30,6 +31,13 @@
|
|||
|
||||
<body class="{% if is_popup %}popup {% endif %}{% block bodyclass %}{% endblock %}"
|
||||
data-admin-utc-offset="{% now "Z" %}">
|
||||
<script id="init-script">
|
||||
window.addEventListener('load', function() {
|
||||
{% block init-scripts %}
|
||||
aircox.init({}, {config: window.AdminApp})
|
||||
{% endblock %}
|
||||
})
|
||||
</script>
|
||||
|
||||
<!-- Container -->
|
||||
<div>
|
||||
|
@ -173,7 +181,9 @@
|
|||
</div>
|
||||
<!-- END Container -->
|
||||
|
||||
{% block outside_bottom %}{% endblock %}
|
||||
{% block player %}
|
||||
<div id="player">{% include "aircox/widgets/player.html" %}</div>
|
||||
{% endblock %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -39,6 +39,14 @@ Usefull context:
|
|||
{% block head_extra %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<script id="init-script">
|
||||
window.addEventListener('load', function() {
|
||||
{% block init-scripts %}
|
||||
aircox.init()
|
||||
aircox.app.enableHotReload(window)
|
||||
{% endblock %}
|
||||
})
|
||||
</script>
|
||||
<div id="app">
|
||||
<nav class="navbar has-shadow" role="navigation" aria-label="main navigation">
|
||||
<div class="container">
|
||||
|
|
18
aircox/templates/aircox/episode_list.html
Normal file
18
aircox/templates/aircox/episode_list.html
Normal file
|
@ -0,0 +1,18 @@
|
|||
{% extends "aircox/page_list.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block filters %}
|
||||
{{ block.super }}
|
||||
<div class="field is-horizontal">
|
||||
<div class="field-label">
|
||||
<label class="label">{% trans "Podcasts" %}</label>
|
||||
</div>
|
||||
<div class="field-body">
|
||||
<div class="checkbox">
|
||||
<input type="checkbox" class="checkbox" name="podcast" value="True"
|
||||
{% if filterset_data.podcast %}checked{% endif %} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
BIN
aircox_streamer/aircox/locale/fr/LC_MESSAGES/django.mo
Normal file
BIN
aircox_streamer/aircox/locale/fr/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
130
aircox_streamer/aircox/locale/fr/LC_MESSAGES/django.po
Normal file
130
aircox_streamer/aircox/locale/fr/LC_MESSAGES/django.po
Normal file
|
@ -0,0 +1,130 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||
# This file is distributed under the same license as the PACKAGE package.
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
#, fuzzy
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2020-01-06 14:14+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
"Language: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: models.py:37
|
||||
msgid "input"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:38
|
||||
msgid "output"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:56
|
||||
msgid "station"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:58
|
||||
msgid "direction"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:59
|
||||
msgid "type"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:61
|
||||
msgid "active"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:62
|
||||
msgid "this port is active"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:65
|
||||
msgid "port settings"
|
||||
msgstr ""
|
||||
|
||||
#: models.py:66
|
||||
msgid ""
|
||||
"list of comma separated params available; this is put in the output config "
|
||||
"file as raw code; plugin related"
|
||||
msgstr ""
|
||||
|
||||
#: templates/aircox_streamer/source_item.html:19
|
||||
msgid "Synchronize source with Liquidsoap"
|
||||
msgstr ""
|
||||
|
||||
#: templates/aircox_streamer/source_item.html:23
|
||||
msgid "Synchronise"
|
||||
msgstr ""
|
||||
|
||||
#: templates/aircox_streamer/source_item.html:26
|
||||
msgid "Restart current track"
|
||||
msgstr ""
|
||||
|
||||
#: templates/aircox_streamer/source_item.html:30
|
||||
msgid "Restart"
|
||||
msgstr ""
|
||||
|
||||
#: templates/aircox_streamer/source_item.html:33
|
||||
msgid "Skip current file"
|
||||
msgstr ""
|
||||
|
||||
#: templates/aircox_streamer/source_item.html:34
|
||||
msgid "Skip"
|
||||
msgstr ""
|
||||
|
||||
#: templates/aircox_streamer/source_item.html:43
|
||||
msgid "Add sound"
|
||||
msgstr ""
|
||||
|
||||
#: templates/aircox_streamer/source_item.html:51
|
||||
msgid "Select a sound"
|
||||
msgstr ""
|
||||
|
||||
#: templates/aircox_streamer/source_item.html:53
|
||||
msgid "Add a sound to the queue (queue may start playing)"
|
||||
msgstr ""
|
||||
|
||||
#: templates/aircox_streamer/source_item.html:62
|
||||
msgid "Add"
|
||||
msgstr ""
|
||||
|
||||
#: templates/aircox_streamer/source_item.html:68
|
||||
msgid "Sounds in queue"
|
||||
msgstr ""
|
||||
|
||||
#: templates/aircox_streamer/source_item.html:86
|
||||
msgid "Status"
|
||||
msgstr ""
|
||||
|
||||
#: templates/aircox_streamer/source_item.html:96
|
||||
msgid "Air time"
|
||||
msgstr ""
|
||||
|
||||
#: templates/aircox_streamer/source_item.html:106
|
||||
msgid "Time left"
|
||||
msgstr ""
|
||||
|
||||
#: templates/aircox_streamer/source_item.html:114
|
||||
msgid "Data source"
|
||||
msgstr ""
|
||||
|
||||
#: templates/aircox_streamer/streamer.html:19
|
||||
msgid "Reload"
|
||||
msgstr ""
|
||||
|
||||
#: templates/aircox_streamer/streamer.html:26
|
||||
#: templates/aircox_streamer/streamer.html:27
|
||||
msgid "Select a station"
|
||||
msgstr ""
|
||||
|
||||
#: urls.py:9 views.py:9
|
||||
msgid "Streamer Monitor"
|
||||
msgstr ""
|
|
@ -51,10 +51,9 @@
|
|||
<form class="columns" @submit.prevent="source.push($event.target.elements['sound_id'].value)">
|
||||
<div class="column field is-small">
|
||||
{# TODO: select station => change the shit #}
|
||||
<a-autocomplete url="{% url "aircox:sound-list" %}?station={{ station.pk }}&search=${query}"
|
||||
class="is-fullwidth"
|
||||
:model="Sound" field="name" value-field="sound_id" value-attr="id"
|
||||
{# FIXME dirty hack awaiting the vue component #}
|
||||
<a-autocomplete class="is-fullwidth"
|
||||
url="{% url "aircox:sound-list" %}?station={{ station.pk }}&search=${query}"
|
||||
name="sound_id" :model="Sound" field="path" value-field="id"
|
||||
placeholder="{% trans "Select a sound" %}"></a-autocomplete>
|
||||
<p class="help">
|
||||
{% trans "Add a sound to the queue (queue may start playing)" %}
|
||||
|
|
|
@ -6,9 +6,14 @@
|
|||
<script src="{% static "aircox/streamer.js" %}"></script>
|
||||
{% endblock %}
|
||||
|
||||
{% block init-scripts %}
|
||||
aircox.init({apiUrl: "{% url "admin:api:streamer-list" %}"},
|
||||
{config: window.StreamerApp})
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{{ block.super }}
|
||||
<div id="app" v-if="streamers" data-api-url="{% url "admin:api:streamer-list" %}">
|
||||
<div id="app" api-url="{% url "admin:api:streamer-list" %}">
|
||||
<div class="navbar toolbar">
|
||||
<div class="navbar-start">
|
||||
<span class="navbar-item control">
|
||||
|
@ -22,7 +27,7 @@
|
|||
</div>
|
||||
<div class="navbar-end">
|
||||
<div class="select navbar-item">
|
||||
<select ref="selectStreamer" onchange="selectStreamer" class="control"
|
||||
<select onchange="selectStreamer" class="control"
|
||||
title="{% trans "Select a station" %}"
|
||||
aria-label="{% trans "Select a station" %}">
|
||||
<option v-for="streamer of streamers" :value="streamer.id">[[ streamer.data.name ]]</option>
|
||||
|
@ -39,7 +44,3 @@
|
|||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block outside_bottom %}
|
||||
<div id="player">{% include "aircox/widgets/player.html" %}</div>
|
||||
{% endblock %}
|
||||
|
||||
|
|
9
assets/admin/app.js
Normal file
9
assets/admin/app.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import App from 'public/app';
|
||||
import AStatistics from './statistics.vue';
|
||||
|
||||
export default {
|
||||
...App,
|
||||
components: {...App.components, AStatistics},
|
||||
}
|
||||
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
import App from 'public/app';
|
||||
import 'public';
|
||||
import '@fortawesome/fontawesome-free/css/all.min.css'
|
||||
import '@fortawesome/fontawesome-free/css/fontawesome.min.css'
|
||||
|
||||
import AdminApp from './app';
|
||||
import './admin.scss';
|
||||
import AStatistics from './statistics.vue';
|
||||
|
||||
window.aircox_admin = {
|
||||
/**
|
||||
|
@ -21,11 +21,5 @@ window.aircox_admin = {
|
|||
},
|
||||
}
|
||||
|
||||
window.aircox.builder.config = {
|
||||
...App,
|
||||
components: {...App.components, AStatistics},
|
||||
}
|
||||
|
||||
|
||||
|
||||
window.AdminApp = AdminApp
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ const App = {
|
|||
player() { return window.aircox.player; },
|
||||
},
|
||||
|
||||
components: {AAutocomplete, AEpisode, APlayer, APlaylist, ASoundItem},
|
||||
components: {AAutocomplete, AEpisode, APlaylist, ASoundItem},
|
||||
}
|
||||
|
||||
export const PlayerApp = {
|
||||
|
|
|
@ -34,16 +34,16 @@ export default class Builder {
|
|||
* @param {Boolean} [reset=False]: if True, force application recreation.
|
||||
* @return `app.mount`'s result.
|
||||
*/
|
||||
mount({content=null, title=null, el=null, reset=false}={}) {
|
||||
mount({content=null, title=null, el=null, reset=false, props=null}={}) {
|
||||
try {
|
||||
this.unmount()
|
||||
|
||||
let config = this.config
|
||||
if(el === null)
|
||||
el = config.el
|
||||
|
||||
if(reset || !this.app)
|
||||
this.app = this.createApp({title,content,el,...config})
|
||||
this.app = this.createApp({title,content,el,...config}, props)
|
||||
|
||||
this.vm = this.app.mount(el)
|
||||
window.scroll(0, 0)
|
||||
return this.vm
|
||||
|
@ -53,7 +53,7 @@ export default class Builder {
|
|||
}
|
||||
}
|
||||
|
||||
createApp({el, title=null, content=null, ...config}) {
|
||||
createApp({el, title=null, content=null, ...config}, props) {
|
||||
const container = document.querySelector(el)
|
||||
if(!container)
|
||||
throw `Error: can't get element ${el}`
|
||||
|
@ -61,7 +61,7 @@ export default class Builder {
|
|||
container.innerHTML = content
|
||||
if(title)
|
||||
document.title = title
|
||||
return createApp(config)
|
||||
return createApp(config, props)
|
||||
}
|
||||
|
||||
unmount() {
|
||||
|
|
|
@ -1,60 +1,74 @@
|
|||
<template>
|
||||
<div class="control">
|
||||
<Autocomplete ref="autocomplete" :data="data" :placeholder="placeholder" :field="field"
|
||||
:loading="isFetching" open-on-focus
|
||||
@typing="fetch" @select="object => onSelect(object)"
|
||||
>
|
||||
</Autocomplete>
|
||||
<input v-if="valueField" ref="value" type="hidden" :name="valueField"
|
||||
:value="selected && selected[valueAttr || valueField]" />
|
||||
<datalist :id="listId">
|
||||
<template v-for="item in items" :key="item.path">
|
||||
<option :value="item[field]"></option>
|
||||
</template>
|
||||
</datalist>
|
||||
<input type="text" :name="name" :placeholder="placeholder"
|
||||
:list="listId" @keyup="onKeyUp"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import debounce from 'lodash/debounce'
|
||||
import {Autocomplete} from 'buefy/dist/components/autocomplete'
|
||||
// import debounce from 'lodash/debounce'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
url: String,
|
||||
model: Function,
|
||||
placeholder: String,
|
||||
field: {type: String, default: 'value'},
|
||||
name: String,
|
||||
field: String,
|
||||
valueField: {type: String, default: 'id'},
|
||||
count: {type: Number, count: 10},
|
||||
valueAttr: String,
|
||||
valueField: String,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
data: [],
|
||||
value: '',
|
||||
items: [],
|
||||
selected: null,
|
||||
isFetching: false,
|
||||
listId: `autocomplete-${ Math.random() }`.replace('.',''),
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
onSelect(option) {
|
||||
console.log('selected', option)
|
||||
select(option, value=null) {
|
||||
if(!option && value !== null)
|
||||
option = this.items.find(item => item[this.field] == value)
|
||||
|
||||
this.selected = option
|
||||
this.$emit('select', option)
|
||||
},
|
||||
|
||||
fetch: debounce(function(query) {
|
||||
if(!query)
|
||||
onKeyUp: function(event) {
|
||||
const value = event.target.value
|
||||
if(value === this.value)
|
||||
return
|
||||
|
||||
if(value !== undefined && value !== null)
|
||||
this.value = value
|
||||
|
||||
if(!value)
|
||||
return this.select(null)
|
||||
|
||||
this.fetch(value)
|
||||
},
|
||||
|
||||
fetch: function(query) {
|
||||
if(!query || this.isFetching)
|
||||
return
|
||||
|
||||
this.isFetching = true
|
||||
this.model.fetchAll(this.url.replace('${query}', query))
|
||||
.then(data => {
|
||||
this.data = data
|
||||
return this.model.fetch(this.url.replace('${query}', query), {many:true})
|
||||
.then(items => { this.items = items || []
|
||||
this.isFetching = false
|
||||
}, data => { this.isFetching = false; Promise.reject(data) })
|
||||
}),
|
||||
this.select(null, query)
|
||||
return items },
|
||||
data => {this.isFetching = false; Promise.reject(data)})
|
||||
},
|
||||
|
||||
components: {
|
||||
Autocomplete,
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ import {Set} from './model'
|
|||
|
||||
import './styles.scss'
|
||||
|
||||
|
||||
window.aircox = {
|
||||
// main application
|
||||
builder: new Builder(App),
|
||||
|
@ -25,9 +26,28 @@ window.aircox = {
|
|||
get playerApp() { return this.playerBuilder && this.playerBuilder.app },
|
||||
get player() { return this.playerBuilder.vm && this.playerBuilder.vm.$refs.player },
|
||||
|
||||
Set: Set, Sound: Sound,
|
||||
Set, Sound,
|
||||
|
||||
|
||||
/**
|
||||
* Initialize main application and player.
|
||||
*/
|
||||
init(props=null, {config=null, builder=null, initPlayer=true}={}) {
|
||||
builder = builder || this.builder
|
||||
this.builder = builder
|
||||
if(config)
|
||||
builder.config = config
|
||||
builder.title = document.title
|
||||
builder.mount({props})
|
||||
|
||||
if(initPlayer) {
|
||||
let playerBuilder = this.playerBuilder
|
||||
playerBuilder.mount()
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
/*
|
||||
window.addEventListener('load', e => {
|
||||
const [app, player] = [aircox.builder, aircox.playerBuilder]
|
||||
app.title = document.title
|
||||
|
@ -36,4 +56,5 @@ window.addEventListener('load', e => {
|
|||
|
||||
player.mount()
|
||||
})
|
||||
*/
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ export function getCsrf() {
|
|||
// TODO: prevent duplicate simple fetch
|
||||
export default class Model {
|
||||
constructor(data, {url=null}={}) {
|
||||
this.url = url;
|
||||
this.url = url || data.url_;
|
||||
this.commit(data);
|
||||
}
|
||||
|
||||
|
@ -45,14 +45,24 @@ export default class Model {
|
|||
}
|
||||
}
|
||||
|
||||
static fromList(items, args=null) {
|
||||
return items ? items.map(d => new this(d, args)) : []
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch item from server
|
||||
*/
|
||||
static fetch(url, options=null, args=null) {
|
||||
static fetch(url, {many=false, ...options}={}, args={}) {
|
||||
options = this.getOptions(options)
|
||||
return fetch(url, options)
|
||||
.then(response => response.json())
|
||||
.then(data => new this(data, {url: url, ...args}));
|
||||
const request = fetch(url, options).then(response => response.json());
|
||||
if(many)
|
||||
return request.then(data => {
|
||||
if(!(data instanceof Array))
|
||||
data = data.results
|
||||
return this.fromList(data, args)
|
||||
})
|
||||
else
|
||||
return request.then(data => new this(data, {url: url, ...args}));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -123,7 +133,7 @@ export class Set {
|
|||
* Fetch multiple items from server
|
||||
*/
|
||||
static fetch(model, url, options=null, args=null) {
|
||||
options = this.getOptions(options)
|
||||
options = model.getOptions(options)
|
||||
return fetch(url, options)
|
||||
.then(response => response.json())
|
||||
.then(data => (data instanceof Array ? data : data.results)
|
||||
|
|
60
assets/streamer/app.js
Normal file
60
assets/streamer/app.js
Normal file
|
@ -0,0 +1,60 @@
|
|||
import AdminApp from 'admin/app';
|
||||
import Model, {Set} from 'public/model';
|
||||
import Sound from 'public/sound';
|
||||
import {setEcoInterval} from 'public/utils';
|
||||
|
||||
import {Streamer, Queue} from './controllers';
|
||||
|
||||
|
||||
export default {
|
||||
...AdminApp,
|
||||
|
||||
props: {
|
||||
...(AdminApp.props || {}),
|
||||
apiUrl: String,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
// current streamer
|
||||
streamer: null,
|
||||
// all streamers
|
||||
streamers: [],
|
||||
// fetch interval id
|
||||
fetchInterval: null,
|
||||
Sound: Sound,
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
...(AdminApp.computed || {}),
|
||||
|
||||
sources() {
|
||||
var sources = this.streamer ? this.streamer.sources : [];
|
||||
return sources.filter(s => s.data)
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
...(AdminApp.methods || {}),
|
||||
|
||||
fetchStreamers() {
|
||||
Streamer.fetch(this.apiUrl, {many:true}).then(streamers => {
|
||||
this.streamers = streamers
|
||||
this.streamer = streamers ? streamers[0] : null
|
||||
})
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.fetchStreamers();
|
||||
this.fetchInterval = setEcoInterval(() => this.streamer && this.streamer.fetch(), 5000)
|
||||
},
|
||||
|
||||
destroyed() {
|
||||
if(this.fetchInterval !== null)
|
||||
clearInterval(this.fetchInterval)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import Model, {Set} from 'public/model';
|
||||
import Model from 'public/model';
|
||||
import {setEcoInterval} from 'public/utils';
|
||||
|
||||
|
||||
|
@ -12,8 +12,8 @@ export class Streamer extends Model {
|
|||
if(!this.data)
|
||||
this.data = { id: data.id, playlists: [], queues: [] }
|
||||
|
||||
data.playlists = Playlist.Set(data.playlists, {args: {streamer: this}});
|
||||
data.queues = Queue.Set(data.queues, {args: {streamer: this}});
|
||||
data.playlists = Playlist.fromList(data.playlists, {streamer: this});
|
||||
data.queues = Queue.fromList(data.queues, {streamer: this});
|
||||
super.commit(data)
|
||||
}
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ export class Queue extends Source {
|
|||
get queue() { return this.data && this.data.queue; }
|
||||
|
||||
commit(data) {
|
||||
data.queue = Request.Set(data.queue);
|
||||
data.queue = Request.fromList(data.queue);
|
||||
super.commit(data)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,18 @@
|
|||
import App from 'public/app';
|
||||
import AdminApp from 'admin/app';
|
||||
import Model, {Set} from 'public/model';
|
||||
import Sound from 'public/sound';
|
||||
import {setEcoInterval} from 'public/utils';
|
||||
|
||||
import {Streamer, Queue} from './controllers';
|
||||
|
||||
window.aircox.builder.config = {
|
||||
...App,
|
||||
|
||||
export const StreamerApp = {
|
||||
...AdminApp,
|
||||
|
||||
props: {
|
||||
...(AdminApp.props || {}),
|
||||
apiUrl: String,
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
|
@ -21,11 +27,7 @@ window.aircox.builder.config = {
|
|||
},
|
||||
|
||||
computed: {
|
||||
...(App.computed || {}),
|
||||
|
||||
apiUrl() {
|
||||
return this.$el && this.$el.dataset.apiUrl;
|
||||
},
|
||||
...(AdminApp.computed || {}),
|
||||
|
||||
sources() {
|
||||
var sources = this.streamer ? this.streamer.sources : [];
|
||||
|
@ -34,10 +36,10 @@ window.aircox.builder.config = {
|
|||
},
|
||||
|
||||
methods: {
|
||||
...(App.methods || {}),
|
||||
...(AdminApp.methods || {}),
|
||||
|
||||
fetchStreamers() {
|
||||
Set.fetch(Streamer, this.apiUrl).then(streamers => {
|
||||
Streamer.fetch(this.apiUrl, {many:true}).then(streamers => {
|
||||
this.streamers = streamers
|
||||
this.streamer = streamers ? streamers[0] : null
|
||||
})
|
||||
|
@ -55,3 +57,5 @@ window.aircox.builder.config = {
|
|||
}
|
||||
}
|
||||
|
||||
window.StreamerApp = StreamerApp
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user